コンテンツにスキップ

ロール戦略

概要

ポジションを次の満期に移行(ロール)する戦略です。


ロール条件

1. ITMロール

def check_itm_roll(position: Position) -> bool:
    """ITMロール条件をチェック"""
    underlying_price = get_underlying_price(position.symbol)

    if position.option_type == "PUT":
        # Put Credit Spread
        return underlying_price < position.sell_strike
    else:
        # Call Credit Spread
        return underlying_price > position.sell_strike

2. DTEロール

DTE_ROLL_THRESHOLD = 7  # 7日以下でロール

def check_dte_roll(position: Position) -> bool:
    """DTEロール条件をチェック"""
    dte = (position.expiration - date.today()).days
    return dte <= DTE_ROLL_THRESHOLD

3. デルタロール

DELTA_ROLL_THRESHOLD = 0.70

def check_delta_roll(position: Position) -> bool:
    """デルタロール条件をチェック"""
    return abs(position.sell_delta) > DELTA_ROLL_THRESHOLD

ロールフロー

sequenceDiagram
    participant PM as PositionMonitor
    participant RS as RollStrategy
    participant IB as IBKR
    participant TS as TradeStore

    PM->>RS: Check Roll Conditions
    RS-->>PM: Roll Needed

    PM->>RS: Generate Roll Order
    RS->>RS: Select New Strikes
    RS->>RS: Calculate Credit/Debit
    RS-->>PM: Roll Order

    PM->>IB: Place Roll Order
    IB-->>PM: Confirmation

    PM->>TS: Record Roll

ロール注文生成

def generate_roll_order(position: Position) -> RollOrder:
    """ロール注文を生成"""

    # 1. 新しい満期を選択
    new_expiration = select_next_expiration(
        position.symbol,
        target_dte=position.strategy.target_dte
    )

    # 2. 新しいストライクを選択
    new_sell_strike, new_buy_strike = select_strikes(
        position.symbol,
        new_expiration,
        position.option_type,
        position.spread_width
    )

    # 3. クレジット/デビット計算
    close_debit = get_spread_price(position)  # 現在ポジションを閉じるコスト
    open_credit = get_spread_price(new_sell_strike, new_buy_strike)  # 新規ポジションのクレジット

    net = open_credit - close_debit

    return RollOrder(
        close_legs=[
            OrderLeg(position.sell_option, "BUY", position.quantity),
            OrderLeg(position.buy_option, "SELL", position.quantity),
        ],
        open_legs=[
            OrderLeg(new_sell_option, "SELL", position.quantity),
            OrderLeg(new_buy_option, "BUY", position.quantity),
        ],
        net_credit=net if net > 0 else None,
        net_debit=-net if net < 0 else None,
    )

ロール実行

async def execute_roll(position: Position, roll_order: RollOrder):
    """ロールを実行"""

    # 1. 4本足のコンボ注文を発注
    result = await ibkr.place_combo_order(
        legs=roll_order.close_legs + roll_order.open_legs,
        order_type="LMT",
        limit_price=roll_order.net_credit or -roll_order.net_debit,
        tif="DAY"
    )

    # 2. トレード記録
    trade_store.record_roll(
        position_id=position.id,
        new_expiration=roll_order.new_expiration,
        new_strikes=(roll_order.new_sell_strike, roll_order.new_buy_strike),
        net_credit=roll_order.net_credit,
        net_debit=roll_order.net_debit,
    )

    # 3. ポジション更新
    position.update(
        expiration=roll_order.new_expiration,
        sell_strike=roll_order.new_sell_strike,
        buy_strike=roll_order.new_buy_strike,
    )

ロール履歴追跡

class RollHistory:
    """ロール履歴"""

    def record(self, position_id: str, roll_details: dict):
        """ロールを記録"""
        self.rolls.append({
            "position_id": position_id,
            "timestamp": datetime.now(),
            "old_expiration": roll_details["old_expiration"],
            "new_expiration": roll_details["new_expiration"],
            "old_strikes": roll_details["old_strikes"],
            "new_strikes": roll_details["new_strikes"],
            "net_credit": roll_details["net_credit"],
            "net_debit": roll_details["net_debit"],
            "reason": roll_details["reason"],
        })

    def get_roll_count(self, position_id: str) -> int:
        """ロール回数を取得"""
        return len([r for r in self.rolls if r["position_id"] == position_id])

実装ファイル

  • strategies/roll_strategy.py - ロール戦略
  • core/execution.py - ロール注文発注