Files
sanguo_vnpy_v2/vnpy_qmt_v0.3.3/vnpy_qmt/td.py
T
claude_dev 70d72f35bb chore(vendor): vendor vnpy_qmt 0.3.3 源码自行维护
vnpy_qmt 是唯一券商交易 gateway(关键路径),社区包断更有风险。
按 vnpy_v4.4.0 同一套约定 vendor 源码、sys.path 引用、不 pip install。
- vnpy_qmt_v0.3.3/vnpy_qmt/ (5文件587行) + LICENSE(Apache-2.0) + README
- run_web.py 加 if-exists 路径块,import 方式不变 (from vnpy_qmt import QmtGateway)
Mac 验证: py_compile 全过 + import 解析到 vendor 副本(仅卡 xtquant Win-only,符合预期)
VPS 生产切换(Part B: rsync+pip uninstall+重启+实证) 待确认
2026-07-15 21:15:53 +08:00

228 lines
8.5 KiB
Python

# -*- coding:utf-8 -*-
"""
@FileName :td.py
@Time :2022/11/8 17:14
@Author :fsksf
"""
import os
import random
from typing import Dict
import datetime
from xtquant.xttrader import XtQuantTraderCallback, XtQuantTrader
from xtquant.xttype import (
XtTrade, XtAsset, XtOrder, XtOrderError, XtCreditOrder, XtOrderResponse,
XtPosition, XtCreditDeal, XtCancelError, XtCancelOrderResponse, StockAccount
)
from vnpy.trader.constant import Direction, Status, Product
from vnpy.trader.object import (
AccountData, TradeData, OrderData, OrderRequest, PositionData
)
from vnpy_qmt.utils import (to_vn_product, to_vn_contract, to_qmt_code,
From_VN_Trade_Type, from_vn_price_type, TO_VN_Trade_Type,
timestamp_to_datetime, TO_VN_ORDER_STATUS)
class TD(XtQuantTraderCallback):
def __init__(self, gateway, *args, **kwargs):
super(TD, self).__init__(*args, **kwargs)
self.gateway = gateway
self.count = 0
self.session_id = int(datetime.datetime.now().strftime('%H%M%S'))
self.trader: XtQuantTrader = None
self.account = None
self.mini_path = None
self.inited = False
self.orders: Dict[str, OrderData] = {}
self.traders: Dict[str, TradeData] = {}
def connect(self, settings: dict):
account = settings['交易账号']
self.mini_path = path = settings['mini路径']
acc = StockAccount(account)
self.account = acc
self.trader = XtQuantTrader(path=path, session=self.session_id)
self.trader.register_callback(self)
self.trader.start()
self.write_log('连接QMT')
cnn_msg = self.trader.connect()
if cnn_msg == 0:
self.write_log('连接成功')
else:
self.write_log(f'连接失败:{cnn_msg}')
sub_msg = self.trader.subscribe(account=acc)
if sub_msg == 0:
self.write_log(f'订阅账户成功: {sub_msg}')
self.inited = True
else:
self.write_log(f'订阅账户【失败】: {sub_msg}')
def get_order_remark(self):
self.count += 1
mark = f'{str(self.session_id)}#{self.count}'
return mark
def send_order(self, req: OrderRequest):
vn_oid = self.get_order_remark()
seq = self.trader.order_stock_async(
account=self.account,
stock_code=to_qmt_code(symbol=req.symbol, exchange=req.exchange),
order_type=From_VN_Trade_Type[req.direction],
price_type=from_vn_price_type(req),
order_volume=int(req.volume),
price=req.price,
order_remark=vn_oid,
)
order = OrderData(gateway_name=self.gateway.gateway_name,
symbol=req.symbol,
exchange=req.exchange,
orderid=vn_oid,
type=req.type,
direction=req.direction,
offset=req.offset,
volume=req.volume,
price=req.price,
status=Status.SUBMITTING)
self.orders[order.orderid] = order
return order.vt_orderid
def cancel_order(self, order_id):
order = self.orders.get(order_id)
if order is None:
return
return self.trader.cancel_order_stock_async(account=self.account, order_id=order.reference)
def query_account(self):
return self.trader.query_stock_asset_async(self.account, callback=self.on_stock_asset)
def query_position(self):
return self.trader.query_stock_positions_async(self.account, callback=self.on_stock_positions_callback)
def query_order(self):
self.trader.query_stock_orders_async(self.account, callback=self.on_stock_order_callback)
def query_trade(self):
self.trader.query_stock_trades_async(self.account, callback=self.on_stock_trade_callback)
def on_disconnected(self):
pass
def on_stock_asset(self, asset: XtAsset):
account = AccountData(
accountid=asset.account_id,
frozen=asset.frozen_cash,
balance=asset.total_asset,
gateway_name=self.gateway.gateway_name
)
self.gateway.on_account(account)
def on_stock_order_callback(self, order_list):
for order in order_list:
self.on_stock_order(order)
def on_stock_positions_callback(self, pos_list):
for pos in pos_list:
self.on_stock_position(pos)
def on_stock_trade_callback(self, trade_list):
for trade in trade_list:
self.on_stock_trade(trade)
def on_stock_order(self, order: XtOrder):
symbol, exchange = to_vn_contract(order.stock_code)
remark_id = order.order_remark
vn_order = OrderData(
orderid=remark_id,
symbol=symbol,
exchange=exchange,
price=order.price,
volume=order.order_volume,
traded=order.traded_volume,
gateway_name=self.gateway.gateway_name,
status=TO_VN_ORDER_STATUS[order.order_status],
direction=TO_VN_Trade_Type[order.order_type],
datetime=timestamp_to_datetime(order.order_time),
reference=order.order_id
)
old_order = self.orders.get(vn_order.orderid, None)
if old_order == vn_order:
return
if vn_order.status == Status.REJECTED:
self.write_log(f'【拒单】 {order.status_msg}')
self.orders[vn_order.orderid] = vn_order
self.gateway.on_order(vn_order)
def on_stock_position(self, position: XtPosition):
try:
symbol, exchange = to_vn_contract(position.stock_code)
except Exception as e:
print(f"on_stock_position 无法解析的代码: {position.stock_code}")
return
# TODO ETF相关字段处理
position_ = PositionData(
gateway_name=self.gateway.gateway_name,
symbol=symbol,
exchange=exchange,
direction=Direction.LONG,
volume=position.volume,
yd_volume=position.yesterday_volume,
price=position.open_price,
pnl=position.market_value - position.volume * position.open_price
)
contract = self.gateway.get_contract(position_.vt_symbol)
if contract:
position_.product = contract.product
position_.__post_init__()
self.gateway.on_position(position_)
def on_stock_trade(self, trade: XtTrade):
symbol, exchange = to_vn_contract(trade.stock_code)
vn_oid = trade.order_remark
if vn_oid is None:
return
order = self.orders.get(vn_oid)
if order is None:
return
trd_typ = TO_VN_Trade_Type[trade.order_type]
trade_ = TradeData(
gateway_name=self.gateway.gateway_name,
symbol=symbol,
exchange=exchange,
orderid=vn_oid,
tradeid=trade.traded_id,
price=trade.traded_price,
datetime=timestamp_to_datetime(trade.traded_time),
volume=trade.traded_volume,
direction=trd_typ
)
self.gateway.on_trade(trade_)
def on_cancel_error(self, cancel_error: XtCancelError):
self.write_log(cancel_error.error_msg)
def on_order_error(self, order_error: XtOrderError):
self.write_log(f'订单错误:{order_error.error_msg}')
vn_oid = order_error.order_remark
old_order = self.orders.get(vn_oid)
if old_order:
old_order.status = Status.REJECTED
self.gateway.on_order(old_order)
def on_order_stock_async_response(self, response: XtOrderResponse):
self.write_log(f'下单成功 {response.order_id} {response.order_remark} {response.strategy_name}')
old_order = self.orders.get(response.order_remark)
if old_order:
if response.error_msg:
old_order.status = Status.REJECTED
self.write_log(f'下单失败 {response.order_remark} 原因: {response.error_msg}')
self.gateway.on_order(old_order)
def on_cancel_order_stock_async_response(self, response: XtCancelOrderResponse):
self.write_log(f'撤单结果: {response.cancel_result}')
def write_log(self, msg):
self.gateway.write_log(f'[ td ] {msg}')