def test_booking(self): def mock_get_instrument(order_book_id): not_delisted_ins = MagicMock() not_delisted_ins.de_listed_date = datetime.datetime.max not_delisted_ins.type = "Future" delisted_ins = MagicMock() delisted_ins.de_listed_date = datetime.datetime.min delisted_ins.type = "Future" if order_book_id == "TF1812": return delisted_ins return not_delisted_ins self.booking.set_state(jsonpickle.encode({ "long_positions": { "RB1812": { "old_quantity": 1, "today_quantity": 3 }}, "short_positions": { "TF1812": { "today_quantity": 4 } } }).encode('utf-8')) self.assertPositions({ (POSITION_DIRECTION.LONG, "RB1812", 3, 1), (POSITION_DIRECTION.SHORT, "TF1812", 4, 0) }) self.env.event_bus.publish_event(Event(EVENT.TRADE, trade=Trade.__from_create__( 0, 0, 2, SIDE.SELL, POSITION_EFFECT.OPEN, "RB1812" ))) self.assertPositions({ (POSITION_DIRECTION.LONG, "RB1812", 3, 1), (POSITION_DIRECTION.SHORT, "RB1812", 2, 0), (POSITION_DIRECTION.SHORT, "TF1812", 4, 0) }) self.env.event_bus.publish_event(Event(EVENT.TRADE, trade=Trade.__from_create__( 0, 0, 3, SIDE.SELL, POSITION_EFFECT.CLOSE, "RB1812" ))) self.assertPositions({ (POSITION_DIRECTION.LONG, "RB1812", 1, 0), (POSITION_DIRECTION.SHORT, "RB1812", 2, 0), (POSITION_DIRECTION.SHORT, "TF1812", 4, 0) }) with self.mock_data_proxy_method("instruments", mock_get_instrument): self.env.trading_dt = datetime.datetime(2018, 9, 3) self.env.event_bus.publish_event(Event(EVENT.PRE_BEFORE_TRADING)) self.assertPositions({ (POSITION_DIRECTION.LONG, "RB1812", 0, 1), (POSITION_DIRECTION.SHORT, "RB1812", 0, 2), })
def test_booking(self): def mock_get_instrument(order_book_id): not_delisted_ins = MagicMock() not_delisted_ins.de_listed_date = datetime.datetime.max not_delisted_ins.type = "Future" delisted_ins = MagicMock() delisted_ins.de_listed_date = datetime.datetime.min delisted_ins.type = "Future" if order_book_id == "TF1812": return delisted_ins return not_delisted_ins self.booking.set_state( jsonpickle.encode({ "long_positions": { "RB1812": { "old_quantity": 1, "today_quantity": 3 } }, "short_positions": { "TF1812": { "today_quantity": 4 } } }).encode('utf-8')) self.assertPositions({(POSITION_DIRECTION.LONG, "RB1812", 3, 1), (POSITION_DIRECTION.SHORT, "TF1812", 4, 0)}) self.env.event_bus.publish_event( Event(EVENT.TRADE, trade=Trade.__from_create__(0, 0, 2, SIDE.SELL, POSITION_EFFECT.OPEN, "RB1812"))) self.assertPositions({(POSITION_DIRECTION.LONG, "RB1812", 3, 1), (POSITION_DIRECTION.SHORT, "RB1812", 2, 0), (POSITION_DIRECTION.SHORT, "TF1812", 4, 0)}) self.env.event_bus.publish_event( Event(EVENT.TRADE, trade=Trade.__from_create__(0, 0, 3, SIDE.SELL, POSITION_EFFECT.CLOSE, "RB1812"))) self.assertPositions({(POSITION_DIRECTION.LONG, "RB1812", 1, 0), (POSITION_DIRECTION.SHORT, "RB1812", 2, 0), (POSITION_DIRECTION.SHORT, "TF1812", 4, 0)}) with self.mock_data_proxy_method("instruments", mock_get_instrument): self.env.trading_dt = datetime.datetime(2018, 9, 3) self.env.event_bus.publish_event(Event(EVENT.PRE_BEFORE_TRADING)) self.assertPositions({ (POSITION_DIRECTION.LONG, "RB1812", 0, 1), (POSITION_DIRECTION.SHORT, "RB1812", 0, 2), })
def _on_pre_bar(self, event): if self.open_oders: data = self._query_filled_orders() if data is not None: for item in data: trade_no = item[u'成交编号'] if trade_no in self._trade_no: continue entrust_no = item[u'合同编号'] order_id = self._order_id_map.get(entrust_no, None) if order_id: order = self._orders.get(order_id, None) if order: account = self._env.get_account( order.order_book_id) user_system_log.info(repr(item)) trade = Trade.__from_create__( order_id=order.order_id, price=float(item[u'成交均价']), amount=int(item[u'成交数量']), side=order.side, position_effect=order.position_effect, order_book_id=order.order_book_id, frozen_price=order.frozen_price, ) order.fill(trade) self._env.event_bus.publish_event( Event(EVENT.TRADE, account=account, trade=trade, order=order)) self._trade_no.add(trade_no)
def on_trade(self, trade_dict): self.on_debug('交易回报: %s' % str(trade_dict)) if self._data_update_date != date.today(): self._cache.cache_trade(trade_dict) else: account = Environment.get_instance().get_account( trade_dict.order_book_id) if trade_dict.trade_id in account._backward_trade_set: return try: order = self.order_objects[trade_dict.order_id] except KeyError: order = Order.__from_create__(trade_dict.order_book_id, trade_dict.amount, trade_dict.side, trade_dict.style, trade_dict.position_effect) commission = cal_commission(trade_dict, order.position_effect) trade = Trade.__from_create__(trade_dict.order_id, trade_dict.price, trade_dict.amount, trade_dict.side, trade_dict.position_effect, trade_dict.order_book_id, trade_id=trade_dict.trade_id, commission=commission, frozen_price=trade_dict.price) order.fill(trade) self._env.event_bus.publish_event( RqEvent(EVENT.TRADE, account=account, trade=trade))
def _on_post_bar(self, event): if self._open_orders: data = self._query_filled_orders() if data is not None: for item in data: trade_no = item[u'成交编号'] if trade_no in self._trade_no: continue entrust_no = item[u'合同编号'] order_id = self._order_id_map.get(entrust_no, None) if order_id: order = self._open_orders.get(order_id, None) if order: trade = Trade.__from_create__( order_id=order.order_id, price=float(item[u'成交均价']), amount=int(item[u'成交数量']), side=order.side, position_effect=order.position_effect, order_book_id=order.order_book_id, frozen_price=order.frozen_price, ) account = self._env.get_account(order.order_book_id) trade._commission = self._env.get_trade_commission(account_type_str2enum(account.type), trade) trade._tax = self._env.get_trade_tax(account_type_str2enum(account.type), trade) order.fill(trade) self._env.event_bus.publish_event(Event(EVENT.TRADE, account=account, trade=trade, order=order)) self._trade_no.add(trade_no) if order.is_final(): str_order_id = str(order.order_id) del self._open_orders[str_order_id] del self._order_id_map[entrust_no] del self._order_id_map[str_order_id]
def _match(self, account, order): # TODO support tick cal env = Environment.get_instance() bar = env.get_bar(order.order_book_id) bar_status = bar._bar_status if bar_status == BAR_STATUS.ERROR: listed_date = bar.instrument.listed_date.date() if listed_date == self._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.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) self._env.event_bus.publish_event(Event(EVENT.ORDER_UNSOLICITED_UPDATE, account=account, order=order)) return if isinstance(order.style, LimitOrder): deal_price = order.style.get_limit_price() else: deal_price = bar.close deal_price = min(deal_price, bar.high) deal_price = max(deal_price, bar.low) deal_price = self._slippage_decider.get_trade_price(order.side, deal_price) if (order.side == SIDE.BUY and bar_status == BAR_STATUS.LIMIT_UP) or ( order.side == SIDE.SELL and bar_status == BAR_STATUS.LIMIT_DOWN): user_system_log.warning(_(u"You have traded {order_book_id} with {quantity} lots in {bar_status}").format( order_book_id=order.order_book_id, quantity=order.quantity, bar_status=bar_status )) ct_amount = account.portfolio.positions.get_or_create(order.order_book_id).cal_close_today_amount(order.quantity, order.side) trade = Trade.__from_create__( order_id=order.order_id, calendar_dt=self._calendar_dt, trading_dt=self._trading_dt, price=deal_price, amount=order.quantity, 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._commission_decider.get_commission(account.type, trade) trade._tax = self._tax_decider.get_tax(account.type, trade) order.fill(trade) env.event_bus.publish_event(Event(EVENT.TRADE, account=account, trade=trade))
def test_booking(self): def mock_get_instrument(order_book_id): not_delisted_ins = MagicMock() not_delisted_ins.de_listed_date = datetime.datetime.max delisted_ins = MagicMock() delisted_ins.de_listed_date = datetime.datetime.min if order_book_id == "TF1812": return delisted_ins return not_delisted_ins self.assertPositions({(POSITION_DIRECTION.LONG, "RB1812", 3, 1), (POSITION_DIRECTION.SHORT, "TF1812", 4, 0)}) self.env.event_bus.publish_event( Event(EVENT.TRADE, trade=Trade.__from_create__(0, 0, 2, SIDE.SELL, POSITION_EFFECT.OPEN, "RB1812"))) self.assertPositions({(POSITION_DIRECTION.LONG, "RB1812", 3, 1), (POSITION_DIRECTION.SHORT, "RB1812", 2, 0), (POSITION_DIRECTION.SHORT, "TF1812", 4, 0)}) self.env.event_bus.publish_event( Event(EVENT.TRADE, trade=Trade.__from_create__(0, 0, 3, SIDE.SELL, POSITION_EFFECT.CLOSE, "RB1812"))) self.assertPositions({(POSITION_DIRECTION.LONG, "RB1812", 1, 0), (POSITION_DIRECTION.SHORT, "RB1812", 2, 0), (POSITION_DIRECTION.SHORT, "TF1812", 4, 0)}) with self.mock_env_method("get_instrument", mock_get_instrument): self.env.trading_dt = datetime.datetime(2018, 8, 31) self.env.event_bus.publish_event(Event(EVENT.POST_SETTLEMENT)) self.assertPositions({ (POSITION_DIRECTION.LONG, "RB1812", 0, 1), (POSITION_DIRECTION.SHORT, "RB1812", 0, 2), })
def make_trade(cls, vnpy_trade, order_id=None): order_id = order_id if order_id is not None else next(Order.order_id_gen) calendar_dt = parse(vnpy_trade.tradeTime) trading_dt = cls.make_trading_dt(calendar_dt) price = vnpy_trade.price amount = vnpy_trade.volume side = cls.SIDE_REVERSE[vnpy_trade.direction] position_effect = cls.make_position_effect(vnpy_trade.exchange, vnpy_trade.offset) order_book_id = cls.make_order_book_id(vnpy_trade.symbol) commission = cls.get_commission(order_book_id, position_effect, price, amount) frozen_price = vnpy_trade.price return Trade.__from_create__( order_id, calendar_dt, trading_dt, price, amount, side, position_effect, order_book_id, commission=commission, frozen_price=frozen_price)
def _handle_dividend_payable(self, trading_date): # type: (date) -> float # 返回总资金的变化量 if not self._dividend_receivable: return 0 payable_date, dividend_value = self._dividend_receivable if payable_date != trading_date: return 0 self._dividend_receivable = None if self.dividend_reinvestment: last_price = self.last_price self.apply_trade(Trade.__from_create__( None, last_price, dividend_value / last_price, SIDE.BUY, POSITION_EFFECT.OPEN, self._order_book_id )) return 0 else: return dividend_value
def _handle_transform(self): if not self._pending_transform: return for predecessor, (successor, conversion_ratio) in six.iteritems(self._pending_transform): predecessor_position = self._positions.pop(predecessor) self._apply_trade(Trade.__from_create__( order_id=None, price=predecessor_position.avg_price / conversion_ratio, amount=predecessor_position.quantity * conversion_ratio, side=SIDE.BUY, position_effect=POSITION_EFFECT.OPEN, order_book_id=successor )) user_system_log.warn(_(u"{predecessor} code has changed to {successor}, change position by system").format( predecessor=predecessor, successor=successor)) self._pending_transform.clear()
def make_trade(vnpy_trade, order_id=None): order_id = order_id if order_id is not None else next(Order.order_id_gen) price = vnpy_trade.price amount = vnpy_trade.volume side = SIDE_REVERSE[vnpy_trade.direction] position_effect = make_position_effect(vnpy_trade.exchange, vnpy_trade.offset) order_book_id = make_order_book_id(vnpy_trade.symbol) commission = cal_commission(order_book_id, position_effect, price, amount) frozen_price = vnpy_trade.price return Trade.__from_create__(order_id, price, amount, side, position_effect, order_book_id, commission=commission, frozen_price=frozen_price)
def _handle_dividend_book_closure(self, trading_date): for order_book_id, position in six.iteritems(self._positions): if position.quantity == 0: continue dividend = Environment.get_instance( ).data_proxy.get_dividend_by_book_date(order_book_id, trading_date) if dividend is None: continue dividend_per_share = sum(dividend['dividend_cash_before_tax'] / dividend['round_lot']) if np.isnan(dividend_per_share): raise RuntimeError( "Dividend per share of {} is not supposed to be nan.". format(order_book_id)) position.dividend_(dividend_per_share) if StockAccount.dividend_reinvestment: last_price = position.last_price dividend_value = position.quantity * dividend_per_share self._static_total_value += dividend_value self._apply_trade( Trade.__from_create__(None, last_price, dividend_value / last_price, SIDE.BUY, POSITION_EFFECT.OPEN, order_book_id)) else: self._dividend_receivable[order_book_id] = { 'quantity': position.quantity, 'dividend_per_share': dividend_per_share, } try: self._dividend_receivable[order_book_id][ 'payable_date'] = self._int_to_date( dividend['payable_date'][0]) except ValueError: self._dividend_receivable[order_book_id][ 'payable_date'] = self._int_to_date( dividend['ex_dividend_date'][0])
def _match(self, account, order): # type: (AbstractAccount, Order) -> Iterable[Trade] if order.position_effect != POSITION_EFFECT.EXERCISE: raise NotImplementedError("match_exercise is not able to handle {} order".format(order.position_effect)) price = self._env.data_proxy.get_last_price(order.order_book_id) position = account.get_position(order.order_book_id, order.position_direction) # type: AbstractPosition quantity = min(position.closable, order.quantity) if quantity == 0: order.mark_cancelled(_(u"Order Cancelled: {} has not no exercisable quantity").format( order.order_book_id )) else: trade = Trade.__from_create__( order.order_id, price, quantity, order.side, POSITION_EFFECT.EXERCISE, order.order_book_id, right_type=order.right_type ) trade._commission = self._env.get_trade_commission(trade) trade._tax = self._env.get_trade_tax(trade) yield trade
def settlement(self, trading_date): # type: (date) -> float super(StockPosition, self).settlement(trading_date) if self.quantity == 0: return 0 if self.direction != POSITION_DIRECTION.LONG: raise RuntimeError( "direction of stock position {} is not supposed to be short". format(self._order_book_id)) next_date = self._env.data_proxy.get_next_trading_date(trading_date) instrument = self._env.data_proxy.instruments(self._order_book_id) delta_cash = 0 if instrument.de_listed_at(next_date): try: transform_data = self._env.data_proxy.get_share_transformation( self._order_book_id) except NotImplementedError: pass else: if transform_data is not None: successor, conversion_ratio = transform_data self._env.portfolio.get_account(successor).apply_trade( Trade.__from_create__( order_id=None, price=self.avg_price / conversion_ratio, amount=self.quantity * conversion_ratio, side=SIDE.BUY, position_effect=POSITION_EFFECT.OPEN, order_book_id=successor)) for direction in POSITION_DIRECTION: successor_position = self._env.portfolio.get_position( successor, direction) successor_position.update_last_price(self._last_price / conversion_ratio) # 把购买 successor 消耗的 cash 补充回来 delta_cash = self.market_value if self.cash_return_by_stock_delisted: delta_cash = self.market_value self._today_quantity = self._old_quantity = 0 return delta_cash
def _handle_dividend_payable(self, trading_date): # type: (date) -> float # 返回总资金的变化量 if not self._dividend_receivable: return 0 payable_date, dividend_value = self._dividend_receivable if payable_date != trading_date: return 0 self._dividend_receivable = None if self.dividend_reinvestment: last_price = self.last_price amount = int(Decimal(dividend_value) / Decimal(last_price)) round_lot = self._instrument.round_lot amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot if amount > 0: self.apply_trade( Trade.__from_create__(None, last_price, amount, SIDE.BUY, POSITION_EFFECT.OPEN, self._order_book_id)) return dividend_value - amount * last_price else: return dividend_value
def match(self, open_orders): price_board = self._env.price_board for account, order in open_orders: order_book_id = order.order_book_id instrument = self._env.get_instrument(order_book_id) 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._trading_dt.date(): reason = _( u"Order Cancelled: current security [{order_book_id}] can not be traded 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) continue if order.type == ORDER_TYPE.LIMIT: if order.side == SIDE.BUY and order.price < deal_price: continue if order.side == SIDE.SELL and order.price > deal_price: continue # 是否限制涨跌停不成交 if self._price_limit: if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(order_book_id): continue if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(order_book_id): continue if self._liquidity_limit: if order.side == SIDE.BUY and price_board.get_a1(order_book_id) == 0: continue if order.side == SIDE.SELL and price_board.get_b1(order_book_id) == 0: continue 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) continue 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) continue 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) continue 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) continue if self._volume_limit: bar = self._env.bar_dict[order_book_id] volume_limit = round(bar.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) continue unfilled = order.unfilled_quantity fill = min(unfilled, volume_limit) else: fill = order.unfilled_quantity ct_amount = account.positions.get_or_create(order.order_book_id).cal_close_today_amount(fill, order.side) 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(account_type_str2enum(account.type), trade) trade._tax = self._env.get_trade_tax(account_type_str2enum(account.type), 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, open_orders): for account, order in open_orders: bar = self._board[order.order_book_id] bar_status = bar._bar_status if bar_status == BAR_STATUS.ERROR: listed_date = bar.instrument.listed_date.date() if listed_date == self._trading_dt.date(): reason = _( u"Order Cancelled: current security [{order_book_id}] can not be traded 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) continue deal_price = self._deal_price_decider(bar) if order.type == ORDER_TYPE.LIMIT: if order.side == SIDE.BUY and order.price < deal_price: continue if order.side == SIDE.SELL and order.price > deal_price: continue else: if self._bar_limit and order.side == SIDE.BUY and bar_status == BAR_STATUS.LIMIT_UP: 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) continue elif self._bar_limit and order.side == SIDE.SELL and bar_status == BAR_STATUS.LIMIT_DOWN: 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) continue if self._bar_limit: if order.side == SIDE.BUY and bar_status == BAR_STATUS.LIMIT_UP: continue if order.side == SIDE.SELL and bar_status == BAR_STATUS.LIMIT_DOWN: continue volume_limit = round( bar.volume * self._volume_percent) - self._turnover[order.order_book_id] round_lot = bar.instrument.round_lot volume_limit = (volume_limit // round_lot) * round_lot if volume_limit <= 0: if order.type == ORDER_TYPE.MARKET: reason = _( 'Order Cancelled: market order {order_book_id} volume {order_volume}' ' due to volume limit').format( order_book_id=order.order_book_id, order_volume=order.quantity) order.mark_cancelled(reason) continue unfilled = order.unfilled_quantity fill = min(unfilled, volume_limit) ct_amount = account.positions.get_or_create( order.order_book_id).cal_close_today_amount(fill, order.side) price = self._slippage_decider.get_trade_price( order.side, deal_price) trade = Trade.__from_create__( order_id=order.order_id, calendar_dt=self._calendar_dt, trading_dt=self._trading_dt, 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._commission_decider.get_commission( account.type, trade) trade._tax = self._tax_decider.get_tax(account.type, trade) order.fill(trade) self._turnover[order.order_book_id] += fill Environment.get_instance().event_bus.publish_event( Event(EVENT.TRADE, account=account, trade=trade)) if order.type == ORDER_TYPE.MARKET and order.unfilled_quantity != 0: reason = _( "Order Cancelled: market order {order_book_id} volume {order_volume} is" " larger than 25 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) order.mark_cancelled(reason)
def _fake_trade(order_book_id, quantity, price): return Trade.__from_create__(0, price, abs(quantity), SIDE.BUY if quantity > 0 else SIDE.SELL, POSITION_EFFECT.OPEN, order_book_id)
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 if order.position_effect == POSITION_EFFECT.EXERCISE: 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._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 _create_trade(self, obid, quantity, side, position_effect): trade = Trade.__from_create__(0, 0, quantity, side, position_effect, obid) return trade
def match(self, open_orders): price_board = self._env.price_board for account, order in open_orders: order_book_id = order.order_book_id instrument = self._env.get_instrument(order_book_id) if np.isnan(price_board.get_last_price(order_book_id)): listed_date = instrument.listed_date.date() if listed_date == self._trading_dt.date(): reason = _( u"Order Cancelled: current security [{order_book_id}] can not be traded 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) continue deal_price = self._deal_price_decider(order_book_id, order.side) if order.type == ORDER_TYPE.LIMIT: if order.side == SIDE.BUY and order.price < deal_price: continue if order.side == SIDE.SELL and order.price > deal_price: continue # 是否限制涨跌停不成交 if self._price_limit: if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up( order_book_id): continue if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down( order_book_id): continue if self._liquidity_limit: if order.side == SIDE.BUY and price_board.get_a1( order_book_id) == 0: continue if order.side == SIDE.SELL and price_board.get_b1( order_book_id) == 0: continue 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) continue 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) continue 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) continue 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) continue if self._volume_limit: bar = self._env.bar_dict[order_book_id] volume_limit = round( bar.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) continue unfilled = order.unfilled_quantity fill = min(unfilled, volume_limit) else: fill = order.unfilled_quantity ct_amount = account.positions.get_or_create( order.order_book_id).cal_close_today_amount(fill, order.side) price = self._slippage_decider.get_trade_price( order.side, deal_price) trade = Trade.__from_create__( order_id=order.order_id, calendar_dt=self._calendar_dt, trading_dt=self._trading_dt, 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._commission_decider.get_commission( account.type, trade) trade._tax = self._tax_decider.get_tax(account.type, trade) order.fill(trade) self._turnover[order.order_book_id] += fill self._env.event_bus.publish_event( Event(EVENT.TRADE, account=account, trade=trade)) 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 25 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) 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 np.isnan(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=order)) return if order.type == ORDER_TYPE.LIMIT: deal_price = order.frozen_price else: deal_price = last_price if self._price_limit: """ 在 Signal 模式下,不再阻止涨跌停是否买进,price_limit 参数表示是否给出警告提示。 """ if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up( order_book_id): user_system_log.warning( _(u"You have traded {order_book_id} with {quantity} lots in {bar_status}" ).format(order_book_id=order_book_id, quantity=order.quantity, bar_status=BAR_STATUS.LIMIT_UP)) return if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down( order_book_id): user_system_log.warning( _(u"You have traded {order_book_id} with {quantity} lots in {bar_status}" ).format(order_book_id=order_book_id, quantity=order.quantity, bar_status=BAR_STATUS.LIMIT_DOWN)) return ct_amount = account.positions.get_or_create( order_book_id).cal_close_today_amount(order.quantity, order.side) trade_price = self._slippage_decider.get_trade_price( order.side, deal_price) trade = Trade.__from_create__(order_id=order.order_id, calendar_dt=self._env.calendar_dt, trading_dt=self._env.trading_dt, 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._commission_decider.get_commission( account.type, trade) trade._tax = self._tax_decider.get_tax(account.type, trade) order.fill(trade) self._env.event_bus.publish_event( Event(EVENT.TRADE, account=account, trade=trade))
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: """ 在 Signal 模式下,不再阻止涨跌停是否买进,price_limit 参数表示是否给出警告提示。 """ if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(order_book_id): user_system_log.warning(_(u"You have traded {order_book_id} with {quantity} lots in {bar_status}").format( order_book_id=order_book_id, quantity=order.quantity, bar_status=BAR_STATUS.LIMIT_UP )) return if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(order_book_id): user_system_log.warning(_(u"You have traded {order_book_id} with {quantity} lots in {bar_status}").format( order_book_id=order_book_id, quantity=order.quantity, bar_status=BAR_STATUS.LIMIT_DOWN )) return ct_amount = account.positions.get_or_create(order_book_id).cal_close_today_amount(order.quantity, order.side) trade_price = self._slippage_decider.get_trade_price(order.side, 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._commission_decider.get_commission(account.type, trade) trade._tax = self._tax_decider.get_tax(account.type, 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)