Skip to content

View original

SafeSizedDailyTrendLongShortBasketNoLeverage

Hypotheses

Binance USD-M Safe-Sized Daily Trend Long-Short Basket (Established Majors, Single Frozen 50-Day Lookback, Hard-Capped No-Leverage Risk Construction, Weekly Rebalance)

Hypotheses

A long-short, daily time-series-momentum basket across ~10 established, confirmed-full-history Binance USD-M majors (BTC, ETH, SOL, BNB, XRP, DOGE, ADA, LTC, LINK, BCH) whose DISTINGUISHING feature is a SAFETY-HARDENED, NON-LEVERAGED RISK CONSTRUCTION — the explicit fix for the two ways trend baskets keep failing in this factory despite a genuine edge. The promoted AbsoluteMomentum sibling validates the trend edge (Sharpe 1.48), but two siblings were abandoned not for lack of edge: the long-horizon basket suffered a LEVERAGE EXPLOSION (single-period -211% — the target_vol/realized_vol scalar reached 3× while 1/vol weights blew up as realized vol→0), and the vol-targeted basket was OPTIMIZER-OVERFIT (a single fast_lookback tuned 20→13, IS spiked, OOS/holdout collapsed negative). This strategy removes both failure modes by construction: (1) NO vol-targeting leverage scalar and NO uncapped inverse-vol weights — sizing is equal-risk with a HARD per-asset weight cap, realized vol FLOORED in any denominator, and gross exposure HARD-CAPPED at 1.0× (no leverage), so a >100% single-period loss is structurally impossible; (2) a SINGLE FROZEN robust lookback (50 days, near the analyst-identified robust region) with a minimal tunable surface, so the walk-forward optimizer has nothing to over-concentrate on. It stays strictly inside the ONLY validated edge (DAILY symmetric trend on established majors — intraday/short-only/long-horizon/MR variants are all refuted), uses pure majors' daily OHLCV (data-safe), is long-short (improves the 87%-long skew), and is the conservative, production-grade 'cannot-blow-up' form of the trend sleeve — distinct from the promoted basket and my ensemble/breakout/cross-sectional siblings by its safety-first risk construction and minimal frozen parameter set.

Hypotheses

Iteration 7 fixes the reported Layer-2 failure (`FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given`) with the smallest safe change. Root cause: the base declares `_bar_ts` as a `@staticmethod`, but the Layer-2 `_StrategyProxy` rebuilds the strategy on a plain wrapper object, re-binding every callable via `types.MethodType(val, wrapper)`. A staticmethod fetched off an instance is a bare function with no `__func__`, so `MethodType` binds the wrapper into its lone `bar` parameter — every `self._bar_ts(bar)` call then passes two args to a one-arg function. Iteration 6's code called `self._bar_ts(bar)` in `calculate_signal` and crashed on the first bar that cleared `min_bars_required` (index 51, ts 1735692660000 — exactly lookback+2). Rather than monkeypatch the base private method (which `_maybe_process_primary()` also calls internally, so an override silently changes base behavior in the real engine), `calculate_signal` now reads `bar.ts_event` / `bar.ts_init` inline and `_bar_ts` is never referenced. I reproduced the failure and verified the fix against the real `StaticAnalyzer` and `StrategyVerifier`: Layer 1 passes and all 6 Layer-2 scenarios (steady_uptrend, steady_downtrend, flat_ranging, volatility_spike, zero_volume, price_gap) pass with no exception. `_submit_entry_instrument` ignores its `bar` argument, so the existing `None` passes are safe for Layer 3. No other line changed — signal logic, inverse-vol weighting, the three risk caps, the frozen 50-day lookback, imports, and class structure are byte-identical to `previous_code`, so no earlier-layer check regresses. The strategy stays on BINANCE futures (not spot) because it takes short legs, and keeps `leverage = 1.0` with `gross_cap = 1.0`, matching the hypothesis's no-leverage mandate and avoiding the `leverage_set_but_unused` gate.

Hypotheses

max_iterations_after_review

Implementation

Long-short daily time-series-momentum basket across 10 established Binance USD-M majors (BTC, ETH, SOL, BNB, XRP, DOGE, ADA, LTC, LINK, BCH). Every 7 daily bars it re-signs each asset by its 50-day return (long if up, short if down) and sizes it equal-risk via normalized inverse-vol weights. Three structural safeguards bound the book: realized vol is floored in the 1/vol denominator, each per-asset weight is hard-capped at 20%, and weights are renormalized to sum to 1 with gross exposure capped at 1.0x — no vol-targeting leverage scalar anywhere, so a >100% single-period loss is arithmetically impossible. Same-side books are resized only on a material (>20%) delta to suppress fee churn; a side flip closes then reopens.

Verification Results

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['assets', 'lookback', 'gross_cap', 'vol_floor', 'max_weight', 'resize_frac', 'min_notional', 'qty_precision', 'rebalance_days', 'default_qty_precision'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - steady_downtrend: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - flat_ranging: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - volatility_spike: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - zero_volume: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - price_gap: TypeError: FactoryStrategy._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000)

Backtest Review

The signal construction is sound and genuinely frozen: a single 50-day lookback, sign-of-return trend, symmetric long/short (257 LONG / 261 SHORT), with a minimal tunable surface (max_weight, rebalance_days). The anti-overfitting intent is correctly executed.

Backtest Review

The vol floor is real and correctly placed: `vol = max(vol, self._vol_floor)` in the 1/vol denominator genuinely prevents the inverse-vol blow-up as realized vol -> 0, which was one of the two named sibling failure modes.

Backtest Review

There is no target_vol/realized_vol leverage scalar anywhere in the code — the second named sibling failure mode was correctly removed by construction.

Backtest Review

The trade population is well-distributed across all 10 majors with adequate count (518 trades over 6.5 years), and benchmark_meaningful is correctly set to false for a long-short book (beta 0.0826, benchmark_correlation 0.162).

Backtest Review

Commission drag is modest: commission_pct_of_gross 1.22%, total_impact_usd $3,963. Fees are not what kills this.

Backtest Review

THE HYPOTHESIS'S CENTRAL CLAIM IS FALSIFIED. It promises gross exposure 'HARD-CAPPED at 1.0x (no leverage), so a >100% single-period loss is structurally impossible.' Observed daily_returns: -1408.51% (2023-03-13), -855.88% (2024-07-08), -246.03% (2025-06-16), -241.08% (2026-01-26), -131.36% (2023-01-23), +893.26% (2024-05-13). annual_returns['2023'] = -1457.29%. exposure_pct 1000.66%, avg_position_pct 127.32%. The failure mode it was purpose-built to prevent occurred repeatedly, and worse than in the sibling it was meant to fix (-211%).

Backtest Review

DEFECT A (verified by reading the code) — the 'HARD cap' is soft by construction. `weights = {a: min(raw[a]/total, max_weight)}` then `weights = {a: w/wsum}`. Since raw[a]/total sums to exactly 1, capping can only LOWER the sum, so wsum <= 1, and dividing by wsum scales every weight UP. Any capped asset lands at max_weight/wsum > max_weight. The renormalization systematically undoes the cap on exactly the assets it clipped. Confirmed: max_weight = 0.20, yet 223 of 288 legs (77%) report leverage > 0.20, median 0.35 (1.75x cap).

Backtest Review

DEFECT B (BLOCKING) — a 128.48x leg cannot come from target space. After renormalization the weights sum to exactly 1.0, so budget*weight sums to equity*gross_cap*leverage = 1.0x equity; target space is safe and Defect A explains only the median, not the tail. Observed per-leg leverage: min 0.05, median 0.35, MAX 128.48. A DOGE leg of quantity 5,220,698 @ $0.050112 = $261,617 notional against ~$83,584 implied equity — a single leg at 3.13x the whole book's gross cap. This can only arise in EXECUTION (order stacking). I have NOT traced it and will not guess a mechanism.

Backtest Review

THE SAME SIGNATURE APPEARS IN A SIBLING. CrossSectionalMomentumLSBasketDaily shares this exact `_rebalance_asset()` — same `for pos in positions: cur_qty += ...` loop, same `_submit_entry_instrument(delta)` resize, same exit-then-enter-in-one-bar flip — and reviewed today with median leg leverage 0.79 against a 0.167 target and max 58.67. Two independent basket strategies, one code shape, one over-exposure signature.

Backtest Review

THE REALIZED TRACK RECORD IS A LOSS. total_return +174.74% but end_unrealized_pct 197.00 — MORE than the entire headline is open mark-to-market never closed, so realized is approximately -22%. Corroborated independently by profit_factor 0.9804 (below 1.0) and expectancy -$42.98/trade across 518 trades, with max_consecutive_losses 21, max_drawdown_duration_days 1808 (~5 years), and information_ratio -0.4374.

Backtest Review

Risk statistics are uninterpretable: compute_daily_returns() (metrics.py:179) keys realized PnL to trade CLOSE date, and avg_holding_period is 40d 21h, so weeks pass with nothing closing. Hence the stretches of exactly 0.0 (2023-02 -> 2024-04; 2024-08 -> 2025-02; 2025-07 -> 2026-01) and rolling_sharpe frozen at -0.1403 for 17 consecutive readings, -0.0524 for 19, -2.8983 for 5. So sharpe_ratio 0.364 (CI [-0.398, 0.748]), annualized_volatility 397.11%, return_kurtosis 834.84, return_skew 25.36, sortino 0.992 and probabilistic_sharpe 0.9272 all mean nothing.

Backtest Review

Degenerate outputs: cagr -100.0, calmar_ratio -1.0, recovery_factor -1.0, max_drawdown_ci_high 101.99% — while the equity curve runs 100,000 -> 274,735 and never drops below 84,111.14. Equity never approached zero. Reported as symptom, not diagnosed.

Backtest Review

entry_diag reports entry_signaled 0 / entries_submitted 0 against 518 real trades, because rebalancing bypasses the base hooks. Legal, but the harness's entry diagnostics are blind to every basket strategy.

Analysis

Code↔hypothesis misalignment found by the semantic auditor — the code does NOT implement the hypothesis. Re-code the strategy to implement the hypothesis EXACTLY (instrument, timeframe, direction, the named edge/mechanic, sizing). Concrete issues: Per-asset hard cap is not enforced after renormalization. In `_rebalance()` the code computes `weights = {a: min(raw[a]/total, self._max_weight)}` then `weights = {a: w/wsum}` where `wsum = sum(weights.values())`. Since `sum(raw[a]/total) == 1` by construction, any binding cap makes `wsum < 1`, so the final division scales every weight up — including the ones that were just capped — leaving capped assets strictly above `max_weight`. Concrete: with `vol_floor=0.01` binding for a quiet BTC (raw=100) and nine legs at vol≈0.04 (raw=25 each), total=325, BTC raw weight=0.31 → capped to 0.20; the other nine renormalize to 0.077 each (0.692); wsum=0.892; final BTC weight = 0.20/0.892 ≈ 0.224 > 0.20. The hypothesis and the class docstring both assert 'each per-asset weight is HARD-CAPPED at max_weight' as one of the three structural safeguards defining the strategy's safety-first differentiator; the code enforces the cap only pre-renormalization. (Note the gross_cap=1.0 claim IS satisfied — weights sum to 1 — so '>100% single-period loss is structurally impossible' still holds; only the per-asset cap claim is violated.)

Outcome Summary

SafeSizedDailyTrendLongShortBasketNoLeverage was written as the corrective sibling to two abandoned trend baskets, removing the vol-targeting leverage scalar and flooring realized vol so inverse-vol weights could not explode. Both of those fixes landed exactly as intended, and the frozen 50-day signal was genuinely minimal and symmetric. But two new defects took their place: the `min(w, max_weight)` cap was silently undone by the subsequent renormalization (223 of 288 legs breached it, median 1.75x the cap), and an untraced execution-path issue produced a single DOGE leg at 128.48x — a signature the reviewer found in an identically-structured sibling's `_rebalance_asset()` on the same day. Underneath the broken sizing there was no edge to salvage anyway: profit factor 0.98, negative expectancy, and a realized track record of roughly -22% hidden behind 197 points of never-closed open position.

Outcome Summary

A weight cap applied before renormalization is not a cap — dividing by a sub-1.0 weight sum scales every clipped asset back above the limit — and a risk constraint asserted in a docstring must be verified against realized per-leg leverage, not assumed from the code's intent.

Outcome Summary

The backtest-review gate returned `iterate` because the hypothesis's central safety claim was falsified: exposure_pct reached 1000.66%, max leg leverage hit 128.48x against a 0.20 target, and daily returns of -1408.51% and -855.88% occurred — the exact blow-up the strategy was purpose-built to prevent, and worse than the -211% sibling it was meant to fix. After 7 iterations it was abandoned with reason `max_iterations_after_review`, never reaching optimization or the post-optimization analyst.

Outcome Summary

Run a weekly-rebalanced, symmetric long-short daily trend basket across 10 established Binance USD-M majors using a single frozen 50-day lookback, with a safety-hardened risk construction — vol-floored inverse-vol weights, a 0.20 per-asset weight cap, and a 1.0x gross exposure cap — explicitly designed so a >100% single-period loss would be structurally impossible.

Outcome Summary

It fired 518 trades (257 long / 261 short) across all 10 majors over 6.5 years with headline total_return +174.74%, but end_unrealized_pct was 197.00 — more than the entire headline was open mark-to-market never closed, putting realized return at approximately -22%, corroborated by profit_factor 0.9804, expectancy -$42.98/trade, information_ratio -0.4374, and max_consecutive_losses 21.

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['assets', 'lookback', 'gross_cap', 'vol_floor', 'max_weight', 'resize_frac', 'min_notional', 'qty_precision', 'rebalance_days', 'default_qty_precision'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: SafeSizedDailyTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - steady_downtrend: TypeError: SafeSizedDailyTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - flat_ranging: TypeError: SafeSizedDailyTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - volatility_spike: TypeError: SafeSizedDailyTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - zero_volume: TypeError: SafeSizedDailyTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000) - price_gap: TypeError: SafeSizedDailyTrendLongShortBasketNoLeverage._bar_ts() takes 1 positional argument but 2 were given (bar timestamp: 1735692660000)
Strategy report

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