248 浏览
0

描述:我的回测程序下的目标价是632.7,然后调用了targetPosTask去建仓,选择的passive模式。由于不知道targetPosTask运行的内在机理,所以在调用targetPosTask之后,我增加了然后调循环api.wait_update去触发下单以及等待position.pos_long == tgtPos and all_orders_filled,来确保单子完全成交后再进行后续的操作。具体打印出来的信息如下:模拟交易第一次下单是目标价减去1个价位0.02,然后撤单了;第二次是按照632.8报单,比我目标价位已经高了0.1,即5个价位;第三次下单目标价是633.02,比我目标价位高了0.32,即16个价位。

下单中:建多仓3.0(手),仓位1为3.0,仓位2为14.0,目标开仓价632.7,止损价629.8,止损空间2.900000000000091,
INFO – 模拟交易下单 TQSIM, PYSDK_target_7d384f293e96bf3b3d003f87f97c328e: 时间: 2024-11-01 10:14:59.999999, 合约: SHFE.au2504, 开平: OPEN, 方向: BUY, 手数: 3, 价格: 632.6800000000001
INFO – 模拟交易委托单 TQSIM, PYSDK_target_7d384f293e96bf3b3d003f87f97c328e: 已撤单
INFO – 模拟交易下单 TQSIM, PYSDK_target_72c724c89559e4d0d6383ec0817e7d65: 时间: 2024-11-01 10:30:00.000000, 合约: SHFE.au2504, 开平: OPEN, 方向: BUY, 手数: 3, 价格: 632.8000000000001
INFO – 模拟交易委托单 TQSIM, PYSDK_target_72c724c89559e4d0d6383ec0817e7d65: 已撤单
INFO – 模拟交易下单 TQSIM, PYSDK_target_ebbd79d9b9def8d540d8063aace58a2e: 时间: 2024-11-01 10:30:59.999999, 合约: SHFE.au2504, 开平: OPEN, 方向: BUY, 手数: 3, 价格: 633.02
INFO – 模拟交易委托单 TQSIM, PYSDK_target_ebbd79d9b9def8d540d8063aace58a2e: 全部成交
成交检查成功!

问1:是否有必要检查position.pos_long == tgtPos and all_orders_filled,也许targetPosTask内在机理就是成交后才运行后续脚本?

问2:targetPosTask在回测过程中下单的价格是如何确定的,比如下按照目标价位下方1个价位,然后按照上方5个价位,然后15个价位?

问3:回测过程中的撮合逻辑是什么,是按照quote价格去撮合,只要价格符合就认为成交,还是说也会考虑盘后的挂单数量?另外,我的程序里订阅最小的是1mK线,按照https://doc.shinnytech.com/tqsdk/latest/reference/tqsdk.backtest.html的说法,quote的更新频率就是按照1mK线来的,这个是不是我的价位

问4:实盘过程中,针对targetPosTask的撮合逻辑和回测结果区别大吗?必竟回测模拟的撮合过程和真实的结果还是有点差距的。

123qxr 已回答的问题 2025年2月1日
0

直接看tqsdk的模拟交易部分的源代码,它回测的时候使用目标持仓函数的逻辑很简单,默认模式下,如果是平仓多头,直接使用买一价下单瞬间成交。如果是PASSIVE模式,由于是使用卖一价下单,如果你订阅的是分钟级别数据,那么是在下一次数据到来的时候才进行撮合,这往往容易导致价格的巨大跳跃。如果是手动限价单,以买入多头为例,只要报价大于等于卖一价,你报价100000000就直接以100000000成交,和实盘的价格撮合逻辑有巨大差异。
为什么要设置这样的交易逻辑是因为回测时默认缺失了tick级的数据:我修改过源代码,无论我报价多少都能成交,实现无条件强制成交。但是这样存在的严重逻辑问题是我不知道这根k线的中间数据,我不能精确的确定实盘中的撮合价格应该是多少,往往会导致开仓成本和平仓成本过高。
所以,直接使用tick级别数据回测是最接近实盘的。但是目前发现tick级回测太慢,速度是分钟级的几十分之一,即使什么函数都不写,跑一个月也要好几十分钟。tqsdk2我也试过,tick级也就比tqsdk快2-3倍,而且官方不维护了,有bug。可能需要更换别的量化回测框架,使用本地数据回测才能解决速度问题了
以下是撮合成交的源代码:

def match_order(order, quote) -> (str, str, float):
    """
    撮合交易规则:
    * 市价单使用对手盘价格成交, 如果没有对手盘(涨跌停)则自动撤单
    * 限价单要求报单价格达到或超过对手盘价格才能成交, 成交价为报单价格, 如果没有对手盘(涨跌停)则无法成交
    * 模拟交易只有全部成交
    returns: status, last_msg, price
    """
    status, last_msg = "ALIVE", ""
    ask_price, bid_price = _get_price_range(quote)
    # order 预期成交价格
    if order["price_type"] in ["ANY", "BEST", "FIVELEVEL"]:
        price = ask_price if order["direction"] == "BUY" else bid_price
    else:
        price = order["limit_price"]
    if order["price_type"] == "ANY" and math.isnan(price):
        status, last_msg = "FINISHED", "市价指令剩余撤销"
    if order.get("time_condition") == "IOC":  # IOC 立即成交,限价下单且不能成交的价格,直接撤单
        if order["direction"] == "BUY" and price < ask_price or order["direction"] == "SELL" and price > bid_price:
            status, last_msg = "FINISHED", "已撤单报单已提交"
    if order["direction"] == "BUY" and price >= ask_price or order["direction"] == "SELL" and price <= bid_price:
        status, last_msg = "FINISHED", "全部成交"
    return status, last_msg, price
1138987769 发表新评论 2025年2月2日

谢谢。关于回测的撮合逻辑我大概明白了。听下来,感觉回测过程最好就不要太纠结由于撮合逻辑导致的成交成本了,我们可以自己拿着回测成交记录再手动调整下,要不然磨刀的时间就太长了,没时间砍柴了。另外,想问下问1,回测或者实盘代码中需要在加一段代码来保证成交后再执行后续动作吗,还是说目前的程序机制就已经是这样的。