def get_trade_price(self, order, price): side = order.side temp_price = price + price * self.rate * (1 if side == SIDE.BUY else -1) temp_bar = Environment.get_instance().bar_dict[order.order_book_id] limit_up, limit_down = temp_bar.limit_up, temp_bar.limit_down if is_valid_price(limit_up): temp_price = min(temp_price, limit_up) if is_valid_price(limit_down): temp_price = max(temp_price, limit_down) return temp_price
def order_value(id_or_ins, cash_amount, price=None, style=None): """ 使用想要花费的金钱买入/卖出股票,而不是买入/卖出想要的股数,正数代表买入,负数代表卖出。股票的股数总是会被调整成对应的100的倍数(在A中国A股市场1手是100股)。当您提交一个卖单时,该方法代表的意义是您希望通过卖出该股票套现的金额。如果金额超出了您所持有股票的价值,那么您将卖出所有股票。需要注意,如果资金不足,该API将不会创建发送订单。 :param id_or_ins: 下单标的物 :type id_or_ins: :class:`~Instrument` object | `str` :param float cash_amount: 需要花费现金购买/卖出证券的数目。正数代表买入,负数代表卖出。 :param float price: 下单价格,默认为None,表示 :class:`~MarketOrder`, 此参数主要用于简化 `style` 参数。 :param style: 下单类型, 默认是市价单。目前支持的订单类型有 :class:`~LimitOrder` 和 :class:`~MarketOrder` :type style: `OrderStyle` object :return: :class:`~Order` object | None :example: .. code-block:: python #买入价值¥10000的平安银行股票,并以市价单发送。如果现在平安银行股票的价格是¥7.5,那么下面的代码会买入1300股的平安银行,因为少于100股的数目将会被自动删除掉: order_value('000001.XSHE', 10000) #卖出价值¥10000的现在持有的平安银行: order_value('000001.XSHE', -10000) """ style = cal_style(price, style) if isinstance(style, LimitOrder): if style.get_limit_price() <= 0: raise RQInvalidArgument(_(u"Limit order price should be positive")) order_book_id = assure_stock_order_book_id(id_or_ins) env = Environment.get_instance() price = env.get_last_price(order_book_id) if not is_valid_price(price): user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)) return account = env.portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK.name] if cash_amount > 0: cash_amount = min(cash_amount, account.cash) if isinstance(style, MarketOrder): amount = int(Decimal(cash_amount) / Decimal(price)) else: amount = int(Decimal(cash_amount) / Decimal(style.get_limit_price())) # if the cash_amount is larger than you current security’s position, # then it will sell all shares of this security. position = account.positions[order_book_id] amount = downsize_amount(amount, position) return order_shares(order_book_id, amount, style=style)
def submit_order(id_or_ins, amount, side, price=None, position_effect=None): """ 通用下单函数,策略可以通过该函数自由选择参数下单。 :param id_or_ins: 下单标的物 :type id_or_ins: :class:`~Instrument` object | `str` :param float amount: 下单量,需为正数 :param side: 多空方向,多(SIDE.BUY)或空(SIDE.SELL) :type side: :class:`~SIDE` enum :param float price: 下单价格,默认为None,表示市价单 :param position_effect: 开平方向,开仓(POSITION_EFFECT.OPEN),平仓(POSITION.CLOSE)或平今(POSITION_EFFECT.CLOSE_TODAY),交易股票不需要该参数 :type position_effect: :class:`~POSITION_EFFECT` enum :return: :class:`~Order` object | None :example: .. code-block:: python # 购买 2000 股的平安银行股票,并以市价单发送: submit_order('000001.XSHE', 2000, SIDE.BUY) # 平 10 份 RB1812 多方向的今仓,并以 4000 的价格发送限价单 submit_order('RB1812', 10, SIDE.SELL, price=4000, position_effect=POSITION_EFFECT.CLOSE_TODAY) """ order_book_id = assure_order_book_id(id_or_ins) env = Environment.get_instance() if env.config.base.run_type != RUN_TYPE.BACKTEST: if "88" in order_book_id: raise RQInvalidArgument(_(u"Main Future contracts[88] are not supported in paper trading.")) if "99" in order_book_id: raise RQInvalidArgument(_(u"Index Future contracts[99] are not supported in paper trading.")) style = cal_style(price, None) market_price = env.get_last_price(order_book_id) if not is_valid_price(market_price): user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id) ) return order = Order.__from_create__( order_book_id=order_book_id, quantity=amount, side=side, style=style, position_effect=position_effect ) if order.type == ORDER_TYPE.MARKET: order.set_frozen_price(market_price) if env.can_submit_order(order): env.broker.submit_order(order) return order
def _on_bar(self, event): # run once if len(self._positions) == 0: price = event.bar_dict[self.benchmark].close if not is_valid_price(price): return position = self._positions.get_or_create(self.benchmark) quantity = self._total_cash / price position._quantity = quantity position._avg_price = price self._total_cash -= quantity * price
def apply_settlement(self): env = Environment.get_instance() settle_price = env.data_proxy.get_settle_price(self.order_book_id, env.trading_dt) if not is_valid_price(settle_price): raise RuntimeError( _("Settlement price {settle_price} of {order_book_id} is invalid" .format(settle_price=settle_price, order_book_id=self.order_book_id))) self._buy_old_holding_list = [(settle_price, self.buy_quantity)] self._sell_old_holding_list = [(settle_price, self.sell_quantity)] self._buy_today_holding_list = [] self._sell_today_holding_list = [] self._buy_transaction_cost = 0. self._sell_transaction_cost = 0. self._buy_realized_pnl = 0. self._sell_realized_pnl = 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: """ 在 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, 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( 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=copy(order)))
def order_shares(id_or_ins, amount, price=None, style=None): """ 落指定股数的买/卖单,最常见的落单方式之一。如有需要落单类型当做一个参量传入,如果忽略掉落单类型,那么默认是市价单(market order)。 :param id_or_ins: 下单标的物 :type id_or_ins: :class:`~Instrument` object | `str` :param int amount: 下单量, 正数代表买入,负数代表卖出。将会根据一手xx股来向下调整到一手的倍数,比如中国A股就是调整成100股的倍数。 :param float price: 下单价格,默认为None,表示 :class:`~MarketOrder`, 此参数主要用于简化 `style` 参数。 :param style: 下单类型, 默认是市价单。目前支持的订单类型有 :class:`~LimitOrder` 和 :class:`~MarketOrder` :type style: `OrderStyle` object :return: :class:`~Order` object | None :example: .. code-block:: python #购买Buy 2000 股的平安银行股票,并以市价单发送: order_shares('000001.XSHE', 2000) #卖出2000股的平安银行股票,并以市价单发送: order_shares('000001.XSHE', -2000) #购买1000股的平安银行股票,并以限价单发送,价格为¥10: order_shares('000001.XSHG', 1000, style=LimitOrder(10)) """ if amount == 0: # 如果下单量为0,则认为其并没有发单,则直接返回None user_system_log.warn(_(u"Order Creation Failed: Order amount is 0.")) return None style = cal_style(price, style) if isinstance(style, LimitOrder): if style.get_limit_price() <= 0: raise RQInvalidArgument(_(u"Limit order price should be positive")) order_book_id = assure_stock_order_book_id(id_or_ins) env = Environment.get_instance() price = env.get_last_price(order_book_id) if not is_valid_price(price): user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)) return if amount > 0: side = SIDE.BUY position_effect = POSITION_EFFECT.OPEN else: amount = abs(amount) side = SIDE.SELL position_effect = POSITION_EFFECT.CLOSE round_lot = int(env.get_instrument(order_book_id).round_lot) if side == SIDE.BUY or amount != env.portfolio.accounts[ DEFAULT_ACCOUNT_TYPE.STOCK.name ].positions[order_book_id].sellable: # 一次性申报卖出时可以卖散股 try: amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot except ValueError: amount = 0 r_order = Order.__from_create__(order_book_id, amount, side, style, position_effect) if amount == 0: # 如果计算出来的下单量为0, 则不生成Order, 直接返回None # 因为很多策略会直接在handle_bar里面执行order_target_percent之类的函数,经常会出现下一个量为0的订单,如果这些订单都生成是没有意义的。 user_system_log.warn(_(u"Order Creation Failed: 0 order quantity")) return if r_order.type == ORDER_TYPE.MARKET: r_order.set_frozen_price(price) if env.can_submit_order(r_order): env.broker.submit_order(r_order) return r_order
def order(id_or_ins, amount, side, position_effect, style): if not isinstance(style, OrderStyle): raise RuntimeError if amount < 0: raise RuntimeError if amount == 0: user_system_log.warn(_(u"Order Creation Failed: Order amount is 0.")) return None if isinstance(style, LimitOrder) and style.get_limit_price() <= 0: raise RQInvalidArgument(_(u"Limit order price should be positive")) order_book_id = assure_future_order_book_id(id_or_ins) env = Environment.get_instance() if env.config.base.run_type != RUN_TYPE.BACKTEST: if "88" in order_book_id: raise RQInvalidArgument( _(u"Main Future contracts[88] are not supported in paper trading." )) if "99" in order_book_id: raise RQInvalidArgument( _(u"Index Future contracts[99] are not supported in paper trading." )) price = env.get_last_price(order_book_id) if not is_valid_price(price): user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] No market data"). format(order_book_id=order_book_id)) return amount = int(amount) position = Environment.get_instance().portfolio.positions[order_book_id] orders = [] if position_effect == POSITION_EFFECT.CLOSE: if side == SIDE.BUY: # 如果平仓量大于持仓量,则 Warning 并 取消订单创建 if amount > position.sell_quantity: user_system_log.warn( _(u"Order Creation Failed: close amount {amount} is larger than position " u"quantity {quantity}").format( amount=amount, quantity=position.sell_quantity)) return [] sell_old_quantity = position.sell_old_quantity if amount > sell_old_quantity: if sell_old_quantity != 0: # 如果有昨仓,则创建一个 POSITION_EFFECT.CLOSE 的平仓单 orders.append( Order.__from_create__(order_book_id, sell_old_quantity, side, style, POSITION_EFFECT.CLOSE)) # 剩下还有仓位,则创建一个 POSITION_EFFECT.CLOSE_TODAY 的平今单 orders.append( Order.__from_create__(order_book_id, amount - sell_old_quantity, side, style, POSITION_EFFECT.CLOSE_TODAY)) else: # 创建 POSITION_EFFECT.CLOSE 的平仓单 orders.append( Order.__from_create__(order_book_id, amount, side, style, POSITION_EFFECT.CLOSE)) else: if amount > position.buy_quantity: user_system_log.warn( _(u"Order Creation Failed: close amount {amount} is larger than position " u"quantity {quantity}").format( amount=amount, quantity=position.buy_quantity)) return [] buy_old_quantity = position.buy_old_quantity if amount > buy_old_quantity: if buy_old_quantity != 0: orders.append( Order.__from_create__(order_book_id, buy_old_quantity, side, style, POSITION_EFFECT.CLOSE)) orders.append( Order.__from_create__(order_book_id, amount - buy_old_quantity, side, style, POSITION_EFFECT.CLOSE_TODAY)) else: orders.append( Order.__from_create__(order_book_id, amount, side, style, POSITION_EFFECT.CLOSE)) else: orders.append( Order.__from_create__(order_book_id, amount, side, style, position_effect)) if not is_valid_price(price): user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] No market data"). format(order_book_id=order_book_id)) return [] for o in orders: if o.type == ORDER_TYPE.MARKET: o.set_frozen_price(price) if env.can_submit_order(o): env.broker.submit_order(o) else: orders.remove(o) # 向前兼容,如果创建的order_list 只包含一个订单的话,直接返回对应的订单,否则返回列表 if len(orders) == 1: return orders[0] else: return orders
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)