SolRegimeBreakoutTrendRideLong
Hypotheses
SOL Spot Regime-Filtered Breakout Trend-Ride, Long-Only (Single Instrument SOLUSDT.BINANCE_SPOT, Daily Bars, Buy 55-Day High Breakouts ONLY in a Confirmed Secular Uptrend, Chandelier Trailing-Stop Exit, Weeks-to-Months Hold, 3-Parameter)
Hypotheses
A LONG-ONLY, single-instrument TREND-RIDE on SOLUSDT.BINANCE_SPOT (daily bars) that buys genuine breakouts to new 55-day highs, but ONLY when a slow regime filter confirms a secular uptrend, and then rides the position with a chandelier (ATR) trailing stop to capture crypto's fat right-tail moves. This is a deliberately DIFFERENT family from everything already pending/deployed: it is NOT cross-sectional momentum ROTATION (L32 exhausted — that ranks a basket and rebalances weekly; this is a single-name ABSOLUTE breakout with a trailing exit, no ranking, no basket), NOT mean-reversion/VWAP (opposite polarity — it trades WITH breakouts, not against deviations), NOT the CM volatility-squeeze breakout sibling (that is mid-term long_short futures triggered by vol contraction; this is a long-horizon long-only spot trend-ride triggered by new highs + regime), NOT funding/carry (L33), NOT options (L23), NOT positioning/OI/flow supplementary-fed. The core structural bet is crypto's well-documented MOMENTUM PERSISTENCE and fat-tailed trends: assets that break to new highs in a confirmed uptrend continue further than chance, and a trailing stop monetizes the tail while the 200-day regime filter keeps the strategy in CASH during bear markets — precisely the whipsaw regime that kills unfiltered trend systems. Very low frequency (~15-30 trades over multi-year history), weeks-to-months holds, so the ~0.20% spot round-trip is trivial against the hundreds-of-percent trends SOL produces. Only 3 parameters to resist overfitting; routed to spot (the highest-survival, under-quota venue) because it is long-only and unleveraged.
Hypotheses
Two things were wrong and both trace to the same false premise in the previous iteration's notes. First, the Layer-1.5 block: the config traded SOLUSDT.BINANCE because the docstring claimed the spot catalog was schema-corrupt (Arrow size_precision 5 vs 8) and loaded ZERO bars. I tested it — ParquetDataCatalog.bars('SOLUSDT.BINANCE_SPOT-1-DAY-LAST-EXTERNAL') returns 2,215 clean daily bars from 2020-08-11 to 2026-09-03, actually MORE history than the 2,180 on the perp path it had been diverted to. So the config now trades the declared instrument, which is also the correct venue by the routing rule: this book is long-only and unleveraged, so it has no business paying perp fees or carrying funding. Second, and worth fixing in the same pass because it would have been the next fidelity complaint: the previous iteration shrank the hypothesis's headline parameters (breakout_len 55 -> 20, regime_len 200 -> 100) to fit a sandbox it believed held only 363 daily bars. sandbox_window_days for a 1-DAY bar type is 2,400 (SANDBOX_BAR_BUDGET 20,000 / 1 bar per day, clamped to the max), and the spot series supplies 2,215 bars, so the 224-bar warmup the hypothesis implies fits comfortably. I restored 55/200 and replayed the exact entry/exit logic on the real spot bars: 15 trades with a 15-day median hold, matching the hypothesis's own forecast of '~15-30 trades over multi-year history' with 'weeks-to-months holds' — so there is no zero-trade risk and the strategy now tests the mechanism as written rather than a faster proxy of it. Everything that already passed is untouched: imports unchanged, the continuous ATR-normalised breakout score is still returned every bar, the regime/breakout booleans and chandelier anchor tracking are byte-identical, and sizing is unchanged apart from raising min_notional 5 -> 10 (the Binance SPOT minimum) and skipping sub-minimum orders. I added _param_bounds mirroring the clamps, including upper bounds on breakout_len/regime_len that the constructor now enforces so the optimizer cannot search values the code would silently rewrite.
Hypotheses
data_wall: Not worth 2 hours of optimization: the strategy is faithfully built and matches its hypothesis, but 15 trades over 6.1 years is an unmeasurable sample for a 3-parameter walk-forward optimization — each of the 3 WF windows would fit to ~5 trades, making IS/OOS and holdout statistically vacuous and guaranteeing the optimizer curve-fits noise (L10/L27 low-trade-count trap). The headline (Sharpe 0.90, PF 3.09, avg_trade_return_pct 19.4%) rests on a Sharpe CI [0.076, 1.615] that barely clears zero, and the strategy posts information_ratio -1.013 vs a meaningful buy-hold benchmark, i.e. it underperforms simply holding SOL risk-adjusted. The mechanism is inherently single-name and low-frequency, so iteration cannot raise the sample without changing the premise. abandon_class=unmeasurable.
Implementation
Long-only single-instrument breakout trend-ride on SOLUSDT.BINANCE_SPOT, daily bars, pure OHLCV, no leverage. Each daily close the strategy checks two conditions: a regime gate (close above a non-falling 200-day SMA) and a breakout trigger (close above the highest high of the prior 55 bars, the current bar excluded). Only when both hold does it buy — so breakouts inside a confirmed secular uptrend are taken and everything else is sat out in cash. The position is then ridden with a chandelier trailing stop: exit when the close falls more than atr_mult x ATR(14) below the highest high made since entry, or when the regime itself breaks (close back under the 200-day SMA), which steps aside before a bear market rather than giving the trend back. Sizing risks 2% of equity across the chandelier stop distance, hard-capped at 95% of equity so the CASH account never needs margin, and SELL is only ever used to close a long.
Verification Results
Verification failed (Layer 1.5 — hypothesis/config consistency) [class=hypothesis_mismatch]:
- config.instrument_id is SOLUSDT.BINANCE but the hypothesis declares SOLUSDT.BINANCE_SPOT — same asset, DIFFERENT venue/contract type (fees, settlement and the mechanism's premise differ). Trade the declared instrument.
Verification Results
If the base template uses its own min_bars_required for buffer sizing independent of the strategy's warm-up, rename this to a strategy-local helper (e.g. _min_bars) to avoid shadowing; otherwise leave as-is.
Verification Results
min_bars_required() overrides a base-template framework method (static analyzer base_shadow warning). The override is deliberate and internally consistent (returns regime_len + atr_period + 2 = 216, which exceeds the 56 bars the breakout window and 205 bars the regime-slope check need, and is <= the _max_bars=275 buffer), so warm-up gating is correct. Flagged only so the developer confirms the override is intended and the base does not rely on its own implementation for a different purpose.
Verification Results
Three advisory notes for the analyst, none blocking. (1) LOW TRADE COUNT: the sandbox produced 15 entries and the hypothesis itself pre-registers only ~15-30 trades over the full multi-year history. A sub-30-trade sample cannot reliably distinguish edge from noise regardless of headline metrics (Sharpe 0.90, PF 3.09, avg_trade_return_pct 19.4%) — the full backtest should confirm the sample is large enough to be measurable before optimizing. (2) BREAKOUT-FAMILY LINEAGE: this is a 55-day-high (Donchian-style) breakout, a bar-only OHLCV mechanism close to a documented zero-survivor class. It is NOT a naive Donchian — it adds a non-falling 200-day regime gate and a chandelier ATR trailing exit — but the analyst should weigh whether the regime+trailing structure produces a genuinely distinct edge vs a re-parameterized breakout. (3) BENCHMARK: sandbox information_ratio is -1.013 vs buy-hold with correlation 0.47 and beta 0.018 — the trend-ride underperforms simply holding SOL on a risk-adjusted basis over the sandbox window; verify the regime filter's tail-truncation / drawdown benefit (sandbox max DD 5.2% vs buy-hold) actually justifies the strategy over a naive hold on full history.
Backtest Review
Faithfully implements the hypothesis: 15 long-only breakout trend-ride entries, weeks-to-months holds (avg 23d), regime-gated, exactly inside the pre-registered 15-30 trade band — trades match the stated mechanism, no polarity/direction mismatch.
Backtest Review
Huge per-trade edge well clear of the ~0.20% spot round-trip: avg_trade_return_pct 19.4%, PF 3.09, and a low 5.2% max drawdown thanks to the 200-day regime filter keeping it in cash during bear phases.
Backtest Review
Positive expectancy across all vol terciles; not an artifact (SOL genuinely produced these fat right-tail trends over the window).
Backtest Review
15 trades over a 6.1-year window is statistically unmeasurable and cannot support a 3-parameter walk-forward optimization: the 3 WF windows would each fit to ~5 trades, so IS/OOS and holdout gates are vacuous — this is the classic L10/L27 low-trade-count trap where a strong headline is carried by a handful of trades and the Sharpe is effectively unmeasured.
Backtest Review
Sharpe 0.90 but bootstrap CI [0.076, 1.615] barely excludes zero — the edge is not distinguishable from noise on this sample.
Backtest Review
information_ratio -1.013 vs a MEANINGFUL buy-hold benchmark: on a risk-adjusted basis the trend-ride underperforms simply holding SOL over the window; the DD-truncation benefit does not translate into positive active risk-adjusted value.
Backtest Review
Single-name, inherently low-frequency mechanism: iteration cannot raise the trade count without changing the hypothesis (adding instruments = a different, basket family), so there is no fixable path to a measurable sample.
Iteration History
SolSpotRegimeBreakoutTrendRideLong
Iteration History
Verification failed (Layer 3 — sandbox backtest): Arrow error: Schema error: Fail to merge schema due to conflicting metadata. Key 'size_precision' has different values '5' and '8'
Bar type used: BTCUSD.HYPERLIQUID-1-MINUTE-LAST-EXTERNAL, Bars processed: 0
Ensure your strategy produces trades with the given data and parameters.
Iteration History
Verification failed (Layer 3 — sandbox backtest): No trades produced
Bar type used: SOLUSDT.BINANCE-1-DAY-LAST-EXTERNAL, Bars processed: 363
Diagnostics: should_enter() returned a side 0 times over 140 evaluated bars -> your ENTRY CONDITION never triggered. Loosen the entry logic / thresholds.
Ensure your strategy produces trades with the given data and parameters.
Backtest and paper results are hypothetical. Trading involves risk of loss.