Пример #1
0
class FutureAccount(AssetAccount):

    forced_liquidation = True

    __abandon_properties__ = [
        "holding_pnl",
        "realized_pnl",
    ]

    def fast_forward(self, orders, trades=None):
        # 计算 Positions
        if trades:
            close_trades = []
            # 先处理开仓
            for trade in trades:
                if trade.exec_id in self._backward_trade_set:
                    continue
                if trade.position_effect == POSITION_EFFECT.OPEN:
                    self._apply_trade(trade)
                else:
                    close_trades.append(trade)
            # 后处理平仓
            for trade in close_trades:
                self._apply_trade(trade)

        # 计算 Frozen Cash
        self._frozen_cash = sum(
            self._frozen_cash_of_order(order) for order in orders
            if order.is_active())

    def order(self, order_book_id, quantity, style, target=False):
        position = self.positions[order_book_id]
        if target:
            # For order_to
            quantity = quantity - position.buy_quantity + position.sell_quantity
        orders = []
        if quantity > 0:
            sell_old_quantity, sell_today_quantity = position.sell_old_quantity, position.sell_today_quantity
            # 平昨仓
            if sell_old_quantity > 0:
                orders.append(
                    order(order_book_id, min(quantity, sell_old_quantity),
                          SIDE.BUY, POSITION_EFFECT.CLOSE, style))
                quantity -= sell_old_quantity
            if quantity <= 0:
                return orders
            # 平今仓
            if sell_today_quantity > 0:
                orders.append(
                    order(order_book_id, min(quantity, sell_today_quantity),
                          SIDE.BUY, POSITION_EFFECT.CLOSE_TODAY, style))
                quantity -= sell_today_quantity
            if quantity <= 0:
                return orders
            # 开多仓
            orders.append(
                order(order_book_id, quantity, SIDE.BUY, POSITION_EFFECT.OPEN,
                      style))
            return orders
        else:
            # 平昨仓
            quantity *= -1
            buy_old_quantity, buy_today_quantity = position.buy_old_quantity, position.buy_today_quantity
            if buy_old_quantity > 0:
                orders.append(
                    order(order_book_id, min(quantity, buy_old_quantity),
                          SIDE.SELL, POSITION_EFFECT.CLOSE, style))
                quantity -= min(quantity, buy_old_quantity)
            if quantity <= 0:
                return orders
            # 平今仓
            if buy_today_quantity > 0:
                orders.append(
                    order(order_book_id, min(quantity, buy_today_quantity),
                          SIDE.SELL, POSITION_EFFECT.CLOSE_TODAY, style))
                quantity -= buy_today_quantity
            if quantity <= 0:
                return orders
            # 开空仓
            orders.append(
                order(order_book_id, quantity, SIDE.SELL, POSITION_EFFECT.OPEN,
                      style))
            return orders

    def _on_order_pending_new(self, event):
        if self != event.account:
            return

        self._frozen_cash += self._frozen_cash_of_order(event.order)

    def _on_order_unsolicited_update(self, event):
        if self != event.account:
            return
        order = event.order
        if order.filled_quantity != 0:
            self._frozen_cash -= order.unfilled_quantity / order.quantity * self._frozen_cash_of_order(
                order)
        else:
            self._frozen_cash -= self._frozen_cash_of_order(event.order)

    def _on_trade(self, event):
        if self != event.account:
            return
        self._apply_trade(event.trade, event.order)

    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 _on_before_trading(self, event):
        pass

    @property
    def type(self):
        return DEFAULT_ACCOUNT_TYPE.FUTURE.name

    @staticmethod
    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 _apply_trade(self, trade, order=None):
        if trade.exec_id in self._backward_trade_set:
            return
        order_book_id = trade.order_book_id
        position = self._positions.get_or_create(order_book_id)
        position.apply_trade(trade)
        position.update_last_price()
        self._backward_trade_set.add(trade.exec_id)
        if order:
            if trade.last_quantity != order.quantity:
                self._frozen_cash -= trade.last_quantity / order.quantity * self._frozen_cash_of_order(
                    order)
            else:
                self._frozen_cash -= self._frozen_cash_of_order(order)

    @property
    def buy_margin(self):
        """
        [float] 多方向保证金
        """
        return sum(position.buy_margin
                   for position in six.itervalues(self._positions))

    @property
    def sell_margin(self):
        """
        [float] 空方向保证金
        """
        return sum(position.sell_margin
                   for position in six.itervalues(self._positions))

    # deprecated propertie
    holding_pnl = deprecated_property("holding_pnl", "position_pnl")
    realized_pnl = deprecated_property("realized_pnl", "trading_pnl")
Пример #2
0
class FuturePositionProxy(PositionProxy):

    __abandon_properties__ = PositionProxy.__abandon_properties__ + [
        "holding_pnl", "buy_holding_pnl", "sell_holding_pnl", "realized_pnl",
        "buy_realized_pnl", "sell_realized_pnl", "buy_avg_holding_price",
        "sell_avg_holding_price"
    ]

    @property
    def type(self):
        return "FUTURE"

    @property
    def margin_rate(self):
        return self._long.margin_rate

    @property
    def contract_multiplier(self):
        return self._long.contract_multiplier

    @property
    def buy_market_value(self):
        """
        [float] 多方向市值
        """
        return self._long.market_value

    @property
    def sell_market_value(self):
        """
        [float] 空方向市值
        """
        return self._short.market_value

    @property
    def buy_position_pnl(self):
        """
        [float] 多方向昨仓盈亏
        """
        return self._long.position_pnl

    @property
    def sell_position_pnl(self):
        """
        [float] 空方向昨仓盈亏
        """
        return self._short.position_pnl

    @property
    def buy_trading_pnl(self):
        """
        [float] 多方向交易盈亏
        """
        return self._long.trading_pnl

    @property
    def sell_trading_pnl(self):
        """
        [float] 空方向交易盈亏
        """
        return self._short.trading_pnl

    @property
    def buy_daily_pnl(self):
        """
        [float] 多方向每日盈亏
        """
        return self.buy_position_pnl + self.buy_trading_pnl

    @property
    def sell_daily_pnl(self):
        """
        [float] 空方向每日盈亏
        """
        return self.sell_position_pnl + self.sell_trading_pnl

    @property
    def buy_pnl(self):
        """
        [float] 买方向累计盈亏
        """
        return self._long.pnl

    @property
    def sell_pnl(self):
        """
        [float] 空方向累计盈亏
        """
        return self._short.pnl

    @property
    def buy_old_quantity(self):
        """
        [int] 多方向昨仓
        """
        return self._long.old_quantity

    @property
    def sell_old_quantity(self):
        """
        [int] 空方向昨仓
        """
        return self._short.old_quantity

    @property
    def buy_today_quantity(self):
        """
        [int] 多方向今仓
        """
        return self._long.today_quantity

    @property
    def sell_today_quantity(self):
        """
        [int] 空方向今仓
        """
        return self._short.today_quantity

    @property
    def buy_quantity(self):
        """
        [int] 多方向持仓
        """
        return self.buy_old_quantity + self.buy_today_quantity

    @property
    def sell_quantity(self):
        """
        [int] 空方向持仓
        """
        return self.sell_old_quantity + self.sell_today_quantity

    @property
    def margin(self):
        """
        [float] 保证金

        保证金 = 持仓量 * 最新价 * 合约乘数 * 保证金率

        股票保证金 = 市值 = 持仓量 * 最新价

        """
        return self._long.margin + self._short.margin

    @property
    def buy_margin(self):
        """
        [float] 多方向持仓保证金
        """
        return self._long.margin

    @property
    def sell_margin(self):
        """
        [float] 空方向持仓保证金
        """
        return self._short.margin

    @property
    def buy_avg_open_price(self):
        """
        [float] 多方向平均开仓价格
        """
        return self._long.avg_price

    @property
    def sell_avg_open_price(self):
        """
        [float] 空方向平均开仓价格
        """
        return self._short.avg_price

    @property
    def buy_transaction_cost(self):
        """
        [float] 多方向交易费率
        """
        return self._long.transaction_cost

    @property
    def sell_transaction_cost(self):
        """
        [float] 空方向交易费率
        """
        return self._short.transaction_cost

    @property
    def closable_today_sell_quantity(self):
        return self._long.today_closable

    @property
    def closable_today_buy_quantity(self):
        return self._long.today_closable

    @property
    def closable_buy_quantity(self):
        """
        [float] 可平多方向持仓
        """
        return self._long.closable

    @property
    def closable_sell_quantity(self):
        """
        [float] 可平空方向持仓
        """
        return self._short.closable

    holding_pnl = deprecated_property("holding_pnl", "position_pnl")
    buy_holding_pnl = deprecated_property("buy_holding_pnl",
                                          "buy_position_pnl")
    sell_holding_pnl = deprecated_property("sell_holding_pnl",
                                           "sell_position_pnl")
    realized_pnl = deprecated_property("realized_pnl", "trading_pnl")
    buy_realized_pnl = deprecated_property("buy_realized_pnl",
                                           "buy_trading_pnl")
    sell_realized_pnl = deprecated_property("sell_realized_pnl",
                                            "sell_trading_pnl")
    buy_avg_holding_price = deprecated_property("buy_avg_holding_price",
                                                "buy_avg_open_price")
    sell_avg_holding_price = deprecated_property("sell_avg_holding_price",
                                                 "sell_avg_open_price")
Пример #3
0
class Account(AbstractAccount):
    """
    账户,多种持仓和现金的集合。

    不同品种的合约持仓可能归属于不同的账户,如股票、转债、场内基金、ETF 期权归属于股票账户,期货、期货期权归属于期货账户
    """

    __abandon_properties__ = [
        "holding_pnl",
        "realized_pnl",
        "dividend_receivable",
    ]

    _position_types = {}  # type: Dict[INSTRUMENT_TYPE, PositionType]

    def __init__(self, type, total_cash, init_positions):
        # type: (str, float, Dict[str, int]) -> None
        self._type = type
        self._total_cash = total_cash  # 包含保证金的总资金

        self._positions = {}
        self._backward_trade_set = set()
        self._frozen_cash = 0

        self.register_event()

        for order_book_id, init_quantity in six.iteritems(init_positions):
            position_direction = POSITION_DIRECTION.LONG if init_quantity > 0 else POSITION_DIRECTION.SHORT
            self._get_or_create_pos(order_book_id, position_direction,
                                    init_quantity)

    def __repr__(self):
        positions_repr = {}
        for order_book_id, positions in six.iteritems(self._positions):
            for direction, position in six.iteritems(positions):
                if position.quantity != 0:
                    positions_repr.setdefault(
                        order_book_id, {})[direction.value] = position.quantity
        return "Account(cash={}, total_value={}, positions={})".format(
            self.cash, self.total_value, positions_repr)

    @classmethod
    def register_position_type(cls, instrument_type, position_type):
        # type: (INSTRUMENT_TYPE, PositionType) -> None
        # TODO: only can called before instantiated
        cls._position_types[instrument_type] = position_type

    def register_event(self):
        event_bus = Environment.get_instance().event_bus
        event_bus.add_listener(
            EVENT.TRADE, lambda e: self.apply_trade(e.trade, e.order)
            if e.account == self else None)
        event_bus.add_listener(EVENT.ORDER_PENDING_NEW,
                               self._on_order_pending_new)
        event_bus.add_listener(EVENT.ORDER_CREATION_REJECT,
                               self._on_order_unsolicited_update)
        event_bus.add_listener(EVENT.ORDER_UNSOLICITED_UPDATE,
                               self._on_order_unsolicited_update)
        event_bus.add_listener(EVENT.ORDER_CANCELLATION_PASS,
                               self._on_order_unsolicited_update)

        event_bus.add_listener(EVENT.PRE_BEFORE_TRADING,
                               self._on_before_trading)
        event_bus.add_listener(EVENT.SETTLEMENT, self._on_settlement)

        event_bus.prepend_listener(EVENT.BAR, self._update_last_price)
        event_bus.prepend_listener(EVENT.TICK, self._update_last_price)

    def get_state(self):
        return {
            'positions': {
                order_book_id: {
                    "long": positions[POSITION_DIRECTION.LONG].get_state(),
                    "short": positions[POSITION_DIRECTION.SHORT].get_state()
                }
                for order_book_id, positions in six.iteritems(self._positions)
            },
            'frozen_cash': self._frozen_cash,
            "total_cash": self._total_cash,
            'backward_trade_set': list(self._backward_trade_set),
        }

    def set_state(self, state):
        self._frozen_cash = state['frozen_cash']
        self._backward_trade_set = set(state['backward_trade_set'])

        self._positions.clear()
        for order_book_id, positions_state in six.iteritems(
                state['positions']):
            for direction in POSITION_DIRECTION:
                state = positions_state[direction]
                position = self._get_or_create_pos(order_book_id, direction)
                position.set_state(state)
        if "total_cash" in state:
            self._total_cash = state["total_cash"]
        else:
            # forward compatible
            total_cash = state["static_total_valueu"]
            for p in self._iter_pos():
                if p._instrument.type == INSTRUMENT_TYPE.FUTURE:
                    continue
                # FIXME: not exactly right
                try:
                    total_cash -= p.equity
                except RuntimeError:
                    total_cash -= p.prev_close * p.quantity

        # forward compatible
        if "dividend_receivable" in state:
            for order_book_id, dividend in six.iteritems(
                    state["dividend_receivable"]):
                self._get_or_create_pos(
                    order_book_id,
                    POSITION_DIRECTION.LONG)._dividend_receivable = (
                        dividend["payable_date"],
                        dividend["quantity"] * dividend["dividend_per_share"])
        if "pending_transform" in state:
            for order_book_id, transform_info in six.iteritems(
                    state["pending_transform"]):
                self._get_or_create_pos(order_book_id, POSITION_DIRECTION.LONG
                                        )._pending_transform = transform_info

    def fast_forward(self, orders=None, trades=None):
        if trades:
            close_trades = []
            # 先处理开仓
            for trade in trades:
                if trade.exec_id in self._backward_trade_set:
                    continue
                if trade.position_effect == POSITION_EFFECT.OPEN:
                    self.apply_trade(trade)
                else:
                    close_trades.append(trade)
            # 后处理平仓
            for trade in close_trades:
                self.apply_trade(trade)

        # 计算 Frozen Cash
        if orders:
            self._frozen_cash = sum(
                self._frozen_cash_of_order(order) for order in orders
                if order.is_active())

    def get_positions(self):
        # type: () -> Iterable[BasePosition]
        """
        获取所有持仓对象列表,
        """
        return self._iter_pos()

    def get_position(self, order_book_id, direction):
        # type: (str, POSITION_DIRECTION) -> BasePosition
        """
        获取某个标的的持仓对象

        :param order_book_id: 标的编号
        :param direction: 持仓方向

        """
        try:
            return self._positions[order_book_id][direction]
        except KeyError:
            instrument_type = Environment.get_instance(
            ).data_proxy.instruments(order_book_id).type
            position_type = self._position_types.get(instrument_type,
                                                     BasePosition)
            return position_type(order_book_id, direction)

    def calc_close_today_amount(self, order_book_id, trade_amount,
                                position_direction):
        return self._get_or_create_pos(
            order_book_id,
            position_direction).calc_close_today_amount(trade_amount)

    @property
    def type(self):
        return self._type

    @property
    @lru_cache(None)
    def positions(self):
        return PositionProxyDict(self._positions, self._position_types)

    @property
    def frozen_cash(self):
        # type: () -> float
        """
        冻结资金
        """
        return self._frozen_cash

    @property
    def cash(self):
        # type: () -> float
        """
        可用资金
        """
        return self._total_cash - self.margin - self._frozen_cash

    @property
    def market_value(self):
        # type: () -> float
        """
        [float] 市值
        """
        return sum(p.market_value *
                   (1 if p.direction == POSITION_DIRECTION.LONG else -1)
                   for p in self._iter_pos())

    @property
    def transaction_cost(self):
        # type: () -> float
        """
        总费用
        """
        return sum(p.transaction_cost for p in self._iter_pos())

    @property
    def margin(self):
        # type: () -> float
        """
        总保证金
        """
        return sum(p.margin for p in self._iter_pos())

    @property
    def buy_margin(self):
        # type: () -> float
        """
        多方向保证金
        """
        return sum(p.margin for p in self._iter_pos(POSITION_DIRECTION.LONG))

    @property
    def sell_margin(self):
        # type: () -> float
        """
        空方向保证金
        """
        return sum(p.margin for p in self._iter_pos(POSITION_DIRECTION.SHORT))

    @property
    def daily_pnl(self):
        # type: () -> float
        """
        当日盈亏
        """
        return self.trading_pnl + self.position_pnl - self.transaction_cost

    @property
    def equity(self):
        # type: () -> float
        """
        总权益
        """
        return sum(p.equity for p in self._iter_pos())

    @property
    def total_value(self):
        # type: () -> float
        """
        账户总权益
        """
        return self._total_cash + self.equity

    @property
    def total_cash(self):
        # type: () -> float
        """
        账户总资金
        """
        return self._total_cash - self.margin

    @property
    def position_pnl(self):
        # type: () -> float
        """
        昨仓盈亏
        """
        return sum(p.position_pnl for p in self._iter_pos())

    @property
    def trading_pnl(self):
        # type: () -> float
        """
        交易盈亏
        """
        return sum(p.trading_pnl for p in self._iter_pos())

    def position_validator_enabled(self, order_book_id):  # type: (str) -> bool
        return self._get_or_create_pos(
            order_book_id, POSITION_DIRECTION.LONG).position_validator_enabled

    def _on_before_trading(self, _):
        trading_date = Environment.get_instance().trading_dt.date()
        for position in self._iter_pos():
            self._total_cash += position.before_trading(trading_date)

    def _on_settlement(self, _):
        trading_date = Environment.get_instance().trading_dt.date()

        for order_book_id, positions in list(six.iteritems(self._positions)):
            for position in six.itervalues(positions):
                delta_cash = position.settlement(trading_date)
                self._total_cash += delta_cash

        for order_book_id, positions in list(six.iteritems(self._positions)):
            if all(p.quantity == 0 for p in six.itervalues(positions)):
                del self._positions[order_book_id]

        self._backward_trade_set.clear()

        # 如果 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 _on_order_pending_new(self, event):
        if event.account != self:
            return
        order = event.order
        self._frozen_cash += self._frozen_cash_of_order(order)

    def _on_order_unsolicited_update(self, event):
        if event.account != self:
            return
        order = event.order
        if order.filled_quantity != 0:
            self._frozen_cash -= order.unfilled_quantity / order.quantity * self._frozen_cash_of_order(
                order)
        else:
            self._frozen_cash -= self._frozen_cash_of_order(event.order)

    def apply_trade(self, trade, order=None):
        # type: (Trade, Optional[Order]) -> None
        if trade.exec_id in self._backward_trade_set:
            return
        order_book_id = trade.order_book_id
        if trade.position_effect == POSITION_EFFECT.MATCH:
            self._total_cash += self._get_or_create_pos(
                order_book_id, POSITION_DIRECTION.LONG).apply_trade(trade)
            self._total_cash += self._get_or_create_pos(
                order_book_id, POSITION_DIRECTION.SHORT).apply_trade(trade)
        else:
            self._total_cash += self._get_or_create_pos(
                order_book_id, trade.position_direction).apply_trade(trade)
        self._backward_trade_set.add(trade.exec_id)
        if order and trade.position_effect != POSITION_EFFECT.MATCH:
            if trade.last_quantity != order.quantity:
                self._frozen_cash -= trade.last_quantity / order.quantity * self._frozen_cash_of_order(
                    order)
            else:
                self._frozen_cash -= self._frozen_cash_of_order(order)

    def _iter_pos(self, direction=None):
        # type: (Optional[POSITION_DIRECTION]) -> Iterable[BasePosition]
        if direction:
            return (p[direction] for p in six.itervalues(self._positions))
        else:
            return chain(
                *[six.itervalues(p) for p in six.itervalues(self._positions)])

    def _get_or_create_pos(self, order_book_id, direction, init_quantity=0):
        # type: (str, Union[str, POSITION_DIRECTION], Optional[int]) -> BasePosition
        if order_book_id not in self._positions:
            instrument_type = Environment.get_instance(
            ).data_proxy.instruments(order_book_id).type
            position_type = self._position_types.get(instrument_type,
                                                     BasePosition)
            if direction == POSITION_DIRECTION.LONG:
                long_init_position, short_init_position = init_quantity, 0
            else:
                long_init_position, short_init_position = 0, init_quantity

            positions = self._positions.setdefault(
                order_book_id, {
                    POSITION_DIRECTION.LONG:
                    position_type(order_book_id, POSITION_DIRECTION.LONG,
                                  long_init_position),
                    POSITION_DIRECTION.SHORT:
                    position_type(order_book_id, POSITION_DIRECTION.SHORT,
                                  short_init_position)
                })
        else:
            positions = self._positions[order_book_id]
        return positions[direction]

    def _update_last_price(self, _):
        env = Environment.get_instance()
        for order_book_id, positions in six.iteritems(self._positions):
            price = env.get_last_price(order_book_id)
            if price == price:
                for position in six.itervalues(positions):
                    position.update_last_price(price)

    def _frozen_cash_of_order(self, order):
        env = Environment.get_instance()
        if order.position_effect == POSITION_EFFECT.OPEN:
            instrument = env.data_proxy.instruments(order.order_book_id)
            order_cost = instrument.calc_cash_occupation(
                order.frozen_price, order.quantity, order.position_direction)
        else:
            order_cost = 0
        return order_cost + env.get_order_transaction_cost(order)

    holding_pnl = deprecated_property("holding_pnl", "position_pnl")
    realized_pnl = deprecated_property("realized_pnl", "trading_pnl")
Пример #4
0
class Account:
    """
    账户,多种持仓和现金的集合。

    不同品种的合约持仓可能归属于不同的账户,如股票、转债、场内基金、ETF 期权归属于股票账户,期货、期货期权归属于期货账户
    """

    __abandon_properties__ = [
        "holding_pnl",
        "realized_pnl",
        "dividend_receivable",
    ]

    def __init__(self, type, total_cash, init_positions):
        # type: (str, float, Dict[str, int]) -> None
        self._type = type
        self._total_cash = total_cash  # 包含保证金的总资金

        self._positions = {}
        self._backward_trade_set = set()
        self._frozen_cash = 0

        self.register_event()

        self._management_fee_calculator_func = lambda account, rate: account.total_value * rate
        self._management_fee_rate = 0.0
        self._management_fees = 0.0

        for order_book_id, init_quantity in init_positions.items():
            position_direction = POSITION_DIRECTION.LONG if init_quantity > 0 else POSITION_DIRECTION.SHORT
            self._get_or_create_pos(order_book_id, position_direction,
                                    init_quantity)

    def __repr__(self):
        positions_repr = {}
        for order_book_id, positions in self._positions.items():
            for direction, position in positions.items():
                if position.quantity != 0:
                    positions_repr.setdefault(
                        order_book_id, {})[direction.value] = position.quantity
        return "Account(cash={}, total_value={}, positions={})".format(
            self.cash, self.total_value, positions_repr)

    def register_event(self):
        event_bus = Environment.get_instance().event_bus
        event_bus.add_listener(
            EVENT.TRADE, lambda e: self.apply_trade(e.trade, e.order)
            if e.account == self else None)
        event_bus.add_listener(EVENT.ORDER_PENDING_NEW,
                               self._on_order_pending_new)
        event_bus.add_listener(EVENT.ORDER_CREATION_REJECT,
                               self._on_order_unsolicited_update)
        event_bus.add_listener(EVENT.ORDER_UNSOLICITED_UPDATE,
                               self._on_order_unsolicited_update)
        event_bus.add_listener(EVENT.ORDER_CANCELLATION_PASS,
                               self._on_order_unsolicited_update)

        event_bus.add_listener(EVENT.PRE_BEFORE_TRADING,
                               self._on_before_trading)
        event_bus.add_listener(EVENT.SETTLEMENT, self._on_settlement)

        event_bus.prepend_listener(EVENT.BAR, self._update_last_price)
        event_bus.prepend_listener(EVENT.TICK, self._update_last_price)

    def get_state(self):
        return {
            'positions': {
                order_book_id: {
                    POSITION_DIRECTION.LONG:
                    positions[POSITION_DIRECTION.LONG].get_state(),
                    POSITION_DIRECTION.SHORT:
                    positions[POSITION_DIRECTION.SHORT].get_state()
                }
                for order_book_id, positions in self._positions.items()
            },
            'frozen_cash': self._frozen_cash,
            "total_cash": self._total_cash,
            'backward_trade_set': list(self._backward_trade_set),
        }

    def set_state(self, state):
        self._frozen_cash = state['frozen_cash']
        self._backward_trade_set = set(state['backward_trade_set'])
        self._total_cash = state["total_cash"]

        self._positions.clear()
        for order_book_id, positions_state in state['positions'].items():
            for direction in POSITION_DIRECTION:
                position = self._get_or_create_pos(order_book_id, direction)
                if direction in positions_state.keys():
                    position.set_state(positions_state[direction])
                else:
                    position.set_state(positions_state[direction.lower()])

    def fast_forward(self, orders=None, trades=None):
        if trades:
            close_trades = []
            # 先处理开仓
            for trade in trades:
                if trade.exec_id in self._backward_trade_set:
                    continue
                if trade.position_effect == POSITION_EFFECT.OPEN:
                    self.apply_trade(trade)
                else:
                    close_trades.append(trade)
            # 后处理平仓
            for trade in close_trades:
                self.apply_trade(trade)

        # 计算 Frozen Cash
        if orders:
            self._frozen_cash = sum(
                self._frozen_cash_of_order(order) for order in orders
                if order.is_active())

    def get_positions(self):
        # type: () -> Iterable[Position]
        """
        获取所有持仓对象列表,
        """
        return self._iter_pos()

    def get_position(self, order_book_id, direction):
        # type: (str, POSITION_DIRECTION) -> Position
        """
        获取某个标的的持仓对象

        :param order_book_id: 标的编号
        :param direction: 持仓方向

        """
        try:
            return self._positions[order_book_id][direction]
        except KeyError:
            return Position(order_book_id, direction)

    def calc_close_today_amount(self, order_book_id, trade_amount,
                                position_direction):
        return self._get_or_create_pos(
            order_book_id,
            position_direction).calc_close_today_amount(trade_amount)

    @property
    def type(self):
        return self._type

    @property
    @lru_cache(None)
    def positions(self):
        return PositionProxyDict(self._positions)

    @property
    def frozen_cash(self):
        # type: () -> float
        """
        冻结资金
        """
        return self._frozen_cash

    @property
    def cash(self):
        # type: () -> float
        """
        可用资金
        """
        return self._total_cash - self.margin - self._frozen_cash

    @property
    def market_value(self):
        # type: () -> float
        """
        [float] 市值
        """
        return sum(p.market_value *
                   (1 if p.direction == POSITION_DIRECTION.LONG else -1)
                   for p in self._iter_pos())

    @property
    def transaction_cost(self):
        # type: () -> float
        """
        总费用
        """
        return sum(p.transaction_cost for p in self._iter_pos())

    @property
    def margin(self):
        # type: () -> float
        """
        总保证金
        """
        return sum(p.margin for p in self._iter_pos())

    @property
    def buy_margin(self):
        # type: () -> float
        """
        多方向保证金
        """
        return sum(p.margin for p in self._iter_pos(POSITION_DIRECTION.LONG))

    @property
    def sell_margin(self):
        # type: () -> float
        """
        空方向保证金
        """
        return sum(p.margin for p in self._iter_pos(POSITION_DIRECTION.SHORT))

    @property
    def daily_pnl(self):
        # type: () -> float
        """
        当日盈亏
        """
        return self.trading_pnl + self.position_pnl - self.transaction_cost

    @property
    def equity(self):
        # type: () -> float
        """
        持仓总权益
        """
        return sum(p.equity for p in self._iter_pos())

    @property
    def total_value(self):
        # type: () -> float
        """
        账户总权益
        """
        return self._total_cash + self.equity

    @property
    def total_cash(self):
        # type: () -> float
        """
        账户总资金
        """
        return self._total_cash - self.margin

    @property
    def position_pnl(self):
        # type: () -> float
        """
        昨仓盈亏
        """
        return sum(p.position_pnl for p in self._iter_pos())

    @property
    def trading_pnl(self):
        # type: () -> float
        """
        交易盈亏
        """
        return sum(p.trading_pnl for p in self._iter_pos())

    def _on_before_trading(self, _):
        trading_date = Environment.get_instance().trading_dt.date()
        for position in self._iter_pos():
            self._total_cash += position.before_trading(trading_date)

    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 _on_order_pending_new(self, event):
        if event.account != self:
            return
        order = event.order
        self._frozen_cash += self._frozen_cash_of_order(order)

    def _on_order_unsolicited_update(self, event):
        if event.account != self:
            return
        order = event.order
        if order.filled_quantity != 0:
            self._frozen_cash -= order.unfilled_quantity / order.quantity * self._frozen_cash_of_order(
                order)
        else:
            self._frozen_cash -= self._frozen_cash_of_order(event.order)

    def apply_trade(self, trade, order=None):
        # type: (Trade, Optional[Order]) -> None
        if trade.exec_id in self._backward_trade_set:
            return
        order_book_id = trade.order_book_id
        if order and trade.position_effect != POSITION_EFFECT.MATCH:
            if trade.last_quantity != order.quantity:
                self._frozen_cash -= trade.last_quantity / order.quantity * self._frozen_cash_of_order(
                    order)
            else:
                self._frozen_cash -= self._frozen_cash_of_order(order)
        if trade.position_effect == POSITION_EFFECT.MATCH:
            delta_cash = self._get_or_create_pos(
                order_book_id, POSITION_DIRECTION.LONG
            ).apply_trade(trade) + self._get_or_create_pos(
                order_book_id, POSITION_DIRECTION.SHORT).apply_trade(trade)
            self._total_cash += delta_cash
        else:
            delta_cash = self._get_or_create_pos(
                order_book_id, trade.position_direction).apply_trade(trade)
            self._total_cash += delta_cash
        self._backward_trade_set.add(trade.exec_id)

    def _iter_pos(self, direction=None):
        # type: (Optional[POSITION_DIRECTION]) -> Iterable[Position]
        if direction:
            return (p[direction] for p in six.itervalues(self._positions))
        else:
            return chain(
                *[six.itervalues(p) for p in six.itervalues(self._positions)])

    def _get_or_create_pos(self, order_book_id, direction, init_quantity=0):
        # type: (str, Union[str, POSITION_DIRECTION], Optional[int]) -> Position
        if order_book_id not in self._positions:
            if direction == POSITION_DIRECTION.LONG:
                long_init_position, short_init_position = init_quantity, 0
            else:
                long_init_position, short_init_position = 0, init_quantity

            positions = self._positions.setdefault(
                order_book_id, {
                    POSITION_DIRECTION.LONG:
                    Position(order_book_id, POSITION_DIRECTION.LONG,
                             long_init_position),
                    POSITION_DIRECTION.SHORT:
                    Position(order_book_id, POSITION_DIRECTION.SHORT,
                             short_init_position)
                })
        else:
            positions = self._positions[order_book_id]
        return positions[direction]

    def _update_last_price(self, _):
        env = Environment.get_instance()
        for order_book_id, positions in self._positions.items():
            price = env.get_last_price(order_book_id)
            if price == price:
                for position in six.itervalues(positions):
                    position.update_last_price(price)

    def _frozen_cash_of_order(self, order):
        env = Environment.get_instance()
        if order.position_effect == POSITION_EFFECT.OPEN:
            instrument = env.data_proxy.instruments(order.order_book_id)
            order_cost = instrument.calc_cash_occupation(
                order.frozen_price, order.quantity, order.position_direction)
        else:
            order_cost = 0
        return order_cost + env.get_order_transaction_cost(order)

    def _management_fee(self):
        # type: () -> float
        """计算账户管理费用"""
        if self._management_fee_rate == 0:
            return 0
        fee = self._management_fee_calculator_func(self,
                                                   self._management_fee_rate)
        return fee

    def register_management_fee_calculator(self, calculator):
        # type: (Callable[[Account, float], float]) -> None
        """
        设置管理费用计算逻辑
        该方法需要传入一个函数

        .. code-block:: python

        def management_fee_calculator(account, rate):
            return len(account.positions) * rate

        def init(context):
            context.portfolio.accounts["STOCK"].set_management_fee_calculator(management_fee_calculator)

        """
        self._management_fee_calculator_func = calculator

    def set_management_fee_rate(self, rate):
        # type: (float) -> None
        """管理费用计算费率"""
        self._management_fee_rate = rate

    @property
    def management_fees(self):
        # type: () -> float
        """该账户的管理费用总计"""
        return self._management_fees

    def deposit_withdraw(self, amount):
        # type: (float) -> None
        """出入金"""
        if (amount < 0) and (self.cash < amount * -1):
            raise ValueError(
                _('insufficient cash, current {}, target withdrawal {}').
                format(self._total_cash, amount))
        self._total_cash += amount

    holding_pnl = deprecated_property("holding_pnl", "position_pnl")
    realized_pnl = deprecated_property("realized_pnl", "trading_pnl")
Пример #5
0
class FuturePositionProxy(AssetPositionProxy):

    __abandon_properties__ = AssetPositionProxy.__abandon_properties__ +[
        "holding_pnl",
        "buy_holding_pnl",
        "sell_holding_pnl",
        "realized_pnl",
        "buy_realized_pnl",
        "sell_realized_pnl",
        "buy_avg_holding_price",
        "sell_avg_holding_price"
    ]

    @property
    def type(self):
        return DEFAULT_ACCOUNT_TYPE.FUTURE.name

    def set_state(self, state):
        assert self.order_book_id == state['order_book_id']
        if "long" in state and "short" in state:
            super(FuturePositionProxy, self).set_state(state)
        else:
            # for compatible
            buy_old_quantity = buy_logical_old_quantity = sum(q for _, q in state.get("buy_old_holding_list", []))
            self._long.set_state({
                "old_quantity": buy_old_quantity,
                "logical_old_quantity": buy_logical_old_quantity,
                "today_quantity": sum(q for _, q in state.get("buy_today_holding_list", [])),
                "avg_price": state.get("buy_avg_open_price"),
                "trade_cost": 0,
                "transaction_cost": state.get("buy_transaction_cost")
            })

            sell_old_quantity = sell_logical_old_quantity = sum(q for _, q in state.get("sell_old_holding_list", []))
            self._long.set_state({
                "old_quantity": sell_old_quantity,
                "logical_old_quantity": sell_logical_old_quantity,
                "today_quantity": sum(q for _, q in state.get("sell_today_holding_list", [])),
                "avg_price": state.get("sell_avg_open_price"),
                "trade_cost": 0,
                "transaction_cost": state.get("sell_transaction_cost")
            })

    @property
    def margin_rate(self):
        return self._long.margin_rate

    @property
    def contract_multiplier(self):
        return self._long.contract_multiplier

    @property
    def buy_market_value(self):
        """
        [float] 多方向市值
        """
        return self._long.market_value

    @property
    def sell_market_value(self):
        """
        [float] 空方向市值
        """
        return self._short.market_value

    @property
    def buy_position_pnl(self):
        """
        [float] 多方向昨仓盈亏
        """
        return self._long.position_pnl

    @property
    def sell_position_pnl(self):
        """
        [float] 空方向昨仓盈亏
        """
        return self._short.position_pnl

    @property
    def buy_trading_pnl(self):
        """
        [float] 多方向交易盈亏
        """
        return self._long.trading_pnl

    @property
    def sell_trading_pnl(self):
        """
        [float] 空方向交易盈亏
        """
        return self._short.trading_pnl

    @property
    def buy_daily_pnl(self):
        """
        [float] 多方向每日盈亏
        """
        return self.buy_position_pnl + self.buy_trading_pnl

    @property
    def sell_daily_pnl(self):
        """
        [float] 空方向每日盈亏
        """
        return self.sell_position_pnl + self.sell_trading_pnl

    @property
    def buy_pnl(self):
        """
        [float] 买方向累计盈亏
        """
        return self._long.pnl

    @property
    def sell_pnl(self):
        """
        [float] 空方向累计盈亏
        """
        return self._short.pnl

    @property
    def buy_old_quantity(self):
        """
        [int] 多方向昨仓
        """
        return self._long.old_quantity

    @property
    def sell_old_quantity(self):
        """
        [int] 空方向昨仓
        """
        return self._short.old_quantity

    @property
    def buy_today_quantity(self):
        """
        [int] 多方向今仓
        """
        return self._long.today_quantity

    @property
    def sell_today_quantity(self):
        """
        [int] 空方向今仓
        """
        return self._short.today_quantity

    @property
    def buy_quantity(self):
        """
        [int] 多方向持仓
        """
        return self.buy_old_quantity + self.buy_today_quantity

    @property
    def sell_quantity(self):
        """
        [int] 空方向持仓
        """
        return self.sell_old_quantity + self.sell_today_quantity

    @property
    def buy_margin(self):
        """
        [float] 多方向持仓保证金
        """
        return self._long.margin

    @property
    def sell_margin(self):
        """
        [float] 空方向持仓保证金
        """
        return self._short.margin

    @property
    def buy_avg_open_price(self):
        """
        [float] 多方向平均开仓价格
        """
        return self._long.avg_price

    @property
    def sell_avg_open_price(self):
        """
        [float] 空方向平均开仓价格
        """
        return self._short.avg_price

    @property
    def buy_transaction_cost(self):
        """
        [float] 多方向交易费率
        """
        return self._long.transaction_cost

    @property
    def sell_transaction_cost(self):
        """
        [float] 空方向交易费率
        """
        return self._short.transaction_cost

    @property
    def closable_today_sell_quantity(self):
        buy_close_today_order_quantity = sum(o.unfilled_quantity for o in self.open_orders if o.side == SIDE.BUY and
                                             o.position_effect == POSITION_EFFECT.CLOSE_TODAY)
        return self.sell_today_quantity - buy_close_today_order_quantity

    @property
    def closable_today_buy_quantity(self):
        sell_close_today_order_quantity = sum(o.unfilled_quantity for o in self.open_orders if o.side == SIDE.SELL and
                                              o.position_effect == POSITION_EFFECT.CLOSE_TODAY)
        return self.buy_today_quantity - sell_close_today_order_quantity

    @property
    def closable_buy_quantity(self):
        """
        [float] 可平多方向持仓
        """
        sell_close_order_quantity = sum(o.unfilled_quantity for o in self.open_orders if o.side == SIDE.SELL and
                                        o.position_effect in (POSITION_EFFECT.CLOSE, POSITION_EFFECT.CLOSE_TODAY))
        return self.buy_quantity - sell_close_order_quantity

    @property
    def closable_sell_quantity(self):
        """
        [float] 可平空方向持仓
        """
        buy_close_order_quantity = sum(o.unfilled_quantity for o in self.open_orders if o.side == SIDE.BUY and
                                       o.position_effect in (POSITION_EFFECT.CLOSE, POSITION_EFFECT.CLOSE_TODAY))
        return self.sell_quantity - buy_close_order_quantity

    def is_de_listed(self):
        """
        判断合约是否过期
        """
        instrument = Environment.get_instance().get_instrument(self.order_book_id)
        current_date = Environment.get_instance().trading_dt
        if instrument.de_listed_date is not None and current_date >= instrument.de_listed_date:
            return True
        return False

    def cal_close_today_amount(self, trade_amount, trade_side):
        if trade_side == SIDE.SELL:
            close_today_amount = trade_amount - self.buy_old_quantity
        else:
            close_today_amount = trade_amount - self.sell_old_quantity
        return max(close_today_amount, 0)

    holding_pnl = deprecated_property("holding_pnl", "position_pnl")
    buy_holding_pnl = deprecated_property("buy_holding_pnl", "buy_position_pnl")
    sell_holding_pnl = deprecated_property("sell_holding_pnl", "sell_position_pnl")
    realized_pnl = deprecated_property("realized_pnl", "trading_pnl")
    buy_realized_pnl = deprecated_property("buy_realized_pnl", "buy_trading_pnl")
    sell_realized_pnl = deprecated_property("sell_realized_pnl", "sell_trading_pnl")
    buy_avg_holding_price = deprecated_property("buy_avg_holding_price", "buy_avg_open_price")
    sell_avg_holding_price = deprecated_property("sell_avg_holding_price", "sell_avg_open_price")