1、策略更新

2、新增qmt
This commit is contained in:
2025-11-29 00:23:12 +08:00
parent 0a942f92d1
commit c9b61db5b7
47 changed files with 97116 additions and 8867 deletions

0
qmt/__init__.py Normal file
View File

35
qmt/qmt_test.py Normal file
View File

@@ -0,0 +1,35 @@
from xtquant import xttrader
from xtquant.xttype import StockAccount
import random
##订阅账户
# 设置 QMT 交易端的数据路径和会话ID
min_path = r"D:\QMT\国金证券QMT交易端\userdata_mini"
session_id = int(random.randint(100000, 999999))
# 创建 XtQuantTrader 实例并启动
xt_trader = xttrader.XtQuantTrader(min_path, session_id)
xt_trader.start()
# 连接 QMT 交易端
connect_result = xt_trader.connect()
if connect_result == 0:
print('连接成功')
else:
print('连接失败')
xt_trader.stop()
exit()
# 设置账户信息
account = StockAccount('8886100517')
# 订阅账户
res = xt_trader.subscribe(account)
if res == 0:
print('订阅成功')
else:
print('订阅失败')
asset = xt_trader.query_stock_asset(account)
print(asset.cash)

182
qmt/qmt_trader.py Normal file
View File

@@ -0,0 +1,182 @@
# 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)