def stock_order_shares(id_or_ins, amount, price=None, style=None): auto_switch_order_value = Environment.get_instance( ).config.mod.sys_accounts.auto_switch_order_value account, position, ins = _get_account_position_ins(id_or_ins) return _order_shares(assure_instrument(id_or_ins), amount, cal_style(price, style), position.quantity, auto_switch_order_value)
def order_lots(id_or_ins, amount, price=None, style=None): # type: (Union[str, Instrument], int, Optional[float], Optional[OrderStyle]) -> Optional[Order] """ 指定手数发送买/卖单。如有需要落单类型当做一个参量传入,如果忽略掉落单类型,那么默认是市价单(market order)。 :param id_or_ins: 下单标的物 :param int amount: 下单量, 正数代表买入,负数代表卖出。将会根据一手xx股来向下调整到一手的倍数,比如中国A股就是调整成100股的倍数。 :param float price: 下单价格,默认为None,表示 :class:`~MarketOrder`, 此参数主要用于简化 `style` 参数。 :param style: 下单类型, 默认是市价单。目前支持的订单类型有 :class:`~LimitOrder` 和 :class:`~MarketOrder` :example: .. code-block:: python #买入20手的平安银行股票,并且发送市价单: order_lots('000001.XSHE', 20) #买入10手平安银行股票,并且发送限价单,价格为¥10: order_lots('000001.XSHE', 10, style=LimitOrder(10)) """ ins = assure_instrument(id_or_ins) auto_switch_order_value = Environment.get_instance( ).config.mod.sys_accounts.auto_switch_order_value return _order_shares(ins, amount * int(ins.round_lot), cal_style(price, style), auto_switch_order_value)
def _get_account_position_ins(id_or_ins): ins = assure_instrument(id_or_ins) try: account = Environment.get_instance().portfolio.accounts[ DEFAULT_ACCOUNT_TYPE.STOCK] except KeyError: raise KeyError( _(u"order_book_id: {order_book_id} needs stock account, please set and try again!" ).format(order_book_id=ins.order_book_id)) position = account.get_position(ins.order_book_id, POSITION_DIRECTION.LONG) return account, position, ins
def _get_account_position_ins(id_or_ins): ins = assure_instrument(id_or_ins) account = Environment.get_instance().portfolio.accounts[ DEFAULT_ACCOUNT_TYPE.STOCK] position = account.get_position(ins.order_book_id, POSITION_DIRECTION.LONG) return account, position, ins
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(target_portfolio.values()) if total_percent > 1 and not np.isclose(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 target_portfolio.items(): order_book_id = assure_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, 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 current_quantities.items(): if order_book_id not in target_portfolio: close_orders.append( Order.__from_create__(order_book_id, quantity, SIDE.SELL, MarketOrder(), POSITION_EFFECT.CLOSE)) for order_book_id, (target_quantity, price) in target_quantities.items(): ins = assure_instrument(order_book_id) round_lot = 100 if order_book_id in current_quantities: delta_quantity = target_quantity - current_quantities[order_book_id] else: delta_quantity = target_quantity if _is_ksh(ins): # KSH can buy(sell) 201, 202 shares delta_quantity = _get_ksh_amount(delta_quantity) else: delta_quantity = int( Decimal(delta_quantity) / Decimal(round_lot)) * round_lot if delta_quantity > 0: open_order = Order.__from_create__(order_book_id, delta_quantity, SIDE.BUY, MarketOrder(), POSITION_EFFECT.OPEN) open_order.set_frozen_price(price) open_orders.append(open_order) elif delta_quantity < -1: close_order = Order.__from_create__(order_book_id, abs(delta_quantity), SIDE.SELL, MarketOrder(), POSITION_EFFECT.CLOSE) close_order.set_frozen_price(price) close_orders.append(close_order) 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 _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