def all_instruments(type=None, date=None): """ 获取某个国家市场的所有合约信息。使用者可以通过这一方法很快地对合约信息有一个快速了解,目前仅支持中国市场。 :param str type: 需要查询合约类型,例如:type='CS'代表股票。默认是所有类型 :param date: 查询时间点 :type date: `str` | `datetime` | `date` :return: `pandas DataFrame` 所有合约的基本信息。 其中type参数传入的合约类型和对应的解释如下: ========================= =================================================== 合约类型 说明 ========================= =================================================== CS Common Stock, 即股票 ETF Exchange Traded Fund, 即交易所交易基金 LOF Listed Open-Ended Fund,即上市型开放式基金 FenjiMu Fenji Mu Fund, 即分级母基金 FenjiA Fenji A Fund, 即分级A类基金 FenjiB Fenji B Funds, 即分级B类基金 INDX Index, 即指数 Future Futures,即期货,包含股指、国债和商品期货 ========================= =================================================== :example: 获取中国市场所有分级基金的基础信息: .. code-block:: python3 :linenos: [In]all_instruments('FenjiA') [Out] abbrev_symbol order_book_id product sector_code symbol 0 CYGA 150303.XSHE null null 华安创业板50A 1 JY500A 150088.XSHE null null 金鹰500A 2 TD500A 150053.XSHE null null 泰达稳健 3 HS500A 150110.XSHE null null 华商500A 4 QSAJ 150235.XSHE null null 鹏华证券A ... """ env = Environment.get_instance() if date is None: dt = env.trading_dt else: dt = pd.Timestamp(date).to_pydatetime() dt = min(dt, env.trading_dt) if type is not None: if isinstance(type, six.string_types): type = [type] types = set() for t in type: if t == 'Stock': types.add('CS') elif t == 'Fund': types.update(['ETF', 'LOF', 'SF', 'FenjiA', 'FenjiB', 'FenjiMu']) else: types.add(t) else: types = None result = env.data_proxy.all_instruments(types, dt) if types is not None and len(types) == 1: return pd.DataFrame([i._ins_dict for i in result]) return pd.DataFrame( [[i.order_book_id, i.symbol, i.type, i.listed_date, i.de_listed_date] for i in result], columns=['order_book_id', 'symbol', 'type', 'listed_date', 'de_listed_date'])
def __init__(self, commission_multiplier): self.commission_multiplier = commission_multiplier self.hedge_type = HEDGE_TYPE.SPECULATION self.env = Environment.get_instance()
def config(self): return Environment.get_instance().config
def update_last_price(self): price = Environment.get_instance().get_last_price(self._order_book_id) if price == price: # 过滤掉 nan self._last_price = price
def _frozen_cash_of_order(order): order_cost = order.frozen_price * order.quantity if order.side == SIDE.BUY else 0 return order_cost + Environment.get_instance( ).get_order_transaction_cost(DEFAULT_ACCOUNT_TYPE.STOCK, order)
def now(self): """ [datetime.datetime] 当前 Bar 所对应的时间 """ return Environment.get_instance().calendar_dt
def __init__(self): self._set = set() Environment.get_instance().event_bus.prepend_listener( EVENT.AFTER_TRADING, self._clear_de_listed)
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 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 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.buy_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 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 [] 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 margin_rate(self): env = Environment.get_instance() margin_info = env.data_proxy.get_margin_info(self.order_book_id) margin_multiplier = env.config.base.margin_multiplier return margin_info['long_margin_ratio'] * margin_multiplier
def contract_multiplier(self): return Environment.get_instance().get_instrument( self.order_book_id).contract_multiplier
def _margin_of(self, quantity, price): env = Environment.get_instance() instrument = env.data_proxy.instruments(self.order_book_id) return quantity * instrument.contract_multiplier * price * self.margin_rate
def open_orders(self): return Environment.get_instance().broker.get_open_orders( self.order_book_id)
def _frozen_cash_of_order(order): order_cost = margin_of( order.order_book_id, order.quantity, order.frozen_price ) if order.position_effect == POSITION_EFFECT.OPEN else 0 return order_cost + Environment.get_instance( ).get_order_transaction_cost(DEFAULT_ACCOUNT_TYPE.FUTURE, order)
def now_time_str(str_format="%H:%M:%S"): return Environment.get_instance().trading_dt.strftime(str_format)
def register_event(self): event_bus = Environment.get_instance().event_bus event_bus.prepend_listener(EVENT.POST_SETTLEMENT, lambda e: self._apply_settlement()) event_bus.add_listener(EVENT.TRADE, lambda e: self.apply_trade(e.trade))
def tick_size(self): return Environment.get_instance().data_proxy.get_tick_size( self.order_book_id)
def suspended(self): if self.isnan: return True return Environment.get_instance().data_proxy.is_suspended(self._instrument.order_book_id, int(self._data['datetime'] // 1000000))
def __init__(self): self._env = Environment.get_instance()
def items(self): return ((o, self.__getitem__(o)) for o in Environment.get_instance().get_universe())
def init_fixture(self): from rqrobot.utils import RqAttrDict from rqrobot.environment import Environment super(EnvironmentFixture, self).init_fixture() self.env = Environment(RqAttrDict(self.env_config))
def keys(self): return (o for o in Environment.get_instance().get_universe())
def run_info(self): """ [:class:`~RunInfo`] 运行信息 """ config = Environment.get_instance().config return RunInfo(config)
def values(self): return (self.__getitem__(o) for o in Environment.get_instance().get_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 | 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 __contains__(self, o): return o in Environment.get_instance().get_universe()
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] 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() == 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 __len__(self): return len(Environment.get_instance().get_universe())
def get_positions(): booking = Environment.get_instance().booking if not booking: raise RuntimeError(_("Booking has not been set, please check your broker configuration.")) return booking.get_positions()
def _get_tax(self, order_book_id, side, cost_money): instrument = Environment.get_instance().get_instrument(order_book_id) if instrument.type != 'CS': return 0 return cost_money * self.tax_rate if side == SIDE.SELL else 0