Exemple #1
0
 def _md_recv(self, pack):
     for d in pack["data"]:
         self._diffs.append(d)
         # 在第一次收到 mdhis_more_data 为 False 的时候,发送账户初始截面信息,这样回测模式下,往后的模块才有正确的时间顺序
         if not self._has_send_init_account and not d.get(
                 "mdhis_more_data", True):
             self._diffs.append(self._sim_trade.init_snapshot())
             self._diffs.append(
                 {"trade": {
                     self._account_key: {
                         "trade_more_data": False
                     }
                 }})
             self._has_send_init_account = True
         _tqsdk_backtest = d.get("_tqsdk_backtest", {})
         if _tqsdk_backtest:
             # 回测时,用 _tqsdk_backtest 对象中 current_dt 作为 TqSim 的 _current_datetime
             self._tqsdk_backtest.update(_tqsdk_backtest)
             self._current_datetime = _timestamp_nano_to_str(
                 self._tqsdk_backtest["current_dt"])
             self._local_time_record = float("nan")
             # 1. 回测时不使用时间差来模拟交易所时间的原因(_local_time_record始终为初始值nan):
             #   在sim收到行情后记录_local_time_record,然后下发行情到api进行merge_diff(),api需要处理完k线和quote才能结束wait_update(),
             #   若处理时间过长,此时下单则在判断下单时间时与测试用例中的预期时间相差较大,导致测试用例无法通过。
             # 2. 回测不使用时间差的方法来判断下单时间仍是可行的: 与使用了时间差的方法相比, 只对在每个交易时间段最后一笔行情时的下单时间判断有差异,
             #   若不使用时间差, 则在最后一笔行情时下单仍判断为在可交易时间段内, 且可成交.
         quotes_diff = d.get("quotes", {})
         # 先根据 quotes_diff 里的 datetime, 确定出 _current_datetime,再 _merge_diff(同时会发送行情到 quote_chan)
         for symbol, quote_diff in quotes_diff.items():
             if quote_diff is None:
                 continue
             # 若直接使用本地时间来判断下单时间是否在可交易时间段内 可能有较大误差,因此判断的方案为:(在接收到下单指令时判断 估计的交易所时间 是否在交易时间段内)
             # 在更新最新行情时间(即self._current_datetime)时,记录当前本地时间(self._local_time_record),
             # 在这之后若收到下单指令,则获取当前本地时间,判 "最新行情时间 + (当前本地时间 - 记录的本地时间)" 是否在交易时间段内。
             # 另外, 若在盘后下单且下单前未订阅此合约:
             # 因为从_md_recv()中获取数据后立即判断下单时间则速度过快(两次time.time()的时间差小于最后一笔行情(14:59:9995)到15点的时间差),
             # 则会立即成交,为处理此情况则将当前时间减去5毫秒(模拟发生5毫秒网络延迟,则两次time.time()的时间差增加了5毫秒)。
             # todo: 按交易所来存储 _current_datetime(issue: #277)
             if quote_diff.get("datetime", "") > self._current_datetime:
                 # 回测时,当前时间更新即可以由 quote 行情更新,也可以由 _tqsdk_backtest.current_dt 更新,
                 # 在最外层的循环里,_tqsdk_backtest.current_dt 是在 rtn_data.data 中数组位置中的最后一个,会在循环最后一个才更新 self.current_datetime
                 # 导致前面处理 order 时的 _current_datetime 还是旧的行情时间
                 self._current_datetime = quote_diff["datetime"]  # 最新行情时间
                 # 更新最新行情时间时的本地时间,回测时不使用时间差
                 self._local_time_record = (
                     time.time() -
                     0.005) if not self._tqsdk_backtest else float("nan")
             if self._current_datetime > self._trading_day_end:  # 结算
                 self._settle()
                 # 若当前行情时间大于交易日的结束时间(切换交易日),则根据此行情时间更新交易日及交易日结束时间
                 trading_day = _get_trading_day_from_timestamp(
                     self._get_current_timestamp())
                 self._trading_day_end = _timestamp_nano_to_str(
                     _get_trading_day_end_time(trading_day) - 999)
         if quotes_diff:
             _merge_diff(self._data, {"quotes": quotes_diff},
                         self._prototype,
                         persist=False,
                         reduce_diff=False,
                         notify_update_diff=True)
Exemple #2
0
 def _get_quotes_from_kline(info, timestamp, kline):
     """
     分为三个包发给下游:
     1. 根据 diff 协议,对于用户收到的最终结果没有影响
     2. TqSim 撮合交易会按顺序处理收到的包,分别比较 high、low、close 三个价格对应的买卖价
     3. TqSim 撮合交易只用到了买卖价,所以最新价只产生一次 close,而不会发送三次
     """
     return [
         {
             "datetime": _timestamp_nano_to_str(timestamp),
             "ask_price1": kline["high"] + info["price_tick"],
             "ask_volume1": 1,
             "bid_price1": kline["high"] - info["price_tick"],
             "bid_volume1": 1,
             "last_price": kline["close"],
             "highest": float("nan"),
             "lowest": float("nan"),
             "average": float("nan"),
             "volume": 0,
             "amount": float("nan"),
             "open_interest": kline["close_oi"],
         },
         {
             "ask_price1": kline["low"] + info["price_tick"],
             "bid_price1": kline["low"] - info["price_tick"],
         },
         {
             "ask_price1": kline["close"] + info["price_tick"],
             "bid_price1": kline["close"] - info["price_tick"],
         }
     ]
Exemple #3
0
 def _get_quotes_from_kline_open(info, timestamp, kline):
     return [
         {  # K线刚生成时的数据都为开盘价
             "datetime": _timestamp_nano_to_str(timestamp),
             "ask_price1": kline["open"] + info["price_tick"],
             "ask_volume1": 1,
             "bid_price1": kline["open"] - info["price_tick"],
             "bid_volume1": 1,
             "last_price": kline["open"],
             "highest": float("nan"),
             "lowest": float("nan"),
             "average": float("nan"),
             "volume": 0,
             "amount": float("nan"),
             "open_interest": kline["open_oi"],
         },
     ]
    async def _target_pos_task(self):
        """负责调整目标持仓的task"""
        all_tasks = []
        try:
            self._quote = await self._api.get_quote(self._symbol)
            async for target_pos in self._pos_chan:
                # lib 中对于时间判断的方案:
                #   如果当前时间(模拟交易所时间)不在交易时间段内,则:等待直到行情更新
                #   行情更新(即下一交易时段开始)后:获取target_pos最新的目标仓位, 开始调整仓位

                # 如果不在可交易时间段内(回测时用 backtest 下发的时间判断,实盘使用 quote 行情判断): 等待更新
                while True:
                    if isinstance(self._api._backtest, TqBacktest):
                        cur_timestamp = self._api._data.get(
                            "_tqsdk_backtest", {}).get("current_dt",
                                                       float("nan"))
                        cur_dt = _timestamp_nano_to_str(cur_timestamp)
                        time_record = float("nan")
                    else:
                        cur_dt = self._quote["datetime"]
                        time_record = self._local_time_record
                    if _is_in_trading_time(self._quote, cur_dt, time_record):
                        break
                    await self._local_time_record_update_chan.recv()

                target_pos = self._pos_chan.recv_latest(
                    target_pos)  # 获取最后一个target_pos目标仓位
                # 确定调仓增减方向
                delta_volume = target_pos - self._pos.pos
                pending_forzen = 0
                for each_priority in self._offset_priority + ",":  # 按不同模式的优先级顺序报出不同的offset单,股指(“昨开”)平昨优先从不平今就先报平昨,原油平今优先("今昨开")就报平今
                    if each_priority == ",":
                        await gather(*[each._task for each in all_tasks])
                        pending_forzen = 0
                        all_tasks = []
                        continue
                    order_offset, order_dir, order_volume = self._get_order(
                        each_priority, delta_volume, pending_forzen)
                    if order_volume == 0:  # 如果没有则直接到下一种offset
                        continue
                    elif order_offset != "OPEN":
                        pending_forzen += order_volume
                    order_task = InsertOrderUntilAllTradedTask(
                        self._api,
                        self._symbol,
                        order_dir,
                        offset=order_offset,
                        volume=order_volume,
                        min_volume=self._min_volume,
                        max_volume=self._max_volume,
                        price=self._price,
                        trade_chan=self._trade_chan,
                        trade_objs_chan=self._trade_objs_chan,
                        account=self._account)
                    all_tasks.append(order_task)
                    delta_volume -= order_volume if order_dir == "BUY" else -order_volume
        finally:
            # 执行 task.cancel() 时, 删除掉该 symbol 对应的 TargetPosTask 实例
            # self._account 类型为 TqSim/TqKq/TqAccount,都包括 _account_key 变量
            TargetPosTaskSingleton._instances.pop(
                self._account._account_key + "#" + self._symbol, None)
            await self._pos_chan.close()
            self._time_update_task.cancel()
            await asyncio.gather(*([t._task for t in all_tasks] +
                                   [self._time_update_task]),
                                 return_exceptions=True)
Exemple #5
0
 def _get_quotes_from_tick(tick):
     quote = {k: v for k, v in tick.items()}
     quote["datetime"] = _timestamp_nano_to_str(tick["datetime"])
     return [quote]