Install
openclaw skills install perfect-storm-options-trader-v2Autonomous but risk-bounded options trading agent spec for the "Perfect Storm" strategy (paper trading only). Use when configuring, operating, or evaluating an OpenClaw-based discretionary options trader that scans an approved universe, grades PS+/PS- setups, selects liquid long call/put contracts, sizes conservatively, enforces strict daily risk controls, and journals every decision. Relevant when integrating OpenClaw with Alpaca (paper) via API/MCP, when writing/validating risk_config.yaml constraints, and when producing auditable trade decision objects (ENTER / HOLD / EXIT / SKIP).
openclaw skills install perfect-storm-options-trader-v2Paper trading only. This skill must never execute against a live brokerage endpoint. If live credentials or a live base URL are detected at any point, stop immediately and request explicit human confirmation before proceeding.
This skill defines the complete behavioral, governance, and execution spec for an OpenClaw agent running the Perfect Storm (PS+/PS−) options strategy.
The agent's mandate in one sentence:
Trade only when setup quality, market regime, liquidity, and risk conditions all align strongly enough to justify premium risk. "No trade" is a valid— often preferred—output.
The agent produces one of five symbolic states per scan cycle per symbol:
| State | Meaning |
|---|---|
IGNORE | Fails pre-filter; stop evaluating this symbol |
WATCHLIST | Interesting but not yet ready; monitor next cycle |
ARM_ENTRY | Setup is forming; prepare contract selection and size |
ENTER | All gates pass; submit limit order |
EXIT | Existing position must be reduced or closed now |
| File | Purpose |
|---|---|
references/AGENTS.md | Full strategy rulebook: universe, setup definitions, indicator stack, sizing/exit logic, journaling schema |
references/risk_config.yaml | Hard limits: account config, per-trade risk, daily/weekly drawdown caps, option filters |
scripts/alpaca.mjs | Minimal Alpaca REST helper for account state, positions, orders |
Always load references/risk_config.yaml at agent startup. If the file is
missing or unreadable, halt and request it before doing anything else.
Execute these phases in strict order. Any gate failure returns the symbol to
IGNORE or SKIP and logs the reason.
1. Load risk_config.yaml — halt if missing
2. Confirm APCA_API_BASE_URL == https://paper-api.alpaca.markets — halt if live
3. Fetch account state via scripts/alpaca.mjs account
4. Compute current portfolio heat (open positions × notional risk)
5. Check daily/weekly loss counters — halt new entries if limits hit
6. Confirm data feeds are live and spread data is fresh (< 60s)
If any safety check fails → set agent_state = HALTED, log reason, do not
scan.
Classify the broad market environment using SPY/QQQ as primary proxies:
| Regime | Signal Characteristics | Allowed Bias |
|---|---|---|
TRENDING_BULL | Price > 20/50/200 EMA stack; ADX > 20; shallow pullbacks | PS+ calls preferred |
TRENDING_BEAR | Price < EMA stack; ADX > 20; weak bounces | PS− puts preferred |
CHOP | Price whipping around EMAs; ADX < 20; no follow-through | Reduce size 50%; raise confidence threshold to exceptional_score |
SHOCK | VIX spike > 25%; spreads blown out; correlation breakdown | Halt new entries |
Default rule: If regime cannot be clearly classified → CHOP.
In CHOP or SHOCK, only enter if confidence_score >= exceptional_score from
risk_config.yaml (default 85).
Start from the approved universe in references/AGENTS.md. For each symbol apply:
✓ Underlying price >= min_stock_price (config)
✓ Avg daily share volume >= min_avg_daily_share_volume (config)
✓ ATR% >= min_atr_percent (config)
✓ Relative volume >= min_relative_volume (config)
✗ Earnings within avoid_earnings_days_before days → SKIP
✗ Macro event within avoid_macro_minutes_before minutes → SKIP
✗ Abnormal spread expansion detected → SKIP
Rank survivors by setup readiness, cap at max_symbols_ranked_per_cycle.
Score one point per condition met. Maximum 10 points.
| Raw Score | Normalized (×10) | Action |
|---|---|---|
| < 7 | < 70 | SKIP |
| 7–8 | 70–80 | WATCHLIST or ENTER at reduced size |
| 8–9 | 80–90 | ENTER at standard size |
| 9–10 | 90–100 | ENTER at full size (still within hard limits) |
DTE: preferred_dte_min (7) to preferred_dte_max (21)
0-DTE only if allow_0dte: true in config (default false)
Delta: delta_min (0.35) to delta_max (0.60) for standard directional trades
Tighter end (0.45–0.65) for strong trend-continuation setups
Day-trade / fast scalp: 0.25–0.45 only if spread/fill quality excellent
OI: >= min_open_interest (config)
Volume: >= min_option_volume (config)
Spread: bid-ask spread as % of mid <= max_bid_ask_spread_percent (config)
When multiple contracts qualify, rank by:
Reject if option premium is so low that one tick is > 5% of premium (lottery contract risk).
Use fixed-fraction risk logic:
max_risk_dollars = account_equity × (max_risk_per_trade_percent / 100)
contracts_raw = floor(max_risk_dollars / (premium_per_contract × 100))
contracts = min(contracts_raw, max_contracts_derived_from_position_pct)
Adjustments:
chop_size_multiplier (0.5)reduced_size_multiplier (0.5) once
reduce_size_after_consecutive_losses threshold is hitIf contracts < 1 after adjustments → SKIP (minimum size violates risk rules).
Also check:
max_open_positions not exceededmax_positions_per_symbol not exceededportfolio_heat > max_portfolio_heat_percentOrder type: LIMIT (always)
Limit price: mid-price of bid-ask at evaluation time, or slight edge inside mid
Chase guard: if current ask has moved > do_not_chase_percent (3%) above
entry plan → cancel and re-evaluate
Fill timeout: cancel_if_not_filled_seconds (45)
Never submit a market order for options. Never re-chase a missed fill blindly.
Choose the tightest applicable stop:
max_option_premium_loss_percent (35%) of entry premiumtime_stop_minutes_intraday
(45 min) → exitend_of_day_flatten_intraday: true → close all intraday
positions before session endPartial exit and runner logic per config:
At R×1.0 → sell scale_out_fractions[0] (50%) of position
At R×2.0 → sell scale_out_fractions[1] (30%) of position
Remaining (20%) = runner — move stop to break-even at R×1.0
Only allow runner in TRENDING_BULL or TRENDING_BEAR regime, never in CHOP.
Exit fully on any of:
avoid_macro_minutes_before)Do not convert an options trade into an "investment."
Map the 10-point raw checklist score to 0–100 (multiply by 10), then apply qualitative adjustments:
| Factor | Adjustment |
|---|---|
| Strong multi-timeframe agreement (3 TFs aligned) | +5 |
| Clean S/R confluence at entry | +5 |
| IV context favorable (IV not historically extreme for long premium) | +3 |
| Recent symbol behavior cooperative (not whippy last 3 sessions) | +3 |
| Spread exceptionally tight (< 3%) | +2 |
| Event risk within 3 days | −10 |
| Higher and execution TF in direct conflict | −15 |
| ADX < 15 (very weak trend) | −10 |
| Spread > 6% of mid | −8 |
Cap at 100. Below min_score_to_trade (70) → SKIP.
| Condition | Action |
|---|---|
Earnings within avoid_earnings_days_before (2) days | Block new entries on that symbol |
FOMC / CPI / NFP within avoid_macro_minutes_before (30) min | Block ALL new entries |
| Unscheduled high-impact headline (spreads blow out > 20%) | Halt new entries, tighten existing stops |
| Already in position as event approaches | Reduce size, tighten stop, or exit per config |
Check after every fill and before every new entry:
daily_realized_loss >= max_daily_loss_percent (4%) → HALT all new entries
consecutive_losses >= max_consecutive_losses (3) → HALT all new entries
portfolio_heat >= max_portfolio_heat_percent (8%) → no new entries
slippage on last trade > max_slippage_percent (5%) → flag; raise fill threshold
Halt conditions are sticky for the session. Manual reset required.
Every symbol evaluated in a cycle must produce a structured decision object:
{
"timestamp": "ISO-8601",
"action": "ENTER | HOLD | EXIT | SKIP | WATCHLIST",
"symbol": "AAPL",
"direction": "bullish | bearish",
"setup_type": "PS+ | PS-",
"confidence_score": 82,
"regime": "TRENDING_BULL | TRENDING_BEAR | CHOP | SHOCK",
"thesis": "Brief narrative: what pattern, why now, what invalidates",
"contract_candidate": {
"expiration": "YYYY-MM-DD",
"strike": 185.0,
"type": "call | put",
"dte": 14,
"delta": 0.48,
"open_interest": 4200,
"volume": 850,
"bid": 2.15,
"ask": 2.25,
"spread_pct": 4.5,
"iv": 0.32
},
"entry_plan": {
"limit_price": 2.20,
"entry_window_minutes": 15
},
"stop_plan": {
"underlying_invalidation": 182.50,
"premium_stop_pct": 35,
"time_stop_minutes": 45
},
"target_plan": {
"r1_price": 3.08,
"r2_price": 4.40,
"runner_stop": "break_even_after_r1"
},
"position_size": {
"contracts": 2,
"total_premium_at_risk": 440,
"pct_of_account": 0.44
},
"risk_checks_passed": true,
"blockers": [],
"journal_note": "Setup quality A. Clean bounce off VWAP+50EMA confluence. PPO improving. ADX 24 with +DI crossing. Room to prior day high ~4.5R. No events next 4 days."
}
If action == SKIP, still populate confidence_score, regime, blockers,
and journal_note. Logged skips are required for strategy improvement.
Log every evaluation, including skips. Required fields per log entry:
Also log:
Use scripts/alpaca.mjs for all broker interaction.
APCA_API_KEY_ID=<your_paper_key>
APCA_API_SECRET_KEY=<your_paper_secret>
APCA_API_BASE_URL=https://paper-api.alpaca.markets
# Health check and account state
node skills/perfect-storm-options-trader/scripts/alpaca.mjs account
# Current open positions
node skills/perfect-storm-options-trader/scripts/alpaca.mjs positions
# Open orders
node skills/perfect-storm-options-trader/scripts/alpaca.mjs orders --status open
# Place a limit order (options use OCC symbol format)
node skills/perfect-storm-options-trader/scripts/alpaca.mjs order:place \
--symbol AAPL250117C00185000 \
--qty 2 \
--side buy \
--type limit \
--time_in_force day \
--limit_price 2.20
# Cancel an order
node skills/perfect-storm-options-trader/scripts/alpaca.mjs order:cancel --id <order_id>
AAPL 250117 C 00185000
ROOT YYMMDD P/C 8-digit strike (×1000, zero-padded)
Example: AAPL call, Jan 17 2025, $185 strike = AAPL250117C00185000
✓ BASE_URL contains "paper-api" — never "api.alpaca.markets"
✓ account.status == "ACTIVE"
✓ account.trading_blocked == false
✓ Buying power sufficient for order
✓ Position limit not exceeded (check open positions count)
✓ Order is LIMIT type
✓ client_order_id set to trace order in journal
risk_config.yaml without exception.risk_config.yaml rules without explicit human instructionWhen rules or conditions conflict, resolve in this order:
Before entering any long premium trade, evaluate whether implied volatility makes the option attractively priced:
IV Rank (IVR) = (current IV − 52w low IV) / (52w high IV − 52w low IV)
IVR < 30 → premium is relatively cheap → favorable for long premium
IVR 30–60 → neutral; ensure setup quality is strong (score ≥ 75)
IVR > 60 → premium is expensive → require score ≥ 85 or skip
A great chart pattern with expensive premium is still a risky trade. Log IV context in every trade journal entry.
Do not allow correlated positions to compound hidden directional risk:
max_portfolio_heat_percent / 2 per directionWhen in doubt, the tighter constraint applies.
Any change to thresholds, filters, or exit logic must:
risk_config.yamlNo live-forward parameter changes mid-session.
This is the prioritized starting universe for each scan cycle. Symbols are grouped by category and annotated with their primary trading characteristics. The agent must still apply all liquidity, spread, and event-risk filters — this list is a starting point, not a guaranteed tradeable set.
Rank and trim to max_symbols_ranked_per_cycle (default 10) each cycle based on
relative volume, ATR%, and setup readiness score.
| Ticker | Why It's On The List |
|---|---|
| NVDA | Highest beta AI play; massive options volume; PS setups are clean and fast |
| AAPL | Deepest options chain in the market; spreads excellent; smooth trend behavior |
| MSFT | Strong institutional trend structure; reliable EMA respect; liquid chain |
| AMZN | Cloud + consumer composite; strong trending periods; liquid weeklies |
| META | High ATR%; clean breakout/breakdown patterns; active options market |
| GOOGL | Stable large-cap with periodic high-momentum setups; good chain depth |
| Ticker | Why It's On The List |
|---|---|
| AMD | Correlated to NVDA but more volatile; excellent PS setups during momentum runs |
| SMCI | Extremely high ATR%; fast-moving; use reduced size; spreads can widen — filter strictly |
| SNOW | Growth-tech with high relative moves; strong trend-or-nothing behavior |
| Ticker | Why It's On The List |
|---|---|
| TSLA | High retail + institutional interest; large ATR%; frequent PS+ and PS− setups |
| COIN | Crypto-correlated; explosive directional moves; requires strict spread filter |
| PLTR | Strong narrative-driven momentum; clean structure on daily and 30m charts |
| Ticker | Why It's On The List |
|---|---|
| JPM | Liquid large-cap; sector proxy; good for regime-aligned put setups during stress |
| GS | High price / active options; strong directional bias during macro moves |
| BAC | Rate-sensitive; use for macro-driven setups around FOMC; very liquid chain |
| Ticker | Why It's On The List |
|---|---|
| WMT | Low beta; use for isolated PS setups when broad market is choppy |
| KO | Stable trend structure; good for low-volatility swing setups |
| PEP | Similar profile to KO; use to diversify away from tech concentration |
| Ticker | Why It's On The List |
|---|---|
| XOM | Macro/oil-driven; useful hedge when tech is choppy; active options chain |
| CVX | Similar to XOM; use for sector-divergence setups or energy trend trades |
| Ticker | Why It's On The List |
|---|---|
| SPY | Primary regime proxy; also tradeable directly for broad-market PS setups |
| QQQ | Tech-heavy regime proxy; use when NASDAQ is the dominant trend driver |
| IWM | Small-cap risk-on/risk-off signal; divergence from SPY/QQQ is informative |
Note: SPY, QQQ, and IWM are listed in
risk_config.yamlandAGENTS.mdas core ETFs. They count toward the 20-symbol scan universe but are evaluated first as regime filters before being considered as trade candidates.
Score each symbol 0–3 on each factor, sum, and take the top max_symbols_ranked_per_cycle:
| Factor | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| Relative volume vs 10-day avg | < 0.8× | 0.8–1.2× | 1.2–2.0× | > 2.0× |
| ATR% (intraday) | < 1.5% | 1.5–2.5% | 2.5–4.0% | > 4.0% |
| Setup confluence score (pre-screen) | < 50 | 50–65 | 65–80 | > 80 |
| Options chain quality (spread + OI) | Poor | Acceptable | Good | Excellent |
Ties broken by options chain quality, then alphabetically.