168 lines
7.8 KiB
Python
168 lines
7.8 KiB
Python
"""
|
|
核心交易操作相关的API端点 (买入、卖出、撤单)。
|
|
"""
|
|
from flask import Blueprint, request, jsonify, abort
|
|
|
|
from core.app_state import get_trader, get_real_trader_manager, is_real_mode, logger
|
|
from base_trader import BaseTrader
|
|
from trade_constants import ORDER_TYPE_LIMIT, ORDER_DIRECTION_BUY, ORDER_DIRECTION_SELL
|
|
|
|
trading_bp = Blueprint('trading_routes', __name__, url_prefix='/yu')
|
|
|
|
@trading_bp.route("/buy", methods=["POST"])
|
|
def buy():
|
|
"""处理买入请求。"""
|
|
logger.info("Received buy request")
|
|
data = request.get_json()
|
|
if not data:
|
|
logger.error("Buy request with no JSON data")
|
|
abort(400, description="Request body must be JSON.")
|
|
|
|
code = data.get("code")
|
|
price_str = data.get("price")
|
|
amount_str = data.get("amount")
|
|
strategy_name = data.get("strategy_name", "default_strategy")
|
|
order_type = data.get("order_type", ORDER_TYPE_LIMIT)
|
|
|
|
try:
|
|
if not all([code, price_str, amount_str]):
|
|
logger.warning(f"Buy request missing parameters: code={code}, price={price_str}, amount={amount_str}")
|
|
raise ValueError("Missing required parameters: code, price, amount")
|
|
|
|
price = float(price_str)
|
|
amount = int(float(amount_str))
|
|
|
|
if order_type == ORDER_TYPE_LIMIT and (price <= 0 or amount <= 0):
|
|
logger.warning(f"Buy request with invalid price/amount: price={price}, amount={amount}")
|
|
raise ValueError("For limit orders, price and amount must be positive")
|
|
|
|
trader = get_trader()
|
|
if not trader:
|
|
logger.error("Trader instance not available for buy request.")
|
|
return jsonify({"success": False, "error": "Trader not available"}), 503
|
|
|
|
if is_real_mode():
|
|
if not BaseTrader.is_trading_time():
|
|
logger.warning(f"Buy attempt outside trading hours: {code}, {price}, {amount}")
|
|
return jsonify({"success": False, "error": "交易失败: 非交易时间不能实盘交易"}), 400
|
|
|
|
if not trader.is_available():
|
|
logger.error(f"Trader not available for real mode buy: {trader.connection_error_message}")
|
|
return jsonify({"success": False, "error": trader.connection_error_message or "交易系统连接失败"}), 503
|
|
|
|
rtm = get_real_trader_manager()
|
|
if not rtm:
|
|
logger.error("RealTraderManager not available for buy request.")
|
|
return jsonify({"success": False, "error": "RealTraderManager not available"}), 503
|
|
|
|
logger.info(f"Using RealTraderManager for buy: {code}, {price}, {amount}, {strategy_name}")
|
|
result = rtm.place_order(strategy_name, code, ORDER_DIRECTION_BUY, amount, price, order_type)
|
|
else:
|
|
result = trader.buy(code, price, amount, strategy_name)
|
|
|
|
if result.get("success"):
|
|
logger.info(f"买入下单成功: {result}")
|
|
return jsonify({"success": True, "order_id": result.get("order_id")}), 200
|
|
else:
|
|
logger.error(f"买入下单失败: {result}")
|
|
return jsonify({"success": False, "error": result.get("error", "Unknown error placing buy order")}), 400
|
|
|
|
except ValueError as e:
|
|
logger.error(f"Invalid buy request parameters: {str(e)}")
|
|
abort(400, description=str(e))
|
|
except Exception as e:
|
|
logger.error(f"Error processing buy request: {str(e)}", exc_info=True)
|
|
abort(500, description="Internal server error during buy request")
|
|
|
|
@trading_bp.route("/sell", methods=["POST"])
|
|
def sell():
|
|
"""处理卖出请求。"""
|
|
logger.info("Received sell request")
|
|
data = request.get_json()
|
|
if not data:
|
|
logger.error("Sell request with no JSON data")
|
|
abort(400, description="Request body must be JSON.")
|
|
|
|
code = data.get("code")
|
|
price_str = data.get("price")
|
|
amount_str = data.get("amount")
|
|
strategy_name = data.get("strategy_name", "default_strategy")
|
|
order_type = data.get("order_type", ORDER_TYPE_LIMIT)
|
|
|
|
try:
|
|
if not all([code, price_str, amount_str]):
|
|
logger.warning(f"Sell request missing parameters: code={code}, price={price_str}, amount={amount_str}")
|
|
raise ValueError("Missing required parameters: code, price, amount")
|
|
|
|
price = float(price_str)
|
|
amount = int(float(amount_str))
|
|
|
|
if order_type == ORDER_TYPE_LIMIT and (price <= 0 or amount <= 0):
|
|
logger.warning(f"Sell request with invalid price/amount: price={price}, amount={amount}")
|
|
raise ValueError("For limit orders, price and amount must be positive")
|
|
|
|
trader = get_trader()
|
|
if not trader:
|
|
logger.error("Trader instance not available for sell request.")
|
|
return jsonify({"success": False, "error": "Trader not available"}), 503
|
|
|
|
if is_real_mode():
|
|
if not BaseTrader.is_trading_time():
|
|
logger.warning(f"Sell attempt outside trading hours: {code}, {price}, {amount}")
|
|
return jsonify({"success": False, "error": "交易失败: 非交易时间不能实盘交易"}), 400
|
|
|
|
if not trader.is_available():
|
|
logger.error(f"Trader not available for real mode sell: {trader.connection_error_message}")
|
|
return jsonify({"success": False, "error": trader.connection_error_message or "交易系统连接失败"}), 503
|
|
|
|
rtm = get_real_trader_manager()
|
|
if not rtm:
|
|
logger.error("RealTraderManager not available for sell request.")
|
|
return jsonify({"success": False, "error": "RealTraderManager not available"}), 503
|
|
|
|
logger.info(f"Using RealTraderManager for sell: {code}, {price}, {amount}, {strategy_name}")
|
|
result = rtm.place_order(strategy_name, code, ORDER_DIRECTION_SELL, amount, price, order_type)
|
|
else:
|
|
result = trader.sell(code, price, amount, strategy_name)
|
|
|
|
if result.get("success"):
|
|
logger.info(f"卖出下单成功: {result}")
|
|
return jsonify({"success": True, "order_id": result.get("order_id")}), 200
|
|
else:
|
|
logger.error(f"卖出下单失败: {result}")
|
|
return jsonify({"success": False, "error": result.get("error", "Unknown error placing sell order")}), 400
|
|
|
|
except ValueError as e:
|
|
logger.error(f"Invalid sell request parameters: {str(e)}")
|
|
abort(400, description=str(e))
|
|
except Exception as e:
|
|
logger.error(f"Error processing sell request: {str(e)}", exc_info=True)
|
|
abort(500, description="Internal server error during sell request")
|
|
|
|
@trading_bp.route("/cancel/<order_id>", methods=["DELETE"])
|
|
def cancel(order_id: str):
|
|
"""根据订单ID处理撤单请求。"""
|
|
logger.info(f"Received cancel request for order {order_id}")
|
|
|
|
try:
|
|
trader = get_trader()
|
|
if not trader:
|
|
logger.error("Trader instance not available for cancel request.")
|
|
return jsonify({"success": False, "error": "Trader not available"}), 503
|
|
|
|
if is_real_mode() and not trader.is_available():
|
|
logger.error(f"Trader not available for real mode cancel: {trader.connection_error_message}")
|
|
return jsonify({"success": False, "error": trader.connection_error_message or "交易系统连接失败"}), 503
|
|
|
|
result = trader.cancel(order_id)
|
|
logger.info(f"撤单结果: {result}")
|
|
|
|
# 假设 cancel 操作总是返回一个字典,即使失败也包含状态
|
|
if isinstance(result, dict) and result.get("success") is False:
|
|
return jsonify(result), 400 # Propagate error from trader if structured
|
|
|
|
return jsonify(result), 200
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error processing cancel request for order {order_id}: {str(e)}", exc_info=True)
|
|
abort(500, description=f"Internal server error during cancel request for order {order_id}") |