Skip to content

View original

BtcSpotDeleveragingResetAccumulation

Hypotheses

BTC Spot Deleveraging-Reset Accumulation — Buy Forced-Selling Washouts Signaled by Open-Interest Collapse + Funding Flip (Long-Only, Multi-Day Mean-Reversion)

Hypotheses

A LONG-ONLY spot BTC accumulation strategy on BINANCE_SPOT whose entry is gated not by a price pattern but by a crypto-native STRUCTURAL deleveraging event: a sharp collapse in perpetual open interest combined with a funding-rate flip to negative, coincident with a price drawdown. This is deliberately a DIFFERENT FAMILY from the abandoned Deribit calendar/term-structure options line (which died verification_loop across 3 iterations) — different instrument class (spot, not options), different mechanism (structural mean-reversion off a forced-liquidation exhaustion signal, not vol-term-structure carry), different data (open interest + funding, not greeks/IV), and a different venue in the portfolio's highest-survival, most under-represented sleeve (BINANCE_SPOT at ~6% vs the ≥15% quota). The thesis: violent cross-market deleveraging (cascading long liquidations) mechanically compresses open interest and drives perp funding negative as shorts pile in at the lows; once forced sellers are exhausted, spot has no structural seller left and mean-reverts several percent over the following days. We buy that exhaustion and hold for the reversion, not a directional forecast. Turnover is low (only genuine washout events fire — a handful per year), so the 0.20% spot round-trip is negligible against multi-percent target moves. It avoids the momentum-confluence graveyard (L38), the major→alt signal-transfer and pair-ratio families (L40), Hyperliquid (L39), the intraday 4H-price-gate-with-a-feed template (L46 — this is a multi-day structural event, not an intrabar gate), and the delta-neutral funding-carry sample/fee traps.

Hypotheses

ITERATION 2 — ONE METHOD SIGNATURE CHANGED, NOTHING ELSE. The Layer-2 failure was a pure binding bug, identical across all six synthetic scenarios: `_at_or_before() takes 4 positional arguments but 5 were given`. I traced it to the verifier rather than guessing. `_StrategyProxy.__init__` (src/verification/strategy_verifier.py, step 2) walks `dir(real_instance)` and rebinds EVERY callable onto a plain wrapper with `types.MethodType(val.__func__ if hasattr(val, '__func__') else val, self._instance)`. A bound instance method has `__func__` and re-binds correctly; a @staticmethod retrieved via getattr is a plain function with NO `__func__`, so it is bound as an instance method and receives the wrapper as an extra leading argument. `_at_or_before` was the only @staticmethod in the file, and it sits on the hot path (both `_oi_at` and `_funding_at` call it on every bar), so every scenario died on the first bar. FIX: it is now a normal instance method, `def _at_or_before(self, ts_arr, val_arr, ts_ns, max_lag_ns)`. Both call sites already used `self._at_or_before(...)`, so nothing else moved — under the real engine and under the proxy it now receives exactly five arguments. I also recorded the reason in the module docstring and on the method itself, because the natural instinct on reading a function that touches no instance state is to re-decorate it as a staticmethod, which would silently reintroduce this failure. NO REGRESSION OF LAYER 1: imports are untouched (numpy, decimal, math, nautilus_trader, base_template — no banned modules added), the class structure and inheritance are unchanged, and every parameter the feedback listed is still set from `self.parameters.get()` in `__init__` with the same defaults. The signal logic that Layer 1 already passed is byte-for-byte identical: the continuous `0.5 - oi_collapse_rank` decision variable, the three-leg washout gate, the coin-denominated (not USD) open-interest choice that keeps leg 1 orthogonal to leg 3, the no-look-ahead nearest-at-or-before lookups with staleness rejection, the calendar-anchored time stop, and the deliberate stand-aside when a feed is missing rather than degrading into ordinary dip-buying. WHAT LAYER 2 SHOULD NOW SHOW. With the feeds absent or synthetic, `_oi_rank` stays None and the strategy correctly emits a flat 0.0 signal and takes no position — that is the intended no-data degradation for this hypothesis, not a frozen-signal defect, since the entire edge is the deleveraging evidence. If the verifier injects synthetic open-interest and funding series, the rank becomes measurable and the signal will vary bar to bar.

Hypotheses

Insufficient sample to optimize: 49 rare washout events over 8.3 years cannot support fitting 3 parameters — walk-forward and holdout windows would each hold ~10-16 trades and fit noise. Despite a real per-trade edge (avg_trade 0.96%, PF 1.38), the daily-return Sharpe is 0.26 with a 95% CI [-0.30, 0.80] straddling zero and information_ratio -0.63, so the risk-adjusted signal is unmeasured at this trade count (L10). This same profile was already abandoned at backtest_review on 2026-08-02 with no change since; re-running the 3-phase optimizer would repeat that outcome. Failure pattern: overfit-risk / thin-sample rare-event strategy.

Implementation

Long-only BTC spot accumulation on BINANCE_SPOT (4H bars) gated by a structural deleveraging event rather than a price pattern. Signal = 0.5 − percentile rank of the 24h change in the BTC perp's COIN open interest against its own trailing history, recomputed every bar (+0.5 = most violent OI collapse on record, 0 = median). A trade fires only when all three legs of a washout coincide: OI change in the bottom 20% of its distribution, perp funding negative (shorts paying), and spot down over the same window. Exits are a 5% reversion target, a 4% stop, or a 30-bar calendar time stop anchored to the position's own open timestamp. Risk-first capital-relative sizing (1% of equity risked at the stop, capped at 50% of equity notional; CASH account, no leverage). Stands aside entirely when the OI or funding feed is missing or stale — there is no price-only dip-buying fallback. Three tunable parameters: oi_collapse_pct, target_pct, max_hold_bars.

Verification Results

REVIVED 2026-09-04 — this run was failed by a VERIFICATION HARNESS BUG, not by your code. Layer 2 re-bound @staticmethod helpers as instance methods, so a correct `@staticmethod def _helper(x)` called as `self._helper(x)` raised "takes 1 positional argument but 2 were given" in every synthetic scenario. The harness is fixed (commit 5dbd27f7) and this strategy now passes Layer 2 unchanged. Your static helpers are correct Python: do NOT convert them to instance methods, inline them, or restructure around them.

Verification Results

Make perp-OI resolution explicit/defensive (try symbol, symbol+'.BINANCE') and log/assert once when no OI series is found, so a mismatch surfaces loudly.

Verification Results

The OI feed is looked up by str(instrument_id) ('BTCUSDT.BINANCE_SPOT') or self._symbol ('BTCUSDT'). If open_interest_by_instrument is keyed by the PERP id ('BTCUSDT.BINANCE'), neither key matches and the strategy silently stands aside forever (zero trades). The sandbox produced 54 trades so it resolved here, but this is a brittle coupling to how the harness keys the spot→perp OI series; a keying change turns the strategy into a no-op with no error.

Verification Results

At backtest review, verify holdout trade count >=10 and that washout events span multiple regimes (2020/2022/2024) rather than one cluster.

Verification Results

Low trade frequency (~8/yr) makes the last-20% holdout thin (~10 trades) and the Sharpe CI straddles zero; with P&L concentrated in a few high-kurtosis events the holdout is noise-sensitive. Informational for measurability, not a code defect.

Backtest Review

Genuine per-trade edge above spot fees: avg_trade_return_pct 0.96% (~5x the 0.25% spot round-trip), PF 1.38, win rate 55%

Backtest Review

Clean, well-motivated structural mechanism (coin-OI collapse + funding flip + drawdown), correct spot venue, verification-safe, low DD 6.5%

Backtest Review

Distinct family from the abandoned options line; low fee drag; positive skew (1.38)

Backtest Review

Only 49 trades over 8.3 years — statistically too thin to optimize; walk-forward windows (~16 trades) and the holdout (~10 trades) will fit noise, not signal

Backtest Review

Sharpe 0.26 with 95% CI [-0.30, 0.80] straddling zero and information_ratio -0.63 — risk-adjusted edge is indistinguishable from noise

Backtest Review

Returns are lumpy and regime-concentrated (2020 & 2026 carry it; 2022–2023 negative); tail_ratio 0.2 signals a fat left tail

Backtest Review

This same profile (49 trades, PF ~1.38, avg_trade ~0.96%, DD ~6.5%) was already abandoned at backtest_review on 2026-08-02 — no improvement to warrant a different verdict now

Outcome Summary

The strategy sought to buy BTC spot into forced-liquidation exhaustion, gating entries on a structural deleveraging event (open-interest collapse + negative funding + drawdown) rather than a price pattern, and it cleared verification to run a full ~8-year backtest. The results were clean but thin: 49 trades with solid per-trade economics and a tiny drawdown, yet only 8.9% total return and a Sharpe of 0.261 whose confidence interval spans zero. The analyst abandoned it at the pre-optimization backtest review, judging the 49-trade sample structurally unmeasurable — walk-forward and holdout validation couldn't populate, and optimizing so few events would just fit noise. Because the low turnover is inherent to the rare washout design, the mechanism is unoptimizable at this trade count, so it was retired before optimization, analysis, or risk review were reached.

Outcome Summary

A washout mechanism that is rare by design produces too few trades to establish or optimize an edge, so per-trade quality alone can't rescue a near-flat, statistically-insignificant sample — event-driven strategies need a signal frequency high enough to clear the measurability floor.

Outcome Summary

It was abandoned at BACKTEST_REVIEW (verdict: abandon): with only 49 trades over ~8 years — below the ~100-trade measurability floor and with an entire year (2024) firing none — the 3-phase optimization's walk-forward and holdout windows could not meaningfully populate, and best-of-225 selection on so few events would overfit, so it wasn't worth the 2-hour optimization.

Outcome Summary

A long-only BINANCE_SPOT BTC accumulation strategy that buys forced-selling washouts identified by a structural deleveraging event — a percentile collapse in coin-denominated perp open interest, a funding-rate flip to negative, and a coincident price drawdown — then holds for the multi-day mean-reversion.

Outcome Summary

Over ~2,983 days (2018–2026) it took 49 long trades with respectable per-trade stats (profit factor 1.38, avg_trade_return_pct 0.96%, 55% win rate) and a low 6.5% max drawdown, but produced only 8.9% total return (~1.1%/yr) with Sharpe 0.261 whose 95% CI [-0.328, 0.821] straddles zero, information ratio -0.60, and return kurtosis of 52.7.

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['stop_pct', 'target_pct', 'funding_max', 'min_notional', 'max_hold_bars', 'oi_window_bars', 'risk_per_trade', 'oi_collapse_pct', 'min_rank_samples', 'oi_max_lag_hours', 'rank_window_bars', 'max_notional_frac', 'funding_max_lag_hours'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: BtcSpotDeleveragingResetAccumulation._at_or_before() takes 4 positional arguments but 5 were given (bar timestamp: 1735690020000) - steady_downtrend: TypeError: BtcSpotDeleveragingResetAccumulation._at_or_before() takes 4 positional arguments but 5 were given (bar timestamp: 1735690020000) - flat_ranging: TypeError: BtcSpotDeleveragingResetAccumulation._at_or_before() takes 4 positional arguments but 5 were given (bar timestamp: 1735690020000) - volatility_spike: TypeError: BtcSpotDeleveragingResetAccumulation._at_or_before() takes 4 positional arguments but 5 were given (bar timestamp: 1735690020000) - zero_volume: TypeError: BtcSpotDeleveragingResetAccumulation._at_or_before() takes 4 positional arguments but 5 were given (bar timestamp: 1735690020000) - price_gap: TypeError: BtcSpotDeleveragingResetAccumulation._at_or_before() takes 4 positional arguments but 5 were given (bar timestamp: 1735690020000)
Strategy report

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