# coding:utf-8 import time, datetime, traceback, sys, json import redis from xtquant import xtdata from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback from xtquant.xttype import StockAccount from xtquant import xtconstant # ================= 配置区域 ================= QMT_PATH = r'D:\qmt\投研\迅投极速交易终端睿智融科版\userdata' ACCOUNT_ID = '2000128' ACCOUNT_TYPE = 'STOCK' REDIS_HOST = '127.0.0.1' REDIS_PORT = 6379 REDIS_PASS = None # 策略基础名称 (不需要加 _real,代码会自动加) STRATEGY_BASE_NAME = 'default_strategy' # =========================================== # 定义监听的队列名称 (只监听实盘队列,物理屏蔽回测数据) LISTEN_QUEUE = f"{STRATEGY_BASE_NAME}_real" class MyXtQuantTraderCallback(XtQuantTraderCallback): def on_disconnected(self): print("连接断开") def on_stock_order(self, order): print(f"委托回报: {order.order_id} {order.order_remark}") def on_stock_trade(self, trade): print(f"成交: {trade.stock_code} {trade.traded_volume}") def on_order_error(self, order_error): print(f"下单失败: {order_error.error_msg}") def init_redis(): try: r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASS, decode_responses=True) r.ping() return r except Exception as e: print(f"Redis连接失败: {e}") return None def is_msg_valid(data): """ 【安全核心】校验消息时效性与合法性 """ try: # 1. 检查是否为回测标记 (防御性编程,虽然队列已物理隔离) if data.get('is_backtest', False): print(f"警报:拦截到回测数据,已丢弃!") return False # 2. 检查时间戳 msg_time_str = data.get('timestamp') if not msg_time_str: print("数据缺失时间戳,丢弃") return False # 解析消息时间 # 格式必须匹配策略端发送的 '%Y-%m-%d %H:%M:%S' msg_dt = datetime.datetime.strptime(msg_time_str, '%Y-%m-%d %H:%M:%S') msg_date = msg_dt.date() # 获取当前服务器日期 today = datetime.date.today() # 3. 【核心】判断是否为当天的消息 if msg_date != today: print(f"拦截过期消息: 消息日期[{msg_date}] != 今日[{today}]") return False # 可选:如果你想更严格,可以判断时间差不能超过5分钟 # delta = datetime.datetime.now() - msg_dt # if abs(delta.total_seconds()) > 300: ... return True except Exception as e: print(f"校验逻辑异常: {e}") return False def process_redis_signal(r_client, xt_trader, acc): try: msg_json = r_client.lpop(LISTEN_QUEUE) if not msg_json: return print(f"收到信号: {msg_json}") data = json.loads(msg_json) if not is_msg_valid(data): return # 之前的校验逻辑 stock_code = data['stock_code'] action = data['action'] price = float(data['price']) # 获取切分份数 # 兼容性处理:如果redis里还是旧key 'weight',也可以尝试获取 div_count = float(data.get('div_count', data.get('weight', 1))) # ========================================================= # 买入逻辑:资金切片法 # ========================================================= if action == 'BUY': # 1. 必须查最新的可用资金 (Available Cash) asset = xt_trader.query_stock_asset(acc) if not asset: print("错误:无法查询资产") return current_cash = asset.cash # 2. 计算下单金额 # 逻辑:Amount = Cash / div_count if div_count <= 0: div_count = 1 # 防止除0 target_amount = current_cash / div_count # 3. 打印调试信息 (非常重要) print(f"【资金分配】可用现金:{current_cash:.2f} / 切分份数:{div_count} = 下单金额:{target_amount:.2f}") # 4. 计算股数 if price <= 0: price = 1.0 # 过滤小额杂单 if target_amount < 2000: print(f"忽略:金额过小 ({target_amount:.2f})") return vol = int(target_amount / price / 100) * 100 if vol >= 100: xt_trader.order_stock(acc, stock_code, xtconstant.STOCK_BUY, vol, xtconstant.FIX_PRICE, price, STRATEGY_BASE_NAME, 'PyBuy') print(f"买入下单: {stock_code} {vol}股") else: print(f"计算股数不足100股") # ========================================================= # 卖出逻辑 (清仓) # ========================================================= elif action == 'SELL': positions = xt_trader.query_stock_positions(acc) target_pos = next((p for p in positions if p.stock_code == stock_code), None) if target_pos and target_pos.can_use_volume > 0: xt_trader.order_stock(acc, stock_code, xtconstant.STOCK_SELL, target_pos.can_use_volume, xtconstant.FIX_PRICE, price, STRATEGY_BASE_NAME, 'PySell') print(f"卖出下单: {stock_code} {target_pos.can_use_volume}股") else: print(f"无可用持仓: {stock_code}") except Exception as e: print(f"处理异常: {e}") traceback.print_exc() if __name__ == '__main__': r_client = init_redis() session_id = int(time.time()) xt_trader = XtQuantTrader(QMT_PATH, session_id) acc = StockAccount(ACCOUNT_ID, ACCOUNT_TYPE) callback = MyXtQuantTraderCallback() xt_trader.register_callback(callback) xt_trader.start() xt_trader.connect() xt_trader.subscribe(acc) print(f"=== 启动监听: {LISTEN_QUEUE} ===") print("只处理当日的实盘/模拟信号,自动过滤回测数据及历史遗留数据。") while True: if r_client: process_redis_signal(r_client, xt_trader, acc) time.sleep(60)