162 views
0

检查回测数据的时候发现以上不正常的交易记录,从管理员 @west@Ringo 那里得知是一个以前就发现的bug有待修复,是因为交易所18点的时候还会传递数据过来,就把这个tick当成了行情,所以交易的时候用这个18点的tick来做了成交。当时发觉哪里不对,策略是按照日k线

if api.is_changing(klines.iloc[-1], "datetime"):

判断后做的交易,再怎么也应该是交易到9点或21点以后的时间的tick上,通过反复试验发现这个问题可能不仅仅是交易所盘后传数据的问题,可能是tqsdk底层代码对日k线处理逻辑存在bug,因为通过日k线调用is_changing datetime就会有这个问题,显示不正常的tick数据时间为:14:59:59或18:00:00,而1分钟,60分钟k线同样是用is_changing datetime做判断当到9点的时候就不会有这个问题并且显示的tick数据时间是正常的 08:59:00.***。这里的原因可能是日k线推进的时候,当tick走到头一天14:59:59的时候就计入到第二天日k线的开盘tick就合成了有问题的日k线,实际上是应该当tick走到08:59:00.***的时候才产生新的一个日k,所以日k线处理开盘bar或is_changing datetime的逻辑按照处理1分钟,60分钟的类似逻辑处理就可以修复了。

并且还发现一个可能也是这个原因导致的问题,就是大周期的日k线用is_changing不能同时和1分钟、60分钟k线用is_changing同时为真,单独1分钟和60分钟是可以的,照理说用is_changing datetime做判断大的粒度应该覆盖小粒度的(小级别k线时间的能在秒单位上被大级别k线整除),应该同一时刻为真的推进。

日k bug复现如下图:

1分钟,60分钟k线正常打印如下图:

以下是图1的代码:

from datetime import date
import sys
import numpy as np
from tqsdk import TqApi, TqAccount, TargetPosTask, BacktestFinished, TqBacktest, TqSim, tafunc
 def test3():
     api = None
    ticks = None
    quote = None
    klines = None
    order = None
    trade = None
    SYMBOL = "DCE.jd2005"
    target_pos = None
    try:
        api = TqApi(backtest=TqBacktest(start_dt=date(2019, 11, 5), end_dt=date(2019, 11, 29)), web_gui="http://127.0.0.1:11111/")
        ticks = api.get_tick_serial(symbol=SYMBOL)
        quote = api.get_quote(symbol=SYMBOL)
        klines = api.get_kline_serial(symbol=SYMBOL, duration_seconds=24 * 3600)
        klines_1min = api.get_kline_serial(symbol=SYMBOL, duration_seconds=60)
        klines_60min = api.get_kline_serial(symbol=SYMBOL, duration_seconds=60*60)
        trade = api.get_trade()
        target_pos = TargetPosTask(api, SYMBOL)
        pass
    except Exception as e:
        print("进入Exception")
        pass
     try:
        while True:
            sys.stdout.flush()
            api.wait_update()
             if api.is_changing(klines.iloc[-1], "datetime"):
            #if api.is_changing(klines_1min.iloc[-1], "datetime"):
            #if api.is_changing(klines_60min.iloc[-1], "datetime") and api.is_changing(klines_1min.iloc[-1], "datetime"):
                print("ticks datetime:%s tick_last_price:%f" % (tafunc.time_to_datetime(ticks.iloc[-1]["datetime"]), ticks.iloc[-1]["last_price"]))
                print("ticks datetime:%s tick_last_price:%f" % (tafunc.time_to_datetime(ticks.iloc[-2]["datetime"]), ticks.iloc[-2]["last_price"]))
                #print(type(ticks.iloc[-2]["last_price"]))
                #print(str(ticks.iloc[-2]["last_price"]))
                #print(ticks.iloc[-2]["last_price"])
                print("ticks datetime:%s tick_last_price:%f" % (tafunc.time_to_datetime(ticks.iloc[-3]["datetime"]), ticks.iloc[-3]["last_price"]))
                print("ticks datetime:%s tick_last_price:%f" % (tafunc.time_to_datetime(ticks.iloc[-4]["datetime"]), ticks.iloc[-4]["last_price"]))
                print("klines_1min datetime:", tafunc.time_to_datetime(klines_1min.iloc[-1]["datetime"]))
                #print("klines_1min datetime:", tafunc.time_to_datetime(klines_1min.iloc[-2]["datetime"]))
                print("klines_60min datetime:", tafunc.time_to_datetime(klines_1min.iloc[-1]["datetime"]))
                #print("klines_60min datetime:", tafunc.time_to_datetime(klines_1min.iloc[-2]["datetime"]))
                print("klines datetime:", tafunc.time_to_datetime(klines.iloc[-1]["datetime"]))
                #print("klines datetime:", tafunc.time_to_datetime(klines.iloc[-2]["datetime"]))
                sys.stdout.flush()
                buy_open_price = ticks.iloc[-1]["last_price"] + 1
                order = api.insert_order(symbol=SYMBOL, direction="BUY", offset="OPEN", volume=1, limit_price=buy_open_price)
                sys.stdout.flush()
                #target_pos.set_target_volume(1)
                #sys.stdout.flush()
     except BacktestFinished as e:
        api.close()
        print("进入BacktestFinished")
        pass
     return 123
 for _ in range(5):
    ret = 0
    try:
        test3()
    except Exception as e:
        print("Exception!!!!!!!!!!")
    print(test3())

以下是图2的代码:

from datetime import date
import sys
import numpy as np
from tqsdk import TqApi, TqAccount, TargetPosTask, BacktestFinished, TqBacktest, TqSim, tafunc
 def test3():
     api = None
    ticks = None
    quote = None
    klines = None
    order = None
    trade = None
    SYMBOL = "DCE.jd2005"
    target_pos = None
    try:
        api = TqApi(backtest=TqBacktest(start_dt=date(2019, 11, 5), end_dt=date(2019, 11, 29)), web_gui="http://127.0.0.1:11111/")
        ticks = api.get_tick_serial(symbol=SYMBOL)
        quote = api.get_quote(symbol=SYMBOL)
        klines = api.get_kline_serial(symbol=SYMBOL, duration_seconds=24 * 3600)
        klines_1min = api.get_kline_serial(symbol=SYMBOL, duration_seconds=60)
        klines_60min = api.get_kline_serial(symbol=SYMBOL, duration_seconds=60*60)
        trade = api.get_trade()
        target_pos = TargetPosTask(api, SYMBOL)
        pass
    except Exception as e:
        print("进入Exception")
        pass
     try:
        while True:
            sys.stdout.flush()
            api.wait_update()
             #if api.is_changing(klines.iloc[-1], "datetime"):
            #if api.is_changing(klines_1min.iloc[-1], "datetime"):
            if api.is_changing(klines_60min.iloc[-1], "datetime") and api.is_changing(klines_1min.iloc[-1], "datetime"):
                print("ticks datetime:%s tick_last_price:%f" % (tafunc.time_to_datetime(ticks.iloc[-1]["datetime"]), ticks.iloc[-1]["last_price"]))
                print("ticks datetime:%s tick_last_price:%f" % (tafunc.time_to_datetime(ticks.iloc[-2]["datetime"]), ticks.iloc[-2]["last_price"]))
                #print(type(ticks.iloc[-2]["last_price"]))
                #print(str(ticks.iloc[-2]["last_price"]))
                #print(ticks.iloc[-2]["last_price"])
                print("ticks datetime:%s tick_last_price:%f" % (tafunc.time_to_datetime(ticks.iloc[-3]["datetime"]), ticks.iloc[-3]["last_price"]))
                print("ticks datetime:%s tick_last_price:%f" % (tafunc.time_to_datetime(ticks.iloc[-4]["datetime"]), ticks.iloc[-4]["last_price"]))
                print("klines_1min datetime:", tafunc.time_to_datetime(klines_1min.iloc[-1]["datetime"]))
                #print("klines_1min datetime:", tafunc.time_to_datetime(klines_1min.iloc[-2]["datetime"]))
                print("klines_60min datetime:", tafunc.time_to_datetime(klines_60min.iloc[-1]["datetime"]))
                #print("klines_60min datetime:", tafunc.time_to_datetime(klines_1min.iloc[-2]["datetime"]))
                print("klines datetime:", tafunc.time_to_datetime(klines.iloc[-1]["datetime"]))
                #print("klines datetime:", tafunc.time_to_datetime(klines.iloc[-2]["datetime"]))
                sys.stdout.flush()
                buy_open_price = ticks.iloc[-1]["last_price"] + 1
                order = api.insert_order(symbol=SYMBOL, direction="BUY", offset="OPEN", volume=1, limit_price=buy_open_price)
                sys.stdout.flush()
                #target_pos.set_target_volume(1)
                #sys.stdout.flush()
     except BacktestFinished as e:
        api.close()
        print("进入BacktestFinished")
        pass
     return 123
 for _ in range(5):
    ret = 0
    try:
        test3()
    except Exception as e:
        print("Exception!!!!!!!!!!")
    print(test3())

Answered question
1

这个函数的实现就很迷…..因为打印所有周期的datetime你可以发现他们是严格按时间顺序推送的,但是大粒度和小粒度的is_changing()就是不能同时判断。所以回测的时候我是人工保存多周期k的datetime自己判断是否更新。

Posted new comment

你看下我的例子py代码,1分钟和60分钟k线时可以同时用is_changing判断到时间变化的,这也是符合逻辑的,应该就是日k线的bug。其他周期的也可以测试下。大力度周期要能被小周期的整除才行。

0

在18点左右,交易所发来下一个交易日的一些信息,比如涨跌停价格,我们服务器以收到次的新数据作为新交易日的起始标志,因此在此时生成新的日线。这个涨跌停价格等信息是有用的,在比如集合竞价阶段作为下单价格的参考。

因为现在TqSim模拟账号允许在任何时间下单都能成交。当你不加行情判断条件而直接在循环里使用insert_order()下单时,在18点那个行情时下单则也能触发成交了。

之后会增加TqSim模拟交易成交时间判断,就不允许在非交易时间段成交了。或者你也可以在功能上线前增加对行情时间的判断代码,判断不在交易时间段内,就不要下单即可做一个简单的过滤。

Edited answer
0

每个K线是不同的实例,并不是保存在同一个变量中,自然用一个is_changing()只能判断一个K线的更新,如果要判断多个K线,多加几个is_changing()判断或使用for循环即可。

Answered question