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
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))
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 = []
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))
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))
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))
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()
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
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
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))
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)
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)))
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)