diff --git a/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py b/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py index 48ae65334..2ae659dd5 100644 --- a/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py +++ b/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py @@ -119,7 +119,7 @@ def submit_order(self, order): order.active() self._env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_PASS, account=account, order=order)) if self._match_immediately: - self._match() + self._match(self._env.calendar_dt) def cancel_order(self, order): account = self._env.get_account(order.order_book_id) @@ -158,16 +158,19 @@ def pre_settlement(self, __): def on_bar(self, event): for matcher in self._matchers.values(): matcher.update(event) - self._match() + self._match(event.calendar_dt) def on_tick(self, event): tick = event.tick self._get_matcher(tick.order_book_id).update(event) - self._match(tick.order_book_id) - - def _match(self, order_book_id=None): - order_filter = lambda a_and_o: not (a_and_o[1].is_final() or (order_book_id and a_and_o[1].order_book_id != order_book_id)) - for account, order in filter(order_filter, self._open_orders): + self._match(event.calendar_dt, tick.order_book_id) + + def _match(self, dt, order_book_id=None): + # 撮合未完成的订单,若指定标的时只撮合指定的标的的订单 + order_filter = lambda a_and_o: (not a_and_o[1].is_final()) and (True if order_book_id is None else a_and_o[1].order_book_id == order_book_id) + # + 需要在交易时段内 + open_order_filter = lambda a_and_o: order_filter(a_and_o) and self._env.data_proxy.instrument(a_and_o[1].order_book_id).during_continuous_auction(dt.time()) + for account, order in filter(open_order_filter, self._open_orders): self._get_matcher(order.order_book_id).match(account, order, open_auction=False) for account, order in filter(order_filter, self._open_auction_orders): self._get_matcher(order.order_book_id).match(account, order, open_auction=True) diff --git a/rqalpha/model/instrument.py b/rqalpha/model/instrument.py index 2302d7133..eb08e43e2 100644 --- a/rqalpha/model/instrument.py +++ b/rqalpha/model/instrument.py @@ -375,6 +375,14 @@ def trading_hours(self): trading_period.append(TimeRange(start, end)) return trading_period + def during_continuous_auction(self, time): + # type: (datetime.time) -> bool + """ 是否处于连续竞价时间段内 """ + for time_range in self.trading_hours: + if time_range.start <= time <= time_range.end: + return True + return False + @property def trading_code(self): # type: () -> str diff --git a/tests/api_tests/mod/sys_simulation/test_match.py b/tests/api_tests/mod/sys_simulation/test_match.py new file mode 100644 index 000000000..631a61126 --- /dev/null +++ b/tests/api_tests/mod/sys_simulation/test_match.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# 版权所有 2019 深圳米筐科技有限公司(下称“米筐科技”) +# +# 除非遵守当前许可,否则不得使用本软件。 +# +# * 非商业用途(非商业用途指个人出于非商业目的使用本软件,或者高校、研究所等非营利机构出于教育、科研等目的使用本软件): +# 遵守 Apache License 2.0(下称“Apache 2.0 许可”), +# 您可以在以下位置获得 Apache 2.0 许可的副本:http://www.apache.org/licenses/LICENSE-2.0。 +# 除非法律有要求或以书面形式达成协议,否则本软件分发时需保持当前许可“原样”不变,且不得附加任何条件。 +# +# * 商业用途(商业用途指个人出于任何商业目的使用本软件,或者法人或其他组织出于任何目的使用本软件): +# 未经米筐科技授权,任何个人不得出于任何商业目的使用本软件(包括但不限于向第三方提供、销售、出租、出借、转让本软件、 +# 本软件的衍生产品、引用或借鉴了本软件功能或源代码的产品或服务),任何法人或其他组织不得出于任何目的使用本软件, +# 否则米筐科技有权追究相应的知识产权侵权责任。 +# 在此前提下,对本软件的使用同样需要遵守 Apache 2.0 许可,Apache 2.0 许可与本许可冲突之处,以本许可为准。 +# 详细的授权流程,请联系 public@ricequant.com 获取。 +import os + +from rqalpha.const import ORDER_STATUS + +__config__ = { + "base": { + "frequency": "1d", + "accounts": { + "future": 1000000, + } + } +} + + +def test_match(): + + try: + import rqalpha_mod_ricequant_data + except Exception: + print("github上无数据,跳过测试,仅在本地测试") + return {} + + __config__ = { + "base": { + "data_bundle_path": os.path.expanduser("~/.rqalpha-plus/bundle"), + "start_date": "2023-07-07", + "end_date": "2023-07-10", + "frequency": "1m", + "accounts": { + "future": 1000000, + } + }, + "mod": { + "ricequant_data": { + "enabled": True + } + } + } + + def init(context): + context.order = None + subscribe("ZN2309") + subscribe("RR2309") + + def handle_bar(context, bar_dict): + dt_str = context.now.strftime("%Y%m%d%H%M%S") + # 当天夜盘 + if dt_str == "20230706210100": + context.order = buy_open("RR2309", 1, price_or_style=3500) + elif dt_str == "20230706230200": + assert context.order.status == ORDER_STATUS.ACTIVE, "盘中休息也保持active" + assert "订单被拒单: [RR2309] 当前缺失市场数据。" != context.order._message, "不能在非交易时段尝试撮合" + # 第二天早盘 + elif dt_str == "20230707091000": + assert context.order.status == ORDER_STATUS.ACTIVE, "夜盘下的order在未成交、撤单等情况下应保持active状态" + # 第二天夜盘 + elif dt_str == "20230707210100": + assert context.order.status == ORDER_STATUS.REJECTED, "上个交易日的订单在当天收盘后未被拒单" + + return locals()