Repository: ricequant/rqalpha (language: python)

Issue:
更新文档



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 886e33e2e..b13e2df37 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -2,6 +2,47 @@
 CHANGELOG
 ==================
 
+4.10.0
+==================
+- 将DefaultMatcher划分DefaultBarMatcher和DefaultTickMatcher
+- Tick回测支持成交量限制，成交量限制为两个tick的成交量之差乘以volume_percent
+- Tick回测handle_bar支持盘前的tick
+- Tick回测不再支持open_auction接口，集合竞价时段内成交一律使用last
+- 修复get_open_auction_bar获取非交易日时的异常
+
+4.9.2
+==================
+- 修改get_pit_financials_ex接口中count参数的含义为当前标的已发布财报的数量
+
+4.9.1
+==================
+- 修复get_pit_financials_ex接口的bug
+
+4.9.0
+==================
+- 定时器新增期货应用场景
+- 改善tick回测性能
+
+4.8.1
+==================
+- 改善回测输出的summary和净值图，新增超额累计收益指标
+
+4.8.0
+==================
+- Position类移除了 _today_quantity 属性，新增 _quantity 属性
+- 部分接口新增了缓存
+- Environment类新增submit_order接口
+- Interface中AbstractPosition移除了margin接口
+- order_target_portfolio接口支持 limit order
+- rqalpha_mod_sys_analyser组件报表新增excel格式
+- 提升框架的整体性能
+
+4.7.1
+==================
+- 补全类型提示
+- 优化翻译和错误提示
+- 全面支持Python3.10
+
 4.7.0
 ==================
 - 重构绘制策略收益图的逻辑。增加绘制买卖点的选项，周度收益曲线和指标改为通过配置项激活
diff --git a/docs/source/api/base_api.rst b/docs/source/api/base_api.rst
index dbad5a595..d80562503 100644
--- a/docs/source/api/base_api.rst
+++ b/docs/source/api/base_api.rst
@@ -58,7 +58,7 @@ handle_tick - 快照数据更新
 ..  py:function:: handle_tick(context, tick)
 
     在 tick 级别的策略中，已订阅快照数据的更新会自动触发该方法的调用。策略具体逻辑可在该方法内实现，包括交易信号的产生、订单的创建等。
-    若订阅了多个合约，不同合约快照数据的更新会分别触发该方法。
+    若订阅了多个合约，不同合约快照数据的更新会分别触发该方法。(触发时间包括集合竞价和连续交易时段)。
 
     :param context: 策略上下文
     :type context: :class:`~rqalpha.core.strategy_context.StrategyContext` object
@@ -83,6 +83,7 @@ open_auction - 集合竞价
 ..  py:function:: open_auction(context, bar_dict)
 
     盘前集合竞价发生时会触发该函数的调用，在该函数内发出的订单会以当日开盘价被撮合。
+    tick级别回测频率不触发集合竞价事件。
 
     :param context: 策略上下文
     :type context: :class:`~rqalpha.core.strategy_context.StrategyContext` object
diff --git a/messages.pot b/messages.pot
index b201240b5..450c1ec12 100644
--- a/messages.pot
+++ b/messages.pot
@@ -1,29 +1,29 @@
 # Translations template for PROJECT.
-# Copyright (C) 2021 ORGANIZATION
+# Copyright (C) 2022 ORGANIZATION
 # This file is distributed under the same license as the PROJECT project.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
 #
 #, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: PROJECT VERSION\n"
 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2021-12-16 16:44+0800\n"
+"POT-Creation-Date: 2022-06-24 10:33+0800\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.8.0\n"
+"Generated-By: Babel 2.10.1\n"
 
-#: rqalpha/environment.py:71
+#: rqalpha/environment.py:70
 msgid ""
 "Environment has not been created. Please Use `Environment.get_instance()`"
 " after RQAlpha init"
 msgstr ""
 
-#: rqalpha/environment.py:162
+#: rqalpha/environment.py:163
 msgid "No such transaction cost decider, order_book_id = {}"
 msgstr ""
 
@@ -47,11 +47,11 @@ msgstr ""
 msgid "system restored"
 msgstr ""
 
-#: rqalpha/main.py:246
+#: rqalpha/main.py:243
 msgid "strategy run successfully, normal exit"
 msgstr ""
 
-#: rqalpha/main.py:251
+#: rqalpha/main.py:248
 msgid "strategy execute exception"
 msgstr ""
 
@@ -76,9 +76,9 @@ msgstr ""
 
 #: rqalpha/apis/api_base.py:158
 #: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_future.py:66
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:88
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:135
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:310
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:89
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:128
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:308
 msgid "Order Creation Failed: [{order_book_id}] No market data"
 msgstr ""
 
@@ -252,7 +252,7 @@ msgstr ""
 msgid "mod tear_down [END]   {}"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_accounts/position_model.py:294
+#: rqalpha/mod/rqalpha_mod_sys_accounts/position_model.py:296
 msgid "{order_book_id} is expired, close all positions by system"
 msgstr ""
 
@@ -271,13 +271,13 @@ msgid ""
 msgstr ""
 
 #: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_future.py:49
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:101
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:156
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:96
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:144
 msgid "Order Creation Failed: 0 order quantity, order_book_id={order_book_id}"
 msgstr ""
 
 #: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_future.py:54
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:84
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:85
 msgid "Limit order price should be positive"
 msgstr ""
 
@@ -305,19 +305,39 @@ msgid ""
 "again!"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:111
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:106
 msgid "insufficient cash, use all remaining cash({}) to create order"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:295
-msgid "total percent should be lower than 1, current: {}"
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:296
+msgid ""
+"function order_target_portfolio: invalid keys of target_portfolio, "
+"expected order_book_ids or Instrument objects, got {} (type: {})"
+msgstr ""
+
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:301
+msgid ""
+"function order_target_portfolio: invalid instrument type, excepted "
+"CS/ETF/LOF/INDX, got {}"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:304
-msgid "target percent of {} should between 0 and 1, current: {}"
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:318
+msgid ""
+"function order_target_portfolio: invalid order price {target_price} of "
+"{id_or_ins}"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:647
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:324
+msgid ""
+"function order_target_portfolio: invalid values of target_portfolio, "
+"excepted float between 0 and 1, got {} (key: {})"
+msgstr ""
+
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:332
+msgid "total percent should be lower than 1, current: {}"
+msgstr ""
+
+#: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:655
 msgid "in get_dividend, start_date {} is later than the previous test day {}"
 msgstr ""
 
@@ -369,17 +389,17 @@ msgstr ""
 msgid "[sys_analyser] Generate report from strategy output file"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:100
+#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:108
 msgid ""
 "config 'base.benchmark' is deprecated, use 'mod.sys_analyser.benchmark' "
 "instead"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:182
+#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:190
 msgid "invalid init benchmark {}, should be in format 'order_book_id:weight'"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:187
+#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:195
 msgid "invalid weight for instrument {order_book_id}: {weight}"
 msgstr ""
 
@@ -501,30 +521,30 @@ msgid "WeeklyMaxDrawdown"
 msgstr ""
 
 #: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:99
-msgid "ExcessReturns"
+msgid "ExcessCumReturns"
 msgstr ""
 
 #: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:100
-msgid "ExcessAnnual"
+msgid "ExcessReturns"
 msgstr ""
 
 #: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:101
-msgid "ExcessSharpe"
+msgid "ExcessAnnual"
 msgstr ""
 
 #: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:102
-msgid "ExcessVolatility"
+msgid "ExcessSharpe"
 msgstr ""
 
 #: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:103
-msgid "ExcessAnnualVolatility"
+msgid "ExcessVolatility"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:104
+#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:105
 msgid "ExcessMaxDD"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:105
+#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:106
 msgid "ExcessMaxDD/ExcessMaxDDD"
 msgstr ""
 
@@ -535,14 +555,10 @@ msgid ""
 msgstr ""
 
 #: rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py:31
-msgid "Order Creation Failed: {order_book_id} is not listed!"
+msgid "Order Creation Failed: {order_book_id} is not listing!"
 msgstr ""
 
 #: rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py:37
-msgid "Order Creation Failed: {order_book_id} has been delisted!"
-msgstr ""
-
-#: rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py:43
 msgid "Order Creation Failed: security {order_book_id} is suspended on {date}"
 msgstr ""
 
@@ -564,51 +580,74 @@ msgid ""
 "trade: [{}...]"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:116
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:101
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:319
 #: rqalpha/mod/rqalpha_mod_sys_simulation/signal_broker.py:66
 msgid ""
 "Order Cancelled: current security [{order_book_id}] can not be traded in "
 "listed date [{listed_date}]"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:123
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:108
 #: rqalpha/mod/rqalpha_mod_sys_simulation/signal_broker.py:72
 msgid "Order Cancelled: current bar [{order_book_id}] miss market data."
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:148
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:128
 #: rqalpha/mod/rqalpha_mod_sys_simulation/signal_broker.py:86
 msgid "Order Cancelled: current bar [{order_book_id}] reach the limit_up price."
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:154
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:134
 #: rqalpha/mod/rqalpha_mod_sys_simulation/signal_broker.py:95
 msgid "Order Cancelled: current bar [{order_book_id}] reach the limit_down price."
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:161
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:167
-msgid "Order Cancelled: [{order_book_id}] has no liquidity."
-msgstr ""
-
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:176
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:143
 msgid "Order Cancelled: {order_book_id} bar no volume"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:192
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:159
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:396
 msgid ""
 "Order Cancelled: market order {order_book_id} volume {order_volume} due "
 "to volume limit"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:227
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:197
 msgid ""
 "Order Cancelled: market order {order_book_id} volume {order_volume} is "
 "larger than {volume_percent_limit} percent of current bar volume, fill "
 "{filled_volume} actually"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:321
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:326
+msgid "Order Cancelled: current tick [{order_book_id}] miss market data."
+msgstr ""
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:351
+msgid "Order Cancelled: current tick [{order_book_id}] reach the limit_up price."
+msgstr ""
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:357
+msgid ""
+"Order Cancelled: current tick [{order_book_id}] reach the limit_down "
+"price."
+msgstr ""
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:364
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:370
+msgid "Order Cancelled: [{order_book_id}] has no liquidity."
+msgstr ""
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:438
+msgid ""
+"Order Cancelled: market order {order_book_id} volume {order_volume} is "
+"larger than {volume_percent_limit} percent of current tick volume, fill "
+"{filled_volume} actually"
+msgstr ""
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:571
 msgid ""
 "Order Cancelled: market order {order_book_id} fill {filled_volume} "
 "actually"
@@ -618,18 +657,18 @@ msgstr ""
 msgid "invalid margin multiplier value: value range is (0, +∞]"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:52
-#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:59
+#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:51
+#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:58
 msgid "Not supported matching type {}"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:63
+#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:62
 msgid ""
 "matching_type = 'next_bar' is abandoned when frequency == '1d',Current "
 "matching_type is 'current_bar'."
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:108
+#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:107
 msgid "NO account_type = ({}) in {}"
 msgstr ""
 
@@ -637,19 +676,19 @@ msgstr ""
 msgid "cancel_order function is not supported in signal mode"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:104
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:108
 msgid "unsupported position_effect {}"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:125
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:129
 msgid "{order_id} order has been cancelled by user."
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:141
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:145
 msgid "Order Rejected: {order_book_id} can not match. Market close."
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:178
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:184
 msgid "{order_book_id} should be subscribed when frequency is tick."
 msgstr ""
 
@@ -657,7 +696,7 @@ msgstr ""
 msgid "Current universe is empty. Please use subscribe function before trade"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_event_source.py:196
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_event_source.py:202
 msgid "Frequency {} is not support."
 msgstr ""
 
@@ -706,15 +745,15 @@ msgstr ""
 msgid "Cash add {}. units {} become to {}"
 msgstr ""
 
-#: rqalpha/portfolio/account.py:308
+#: rqalpha/portfolio/account.py:313
 msgid "Trigger Forced Liquidation, current total_value is 0"
 msgstr ""
 
-#: rqalpha/portfolio/account.py:434
+#: rqalpha/portfolio/account.py:457
 msgid "insufficient cash, current {}, target withdrawal {}"
 msgstr ""
 
-#: rqalpha/portfolio/position.py:165
+#: rqalpha/portfolio/position.py:160
 msgid "invalid price of {order_book_id}: {price}"
 msgstr ""
 
diff --git a/rqalpha/apis/api_abstract.py b/rqalpha/apis/api_abstract.py
index b1377ed04..154507ae9 100644
--- a/rqalpha/apis/api_abstract.py
+++ b/rqalpha/apis/api_abstract.py
@@ -79,7 +79,8 @@ def order_shares(id_or_ins, amount, price=None, style=None):
 def order_value(id_or_ins, cash_amount, price=None, style=None):
     # type: (Union[str, Instrument], float, Optional[float], Optional[OrderStyle]) -> Optional[Order]
     """
-    使用想要花费的金钱买入/卖出股票，而不是买入/卖出想要的股数，正数代表买入，负数代表卖出。股票的股数总是会被调整成对应的100的倍数（在A中国A股市场1手是100股）。如果资金不足，该API将不会创建发送订单。
+    使用想要花费的金钱买入/卖出股票，而不是买入/卖出想要的股数，正数代表买入，负数代表卖出。股票的股数总是会被调整成对应的100的倍数（在A中国A股市场1手是100股）。
+    如果资金不足，该API将会使用最大可用资金发单。
 
     需要注意：
     当您提交一个买单时，cash_amount 代表的含义是您希望买入股票消耗的金额（包含税费），最终买入的股数不仅和发单的价格有关，还和税费相关的参数设置有关。
@@ -118,7 +119,7 @@ def order_value(id_or_ins, cash_amount, price=None, style=None):
 def order_percent(id_or_ins, percent, price=None, style=None):
     # type: (Union[str, Instrument], float, Optional[float], Optional[OrderStyle]) -> Optional[Order]
     """
-    发送一个花费价值等于目前投资组合（市场价值和目前现金的总和）一定百分比现金的买/卖单，正数代表买，负数代表卖。股票的股数总是会被调整成对应的一手的股票数的倍数（1手是100股）。百分比是一个小数，并且小于或等于1（<=100%），0.5表示的是50%.需要注意，如果资金不足，该API将不会创建发送订单。
+    发送一个花费价值等于目前投资组合（市场价值和目前现金的总和）一定百分比现金的买/卖单，正数代表买，负数代表卖。股票的股数总是会被调整成对应的一手的股票数的倍数（1手是100股）。百分比是一个小数，并且小于或等于1（<=100%），0.5表示的是50%.需要注意，如果资金不足，该API将会使用最大可用资金发单。
 
     需要注意：
 
@@ -160,7 +161,7 @@ def order_target_value(id_or_ins, cash_amount, price=None, style=None):
     加仓时，cash_amount 代表现有持仓的价值加上即将花费（包含税费）的现金的总价值。
     减仓时，cash_amount 代表调整仓位的目标价至。
 
-    需要注意，如果资金不足，该API将不会创建发送订单。
+    需要注意，如果资金不足，该API将会使用最大可用资金发单。
 
     :param id_or_ins: 下单标的物
     :param cash_amount: 最终的该证券的仓位目标价值。
diff --git a/rqalpha/apis/api_rqdatac.py b/rqalpha/apis/api_rqdatac.py
index b96d42331..ae944cd79 100644
--- a/rqalpha/apis/api_rqdatac.py
+++ b/rqalpha/apis/api_rqdatac.py
@@ -1097,25 +1097,70 @@ def get_pit_financials_ex(order_book_ids, fields, count, statements='latest'):
     if isinstance(order_book_ids, str):
         order_book_ids = [order_book_ids]
     env = Environment.get_instance()
-    dt = env.calendar_dt.date()
-    year = dt.year
-    mon = dt.month
-    q = (mon - 4) // 3 + 1
-    y = year
-    if q <= 0:
-        y -= 1
-        q = 4
-    end_quarter = str(y) + 'q' + str(q)
 
-    delta_year = count // 4
-    start_q = q - count % 4 + 1
-    if start_q <= 0:
-        start_q = 4
-        delta_year += 1
-    start_quarter = str(y - delta_year) + 'q' + str(start_q)
+    if count < 0:
+        user_log.warn("function get_pit_financials_ex : count must >= 0")
+        return None
+
+    # 退市和未退市池
+    de_listed_list, active_list = [], []
+
+    # 遍历每个标的划分池子
+    for order_book_id in order_book_ids:
+        instrument = env.get_instrument(order_book_id)
+        if env.calendar_dt > instrument.de_listed_date:
+            de_listed_list.append(instrument)
+        else:
+            active_list.append(order_book_id)
+
+    # 需要的数量，传入的 0 代表 1个
+    count += 1
+
+    def _get_data(symbol_list, start_dt):
+        dt = start_dt.date()
+        year = dt.year
+        mon = dt.month
+        q = (mon - 4) // 3 + 1
+        y = year
+        if q <= 0:
+            y -= 1
+            q = 4
+        end_quarter = str(y) + 'q' + str(q)
+
+        # 多获取4个季度的财报，以防因为财报未发布导致数量不够
+        q_num = y * 4 + q - count - 4
+
+        start_y = q_num // 4
+        start_q = q_num % 4 + 1
+        start_quarter = "{}q{}".format(start_y, start_q)
+
+        if start_quarter > end_quarter:
+            start_quarter = end_quarter
+
+        result = rqdatac.get_pit_financials_ex(
+            fields=fields, start_quarter=start_quarter, end_quarter=end_quarter, order_book_ids=symbol_list,
+            statements=statements, market='cn', date=env.calendar_dt.date()
+        )
+
+        return result
+
+    # 保存结果
+    result_list = []
+
+    # 获取未退市标的的数据
+    result = _get_data(active_list, env.calendar_dt)
+
+    if isinstance(result, pd.DataFrame):
+        result_list = [group_df.iloc[-count:] for _, group_df in result.groupby("order_book_id")]
+
+    # 获取退市的数据
+    for instrument in de_listed_list:
+        result = _get_data([instrument.order_book_id], instrument.de_listed_date)
+        if isinstance(result, pd.DataFrame):
+            result_list.append(result.iloc[-count:])
+
+    result = pd.concat(result_list) if len(result_list) > 0 else None
 
-    result = rqdatac.get_pit_financials_ex(fields=fields, start_quarter=start_quarter, end_quarter=end_quarter,
-        order_book_ids=order_book_ids, statements=statements, market='cn', date=dt)
     return result
 
 
diff --git a/rqalpha/data/base_data_source/data_source.py b/rqalpha/data/base_data_source/data_source.py
index 04af544c8..ba0e9fc98 100644
--- a/rqalpha/data/base_data_source/data_source.py
+++ b/rqalpha/data/base_data_source/data_source.py
@@ -91,10 +91,12 @@ def _p(name):
 
         self._instruments_stores = {}  # type: Dict[INSTRUMENT_TYPE, AbstractInstrumentStore]
         self._ins_id_or_sym_type_map = {}  # type: Dict[str, INSTRUMENT_TYPE]
+        instruments = []
         with open(_p('instruments.pk'), 'rb') as f:
-            instruments = [Instrument(
-                i, lambda i: self._future_info_store.get_future_info(i)["tick_size"]
-            ) for i in pickle.load(f)]
+            for i in pickle.load(f):
+                if i["type"] == "Future" and Instrument.is_future_continuous_contract(i["order_book_id"]):
+                    i["listed_date"] = datetime(1990, 1, 1)
+                instruments.append(Instrument(i, lambda i: self._future_info_store.get_future_info(i)["tick_size"]))
         for ins_type in self.DEFAULT_INS_TYPES:
             self.register_instruments_store(InstrumentStore(instruments, ins_type))
 
@@ -221,7 +223,10 @@ def get_bar(self, instrument, dt, frequency):
     def get_open_auction_bar(self, instrument, dt):
         # type: (Instrument, Union[datetime, date]) -> Dict
         day_bar = self.get_bar(instrument, dt, "1d")
-        bar = {k: day_bar[k] if k in day_bar.dtype.names else np.nan for k in self.OPEN_AUCTION_BAR_FIELDS}
+        if day_bar is None:
+            bar = dict.fromkeys(self.OPEN_AUCTION_BAR_FIELDS, np.nan)
+        else:
+            bar = {k: day_bar[k] if k in day_bar.dtype.names else np.nan for k in self.OPEN_AUCTION_BAR_FIELDS}
         bar["last"] = bar["open"]
         return bar
 
diff --git a/rqalpha/data/data_proxy.py b/rqalpha/data/data_proxy.py
index 6d26ccb5b..9b54c97c9 100644
--- a/rqalpha/data/data_proxy.py
+++ b/rqalpha/data/data_proxy.py
@@ -15,7 +15,6 @@
 #         在此前提下，对本软件的使用同样需要遵守 Apache 2.0 许可，Apache 2.0 许可与本许可冲突之处，以本许可为准。
 #         详细的授权流程，请联系 public@ricequant.com 获取。
 
-import re
 from datetime import datetime, date
 from typing import Union, List, Sequence, Optional
 
@@ -282,13 +281,11 @@ def instruments(self, sym_or_ids):
         else:
             return list(self._data_source.get_instruments(id_or_syms=sym_or_ids))
 
-    FUTURE_CONTINUOUS_CONTRACT = re.compile("^[A-Z]{1,2}(88|888|99|889)$")
-
     def get_future_contracts(self, underlying, date):
         # type: (str, DateLike) -> List[str]
         return sorted(i.order_book_id for i in self.all_instruments(
             [INSTRUMENT_TYPE.FUTURE], date
-        ) if i.underlying_symbol == underlying and not re.match(self.FUTURE_CONTINUOUS_CONTRACT, i.order_book_id))
+        ) if i.underlying_symbol == underlying and not Instrument.is_future_continuous_contract(i.order_book_id))
 
     def get_trading_period(self, sym_or_ids, default_trading_period=None):
         # type: (StrOrIter, Optional[Sequence[TimeRange]]) -> List[TimeRange]
diff --git a/rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py b/rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py
index 0bb09824c..c5351cbf4 100644
--- a/rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py
+++ b/rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py
@@ -27,14 +27,8 @@ def __init__(self, env):
 
     def can_submit_order(self, order, account=None):
         instrument = self._env.data_proxy.instrument(order.order_book_id)
-        if instrument.listed_date > self._env.trading_dt:
-            user_system_log.warn(_(u"Order Creation Failed: {order_book_id} is not listed!").format(
-                order_book_id=order.order_book_id,
-            ))
-            return False
-
-        if instrument.de_listed_date.date() < self._env.trading_dt.date():
-            user_system_log.warn(_(u"Order Creation Failed: {order_book_id} has been delisted!").format(
+        if not instrument.listing_at(self._env.trading_dt):
+            user_system_log.warn(_(u"Order Creation Failed: {order_book_id} is not listing!").format(
                 order_book_id=order.order_book_id,
             ))
             return False
diff --git a/rqalpha/mod/rqalpha_mod_sys_simulation/README.rst b/rqalpha/mod/rqalpha_mod_sys_simulation/README.rst
index 3c1a21dc3..5081d72b1 100644
--- a/rqalpha/mod/rqalpha_mod_sys_simulation/README.rst
+++ b/rqalpha/mod/rqalpha_mod_sys_simulation/README.rst
@@ -34,14 +34,15 @@ RQAlpha 回测 Mod，启用该模块开启回测功能。
         #   分钟回测的可选值有 "current_bar"|"next_bar"|"vwap"（以当前 bar 收盘价｜下一个 bar 的开盘价｜成交量加权平均价撮合)
         #   tick 回测的可选值有 "last"|"best_own"|"best_counterparty"（以最新价｜己方最优价｜对手方最优价撮合）和 "counterparty_offer"（逐档撮合）
         "matching_type": "current_bar",
-        # 开启对于处于涨跌停状态的证券的撮合限制（信号模式下该限制不生效）
+        # 开启对于处于涨跌停状态的证券的撮合限制
         "price_limit": True,
         # 开启对于对手盘无流动性的证券的撮合限制（仅在 tick 回测下生效）
         "liquidity_limit": False,
-        # 开启成交量限制（仅在日和分钟回测下生效）
+        # 开启成交量限制
         #   开启该限制意味着每个 bar 的累计成交量将不会超过该时间段内市场上总成交量的一定比值（volume_percent）
+        #   开启该限制意味着每个 tick 的累计成交量将不会超过当前tick与上一个tick的市场总成交量之差的一定比值
         "volume_limit": True,
-        # 每个 bar 可成交数量占市场总成交量的比值，在 volume_limit 开启时生效
+        # 每个 bar/tick 可成交数量占市场总成交量的比值，在 volume_limit 开启时生效
         "volume_percent": 0.25,
         # 滑点模型，可选值有 "PriceRatioSlippage"（按价格比例设置滑点）和 "TickSizeSlippage"（按跳设置滑点）
         #    亦可自己实现滑点模型，选择自己实现的滑点模型时，此处需传入包含包和模块的完整类路径
@@ -88,3 +89,8 @@ RQAlpha 回测 Mod，启用该模块开启回测功能。
 *   :code:`rqalpha run` 命令增加 :code:`--slippage` / :code:`-sp` 选项，您可以指定成交所产生的滑点，目前支持按照当前价格的百分比的方式计算滑点。
 *   :code:`rqalpha run` 命令增加 :code:`--commission-multiplier` / :code:`--cm` 选项，您可以指定手续费乘数
 *   :code:`rqalpha run` 命令增加 :code:`--matching-type` / :code:`--mt` 选项，您可以指定撮合的锚定价格及对应的方式
+
+注意事项
+===============================
+*   在tick级别回测频率下，开盘集合竞价期间的撮合将无视 matching_type 的设置，一律用last撮合。
+*   默认情况下Tick回测使用DefaultTickMatcher，日频和分钟频率回测使用DefaultBarMatcher。
\ No newline at end of file
diff --git a/rqalpha/mod/rqalpha_mod_sys_simulation/__init__.py b/rqalpha/mod/rqalpha_mod_sys_simulation/__init__.py
index e02ad4ee3..b05e5a430 100644
--- a/rqalpha/mod/rqalpha_mod_sys_simulation/__init__.py
+++ b/rqalpha/mod/rqalpha_mod_sys_simulation/__init__.py
@@ -28,10 +28,11 @@
     "price_limit": True,
     # 开启对于对手盘无流动性的证券的撮合限制（仅在 tick 回测下生效）
     "liquidity_limit": False,
-    # 开启成交量限制（仅在日和分钟回测下生效）
+    # 开启成交量限制
     #   开启该限制意味着每个 bar 的累计成交量将不会超过该时间段内市场上总成交量的一定比值（volume_percent）
+    #   开启该限制意味着每个 tick 的累计成交量将不会超过当前tick与上一个tick的市场总成交量之差的一定比值
     "volume_limit": True,
-    # 每个 bar 可成交数量占市场总成交量的比值，在 volume_limit 开启时生效
+    # 每个 bar/tick 可成交数量占市场总成交量的比值，在 volume_limit 开启时生效
     "volume_percent": 0.25,
     # 滑点模型，可选值有 "PriceRatioSlippage"（按价格比例设置滑点）和 "TickSizeSlippage"（按跳设置滑点）
     #    亦可自己实现滑点模型，选择自己实现的滑点模型时，此处需传入包含包和模块的完整类路径
diff --git a/rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py b/rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py
index a08f94b76..1ebaea163 100644
--- a/rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py
+++ b/rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py
@@ -14,16 +14,17 @@
 #         否则米筐科技有权追究相应的知识产权侵权责任。
 #         在此前提下，对本软件的使用同样需要遵守 Apache 2.0 许可，Apache 2.0 许可与本许可冲突之处，以本许可为准。
 #         详细的授权流程，请联系 public@ricequant.com 获取。
-
+import datetime
 from collections import defaultdict
-
 from rqalpha.const import MATCHING_TYPE, ORDER_TYPE, POSITION_EFFECT, SIDE
 from rqalpha.environment import Environment
 from rqalpha.core.events import EVENT, Event
 from rqalpha.model.order import Order
 from rqalpha.model.trade import Trade
+from rqalpha.model.tick import TickObject
 from rqalpha.portfolio.account import Account
 from rqalpha.utils import is_valid_price
+from typing import Dict
 from rqalpha.utils.i18n import gettext as _
 from .slippage import SlippageDecider
 
@@ -37,14 +38,13 @@ def update(self):
         raise NotImplementedError
 
 
-class DefaultMatcher(AbstractMatcher):
+class DefaultBarMatcher(AbstractMatcher):
     def __init__(self, env, mod_config):
         self._slippage_decider = SlippageDecider(mod_config.slippage_model, mod_config.slippage)
         self._turnover = defaultdict(int)
         self._volume_percent = mod_config.volume_percent
         self._price_limit = mod_config.price_limit
-        self._liquidity_limit = mod_config.liquidity_limit and env.config.base.frequency == "tick"
-        self._inactive_limit = env.config.base.frequency != 'tick' and mod_config.inactive_limit
+        self._inactive_limit = mod_config.inactive_limit
         self._volume_limit = mod_config.volume_limit
         self._env = env  # type: Environment
         self._deal_price_decider = self._create_deal_price_decider(mod_config.matching_type)
@@ -54,14 +54,6 @@ def _create_deal_price_decider(self, matching_type):
             MATCHING_TYPE.CURRENT_BAR_CLOSE: self._current_bar_close_decider,
             MATCHING_TYPE.VWAP: self._vwap_decider,
             MATCHING_TYPE.NEXT_BAR_OPEN: self._next_bar_open_decider,
-            MATCHING_TYPE.NEXT_TICK_LAST: lambda order_book_id, side: self._env.price_board.get_last_price(
-                order_book_id),
-            MATCHING_TYPE.NEXT_TICK_BEST_OWN: lambda order_book_id, side: self._best_own_price_decider(order_book_id,
-                                                                                                       side),
-            MATCHING_TYPE.COUNTERPARTY_OFFER: None,
-            MATCHING_TYPE.NEXT_TICK_BEST_COUNTERPARTY: lambda order_book_id, side: (
-                self._env.price_board.get_a1(order_book_id) if side == SIDE.BUY else self._env.price_board.get_b1(
-                    order_book_id))
         }
         return decider_dict[matching_type]
 
@@ -85,13 +77,6 @@ def _vwap_decider(self, order_book_id, _):
         except (KeyError, TypeError, ZeroDivisionError):
             return 0
 
-    def _best_own_price_decider(self, order_book_id, side):
-        price = self._env.price_board.get_b1(order_book_id) if side == SIDE.BUY else self._env.price_board.get_a1(
-            order_book_id)
-        if price == 0:
-            price = self._env.price_board.get_last_price(order_book_id)
-        return price
-
     def _open_auction_deal_price_decider(self, order_book_id, _):
         return self._env.data_proxy.get_open_auction_bar(order_book_id, self._env.calendar_dt).open
 
@@ -137,11 +122,6 @@ def match(self, account, order, open_auction):
                     return
                 if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(order_book_id):
                     return
-            if self._liquidity_limit:
-                if order.side == SIDE.BUY and price_board.get_a1(order_book_id) == 0:
-                    return
-                if order.side == SIDE.SELL and price_board.get_b1(order_book_id) == 0:
-                    return
         else:
             if self._price_limit:
                 if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(order_book_id):
@@ -156,19 +136,6 @@ def match(self, account, order, open_auction):
                     ).format(order_book_id=order.order_book_id)
                     order.mark_rejected(reason)
                     return
-            if self._liquidity_limit:
-                if order.side == SIDE.BUY and price_board.get_a1(order_book_id) == 0:
-                    reason = _(
-                        "Order Cancelled: [{order_book_id}] has no liquidity."
-                    ).format(order_book_id=order.order_book_id)
-                    order.mark_rejected(reason)
-                    return
-                if order.side == SIDE.SELL and price_board.get_b1(order_book_id) == 0:
-                    reason = _(
-                        "Order Cancelled: [{order_book_id}] has no liquidity."
-                    ).format(order_book_id=order.order_book_id)
-                    order.mark_rejected(reason)
-                    return
 
         if self._inactive_limit:
             bar_volume = self._env.get_bar(order_book_id).volume
@@ -242,7 +209,258 @@ def update(self):
         self._turnover.clear()
 
 
-class CounterPartyOfferMatcher(DefaultMatcher):
+class DefaultTickMatcher(AbstractMatcher):
+    """ tick回测使用 """
+
+    SUPPORT_POSITION_EFFECTS = (POSITION_EFFECT.OPEN, POSITION_EFFECT.CLOSE, POSITION_EFFECT.CLOSE_TODAY)
+    SUPPORT_SIDES = (SIDE.BUY, SIDE.SELL)
+
+    def __init__(self, env, mod_config):
+        self._slippage_decider = SlippageDecider(mod_config.slippage_model, mod_config.slippage)
+        self._turnover = defaultdict(int)
+        self._volume_percent = mod_config.volume_percent
+        self._price_limit = mod_config.price_limit
+        self._liquidity_limit = mod_config.liquidity_limit
+        self._volume_limit = mod_config.volume_limit
+        self._env = env  # type: Environment
+        self._deal_price_decider = self._create_deal_price_decider(mod_config.matching_type)
+
+        # 每个交易日期内的上一个时刻的tick(第一个除外)
+        self._last_tick: Dict[str, TickObject] = dict()
+        # 当前的tick
+        self._cur_tick: Dict[str, TickObject] = dict()
+
+        # 订阅一些事件
+        self._env.event_bus.prepend_listener(EVENT.TICK, self._on_tick)
+        self._env.event_bus.add_listener(EVENT.BEFORE_TRADING, self._on_before_trading)
+
+    def _create_deal_price_decider(self, matching_type):
+        decider_dict = {
+            MATCHING_TYPE.NEXT_TICK_LAST: lambda order_book_id, side: self._env.price_board.get_last_price(
+                order_book_id),
+            MATCHING_TYPE.NEXT_TICK_BEST_OWN: lambda order_book_id, side: self._best_own_price_decider(order_book_id,
+                                                                                                       side),
+            MATCHING_TYPE.COUNTERPARTY_OFFER: None,
+            MATCHING_TYPE.NEXT_TICK_BEST_COUNTERPARTY: lambda order_book_id, side: (
+                self._env.price_board.get_a1(order_book_id) if side == SIDE.BUY else self._env.price_board.get_b1(
+                    order_book_id))
+        }
+        return decider_dict[matching_type]
+
+    def _best_own_price_decider(self, order_book_id, side):
+        """ 己方最优价 """
+        price = self._env.price_board.get_b1(order_book_id) if side == SIDE.BUY else self._env.price_board.get_a1(
+            order_book_id)
+        if price == 0:
+            price = self._env.price_board.get_last_price(order_book_id)
+        return price
+
+    def _on_before_trading(self, event):
+        # 在每个交易日的盘前删除前一个交易日的数据
+        self._last_tick.clear()
+        self._cur_tick.clear()
+
+    def _on_tick(self, event):
+        # 保存上一个时刻的tick
+        self._last_tick[event.tick.order_book_id] = self._cur_tick.get(event.tick.order_book_id)
+        # 保存当前时刻的tick
+        self._cur_tick[event.tick.order_book_id] = event.tick
+
+    def _get_today_history_ticks(self, order_book_id, count):
+        """ 获取当前交易日的历史tick数据 """
+        cal_dt = self._env.calendar_dt
+        tick_list = self._env.data_proxy.history_ticks(order_book_id, count, cal_dt)
+        start = cal_dt if cal_dt.hour >= 19 else cal_dt - datetime.timedelta(days=1)
+        start = start.replace(hour=17, minute=0, second=0, microsecond=0)
+        ticks = [tick for tick in tick_list if start <= tick.datetime <= cal_dt]
+        return ticks
+
+    def _get_last_tick(self, order_book_id):
+        """ 获取上一个tick """
+        _last_tick = self._last_tick.get(order_book_id)
+        trading_dt = self._env.trading_dt
+        # 上一根tick缺失
+        if not _last_tick:
+            tick_list = self._get_today_history_ticks(order_book_id, 2)
+            _last_tick = tick_list[0] if len(tick_list) == 2 else None
+        else:
+            # 两个tick之间的时间差(秒)
+            diff_time = trading_dt.timestamp() - _last_tick.datetime.timestamp()
+            # 在非回测状态下，时差间隔太大时，需要重新获取tick
+            if diff_time > 5:
+                tick_list = self._get_today_history_ticks(order_book_id, 2)
+                _last_tick = tick_list[0] if len(tick_list) == 2 else None
+        return _last_tick
+
+    def match(self, account, order, open_auction):  # type: (Account, Order, bool) -> None
+
+        # order 是否合法
+        if not (order.position_effect in self.SUPPORT_POSITION_EFFECTS and order.side in self.SUPPORT_SIDES):
+            raise NotImplementedError
+
+        # 标的信息
+        order_book_id = order.order_book_id
+        instrument = self._env.get_instrument(order_book_id)
+
+        # 获取tick数据
+        _cur_tick = self._cur_tick.get(order_book_id)
+
+        # 判断订单在交易时间下处于那个阶段
+        if instrument.during_call_auction(self._env.calendar_dt):
+            # 集合竞价时段内撮合无视 matching_type 的设置，直接使用 last 进行撮合
+            deal_price = _cur_tick.last
+            # 集合竞价默认开启成交量限制，用来保证在集合竞价中只有一笔成交
+            _volume_limit_flag = True
+        else:
+            deal_price = self._deal_price_decider(order_book_id, order.side)
+            _volume_limit_flag = self._volume_limit
+
+        # 确认价格是否有效
+        if not is_valid_price(deal_price):
+            listed_date = instrument.listed_date.date()
+            if listed_date == self._env.trading_dt.date():
+                reason = _(
+                    u"Order Cancelled: current security [{order_book_id}] can not be traded"
+                    u" in listed date [{listed_date}]").format(
+                    order_book_id=order.order_book_id,
+                    listed_date=listed_date,
+                )
+            else:
+                reason = _(u"Order Cancelled: current tick [{order_book_id}] miss market data.").format(
+                    order_book_id=order.order_book_id)
+            order.mark_rejected(reason)
+            return
+
+        price_board = self._env.price_board
+        if order.type == ORDER_TYPE.LIMIT:
+            if order.side == SIDE.BUY and order.price < deal_price:
+                return
+            if order.side == SIDE.SELL and order.price > deal_price:
+                return
+            # 是否限制涨跌停不成交
+            if self._price_limit:
+                if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(order_book_id):
+                    return
+                if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(order_book_id):
+                    return
+            if self._liquidity_limit:
+                if order.side == SIDE.BUY and price_board.get_a1(order_book_id) == 0:
+                    return
+                if order.side == SIDE.SELL and price_board.get_b1(order_book_id) == 0:
+                    return
+        else:
+            if self._price_limit:
+                if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(order_book_id):
+                    reason = _(
+                        "Order Cancelled: current tick [{order_book_id}] reach the limit_up price."
+                    ).format(order_book_id=order.order_book_id)
+                    order.mark_rejected(reason)
+                    return
+                if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(order_book_id):
+                    reason = _(
+                        "Order Cancelled: current tick [{order_book_id}] reach the limit_down price."
+                    ).format(order_book_id=order.order_book_id)
+                    order.mark_rejected(reason)
+                    return
+            if self._liquidity_limit:
+                if order.side == SIDE.BUY and price_board.get_a1(order_book_id) == 0:
+                    reason = _(
+                        "Order Cancelled: [{order_book_id}] has no liquidity."
+                    ).format(order_book_id=order.order_book_id)
+                    order.mark_rejected(reason)
+                    return
+                if order.side == SIDE.SELL and price_board.get_b1(order_book_id) == 0:
+                    reason = _(
+                        "Order Cancelled: [{order_book_id}] has no liquidity."
+                    ).format(order_book_id=order.order_book_id)
+                    order.mark_rejected(reason)
+                    return
+
+        # 是否开启成交量限制
+        if _volume_limit_flag:
+            # 获取上一个tick
+            _last_tick = self._get_last_tick(order_book_id)
+
+            if _last_tick:
+                # 当前的时刻的成交量 = 当前tick - 上一个时刻的tick
+                volume = _cur_tick.volume - _last_tick.volume
+            else:
+                # 当天第一个tick
+                volume = _cur_tick.volume
+
+            if self._volume_limit:
+                volume_limit = round(volume * self._volume_percent) - self._turnover[order.order_book_id]
+            else:
+                # 主要是处理未开启时集合竞价的成交情况，只要有成交量就表示能撮合
+                volume_limit = volume
+
+            # 对成交量根据 1手 : n股 的比例进行限制
+            volume_limit = (volume_limit // instrument.round_lot) * instrument.round_lot
+
+            if volume_limit <= 0:
+                # 集合竞价无法撤单
+                if order.type == ORDER_TYPE.MARKET and not instrument.during_call_auction(self._env.calendar_dt):
+                    reason = _(u"Order Cancelled: market order {order_book_id} volume {order_volume}"
+                               u" due to volume limit").format(
+                        order_book_id=order.order_book_id,
+                        order_volume=order.quantity
+                    )
+                    order.mark_cancelled(reason)
+                return
+
+            # 实际成交数量
+            if self._volume_limit:
+                fill = min(order.unfilled_quantity, volume_limit)
+            else:
+                fill = order.unfilled_quantity
+        else:
+            # 下单数量就是成交数量
+            fill = order.unfilled_quantity
+
+        # 平今的数量
+        ct_amount = account.calc_close_today_amount(order_book_id, fill, order.position_direction)
+
+        # 对价格进行滑点处理
+        if instrument.during_call_auction(self._env.calendar_dt):
+            price = deal_price
+        else:
+            price = self._slippage_decider.get_trade_price(order, deal_price)
+
+        # 成交记录
+        trade = Trade.__from_create__(
+            order_id=order.order_id,
+            price=price,
+            amount=fill,
+            side=order.side,
+            position_effect=order.position_effect,
+            order_book_id=order.order_book_id,
+            frozen_price=order.frozen_price,
+            close_today_amount=ct_amount
+        )
+        trade._commission = self._env.get_trade_commission(trade)
+        trade._tax = self._env.get_trade_tax(trade)
+        order.fill(trade)
+        self._turnover[order.order_book_id] += fill
+
+        self._env.event_bus.publish_event(Event(EVENT.TRADE, account=account, trade=trade, order=order))
+
+        if order.type == ORDER_TYPE.MARKET and order.unfilled_quantity != 0:
+            reason = _(
+                u"Order Cancelled: market order {order_book_id} volume {order_volume} is"
+                u" larger than {volume_percent_limit} percent of current tick volume, fill {filled_volume} actually"
+            ).format(
+                order_book_id=order.order_book_id,
+                order_volume=order.quantity,
+                filled_volume=order.filled_quantity,
+                volume_percent_limit=self._volume_percent * 100.0
+            )
+            order.mark_cancelled(reason)
+
+    def update(self):
+        self._turnover.clear()
+
+
+class CounterPartyOfferMatcher(DefaultTickMatcher):
     def __init__(self, env, mod_config):
         super(CounterPartyOfferMatcher, self).__init__(env, mod_config)
         self._env = env
@@ -250,7 +468,7 @@ def __init__(self, env, mod_config):
         self._b_volume = {}
         self._a_price = {}
         self._b_price = {}
-        self._env.event_bus.add_listener(EVENT.TICK, self._pre_tick)
+        self._env.event_bus.prepend_listener(EVENT.TICK, self._pre_tick)
 
     def match(self, account, order, open_auction):
         # type: (Account, Order, bool) -> None
@@ -264,6 +482,7 @@ def match(self, account, order, open_auction):
         按照该tick，a1，b1进行成交，剩余订单直接撤单
         """
         order_book_id = order.order_book_id
+        instrument = self._env.get_instrument(order_book_id)
 
         self._pop_volume_and_price(order)
         if order.side == SIDE.BUY:
@@ -287,6 +506,13 @@ def match(self, account, order, open_auction):
                 # if order.unfilled_quantity != 0:
                 return self.match(account, order, open_auction)
 
+        if instrument.during_call_auction(self._env.trading_dt):
+            # 集合竞价期间一律使用last
+            matching_price = self._cur_tick[order_book_id].last
+            _volume_limit_flag = True
+        else:
+            _volume_limit_flag = self._volume_limit
+
         if matching_price != matching_price:
             return
 
@@ -297,13 +523,55 @@ def match(self, account, order, open_auction):
                 return
             if order.side == SIDE.SELL and order.price > matching_price:
                 return
-        fill = order.unfilled_quantity
+
+        # 成交量限制
+        if _volume_limit_flag:
+            # 获取tick
+            _last_tick = self._get_last_tick(order_book_id)
+            _cur_tick = self._cur_tick[order_book_id]
+
+            if _last_tick:
+                # 当前的时刻的成交量 = 当前tick - 上一个时刻的tick
+                volume = _cur_tick.volume - _last_tick.volume
+            else:
+                # 当天第一个tick
+                volume = _cur_tick.volume
+
+            if self._volume_limit:
+                volume_limit = round(volume * self._volume_percent) - self._turnover[order.order_book_id]
+            else:
+                volume_limit = volume
+
+            # 对成交量根据 1手 : n股 的比例进行限制
+            volume_limit = (volume_limit // instrument.round_lot) * instrument.round_lot
+
+            if volume_limit <= 0:
+                # 集合竞价无法撤单
+                if order.type == ORDER_TYPE.MARKET and not instrument.during_call_auction(self._env.calendar_dt):
+                    reason = _(u"Order Cancelled: market order {order_book_id} volume {order_volume}"
+                               u" due to volume limit").format(
+                        order_book_id=order.order_book_id,
+                        order_volume=order.quantity
+                    )
+                    order.mark_cancelled(reason)
+                return
+
+            if self._volume_limit:
+                if instrument.during_call_auction(self._env.trading_dt):
+                    fill = min(order.unfilled_quantity, volume_limit)
+                else:
+                    fill = min(order.unfilled_quantity, amount, volume_limit)
+            else:
+                fill = min(order.unfilled_quantity, amount)
+        else:
+            fill = min(order.unfilled_quantity, amount)
+
         ct_amount = account.calc_close_today_amount(order_book_id, fill, order.position_direction)
 
         trade = Trade.__from_create__(
             order_id=order.order_id,
             price=matching_price,
-            amount=min(amount, fill),
+            amount=fill,
             side=order.side,
             position_effect=order.position_effect,
             order_book_id=order.order_book_id,
@@ -313,12 +581,15 @@ def match(self, account, order, open_auction):
         trade._commission = self._env.get_trade_commission(trade)
         trade._tax = self._env.get_trade_tax(trade)
         order.fill(trade)
+        self._turnover[order.order_book_id] += fill
         self._env.event_bus.publish_event(Event(EVENT.TRADE, account=account, trade=trade, order=order))
 
-        if order.side == SIDE.BUY:
-            self._a_volume[order.order_book_id][0] -= min(amount, fill)
-        else:
-            self._b_volume[order.order_book_id][0] -= min(amount, fill)
+        if not instrument.during_call_auction(self._env.trading_dt):
+            # 常规交易时间逐档
+            if order.side == SIDE.BUY:
+                self._a_volume[order.order_book_id][0] -= fill
+            else:
+                self._b_volume[order.order_book_id][0] -= fill
 
         if order.type == ORDER_TYPE.MARKET and order.unfilled_quantity != 0:
             reason = _("Order Cancelled: market order {order_book_id} fill {filled_volume} actually").format(
@@ -350,6 +621,3 @@ def _pre_tick(self, event):
 
         self._a_price[order_book_id] = event.tick.asks
         self._b_price[order_book_id] = event.tick.bids
-
-    def update(self):
-        pass
diff --git a/rqalpha/mod/rqalpha_mod_sys_simulation/mod.py b/rqalpha/mod/rqalpha_mod_sys_simulation/mod.py
index 9465df5e5..318890971 100644
--- a/rqalpha/mod/rqalpha_mod_sys_simulation/mod.py
+++ b/rqalpha/mod/rqalpha_mod_sys_simulation/mod.py
@@ -42,7 +42,6 @@ def start_up(self, env, mod_config):
             raise patch_user_exc(ValueError(_(u"invalid margin multiplier value: value range is (0, +∞]")))
 
         if env.config.base.frequency == "tick":
-            mod_config.volume_limit = False
             if mod_config.matching_type not in [
                 MATCHING_TYPE.NEXT_TICK_LAST,
                 MATCHING_TYPE.NEXT_TICK_BEST_OWN,
diff --git a/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py b/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py
index 9205b7019..eb842c73d 100644
--- a/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py
+++ b/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py
@@ -30,7 +30,7 @@
 from rqalpha.model.order import Order
 from rqalpha.environment import Environment
 
-from .matcher import DefaultMatcher, AbstractMatcher, CounterPartyOfferMatcher
+from .matcher import DefaultBarMatcher, AbstractMatcher, CounterPartyOfferMatcher, DefaultTickMatcher
 
 
 class SimulationBroker(AbstractBroker, Persistable):
@@ -51,6 +51,7 @@ def __init__(self, env, mod_config):
         if self._mod_config.matching_type == MATCHING_TYPE.COUNTERPARTY_OFFER:
             for instrument_type in INSTRUMENT_TYPE:
                 self.register_matcher(instrument_type, CounterPartyOfferMatcher(self._env, self._mod_config))
+
         # 该事件会触发策略的before_trading函数
         self._env.event_bus.add_listener(EVENT.BEFORE_TRADING, self.before_trading)
         # 该事件会触发策略的handle_bar函数
@@ -68,7 +69,10 @@ def _get_matcher(self, order_book_id):
         try:
             return self._matchers[instrument_type]
         except KeyError:
-            return self._matchers.setdefault(instrument_type, DefaultMatcher(self._env, self._mod_config))
+            if self._env.config.base.frequency == "tick":
+                return self._matchers.setdefault(instrument_type, DefaultTickMatcher(self._env, self._mod_config))
+            else:
+                return self._matchers.setdefault(instrument_type, DefaultBarMatcher(self._env, self._mod_config))
 
     def register_matcher(self, instrument_type, matcher):
         # type: (INSTRUMENT_TYPE, AbstractMatcher) -> None
diff --git a/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_event_source.py b/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_event_source.py
index 7f82a1c04..6996c64ca 100644
--- a/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_event_source.py
+++ b/rqalpha/mod/rqalpha_mod_sys_simulation/simulation_event_source.py
@@ -22,7 +22,7 @@
 from rqalpha.core.events import Event, EVENT
 from rqalpha.utils.exception import patch_user_exc
 from rqalpha.utils.datetime_func import convert_int_to_datetime
-from rqalpha.const import DEFAULT_ACCOUNT_TYPE
+from rqalpha.const import DEFAULT_ACCOUNT_TYPE, INSTRUMENT_TYPE
 from rqalpha.utils.i18n import gettext as _
 
 
@@ -169,16 +169,22 @@ def events(self, start_date, end_date, frequency):
                         if last_tick is None:
                             last_tick = tick
 
-                            yield Event(
-                                EVENT.BEFORE_TRADING,
-                                calendar_dt=calendar_dt - timedelta(minutes=30),
-                                trading_dt=trading_dt - timedelta(minutes=30)
-                            )
-                            yield Event(
-                                EVENT.OPEN_AUCTION,
-                                calendar_dt=calendar_dt - timedelta(minutes=3),
-                                trading_dt=trading_dt - timedelta(minutes=3),
-                            )
+                            """
+                            这里区分时间主要是为了对其之前，之前对获取tick数据的时间有限制，期货的盘前时间是20:30，股票是09:00。
+                            在解除获取tick数据的限制后，股票的tick的开始时间是09:15，而期货则是20:59
+                            """
+                            if self._env.get_instrument(tick.order_book_id).type == INSTRUMENT_TYPE.FUTURE:
+                                yield Event(
+                                    EVENT.BEFORE_TRADING,
+                                    calendar_dt=calendar_dt - timedelta(minutes=30),
+                                    trading_dt=trading_dt - timedelta(minutes=30),
+                                )
+                            else:
+                                yield Event(
+                                    EVENT.BEFORE_TRADING,
+                                    calendar_dt=calendar_dt - timedelta(minutes=15),
+                                    trading_dt=trading_dt - timedelta(minutes=15),
+                                )
 
                         if self._universe_changed:
                             self._universe_changed = False
diff --git a/rqalpha/model/instrument.py b/rqalpha/model/instrument.py
index cd92eb55c..a5a8e8adc 100644
--- a/rqalpha/model/instrument.py
+++ b/rqalpha/model/instrument.py
@@ -15,6 +15,7 @@
 #         在此前提下，对本软件的使用同样需要遵守 Apache 2.0 许可，Apache 2.0 许可与本许可冲突之处，以本许可为准。
 #         详细的授权流程，请联系 public@ricequant.com 获取。
 
+import re
 import copy
 import datetime
 from typing import Dict, Callable, Optional
@@ -28,11 +29,10 @@
 
 
 class Instrument(metaclass=PropertyReprMeta):
-    DEFAULT_LISTED_DATE = datetime.datetime(1990, 1, 1)
     DEFAULT_DE_LISTED_DATE = datetime.datetime(2999, 12, 31)
 
     @staticmethod
-    def _fix_date(ds, dflt):
+    def _fix_date(ds, dflt=None):
         if isinstance(ds, datetime.datetime):
             return ds
         if ds == '0000-00-00':
@@ -48,7 +48,7 @@ def __init__(self, dic, future_tick_size_getter=None):
         self._future_tick_size_getter = future_tick_size_getter
 
         if "listed_date" in dic:
-            self.__dict__["listed_date"] = self._fix_date(dic["listed_date"], self.DEFAULT_LISTED_DATE)
+            self.__dict__["listed_date"] = self._fix_date(dic["listed_date"])
         if "de_listed_date" in dic:
             self.__dict__["de_listed_date"] = self._fix_date(dic["de_listed_date"], self.DEFAULT_DE_LISTED_DATE)
         if "maturity_date" in self.__dict__:
@@ -86,8 +86,7 @@ def round_lot(self) -> int:
         return int(self.__dict__["round_lot"])
 
     @property
-    def listed_date(self):
-        # type: () -> datetime.datetime
+    def listed_date(self) -> Optional[datetime.datetime]:
         """
         [datetime] 股票：该证券上市日期。期货：期货的上市日期，主力连续合约与指数连续合约都为 datetime(1990, 1, 1)。
         """
@@ -329,11 +328,11 @@ def listing_at(self, dt):
 
     def listed_at(self, dt):
         """
-        该合约在指定日期是否已上日
+        该合约在指定日期是否已上市
         :param dt: datetime.datetime
         :return: bool
         """
-        return self.listed_date <= dt
+        return self.listed_date and self.listed_date <= dt
 
     def de_listed_at(self, dt):
         """
@@ -386,8 +385,29 @@ def trading_code(self):
     def trade_at_night(self):
         return any(r.start <= datetime.time(4, 0) or r.end >= datetime.time(19, 0) for r in (self.trading_hours or []))
 
+    def during_call_auction(self, dt):
+        """ 是否处于集合竞价交易时段 """
+        # 当前的分钟数
+        _minute = dt.hour * 60 + dt.minute
+
+        if self.type in [INSTRUMENT_TYPE.CS, INSTRUMENT_TYPE.ETF]:
+            # 9:30 前或 14:57 之后为集合竞价
+            return _minute < 9 * 60 + 30 or _minute >= 14 * 60 + 57
+        elif self.type == INSTRUMENT_TYPE.FUTURE:
+            # 期货开盘时间
+            start_time = self.trading_hours[0].start
+
+            # -1 是因为获取到的时间都是开盘后1分钟，如 09:31
+            start_minute = start_time.hour * 60 + start_time.minute - 1
+
+            # 开盘集合竞价时间段为开盘前5分钟，期货无收盘集合竞价
+            return start_minute - 5 <= _minute < start_minute
+        else:
+            # 其他品种由子类实现
+            return False
+
     def days_from_listed(self):
-        if self.listed_date == self.DEFAULT_LISTED_DATE:
+        if not self.listed_date:
             return -1
 
         date = Environment.get_instance().trading_dt.date()
@@ -426,6 +446,11 @@ def calc_cash_occupation(self, price, quantity, direction):
         else:
             raise NotImplementedError
 
+    FUTURE_CONTINUOUS_CONTRACT = re.compile("^[A-Z]+(88|888|99|889)$")
+
+    @classmethod
+    def is_future_continuous_contract(cls, order_book_id):
+        return re.match(cls.FUTURE_CONTINUOUS_CONTRACT, order_book_id)
 
 class SectorCodeItem(object):
     def __init__(self, cn, en, name):
diff --git a/rqalpha/portfolio/__init__.py b/rqalpha/portfolio/__init__.py
index 54c31440a..ca1261671 100644
--- a/rqalpha/portfolio/__init__.py
+++ b/rqalpha/portfolio/__init__.py
@@ -17,6 +17,7 @@
 
 from itertools import chain
 from datetime import date
+from collections.abc import Mapping
 from typing import Callable, Dict, List, Tuple, Union
 
 import jsonpickle
@@ -30,7 +31,7 @@
 from rqalpha.model.order import Order, OrderStyle
 from rqalpha.portfolio.account import Account
 from rqalpha.data import DataProxy
-from rqalpha.utils import merge_dicts, is_valid_price
+from rqalpha.utils import is_valid_price
 from rqalpha.utils.functools import lru_cache
 from rqalpha.utils.i18n import gettext as _
 from rqalpha.utils.logger import user_log
@@ -272,21 +273,14 @@ def deposit_withdraw(self, account_type, amount):
         self._units = _units
 
 
-class MixedPositions(dict):
-
+class MixedPositions(Mapping):
     def __init__(self, accounts):
         super(MixedPositions, self).__init__()
         self._accounts = accounts
 
-    def __missing__(self, key):
-        account_type = Portfolio.get_account_type(key)
-        for a_type in self._accounts:
-            if a_type == account_type:
-                return self._accounts[a_type].positions[key]
-        return None
-
-    def __contains__(self, item):
-        return item in self.keys()
+    def __getitem__(self, item):
+        account_type = Portfolio.get_account_type(item)
+        return self._accounts[account_type].positions[item]
 
     def __repr__(self):
         keys = []
@@ -298,19 +292,5 @@ def __len__(self):
         return sum(len(account.positions) for account in six.itervalues(self._accounts))
 
     def __iter__(self):
-        keys = []
-        for account in six.itervalues(self._accounts):
-            keys += account.positions.keys()
-        for key in sorted(keys):
-            yield key
-
-    def items(self):
-        items = merge_dicts(*[account.positions.items() for account in six.itervalues(self._accounts)])
-        for k in sorted(items.keys()):
-            yield k, items[k]
-
-    def keys(self):
-        keys = []
-        for account in six.itervalues(self._accounts):
-            keys += list(account.positions.keys())
-        return sorted(keys)
+        for account in self._accounts.values():
+            yield from account.positions.keys()
diff --git a/rqalpha/utils/__init__.py b/rqalpha/utils/__init__.py
index d098840a6..e639ea9ab 100644
--- a/rqalpha/utils/__init__.py
+++ b/rqalpha/utils/__init__.py
@@ -127,13 +127,6 @@ def create_custom_exception(exc_type, exc_val, exc_tb, strategy_filename):
     return user_exc
 
 
-def merge_dicts(*dict_args):
-    result = {}
-    for d in dict_args:
-        result.update(d)
-    return result
-
-
 def account_type_str2enum(type_str):
     return DEFAULT_ACCOUNT_TYPE[type_str]
 
diff --git a/rqalpha/utils/translations/zh_Hans_CN/LC_MESSAGES/messages.mo b/rqalpha/utils/translations/zh_Hans_CN/LC_MESSAGES/messages.mo
index 150a6460b..ae12b3ac7 100644
Binary files a/rqalpha/utils/translations/zh_Hans_CN/LC_MESSAGES/messages.mo and b/rqalpha/utils/translations/zh_Hans_CN/LC_MESSAGES/messages.mo differ
diff --git a/rqalpha/utils/translations/zh_Hans_CN/LC_MESSAGES/messages.po b/rqalpha/utils/translations/zh_Hans_CN/LC_MESSAGES/messages.po
index bfa5a0efa..e95f9cc87 100644
--- a/rqalpha/utils/translations/zh_Hans_CN/LC_MESSAGES/messages.po
+++ b/rqalpha/utils/translations/zh_Hans_CN/LC_MESSAGES/messages.po
@@ -7,16 +7,16 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PROJECT VERSION\n"
 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2022-03-10 10:15+0800\n"
+"POT-Creation-Date: 2022-06-24 10:33+0800\n"
 "PO-Revision-Date: 2016-10-24 21:20+0800\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language: zh_Hans_CN\n"
 "Language-Team: zh_Hans_CN <LL@li.org>\n"
-"Plural-Forms: nplurals=1; plural=0\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.9.1\n"
+"Generated-By: Babel 2.10.1\n"
 
 #: rqalpha/environment.py:70
 msgid ""
@@ -332,7 +332,9 @@ msgstr "现金不足，使用当前剩余资金({})创建订单"
 msgid ""
 "function order_target_portfolio: invalid keys of target_portfolio, "
 "expected order_book_ids or Instrument objects, got {} (type: {})"
-msgstr "函数 order_target_portfolio：不合法的参数。target_portfolio 的键应为合约代码或 Instrument 对象，实际传入了 {}（类型：{}）。"
+msgstr ""
+"函数 order_target_portfolio：不合法的参数。target_portfolio 的键应为合约代码或 Instrument "
+"对象，实际传入了 {}（类型：{}）。"
 
 #: rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py:301
 msgid ""
@@ -410,17 +412,17 @@ msgstr ""
 msgid "[sys_analyser] Generate report from strategy output file"
 msgstr "【sys_analyser】使用策略输出的文件生成报告"
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:100
+#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:108
 msgid ""
 "config 'base.benchmark' is deprecated, use 'mod.sys_analyser.benchmark' "
 "instead"
 msgstr "配置'base.benchmark'已被弃用，使用'mod.sys_analyser.benchmark'代替"
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:182
+#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:190
 msgid "invalid init benchmark {}, should be in format 'order_book_id:weight'"
 msgstr ""
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:187
+#: rqalpha/mod/rqalpha_mod_sys_analyser/mod.py:195
 msgid "invalid weight for instrument {order_book_id}: {weight}"
 msgstr ""
 
@@ -542,34 +544,30 @@ msgid "WeeklyMaxDrawdown"
 msgstr "周度最大回撤"
 
 #: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:99
+msgid "ExcessCumReturns"
+msgstr "超额累计收益"
+
+#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:100
 msgid "ExcessReturns"
 msgstr "超额收益率"
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:100
+#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:101
 msgid "ExcessAnnual"
 msgstr "年化超额收益率"
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:101
+#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:102
 msgid "ExcessSharpe"
 msgstr "超额夏普率"
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:102
+#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:103
 msgid "ExcessVolatility"
 msgstr "超额收益波动率"
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:103
-msgid "ExcessAnnualVolatility"
-msgstr "超额收益年化波动率"
-
-#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:99
-msgid "ExcessCumReturns"
-msgstr "超额累计收益"
-
-#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:104
+#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:105
 msgid "ExcessMaxDD"
 msgstr "超额收益最大回撤"
 
-#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:105
+#: rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py:106
 msgid "ExcessMaxDD/ExcessMaxDDD"
 msgstr "超额收益最大回撤/最长回撤持续期"
 
@@ -580,14 +578,11 @@ msgid ""
 msgstr "订单创建失败: 可用资金不足。当前资金: {cash:.2f}，{order_book_id} 下单所需资金: {cost_money:.2f}"
 
 #: rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py:31
-msgid "Order Creation Failed: {order_book_id} is not listed!"
-msgstr "订单创建失败: {order_book_id} 未上市"
+#, fuzzy
+msgid "Order Creation Failed: {order_book_id} is not listing!"
+msgstr "订单创建失败: {order_book_id} 未上市或已退市"
 
 #: rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py:37
-msgid "Order Creation Failed: {order_book_id} has been delisted!"
-msgstr "订单创建失败: {order_book_id} 已退市"
-
-#: rqalpha/mod/rqalpha_mod_sys_risk/validators/is_trading_validator.py:43
 msgid "Order Creation Failed: security {order_book_id} is suspended on {date}"
 msgstr "订单创建失败: {order_book_id} 在 {date} 时停牌"
 
@@ -609,44 +604,41 @@ msgid ""
 "trade: [{}...]"
 msgstr "订单创建失败，当前存在可能导致自成交的挂单：[{}...]"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:116
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:101
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:319
 #: rqalpha/mod/rqalpha_mod_sys_simulation/signal_broker.py:66
 msgid ""
 "Order Cancelled: current security [{order_book_id}] can not be traded in "
 "listed date [{listed_date}]"
 msgstr "订单被撤销: [{order_book_id}] 上市首日无法交易"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:123
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:108
 #: rqalpha/mod/rqalpha_mod_sys_simulation/signal_broker.py:72
 msgid "Order Cancelled: current bar [{order_book_id}] miss market data."
 msgstr "订单被拒单: [{order_book_id}] 当前缺失市场数据。"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:148
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:128
 #: rqalpha/mod/rqalpha_mod_sys_simulation/signal_broker.py:86
 msgid "Order Cancelled: current bar [{order_book_id}] reach the limit_up price."
 msgstr "订单被拒单: [{order_book_id}] 已涨停。"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:154
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:134
 #: rqalpha/mod/rqalpha_mod_sys_simulation/signal_broker.py:95
 msgid "Order Cancelled: current bar [{order_book_id}] reach the limit_down price."
 msgstr "订单被拒单: [{order_book_id}] 已跌停。"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:161
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:167
-msgid "Order Cancelled: [{order_book_id}] has no liquidity."
-msgstr "合约 [{order_book_id}] 流动性不足，拒单。"
-
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:176
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:143
 msgid "Order Cancelled: {order_book_id} bar no volume"
 msgstr "订单被撤销：{order_book_id} 当前无成交量"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:192
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:159
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:396
 msgid ""
 "Order Cancelled: market order {order_book_id} volume {order_volume} due "
 "to volume limit"
 msgstr "订单被撤销: 订单 [{order_book_id}] 的下单量 {order_volume} 超过了成交量限制。"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:227
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:197
 msgid ""
 "Order Cancelled: market order {order_book_id} volume {order_volume} is "
 "larger than {volume_percent_limit} percent of current bar volume, fill "
@@ -655,7 +647,36 @@ msgstr ""
 "{order_book_id} 下单量 {order_volume} 超过当前 Bar 成交量的 "
 "{volume_percent_limit}%，实际成交 {filled_volume}"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:321
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:326
+msgid "Order Cancelled: current tick [{order_book_id}] miss market data."
+msgstr "订单被拒单: [{order_book_id}] 当前缺失市场数据。"
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:351
+msgid "Order Cancelled: current tick [{order_book_id}] reach the limit_up price."
+msgstr "订单被拒单: [{order_book_id}] 已涨停。"
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:357
+msgid ""
+"Order Cancelled: current tick [{order_book_id}] reach the limit_down "
+"price."
+msgstr "订单被拒单: [{order_book_id}] 已跌停。"
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:364
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:370
+msgid "Order Cancelled: [{order_book_id}] has no liquidity."
+msgstr "合约 [{order_book_id}] 流动性不足，拒单。"
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:438
+#, fuzzy
+msgid ""
+"Order Cancelled: market order {order_book_id} volume {order_volume} is "
+"larger than {volume_percent_limit} percent of current tick volume, fill "
+"{filled_volume} actually"
+msgstr ""
+"{order_book_id} 下单量 {order_volume} 超过当前 Tick 成交量的 "
+"{volume_percent_limit}%，实际成交 {filled_volume}"
+
+#: rqalpha/mod/rqalpha_mod_sys_simulation/matcher.py:571
 msgid ""
 "Order Cancelled: market order {order_book_id} fill {filled_volume} "
 "actually"
@@ -665,18 +686,18 @@ msgstr "订单取消：标的 {order_book_id} 实际成交 {filled_volume}"
 msgid "invalid margin multiplier value: value range is (0, +∞]"
 msgstr "无效的 保证金乘数 设置: 其值范围为 (0, +∞]"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:52
-#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:59
+#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:51
+#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:58
 msgid "Not supported matching type {}"
 msgstr "不支持撮合类型: {}"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:63
+#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:62
 msgid ""
 "matching_type = 'next_bar' is abandoned when frequency == '1d',Current "
 "matching_type is 'current_bar'."
 msgstr "日回测 'next_bar' 撮合方式已废弃，当前按 'current_bar' 撮合方式成交"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:108
+#: rqalpha/mod/rqalpha_mod_sys_simulation/mod.py:107
 msgid "NO account_type = ({}) in {}"
 msgstr ""
 
@@ -684,19 +705,19 @@ msgstr ""
 msgid "cancel_order function is not supported in signal mode"
 msgstr "在 Signal 模式下，不支持 cancel_order 函数"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:104
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:108
 msgid "unsupported position_effect {}"
 msgstr "不支持的 position_effect {}"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:125
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:129
 msgid "{order_id} order has been cancelled by user."
 msgstr "订单 {order_id} 被手动取消。"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:141
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:145
 msgid "Order Rejected: {order_book_id} can not match. Market close."
 msgstr "订单被拒单: {order_book_id} 当天交易结束，订单无法成交。"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:180
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py:184
 msgid "{order_book_id} should be subscribed when frequency is tick."
 msgstr "tick回测下单失败，请使用 subscribe 订阅合约 {order_book_id}。"
 
@@ -704,7 +725,7 @@ msgstr "tick回测下单失败，请使用 subscribe 订阅合约 {order_book_id
 msgid "Current universe is empty. Please use subscribe function before trade"
 msgstr "当前合约池为空。请使用 subscribe 订阅合约。"
 
-#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_event_source.py:196
+#: rqalpha/mod/rqalpha_mod_sys_simulation/simulation_event_source.py:202
 msgid "Frequency {} is not support."
 msgstr "不支持回测频率: {}"
 
@@ -757,7 +778,7 @@ msgstr "入金{}元，份额由{}变动为{}"
 msgid "Trigger Forced Liquidation, current total_value is 0"
 msgstr "触发强制清算，当前总权益为 0"
 
-#: rqalpha/portfolio/account.py:448
+#: rqalpha/portfolio/account.py:457
 msgid "insufficient cash, current {}, target withdrawal {}"
 msgstr "现金不足，当前现金 {}，目标出金 {}"
 
@@ -1384,3 +1405,9 @@ msgstr "不支持 API {}。请确保您设置了该 API 所需的账户并开启
 #~ msgid "target percent of {} should between 0 and 1, current: {}"
 #~ msgstr "{} 的目标持仓需要在 0 到 1 之间，当前值：{}"
 
+#~ msgid "ExcessAnnualVolatility"
+#~ msgstr "超额收益年化波动率"
+
+#~ msgid "Order Creation Failed: {order_book_id} has been delisted!"
+#~ msgstr "订单创建失败: {order_book_id} 已退市"
+


Test patch:
diff --git a/tests/unittest/test_mod/test_sys_simulation/test_simulation_event_source.py b/tests/unittest/test_mod/test_sys_simulation/test_simulation_event_source.py
index 6983e937c..93b4bde8c 100644
--- a/tests/unittest/test_mod/test_sys_simulation/test_simulation_event_source.py
+++ b/tests/unittest/test_mod/test_sys_simulation/test_simulation_event_source.py
@@ -61,11 +61,6 @@ def test_tick_events(self):
                 datetime(2018, 9, 13, 20, 29, 0, 500000), datetime(2018, 9, 14, 20, 29, 0, 500000)
             )
 
-            self.assertEvent(
-                next(events), EVENT.OPEN_AUCTION,
-                datetime(2018, 9, 13, 20, 56, 0, 500000), datetime(2018, 9, 14, 20, 56, 0, 500000)
-            )
-
             self.env.update_universe({"TF1812"})
             self.assertEvent(
                 next(events), EVENT.TICK,