real_trader/src/trade_server.py
2025-05-11 01:18:04 +08:00

323 lines
10 KiB
Python

import schedule
import threading
import time
from real.xt_trader import XtTrader
from flask import Flask, request, abort, jsonify
from config import Config
from simulation.simulation_trader import SimulationTrader
from logger_config import get_logger
from real.real_trader_manager import RealTraderManager
from trade_constants import *
# 获取日志记录器
logger = get_logger("server")
# 全局交易实例(采用单例模式)
_trader_instance = None # 交易实例(单例)
_real_trader_manager_instance = None # 实盘交易管理器实例(单例)
def is_real_mode():
return not Config.SIMULATION_MODE
# 获取实盘交易管理器实例的辅助函数
def get_real_trader_manager():
global _real_trader_manager_instance
if _real_trader_manager_instance is None:
_real_trader_manager_instance = (
None if Config.SIMULATION_MODE else RealTraderManager(get_trader())
)
return _real_trader_manager_instance
# 获取交易实例 - 根据情况返回模拟或实盘交易实例
def get_trader():
global _trader_instance
if _trader_instance is None:
_trader_instance = SimulationTrader() if Config.SIMULATION_MODE else XtTrader()
return _trader_instance
def login():
try:
global _trader_instance
# 如果已经有实例,先销毁
if _trader_instance is not None and is_real_mode():
_trader_instance.logout()
_trader_instance = None
# 创建新的XtTrader实例
_trader_instance = SimulationTrader() if Config.SIMULATION_MODE else XtTrader()
logger.info("开始登录")
_trader_instance.login()
result = _trader_instance.get_balance()
if result and result["account_id"] is not None:
logger.info(f"查询余额成功: {result}")
else:
logger.error(f"登录失败: {result}")
raise Exception(f"登录失败: {result}")
except Exception as e:
logger.error(f"登录失败: {e}")
raise Exception(f"登录失败: {e}")
def logout():
global _trader_instance
if _trader_instance is not None:
_trader_instance.logout()
logger.info("登出成功")
# 销毁实例
if is_real_mode():
_trader_instance = None
logger.info("XtTrader实例已销毁")
def setup_scheduler():
# 设置每日任务
schedule.every().day.at(Config.MARKET_OPEN_TIME).do(login)
schedule.every().day.at(Config.MARKET_CLOSE_TIME).do(logout)
# 启动调度线程
scheduler_thread = threading.Thread(target=_run_scheduler, daemon=True)
scheduler_thread.start()
logger.info("定时任务调度器已启动")
return scheduler_thread
def _run_scheduler():
"""定时任务执行线程"""
while True:
try:
schedule.run_pending()
time.sleep(1)
except Exception as e:
logger.error(f"调度器执行错误: {str(e)}")
# 设置并启动调度器
setup_scheduler()
# 程序启动时初始化交易实例
login()
# 添加请求频率限制
app = Flask(__name__)
@app.route("/yu/healthcheck", methods=["GET"])
def health_check():
return "ok", 200
@app.route("/yu/buy", methods=["POST"])
def buy():
"""Buy an item with given parameters."""
logger.info("Received buy request")
# Get data from request body
data = request.get_json()
code = data.get("code")
price_str = data.get("price")
amount_str = data.get("amount")
strategy_name = data.get(
"strategy_name", "default_strategy"
) # 新增策略名称参数,默认为空
try:
if not all([code, price_str, amount_str]):
raise ValueError("Missing required parameters")
price = float(price_str)
amount = int(amount_str)
if price <= 0 or amount <= 0:
raise ValueError("Price and amount must be positive")
# 检查是否在交易时间内
if not get_trader().is_trading_time():
logger.warning(
f"交易失败 - 非交易时间不能交易 - 代码: {code}, 价格: {price}, 数量: {amount}"
)
return (
jsonify(
{"success": False, "error": f"交易失败: 非交易时间不能实盘交易"}
),
400,
)
if is_real_mode():
logger.info(
f"使用RealTraderManager执行买入: 代码={code}, 价格={price}, 数量={amount}, 策略={strategy_name}"
)
rtm = get_real_trader_manager()
result = rtm.place_order(
strategy_name, code, ORDER_DIRECTION_BUY, amount, price
)
else:
result = get_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}), 400
except ValueError as e:
logger.error(f"Invalid request parameters: {str(e)}")
abort(400, description=str(e))
except Exception as e:
logger.error(f"Error processing buy request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/sell", methods=["POST"])
def sell():
"""Sell an item with given parameters."""
logger.info("Received sell request")
# Get data from request body
data = request.get_json()
code = data.get("code")
price_str = data.get("price")
amount_str = data.get("amount")
strategy_name = data.get("strategy_name", "") # 新增策略名称参数,默认为空
try:
if not all([code, price_str, amount_str]):
raise ValueError("Missing required parameters")
price = float(price_str)
amount = int(amount_str)
if price <= 0 or amount <= 0:
raise ValueError("Price and amount must be positive")
# 检查是否在交易时间内
if not get_trader().is_trading_time():
logger.warning(
f"交易失败 - 非交易时间不能交易 - 代码: {code}, 价格: {price}, 数量: {amount}"
)
return (
jsonify(
{"success": False, "error": f"交易失败: 非交易时间不能实盘交易"}
),
400,
)
if is_real_mode():
rtm = get_real_trader_manager()
result = rtm.place_order(
strategy_name, code, ORDER_DIRECTION_SELL, amount, price
)
else:
result = get_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}), 400
except ValueError as e:
logger.error(f"Invalid request parameters: {str(e)}")
abort(400, description=str(e))
except Exception as e:
logger.error(f"Error processing sell request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/cancel/<order_id>", methods=["DELETE"])
def cancel(order_id):
logger.info(f"Received cancel request for entrust_no={order_id}")
try:
result = get_trader().cancel(order_id)
return jsonify({"success": True, "data": result}), 200
except Exception as e:
logger.error(f"Error processing cancel request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/balance", methods=["GET"])
def get_balance():
"""Get the balance of the account."""
logger.info("Received balance request")
try:
# 直接使用实盘交易实例,不考虑模拟盘
balance = get_trader().get_balance()
logger.info(f"实盘交易余额: {balance}")
return jsonify({"success": True, "data": balance}), 200
except Exception as e:
logger.error(f"Error processing balance request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/positions", methods=["GET"])
def get_positions():
"""Get the positions of the account."""
logger.info("Received positions request")
try:
result = get_trader().get_positions()
return jsonify({"success": True, "data": result}), 200
except Exception as e:
logger.error(f"Error processing positions request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/todaytrades", methods=["GET"])
def get_today_trades():
"""Get the today's trades of the account."""
logger.info("Received today trades request")
try:
trades = get_trader().get_today_trades()
logger.info(f"今日成交: {trades}")
return jsonify({"success": True, "data": trades}), 200
except Exception as e:
logger.error(f"Error processing today trades request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/todayorders", methods=["GET"])
def get_today_orders():
"""Get the today's entrust of the account."""
logger.info("Received today entrust request")
try:
entrust = get_trader().get_today_orders()
logger.info(f"今日委托: {entrust}")
return jsonify({"success": True, "data": entrust}), 200
except Exception as e:
logger.error(f"Error processing today entrust request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/clear/<strategy_name>", methods=["DELETE"])
def clear_strategy(strategy_name):
"""清除指定策略的持仓管理数据"""
logger.info(f"接收到清除策略持仓请求: {strategy_name}")
try:
get_trader().clear_position_manager(strategy_name)
return jsonify({"success": True, "message": "clear success"}), 200
except Exception as e:
logger.error(f"清除策略持仓时出错: {str(e)}")
abort(500, description="服务器内部错误")
if __name__ == "__main__":
logger.info(f"Server starting on {Config.HOST}:{Config.PORT}")
app.run(debug=Config.DEBUG, host=Config.HOST, port=Config.PORT)