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) amount = int(Decimal(cash_amount) / Decimal(price)) if cash_amount > 0: round_lot = int(ins.round_lot) amount = int(Decimal(amount) / Decimal(round_lot)) * 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 -= round_lot else: user_system_log.warn(_(u"Order Creation Failed: 0 order quantity")) return if amount < 0: amount = max(amount, -position.closable) return _order_shares(ins, amount, style, auto_switch_order_value=False)
def _on_settlement(self, event): trading_date = Environment.get_instance().trading_dt.date() for order_book_id, positions in list(self._positions.items()): for position in six.itervalues(positions): delta_cash = position.settlement(trading_date) self._total_cash += delta_cash for order_book_id, positions in list(self._positions.items()): if all(p.quantity == 0 and p.equity == 0 for p in six.itervalues(positions)): del self._positions[order_book_id] self._backward_trade_set.clear() fee = self._management_fee() self._management_fees += fee self._total_cash -= fee # 如果 total_value <= 0 则认为已爆仓,清空仓位,资金归0 forced_liquidation = Environment.get_instance( ).config.base.forced_liquidation if self.total_value <= 0 and forced_liquidation: if self._positions: user_system_log.warn( _("Trigger Forced Liquidation, current total_value is 0")) self._positions.clear() self._total_cash = 0
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) self._open_auction = scope.get("open_auction", 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) if self._open_auction is not None: event_bus.add_listener(EVENT.OPEN_AUCTION, self.open_auction)
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 :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 start_up(self, env, mod_config): env.set_price_board(StockLimitUpDownPriceBoard()) type_ = DataSourceType(mod_config.source) if type_ in [DataSourceType.MONGO, DataSourceType.REAL_TIME]: args = (env.config.base.data_bundle_path, mod_config.mongo_url) data_source_cls = MongoCacheDataSource if mod_config.enable_cache else MongoDataSource elif type_ == DataSourceType.BUNDLE: args = (env.config.base.data_bundle_path, mod_config.bundle_path) data_source_cls = BundleCacheDataSource if mod_config.enable_cache else BundleDataSource else: raise RuntimeError("data source type [%s] is not supported" % mod_config.source) if mod_config.enable_cache: if mod_config.cache_length: CacheMixin.set_cache_length(int(mod_config.cache_length)) if mod_config.max_cache_space: CacheMixin.set_cache_length(int(mod_config.cache_length)) data_source = data_source_cls(*args) mod_config.redis_uri = mod_config.redis_url # fit rqalpha if env.config.base.run_type is RUN_TYPE.BACKTEST and env.config.base.persist_mode == PERSIST_MODE.ON_NORMAL_EXIT: # generate user context using backtest persist_provider = DiskPersistProvider(mod_config.persist_path) env.set_persist_provider(persist_provider) is_real_time = env.config.base.run_type in (RUN_TYPE.PAPER_TRADING, RUN_TYPE.LIVE_TRADING) if is_real_time or type_ == DataSourceType.REAL_TIME: user_system_log.warn( _("[Warning] When you use this version of RealtimeTradeMod, history_bars can only " "get data from yesterday.")) if mod_config.redis_url: data_source = RedisDataSource(env.config.base.data_bundle_path, mod_config.redis_url, datasource=data_source) system_log.info(_("RealtimeTradeMod using market from redis")) else: data_source = DirectDataSource( env.config.base.data_bundle_path) system_log.info( _("RealtimeTradeMod using market from network")) if is_real_time: event_source = RealTimeEventSource(mod_config.fps, mod_config) # add persist persist_provider = DiskPersistProvider(mod_config.persist_path) env.set_persist_provider(persist_provider) env.config.base.persist = True env.config.base.persist_mode = PERSIST_MODE.REAL_TIME else: event_source = IntervalEventSource(env) env.set_data_source(data_source) # a patch to start_date since it's real time mod if env.config.base.start_date == datetime.now().date(): trading_dates = data_source.get_trading_calendar() pos = trading_dates.searchsorted(env.config.base.start_date) if trading_dates[pos].to_pydatetime().date( ) != env.config.base.start_date: env.config.base.start_date = trading_dates[max( 0, pos - 1)].to_pydatetime().date() env.set_event_source(event_source)
def can_submit_order(self, order, account=None): 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 _settlement(self, event, check_delist=True): delete_list = [] for direction, positions in list(self._positions_dict.items()): for order_book_id, position in list(positions.items()): pass 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 _on_settlement(self, event): self._static_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() # 如果 total_value <= 0 则认为已爆仓,清空仓位,资金归0 if self._static_total_value <= 0 and self.forced_liquidation: if self._positions: user_system_log.warn( _("Trigger Forced Liquidation, current total_value is 0")) self._positions.clear() self._static_total_value = 0 self._backward_trade_set.clear()
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 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的订单,如果这些订单都生成是没有意义的。 user_system_log.warn(_(u"Order Creation Failed: 0 order quantity")) return env.broker.submit_order(order) return order
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: try: transform_data = env.data_proxy.get_share_transformation( order_book_id) except NotImplementedError: pass else: if transform_data is not None: self._pending_transform[order_book_id] = transform_data continue 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 can_submit_order(self, order, account=None): 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 :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] round_lot = int(env.get_instrument(order_book_id).round_lot) if cash_amount > 0: cash_amount = min(cash_amount, account.cash) if isinstance(style, MarketOrder): amount = int(Decimal(cash_amount) / Decimal(price) / Decimal(round_lot)) * round_lot else: amount = int(Decimal(cash_amount) / Decimal(style.get_limit_price()) / Decimal(round_lot)) * round_lot # 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 start_up(self, env, mod_config): if env.config.base.run_type in (RUN_TYPE.PAPER_TRADING, RUN_TYPE.LIVE_TRADING): user_system_log.warn( _("[Warning] When you use this version of RealtimeTradeMod, history_bars can only get data from yesterday." )) if mod_config.redis_uri: env.set_data_source( RedisDataSource(env.config.base.data_bundle_path, mod_config.redis_uri)) system_log.info(_("RealtimeTradeMod using market from redis")) else: env.set_data_source( DirectDataSource(env.config.base.data_bundle_path)) system_log.info( _("RealtimeTradeMod using market from network")) env.set_event_source( RealtimeEventSource(mod_config.fps, mod_config)) # add persist persist_provider = DiskPersistProvider(mod_config.persist_path) env.set_persist_provider(persist_provider) env.config.base.persist = True env.config.base.persist_mode = PERSIST_MODE.REAL_TIME
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 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 pnl(self): """ [已弃用] 请使用 total_value """ user_system_log.warn( _(u"[abandon] {} is no longer used.").format('account.pnl')) return 0
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 order_shares(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 amount: 下单量, 正数代表买入,负数代表卖出。将会根据一手xx股来向下调整到一手的倍数,比如中国A股就是调整成100股的倍数。 :param price: 下单价格,默认为None,表示 :class:`~MarketOrder`, 此参数主要用于简化 `style` 参数。 :param style: 下单类型, 默认是市价单。目前支持的订单类型有 :class:`~LimitOrder` 和 :class:`~MarketOrder` :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) auto_switch_order_value = Environment.get_instance().config.mod.sys_accounts.auto_switch_order_value return _order_shares(order_book_id, amount, style, auto_switch_order_value)
def update_realtime_quotes(self, order_book_ids, print_log=False): if print_log: user_system_log.info('update_realtime_quotes\n%s' % repr(order_book_ids)) if not order_book_ids: return codes = [ts_code(book_id) for book_id in order_book_ids] try: df = ts.get_realtime_quotes(codes) except Exception as e: user_system_log.warn(repr(e)) return columns = set(df.columns) - set(['name', 'time', 'date', 'code']) for label in columns: df[label] = df[label].map(lambda x: 0 if str(x).strip() == '' else x) df[label] = df[label].astype(float) df['chg'] = df['price'] / df['pre_close'] - 1 df['order_book_id'] = df['code'].apply(order_book_id) df = df.set_index('order_book_id').sort_index() df['order_book_id'] = df.index df['datetime'] = df['date'] + ' ' + df['time'] df['close'] = df['price'] df['last'] = df['price'] df = df.rename( columns={ 'pre_close': 'prev_close', 'amount': 'total_turnover', 'b1_p': 'b1', 'a1_p': 'a1', 'b2_p': 'b2', 'a2_p': 'a2', 'b3_p': 'b3', 'a3_p': 'a3', 'b4_p': 'b4', 'a4_p': 'a4', 'b5_p': 'b5', 'a5_p': 'a5', }) df['limit_up'] = df.apply(lambda row: row.prev_close * (1.1 if 'ST' not in row['name'] else 1.05), axis=1).round(2) df['limit_down'] = df.apply(lambda row: row.prev_close * (0.9 if 'ST' not in row['name'] else 0.95), axis=1).round(2) del df['code'] del df['date'] del df['time'] self._env.price_board.set_snapshot(df) if print_log: user_system_log.info(repr(df))
def can_submit_order(self, order, account=None): if (order.type != ORDER_TYPE.LIMIT) or (order.position_effect == POSITION_EFFECT.EXERCISE): 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}, order_book_id={order_book_id}" ).format(order_book_id=order.order_book_id, 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}, order_book_id={order_book_id}" ).format(order_book_id=order.order_book_id, limit_price=order.price, limit_down=limit_down) user_system_log.warn(reason) return False return True
def can_submit_order(self, order, account=None): # type: (Order, Optional[AbstractAccount]) -> bool if account is None: return True if not account.position_validator_enabled(order.order_book_id): return True if order.position_effect in (POSITION_EFFECT.OPEN, POSITION_EFFECT.EXERCISE): return True position = account.get_position( order.order_book_id, order.position_direction) # type: AbstractPosition if order.position_effect == POSITION_EFFECT.CLOSE_TODAY and order.quantity > position.today_closable: user_system_log.warn( _("Order Creation Failed: not enough today position {order_book_id} to close, target" " quantity is {quantity}, closable today quantity is {closable}" ).format( order_book_id=order.order_book_id, quantity=order.quantity, closable=position.today_closable, )) return False if order.position_effect == POSITION_EFFECT.CLOSE and order.quantity > position.closable: user_system_log.warn( _("Order Creation Failed: not enough position {order_book_id} to close or exercise, target" " sell quantity is {quantity}, closable quantity is {closable}" ).format( order_book_id=order.order_book_id, quantity=order.quantity, closable=position.closable, )) return False return True
def cancel_order(self, order): if self._open_orders.get(order.order_id, None): url = '%s%s?%s' % (self._address, '/cancel', self._order_id_map[order.order_id]) user_system_log.info('loading: %s' % url) try: with request.urlopen(url) as f: user_system_log.info('status: %d %s' % (f.status, f.reason)) if f.status == 200: data = f.read().decode('utf-8') resp = json.loads(data) if resp.get('success', False): account = self._env.get_account(order.order_book_id) self._env.event_bus.publish_event(Event(EVENT.ORDER_PENDING_CANCEL, account=account, order=order)) order.mark_cancelled("%d order has been cancelled." % order.order_id) self._env.event_bus.publish_event(Event(EVENT.ORDER_CANCELLATION_PASS, account=account, order=order)) str_order_id = str(order.order_id) entrust_no = self._order_id_map[str_order_id] del self._open_orders[str_order_id] del self._order_id_map[entrust_no] del self._order_id_map[str_order_id] return else: user_system_log.warn(resp.get('msg', 'request failed')) except Exception as e: user_system_log.warn(repr(e)) return else: user_system_log.info('cancel order not fund: %s' % order.order_id)
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 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 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 round_price(self, tick_size): if tick_size: with decimal_rounding_floor(): limit_price_decimal = Decimal("{:.4f}".format(self.limit_price)) tick_size_decimal = Decimal("{:.4f}".format(tick_size)) self.limit_price = float((limit_price_decimal / tick_size_decimal).to_integral() * tick_size_decimal) else: user_system_log.warn('Invalid tick size: {}'.format(tick_size))
def round_price(self, tick_size): if tick_size: with decimal_rounding_floor(): limit_price_decimal = Decimal("{:.4f}".format(self.limit_price)) tick_size_decimal = Decimal("{:.4f}".format(tick_size)) self.limit_price = float((limit_price_decimal / tick_size_decimal).to_integral() * tick_size_decimal) else: user_system_log.warn('Invalid tick size: {}'.format(tick_size))
def sold_value(self): """ [已弃用] """ user_system_log.warn( _(u"[abandon] {} is no longer valid.").format( 'stock_position.sold_value')) return 0
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 get_state(self): dict_data = {} for key, value in six.iteritems(self.__dict__): try: dict_data[key] = pickle.dumps(value) except Exception: user_system_log.warn("g.{} can not pickle", key) return pickle.dumps(dict_data)
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 round_price(self, tick_size): if tick_size: with decimal_rounding_floor(): self.limit_price = float( (Decimal(self.limit_price) / Decimal(tick_size)).to_integral() * Decimal(tick_size)) else: user_system_log.warn('Invalid tick size: {}'.format(tick_size))
def set_state(self, state): dict_data = jsonpickle.decode(state) for key, value in six.iteritems(dict_data): try: self.__dict__[key] = 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 starting_cash(self): """ [已弃用] 请使用 total_value """ user_system_log.warn( _(u"[abandon] {} is no longer used.").format( 'account.starting_cash')) return 0
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 g.{} {}", key, type(self.__dict__[key])) except Exception: user_system_log.warn("g.{} restore failed", key)
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 _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 start_up(self, env, mod_config): if env.config.base.run_type in (RUN_TYPE.PAPER_TRADING, RUN_TYPE.LIVE_TRADING): user_system_log.warn(_("[Warning] When you use this version of RealtimeTradeMod, history_bars can only get data from yesterday.")) if mod_config.redis_uri: env.set_data_source(RedisDataSource(env.config.base.data_bundle_path, mod_config.redis_uri)) system_log.info(_("RealtimeTradeMod using market from redis")) else: env.set_data_source(DirectDataSource(env.config.base.data_bundle_path)) system_log.info(_("RealtimeTradeMod using market from network")) env.set_event_source(RealtimeEventSource(mod_config.fps, mod_config)) # add persist persist_provider = DiskPersistProvider(mod_config.persist_path) env.set_persist_provider(persist_provider) env.config.base.persist = True env.config.base.persist_mode = PERSIST_MODE.REAL_TIME
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 = order.frozen_price * order.quantity * instrument.contract_multiplier * instrument.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 _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: self._positions.clear() self._total_cash = 0 self._backward_trade_set.clear()
def _on_settlement(self, event): env = Environment.get_instance() for position in list(self._positions.values()): order_book_id = position.order_book_id if self.AGGRESSIVE_UPDATE_LAST_PRICE: position.update_last_price() if position.is_de_listed() and position.quantity != 0: if env.config.validator.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._transaction_cost = 0 self._backward_trade_set.clear() self._handle_dividend_book_closure(event.trading_dt.date())
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 can_submit_order(self, order, account=None): 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(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 _settlement(self, event, check_delist=True): delete_list = [] for direction, positions in list(self._positions_dict.items()): for order_book_id, position in list(positions.items()): pass 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 starting_cash(self): """ [已弃用] 请使用 total_value """ user_system_log.warn(_(u"[abandon] {} is no longer used.").format('account.starting_cash')) return 0
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 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 portfolio_value(self): """ [已弃用] 请使用 total_value """ user_system_log.warn(_(u"[abandon] {} is no longer used.").format('account.portfolio_value')) return self.total_value
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 sold_value(self): """ [已弃用] """ user_system_log.warn(_(u"[abandon] {} is no longer valid.").format('stock_position.sold_value')) return 0
def mark_rejected(self, reject_reason): if not self.is_final(): self._message = reject_reason self._status = ORDER_STATUS.REJECTED user_system_log.warn(reject_reason)
def mark_cancelled(self, cancelled_reason, user_warn=True): if not self.is_final(): self._message = cancelled_reason self._status = ORDER_STATUS.CANCELLED if user_warn: user_system_log.warn(cancelled_reason)
def round_price(self, tick_size): if tick_size: with decimal_rounding_floor(): self.limit_price = float((Decimal(self.limit_price) / Decimal(tick_size)).to_integral() * Decimal(tick_size)) else: user_system_log.warn('Invalid tick size: {}'.format(tick_size))
def pnl(self): """ [已弃用] 请使用 total_value """ user_system_log.warn(_(u"[abandon] {} is no longer used.").format('account.pnl')) return 0
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