def get_dividend(order_book_id, start_date, *args, **kwargs): # adjusted 参数在不复权数据回测时不再提供 env = Environment.get_instance() dt = env.trading_dt.date() - datetime.timedelta(days=1) start_date = to_date(start_date) if start_date > dt: raise RQInvalidArgument( _(u"in get_dividend, start_date {} is later than the previous test day {}").format( start_date, dt )) order_book_id = assure_order_book_id(order_book_id) array = env.data_proxy.get_dividend(order_book_id) if array is None: return None sd = start_date.year * 10000 + start_date.month * 100 + start_date.day ed = dt.year * 10000 + dt.month * 100 + dt.day return array[(array['announcement_date'] >= sd) & (array['announcement_date'] <= ed)]
def get_yield_curve(date=None, tenor=None): """ 获取某个国家市场指定日期的收益率曲线水平。 数据为2002年至今的中债国债收益率曲线,来源于中央国债登记结算有限责任公司。 :param date: 查询日期,默认为策略当前日期前一天 :type date: `str` | `date` | `datetime` | `pandas.Timestamp` :param str tenor: 标准期限,'0S' - 隔夜,'1M' - 1个月,'1Y' - 1年,默认为全部期限 :return: `pandas.DataFrame` - 查询时间段内无风险收益率曲线 :example: .. code-block:: python3 :linenos: [In] get_yield_curve('20130104') [Out] 0S 1M 2M 3M 6M 9M 1Y 2Y \ 2013-01-04 0.0196 0.0253 0.0288 0.0279 0.0280 0.0283 0.0292 0.0310 3Y 4Y ... 6Y 7Y 8Y 9Y 10Y \ 2013-01-04 0.0314 0.0318 ... 0.0342 0.0350 0.0353 0.0357 0.0361 ... """ env = Environment.get_instance() trading_date = env.trading_dt.date() yesterday = env.data_proxy.get_previous_trading_date(trading_date) if date is None: date = yesterday else: date = pd.Timestamp(date) if date > yesterday: raise RQInvalidArgument('get_yield_curve: {} >= now({})'.format( date, yesterday)) return env.data_proxy.get_yield_curve(start_date=date, end_date=date, tenor=tenor)
def get_pit_financials(fields, quarter=None, interval=None, order_book_ids=None, if_adjusted='all'): if quarter is None: valid = True else: valid = isinstance(quarter, six.string_types) and quarter[-2] == 'q' if valid: try: valid = 1990 <= int(quarter[:-2]) <= 2050 and 1 <= int(quarter[-1]) <= 4 except ValueError: valid = False if not valid: raise RQInvalidArgument( _(u"function {}: invalid {} argument, quarter should be in form of '2012q3', " u"got {} (type: {})").format( 'get_pit_financials', 'quarter', quarter, type(quarter) )) env = Environment.get_instance() dt = env.calendar_dt.date() year = dt.year mon = dt.month day = dt.day int_date = year * 10000 + mon * 100 + day q = (mon - 4) // 3 + 1 y = year if q <= 0: y -= 1 q = 4 default_quarter = str(y) + 'q' + str(q) if quarter is None or quarter > default_quarter: quarter = default_quarter result = rqdatac.get_pit_financials(fields, quarter, interval, order_book_ids, if_adjusted, max_info_date=int_date, market='cn') if result is None: return pd.DataFrame() if if_adjusted == 'ignore': result = result.reset_index().sort_values('info_date') result = result.groupby(['order_book_id', 'end_date'], as_index=False).fillna(method='ffill') result = result.drop(['info_date', 'if_adjusted'], axis=1) result = result.drop_duplicates(['order_book_id', 'end_date'], keep='last') result = result.set_index(['order_book_id', 'end_date']).sort_index() return result
def index_weights(order_book_id, date=None): # type: (str, Optional[Union[str, datetime.date]]) -> pd.Series """ 获取T-1日的指数权重 :param order_book_id: 指数 :param date: 可选,默认为T-1日 :return: 每只股票在指数中的构成权重 :example: 获取上证50指数上个交易日的指数构成 .. code-block:: python3 :linenos: index_weights('000016.XSHG') # [Out] # Order_book_id # 600000.XSHG 0.03750 # 600010.XSHG 0.00761 # 600016.XSHG 0.05981 # 600028.XSHG 0.01391 # 600029.XSHG 0.00822 # 600030.XSHG 0.03526 # 600036.XSHG 0.04889 # 600050.XSHG 0.00998 # 600104.XSHG 0.02122 """ env = Environment.get_instance() data_proxy = env.data_proxy dt = to_date(data_proxy.get_previous_trading_date(env.trading_dt.date())) if date is None: date = dt else: date = to_date(date) if date > dt: raise RQInvalidArgument( _('in index_weights, date {} is no earlier than previous test date {}' ).format(date, dt)) order_book_id = assure_order_book_id(order_book_id) return rqdatac.index_weights(order_book_id, date)
def add_indicator(order_book_id, frequency, field_name, field_list): """ Description : 给数据添加指标的。 Arg : @order_book_id : 股票id @frequency : 频率 @field_list : 列名 @field_list : 列数据 Returns : Raises : """ order_book_id = assure_order_book_id(order_book_id) env = Environment.get_instance() if frequency[-1] == 'm' and env.config.base.frequency == '1d': raise RQInvalidArgument('can not get minute history in day back test') return env.data_proxy.add_indicator(order_book_id, frequency, field_name, field_list)
def get_split(order_book_ids, start_date=None): # type: (Union[str, List[str]], Optional[Union[str, datetime.date]]) -> pd.DataFrame """ 获取某只股票到策略当前日期前一天的拆分情况(包含起止日期)。 :param order_book_ids: 证券代码,证券的独特的标识符,例如:'000001.XSHE' :param start_date: 开始日期,用户必须指定,需要早于策略当前日期 :return: 查询时间段内的某个股票的拆分数据 * ex_dividend_date: 除权除息日,该天股票的价格会因为拆分而进行调整 * book_closure_date: 股权登记日 * split_coefficient_from: 拆分因子(拆分前) * split_coefficient_to: 拆分因子(拆分后) 例如:每10股转增2股,则split_coefficient_from = 10, split_coefficient_to = 12. :example: .. code-block:: python3 :linenos: get_split('000001.XSHE', start_date='2010-01-04') #[Out] # book_closure_date payable_date split_coefficient_from \ #ex_dividend_date #2013-06-20 2013-06-19 2013-06-20 10 # split_coefficient_to #ex_dividend_date #2013-06-20 16.0 """ # order_book_id 支持list类型 env = Environment.get_instance() dt = env.trading_dt.date() - datetime.timedelta(days=1) start_date = to_date(start_date) if start_date > dt: raise RQInvalidArgument( _('in get_split, start_date {} is no earlier than the previous test day {}' ).format(start_date, dt)) if isinstance(order_book_ids, six.string_types): order_book_ids = [order_book_ids] order_book_ids = [assure_order_book_id(i) for i in order_book_ids] return rqdatac.get_split(order_book_ids, start_date, dt)
def get_dividend(order_book_id, start_date): """ 获取某只股票到策略当前日期前一天的分红情况(包含起止日期)。 :param order_book_id: 股票代码 :type order_book_id: str :param start_date: 开始日期,需要早于策略当前日期 :type start_date: `str` | `date` | `datetime` | `pandas.Timestamp` :return: ndarray ========================= =================================================== fields 字段名 ========================= =================================================== announcement_date 分红宣布日 book_closure_date 股权登记日 dividend_cash_before_tax 税前分红 ex_dividend_date 除权除息日 payable_date 分红到帐日 round_lot 分红最小单位 ========================= =================================================== """ # adjusted 参数在不复权数据回测时不再提供 env = Environment.get_instance() dt = env.trading_dt.date() - datetime.timedelta(days=1) start_date = to_date(start_date) if start_date > dt: raise RQInvalidArgument( _( u"in get_dividend, start_date {} is later than the previous test day {}" ).format(start_date, dt) ) order_book_id = assure_order_book_id(order_book_id) array = env.data_proxy.get_dividend(order_book_id) if array is None: return None sd = start_date.year * 10000 + start_date.month * 100 + start_date.day ed = dt.year * 10000 + dt.month * 100 + dt.day return array[ (array["announcement_date"] >= sd) & (array["announcement_date"] <= ed) ]
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 unsubscribe(id_or_symbols): """ 取消订阅合约行情。取消订阅会导致合约池内合约的减少,如果当前合约池中没有任何合约,则策略直接退出。 :param id_or_ins: 标的物 :type id_or_ins: :class:`~Instrument` object | `str` | List[:class:`~Instrument`] | List[`str`] """ current_universe = Environment.get_instance().get_universe() if isinstance(id_or_symbols, six.string_types): order_book_id = instruments(id_or_symbols).order_book_id current_universe.discard(order_book_id) elif isinstance(id_or_symbols, Instrument): current_universe.discard(id_or_symbols.order_book_id) elif isinstance(id_or_symbols, Iterable): for item in id_or_symbols: i = assure_order_book_id(item) current_universe.discard(i) else: raise RQInvalidArgument(_(u"unsupported order_book_id type")) Environment.get_instance().update_universe(current_universe)
def subscribe(id_or_symbols): """ 订阅合约行情。该操作会导致合约池内合约的增加,从而影响handle_bar中处理bar数据的数量。 需要注意,用户在初次编写策略时候需要首先订阅合约行情,否则handle_bar不会被触发。 :param id_or_ins: 标的物 :type id_or_ins: :class:`~Instrument` object | `str` | List[:class:`~Instrument`] | List[`str`] """ current_universe = Environment.get_instance().get_universe() if isinstance(id_or_symbols, six.string_types): order_book_id = instruments(id_or_symbols).order_book_id current_universe.add(order_book_id) elif isinstance(id_or_symbols, Instrument): current_universe.add(id_or_symbols.order_book_id) elif isinstance(id_or_symbols, Iterable): for item in id_or_symbols: current_universe.add(assure_order_book_id(item)) else: raise RQInvalidArgument(_(u"unsupported order_book_id type")) verify_that('id_or_symbols')._are_valid_instruments("subscribe", id_or_symbols) Environment.get_instance().update_universe(current_universe)
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) return _order_value(order_book_id, cash_amount, style)
def unsubscribe(id_or_symbols): # type: (Union[str, Instrument, Iterable[str], Iterable[Instrument]]) -> None """ 取消订阅合约行情。取消订阅会导致合约池内合约的减少,如果当前合约池中没有任何合约,则策略直接退出。 :param id_or_symbols: 标的物 """ current_universe = Environment.get_instance().get_universe() if isinstance(id_or_symbols, six.string_types): order_book_id = instruments(id_or_symbols).order_book_id current_universe.discard(order_book_id) elif isinstance(id_or_symbols, Instrument): current_universe.discard(id_or_symbols.order_book_id) elif isinstance(id_or_symbols, Iterable): for item in id_or_symbols: i = assure_order_book_id(item) current_universe.discard(i) else: raise RQInvalidArgument(_(u"unsupported order_book_id type")) Environment.get_instance().update_universe(current_universe)
def history_bars(self, instrument, bar_count, frequency, fields, dt, skip_suspended=True, include_now=False, adjust_type='pre', adjust_orig=None): if frequency != '1d': raise NotImplementedError if skip_suspended and instrument.type == 'CS': bars = self._filtered_day_bars(instrument) else: bars = self._all_day_bars_of(instrument) if not self._are_fields_valid(fields, bars.dtype.names): raise RQInvalidArgument("invalid fileds: {}".format(fields)) if len(bars) <= 0: return bars dt = np.uint64(convert_date_to_int(dt)) i = bars['datetime'].searchsorted(dt, side='right') left = i - bar_count if i >= bar_count else 0 bars = bars[left:i] if adjust_type == 'none' or instrument.type in {'Future', 'INDX'}: # 期货及指数无需复权 return bars if fields is None else bars[fields] if isinstance(fields, str) and fields not in FIELDS_REQUIRE_ADJUSTMENT: return bars if fields is None else bars[fields] return adjust_bars(bars, self.get_ex_cum_factor(instrument.order_book_id), fields, adjust_type, adjust_orig)
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) 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 subscribe(id_or_symbols): # type: (Union[str, Instrument, Iterable[str], Iterable[Instrument]]) -> None """ 订阅合约行情。该操作会导致合约池内合约的增加,从而影响handle_bar中处理bar数据的数量。 需要注意,用户在初次编写策略时候需要首先订阅合约行情,否则handle_bar不会被触发。 :param id_or_symbols: 标的物 """ current_universe = Environment.get_instance().get_universe() if isinstance(id_or_symbols, six.string_types): order_book_id = instruments(id_or_symbols).order_book_id current_universe.add(order_book_id) elif isinstance(id_or_symbols, Instrument): current_universe.add(id_or_symbols.order_book_id) elif isinstance(id_or_symbols, Iterable): for item in id_or_symbols: current_universe.add(assure_order_book_id(item)) else: raise RQInvalidArgument(_(u"unsupported order_book_id type")) verify_that("id_or_symbols")._are_valid_instruments("subscribe", id_or_symbols) Environment.get_instance().update_universe(current_universe)
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 history_bars( order_book_id, bar_count, frequency, fields=None, skip_suspended=True, include_now=False, adjust_type="pre", ): # type:(str, int, str, Optional[Union[str, List[str]]], Optional[bool], Optional[bool], Optional[str]) -> np.ndarray """ 获取指定合约的历史 k 线行情,同时支持日以及分钟历史数据。不能在init中调用。 日回测获取分钟历史数据:不支持 日回测获取日历史数据 ========================= =================================================== 调用时间 返回数据 ========================= =================================================== T日before_trading T-1日day bar T日handle_bar T日day bar ========================= =================================================== 分钟回测获取日历史数据 ========================= =================================================== 调用时间 返回数据 ========================= =================================================== T日before_trading T-1日day bar T日handle_bar T-1日day bar ========================= =================================================== 分钟回测获取分钟历史数据 ========================= =================================================== 调用时间 返回数据 ========================= =================================================== T日before_trading T-1日最后一个minute bar T日handle_bar T日当前minute bar ========================= =================================================== :param order_book_id: 合约代码 :param bar_count: 获取的历史数据数量,必填项 :param frequency: 获取数据什么样的频率进行。'1d'或'1m'分别表示每日和每分钟,必填项 :param fields: 返回数据字段。必填项。见下方列表。 :param skip_suspended: 是否跳过停牌数据 :param include_now: 是否包含当前数据 :param adjust_type: 复权类型,默认为前复权 pre;可选 pre, none, post ========================= =================================================== fields 字段名 ========================= =================================================== datetime 时间戳 open 开盘价 high 最高价 low 最低价 close 收盘价 volume 成交量 total_turnover 成交额 open_interest 持仓量(期货专用) basis_spread 期现差(股指期货专用) settlement 结算价(期货日线专用) prev_settlement 结算价(期货日线专用) ========================= =================================================== :example: 获取最近5天的日线收盘价序列(策略当前日期为20160706): .. code-block:: python3 :linenos: [In] logger.info(history_bars('000002.XSHE', 5, '1d', 'close')) [Out] [ 8.69 8.7 8.71 8.81 8.81] """ order_book_id = assure_order_book_id(order_book_id) env = Environment.get_instance() dt = env.calendar_dt if frequency[-1] not in {"m", "d"}: raise RQInvalidArgument("invalid frequency {}".format(frequency)) if frequency[-1] == "m" and env.config.base.frequency == "1d": raise RQInvalidArgument("can not get minute history in day back test") if frequency[-1] == "d" and frequency != "1d": raise RQInvalidArgument("invalid frequency") if adjust_type not in {"pre", "post", "none"}: raise RuntimeError("invalid adjust_type") if frequency == "1d": sys_frequency = Environment.get_instance().config.base.frequency if ( sys_frequency in ["1m", "tick"] and not include_now and ExecutionContext.phase() != EXECUTION_PHASE.AFTER_TRADING ) or (ExecutionContext.phase() in (EXECUTION_PHASE.BEFORE_TRADING, EXECUTION_PHASE.OPEN_AUCTION)): dt = env.data_proxy.get_previous_trading_date(env.trading_dt.date()) # 当 EXECUTION_PHASE.BEFORE_TRADING 的时候,强制 include_now 为 False include_now = False if sys_frequency == "1d": # 日回测不支持 include_now include_now = False if fields is None: fields = ["datetime", "open", "high", "low", "close", "volume"] return env.data_proxy.history_bars( order_book_id, bar_count, frequency, fields, dt, skip_suspended=skip_suspended, include_now=include_now, adjust_type=adjust_type, adjust_orig=env.trading_dt, )
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 history_bars(order_book_id, bar_count, frequency, fields=None, skip_suspended=True, include_now=False, adjust_type='pre'): """ 获取指定合约的历史行情,同时支持日以及分钟历史数据。不能在init中调用。 注意,该API会自动跳过停牌数据。 日回测获取分钟历史数据:不支持 日回测获取日历史数据 ========================= =================================================== 调用时间 返回数据 ========================= =================================================== T日before_trading T-1日day bar T日handle_bar T日day bar ========================= =================================================== 分钟回测获取日历史数据 ========================= =================================================== 调用时间 返回数据 ========================= =================================================== T日before_trading T-1日day bar T日handle_bar T-1日day bar ========================= =================================================== 分钟回测获取分钟历史数据 ========================= =================================================== 调用时间 返回数据 ========================= =================================================== T日before_trading T-1日最后一个minute bar T日handle_bar T日当前minute bar ========================= =================================================== :param order_book_id: 合约代码 :type order_book_id: `str` :param int bar_count: 获取的历史数据数量,必填项 :param str frequency: 获取数据什么样的频率进行。'1d'或'1m'分别表示每日和每分钟,必填项 :param str fields: 返回数据字段。必填项。见下方列表。 ========================= =================================================== fields 字段名 ========================= =================================================== datetime 时间戳 open 开盘价 high 最高价 low 最低价 close 收盘价 volume 成交量 total_turnover 成交额 open_interest 持仓量(期货专用) basis_spread 期现差(股指期货专用) settlement 结算价(期货日线专用) prev_settlement 结算价(期货日线专用) ========================= =================================================== :param bool skip_suspended: 是否跳过停牌数据 :param bool include_now: 是否包含当前数据 :param str adjust_type: 复权类型,默认为前复权 pre;可选 pre, none, post :return: `ndarray`, 方便直接与talib等计算库对接,效率较history返回的DataFrame更高。 :example: 获取最近5天的日线收盘价序列(策略当前日期为20160706): .. code-block:: python3 :linenos: [In] logger.info(history_bars('000002.XSHE', 5, '1d', 'close')) [Out] [ 8.69 8.7 8.71 8.81 8.81] """ order_book_id = assure_order_book_id(order_book_id) env = Environment.get_instance() dt = env.calendar_dt if frequency[-1] == 'm' and env.config.base.frequency == '1d': raise RQInvalidArgument('can not get minute history in day back test') if adjust_type not in {'pre', 'post', 'none'}: raise RuntimeError('invalid adjust_type') if frequency == '1d': sys_frequency = Environment.get_instance().config.base.frequency if ((sys_frequency in ['1m', 'tick'] and not include_now) or ExecutionContext.phase() == EXECUTION_PHASE.BEFORE_TRADING): dt = env.data_proxy.get_previous_trading_date( env.trading_dt.date()) # 当 EXECUTION_PHASE.BEFORE_TRADING 的时候,强制 include_now 为 False include_now = False if sys_frequency == "1d": # 日回测不支持 include_now include_now = False if fields is None: fields = ["datetime", "open", "high", "low", "close", "volume"] return env.data_proxy.history_bars(order_book_id, bar_count, frequency, fields, dt, skip_suspended=skip_suspended, include_now=include_now, adjust_type=adjust_type, adjust_orig=env.trading_dt)
def raise_not_valid_future_error(self, func_name, arg_name, value): raise RQInvalidArgument( _(u"function {}: invalid {} argument, expect a valid future instrument/order_book_id/symbol, " u"got {} (type: {})").format( func_name, self._arg_name, value, type(value) ))
def get_price( order_book_ids, # type: Union[str, Iterable[str]] start_date, # type: Union[datetime.date, str] end_date=None, # type: Optional[Union[datetime.date, datetime.datetime, str]] frequency='1d', # type: Optional[str] fields=None, # type: Optional[Iterable[str]] adjust_type='pre', # type: Optional[str] skip_suspended=False, # type: Optional[bool] expect_df=False # type: Optional[bool] ): # type: (...) -> Union[pd.DataFrame, pd.Panel, pd.Series] """ 获取指定合约或合约列表的历史行情(包含起止日期,日线或分钟线),不能在'handle_bar'函数中进行调用。 注意,这一函数主要是为满足在研究平台编写策略习惯而引入。在编写策略中,使用history_bars进行数据获取会更方便。 :param order_book_ids: 合约代码,合约代码,可传入order_book_id, order_book_id list, symbol, symbol list :param start_date: 开始日期,用户必须指定 :param end_date: 结束日期,默认为策略当前日期前一天 :param frequency: 历史数据的频率。 现在支持日/分钟级别的历史数据,默认为'1d'。使用者可自由选取不同频率,例如'5m'代表5分钟线 :param fields: 期望返回的字段名称,如 open,close 等 :param adjust_type: 权息修复方案。前复权 - pre,后复权 - post,不复权 - none :param skip_suspended: 是否跳过停牌数据。默认为False,不跳过,用停牌前数据进行补齐。True则为跳过停牌期。注意,当设置为True时,函数order_book_id只支持单个合约传入 :param expect_df: 是否期望始终返回 DataFrame。pandas 0.25.0 以上该参数应设为 True,以避免因试图构建 Panel 产生异常 当 expect_df 为 False 时,返回值的类型如下 * 传入一个order_book_id,多个fields,函数会返回一个pandas DataFrame * 传入一个order_book_id,一个field,函数会返回pandas Series * 传入多个order_book_id,一个field,函数会返回一个pandas DataFrame * 传入多个order_book_id,函数会返回一个pandas Panel ========================= ========================= ============================================================================== 参数 类型 说明 ========================= ========================= ============================================================================== open float 开盘价 close float 收盘价 high float 最高价 low float 最低价 limit_up float 涨停价 limit_down float 跌停价 total_turnover float 总成交额 volume float 总成交量 acc_net_value float 累计净值(仅限基金日线数据) unit_net_value float 单位净值(仅限基金日线数据) discount_rate float 折价率(仅限基金日线数据) settlement float 结算价 (仅限期货日线数据) prev_settlement float 昨日结算价(仅限期货日线数据) open_interest float 累计持仓量(期货专用) basis_spread float 基差点数(股指期货专用,股指期货收盘价-标的指数收盘价) trading_date pandas.TimeStamp 交易日期(仅限期货分钟线数据),对应期货夜盘的情况 ========================= ========================= ============================================================================== :example: 获取单一股票历史日线行情: .. code-block:: python3 :linenos: get_price('000001.XSHE', start_date='2015-04-01', end_date='2015-04-12') #[Out] #open close high low total_turnover volume limit_up limit_down #2015-04-01 10.7300 10.8249 10.9470 10.5469 2.608977e+09 236637563.0 11.7542 9.6177 #2015-04-02 10.9131 10.7164 10.9470 10.5943 2.222671e+09 202440588.0 11.9102 9.7397 #2015-04-03 10.6486 10.7503 10.8114 10.5876 2.262844e+09 206631550.0 11.7881 9.6448 #2015-04-07 10.9538 11.4015 11.5032 10.9538 4.898119e+09 426308008.0 11.8288 9.6787 #2015-04-08 11.4829 12.1543 12.2628 11.2929 5.784459e+09 485517069.0 12.5409 10.2620 #2015-04-09 12.1747 12.2086 12.9208 12.0255 5.794632e+09 456921108.0 13.3684 10.9403 #2015-04-10 12.2086 13.4294 13.4294 12.1069 6.339649e+09 480990210.0 13.4294 10.9877 #... """ env = Environment.get_instance() yesterday = env.trading_dt.date() - datetime.timedelta(days=1) if end_date is not None: end_date = to_date(end_date) if end_date > yesterday: raise RQInvalidArgument( _('in get_price, end_date {} is no earlier than the previous test day {}' ).format(end_date, yesterday)) else: end_date = yesterday start_date = to_date(start_date) if start_date > yesterday: raise RQInvalidArgument( _('in get_price, start_date {} is no earlier than the previous test day {}' ).format(start_date, yesterday)) if end_date < start_date: raise RQInvalidArgument( _('in get_price, start_date {} > end_date {}').format( start_date, end_date)) if isinstance(order_book_ids, six.string_types): order_book_ids = assure_order_book_id(order_book_ids) else: order_book_ids = [assure_order_book_id(i) for i in order_book_ids] return rqdatac.get_price(order_book_ids, start_date, end_date, frequency=frequency, fields=fields, adjust_type=adjust_type, skip_suspended=skip_suspended, expect_df=expect_df)
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 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 if price == 0: return order_shares(order_book_id, 0, style) 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 check_greater_than(func_name, value): if value <= low: raise RQInvalidArgument( _(u"function {}: invalid {} argument, expect a value > {}, got {} (type: {})").format( func_name, self._arg_name, low, value, type(value) ))
def history_bars(self, instrument, bar_count, frequency, fields, dt, skip_suspended=True, include_now=False, adjust_type='pre', adjust_orig=None): if frequency not in ['1m', '5m', '15m', '1d']: raise NotImplementedError if skip_suspended and instrument.type == 'CS': bars = self._filtered_day_bars(instrument) elif frequency == '1d': bars = self._all_day_bars_of(instrument) elif frequency in ['1m', '5m', '15m']: dt_end = np.uint64(convert_date_to_int(dt + timedelta(days=1))) dt_start = np.uint( convert_date_to_int(dt - timedelta(days=int(bar_count / 180) + 30))) # @TODO bars = self._all_min_bars_of(instrument, dt_start, dt_end) if not self._are_fields_valid(fields, bars.dtype.names): raise RQInvalidArgument("invalid fileds: {}".format(fields)) if len(bars) <= 0: return bars if frequency == '1d': dt = np.uint64(convert_date_to_int(dt)) elif frequency in ['1m', '5m', '15m']: dt = np.uint64(convert_date_min_to_int(dt)) count = int(frequency[:-1]) i = bars['datetime'].searchsorted(dt, side='right') if i <= 0: return None left = i - bar_count * count if i >= bar_count * count else 0 if count > 1: merge_bars = [] for j in range(0, bar_count): if left + (j + 1) * count > i: break merge_bar = self._history_merge_min_bar( bars[left + j * count:left + (j + 1) * count]) merge_bars.append(merge_bar) bars = np.array(merge_bars) else: bars = bars[left:i] if adjust_type == 'none' or instrument.type in {'Future', 'INDX'}: # 期货及指数无需复权 return bars if fields is None else bars[fields] if isinstance(fields, str) and fields not in FIELDS_REQUIRE_ADJUSTMENT: return bars if fields is None else bars[fields] return adjust_bars(bars, self.get_ex_cum_factor(instrument.order_book_id), fields, adjust_type, adjust_orig)
def get_financials(query, quarter=None, interval='4q', expect_df=False): if quarter is None: valid = True else: valid = isinstance(quarter, six.string_types) and quarter[-2] == 'q' if valid: try: valid = 1990 <= int(quarter[:-2]) <= 2050 and 1 <= int( quarter[-1]) <= 4 except ValueError: valid = False if not valid: raise RQInvalidArgument( _(u"function {}: invalid {} argument, quarter should be in form of '2012q3', " u"got {} (type: {})").format('get_financials', 'quarter', quarter, type(quarter))) env = Environment.get_instance() dt = env.calendar_dt.date() - datetime.timedelta( days=1) # Take yesterday's data as default year = dt.year mon = dt.month day = dt.day int_date = year * 10000 + mon * 100 + day q = (mon - 4) // 3 + 1 y = year if q <= 0: y -= 1 q = 4 default_quarter = str(y) + 'q' + str(q) if quarter is None or quarter > default_quarter: quarter = default_quarter include_date = False for d in query.column_descriptions: if d['name'] == 'announce_date': include_date = True if not include_date: query = query.add_column(rqdatac.fundamentals.announce_date) result = rqdatac.get_financials(query, quarter, interval, expect_df=expect_df) if result is None: return pd.DataFrame() if isinstance(result, pd.Series): return result elif isinstance(result, pd.DataFrame): result = result[(result['announce_date'] <= int_date) | pd.isnull(result['announce_date'])] if not include_date: del result['announce_date'] else: d = dict() for order_book_id in result.minor_axis: df = result.minor_xs(order_book_id) df = df[(df.announce_date < int_date) | (pd.isnull(df.announce_date))] d[order_book_id] = df pl = pd.Panel.from_dict(d, orient='minor') if not include_date: pl.drop('announce_date', axis=0, inplace=True) if len(pl.items) == 1: pl = pl[pl.items[0]] return pl return result
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 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 check_less_than(func_name, value): if value >= high: raise RQInvalidArgument( _(u"function {}: invalid {} argument, expect a value < {}, got {} (type: {})").format( func_name, self._arg_name, high, value, type(value) ))
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 check_is_instance_of(func_name, value): if not isinstance(value, types): raise RQInvalidArgument( _(u"function {}: invalid {} argument, expect a value of type {}, got {} (type: {})").format( func_name, self._arg_name, types, value, type(value) ))