rqalpha.model.order 源代码

# -*- coding: utf-8 -*-
# 版权所有 2019 深圳米筐科技有限公司(下称“米筐科技”)
#
# 除非遵守当前许可,否则不得使用本软件。
#
#     * 非商业用途(非商业用途指个人出于非商业目的使用本软件,或者高校、研究所等非营利机构出于教育、科研等目的使用本软件):
#         遵守 Apache License 2.0(下称“Apache 2.0 许可”),
#         您可以在以下位置获得 Apache 2.0 许可的副本:http://www.apache.org/licenses/LICENSE-2.0。
#         除非法律有要求或以书面形式达成协议,否则本软件分发时需保持当前许可“原样”不变,且不得附加任何条件。
#
#     * 商业用途(商业用途指个人出于任何商业目的使用本软件,或者法人或其他组织出于任何目的使用本软件):
#         未经米筐科技授权,任何个人不得出于任何商业目的使用本软件(包括但不限于向第三方提供、销售、出租、出借、转让本软件、
#         本软件的衍生产品、引用或借鉴了本软件功能或源代码的产品或服务),任何法人或其他组织不得出于任何目的使用本软件,
#         否则米筐科技有权追究相应的知识产权侵权责任。
#         在此前提下,对本软件的使用同样需要遵守 Apache 2.0 许可,Apache 2.0 许可与本许可冲突之处,以本许可为准。
#         详细的授权流程,请联系 public@ricequant.com 获取。

import time
from decimal import Decimal

import numpy as np

from rqalpha.const import ORDER_STATUS, ORDER_TYPE, SIDE, POSITION_EFFECT, POSITION_DIRECTION, ALGO
from rqalpha.utils import id_gen, decimal_rounding_floor, get_position_direction
from rqalpha.utils.repr import property_repr, properties
from rqalpha.utils.logger import user_system_log
from rqalpha.environment import Environment


[文档]class Order(object): order_id_gen = id_gen(int(time.time()) * 10000) __repr__ = property_repr def __init__(self): self._order_id = None self._secondary_order_id = None self._calendar_dt = None self._trading_dt = None self._quantity = None self._order_book_id = None self._side = None self._position_effect = None self._message = None self._filled_quantity = None self._status = None self._frozen_price = None self._init_frozen_cash = None self._type = None self._avg_price = None self._transaction_cost = None self._style = None self._kwargs = {} @staticmethod def _str_to_enum(enum_class, s): return enum_class.__members__[s] def get_state(self): return { 'order_id': self._order_id, 'secondary_order_id': self._secondary_order_id, 'calendar_dt': self._calendar_dt, 'trading_dt': self._trading_dt, 'order_book_id': self._order_book_id, 'quantity': self._quantity, 'side': self._side, 'position_effect': self._position_effect, 'message': self._message, 'filled_quantity': self._filled_quantity, 'status': self._status, 'frozen_price': self._frozen_price, 'type': self._type, 'transaction_cost': self._transaction_cost, 'avg_price': self._avg_price, 'kwargs': self._kwargs, } def set_state(self, d): self._order_id = d['order_id'] if 'secondary_order_id' in d: self._secondary_order_id = d['secondary_order_id'] self._calendar_dt = d['calendar_dt'] self._trading_dt = d['trading_dt'] self._order_book_id = d['order_book_id'] self._quantity = d['quantity'] self._side = SIDE[d["side"]] self._position_effect = POSITION_EFFECT[d["position_effect"]] if d["position_effect"] else None self._message = d['message'] self._filled_quantity = d['filled_quantity'] self._status = ORDER_STATUS[d["status"]] self._frozen_price = d['frozen_price'] self._type = ORDER_TYPE[d["type"]] self._transaction_cost = d['transaction_cost'] self._avg_price = d['avg_price'] self._kwargs = d['kwargs'] @classmethod def __from_create__(cls, order_book_id, quantity, side, style, position_effect, **kwargs): env = Environment.get_instance() order = cls() order._order_id = next(order.order_id_gen) order._calendar_dt = env.calendar_dt order._trading_dt = env.trading_dt order._quantity = quantity order._order_book_id = order_book_id order._side = side order._position_effect = position_effect order._message = "" order._filled_quantity = 0 order._status = ORDER_STATUS.PENDING_NEW order._style = style if isinstance(style, LimitOrder): if env.config.base.round_price: tick_size = env.data_proxy.get_tick_size(order_book_id) style.round_price(tick_size) order._frozen_price = style.get_limit_price() order._type = ORDER_TYPE.LIMIT elif isinstance(style, ALGO_ORDER_STYLES): algo_price, _ = env.data_proxy.get_algo_bar(order_book_id, style, env.calendar_dt) order._frozen_price = env.get_last_price(order_book_id) if np.isnan(algo_price) else algo_price order._type = ORDER_TYPE.ALGO else: order._frozen_price = env.get_last_price(order_book_id) order._type = ORDER_TYPE.MARKET order._avg_price = 0 order._transaction_cost = 0 order._kwargs = kwargs return order @property def order_id(self): # type: () -> int """ [int] 唯一标识订单的id """ return self._order_id @property def secondary_order_id(self): """ [str] 实盘交易中交易所产生的订单ID """ return self._secondary_order_id @property def trading_datetime(self): """ [datetime.datetime] 订单的交易日期(对应期货夜盘) """ return self._trading_dt @property def datetime(self): """ [datetime.datetime] 订单创建时间 """ return self._calendar_dt @property def quantity(self): """ [int] 订单数量 """ if np.isnan(self._quantity): raise RuntimeError("Quantity of order {} is not supposed to be nan.".format(self.order_id)) return self._quantity @property def unfilled_quantity(self): """ [int] 订单未成交数量 """ return self.quantity - self.filled_quantity @property def order_book_id(self): """ [str] 合约代码 """ return self._order_book_id @property def side(self): # type: () -> SIDE """ [SIDE] 订单方向 """ return self._side @property def position_effect(self): """ [POSITION_EFFECT] 订单开平(期货专用) """ if self._position_effect is None: if self._side == SIDE.BUY: return POSITION_EFFECT.OPEN else: return POSITION_EFFECT.CLOSE return self._position_effect @property def position_direction(self): # type: () -> POSITION_DIRECTION return get_position_direction(self._side, self._position_effect) @property def message(self): """ [str] 信息。比如拒单时候此处会提示拒单原因 """ return self._message @property def filled_quantity(self): """ [int] 订单已成交数量 """ if np.isnan(self._filled_quantity): raise RuntimeError("Filled quantity of order {} is not supposed to be nan.".format(self.order_id)) return self._filled_quantity @property def status(self): """ [ORDER_STATUS] 订单状态 """ return self._status @property def price(self): """ [float] 订单价格,只有在订单类型为'限价单'的时候才有意义 """ return 0 if self.type == ORDER_TYPE.MARKET else self.frozen_price @property def type(self): """ [ORDER_TYPE] 订单类型 """ return self._type @property def style(self): """ [ORDER_STYLE] 订单类型 """ return self._style @property def avg_price(self): """ [float] 成交均价 """ return self._avg_price @property def transaction_cost(self): """ [float] 费用 """ return self._transaction_cost @property def frozen_price(self): """ [float] 冻结价格 """ if np.isnan(self._frozen_price): raise RuntimeError("Frozen price of order {} is not supposed to be nan.".format(self.order_id)) return self._frozen_price @property def init_frozen_cash(self): """ [float] 冻结资金 """ if np.isnan(self._init_frozen_cash): raise RuntimeError("Frozen cash of order {} is not supposed to be nan.".format(self.order_id)) return self._init_frozen_cash @property def kwargs(self): return self._kwargs def __getattr__(self, item): try: return self.__dict__["_kwargs"][item] except KeyError: raise AttributeError("'{}' object has no attribute '{}'".format(self.__class__.__name__, item)) def is_final(self): return self._status not in { ORDER_STATUS.PENDING_NEW, ORDER_STATUS.ACTIVE, ORDER_STATUS.PENDING_CANCEL } def is_active(self): return self.status == ORDER_STATUS.ACTIVE def active(self): self._status = ORDER_STATUS.ACTIVE def set_pending_cancel(self): if not self.is_final(): self._status = ORDER_STATUS.PENDING_CANCEL def fill(self, trade): quantity = trade.last_quantity assert self.filled_quantity + quantity <= self.quantity new_quantity = self._filled_quantity + quantity self._transaction_cost += trade.commission + trade.tax if trade.position_effect != POSITION_EFFECT.MATCH: self._avg_price = (self._avg_price * self._filled_quantity + trade.last_price * quantity) / new_quantity self._filled_quantity = new_quantity if self.unfilled_quantity == 0: self._status = ORDER_STATUS.FILLED 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 set_frozen_price(self, value): self._frozen_price = value def set_frozen_cash(self, value): self._init_frozen_cash = value def set_secondary_order_id(self, secondary_order_id): self._secondary_order_id = str(secondary_order_id) def __simple_object__(self): return properties(self)
class OrderStyle(object): def get_limit_price(self): raise NotImplementedError class AlgoOrder(OrderStyle): __repr__ = ORDER_TYPE.ALGO.__repr__ def __init__(self, start_min, end_min): self.start_min = start_min self.end_min = end_min def get_limit_price(self): return None
[文档]class TWAPOrder(AlgoOrder): TYPE = ALGO.TWAP __repr__ = ALGO.TWAP.__repr__
[文档]class VWAPOrder(AlgoOrder): TYPE = ALGO.VWAP __repr__ = ALGO.VWAP.__repr__
[文档]class MarketOrder(OrderStyle): __repr__ = ORDER_TYPE.MARKET.__repr__ def get_limit_price(self): return None
[文档]class LimitOrder(OrderStyle): __repr__ = ORDER_TYPE.LIMIT.__repr__ def __init__(self, limit_price): self.limit_price = float(limit_price) def get_limit_price(self): return self.limit_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))
ALGO_ORDER_STYLES = (VWAPOrder, TWAPOrder) ALL_ORDER_STYPES = (LimitOrder, MarketOrder, TWAPOrder, VWAPOrder)