real_trader/src/routes/trading_routes.py
2025-05-22 22:30:08 +08:00

168 lines
7.9 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 core.base_trader import BaseTrader
from core.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}")