Files
NewStock/qmt/qmt_trader.py
2025-11-29 00:23:12 +08:00

182 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)