DeribitEthWeeklyBullPutSpreadSkewHarvest
Hypotheses
Deribit ETH Weekly Bull-Put Credit-Spread — Rich Put-Skew Harvest, Trend-Regime Gated, Defined Risk
Hypotheses
A DEFINED-RISK options premium-harvest on DERIBIT ETH — targeting the most under-represented bucket in the portfolio (options combined 4.9% vs 15% quota; BINANCE_OPTIONS at 1 experiment, DERIBIT at 4.9%). It is deliberately DIFFERENT from the already-pending symmetric BTC iron-condor: this is an ETH (BTC survival is only 2%), directionally-tilted, single-wing structure that harvests the PERSISTENT NEGATIVE SKEW in crypto index options — OTM puts are systematically overpriced (chronic crash-hedge demand) relative to how often ETH actually crashes in non-downtrend regimes. Each weekly cycle, when ETH is NOT in a downtrend, we SELL a ~0.20-delta put and BUY a ~0.08-delta put further out (bull put credit spread). Max loss is hard-capped by the long wing, which directly answers the risk_reject/52–74%-DD failures of naked/structural short exposure (L48). A simple trend-regime gate (price above its slow moving average) keeps us from selling puts into the fat left tail — we stand aside in downtrends rather than catching a falling knife. This is NOT the exhausted 'gate a 4H price move with a supplementary feed' template (L46): the edge is option premium DECAY + skew richness (a cash flow of 1–4% of notional per structure), not a predicted directional price move, so it clears the ~0.06% RT option fee by one to two orders of magnitude. Only ~1 structure/week held toward expiry → negligible fee drag. Simple: 3 parameters (short-put delta, DTE/wing width, trend lookback) to resist the 300x overfit graveyard.
Hypotheses
ITERATION 3 — ONE CONFIG VALUE CHANGED (lookback_days 365 -> 90). The strategy LOGIC is byte-for-byte identical; only the docstring gained a section explaining the constraint. THE FEEDBACK'S DIAGNOSIS DOES NOT APPLY HERE, and I checked rather than assuming. There is no per-bar work that scales with history in this strategy: each primary bar does one bounded list append (trimmed to trend_lookback_bars + 2), the base's fixed periods x legs scan, and nothing else; the trend SMA is a single 720-element numpy mean evaluated only at a week's FIRST entry attempt and then cached per expiry; there is no supplementary series to rescan and no sorted()/min() in the hot path. THE ACTUAL CAUSE IS PER-INSTRUMENT DATA LOADING IN THE ENGINE, WHICH I MEASURED. src/backtesting/runner.py loads extras one at a time — `for ebt_str in extra_bar_types: self.catalog.bars(bar_types=[str(ebt)], start=start, end=end)`. On this catalog (2,476 ETH Deribit option bar directories, ~10k overall) that costs 7.26s PER OPTION INSTRUMENT: 20 instruments took 145.1s loaded individually. A 365-day weekly chain is ~52 periods x 2 legs = ~104 instruments = ~755s of loading before the engine processes its first bar — roughly 2.5x the 300s cap, which is exactly the observed failure and is unreachable from strategy code. THE FIX I APPLIED IS THE ONLY LEVER A STRATEGY HAS: leg count. At ~7.3s per leg, staying safely inside 300s means ~25 legs, so the chain window becomes 90 days (~13 weekly periods x 2 legs = ~26 instruments, ~190s of loading, leaving ~110s for the run). THE FIX THE ENGINE SHOULD APPLY — and I measured this too, because it decides whether this hypothesis can ever be evaluated properly: passing the whole list to catalog.bars() in ONE call loads the same 20 instruments in 7.8s (0.39s each), an 18.6x speedup. With that batched read a full 365-day chain would load in ~40s and this strategy could go straight back to lookback_days=365. That is a one-line change in runner.py's extras loop and it unblocks EVERY rolling-options strategy in the factory, not just this one — I'd escalate it to the backtest engineer as the highest-leverage item here, since the options bucket is deliberately being filled right now and every one of those strategies pays this tax. HONEST COST OF MY WORKAROUND. 90 days gives ~13 weekly structures, and the trend gate historically passes about 48% of weeks, so expect roughly 6-7 trades. That clears the >=5 trades / >=10 days evidence floor but it is thin: a 7-trade sample cannot support a meaningful walk-forward or holdout, so the analyst should read the eventual result as a smoke test of the mechanism rather than as evidence of a durable edge, and the strategy genuinely deserves re-running at 365 days once the batched read lands. This does NOT change my read of the underlying edge, which I measured at iteration 1 and have not touched: over 279 weekly cycles the structure earns +0.357% of spot per week with the trend gate (t=+2.48, win 0.83, worst week -4.36%) at Black-Scholes prices with flat DVOL — a conservative model, since flat IV ignores the very put skew being harvested. NO REGRESSION: imports unchanged, no banned imports, class structure unchanged, the defined-risk wing validation and the inert FactoryStrategy hooks (which are what keep the underlying perp from ever being traded) are exactly as Layers 1 and 2 already passed them.
Hypotheses
L67 defined-risk Deribit options VRP structure with a negative per-notional edge, on a structurally unmeasurable single-regime sample — not worth 2 hours of optimization. The headline is attractive (Sharpe 6.87, PF 2.36, +2.17% return, win 50%) but avg_trade_return_pct is NEGATIVE at -0.80% because per-trade return is measured against the defined-risk wing notional; per L67 (BTC iron-condor, ETH bull-put siblings all abandoned on this exact signature) that positive-Sharpe/PF-but-negative-per-notional combination is structural for defined-risk credit spreads and cannot clear the viability floor, so the Sharpe must not trigger optimize. It is also unmeasurable: the option legs only exist ~2026-06 to 2026-09 (data_days 57), so despite the 2019-2026 perp clock the effective option history is ~3 months / 16 trades / effective_daily_obs 8, the engine flags metrics_reliable=FALSE, and the Sharpe 6.87 (CI [6.54, 7.42]) is a small-sample artifact of 8 positive theta-decay days in a single mid-2026 uptrend where the crash regime the skew prices for is entirely unsampled. The 90-day window is forced by an engine per-instrument catalog-loading timeout the developer discloses — an infrastructure limitation no strategy-code iteration can fix — so walk-forward and holdout cannot populate. Failure pattern: fee_edge/no_edge defined-risk Deribit options VRP (negative per-notional) + structurally unmeasurable single-regime option sample (L67).
Implementation
Weekly defined-risk bull put credit spread on Deribit ETH options. Each weekly cycle the chain resolver supplies two front-week (7 DTE) puts: the ~5% OTM put is SOLD and the ~10% OTM put is BOUGHT as a wing, so maximum loss is hard-capped at the 5% strike width. The structure is established only when ETH is out of a downtrend — underlying close above its 720-bar (30-day) simple moving average — otherwise the week is skipped entirely. Positions are held until 2 hours before expiry, then closed. Leg roles are derived from strike order within the expiry (higher strike = short, lower = wing), and a week is traded ONLY if both legs resolved: an unpaired short put is dropped rather than sold naked, so risk stays defined even when the resolver cannot find a wing. Sizing is capital-relative (notional = equity × 0.5, floor of one contract), so the capped loss per structure is ~2.5% of equity. The rolling chain window is 90 days (~13 weekly structures) because the backtest engine's per-instrument catalog loading caps how many option legs fit inside the Layer-3 wall clock. Three tunable parameters: trend_lookback_bars, trend_buffer, roll_buffer_hours.
Verification Results
CLEAN RESTART 2026-09-04 — this run's verdict history and learning records were removed and it was restarted from verification. Its previous abandonment came from the pipeline, not from the market: the Layer-2 harness mis-bound @staticmethod helpers (fixed), QA issued terminal performance verdicts on an unoptimized smoke test (removed — QA now judges correctness only), and sandbox timeouts came from backtest-slot starvation (fixed). The hypothesis and the strategy code are unchanged. Verify the code on its merits; performance is decided later by the full backtest and the optimizer.
Backtest Review
Defined-risk is genuinely enforced in code (both legs validated per expiry; unpaired shorts dropped), so it correctly avoids the naked-short drawdown trap
Backtest Review
Clean schedule-driven options plumbing; fills the under-represented Deribit bucket
Backtest Review
L67 defined-risk Deribit options signature: flattering headline (Sharpe 6.87, PF 2.36, +2.17% return) but avg_trade_return_pct is NEGATIVE at -0.80% because per-trade return is measured against the defined-risk wing notional — structural for credit spreads, cannot clear the viability floor
Backtest Review
Structurally unmeasurable sample: the option legs only exist ~2026-06 to 2026-09 (data_days 57), so despite the 2019-2026 ETH-perp clock the effective option history is ~3 months, 16 trades, effective_daily_obs 8; the engine sets metrics_reliable=FALSE
Backtest Review
Sharpe 6.87 with CI [6.54, 7.42] is an implausible small-sample artifact on 8 observations, all in one mid-2026 uptrend where puts decayed worthless — the crash regime the skew prices for is entirely unsampled
Backtest Review
The 90-day window is forced by a per-instrument catalog-loading timeout (an engine limitation the developer discloses), so walk-forward/holdout windows cannot populate and the tail risk is untested
Iteration History
Verification failed (Layer 1 — static analysis):
- Required method not implemented: should_enter (line None)
- Required method not implemented: should_exit (line None)
- Required method not implemented: position_size (line None)
- Required method not implemented: calculate_signal (line None)
Iteration History
Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit. This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).
Iteration History
Verification failed (Layer 4 — QA review):
- STRUCTURALLY UNMEASURABLE TRADE COUNT. The chain window is capped at 90 days, which the developer's own estimate puts at ~13 weekly structures, of which the trend gate passes ~48% -> ~6-7 trades. That is below the ~30-trade floor an options strategy needs to distinguish a premium-harvest edge from noise, and it cannot populate a 3-window walk-forward or a 15-day holdout (each window would hold 1-2 structures). The developer states this directly: 'a 7-trade sample cannot support a meaningful walk-forward or holdout ... read the eventual result as a smoke test rather than evidence of a durable edge.' Worse, the Layer-3 sandbox is not even a usable smoke test — data_days=5, metrics_reliable=false, sharpe_note='Unreliable: only 5 daily returns', and the per-trade figures are internally contradictory (avg_trade_return_pct -0.71% NEGATIVE while avg_trade_pnl_usd +184 and total_return positive; 10 trades with a 6d22h avg hold over a 5-day measured window). The reported metrics cannot be trusted for any conclusion, and advancing to the full backtest + 3-phase optimization would burn hours on an unmeasurable sample.
- DISCLOSED strike-selection deviation from the hypothesis. The hypothesis specifies a ~0.20-delta short put and a ~0.08-delta wing; the code selects strikes by MONEYNESS (5% / 10% OTM), which at ETH's ~70 vol lands near -0.28 / -0.12 delta. The developer discloses this and justifies it soundly: a 15%-OTM weekly wing is past the depth where contracts reliably have candles, and a wing that fails to resolve would degenerate the defined-risk spread into a naked short. The 5/10 structure preserves the same bull-put-spread geometry with a TIGHTER (5% width) risk cap at strikes that actually trade. This is a defensible, risk-reducing deviation, not a mechanism swap — the core edge (harvest rich put skew in a defined-risk wrapper, trend-gated) is intact — so it is a warning, not a blocker.
Backtest and paper results are hypothetical. Trading involves risk of loss.