Beispiel #1
0
    def cancel_order(self, order):
        account = self._env.get_account(order.order_book_id)

        self._env.event_bus.publish_event(Event(EVENT.ORDER_PENDING_CANCEL, account=account, order=order))

        order.mark_cancelled(_(u"{order_id} order has been cancelled by user.").format(order_id=order.order_id))

        self._env.event_bus.publish_event(Event(EVENT.ORDER_CANCELLATION_PASS, account=account, order=order))

        try:
            self._open_orders.remove((account, order))
        except ValueError:
            pass
Beispiel #2
0
 def submit_order(self, order):
     if order.position_effect == POSITION_EFFECT.EXERCISE:
         raise NotImplementedError(
             "SignalBroker does not support exercise order temporarily")
     account = self._env.get_account(order.order_book_id)
     self._env.event_bus.publish_event(
         Event(EVENT.ORDER_PENDING_NEW, account=account, order=order))
     if order.is_final():
         return
     order.active()
     self._env.event_bus.publish_event(
         Event(EVENT.ORDER_CREATION_PASS, account=account, order=order))
     self._match(account, order)
    def _match(self, order_book_id=None):
        order_filter = None if order_book_id is None else lambda a_and_o: a_and_o[
            1].order_book_id == order_book_id
        for account, order in filter(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)
        final_orders = [
            (a, o)
            for a, o in chain(self._open_orders, self._open_auction_orders)
            if o.is_final()
        ]
        self._open_orders = [
            (a, o)
            for a, o in chain(self._open_orders, self._open_auction_orders)
            if not o.is_final()
        ]
        self._open_auction_orders.clear()

        for account, order in final_orders:
            if order.status == ORDER_STATUS.REJECTED or order.status == ORDER_STATUS.CANCELLED:
                self._env.event_bus.publish_event(
                    Event(EVENT.ORDER_UNSOLICITED_UPDATE,
                          account=account,
                          order=order))
Beispiel #4
0
 def after_trading(self, __):
     for account, order in self._open_orders:
         order.mark_rejected(_(u"Order Rejected: {order_book_id} can not match. Market close.").format(
             order_book_id=order.order_book_id
         ))
         self._env.event_bus.publish_event(Event(EVENT.ORDER_UNSOLICITED_UPDATE, account=account, order=order))
     self._open_orders = []
Beispiel #5
0
    def run(self, bar_dict):
        conf = self._env.config.base
        for event in self._env.event_source.events(conf.start_date, conf.end_date, conf.frequency):
            if event.event_type == EVENT.TICK:
                if self._ensure_before_trading(event):
                    self._split_and_publish(event)
            elif event.event_type == EVENT.BAR:
                if self._ensure_before_trading(event):
                    bar_dict.update_dt(event.calendar_dt)
                    event.bar_dict = bar_dict
                    self._split_and_publish(event)
            elif event.event_type == EVENT.OPEN_AUCTION:
                if self._ensure_before_trading(event):
                    bar_dict.update_dt(event.calendar_dt)
                    event.bar_dict = bar_dict
                    self._split_and_publish(event)
            elif event.event_type == EVENT.BEFORE_TRADING:
                self._ensure_before_trading(event)
            elif event.event_type == EVENT.AFTER_TRADING:
                self._split_and_publish(event)
            else:
                self._env.event_bus.publish_event(event)

        # publish settlement after last day
        if self._env.trading_dt.date() == conf.end_date:
            self._split_and_publish(Event(EVENT.SETTLEMENT))
Beispiel #6
0
def output_profile_result(env):
    stdout_trap = six.StringIO()
    env.profile_deco.print_stats(stdout_trap)
    profile_output = stdout_trap.getvalue()
    profile_output = profile_output.rstrip()
    six.print_(profile_output)
    env.event_bus.publish_event(
        Event(EVENT.ON_LINE_PROFILER_RESULT, result=profile_output))
Beispiel #7
0
    def init(self):
        if self._init:
            with ExecutionContext(EXECUTION_PHASE.ON_INIT):
                with ModifyExceptionFromType(EXC_TYPE.USER_EXC):
                    self._init(self._user_context)

        Environment.get_instance().event_bus.publish_event(
            Event(EVENT.POST_USER_INIT))
Beispiel #8
0
 def submit_order(self, order):
     if order.position_effect == POSITION_EFFECT.MATCH:
         raise TypeError(_("unsupported position_effect {}").format(order.position_effect))
     account = self._env.get_account(order.order_book_id)
     self._env.event_bus.publish_event(Event(EVENT.ORDER_PENDING_NEW, account=account, order=order))
     if order.is_final():
         return
     if order.position_effect == POSITION_EFFECT.EXERCISE:
         return self._open_exercise_orders.append((account, order))
     if ExecutionContext.phase() == EXECUTION_PHASE.OPEN_AUCTION:
         self._open_auction_orders.append((account, order))
     else:
         self._open_orders.append((account, order))
     order.active()
     self._env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_PASS, account=account, order=order))
     if self._match_immediately:
         self._match()
Beispiel #9
0
 def _ensure_before_trading(self, event):
     # return True if before_trading won't run this time
     if self._last_before_trading == event.trading_dt.date() or self._env.config.extra.is_hold:
         return True
     if self._last_before_trading:
         # don't publish settlement on first day
         previous_trading_date = self._env.data_proxy.get_previous_trading_date(event.trading_dt).date()
         if self._env.trading_dt.date() != previous_trading_date:
             self._env.update_time(
                 datetime.combine(previous_trading_date, self._env.calendar_dt.time()),
                 datetime.combine(previous_trading_date, self._env.trading_dt.time())
             )
         system_log.debug("publish settlement events with calendar_dt={}, trading_dt={}".format(
             self._env.calendar_dt, self._env.trading_dt
         ))
         self._split_and_publish(Event(EVENT.SETTLEMENT))
     self._last_before_trading = event.trading_dt.date()
     self._split_and_publish(Event(EVENT.BEFORE_TRADING, calendar_dt=event.calendar_dt, trading_dt=event.trading_dt))
     return False
Beispiel #10
0
def run(config, source_code=None, user_funcs=None):
    env = Environment(config)
    persist_helper = None
    init_succeed = False
    mod_handler = ModHandler()

    try:
        # avoid register handlers everytime
        # when running in ipython
        set_loggers(config)
        init_rqdatac(getattr(config.base, 'rqdatac_uri', None))
        system_log.debug("\n" + pformat(config.convert_to_dict()))

        env.set_strategy_loader(
            init_strategy_loader(env, source_code, user_funcs, config))
        mod_handler.set_env(env)
        mod_handler.start_up()

        if not env.data_source:
            env.set_data_source(
                BaseDataSource(config.base.data_bundle_path,
                               getattr(config.base, "future_info", {})))
        if env.price_board is None:
            from rqalpha.data.bar_dict_price_board import BarDictPriceBoard
            env.price_board = BarDictPriceBoard()
        env.set_data_proxy(DataProxy(env.data_source, env.price_board))

        _adjust_start_date(env.config, env.data_proxy)

        ctx = ExecutionContext(const.EXECUTION_PHASE.GLOBAL)
        ctx._push()

        # FIXME
        start_dt = datetime.datetime.combine(config.base.start_date,
                                             datetime.datetime.min.time())
        env.calendar_dt = start_dt
        env.trading_dt = start_dt

        assert env.broker is not None
        assert env.event_source is not None
        if env.portfolio is None:
            from rqalpha.portfolio import Portfolio
            env.set_portfolio(
                Portfolio(config.base.accounts, config.base.init_positions))

        env.event_bus.publish_event(Event(EVENT.POST_SYSTEM_INIT))

        scope = create_base_scope()
        scope.update({"g": env.global_vars})
        scope.update(get_strategy_apis())
        scope = env.strategy_loader.load(scope)

        if config.extra.enable_profiler:
            enable_profiler(env, scope)

        ucontext = StrategyContext()
        executor = Executor(env)

        persist_helper = init_persist_helper(env, ucontext, executor, config)
        user_strategy = Strategy(env.event_bus, scope, ucontext)
        env.user_strategy = user_strategy

        env.event_bus.publish_event(Event(EVENT.BEFORE_STRATEGY_RUN))
        if persist_helper:
            with LogCapture(user_log) as log_capture:
                user_strategy.init()
        else:
            user_strategy.init()

        if config.extra.context_vars:
            for k, v in config.extra.context_vars.items():
                if isinstance(v, RqAttrDict):
                    v = v.__dict__
                setattr(ucontext, k, v)

        if persist_helper:
            env.event_bus.publish_event(Event(EVENT.BEFORE_SYSTEM_RESTORED))
            restored_obj_state = persist_helper.restore(None)
            check_key = ["global_vars", "user_context", "executor", "universe"]
            kept_current_init_data = not any(
                v for k, v in restored_obj_state.items() if k in check_key)
            system_log.debug(
                "restored_obj_state: {}".format(restored_obj_state))
            system_log.debug(
                "kept_current_init_data: {}".format(kept_current_init_data))
            if kept_current_init_data:
                # 未能恢复init相关数据 保留当前策略初始化变量(展示当前策略初始化日志)
                log_capture.replay()
            else:
                user_system_log.info(_('system restored'))
            env.event_bus.publish_event(Event(EVENT.POST_SYSTEM_RESTORED))

        init_succeed = True

        bar_dict = BarMap(env.data_proxy, config.base.frequency)
        executor.run(bar_dict)
        env.event_bus.publish_event(Event(EVENT.POST_STRATEGY_RUN))

        if env.profile_deco:
            output_profile_result(env)
        release_print(scope)
    except CustomException as e:
        if init_succeed and persist_helper and env.config.base.persist_mode == const.PERSIST_MODE.ON_CRASH:
            persist_helper.persist()

        code = _exception_handler(e)
        mod_handler.tear_down(code, e)
    except Exception as e:
        system_log.error(traceback.format_exc())

        if init_succeed and persist_helper and env.config.base.persist_mode == const.PERSIST_MODE.ON_CRASH:
            persist_helper.persist()

        exc_type, exc_val, exc_tb = sys.exc_info()
        user_exc = create_custom_exception(exc_type, exc_val, exc_tb,
                                           config.base.strategy_file)

        code = _exception_handler(user_exc)
        mod_handler.tear_down(code, user_exc)
    else:
        if persist_helper and env.config.base.persist_mode == const.PERSIST_MODE.ON_NORMAL_EXIT:
            persist_helper.persist()
        result = mod_handler.tear_down(const.EXIT_CODE.EXIT_SUCCESS)
        system_log.debug(_(u"strategy run successfully, normal exit"))
        return result
Beispiel #11
0
    def events(self, start_date, end_date, frequency):
        trading_dates = self._env.data_proxy.get_trading_dates(
            start_date, end_date)
        if frequency == "1d":
            # 根据起始日期和结束日期,获取所有的交易日,然后再循环获取每一个交易日
            for day in trading_dates:
                date = day.to_pydatetime()
                dt_before_trading = date.replace(hour=0, minute=0)

                dt_bar = self._get_day_bar_dt(date)
                dt_after_trading = self._get_after_trading_dt(date)

                yield Event(EVENT.BEFORE_TRADING,
                            calendar_dt=dt_before_trading,
                            trading_dt=dt_before_trading)
                yield Event(EVENT.OPEN_AUCTION,
                            calendar_dt=dt_before_trading,
                            trading_dt=dt_before_trading)
                yield Event(EVENT.BAR, calendar_dt=dt_bar, trading_dt=dt_bar)
                yield Event(EVENT.AFTER_TRADING,
                            calendar_dt=dt_after_trading,
                            trading_dt=dt_after_trading)
        elif frequency == '1m':
            for day in trading_dates:
                before_trading_flag = True
                date = day.to_pydatetime()
                last_dt = None
                done = False

                dt_before_day_trading = date.replace(hour=8, minute=30)

                while True:
                    if done:
                        break
                    exit_loop = True
                    trading_minutes = self._get_trading_minutes(date)
                    for calendar_dt in trading_minutes:
                        if last_dt is not None and calendar_dt < last_dt:
                            continue

                        if calendar_dt < dt_before_day_trading:
                            trading_dt = calendar_dt.replace(year=date.year,
                                                             month=date.month,
                                                             day=date.day)
                        else:
                            trading_dt = calendar_dt
                        if before_trading_flag:
                            before_trading_flag = False
                            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),
                            )
                        if self._universe_changed:
                            self._universe_changed = False
                            last_dt = calendar_dt
                            exit_loop = False
                            break
                        # yield handle bar
                        yield Event(EVENT.BAR,
                                    calendar_dt=calendar_dt,
                                    trading_dt=trading_dt)
                    if exit_loop:
                        done = True

                dt = self._get_after_trading_dt(date)
                yield Event(EVENT.AFTER_TRADING, calendar_dt=dt, trading_dt=dt)
        elif frequency == "tick":
            data_proxy = self._env.data_proxy
            for day in trading_dates:
                date = day.to_pydatetime()
                last_tick = None
                last_dt = None
                dt_before_day_trading = date.replace(hour=8, minute=30)
                while True:
                    for tick in data_proxy.get_merge_ticks(
                            self._get_universe(), date, last_dt):
                        # find before trading time

                        calendar_dt = tick.datetime

                        if calendar_dt < dt_before_day_trading:
                            trading_dt = calendar_dt.replace(year=date.year,
                                                             month=date.month,
                                                             day=date.day)
                        else:
                            trading_dt = calendar_dt

                        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),
                            )

                        if self._universe_changed:
                            self._universe_changed = False
                            break

                        last_dt = calendar_dt
                        yield Event(EVENT.TICK,
                                    calendar_dt=calendar_dt,
                                    trading_dt=trading_dt,
                                    tick=tick)

                    else:
                        break

                dt = self._get_after_trading_dt(date)
                yield Event(EVENT.AFTER_TRADING, calendar_dt=dt, trading_dt=dt)
        else:
            raise NotImplementedError(
                _("Frequency {} is not support.").format(frequency))
 def before_trading(self, _):
     for account, order in self._open_orders:
         order.active()
         self._env.event_bus.publish_event(
             Event(EVENT.ORDER_CREATION_PASS, account=account, order=order))
Beispiel #13
0
    def match(self, account, order, open_auction):
        # type: (Account, Order, bool) -> None
        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)

        if open_auction:
            deal_price = self._open_auction_deal_price_decider(
                order_book_id, order.side)
        else:
            deal_price = self._deal_price_decider(order_book_id, order.side)

        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 bar [{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 bar [{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 bar [{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 self._inactive_limit:
            bar_volume = self._env.get_bar(order_book_id).volume
            if bar_volume == 0:
                reason = _(u"Order Cancelled: {order_book_id} bar no volume"
                           ).format(order_book_id=order.order_book_id)
                order.mark_cancelled(reason)
                return

        if self._volume_limit:
            if open_auction:
                volume = self._env.data_proxy.get_open_auction_bar(
                    order_book_id, self._env.calendar_dt).volume
            else:
                volume = self._env.get_bar(order_book_id).volume
            if volume == volume:
                volume_limit = round(
                    volume *
                    self._volume_percent) - self._turnover[order.order_book_id]

                round_lot = instrument.round_lot
                volume_limit = (volume_limit // round_lot) * round_lot
                if volume_limit <= 0:
                    if order.type == ORDER_TYPE.MARKET:
                        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

                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)
        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 bar 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)
Beispiel #14
0
    def _match(self, account, order):
        order_book_id = order.order_book_id
        price_board = self._env.price_board

        last_price = price_board.get_last_price(order_book_id)

        if not is_valid_price(last_price):
            instrument = self._env.get_instrument(order_book_id)
            listed_date = instrument.listed_date.date()
            if listed_date == self._env.trading_dt.date():
                reason = _(
                    "Order Cancelled: current security [{order_book_id}] can not be traded in listed date [{listed_date}]"
                ).format(
                    order_book_id=order_book_id,
                    listed_date=listed_date,
                )
            else:
                reason = _(
                    u"Order Cancelled: current bar [{order_book_id}] miss market data."
                ).format(order_book_id=order_book_id)
            order.mark_rejected(reason)
            self._env.event_bus.publish_event(
                Event(EVENT.ORDER_UNSOLICITED_UPDATE,
                      account=account,
                      order=copy(order)))
            return

        if order.type == ORDER_TYPE.LIMIT:
            deal_price = order.frozen_price
        else:
            deal_price = last_price

        if self._price_limit:
            if order.position_effect != POSITION_EFFECT.EXERCISE:
                if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(
                        order_book_id):
                    order.mark_rejected(
                        _("Order Cancelled: current bar [{order_book_id}] reach the limit_up price."
                          ).format(order_book_id=order.order_book_id))
                    self._env.event_bus.publish_event(
                        Event(EVENT.ORDER_UNSOLICITED_UPDATE,
                              account=account,
                              order=copy(order)))
                    return

                if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(
                        order_book_id):
                    order.mark_rejected(
                        _("Order Cancelled: current bar [{order_book_id}] reach the limit_down price."
                          ).format(order_book_id=order.order_book_id))
                    self._env.event_bus.publish_event(
                        Event(EVENT.ORDER_UNSOLICITED_UPDATE,
                              account=account,
                              order=copy(order)))
                    return

        ct_amount = account.calc_close_today_amount(order_book_id,
                                                    order.quantity,
                                                    order.position_direction)
        trade_price = self._slippage_decider.get_trade_price(order, deal_price)
        trade = Trade.__from_create__(order_id=order.order_id,
                                      price=trade_price,
                                      amount=order.quantity,
                                      side=order.side,
                                      position_effect=order.position_effect,
                                      order_book_id=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._env.event_bus.publish_event(
            Event(EVENT.TRADE, account=account, trade=trade,
                  order=copy(order)))
Beispiel #15
0
    def match(self, account, order, open_auction):
        # type: (Account, Order, bool) -> None
        #
        """限价撮合:
        订单买价>卖x价
        买量>卖x量,按照卖x价成交,订单减去卖x量,继续撮合卖x+1,直至该tick中所有报价被买完。买完后若有剩余买量,则在下一个tick继续撮合。
        买量<卖x量,按照卖x价成交。
        反之亦然
        市价单:
        按照该tick,a1,b1进行成交,剩余订单直接撤单
        """
        order_book_id = order.order_book_id

        self._pop_volume_and_price(order)
        if order.side == SIDE.BUY:
            if len(self._a_volume[order_book_id]) == 0:
                return
            volume_limit = self._a_volume[order_book_id][0]
            matching_price = self._a_price[order_book_id][0]
        else:
            if len(self._b_volume[order_book_id]) == 0:
                return
            volume_limit = self._b_volume[order_book_id][0]
            matching_price = self._b_price[order_book_id][0]

        if order.type == ORDER_TYPE.MARKET:
            amount = volume_limit
        else:
            if volume_limit != volume_limit:
                return
            amount = volume_limit
            if amount == 0.0 and order.unfilled_quantity != 0:
                # if order.unfilled_quantity != 0:
                return self.match(account, order, open_auction)

        if matching_price != matching_price:
            return

        if not (order.position_effect in self.SUPPORT_POSITION_EFFECTS
                and order.side in self.SUPPORT_SIDES):
            raise NotImplementedError
        if order.type == ORDER_TYPE.LIMIT:
            if order.side == SIDE.BUY and order.price < matching_price:
                return
            if order.side == SIDE.SELL and order.price > matching_price:
                return
        fill = order.unfilled_quantity
        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),
                                      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._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 order.type == ORDER_TYPE.MARKET and order.unfilled_quantity != 0:
            reason = _(
                "Order Cancelled: market order {order_book_id} fill {filled_volume} actually"
            ).format(
                order_book_id=order.order_book_id,
                filled_volume=order.filled_quantity,
            )
            order.mark_cancelled(reason)
            return
        if order.unfilled_quantity != 0:
            self.match(account, order, open_auction)