检查回测数据的时候发现以上不正常的交易记录,从管理员 @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())
每个K线是不同的实例,并不是保存在同一个变量中,自然用一个is_changing()只能判断一个K线的更新,如果要判断多个K线,多加几个is_changing()判断或使用for循环即可。