def _settlement(self, event): total_value = self.total_value for position in list(self._positions.values()): order_book_id = position.order_book_id if position.is_de_listed( ) and position.buy_quantity + position.sell_quantity != 0: user_system_log.warn( _(u"{order_book_id} is expired, close all positions by system" ).format(order_book_id=order_book_id)) del self._positions[order_book_id] elif position.buy_quantity == 0 and position.sell_quantity == 0: del self._positions[order_book_id] else: position.apply_settlement() self._total_cash = total_value - self.margin - self.holding_pnl # 如果 total_value <= 0 则认为已爆仓,清空仓位,资金归0 if total_value <= 0 and self.forced_liquidation: if self._positions: user_system_log.warn( _("Trigger Forced Liquidation, current total_value is {}"), total_value) self._positions.clear() self._total_cash = 0 self._backward_trade_set.clear()
def pnl(self): """ [已弃用] 请使用 total_value """ user_system_log.warn( _(u"[abandon] {} is no longer used.").format('account.pnl')) return 0
def can_submit_order(self, account, order): if order.type != ORDER_TYPE.LIMIT: return True # FIXME: it may be better to round price in data source limit_up = round(self._env.price_board.get_limit_up(order.order_book_id), 4) if order.price > limit_up: reason = _( "Order Creation Failed: limit order price {limit_price} is higher than limit up {limit_up}." ).format( limit_price=order.price, limit_up=limit_up ) user_system_log.warn(reason) return False limit_down = round(self._env.price_board.get_limit_down(order.order_book_id), 4) if order.price < limit_down: reason = _( "Order Creation Failed: limit order price {limit_price} is lower than limit down {limit_down}." ).format( limit_price=order.price, limit_down=limit_down ) user_system_log.warn(reason) return False return True
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 portfolio_value(self): """ [已弃用] 请使用 total_value """ user_system_log.warn( _(u"[abandon] {} is no longer used.").format( 'account.portfolio_value')) return self.total_value
def average_cost(self): """ [已弃用] 请使用 avg_price 获取持仓买入均价 """ user_system_log.warn( _(u"[abandon] {} is no longer valid.").format( 'stock_position.average_cost')) return self._avg_price
def sold_value(self): """ [已弃用] """ user_system_log.warn( _(u"[abandon] {} is no longer valid.").format( 'stock_position.sold_value')) return 0
def bought_value(self): """ [已弃用] """ user_system_log.warn( _(u"[abandon] {} is no longer valid.").format( 'stock_position.bought_value')) return self._quantity * self._avg_price
def daily_realized_pnl(self): """ [已弃用] 请使用 realized_pnl """ user_system_log.warn( _(u"[abandon] {} is no longer used.").format( 'future_account.daily_realized_pnl')) return self.realized_pnl
def starting_cash(self): """ [已弃用] 请使用 total_value """ user_system_log.warn( _(u"[abandon] {} is no longer used.").format( 'account.starting_cash')) return 0
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 get_state(self): dict_data = {} for key, value in six.iteritems(self.__dict__): try: dict_data[key] = pickle.dumps(value) except Exception as e: user_detail_log.exception("g.{} can not pickle", key) user_system_log.warn("g.{} can not pickle", key) return pickle.dumps(dict_data)
def set_state(self, state): dict_data = pickle.loads(state) for key, value in six.iteritems(dict_data): try: self.__dict__[key] = pickle.loads(value) system_log.debug("restore context.{} {}", key, type(self.__dict__[key])) except Exception as e: user_system_log.warn('context.{} can not restore', key)
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 get_state(self): dict_data = {} for key, value in six.iteritems(self.__dict__): if key.startswith("_"): continue try: dict_data[key] = pickle.dumps(value) except Exception as e: user_system_log.warn("context.{} can not pickle", key) return pickle.dumps(dict_data)
def _stock_validator(account, order): if order.side != SIDE.SELL: return True position = account.positions[order.order_book_id] if order.quantity <= position.sellable: return True user_system_log.warn( _("Order Creation Failed: not enough stock {order_book_id} to sell, you want to sell {quantity}," " sellable {sellable}").format( order_book_id=order.order_book_id, quantity=order.quantity, sellable=position.sellable, )) return False
def _stock_validator(self, account, order): if order.side == SIDE.SELL: return True frozen_value = order.frozen_price * order.quantity cost_money = frozen_value + self._env.get_order_transaction_cost( DEFAULT_ACCOUNT_TYPE.STOCK, order) if cost_money <= account.cash: return True user_system_log.warn( _("Order Creation Failed: not enough money to buy {order_book_id}, needs {cost_money:.2f}, " "cash {cash:.2f}").format( order_book_id=order.order_book_id, cost_money=cost_money, cash=account.cash, )) return False
def _on_settlement(self, event): env = Environment.get_instance() for position in list(self._positions.values()): order_book_id = position.order_book_id if position.is_de_listed() and position.quantity != 0: if env.config.mod.sys_accounts.cash_return_by_stock_delisted: self._total_cash += position.market_value user_system_log.warn( _(u"{order_book_id} is expired, close all positions by system" ).format(order_book_id=order_book_id)) self._positions.pop(order_book_id, None) elif position.quantity == 0: self._positions.pop(order_book_id, None) else: position.apply_settlement() self._backward_trade_set.clear()
def _future_validator(account, order): if order.position_effect == POSITION_EFFECT.OPEN: return True position = account.positions[order.order_book_id] if order.side == SIDE.BUY and order.position_effect == POSITION_EFFECT.CLOSE_TODAY \ and order.quantity > position._closable_today_sell_quantity: user_system_log.warn( _("Order Creation Failed: not enough today position {order_book_id} to buy close, target" " quantity is {quantity}, closable today quantity {closable}" ).format( order_book_id=order.order_book_id, quantity=order.quantity, closable=position._closable_today_sell_quantity, )) return False if order.side == SIDE.SELL and order.position_effect == POSITION_EFFECT.CLOSE_TODAY \ and order.quantity > position._closable_today_buy_quantity: user_system_log.warn( _("Order Creation Failed: not enough today position {order_book_id} to sell close, target" " quantity is {quantity}, closable today quantity {closable}" ).format( order_book_id=order.order_book_id, quantity=order.quantity, closable=position._closable_today_buy_quantity, )) return False if order.side == SIDE.BUY and order.quantity > position.closable_sell_quantity: user_system_log.warn( _("Order Creation Failed: not enough securities {order_book_id} to buy close, target" " sell quantity is {quantity}, sell_closable_quantity {closable}" ).format( order_book_id=order.order_book_id, quantity=order.quantity, closable=position.closable_sell_quantity, )) return False elif order.side == SIDE.SELL and order.quantity > position.closable_buy_quantity: user_system_log.warn( _("Order Creation Failed: not enough securities {order_book_id} to sell close, target" " sell quantity is {quantity}, buy_closable_quantity {closable}" ).format( order_book_id=order.order_book_id, quantity=order.quantity, closable=position.closable_buy_quantity, )) return False return True
def __init__(self, event_bus, scope, ucontext): self._user_context = ucontext self._current_universe = set() self._init = scope.get('init', None) self._handle_bar = scope.get('handle_bar', None) self._handle_tick = scope.get('handle_tick', None) func_before_trading = scope.get('before_trading', None) if func_before_trading is not None and func_before_trading.__code__.co_argcount > 1: self._before_trading = lambda context: func_before_trading(context, None) user_system_log.warn(_(u"deprecated parameter[bar_dict] in before_trading function.")) else: self._before_trading = func_before_trading self._after_trading = scope.get('after_trading', None) if self._before_trading is not None: event_bus.add_listener(EVENT.BEFORE_TRADING, self.before_trading) if self._handle_bar is not None: event_bus.add_listener(EVENT.BAR, self.handle_bar) if self._handle_tick is not None: event_bus.add_listener(EVENT.TICK, self.handle_tick) if self._after_trading is not None: event_bus.add_listener(EVENT.AFTER_TRADING, self.after_trading) self._before_day_trading = scope.get('before_day_trading', None) self._before_night_trading = scope.get('before_night_trading', None) if self._before_day_trading is not None: user_system_log.warn(_(u"[deprecated] before_day_trading is no longer used. use before_trading instead.")) if self._before_night_trading is not None: user_system_log.warn(_(u"[deprecated] before_night_trading is no longer used. use before_trading instead.")) self._force_run_before_trading = Environment.get_instance().config.extra.force_run_init_when_pt_resume
def _apply_settlement(self, check_delist=True): delete_list = [] for direction, positions in self._positions_dict.items(): for order_book_id, position in positions.items(): if check_delist and position.is_de_listed( ) and position.quantity != 0: user_system_log.warn( _(u"{order_book_id} is expired, close all positions by system" ).format(order_book_id=order_book_id)) delete_list.append((order_book_id, direction)) # del self._positions[order_book_id] elif position.quantity == 0: delete_list.append((order_book_id, direction)) # del self._positions[order_book_id] else: position.apply_settlement() for order_book_id, direction in delete_list: self._positions_dict[direction].pop(order_book_id) self._backward_trade_set.clear()
def can_submit_order(self, account, order): instrument = self._env.data_proxy.instruments(order.order_book_id) if instrument.listed_date > self._env.trading_dt: user_system_log.warn( _(u"Order Creation Failed: {order_book_id} is not listed!"). format(order_book_id=order.order_book_id, )) return False if instrument.de_listed_date.date() < self._env.trading_dt.date(): user_system_log.warn( _(u"Order Creation Failed: {order_book_id} has been delisted!" ).format(order_book_id=order.order_book_id, )) return False if instrument.type == 'CS' and self._env.data_proxy.is_suspended( order.order_book_id, self._env.trading_dt): user_system_log.warn( _(u"Order Creation Failed: security {order_book_id} is suspended on {date}" ).format(order_book_id=order.order_book_id, date=self._env.trading_dt)) return False if instrument.type == 'PublicFund': if order.side == SIDE.BUY and self._env.data_proxy.non_subscribable( order.order_book_id, self._env.trading_dt): user_system_log.warn( _(u"Order Creation Failed: security {order_book_id} cannot be subscribed on {date}" ).format(order_book_id=order.order_book_id, date=self._env.trading_dt)) return False elif order.side == SIDE.SELL and self._env.data_proxy.non_redeemable( order.order_book_id, self._env.trading_dt): user_system_log.warn( _(u"Order Creation Failed: security {order_book_id} cannot be redeemed on {date}" ).format(order_book_id=order.order_book_id, date=self._env.trading_dt)) return False return True
def _future_validator(self, account, order): if order.position_effect != POSITION_EFFECT.OPEN: return True instrument = self._env.get_instrument(order.order_book_id) margin_info = self._env.data_proxy.get_margin_info(order.order_book_id) margin_rate = margin_info['long_margin_ratio' if order.side == 'BUY' else 'short_margin_ratio'] margin = order.frozen_price * order.quantity * instrument.contract_multiplier * margin_rate cost_money = margin * self._env.config.base.margin_multiplier cost_money += self._env.get_order_transaction_cost( DEFAULT_ACCOUNT_TYPE.FUTURE, order) if cost_money <= account.cash: return True user_system_log.warn( _("Order Creation Failed: not enough money to buy {order_book_id}, needs {cost_money:.2f}," " cash {cash:.2f}").format( order_book_id=order.order_book_id, cost_money=cost_money, cash=account.cash, )) return False
def margin_rate(self, value): user_system_log.warn( _(u"[abandon] {} is no longer used.").format( 'context.margin_rate'))
def total_trades(self): """abandon""" user_system_log.warn( _(u"[abandon] {} is no longer valid.").format( 'position.total_trades')) return 0
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 slippage(self, value): user_system_log.warn( _(u"[abandon] {} is no longer used.").format('context.slippage'))
def short_selling_allowed(self, value): user_system_log.warn( _(u"[abandon] {} is no longer used.").format( 'context.short_selling_allowed'))
def commission(self, value): user_system_log.warn( _(u"[abandon] {} is no longer used.").format('context.commission'))
def benchmark(self, value): user_system_log.warn( _(u"[abandon] {} is no longer used.").format('context.benchmark'))