Skip to content
All strategies

Strategies

LINK Spot Trend-Participation — Long/Flat, No-Leverage: Hold the Spot Asset While a Slow DAILY Trend Regime Is UP, Rotate to CASH When the Trend Breaks — Continuous Regime Participation (NOT Rare Deep-Drawdown Dip-Buying), Captures Alt Up-Trends While Sidestepping Bear Drawdowns (LINKUSDT.BINANCE_SPOT, Daily, 3-Parameter)

Outcome: Abandoned

LinkSpotTrendParticipation

avg_trade_return_pct 6.12% clears the spot fee floor easily (not a fee failure)

Hypothesis

A LONG-ONLY, no-leverage, single-instrument SPOT trend-participation strategy that holds LINK while its slow daily trend regime is UP and rotates to CASH when the trend breaks. It is deliberately routed to BINANCE_SPOT — the single most under-represented high-survival venue (11.1% vs the ≥15% quota) and the venue-appropriate home for long-only no-leverage strategies per the factory routing rule — and it is the SPOT, long/flat counterpart to the directional futures strategies already in our pipeline (which are all……Show moreShow less

A LONG-ONLY, no-leverage, single-instrument SPOT trend-participation strategy that holds LINK while its slow daily trend regime is UP and rotates to CASH when the trend breaks. It is deliberately routed to BINANCE_SPOT — the single most under-represented high-survival venue (11.1% vs the ≥15% quota) and the venue-appropriate home for long-only no-leverage strategies per the factory routing rule — and it is the SPOT, long/flat counterpart to the directional futures strategies already in our pipeline (which are all long-SHORT with leverage). Critically, it is NOT the dead Binance-spot deep-drawdown accumulation basket: that strategy triggered only on RARE deep drawdowns and produced ZERO trades after 2023 (empty walk-forward/holdout). This strategy is CONTINUOUS regime participation — it is long during every up-trend and flat during every down-trend across all years, so it trades in every regime cycle and cannot go dead in a later window. The economic goal is a risk-adjusted improvement over buy-and-hold: capture the large alt up-trends (which dwarf the 0.20% spot round-trip cost) while sidestepping the deep bear drawdowns that destroy buy-and-hold Sharpe. LINK is a liquid large-cap with rock-solid multi-year daily SPOT history, so walk-forward windows and the 15-day holdout populate. 3 parameters: entry trend-SMA length, exit-SMA length, and ATR trailing multiple.

ITERATION 2 -- single, surgical fix for the Layer-2 failure. All six synthetic scenarios died with 'TypeError: _roll() takes 3 positional arguments but 4 were given': the verification proxy re-binds the strategy's class functions, which strips the @staticmethod wrapper, so the shared rolling-sum helper received `self` as its first argument on the very first bar. The helper was pure convenience, so I removed it entirely rather than converting it to an instance method -- the same failure mode cannot recur if there……Show moreShow less

ITERATION 2 -- single, surgical fix for the Layer-2 failure. All six synthetic scenarios died with 'TypeError: _roll() takes 3 positional arguments but 4 were given': the verification proxy re-binds the strategy's class functions, which strips the @staticmethod wrapper, so the shared rolling-sum helper received `self` as its first argument on the very first bar. The helper was pure convenience, so I removed it entirely rather than converting it to an instance method -- the same failure mode cannot recur if there is no helper. Its four lines are now inlined at the three call sites (true-range deque in _push_true_range, entry-SMA and exit-SMA deques in calculate_signal), which is also the form the rest of the codebase uses. NOTHING ELSE CHANGED: same imports, same signal, same entry/exit/sizing logic, same config and parameter bounds, so the Layer-1 static pass stays green. I re-ran the full replay of the edited class over the real 2761-bar LINKUSDT.BINANCE_SPOT daily history (0.10% per fill, next-open execution) and it is byte-for-byte the same strategy as iteration 1: 109 trades, +4.73% average per round trip, profit factor 2.20, +786% total, Sharpe 0.44, 88.2% max drawdown; trailing 545 days +0.09% per trade (-2.8% total) versus buy-and-hold -55.6%; trailing 365 days 18 trades. I also drove 200 perfectly flat bars (the shape of the flat_ranging and zero_volume scenarios) through calculate_signal/should_enter/should_exit/position_size: no exception, ATR resolves to 0 and the signal returns 0.0, so the degenerate-scenario path is clean. The iteration-1 caveats stand unchanged and still belong to the analyst's decision: the Layer-3 sandbox covers the trailing 365 days in which LINK fell 55.7%, so a long-only mandate will print roughly -19% there (a third of the asset's loss) even though the full backtest is strongly positive; and Sharpe 0.44 with a >60% drawdown cannot clear the 1.5-Sharpe / 10%-drawdown promotion floors, so this hypothesis should be judged against buy-and-hold (Sharpe 0.21, 90% drawdown) or abandoned at backtest review.

Hard abandon on drawdown: max_drawdown 94.09% (CI to 100%) is far past the 50% limit (L19) and can never clear the promotion drawdown floor. The +1901% headline is crypto beta (beta 0.73, benchmark_correlation 0.80), not an active edge — information_ratio is negative (-0.17) so it does not beat buy-and-hold on a risk-adjusted basis, and profit_factor 1.18 is below the 1.2 meaningful-edge line. The Sharpe is an artifact of a handful of outlier up-days (kurtosis 184.9, skew 11.3) plus equity-curve-riding position sizing (avg_position_pct 131%). The hypothesis itself concedes the strategy rides the 2022 bear down (2022 -78.4%) with a self-reported 88% drawdown; optimizing 3 parameters cannot fix a mechanism that structurally holds through deep bear legs. This is the long-only spot beta-carrying-the-drawdown pattern (L49) — not worth 2 hours of optimization.

Implementation

Long/flat, no-leverage spot trend participation on LINKUSDT.BINANCE_SPOT daily bars. The signal is a continuous trend score, (close - SMA(entry_sma)) / ATR(14) -- distance above/below the trend baseline in units of daily volatility, computed every bar. The strategy buys the asset with 98% of the cash balance whenever that score turns positive (close above SMA(entry_sma)) and rotates back to 100% CASH when either close falls below SMA(exit_sma) or price gives back atr_trail_mult x ATR from the highest high since entry (chandelier trail). It is invested in roughly half of all days across every year of the sample -- continuous regime participation, not rare-event dip-buying -- and it never shorts, never borrows, and holds only the asset or cash, which is why it is routed to BINANCE_SPOT (CASH account, leverage 1.0). Signals are taken on the daily close and filled at the next day's open. Three tunables: entry_sma, exit_sma, atr_trail_mult.

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.

No code change; verify equity and position-value denominators are snapshotted together.

Sandbox avg_position_pct 108.8% on a CASH spot account that can't borrow, but position_size() is capped at equity*invest_frac (<=0.99) — code never requests leverage, so this is a mark-to-market metrics artifact, not an over-commit. Flagged for analyst confirmation.

Set _in_trade only after a non-zero size, or reconcile from cache.positions_open() each bar.

should_enter() sets _in_trade=True before the order is confirmed filled; if position_size() returns 0 the flag stays set. Base gates enter/exit on the actual cached position, so only chandelier peak-tracking is affected and it self-heals. Non-fatal.

Backtest Review

Sharpe
1.29
Total return
1901.23%
Max drawdown
94.09%
Trades
120
Win rate
27.5%
Profit factor
1.18

avg_trade_return_pct 6.12% clears the spot fee floor easily (not a fee failure)

120 trades across all years (mechanism fires continuously as designed, no dead windows)

positive alpha (0.085) and Sortino, entry diagnostics clean (120 signaled, 120 submitted, no drops)

max_drawdown 94.09% with CI [99.68%, 100.0%] — hard-abandon territory (L19), cannot clear the promotion drawdown floor

profit_factor 1.18 is below the 1.2 meaningful-edge threshold

information_ratio -0.17: negative risk-adjusted active return vs buy-hold; beta 0.73 / corr 0.80 means the +1901% is crypto beta, not skill (L49)

Sharpe is outlier-driven — kurtosis 184.9, skew 11.3, with a few freak >100%/day returns and avg_position_pct 131% pointing to equity-curve-riding sizing rather than a robust edge

2022 -78.4%, 2025 -13.5% — the strategy rides bear legs down through failed re-entries, exactly as the hypothesis concedes (88% self-reported DD)

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['exit_sma', 'entry_sma', 'atr_period', 'invest_frac', 'min_notional', '_param_bounds', 'atr_trail_mult'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: LinkSpotTrendParticipation._roll() takes 3 positional arguments but 4 were given (bar timestamp: 1735692060000) - steady_downtrend: TypeError: LinkSpotTrendParticipation._roll() takes 3 positional arguments but 4 were given (bar timestamp: 1735692060000) - flat_ranging: TypeError: LinkSpotTrendParticipation._roll() takes 3 positional arguments but 4 were given (bar timestamp: 1735692060000) - volatility_spike: TypeError: LinkSpotTrendParticipation._roll() takes 3 positional arguments but 4 were given (bar timestamp: 1735692060000) - zero_volume: TypeError: LinkSpotTrendParticipation._roll() takes 3 positional arguments but 4 were given (bar timestamp: 1735692060000) - price_gap: TypeError: LinkSpotTrendParticipation._roll() takes 3 positional arguments but 4 were given (bar timestamp: 1735692060000)

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