Skip to content

View translation

BtcDeribitWeeklyRiskReversalSkewMeanReversion

Hypotheses

Deribit BTC Options Skew Mean-Reversion (25-Delta Risk-Reversal Harvest) — Delta-Hedged, Fade Extreme Call/Put Skew Toward Its Own Norm, Market-Neutral to Both Direction and Vol Level (DERIBIT, ~5-10 Day Hold, 3-Parameter)

Hypotheses

A MARKET-NEUTRAL, options-native strategy on Deribit BTC options that trades the VOLATILITY SKEW mean-reverting — a distinct edge from every pending options sleeve (which trade the vol LEVEL: short strangle, delta-hedged short straddle, short-put VRP, long-gamma squeeze). Here the signal is the 25-delta RISK REVERSAL = (25Δ call IV − 25Δ put IV), computed from Deribit greeks/IV. Crypto skew swings between deep put-richness (fear) and call-richness (euphoric froth) and MEAN-REVERTS toward its own trailing norm. When the risk reversal is at a trailing-percentile EXTREME, the strategy sells the rich wing and buys the cheap wing (a short or long risk-reversal), then DELTA-HEDGES with the BTC perp so the position is neutral to direction. Because it is long one wing and short the other it is also roughly neutral to the overall vol LEVEL — it is a pure bet on the skew normalizing, which is orthogonal to the level-VRP the other sleeves harvest. It is NOT momentum/trend (0/213 dead, L56), NOT funding/cross-sectional carry/reversal/beta (all confirmed dead), NOT a liquidation/quarterly feed (L58), NOT a sub-fee microstructure fade, and NOT a duplicate of the pending vol-LEVEL options trades. It fills the most under-represented buckets: options (5.4% vs ≥15%) and market-neutral direction (long-only is 86% vs ≤55%). Exactly 3 tunable parameters: the skew entry percentile, the skew exit percentile, and the delta rehedge band.

Hypotheses

Iteration 3 targets exactly the two QA blockers. (1) MEASURABILITY. QA concluded the window "cannot be expanded by ANY change to this strategy" — that holds for the WEEKLY expression only. The binding constraint is bar-type COUNT (each catalog.bars() call fsspec-globs 37,459 dirs for a fixed ~9s, ~21 queries under the 300s cap), and a weekly roll spends 2 queries per 7 CALENDAR days, which is what pinned the sample to 56 days. Switching to the MONTHLY chain spends 2 queries per ~30 days: I verified against the real expiry calendar that 300 days = 10 monthly expiries = 20 legs + 1 perp = 21 queries (~195s, same budget that already passed Layer 3 at 17), so calendar span goes 56 -> 300 days at unchanged infrastructure cost. Offsets widen 0.05 -> 0.10 because 25-delta at 30 DTE sits near ±10% moneyness (30d sigma ≈ 17% at 60% IV, 25-delta ≈ 0.67 sigma), keeping the legs true 25-delta wings inside the liquid ±10-15% band. (2) TRADE COUNT, which was the deeper objection: the old code latched each period shut after one round trip, so trades were structurally capped at one per contract pair — that, not the calendar, is why 8 cycles meant 7 structures. Since skew reverts on a ~5-10 day timescale while a monthly pair lives ~30 days, the same wings support several independent round trips. The latch is replaced by a single `_open_exp` one-structure-at-a-time guard plus `_maybe_reenter()`, so trade count now scales with how often skew is extreme rather than with how many contracts the query budget can afford — the combination should give ~10 cycles x several round trips across 300 days spanning multiple regimes, enough to populate the 3 walk-forward windows instead of ~2 cycles each. (3) QA's third note is fixed directly: the ±0.25 hard-coded hedge fallback is gone; `_leg_delta()` reuses the leg's last successfully solved IV (persisted in `_iv_by_iid`), else `fallback_iv`, and computes a real BS delta from the leg's own strike and TTE — an invalid leg (strike<=0 or expired) now contributes 0.0 rather than an invented moneyness. Layer 1/2 surfaces are untouched (imports identical, StaticAnalyzer re-run: passed; calculate_signal/should_enter/should_exit/position_size unchanged and safe with an empty schedule under the Layer-2 proxy). Honest caveat for the analyst: ~10 monthly cycles still cannot fully sample the short-wing tail, and the 15-day holdout will contain at most one structure — the true fix is platform-side (cache the globbed file list across catalog.bars() calls), after which lookback_days goes to 365+ on the weekly chain with no change to this strategy.

Hypotheses

Suspected benign-window / inadequate-sample artifact for a skew-tail-bearing options strategy — not worth 2 hours of optimization. Although the BTC-PERP series starts in 2018, the option legs only begin quoting in late 2025, so the actual trades run only 2025-11-12 to 2026-08-18 (annual_returns {2025,2026}, effective_daily_obs 37) — ~10 months in a single rising regime. The 95 trades are not independent: the iteration-3 re-entry mechanism reopens the SAME two wings several times per monthly cycle, so the sample is ~10 autocorrelated monthly cycles, not 95 observations. The headline metrics (Sharpe 3.83 with CI [1.84,5.41], profit_factor 2.09, max_drawdown 2.4%) are abnormally smooth for a risk reversal, which is short one wing and whose defining risk is a skew blow-out/gap — precisely the event barely present in this calm window, so the high Sharpe reflects the absence of the tail, not its absence of risk. The signal is also reconstructed: the docstring concedes there is no real per-strike IV history, so RR is Black-Scholes-inverted from each wing's daily close rather than observed greeks. A 3-phase optimization carved from this one benign year would curve-fit the regime, and the last-20% holdout sits inside the same ~2-month window and validates nothing; the ~1-year option-history limitation is structural, not a code fix, so iteration cannot help. This is NOT the L67 defined-risk fee-death case (avg_trade is positive) — the disqualifier is an inadequate single-regime sample for a tail-bearing strategy, the same call as this session's earlier Deribit VRP harvester. Symptom stated from the visible data spans; recommend manual/factory-gate review rather than spending optimization budget. Failure pattern: untested short-vol/skew tail on structurally limited options history.

Implementation

Market-neutral Deribit BTC 25-delta risk-reversal skew mean-reversion on the MONTHLY chain. Each monthly cycle the rolling chain supplies a ~25-delta call (+10% moneyness at 30 DTE) and put (-10%); their own daily bars are inverted through Black-Scholes into implied vols and the continuous decision variable is the risk reversal RR = IV(call) - IV(put), computed every bar whether or not a trade is on. When RR's trailing percentile rank is extreme (>= 1-entry_pct or <= entry_pct) the strategy sells the rich wing and buys the cheap wing 1:1 in contracts, and delta-hedges the residual with the BTC perp only when net option delta drifts outside rehedge_band x contracts. It exits when the rank returns inside 0.5 +- exit_pct (skew normalized), on a 3%-of-equity structure loss cap, or at the 48h roll buffer before European cash settlement — and then RE-ENTERS the same wings if skew goes extreme again, so one contract pair supports several round trips over its ~30-day life. One structure open at a time; the perp is flattened the moment no option leg is open, so it can never become a standalone directional position, and there is no underlying-only smoke path.

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

Genuinely differentiated edge (skew mean-reversion vs the pending vol-level sleeves); delta- and roughly vega-neutral construction (net_entry_delta -0.32, balanced 37 puts/37 calls)

Backtest Review

Data/plumbing works this iteration (95 option trades, no liquidation); commission trivial (0.81% of gross)

Backtest Review

Both legs win in-sample (long RR 57% / short RR 79%)

Backtest Review

Effective trading window is ~10 months (2025-11 to 2026-08), single rising regime — annual_returns only {2025,2026}, effective_daily_obs 37

Backtest Review

95 trades are re-entries on the same monthly wings (iteration-3 re-entry), i.e. ~10 cycles of autocorrelated round trips, not independent observations

Backtest Review

Sharpe 3.83 (CI [1.84,5.41]), PF 2.09, max_drawdown 2.4% — abnormally smooth for a short-wing skew trade whose skew-gap tail risk is barely sampled in this benign window (suspected artifact)

Backtest Review

Signal is reconstructed via Black-Scholes inversion of each wing's daily close (no real per-strike IV/greeks history)

Backtest Review

Holdout (last 20%) sits inside the same single regime — cannot validate robustness; the ~1yr option-history limit is structural, not tunable

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 options strategy, in its most extreme form yet (L26). The sandbox ran only data_days=6, produced 14 trades (~7 risk-reversal structures), and the engine itself flagged metrics_reliable=FALSE. Every headline number is small-sample noise: Sharpe 19.33 with CI [7.03, 73.50], win_rate 0.79, profit_factor 7.18, avg_trade_return_pct 39% — the exact L26/L17 mirage. The trade count is capped at 8 weekly cycles by an infrastructure ceiling that is WORSENING: the developer documents the catalog glob tree grew to 37,459 directories (from 35,824), which forced lookback_days DOWN from the 84 that SIGKILLed in iteration 1 to 56 here (each catalog.bars() call costs ~9s and two option legs/week is irreducible for a risk reversal). The window cannot be expanded by ANY change to this strategy. Critically, a risk reversal is SHORT one wing, so its loss is not premium-bounded and its entire risk lives in the tail wing blowing out — ~7 cycles cannot sample that tail. The developer concedes: '~8 weekly cycles cannot sample a short-wing tail, so the metrics should be read as a mechanism check, not as edge.' - Feasibility mismatch with the optimization pipeline (same class as the strangle and delta-hedged straddle sleeves). Pinned to a 56-day window (6 days actually sampled), the strategy cannot populate Phase 2 walk-forward (3 IS/OOS windows) or the Phase 3 15-day holdout from ~7 weekly structures — ~2 cycles per walk-forward window and ~2 in the holdout. It cannot clear the gates it would be routed into, independent of whether the skew-reversion edge is real. - Delta-hedge fallback uses a hard-coded +/-0.25 delta whenever a wing's solved IV is missing/non-finite (strike<=0, tte<=0, or iv<=0). Over a tiny 7-cycle sample a few bars hedged at an assumed 0.25 rather than the true delta can materially distort the (already unreliable) hedge PnL. Acceptable as a degrade path but worth noting given the sample size.

Iteration History

Verification failed (Layer 4 — QA review): - STILL structurally unmeasurable (L26) — the iteration-3 measurability fix did not land. The whole thesis was 'monthly roll -> 300 calendar days -> ~10 cycles across multiple regimes,' but the sandbox actually ran only data_days=34, i.e. roughly ONE monthly cycle. The 300-day span did not materialize (the monthly Deribit option daily-bar history available in the catalog is itself short — the same data limitation the developer noted for per-strike IV). So the realized sample is still a single regime. The re-entry mechanism produced 90 trades, but those are SERIALLY-DEPENDENT round trips on the same 1-2 contract pairs inside 34 days, not 90 independent observations — it inflates the raw count (which flips metrics_reliable to TRUE) without adding independent tail samples. A risk reversal is short one wing, so its entire risk is the tail wing gapping; one month of one regime cannot sample that. The metrics are the L26/L17 small-sample mirage: Sharpe 10.86 (>5) with CI [4.89, 19.25], PF 2.11, win_rate 0.70, avg_trade_return_pct 47.8%. - Feasibility mismatch with the optimization pipeline persists. With only 34 days actually sampled (~1 monthly structure, whose re-entries are serially correlated), Phase 2 walk-forward (3 IS/OOS windows) and the Phase 3 15-day holdout cannot be populated with independent structures — the developer concedes 'the 15-day holdout will contain at most one structure.' The strategy cannot clear the OOS/holdout gates it would be routed into, independent of whether the skew-reversion edge is real. - The re-entry path mutates the base class's own bookkeeping sets directly (self._opened.add(iid); self._closed.discard(iid) in _open_structure) to re-arm legs RollingOptionsStrategy has already closed. This reaches into base-class internals and is fragile: if the base also inspects _closed for roll-buffer settlement, re-discarding an iid could desync the roll/settlement logic (e.g. a leg re-opened late in the cycle that the base then does not close at the buffer). It functioned in the sandbox but is a latent correctness risk in live/paper.
Strategy report

Backtest and paper results are hypothetical. Trading involves risk of loss.