Skip to content

View original

SolCrowdedPositioningContrarianLS

Hypotheses

Crowded-Positioning Contrarian — Long-Short: FADE Retail Positioning Extremes Using the Global Long/Short Account Ratio + Taker Buy/Sell Volume Ratio (Confirmed Supplementary Feeds, NOT the Liquidations Feed), Enter Only at a Trailing-Percentile Extreme WITH Price-Exhaustion Confirmation, Reversion TP + ATR/Time Stop (SOLUSDT.BINANCE USD-M, 4H Bars, 3-Parameter)

Hypotheses

A LONG-SHORT, single-instrument, POSITIONING-CONTRARIAN strategy that fades crowded retail positioning on SOLUSDT.BINANCE USD-M — a behavioral-flow mechanism family with no precedent in the 2,573-experiment portfolio and outside every dying family (it is NOT price-only mean reversion like the AVAX wick-fade, NOT momentum confluence, NOT vol-RV options, NOT carry/basis, NOT the thin liquidations feed). It consumes two CONFIRMED supplementary feeds — the Global Long/Short Account Ratio and the Taker Buy/Sell Volume Ratio — both collected historically via Binance metrics (BinanceFuturesDataCollector + Vision metrics), so it dodges the L61 data_unavailable trap that killed liquidation-feed ideas. Core premise (a documented crypto edge): when the retail crowd is maximally positioned one way — global long/short ACCOUNT ratio at a trailing-window extreme — and aggressive taker flow confirms the crowd is still pressing, the marginal buyer/seller is exhausted and price mean-reverts as the crowd gets squeezed out. The strategy fades only at genuine extremes (trailing percentile) AND requires price-exhaustion confirmation (failure to make a new extreme), so it does not blindly fight a trend. Directional/long-short (attacks the 85.8%-long-only imbalance), event-gated (low turnover), and every per-trade move is large enough to clear fees by an order of magnitude. All rolling percentile/ATR series are precomputed ONCE in __init__ with bisect lookups — no per-bar min()/sorted()/full-series rescans (dodges the Layer-3 timeout that killed the prior straddle).

Hypotheses

Iteration 7 makes the smallest change that targets the reported backtest-gate failure (Sharpe 0.03 from the mtm curve disagreeing in sign with the -15.21% equity-curve return). The divergence is an excursion-size artefact: iteration 6's bracket already narrowed it (-20.30% -> -15.21%) but a 3.5%/6.5% box held up to 12 bars still lets the mark series record travel that never becomes a fill. So: stop_loss_pct 0.035 -> 0.025, take_profit_pct 0.065 -> 0.045 (same ~1.8 R:R, both reachable inside the reversion horizon), max_hold_bars 12 -> 9, exit_percentile 0.58 -> 0.56 so more positions close on the thesis than at the outer bracket. Sizing already risks risk_frac of equity against the real bracket stop, so it re-scales automatically. Signal maths, supplementary parsing, the precomputed percentile grids, imports and class structure are unchanged from the code that passed Layers 1, 1.5, 2 and 3, so no earlier layer regresses. Honest caveat: the mtm Sharpe of ~0.03 says the mechanism is gross-FLAT, not gross-positive-eaten-by-costs - a flat gross signal cannot be made profitable by trading less or by tightening exits, which is why my recommendation is abandon rather than continue.

Hypotheses

Negative expectancy on a reliable 827-trade / 6-year sample: profit_factor 0.894, avg_trade_return_pct 0.0297% (near zero and far below the 0.15% USD-M fee floor), expectancy -$39.66/trade, total_return -33.2%, Sharpe -0.017 (CI [-0.85, 0.83] straddles zero). Commission is only 9.5% of gross, so this is NOT fee-fragility — the crowded-positioning fade signal is gross-flat/negative: pressure reverts within ~2.5 bars, so realized per-trade moves are tiny and most positions exit on the signal rather than the (structurally adequate) 4.5%/2.5% bracket. The mechanism is correctly implemented (balanced long/short, orthogonal supplementary feeds) so this is a fair test, and the premise is falsified: the strategy loses in nearly every year and in the calm (-44.8%) and stressed (-32.6%) vol terciles — the only positive contribution is the normal tercile (+51.6%), i.e. regime-fragile noise, not a robust edge. This is iteration 7; six prior iterations converged the mtm/realized curves but never conjured a positive gross edge, and the 3 tunable parameters cannot invert a PF-0.894, sub-fee signal. Not worth 2 hours of optimization. abandon_class=negative_expectancy.

Implementation

Long/short positioning-contrarian strategy on SOLUSDT.BINANCE USD-M 4H bars. It fades crowded retail positioning: the trailing percentile of the Global Long/Short ACCOUNT ratio (crowd positioning) is averaged with the trailing percentile of the Taker Buy/Sell VOLUME ratio (aggressive flow still pressing) into a continuous 'pressure' score in [-1, +1], recomputed every bar. When pressure sits at an extreme AND price confirms exhaustion by failing to extend its recent N-bar high (for shorts) or low (for longs), the strategy takes the contrarian side. Exits are a venue-managed OUO bracket (2.5% stop / 4.5% take-profit, both firing intra-bar), a signal exit when pressure reverts toward neutral, or a 9-bar (1.5 day) time stop. Both supplementary percentile series are precomputed once onto a fixed time grid, so per-bar cost is an O(log n) lookup. If neither feed is available the strategy stays flat forever - it never falls back to a price-only proxy.

Verification Results

Analyst should confirm on full history whether the crowding-fade produces a positive gross edge in any robust regime before optimizing; if gross-flat, abandon rather than tune.

Verification Results

Unoptimized sandbox smoke test shows below-floor per-trade economics (avg_trade_return_pct 0.0297%) and a near-zero, regime-dependent edge. Structurally viable (bracket TP 4.5% clears fees), but the mechanism as parameterized produces very small realized reversions.

Verification Results

Sandbox is a fee-bleed/flat-gross result: avg_trade_return_pct 0.0297% (below the 0.15% USD-M floor), profit_factor 0.893, total_return -33.2%, Sharpe ~0 with CI [-0.85, 0.83] straddling zero over 827 trades, commission only 9.5% of gross. This is NOT a code/fee-structure defect (TP 4.5% / SL 2.5% are structurally large enough to clear costs), but the realized per-trade move is tiny because pressure reverts within ~2.5 bars and most positions exit on the signal, not the bracket. Regime table shows the entire positive contribution is in the 'normal' vol tercile (+51.6%) while calm (-44.8%) and stressed (-32.6%) are both negative, so the fade edge is regime-fragile. The developer's own recommendation is abandon on the grounds that the mtm signal is gross-flat (Sharpe 0.03), which would mean tighter exits/fewer trades cannot rescue it. Advisory for the analyst's full-history backtest review; does not block.

Backtest Review

Well-formed behavioural-flow mechanism using two confirmed supplementary feeds (global L/S account ratio + taker buy/sell volume ratio), orthogonal to dying families

Backtest Review

Reliable, decisive sample: 827 trades over 6 years (2020-2026), balanced long/short (393 long / 434 short), event-gated low turnover, precomputed percentile grids (no timeout risk)

Backtest Review

Bracket levels (TP 4.5% / SL 2.5%) are structurally large enough to clear USD-M fees — this is NOT a fee-structure/code defect

Backtest Review

Negative expectancy on a reliable sample: profit_factor 0.894 (<1), avg_trade_return_pct 0.0297% (far below the 0.15% USD-M floor and near zero), expectancy -$39.66/trade, total_return -33.2%, Sharpe -0.017 (CI [-0.85, 0.83] straddles zero)

Backtest Review

Not fee-fragility — commission is only 9.5% of gross, so the fade signal is gross-flat-to-negative. The realized per-trade move is tiny because pressure reverts within ~2.5 bars and most positions exit on the signal, not the bracket

Backtest Review

Loses in nearly every calendar year (2022 -23.7%, 2023 -5.2%, 2025 -13.6%, 2026 -3.9%); only 2024 (+11.3%) is positive

Backtest Review

Regime-fragile: the entire positive contribution is the normal vol tercile (+51.6%); calm (-44.8%) and stressed (-32.6%) are both negative — the crowding-fade edge does not hold in the regimes it targets

Backtest Review

This is iteration 7; six prior developer iterations converged the mtm/realized curves but never produced a positive gross edge, and the developer's own recommendation was abandon

Analysis

Backtest gate: - metrics unreliable: Incoherent: Sharpe 0.03 disagrees in sign with total_return -15.21% — the Sharpe is computed from the mtm_equity_curve series while the return comes from the equity curve. Judge on the equity curve.

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['risk_frac', 'atr_period', 'exhaust_atr', 'tp_atr_mult', 'min_notional', '_param_bounds', 'max_hold_bars', 'stop_atr_mult', 'exit_percentile', 'entry_percentile', 'exhaust_lookback', 'max_notional_frac', 'min_window_points', 'max_staleness_hours', 'percentile_window_days'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: SolCrowdedPositioningContrarianLS._row_ratio() takes 2 positional arguments but 3 were given (bar timestamp: 1735691040000) - steady_downtrend: TypeError: SolCrowdedPositioningContrarianLS._row_ratio() takes 2 positional arguments but 3 were given (bar timestamp: 1735691040000) - flat_ranging: TypeError: SolCrowdedPositioningContrarianLS._row_ratio() takes 2 positional arguments but 3 were given (bar timestamp: 1735691040000) - volatility_spike: TypeError: SolCrowdedPositioningContrarianLS._row_ratio() takes 2 positional arguments but 3 were given (bar timestamp: 1735691040000) - zero_volume: TypeError: SolCrowdedPositioningContrarianLS._row_ratio() takes 2 positional arguments but 3 were given (bar timestamp: 1735691040000) - price_gap: TypeError: SolCrowdedPositioningContrarianLS._row_ratio() takes 2 positional arguments but 3 were given (bar timestamp: 1735691040000)

Iteration History

sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (2400 days of SOLUSDT.BINANCE-4-HOUR-LAST-EXTERNAL). 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 1 — config): config.instrument_id and config.bar_type are REQUIRED (got instrument_id=None, bar_type=None). Set both to the hypothesis's instrument, e.g. instrument_id='BTCUSDT.BINANCE', bar_type='BTCUSDT.BINANCE-4-HOUR-LAST-EXTERNAL'.
Strategy report

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