Skip to content

View original

OpArbL2RollupPairsSpreadMeanReversion

Hypotheses

OP-ARB Ethereum L2 Rollup Pairs Spread Mean Reversion

Hypotheses

A market-neutral (dollar-neutral) pairs trading strategy on the two dominant Ethereum Layer-2 optimistic rollups: OPUSDT (Optimism) and ARBUSDT (Arbitrum) on Binance USD-M futures. Both are direct competitors in the same niche — EVM-compatible optimistic rollups securing transactions via Ethereum L1 — and share virtually identical fundamental drivers (ETH gas fees, L2 TVL flows, EIP-4844 blob upgrades, restaking narratives). Their 90-day rolling correlation typically exceeds 0.85, and their price ratio (OP/ARB) oscillates within a stable regime, diverging on idiosyncratic catalysts (Optimism Superchain announcements, Arbitrum Stylus releases, governance airdrops, ARB unlocks) and reverting as ecosystem flows normalize. Strategy follows the proven recipe of the successful ADA-DOT pairs trade (Sharpe 6.4): compute log-ratio z-score on 4H bars, fade extreme readings (|z|>2.0), exit at z-reversion to ±0.3. Long the underperforming rollup, short the outperforming rollup in equal dollar amounts. Captures relative-value alpha that is uncorrelated to overall crypto beta — diversifying portfolio away from BTC-direction exposure and from the L1-altcoin pairs already in the pipeline (ADA-DOT, SOL-AVAX).

Hypotheses

Iteration 2 fixes the Layer-2 TypeError with the smallest possible change and no regression. Root cause: _trim_dict was a @staticmethod called via self._trim_dict(d, max_keep); the Layer-2 verification proxy transfers every callable and re-binds it via types.MethodType, so a self-called staticmethod receives an extra self argument and raised 'takes 2 positional arguments but 3 were given' on the first bar of every scenario. The fix converts _trim_dict to an instance method (unused self) so it survives the proxy re-binding and behaves identically in the real engine. I verified by running the actual _StrategyProxy with the ARB extra fed across all six synthetic scenarios: no exceptions and 398–406 unique (non-frozen) signals each. The timestamp-aligned z-score logic, dual-leg dollar-neutral execution, the Layer-2 SMA-deviation fallback (signal-only — should_enter still requires real synced-ratio data so no bogus trade fires on the degraded path), imports, and all eight parameters are unchanged, so Layer 1 stays green and the strategy now advances past Layer 2.

Hypotheses

Negative-expectancy market-neutral pairs mean-reversion with no tradeable edge — not worth 2 hours of optimization. The OP/ARB ratio does not mean-revert profitably: profit_factor 0.90, avg_trade_return_pct -0.316% (NEGATIVE), expectancy -$68/trade over 220 trades, Sharpe -0.437 (CI [-1.39, 0.47]), total_return -15.2%, with avg_loss ($1,504) exceeding avg_win ($1,196). It loses in EVERY year (2023 -3.6%, 2024 -2.4%, 2025 -0.8%, 2026 -8.9%) and its rolling Sharpe is negative across most of the sample (to ~-6 in 2026) — the ratio trends rather than reverts, so fading extremes is systematically wrong, and the last-20% holdout window is deeply negative. This is the L45/L53 zero-survivor market-neutral pairs class (0/84); the cited ADA-DOT 'Sharpe 6.4' sibling is an artifact-level number (>5 red flag) and a same-mechanism sibling (SUI-APT) already died de-cointegrated, so retargeting the fade to yet another altcoin pair keeps reproducing the same failure rather than fixing a single broken-but-otherwise-working target. No tuning of z_entry/z_exit/z_lookback flips a negative per-trade edge positive. Failure pattern: no_edge market-neutral pairs mean-reversion (L45/L53).

Implementation

Dollar-neutral OP/ARB Ethereum L2 rollup pairs mean-reversion on Binance USD-M futures, 4H bars (OPUSDT primary, ARBUSDT hedge, pure OHLCV). It maintains timestamp-aligned closes, forms log(OP/ARB), and computes its z-score over a rolling 90-bar window; at |z|>2.0 it opens the equal-notional pair (z>+2 → short OP/long ARB; z<-2 → long OP/short ARB) and exits on convergence inside |z|<0.3, a 90-bar max-hold, or a 10% per-leg emergency stop. Each leg is 15% of equity (market-neutral). Leverage 1.0.

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

Correctly dollar-neutral, timestamp-aligned dual-leg implementation; reasonable trade count (220) and controlled drawdown (17.3%)

Backtest Review

Genuinely uncorrelated to crypto beta by construction

Backtest Review

Negative expectancy: profit_factor 0.90, avg_trade_return_pct -0.316% (NEGATIVE), expectancy -$68/trade, Sharpe -0.437, total_return -15.2%

Backtest Review

Loses in EVERY year (2023 -3.6%, 2024 -2.4%, 2025 -0.8%, 2026 -8.9%) and most regimes — the OP/ARB ratio trends rather than reverts, so fading extremes is systematically on the wrong side; avg_loss > avg_win

Backtest Review

L45/L53 zero-survivor market-neutral pairs class (0/84); the cited ADA-DOT 'Sharpe 6.4' sibling is an artifact-level number and a same-mechanism sibling (SUI-APT) already died de-cointegrated

Backtest Review

Recent regime is the worst (rolling Sharpe to ~-6 in 2026), so the last-20% holdout window is deeply negative — OOS/holdout would fail

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['z_exit', 'z_entry', 'min_warmup', 'z_lookback', 'per_leg_pct', 'min_notional', 'max_hold_bars', 'emergency_stop_pct'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: OpArbL2RollupPairsSpreadMeanReversion._trim_dict() takes 2 positional arguments but 3 were given (bar timestamp: 1735689600000) - steady_downtrend: TypeError: OpArbL2RollupPairsSpreadMeanReversion._trim_dict() takes 2 positional arguments but 3 were given (bar timestamp: 1735689600000) - flat_ranging: TypeError: OpArbL2RollupPairsSpreadMeanReversion._trim_dict() takes 2 positional arguments but 3 were given (bar timestamp: 1735689600000) - volatility_spike: TypeError: OpArbL2RollupPairsSpreadMeanReversion._trim_dict() takes 2 positional arguments but 3 were given (bar timestamp: 1735689600000) - zero_volume: TypeError: OpArbL2RollupPairsSpreadMeanReversion._trim_dict() takes 2 positional arguments but 3 were given (bar timestamp: 1735689600000) - price_gap: TypeError: OpArbL2RollupPairsSpreadMeanReversion._trim_dict() takes 2 positional arguments but 3 were given (bar timestamp: 1735689600000)

Iteration History

data_unavailable: backtest data for OPUSDT.BINANCE could not be collected after 5 retries: - bars (bar_type=ARBUSDT.BINANCE-4-HOUR-LAST-EXTERNAL): got 0, needed 100, or 60+ days span The data layer (not the strategy) is the blocker — re-evaluate this hypothesis once collection is fixed / the symbol is backfilled.
Strategy report

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