323 lines
10 KiB
Python
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)
|