Skip to main content

Module protective

Module protective 

Source
Expand description

Broker-resident profit-target close order for open option spreads.

Schwab’s live order API rejects a stop trigger on a multi-leg complex option order (confirmed via orders preview: complexOrderStrategyType: VERTICAL/IRON_CONDOR + orderType: STOP_LIMIT + stopPrice -> HTTP 400 “Stop price must be populated only for stop orders” — the identical shape works fine on a single-leg option). A plain GTC complex NET_DEBIT limit order to close at the profit target, however, is accepted structurally. So only the profit-target side of exit protection can live on Schwab’s servers; stop-loss and DTE-close remain dependent on the agent’s own tick loop (see docs/OPTIONS_RULES.md).

Structs§

ProtectivePlaceResult
ReconcileProtectiveSummary

Functions§

build_profit_target_close_order
Build a resting GTC complex NET_DEBIT order that closes entry_order’s legs at the profit-target debit, by inverting each leg’s opening instruction (BUY_TO_OPEN -> SELL_TO_CLOSE, SELL_TO_OPEN -> BUY_TO_CLOSE) rather than re-deriving strikes/symbols.
cancel_protective_order
Cancel a resting protective order (e.g. before the mechanical tick loop closes the position early on a stop/DTE/thesis exit, to avoid a duplicate-close race).
place_profit_target_order_with_retry
Place the profit-target close order with retry. Failure here does NOT block the entry (the position is still opened without broker-side protection) — callers should record the failure on TrackedPosition::protective_order_attempts and let the reconcile pass retry.
profit_target_debit
Debit (per share) at which profit_target_pct is captured, given the entry credit. Mirrors the profit_pct formula in agent::exits: ((entry - debit) / entry) * 100.
reconcile_protective_orders
Retry placing the profit-target close order for any open position missing one. Runs every tick (called from reconcile_open_positions’s caller in tick_once) so a placement failure right after entry — or a position that predates this feature and has no entry_params to rebuild from — keeps getting retried rather than staying naked indefinitely. Positions with no stored entry_params cannot be rebuilt and are counted as unprotected but not retried (nothing to retry with).