问题描述
国金证券,我是监听一个组合,先卖后买,卖完之后,没隔 0.3 秒查询余额然后买入,循环 8 秒,结果 cash 一直没有增加...
是我代码的问题么?还是国金客户端哪里设置的错了缓存之类的?
解决方案
这是一个在 QMT/miniQMT 实盘交易中非常常见的问题,不是你的代码写错了,也不是客户端设置错误,而是由底层接口的缓存机制和券商柜台的同步延迟造成的。
为什么会出现资金未及时更新的情况?
- 客户端缓存机制:QMT 客户端为了降低对券商柜台的查询压力,对资产查询(如
query_stock_asset或get_trade_detail_data查询 account)是有缓存机制的。你每隔 0.3 秒去高频轮询,极大概率命中的都是本地缓存的旧数据,而不是实时去柜台拉取的最新数据。 - 柜台结算延迟:即使订单在交易所已经撮合成交,券商柜台进行资金解冻、清算并更新到你的可用资金账户中,也需要一定的毫秒级到秒级的时间。0.3秒的轮询频率远快于柜台资金状态同步的频率。
- 字段查看错误:请确保你查询的是可用资金(Available)而不是可取资金(FetchBalance)。A股是T+1交收,卖出后的钱当天只能买股票(可用),不能提现(可取)。
推荐的解决方案
在量化交易中,极其不推荐使用高频轮询(While + Sleep)的方式来等待资金更新。正确的做法是采用事件驱动(回调函数)或本地资金影子账户的方式。
方案一:使用成交回报回调(推荐)
利用 QMT 提供的 deal_callback(成交主推函数)。当卖出订单真实成交时,系统会主动推送成交信息。你可以在收到卖出成交的回报后,再触发买入逻辑。
def init(ContextInfo):
ContextInfo.set_account('你的资金账号')
ContextInfo.buy_flag = False # 控制是否已经触发买入
def deal_callback(ContextInfo, dealInfo):
# 当收到成交回报时触发
print('收到成交回报,成交代码:', dealInfo.m_strInstrumentID)
# 判断如果是你刚才卖出的股票成交了
if dealInfo.m_nDirection == 49: # 49代表卖出
print(f'卖出成交数量: {dealInfo.m_nVolume}, 成交金额: {dealInfo.m_dTradeAmount}')
# 此时说明钱已经回来了(或者即将回来),可以执行买入逻辑
if not ContextInfo.buy_flag:
# 执行买入操作
# passorder(23, 1101, ContextInfo.accID, '买入代码', 5, -1, 买入数量, ContextInfo)
ContextInfo.buy_flag = True
方案二:维护本地“影子资金”
如果你必须连续执行“卖出 -> 买入”的换仓操作,最稳妥的方法是在代码中自己计算可用资金,而不是依赖接口返回。
- 策略初始化时,查询一次初始可用资金
local_cash = get_trade_detail_data(acc, 'stock', 'account')[0].m_dAvailable。 - 当你下达卖出指令并确认成交后,在代码里手动加上这笔钱:
local_cash += (卖出成交价 * 卖出数量 - 预估手续费)。 - 下买入单时,直接使用你本地维护的
local_cash作为资金上限进行下单。
方案三:降低轮询频率并强制刷新
如果你一定要用轮询的方式,请将 sleep(0.3) 改为 sleep(1) 或 sleep(2)。过高的频率不仅查不到最新资金,还可能触发券商柜台的流控限制(Rate Limit),导致账号被临时限制查询。
总结:放弃 0.3 秒的高频轮询,改用 deal_callback 监听卖出成交事件,或者在本地自行计算卖出释放的资金,是解决此类换仓资金衔接问题的最佳实践。