def order_shares(id_or_ins, amount, price=None, style=None): order_book_id = assure_order_book_id(id_or_ins) env = Environment.get_instance() 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) try: amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot except ValueError: amount = 0 order = Order.__from_create__(order_book_id, amount, side, style, position_effect) if amount == 0: # 如果计算出来的下单量为0, 则不生成Order, 直接返回None # 因为很多策略会直接在handle_bar里面执行order_target_percent之类的函数,经常会出现下一个量为0的订单,如果这些订单都生成是没有意义的。 order.mark_rejected(_(u"Order Creation Failed: 0 order quantity")) return order env.broker.submit_order(order) return order
def submit_order(id_or_ins, amount, side, price=None, position_effect=None): 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 _sell_all_stock(order_book_id, amount, style): env = Environment.get_instance() order = Order.__from_create__(order_book_id, amount, SIDE.SELL, style, None) if amount == 0: order.mark_rejected(_(u"Order Creation Failed: 0 order quantity")) return order if env.can_submit_order(order): env.broker.submit_order(order) return order
def _sell_all_stock(order_book_id, amount, style): env = Environment.get_instance() order = Order.__from_create__(order_book_id, amount, SIDE.SELL, style, POSITION_EFFECT.CLOSE) if amount == 0: user_system_log.warn(_(u"Order Creation Failed: 0 order quantity")) return if env.can_submit_order(order): env.broker.submit_order(order) return order
def set_state(self, state): self._open_orders = [] self._delayed_orders = [] value = jsonpickle.loads(state.decode('utf-8')) for v in value['open_orders']: o = Order() o.set_state(v) account = self._env.get_account(o.order_book_id) self._open_orders.append((account, o)) for v in value['delayed_orders']: o = Order() o.set_state(v) account = self._env.get_account(o.order_book_id) self._delayed_orders.append((account, o))
def make_order_from_vnpy_trade(vnpy_trade): order_book_id = make_order_book_id(vnpy_trade.symbol) quantity = vnpy_trade.volume side = SIDE_REVERSE[vnpy_trade.direction] style = LimitOrder(vnpy_trade.price) position_effect = make_position_effect(vnpy_trade.exchange, vnpy_trade.offset) order = Order.__from_create__(order_book_id, quantity, side, style, position_effect) order._filled_quantity = vnpy_trade.volume order._status = ORDER_STATUS.FILLED order._avg_price = vnpy_trade.price order._transaction_cost = 0 return order
def make_order(vnpy_order): order_book_id = make_order_book_id(vnpy_order.symbol) quantity = vnpy_order.totalVolume side = SIDE_REVERSE[vnpy_order.direction] style = LimitOrder(vnpy_order.price) position_effect = make_position_effect(vnpy_order.exchange, vnpy_order.offset) order_id = vnpy_order.orderID order = Order.__from_create__(order_book_id, quantity, side, style, position_effect) order._filled_quantity = vnpy_order.totalVolume order._order_id = order_id return order
def make_order_from_vnpy_trade(cls, vnpy_trade): calendar_dt = parse(vnpy_trade.tradeTime) trading_dt = cls.make_trading_dt(calendar_dt) order_book_id = cls.make_order_book_id(vnpy_trade.symbol) quantity = vnpy_trade.volume side = cls.SIDE_REVERSE[vnpy_trade.direction] style = LimitOrder(vnpy_trade.price) position_effect = cls.make_position_effect(vnpy_trade.exchange, vnpy_trade.offset) order = Order.__from_create__(calendar_dt, trading_dt, order_book_id, quantity, side, style, position_effect) order._filled_quantity = vnpy_trade.volume order._status = ORDER_STATUS.FILLED order._avg_price = vnpy_trade.price # FIXME: 用近似值代替 order._transaction_cost = 0 return order
def _order_shares(order_book_id, amount, style, auto_switch_order_value): 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 if side == SIDE.BUY: # 卖出不再限制 round_lot, order_shares 不再依赖 portfolio round_lot = int(env.get_instrument(order_book_id).round_lot) 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) reject_validator_type = env.validate_order_submission(r_order) if not reject_validator_type: env.broker.submit_order(r_order) return r_order else: if auto_switch_order_value and reject_validator_type == FRONT_VALIDATOR_TYPE.CASH: remaining_cash = env.portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK.name].cash user_system_log.warn(_( "Insufficient cash, use all remaining cash({}) to create order").format(remaining_cash) ) return _order_value(order_book_id, remaining_cash, style)
def set_state(self, state): value = jsonpickle.loads(state.decode('utf-8')) for v in value['open_orders']: o = Order() o.set_state(v) account = self._env.get_account(o.order_book_id) self._open_orders.append((account, o)) for v in value['delayed_orders']: o = Order() o.set_state(v) account = self._env.get_account(o.order_book_id) self._delayed_orders.append((account, o))
def _order_value(account, position, ins, cash_amount, style): env = Environment.get_instance() if cash_amount > 0: cash_amount = min(cash_amount, account.cash) if isinstance(style, LimitOrder): price = style.get_limit_price() else: price = env.data_proxy.get_last_price(ins.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=ins.order_book_id)) return amount = int(Decimal(cash_amount) / Decimal(price)) min_round_lot = int(ins.round_lot) if cash_amount > 0: if _is_ksh(ins): amount = _get_ksh_amount(amount) min_round_lot = 1 else: amount = int( Decimal(amount) / Decimal(min_round_lot)) * min_round_lot while amount > 0: expected_transaction_cost = env.get_order_transaction_cost( Order.__from_create__(ins.order_book_id, amount, SIDE.BUY, LimitOrder(price), POSITION_EFFECT.OPEN)) if amount * price + expected_transaction_cost <= cash_amount: break amount -= min_round_lot else: user_system_log.warn( _(u"Order Creation Failed: 0 order quantity, order_book_id={order_book_id}" ).format(order_book_id=ins.order_book_id)) return if amount < 0: amount = max(amount, -position.closable) return _order_shares(ins, amount, style, position.quantity, auto_switch_order_value=False)
def _order_value(order_book_id, cash_amount, style): 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) price = price if isinstance(style, MarketOrder) else style.get_limit_price() amount = int(Decimal(cash_amount) / Decimal(price)) if cash_amount > 0: round_lot = int(env.get_instrument(order_book_id).round_lot) # FIXME: logic duplicate with order_shares amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot while amount > 0: dummy_order = Order.__from_create__(order_book_id, amount, SIDE.BUY, LimitOrder(price), POSITION_EFFECT.OPEN) expected_transaction_cost = env.get_order_transaction_cost(dummy_order) if amount * price + expected_transaction_cost <= cash_amount: break amount -= round_lot else: user_system_log.warn(_(u"Order Creation Failed: 0 order quantity")) return # if the cash_amount is larger than you current security’s position, # then it will sell all shares of this security. position = account.get_position(order_book_id, POSITION_DIRECTION.LONG) amount = downsize_amount(amount, position) return _order_shares(order_book_id, amount, style, auto_switch_order_value=False)
def _submit_order(ins, amount, side, position_effect, style, quantity, auto_switch_order_value): env = Environment.get_instance() if isinstance(style, LimitOrder): if style.get_limit_price() <= 0: raise RQInvalidArgument(_(u"Limit order price should be positive")) price = env.data_proxy.get_last_price(ins.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=ins.order_book_id)) return round_lot = int(ins.round_lot) if side in [SIDE.BUY, side.SELL]: if not (side == SIDE.SELL and quantity == abs(amount)): if _is_ksh(ins): # KSH can buy(sell) 201, 202 shares amount = _get_ksh_amount(amount) else: amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot if amount == 0: user_system_log.warn( _(u"Order Creation Failed: 0 order quantity, order_book_id={order_book_id}" ).format(order_book_id=ins.order_book_id)) return order = Order.__from_create__(ins.order_book_id, abs(amount), side, style, position_effect) if order.type == ORDER_TYPE.MARKET: order.set_frozen_price(price) if side == SIDE.BUY and auto_switch_order_value: account, position, ins = _get_account_position_ins(ins) if not is_cash_enough(env, order, account): user_system_log.warn( _("insufficient cash, use all remaining cash({}) to create order" ).format(account.cash)) return _order_value(account, position, ins, account.cash, style) if env.can_submit_order(order): env.broker.submit_order(order) return order
def exercise(id_or_ins, amount, right_type=RIGHT_TYPE.SELL_BACK): # type: (Union[str, Instrument], Union[int, float], Optional[RIGHT_TYPE]) -> Optional[Order] """ 行权。针对期权、可转债等含权合约,行使合约权利方被赋予的权利。 :param id_or_ins: 行权合约,order_book_id 或 Instrument 对象 :param amount: 参与行权的合约数量 :param right_type: 权利类型,对于含有多种权利的合约(如可转债),选择行使何种权利 :example: .. code-block:: python # 行使一张豆粕1905购2350的权力 exercise("M1905C2350", 1) """ amount = int(amount) if amount <= 0: user_system_log.warn( _(u"Order Creation Failed: Order amount should be positive.")) return None if right_type != RIGHT_TYPE.SELL_BACK: raise NotImplementedError(_("exercise only supports sell back now")) env = Environment.get_instance() if isinstance(id_or_ins, Instrument): order_book_id = id_or_ins.order_book_id else: order_book_id = env.data_proxy.instruments(id_or_ins).order_book_id order = Order.__from_create__(order_book_id, amount, SIDE.SELL, None, POSITION_EFFECT.EXERCISE, right_type=right_type) if env.can_submit_order(order): env.broker.submit_order(order) return order
def order_value(id_or_ins, cash_amount, price=None, style=None): """ 使用想要花费的金钱买入/卖出股票,而不是买入/卖出想要的股数,正数代表买入,负数代表卖出。股票的股数总是会被调整成对应的100的倍数(在A中国A股市场1手是100股)。如果资金不足,该API将不会创建发送订单。 需要注意: 当您提交一个买单时,cash_amount 代表的含义是您希望买入股票消耗的金额(包含税费),最终买入的股数不仅和发单的价格有关,还和税费相关的参数设置有关。 当您提交一个卖单时,cash_amount 代表的意义是您希望卖出股票的总价值。如果金额超出了您所持有股票的价值,那么您将卖出所有股票。 :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买入平安银行股票,并以市价单发送。具体下单的数量与您策略税费相关的配置有关。 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) price = price if isinstance(style, MarketOrder) else style.get_limit_price() amount = int(Decimal(cash_amount) / Decimal(price)) if cash_amount > 0: round_lot = int(env.get_instrument(order_book_id).round_lot) # FIXME: logic duplicate with order_shares amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot while amount > 0: dummy_order = Order.__from_create__(order_book_id, amount, SIDE.BUY, style, POSITION_EFFECT.OPEN) expected_transaction_cost = env.get_order_transaction_cost(DEFAULT_ACCOUNT_TYPE.STOCK, dummy_order) if amount * price + expected_transaction_cost <= cash_amount: break amount -= round_lot else: user_system_log.warn(_(u"Order Creation Failed: 0 order quantity")) return # 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 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 np.isnan(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.sell_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 np.isnan(price) or price == 0: user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] No market data"). format(order_book_id=order_book_id)) for o in orders: o.mark_rejected( _(u"Order Creation Failed: [{order_book_id}] No market data"). format(order_book_id=order_book_id)) return orders 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) # 向前兼容,如果创建的order_list 只包含一个订单的话,直接返回对应的订单,否则返回列表 if len(orders) == 1: return orders[0] else: return orders
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 and env.get_instrument(order_book_id).type == "Future" ): 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 amount = int(amount) 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 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 np.isnan(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.sell_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 np.isnan(price) or price == 0: user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)) for o in orders: o.mark_rejected( _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)) return orders 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) # 向前兼容,如果创建的order_list 只包含一个订单的话,直接返回对应的订单,否则返回列表 if len(orders) == 1: return orders[0] else: return orders
def _submit_order(id_or_ins, amount, side, position_effect, style): amount = int(amount) 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")) instrument = assure_instrument(id_or_ins) order_book_id = instrument.order_book_id env = Environment.get_instance() if env.config.base.run_type != RUN_TYPE.BACKTEST and instrument.type == INSTRUMENT_TYPE.FUTURE: 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 env = Environment.get_instance() orders = [] if position_effect in (POSITION_EFFECT.CLOSE_TODAY, POSITION_EFFECT.CLOSE): direction = POSITION_DIRECTION.LONG if side == SIDE.SELL else POSITION_DIRECTION.SHORT position = env.portfolio.get_position(order_book_id, direction) # type: Position if position_effect == POSITION_EFFECT.CLOSE_TODAY: if amount > position.today_closable: user_system_log.warning( _("Order Creation Failed: " "close today amount {amount} is larger than today closable quantity {quantity}" ).format(amount=amount, quantity=position.today_closable)) return [] orders.append( Order.__from_create__(order_book_id, amount, side, style, POSITION_EFFECT.CLOSE_TODAY)) else: quantity, old_quantity = position.quantity, position.old_quantity if amount > quantity: user_system_log.warn( _(u"Order Creation Failed: close amount {amount} is larger than position quantity {quantity}" ).format(amount=amount, quantity=quantity)) return [] if amount > old_quantity: if old_quantity != 0: # 如果有昨仓,则创建一个 POSITION_EFFECT.CLOSE 的平仓单 orders.append( Order.__from_create__(order_book_id, old_quantity, side, style, POSITION_EFFECT.CLOSE)) # 剩下还有仓位,则创建一个 POSITION_EFFECT.CLOSE_TODAY 的平今单 orders.append( Order.__from_create__(order_book_id, amount - 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)) elif position_effect == POSITION_EFFECT.OPEN: orders.append( Order.__from_create__(order_book_id, amount, side, style, position_effect)) else: raise NotImplementedError() if len(orders) > 1: user_system_log.warn( _("Order was separated, original order: {original_order_repr}, new orders: [{new_orders_repr}]" ). format( original_order_repr= "Order(order_book_id={}, quantity={}, side={}, position_effect={})" .format(order_book_id, amount, side, position_effect), new_orders_repr=", ".join([ "Order({}, {}, {}, {})".format(o.order_book_id, o.quantity, o.side, o.position_effect) for o in orders ]))) 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 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 :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 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 else: amount = abs(amount) side = SIDE.SELL round_lot = int(env.get_instrument(order_book_id).round_lot) 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, None) if price == 0: user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)) r_order.mark_rejected( _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)) return r_order if amount == 0: # 如果计算出来的下单量为0, 则不生成Order, 直接返回None # 因为很多策略会直接在handle_bar里面执行order_target_percent之类的函数,经常会出现下一个量为0的订单,如果这些订单都生成是没有意义的。 r_order.mark_rejected(_(u"Order Creation Failed: 0 order quantity")) return r_order 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 send_order(order_book_id, amount, side, position_effect, style): env = Environment.get_instance() order = Order.__from_create__(order_book_id, amount, side, style, position_effect) env.broker.submit_order(order) return 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 :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)) """ orig_amount = amount if amount == 0: # 如果下单量为0,则认为其并没有发单,则直接返回None 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 ): # fixme if the security was not traded yesterday,it won't be traded ? --- this requires env.get_last_price() would not skip suspended price print('dt,last', env.price_board._bar_dict.dt, env.price_board._bar_dict[order_book_id]) user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] last price {price}"). format(order_book_id=order_book_id, price=price)) return if amount > 0: side = SIDE.BUY else: amount = abs(amount) side = SIDE.SELL round_lot = int(env.get_instrument(order_book_id).round_lot) 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, None) if price == 0: user_system_log.warn( _(u"Order Creation Failed: [{order_book_id}] last price is 0"). format(order_book_id=order_book_id)) r_order.mark_rejected( _(u"Order Creation Failed: [{order_book_id}] last price is 0"). format(order_book_id=order_book_id)) return r_order if amount == 0: # 如果计算出来的下单量为0, 则不生成Order, 直接返回None # 因为很多策略会直接在handle_bar里面执行order_target_percent之类的函数,经常会出现下一个量为0的订单,如果这些订单都生成是没有意义的。 r_order.mark_rejected( _(u"Order Creation Failed: [{order_book_id}] {orig_amount} order quantity. round_lot {round_lot}" ).format(order_book_id=order_book_id, orig_amount=round(orig_amount, 5), round_lot=round_lot)) return r_order 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_value(id_or_ins, cash_amount, price=None, style=None): """ 使用想要花费的金钱买入/卖出股票,而不是买入/卖出想要的股数,正数代表买入,负数代表卖出。股票的股数总是会被调整成对应的100的倍数(在A中国A股市场1手是100股)。如果资金不足,该API将不会创建发送订单。 需要注意: 当您提交一个买单时,cash_amount 代表的含义是您希望买入股票消耗的金额(包含税费),最终买入的股数不仅和发单的价格有关,还和税费相关的参数设置有关。 当您提交一个卖单时,cash_amount 代表的意义是您希望卖出股票的总价值。如果金额超出了您所持有股票的价值,那么您将卖出所有股票。 :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买入平安银行股票,并以市价单发送。具体下单的数量与您策略税费相关的配置有关。 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) price = price if isinstance(style, MarketOrder) else style.get_limit_price() amount = int(Decimal(cash_amount) / Decimal(price)) if cash_amount > 0: round_lot = int(env.get_instrument(order_book_id).round_lot) # FIXME: logic duplicate with order_shares amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot while amount > 0: dummy_order = Order.__from_create__(order_book_id, amount, SIDE.BUY, LimitOrder(price), POSITION_EFFECT.OPEN) expected_transaction_cost = env.get_order_transaction_cost(DEFAULT_ACCOUNT_TYPE.STOCK, dummy_order) if amount * price + expected_transaction_cost <= cash_amount: break amount -= round_lot else: user_system_log.warn(_(u"Order Creation Failed: 0 order quantity")) return # 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 order_target_portfolio(target_portfolio): # type: (Dict[Union[str, Instrument]: float]) -> List[Order] """ 买入/卖出证券以批量调整证券的仓位,以期使其持仓市值占账户总权益的比重达到指定值。 :param target_portfolio: 下单标的物及其目标市值占比的字典 :example: .. code-block:: python # 调整仓位,以使平安银行和万科 A 的持仓占比分别达到 10% 和 15% order_target_portfolio({ '000001.XSHE': 0.1 '000002.XSHE': 0.15 }) """ if isinstance(target_portfolio, pd.Series): # FIXME: kind of dirty total_percent = sum(target_portfolio) else: total_percent = sum(six.itervalues(target_portfolio)) if total_percent > 1: raise RQInvalidArgument(_(u"total percent should be lower than 1, current: {}").format(total_percent)) env = Environment.get_instance() account = env.portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK] account_value = account.total_value target_quantities = {} for id_or_ins, target_percent in six.iteritems(target_portfolio): order_book_id = assure_stock_order_book_id(id_or_ins) if target_percent < 0: raise RQInvalidArgument(_(u"target percent of {} should between 0 and 1, current: {}").format( order_book_id, target_percent )) price = env.data_proxy.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) ) continue target_quantities[order_book_id] = account_value * target_percent / price close_orders, open_orders = [], [] current_quantities = { p.order_book_id: p.quantity for p in account.get_positions() if p.direction == POSITION_DIRECTION.LONG } for order_book_id, quantity in six.iteritems(current_quantities): if order_book_id not in target_portfolio: close_orders.append(Order.__from_create__( order_book_id, quantity, SIDE.SELL, MarketOrder(), POSITION_EFFECT.CLOSE )) round_lot = 100 for order_book_id, target_quantity in six.iteritems(target_quantities): if order_book_id in current_quantities: delta_quantity = target_quantity - current_quantities[order_book_id] else: delta_quantity = target_quantity if delta_quantity >= round_lot: delta_quantity = math.floor(delta_quantity / round_lot) * round_lot open_orders.append(Order.__from_create__( order_book_id, delta_quantity, SIDE.BUY, MarketOrder(), POSITION_EFFECT.OPEN )) elif delta_quantity < -1: delta_quantity = math.floor(delta_quantity) close_orders.append(Order.__from_create__( order_book_id, abs(delta_quantity), SIDE.SELL, MarketOrder(), POSITION_EFFECT.CLOSE )) submit_orders = [] for order in chain(close_orders, open_orders): if env.can_submit_order(order): submit_orders.append(order) env.broker.submit_order(order) return submit_orders
def _account_order_from_state(order_state): o = Order() o.set_state(order_state) account = self._env.get_account(o.order_book_id) return account, o
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 and env.get_instrument(order_book_id).type == "Future"): 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 amount = int(amount) 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 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 :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 _sell_all_stock(order_book_id, amount, style): env = Environment.get_instance() order = Order.__from_create__(order_book_id, amount, SIDE.SELL, style, None) if env.can_submit_order(order): env.broker.submit_order(order) return order