diff --git a/Crypto Trading Bot Case Study.pdf b/Crypto Trading Bot Case Study.pdf index f5d71b5..08a38ec 100644 Binary files a/Crypto Trading Bot Case Study.pdf and b/Crypto Trading Bot Case Study.pdf differ diff --git a/README.md b/README.md index a3c0087..2251ea1 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ panel cannot start a live bot at all. | Path | What | |---|---| | `user_data/strategies/TrendRider4h.py` | 4h Donchian breakout trend-follower, BTC bear-market filter, ATR chandelier exit | +| `user_data/strategies/TrendRider4hPIT.py` | TrendRider4h re-tuned on point-in-time pairs (candidate; own parameter file) | | `user_data/strategies/TrendDipBuyer1h.py` | 1h buy-the-dip inside an uptrend (mean reversion) | | `user_data/strategies/EmaRsiMomentum.py` | original 1h EMA-crossover + RSI strategy (hyperoptable) | | `user_data/strategies/MomentumRotation1d.py` | daily rotation into the 3 strongest coins by risk-adjusted momentum while BTC is in an uptrend (candidate) | @@ -153,9 +154,17 @@ downloaded, so some survivorship bias remains. Tuning against OOS would make it in-sample too, and the numbers would stop meaning anything. -Hyperopt saves the best parameters to `user_data/strategies/.json`, -which every later backtest/dry-run picks up automatically. Delete that file to -go back to the defaults in the `.py`. +Hyperopt tunes on the point-in-time pairs at the start of the in-sample period, +the same pairs the in-sample backtest uses. It never uses `config.json`'s live +list, because that's today's top coins. + +It saves the best parameters to `user_data/strategies/.json`, which +every later backtest and dry-run picks up automatically. Delete that file to go +back to the defaults in the `.py`. + +To try new parameters without touching a strategy that's paper trading, tune a +subclass with its own name instead. For example, `TrendRider4hPIT` is +TrendRider4h tuned on point-in-time pairs. ### Go-live gates diff --git a/RESEARCH.md b/RESEARCH.md index 2f09747..f954df5 100644 --- a/RESEARCH.md +++ b/RESEARCH.md @@ -1,6 +1,6 @@ # Research results -**Generated:** 2026-10-11 by `python scripts/research.py gates`. Raw data is in +**Generated:** 2026-10-15 by `python scripts/research.py gates`. Raw data is in `user_data/research/latest.json`, and the panel's **Research** tab shows the same numbers. The same run with a 65 USDT (≈100 AUD) account is in `latest-65.json` (`research.py gates --balance 65`). The case study PDF is built from both @@ -31,7 +31,8 @@ The same run with a 65 USDT (≈100 AUD) account is in `latest-65.json` | Strategy | IS return | OOS return | OOS max DD | OOS profit factor | OOS trades | Walk-forward | Gates | |---|---|---|---|---|---|---|---| | **TrendRider4h** | +5.6% | **+17.3%** | 9.8% | 1.43 | 99 | 64% | **8 / 9** | -| MomentumRotation1d (new) | +24.4% | +13.1% | **5.2%** | **1.59** | 63 | 57% | 7 / 9 | +| MomentumRotation1d | +24.4% | +13.1% | **5.2%** | **1.59** | 63 | 57% | 7 / 9 | +| TrendRider4hPIT (re-tuned) | **+45.6%** | +13.8% | 9.9% | 1.33 | 98 | 50% | 7 / 9 | | EmaRsiMomentum | +7.7% | +5.1% | 18.1% | 1.13 | 74 | 43% | 6 / 9 | | TrendDipBuyer1h | −8.0% | −17.8% | 19.2% | 0.73 | 300 | 36% | 4 / 9 | @@ -59,6 +60,28 @@ the next monthly re-run will settle it either way. MomentumRotation1d fails the sample-size gate (63 trades) and the walk-forward gate (57% against 60%). +## Re-tuning TrendRider4h on point-in-time pairs + +TrendRider4h's parameters were tuned on 15 hand-picked pairs, chosen with +today's knowledge. `research.py hyperopt` also had a bug: it tuned on +`config.json`'s live list, today's top coins. It now tunes on the +point-in-time universe at the start of the in-sample period. + +TrendRider4hPIT is the same strategy with its own parameter file. It was tuned +that way for 300 trials (Calmar objective), using in-sample data only: + +| | Breakout | ADX | Trailing stop | Exit window | IS | OOS | OOS PF | Walk-forward | +|---|---|---|---|---|---|---|---|---| +| TrendRider4h | 15 | 23 | 4.9 × ATR | 6 | +5.6% | **+17.3%** | **1.43** | **64%** | +| TrendRider4hPIT | 28 | 22 | 3.9 × ATR | 5 | **+45.6%** | +13.8% | 1.33 | 50% | + +**Re-tuning made it worse where it counts.** In-sample return went up eightfold; +out of sample it fell by 3.5 points, and its out-of-sample quarters went from +3 of 6 profitable to 2 of 6. That is overfitting: the tuning fitted the +in-sample period rather than finding a better strategy. TrendRider4h stays the +lead candidate; TrendRider4hPIT is kept as evidence. The 65 USDT run gives +the same picture (+17.5% against +14.0%). + ## Small accounts (≈100 AUD) Same gates, starting with 65 USDT (about 100 AUD at an assumed 0.65 USD per AUD): @@ -67,6 +90,7 @@ Same gates, starting with 65 USDT (about 100 AUD at an assumed 0.65 USD per AUD) |---|---|---|---|---|---|---| | TrendRider4h | 65 → 76.35 | +17.5% | +17.3% | 9.7% | 99 | 8 / 9 | | MomentumRotation1d | 65 → 74.08 | +14.0% | +13.1% | 7.1% | 64 | 7 / 9 | +| TrendRider4hPIT | 65 → 74.07 | +14.0% | +13.8% | 9.8% | 98 | 7 / 9 | | EmaRsiMomentum | 65 → 68.29 | +5.1% | +5.1% | 18.1% | 74 | 6 / 9 | | TrendDipBuyer1h | 65 → 53.49 | −17.7% | −17.8% | 19.1% | 300 | 4 / 9 | @@ -198,8 +222,8 @@ The gap between the IS results is the most important finding: - **Survivorship bias.** Binance doesn't serve data for delisted coins, so coins that died between 2023 and 2026 are missing from the candidate pool. - **Tuning on hand-picked pairs.** TrendRider4h's parameters were tuned on the - hand-picked 15, not on point-in-time universes. Re-tuning on point-in-time IS - universes is a valid next step. It must not touch OOS. + hand-picked 15. Re-tuning on point-in-time pairs (TrendRider4hPIT) did worse + out of sample, so the original parameters stay. - **No modelled slippage.** Backtests fill at the candle price plus fees. Paper trading is where slippage shows up. @@ -212,8 +236,8 @@ The gap between the IS results is the most important finding: the same weeks. 2. **Re-run `research.py gates` monthly.** New data adds OOS trades, and the 100-trade gate will be settled honestly. -3. **Optionally re-hyperopt TrendRider4h** on point-in-time IS universes. Treat - it as a new candidate and judge it only on OOS. +3. **Don't re-tune TrendRider4h again on this in-sample period.** The + point-in-time re-tune overfitted. More tuning would just fit it harder. 4. **Keep MomentumRotation1d as a candidate, not a replacement.** It needs more OOS trades and a better walk-forward record. Don't re-tune its lookback on the OOS period. diff --git a/scripts/case_study.py b/scripts/case_study.py index 872382d..ec1e472 100644 --- a/scripts/case_study.py +++ b/scripts/case_study.py @@ -338,6 +338,27 @@ def passed(s): f"that: it made only {pct(wf_r[up]['profit_pct'])} in that strongest quarter (and {pct(wf_r[down]['profit_pct'])} " "in the worst one). Its rally-capture rationale is not supported by these results.") + retune = strategies.get("TrendRider4hPIT") + retune_section = "" + if retune is not None and "TrendRider4h" in strategies: + orig = strategies["TrendRider4h"] + rows = [[esc(name), pct(s["is"]["profit_pct"]), pct(s["oos"]["profit_pct"]), f'{s["oos"]["profit_factor"]:.2f}', + pct(s["walkforward"]["pct_profitable"], 0, sign=False), f"{passed(name)} / {n_gates}"] + for name, s in (("TrendRider4h", orig), ("TrendRider4hPIT", retune))] + better = retune["oos"]["profit_pct"] > orig["oos"]["profit_pct"] + retune_section = ( + "

5.7 Re-tuning on point-in-time pairs

" + + table(["Version", "IS return", "OOS return", "OOS PF", "WF profitable", "Gates"], rows, + caption="Table 8. TrendRider4h tuned on hand-picked pairs vs re-tuned on point-in-time in-sample pairs (300 trials).") + + "

TrendRider4h's parameters were originally tuned on a hand-picked pair list, and the tuning command itself " + "used today's live pair list. Both leak hindsight. Re-tuning on the point-in-time universe " + f"raised the in-sample return from {pct(orig['is']['profit_pct'])} to {pct(retune['is']['profit_pct'])}, " + f"but the out-of-sample return {'rose' if better else 'fell'} from {pct(orig['oos']['profit_pct'])} to " + f"{pct(retune['oos']['profit_pct'])}" + + ("." if better else ", and the walk-forward record weakened. A large in-sample gain that doesn't carry over to new " + "data is the signature of overfitting, so the original parameters stay.") + + "

") + verdict = (f"{len(eligible)} of {len(strategies)} strategies passed every gate" if eligible else f"none of the {len(strategies)} strategies passed every gate, so none is eligible for real money") rot_text = "" @@ -380,9 +401,10 @@ def passed(s):

Marketing for trading bots tends to report backtests, which are easy to make look good. This study asks whether a strategy still shows an edge when it is tested the way a sceptical reviewer would test it, and whether that holds for a very small account. Four questions guide it:

+
  1. RQ1. How is the 2026 automated-trading market structured, and what risks does it carry for retail users?
  2. -
  3. RQ2. Does any of four rule-based strategies show an edge that survives point-in-time, out-of-sample testing and nine go-live gates?
  4. +
  5. RQ2. Does any of {len(strategies)} rule-based strategies show an edge that survives point-in-time, out-of-sample testing and nine go-live gates?
  6. RQ3. Can the system trade an account of about 100 AUD, and with what results?
  7. RQ4. Does taking more risk per trade produce proportionally more profit?
@@ -510,6 +532,7 @@ def passed(s): were chosen on in-sample data only.
  • EmaRsiMomentum: a 1-hour EMA crossover with RSI, ADX and 4-hour EMA200 filters and a take-profit ladder.
  • TrendDipBuyer1h: buys 1-hour pullbacks inside an established uptrend (mean reversion).
  • +{'
  • TrendRider4hPIT (new): TrendRider4h with its parameters re-tuned on point-in-time in-sample pairs, to test whether the original tuning on hand-picked pairs was flattering it.
  • ' if retune else ''}

    5. Results

    @@ -552,6 +575,7 @@ def passed(s): caption="Table 7. MomentumRotation1d in-sample return by lookback (top 3, 65 USDT start, measured before the warm-up fix).")}

    The 14-day setting was the in-sample best, but neighbouring settings fell sharply, from +37% at 14 days to −4% at 21 days. A peak this narrow suggests part of the in-sample result is luck, so its out-of-sample figure should be read with that caution.

    +{retune_section}

    6. Discussion

    RQ1. The market sells infrastructure rather than proven edges. Fixed fees, curve-fitted marketplace strategies and centralised diff --git a/scripts/research.py b/scripts/research.py index 69517dd..804ee59 100644 --- a/scripts/research.py +++ b/scripts/research.py @@ -322,8 +322,12 @@ def bias(strategies: list[str]) -> dict: def hyperopt(strategy: str, epochs: int, spaces: list[str], loss: str) -> None: + # Tune on the same point-in-time pairs the in-sample backtest uses. Without + # --pairs Freqtrade would use config.json's live list, i.e. today's top + # coins by volume, which leaks the future into the tuning. cmd = [*FREQTRADE, "hyperopt", *config_args(), "--strategy", strategy, "--timerange", tr(*IS_RANGE), "--enable-protections", + "--pairs", *universe_at(IS_RANGE[0]), "--hyperopt-loss", loss, "--spaces", *spaces, "--epochs", str(epochs), "--min-trades", "60", "-j", "-2", "--random-state", "42"] print(" ".join(cmd)) diff --git a/tests/test_universe.py b/tests/test_universe.py index c5ee9f6..9991402 100644 --- a/tests/test_universe.py +++ b/tests/test_universe.py @@ -66,3 +66,17 @@ def test_recursive_gate_ignores_fewer_startup_candles_than_the_strategy_uses(): # 5.978% is at 199 candles, below the 360 the strategy always gets. assert research.worst_recursive_deviation(RECURSIVE_TABLE) == 0.392 assert research.worst_recursive_deviation("no table here") is None + + +def test_hyperopt_tunes_on_point_in_time_pairs(monkeypatch): + """Without --pairs Freqtrade would tune on config.json's live list (today's + top coins), leaking the future into the parameters.""" + import research + + calls = [] + monkeypatch.setattr(research, "universe_at", lambda when: ("AAA/USDT", "BBB/USDT") if when == research.IS_RANGE[0] else ()) + monkeypatch.setattr(research.subprocess, "run", lambda cmd, **kw: calls.append(cmd)) + research.hyperopt("TrendRider4hPIT", 5, ["buy"], "CalmarHyperOptLoss") + cmd = calls[0] + assert cmd[cmd.index("--pairs") + 1:cmd.index("--pairs") + 3] == ["AAA/USDT", "BBB/USDT"] + assert cmd[cmd.index("--timerange") + 1] == research.tr(*research.IS_RANGE) diff --git a/user_data/research/latest-65.json b/user_data/research/latest-65.json index dd3cde7..531499c 100644 --- a/user_data/research/latest-65.json +++ b/user_data/research/latest-65.json @@ -1,5 +1,5 @@ { - "generated": "2026-10-10T15:41:02+00:00", + "generated": "2026-10-15T10:49:44+00:00", "periods": { "is": [ "2023-06-01", @@ -1021,6 +1021,245 @@ } ], "eligible_for_paper": false + }, + "TrendRider4hPIT": { + "is": { + "strategy": "TrendRider4hPIT", + "timerange": "20230601-20250601", + "start": "2023-06-01 00:00:00", + "end": "2025-06-01 00:00:00", + "starting_balance": 65.0, + "final_balance": 94.78, + "profit_pct": 45.82, + "market_change_pct": 85.76, + "max_drawdown_pct": 12.09, + "profit_factor": 1.32, + "winrate_pct": 37.2, + "trades": 285, + "calmar": 9.9, + "sharpe": 0.58, + "cagr_pct": 20.72, + "exit_reasons": { + "exit_signal": { + "count": 224, + "profit_abs": 32.21 + }, + "trailing_stop_loss": { + "count": 56, + "profit_abs": 4.24 + }, + "stop_loss": { + "count": 5, + "profit_abs": -6.67 + } + } + }, + "oos": { + "strategy": "TrendRider4hPIT", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 65.0, + "final_balance": 74.07, + "profit_pct": 13.95, + "market_change_pct": -31.1, + "max_drawdown_pct": 9.79, + "profit_factor": 1.34, + "winrate_pct": 38.8, + "trades": 98, + "calmar": 5.49, + "sharpe": 0.4, + "cagr_pct": 10.09, + "exit_reasons": { + "exit_signal": { + "count": 81, + "profit_abs": 9.97 + }, + "trailing_stop_loss": { + "count": 16, + "profit_abs": 0.04 + }, + "stop_loss": { + "count": 1, + "profit_abs": -0.94 + } + } + }, + "stress": { + "strategy": "TrendRider4hPIT", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 65.0, + "final_balance": 72.74, + "profit_pct": 11.91, + "market_change_pct": -31.1, + "max_drawdown_pct": 10.12, + "profit_factor": 1.28, + "winrate_pct": 38.8, + "trades": 98, + "calmar": 4.53, + "sharpe": 0.34, + "cagr_pct": 8.63, + "exit_reasons": { + "exit_signal": { + "count": 81, + "profit_abs": 8.87 + }, + "trailing_stop_loss": { + "count": 16, + "profit_abs": -0.18 + }, + "stop_loss": { + "count": 1, + "profit_abs": -0.95 + } + } + }, + "walkforward": { + "windows": [ + { + "window": "2023-06-01 -> 2023-09-01", + "profit_pct": -4.68, + "trades": 28, + "market_change_pct": -19.69 + }, + { + "window": "2023-09-01 -> 2023-12-01", + "profit_pct": 12.67, + "trades": 41, + "market_change_pct": 54.45 + }, + { + "window": "2023-12-01 -> 2024-03-01", + "profit_pct": 17.12, + "trades": 55, + "market_change_pct": 54.6 + }, + { + "window": "2024-03-01 -> 2024-06-01", + "profit_pct": 14.0, + "trades": 46, + "market_change_pct": 21.27 + }, + { + "window": "2024-06-01 -> 2024-09-01", + "profit_pct": -3.21, + "trades": 25, + "market_change_pct": -37.95 + }, + { + "window": "2024-09-01 -> 2024-12-01", + "profit_pct": 23.99, + "trades": 42, + "market_change_pct": 97.89 + }, + { + "window": "2024-12-01 -> 2025-03-01", + "profit_pct": 15.01, + "trades": 24, + "market_change_pct": -41.03 + }, + { + "window": "2025-03-01 -> 2025-06-01", + "profit_pct": -0.77, + "trades": 21, + "market_change_pct": 3.16 + }, + { + "window": "2025-06-01 -> 2025-09-01", + "profit_pct": 1.76, + "trades": 44, + "market_change_pct": 18.99 + }, + { + "window": "2025-09-01 -> 2025-12-01", + "profit_pct": 0.77, + "trades": 23, + "market_change_pct": -42.56 + }, + { + "window": "2025-12-01 -> 2026-03-01", + "profit_pct": 0.0, + "trades": 0, + "market_change_pct": -31.3 + }, + { + "window": "2026-03-01 -> 2026-06-01", + "profit_pct": 0.0, + "trades": 0, + "market_change_pct": 5.11 + }, + { + "window": "2026-06-01 -> 2026-09-01", + "profit_pct": -0.28, + "trades": 7, + "market_change_pct": -0.76 + }, + { + "window": "2026-09-01 -> 2026-10-10", + "profit_pct": -1.15, + "trades": 22, + "market_change_pct": 20.38 + } + ], + "pct_profitable": 50.0, + "worst_pct": -4.68 + }, + "bias": { + "lookahead_clean": true, + "lookahead_note": "20 signals checked; biased indicators: none", + "recursive_max_diff_pct": 0.0, + "recursive_note": "worst indicator deviation at the strategy's startup candle count or more" + }, + "gates": [ + { + "label": "OOS profit > 0%", + "value": 13.95, + "pass": true + }, + { + "label": "OOS profit factor >= 1.3", + "value": 1.34, + "pass": true + }, + { + "label": "OOS max drawdown <= 20%", + "value": 9.79, + "pass": true + }, + { + "label": "OOS Calmar >= 1.0", + "value": 5.49, + "pass": true + }, + { + "label": "OOS trades >= 100", + "value": 98, + "pass": false + }, + { + "label": "Profitable in >= 60% of walk-forward windows", + "value": 50.0, + "pass": false + }, + { + "label": "OOS still profitable with +50% fees", + "value": 11.91, + "pass": true + }, + { + "label": "No lookahead bias", + "value": true, + "pass": true + }, + { + "label": "Indicators stable vs startup candles (<= 1%)", + "value": 0.0, + "pass": true + } + ], + "eligible_for_paper": false } }, "balance": 65.0 diff --git a/user_data/research/latest.json b/user_data/research/latest.json index 2da0dbf..044e818 100644 --- a/user_data/research/latest.json +++ b/user_data/research/latest.json @@ -1,5 +1,5 @@ { - "generated": "2026-10-10T15:37:03+00:00", + "generated": "2026-10-15T10:38:13+00:00", "periods": { "is": [ "2023-06-01", @@ -1013,6 +1013,245 @@ } ], "eligible_for_paper": false + }, + "TrendRider4hPIT": { + "is": { + "strategy": "TrendRider4hPIT", + "timerange": "20230601-20250601", + "start": "2023-06-01 00:00:00", + "end": "2025-06-01 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1456.0, + "profit_pct": 45.6, + "market_change_pct": 85.76, + "max_drawdown_pct": 12.18, + "profit_factor": 1.31, + "winrate_pct": 37.2, + "trades": 285, + "calmar": 9.78, + "sharpe": 0.58, + "cagr_pct": 20.63, + "exit_reasons": { + "exit_signal": { + "count": 224, + "profit_abs": 497.36 + }, + "trailing_stop_loss": { + "count": 56, + "profit_abs": 63.21 + }, + "stop_loss": { + "count": 5, + "profit_abs": -104.57 + } + } + }, + "oos": { + "strategy": "TrendRider4hPIT", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1138.05, + "profit_pct": 13.8, + "market_change_pct": -31.1, + "max_drawdown_pct": 9.86, + "profit_factor": 1.33, + "winrate_pct": 38.8, + "trades": 98, + "calmar": 5.39, + "sharpe": 0.39, + "cagr_pct": 9.98, + "exit_reasons": { + "exit_signal": { + "count": 81, + "profit_abs": 152.27 + }, + "trailing_stop_loss": { + "count": 16, + "profit_abs": 0.35 + }, + "stop_loss": { + "count": 1, + "profit_abs": -14.57 + } + } + }, + "stress": { + "strategy": "TrendRider4hPIT", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1117.39, + "profit_pct": 11.74, + "market_change_pct": -31.1, + "max_drawdown_pct": 10.19, + "profit_factor": 1.27, + "winrate_pct": 38.8, + "trades": 98, + "calmar": 4.44, + "sharpe": 0.34, + "cagr_pct": 8.51, + "exit_reasons": { + "exit_signal": { + "count": 81, + "profit_abs": 135.26 + }, + "trailing_stop_loss": { + "count": 16, + "profit_abs": -3.08 + }, + "stop_loss": { + "count": 1, + "profit_abs": -14.79 + } + } + }, + "walkforward": { + "windows": [ + { + "window": "2023-06-01 -> 2023-09-01", + "profit_pct": -4.68, + "trades": 28, + "market_change_pct": -19.69 + }, + { + "window": "2023-09-01 -> 2023-12-01", + "profit_pct": 12.66, + "trades": 41, + "market_change_pct": 54.45 + }, + { + "window": "2023-12-01 -> 2024-03-01", + "profit_pct": 17.04, + "trades": 55, + "market_change_pct": 54.6 + }, + { + "window": "2024-03-01 -> 2024-06-01", + "profit_pct": 14.02, + "trades": 46, + "market_change_pct": 21.27 + }, + { + "window": "2024-06-01 -> 2024-09-01", + "profit_pct": -3.27, + "trades": 25, + "market_change_pct": -37.95 + }, + { + "window": "2024-09-01 -> 2024-12-01", + "profit_pct": 24.01, + "trades": 42, + "market_change_pct": 97.89 + }, + { + "window": "2024-12-01 -> 2025-03-01", + "profit_pct": 15.19, + "trades": 24, + "market_change_pct": -41.03 + }, + { + "window": "2025-03-01 -> 2025-06-01", + "profit_pct": -0.69, + "trades": 21, + "market_change_pct": 3.16 + }, + { + "window": "2025-06-01 -> 2025-09-01", + "profit_pct": 1.66, + "trades": 44, + "market_change_pct": 18.99 + }, + { + "window": "2025-09-01 -> 2025-12-01", + "profit_pct": 0.88, + "trades": 23, + "market_change_pct": -42.56 + }, + { + "window": "2025-12-01 -> 2026-03-01", + "profit_pct": 0.0, + "trades": 0, + "market_change_pct": -31.3 + }, + { + "window": "2026-03-01 -> 2026-06-01", + "profit_pct": 0.0, + "trades": 0, + "market_change_pct": 5.11 + }, + { + "window": "2026-06-01 -> 2026-09-01", + "profit_pct": -0.41, + "trades": 7, + "market_change_pct": -0.76 + }, + { + "window": "2026-09-01 -> 2026-10-10", + "profit_pct": -1.11, + "trades": 22, + "market_change_pct": 20.38 + } + ], + "pct_profitable": 50.0, + "worst_pct": -4.68 + }, + "bias": { + "lookahead_clean": true, + "lookahead_note": "20 signals checked; biased indicators: none", + "recursive_max_diff_pct": 0.0, + "recursive_note": "worst indicator deviation at the strategy's startup candle count or more" + }, + "gates": [ + { + "label": "OOS profit > 0%", + "value": 13.8, + "pass": true + }, + { + "label": "OOS profit factor >= 1.3", + "value": 1.33, + "pass": true + }, + { + "label": "OOS max drawdown <= 20%", + "value": 9.86, + "pass": true + }, + { + "label": "OOS Calmar >= 1.0", + "value": 5.39, + "pass": true + }, + { + "label": "OOS trades >= 100", + "value": 98, + "pass": false + }, + { + "label": "Profitable in >= 60% of walk-forward windows", + "value": 50.0, + "pass": false + }, + { + "label": "OOS still profitable with +50% fees", + "value": 11.74, + "pass": true + }, + { + "label": "No lookahead bias", + "value": true, + "pass": true + }, + { + "label": "Indicators stable vs startup candles (<= 1%)", + "value": 0.0, + "pass": true + } + ], + "eligible_for_paper": false } }, "balance": 1000.0 diff --git a/user_data/research/report-2026-10-15.json b/user_data/research/report-2026-10-15.json new file mode 100644 index 0000000..044e818 --- /dev/null +++ b/user_data/research/report-2026-10-15.json @@ -0,0 +1,1258 @@ +{ + "generated": "2026-10-15T10:38:13+00:00", + "periods": { + "is": [ + "2023-06-01", + "2025-06-01" + ], + "oos": [ + "2025-06-01", + "2026-10-10" + ] + }, + "benchmarks": { + "is": { + "equal_weight_pct": 89.11, + "btc_pct": 293.93 + }, + "oos": { + "equal_weight_pct": -32.82, + "btc_pct": -21.78 + } + }, + "universe": { + "method": "point-in-time top 25 by 30d volume", + "is": [ + "BTC/USDT", + "ETH/USDT", + "XRP/USDT", + "BNB/USDT", + "LTC/USDT", + "SOL/USDT", + "CFX/USDT", + "DOGE/USDT", + "ARPA/USDT", + "ADA/USDT", + "GALA/USDT", + "RAD/USDT", + "APT/USDT", + "OP/USDT", + "TRX/USDT", + "INJ/USDT", + "LDO/USDT", + "AVAX/USDT", + "FIL/USDT", + "SHIB/USDT", + "MASK/USDT", + "LINK/USDT", + "FET/USDT", + "JASMY/USDT", + "STX/USDT" + ], + "oos": [ + "BTC/USDT", + "ETH/USDT", + "SOL/USDT", + "PEPE/USDT", + "XRP/USDT", + "DOGE/USDT", + "SUI/USDT", + "BNB/USDT", + "TRX/USDT", + "WIF/USDT", + "ADA/USDT", + "NEIRO/USDT", + "ENA/USDT", + "WLD/USDT", + "AVAX/USDT", + "LINK/USDT", + "LTC/USDT", + "BONK/USDT", + "UNI/USDT", + "AAVE/USDT", + "ETHFI/USDT", + "TAO/USDT", + "SHIB/USDT", + "NEAR/USDT", + "ARB/USDT" + ] + }, + "strategies": { + "EmaRsiMomentum": { + "is": { + "strategy": "EmaRsiMomentum", + "timerange": "20230601-20250601", + "start": "2023-06-01 00:00:00", + "end": "2025-06-01 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1077.2, + "profit_pct": 7.72, + "market_change_pct": 85.52, + "max_drawdown_pct": 21.55, + "profit_factor": 1.07, + "winrate_pct": 52.9, + "trades": 189, + "calmar": 0.94, + "sharpe": 0.14, + "cagr_pct": 3.78, + "exit_reasons": { + "roi": { + "count": 110, + "profit_abs": 1132.39 + }, + "trailing_stop_loss": { + "count": 79, + "profit_abs": -1055.18 + } + } + }, + "oos": { + "strategy": "EmaRsiMomentum", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1051.15, + "profit_pct": 5.12, + "market_change_pct": -31.67, + "max_drawdown_pct": 18.13, + "profit_factor": 1.13, + "winrate_pct": 58.1, + "trades": 74, + "calmar": 1.09, + "sharpe": 0.15, + "cagr_pct": 3.74, + "exit_reasons": { + "roi": { + "count": 43, + "profit_abs": 444.39 + }, + "trailing_stop_loss": { + "count": 31, + "profit_abs": -393.24 + } + } + }, + "stress": { + "strategy": "EmaRsiMomentum", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1031.29, + "profit_pct": 3.13, + "market_change_pct": -31.67, + "max_drawdown_pct": 18.38, + "profit_factor": 1.08, + "winrate_pct": 52.7, + "trades": 74, + "calmar": 0.66, + "sharpe": 0.09, + "cagr_pct": 2.29, + "exit_reasons": { + "roi": { + "count": 43, + "profit_abs": 423.81 + }, + "trailing_stop_loss": { + "count": 31, + "profit_abs": -392.52 + } + } + }, + "walkforward": { + "windows": [ + { + "window": "2023-06-01 -> 2023-09-01", + "profit_pct": -5.74, + "trades": 18, + "market_change_pct": -20.06 + }, + { + "window": "2023-09-01 -> 2023-12-01", + "profit_pct": 6.3, + "trades": 23, + "market_change_pct": 51.5 + }, + { + "window": "2023-12-01 -> 2024-03-01", + "profit_pct": 9.01, + "trades": 47, + "market_change_pct": 57.02 + }, + { + "window": "2024-03-01 -> 2024-06-01", + "profit_pct": -7.08, + "trades": 23, + "market_change_pct": 20.51 + }, + { + "window": "2024-06-01 -> 2024-09-01", + "profit_pct": -8.48, + "trades": 17, + "market_change_pct": -37.21 + }, + { + "window": "2024-09-01 -> 2024-12-01", + "profit_pct": 1.33, + "trades": 22, + "market_change_pct": 97.44 + }, + { + "window": "2024-12-01 -> 2025-03-01", + "profit_pct": -2.33, + "trades": 14, + "market_change_pct": -42.39 + }, + { + "window": "2025-03-01 -> 2025-06-01", + "profit_pct": -1.88, + "trades": 18, + "market_change_pct": 5.71 + }, + { + "window": "2025-06-01 -> 2025-09-01", + "profit_pct": 4.77, + "trades": 20, + "market_change_pct": 19.44 + }, + { + "window": "2025-09-01 -> 2025-12-01", + "profit_pct": -2.48, + "trades": 13, + "market_change_pct": -42.63 + }, + { + "window": "2025-12-01 -> 2026-03-01", + "profit_pct": -7.78, + "trades": 7, + "market_change_pct": -33.34 + }, + { + "window": "2026-03-01 -> 2026-06-01", + "profit_pct": -6.09, + "trades": 16, + "market_change_pct": 7.44 + }, + { + "window": "2026-06-01 -> 2026-09-01", + "profit_pct": 1.73, + "trades": 10, + "market_change_pct": -0.87 + }, + { + "window": "2026-09-01 -> 2026-10-10", + "profit_pct": 13.76, + "trades": 15, + "market_change_pct": 19.98 + } + ], + "pct_profitable": 42.9, + "worst_pct": -8.48 + }, + "bias": { + "lookahead_clean": true, + "lookahead_note": "20 signals checked; biased indicators: none", + "recursive_max_diff_pct": 0.001, + "recursive_note": "worst indicator deviation at the strategy's startup candle count or more" + }, + "gates": [ + { + "label": "OOS profit > 0%", + "value": 5.12, + "pass": true + }, + { + "label": "OOS profit factor >= 1.3", + "value": 1.13, + "pass": false + }, + { + "label": "OOS max drawdown <= 20%", + "value": 18.13, + "pass": true + }, + { + "label": "OOS Calmar >= 1.0", + "value": 1.09, + "pass": true + }, + { + "label": "OOS trades >= 100", + "value": 74, + "pass": false + }, + { + "label": "Profitable in >= 60% of walk-forward windows", + "value": 42.9, + "pass": false + }, + { + "label": "OOS still profitable with +50% fees", + "value": 3.13, + "pass": true + }, + { + "label": "No lookahead bias", + "value": true, + "pass": true + }, + { + "label": "Indicators stable vs startup candles (<= 1%)", + "value": 0.001, + "pass": true + } + ], + "eligible_for_paper": false + }, + "MomentumRotation1d": { + "is": { + "strategy": "MomentumRotation1d", + "timerange": "20230601-20250601", + "start": "2023-06-01 00:00:00", + "end": "2025-06-01 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1244.39, + "profit_pct": 24.44, + "market_change_pct": 89.11, + "max_drawdown_pct": 14.77, + "profit_factor": 1.29, + "winrate_pct": 40.8, + "trades": 201, + "calmar": 4.32, + "sharpe": 0.41, + "cagr_pct": 11.54, + "exit_reasons": { + "exit_signal": { + "count": 149, + "profit_abs": 222.64 + }, + "trailing_stop_loss": { + "count": 45, + "profit_abs": 149.39 + }, + "stop_loss": { + "count": 7, + "profit_abs": -127.63 + } + } + }, + "oos": { + "strategy": "MomentumRotation1d", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-09 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1131.24, + "profit_pct": 13.12, + "market_change_pct": -32.82, + "max_drawdown_pct": 5.21, + "profit_factor": 1.59, + "winrate_pct": 42.9, + "trades": 63, + "calmar": 9.72, + "sharpe": 0.32, + "cagr_pct": 9.52, + "exit_reasons": { + "exit_signal": { + "count": 44, + "profit_abs": 202.89 + }, + "force_exit": { + "count": 2, + "profit_abs": -20.52 + }, + "trailing_stop_loss": { + "count": 17, + "profit_abs": -51.13 + } + } + }, + "stress": { + "strategy": "MomentumRotation1d", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-09 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1123.73, + "profit_pct": 12.37, + "market_change_pct": -32.82, + "max_drawdown_pct": 5.35, + "profit_factor": 1.54, + "winrate_pct": 42.9, + "trades": 63, + "calmar": 8.92, + "sharpe": 0.3, + "cagr_pct": 8.98, + "exit_reasons": { + "exit_signal": { + "count": 44, + "profit_abs": 197.48 + }, + "force_exit": { + "count": 2, + "profit_abs": -20.53 + }, + "trailing_stop_loss": { + "count": 17, + "profit_abs": -53.22 + } + } + }, + "walkforward": { + "windows": [ + { + "window": "2023-06-01 -> 2023-09-01", + "profit_pct": -1.77, + "trades": 25, + "market_change_pct": -21.36 + }, + { + "window": "2023-09-01 -> 2023-12-01", + "profit_pct": 5.56, + "trades": 23, + "market_change_pct": 57.28 + }, + { + "window": "2023-12-01 -> 2024-03-01", + "profit_pct": 13.85, + "trades": 30, + "market_change_pct": 59.77 + }, + { + "window": "2024-03-01 -> 2024-06-01", + "profit_pct": 3.75, + "trades": 24, + "market_change_pct": 9.47 + }, + { + "window": "2024-06-01 -> 2024-09-01", + "profit_pct": -8.01, + "trades": 25, + "market_change_pct": -40.81 + }, + { + "window": "2024-09-01 -> 2024-12-01", + "profit_pct": 0.75, + "trades": 29, + "market_change_pct": 114.33 + }, + { + "window": "2024-12-01 -> 2025-03-01", + "profit_pct": -10.7, + "trades": 26, + "market_change_pct": -43.91 + }, + { + "window": "2025-03-01 -> 2025-06-01", + "profit_pct": -3.86, + "trades": 24, + "market_change_pct": 5.5 + }, + { + "window": "2025-06-01 -> 2025-09-01", + "profit_pct": 9.29, + "trades": 24, + "market_change_pct": 15.51 + }, + { + "window": "2025-09-01 -> 2025-12-01", + "profit_pct": 2.03, + "trades": 15, + "market_change_pct": -42.47 + }, + { + "window": "2025-12-01 -> 2026-03-01", + "profit_pct": 0.0, + "trades": 0, + "market_change_pct": -33.6 + }, + { + "window": "2026-03-01 -> 2026-06-01", + "profit_pct": -0.69, + "trades": 3, + "market_change_pct": 7.9 + }, + { + "window": "2026-06-01 -> 2026-09-01", + "profit_pct": 4.91, + "trades": 4, + "market_change_pct": -0.46 + }, + { + "window": "2026-09-01 -> 2026-10-10", + "profit_pct": 10.19, + "trades": 13, + "market_change_pct": 19.59 + } + ], + "pct_profitable": 57.1, + "worst_pct": -10.7 + }, + "bias": { + "lookahead_clean": true, + "lookahead_note": "cross-sectional: 132 trades compared with later data removed from all pairs (94 in the full run); no differences", + "recursive_max_diff_pct": 0.392, + "recursive_note": "worst indicator deviation at the strategy's startup candle count or more" + }, + "gates": [ + { + "label": "OOS profit > 0%", + "value": 13.12, + "pass": true + }, + { + "label": "OOS profit factor >= 1.3", + "value": 1.59, + "pass": true + }, + { + "label": "OOS max drawdown <= 20%", + "value": 5.21, + "pass": true + }, + { + "label": "OOS Calmar >= 1.0", + "value": 9.72, + "pass": true + }, + { + "label": "OOS trades >= 100", + "value": 63, + "pass": false + }, + { + "label": "Profitable in >= 60% of walk-forward windows", + "value": 57.1, + "pass": false + }, + { + "label": "OOS still profitable with +50% fees", + "value": 12.37, + "pass": true + }, + { + "label": "No lookahead bias", + "value": true, + "pass": true + }, + { + "label": "Indicators stable vs startup candles (<= 1%)", + "value": 0.392, + "pass": true + } + ], + "eligible_for_paper": false + }, + "TrendDipBuyer1h": { + "is": { + "strategy": "TrendDipBuyer1h", + "timerange": "20230601-20250601", + "start": "2023-06-01 00:00:00", + "end": "2025-06-01 00:00:00", + "starting_balance": 1000.0, + "final_balance": 920.32, + "profit_pct": -7.97, + "market_change_pct": 85.52, + "max_drawdown_pct": 26.4, + "profit_factor": 0.96, + "winrate_pct": 64.3, + "trades": 932, + "calmar": -0.79, + "sharpe": -0.34, + "cagr_pct": -4.06, + "exit_reasons": { + "exit_signal": { + "count": 732, + "profit_abs": 867.7 + }, + "roi": { + "count": 92, + "profit_abs": 550.15 + }, + "trailing_stop_loss": { + "count": 108, + "profit_abs": -1497.52 + } + } + }, + "oos": { + "strategy": "TrendDipBuyer1h", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 821.78, + "profit_pct": -17.82, + "market_change_pct": -31.67, + "max_drawdown_pct": 19.23, + "profit_factor": 0.73, + "winrate_pct": 62.7, + "trades": 300, + "calmar": -3.57, + "sharpe": -1.29, + "cagr_pct": -13.45, + "exit_reasons": { + "roi": { + "count": 25, + "profit_abs": 125.05 + }, + "exit_signal": { + "count": 242, + "profit_abs": 121.05 + }, + "trailing_stop_loss": { + "count": 33, + "profit_abs": -424.31 + } + } + }, + "stress": { + "strategy": "TrendDipBuyer1h", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 752.6, + "profit_pct": -24.74, + "market_change_pct": -31.67, + "max_drawdown_pct": 25.85, + "profit_factor": 0.62, + "winrate_pct": 59.2, + "trades": 284, + "calmar": -3.69, + "sharpe": -1.83, + "cagr_pct": -18.87, + "exit_reasons": { + "roi": { + "count": 22, + "profit_abs": 108.34 + }, + "exit_signal": { + "count": 228, + "profit_abs": 65.49 + }, + "trailing_stop_loss": { + "count": 34, + "profit_abs": -421.23 + } + } + }, + "walkforward": { + "windows": [ + { + "window": "2023-06-01 -> 2023-09-01", + "profit_pct": -7.65, + "trades": 116, + "market_change_pct": -20.06 + }, + { + "window": "2023-09-01 -> 2023-12-01", + "profit_pct": 3.35, + "trades": 119, + "market_change_pct": 51.5 + }, + { + "window": "2023-12-01 -> 2024-03-01", + "profit_pct": 7.23, + "trades": 171, + "market_change_pct": 57.02 + }, + { + "window": "2024-03-01 -> 2024-06-01", + "profit_pct": -0.33, + "trades": 135, + "market_change_pct": 20.51 + }, + { + "window": "2024-06-01 -> 2024-09-01", + "profit_pct": -5.66, + "trades": 79, + "market_change_pct": -37.21 + }, + { + "window": "2024-09-01 -> 2024-12-01", + "profit_pct": 7.37, + "trades": 140, + "market_change_pct": 97.44 + }, + { + "window": "2024-12-01 -> 2025-03-01", + "profit_pct": 6.81, + "trades": 116, + "market_change_pct": -42.39 + }, + { + "window": "2025-03-01 -> 2025-06-01", + "profit_pct": -2.36, + "trades": 93, + "market_change_pct": 5.71 + }, + { + "window": "2025-06-01 -> 2025-09-01", + "profit_pct": -8.82, + "trades": 135, + "market_change_pct": 19.44 + }, + { + "window": "2025-09-01 -> 2025-12-01", + "profit_pct": -4.94, + "trades": 74, + "market_change_pct": -42.63 + }, + { + "window": "2025-12-01 -> 2026-03-01", + "profit_pct": 0.0, + "trades": 0, + "market_change_pct": -33.34 + }, + { + "window": "2026-03-01 -> 2026-06-01", + "profit_pct": 0.29, + "trades": 1, + "market_change_pct": 7.44 + }, + { + "window": "2026-06-01 -> 2026-09-01", + "profit_pct": -0.78, + "trades": 18, + "market_change_pct": -0.87 + }, + { + "window": "2026-09-01 -> 2026-10-10", + "profit_pct": -0.13, + "trades": 86, + "market_change_pct": 19.98 + } + ], + "pct_profitable": 35.7, + "worst_pct": -8.82 + }, + "bias": { + "lookahead_clean": true, + "lookahead_note": "20 signals checked; biased indicators: none", + "recursive_max_diff_pct": 0.0, + "recursive_note": "worst indicator deviation at the strategy's startup candle count or more" + }, + "gates": [ + { + "label": "OOS profit > 0%", + "value": -17.82, + "pass": false + }, + { + "label": "OOS profit factor >= 1.3", + "value": 0.73, + "pass": false + }, + { + "label": "OOS max drawdown <= 20%", + "value": 19.23, + "pass": true + }, + { + "label": "OOS Calmar >= 1.0", + "value": -3.57, + "pass": false + }, + { + "label": "OOS trades >= 100", + "value": 300, + "pass": true + }, + { + "label": "Profitable in >= 60% of walk-forward windows", + "value": 35.7, + "pass": false + }, + { + "label": "OOS still profitable with +50% fees", + "value": -24.74, + "pass": false + }, + { + "label": "No lookahead bias", + "value": true, + "pass": true + }, + { + "label": "Indicators stable vs startup candles (<= 1%)", + "value": 0.0, + "pass": true + } + ], + "eligible_for_paper": false + }, + "TrendRider4h": { + "is": { + "strategy": "TrendRider4h", + "timerange": "20230601-20250601", + "start": "2023-06-01 00:00:00", + "end": "2025-06-01 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1056.11, + "profit_pct": 5.61, + "market_change_pct": 85.76, + "max_drawdown_pct": 15.21, + "profit_factor": 1.05, + "winrate_pct": 35.8, + "trades": 274, + "calmar": 0.96, + "sharpe": 0.11, + "cagr_pct": 2.76, + "exit_reasons": { + "exit_signal": { + "count": 242, + "profit_abs": 189.2 + }, + "trailing_stop_loss": { + "count": 26, + "profit_abs": -39.65 + }, + "stop_loss": { + "count": 6, + "profit_abs": -93.45 + } + } + }, + "oos": { + "strategy": "TrendRider4h", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1173.08, + "profit_pct": 17.31, + "market_change_pct": -31.1, + "max_drawdown_pct": 9.79, + "profit_factor": 1.43, + "winrate_pct": 34.3, + "trades": 99, + "calmar": 6.81, + "sharpe": 0.48, + "cagr_pct": 12.47, + "exit_reasons": { + "exit_signal": { + "count": 93, + "profit_abs": 219.29 + }, + "trailing_stop_loss": { + "count": 6, + "profit_abs": -46.2 + } + } + }, + "stress": { + "strategy": "TrendRider4h", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1153.88, + "profit_pct": 15.39, + "market_change_pct": -31.1, + "max_drawdown_pct": 10.0, + "profit_factor": 1.37, + "winrate_pct": 34.3, + "trades": 99, + "calmar": 5.93, + "sharpe": 0.43, + "cagr_pct": 11.11, + "exit_reasons": { + "exit_signal": { + "count": 93, + "profit_abs": 200.98 + }, + "trailing_stop_loss": { + "count": 6, + "profit_abs": -47.1 + } + } + }, + "walkforward": { + "windows": [ + { + "window": "2023-06-01 -> 2023-09-01", + "profit_pct": -6.98, + "trades": 35, + "market_change_pct": -19.69 + }, + { + "window": "2023-09-01 -> 2023-12-01", + "profit_pct": 0.67, + "trades": 34, + "market_change_pct": 54.45 + }, + { + "window": "2023-12-01 -> 2024-03-01", + "profit_pct": 22.35, + "trades": 48, + "market_change_pct": 54.6 + }, + { + "window": "2024-03-01 -> 2024-06-01", + "profit_pct": 2.15, + "trades": 47, + "market_change_pct": 21.27 + }, + { + "window": "2024-06-01 -> 2024-09-01", + "profit_pct": -5.51, + "trades": 25, + "market_change_pct": -37.95 + }, + { + "window": "2024-09-01 -> 2024-12-01", + "profit_pct": 30.74, + "trades": 43, + "market_change_pct": 97.89 + }, + { + "window": "2024-12-01 -> 2025-03-01", + "profit_pct": 0.55, + "trades": 32, + "market_change_pct": -41.03 + }, + { + "window": "2025-03-01 -> 2025-06-01", + "profit_pct": 0.79, + "trades": 21, + "market_change_pct": 3.16 + }, + { + "window": "2025-06-01 -> 2025-09-01", + "profit_pct": 5.37, + "trades": 46, + "market_change_pct": 18.99 + }, + { + "window": "2025-09-01 -> 2025-12-01", + "profit_pct": -0.92, + "trades": 27, + "market_change_pct": -42.56 + }, + { + "window": "2025-12-01 -> 2026-03-01", + "profit_pct": 0.0, + "trades": 0, + "market_change_pct": -31.3 + }, + { + "window": "2026-03-01 -> 2026-06-01", + "profit_pct": -0.86, + "trades": 1, + "market_change_pct": 5.11 + }, + { + "window": "2026-06-01 -> 2026-09-01", + "profit_pct": 6.13, + "trades": 7, + "market_change_pct": -0.76 + }, + { + "window": "2026-09-01 -> 2026-10-10", + "profit_pct": 0.2, + "trades": 21, + "market_change_pct": 20.38 + } + ], + "pct_profitable": 64.3, + "worst_pct": -6.98 + }, + "bias": { + "lookahead_clean": true, + "lookahead_note": "20 signals checked; biased indicators: none", + "recursive_max_diff_pct": 0.0, + "recursive_note": "worst indicator deviation at the strategy's startup candle count or more" + }, + "gates": [ + { + "label": "OOS profit > 0%", + "value": 17.31, + "pass": true + }, + { + "label": "OOS profit factor >= 1.3", + "value": 1.43, + "pass": true + }, + { + "label": "OOS max drawdown <= 20%", + "value": 9.79, + "pass": true + }, + { + "label": "OOS Calmar >= 1.0", + "value": 6.81, + "pass": true + }, + { + "label": "OOS trades >= 100", + "value": 99, + "pass": false + }, + { + "label": "Profitable in >= 60% of walk-forward windows", + "value": 64.3, + "pass": true + }, + { + "label": "OOS still profitable with +50% fees", + "value": 15.39, + "pass": true + }, + { + "label": "No lookahead bias", + "value": true, + "pass": true + }, + { + "label": "Indicators stable vs startup candles (<= 1%)", + "value": 0.0, + "pass": true + } + ], + "eligible_for_paper": false + }, + "TrendRider4hPIT": { + "is": { + "strategy": "TrendRider4hPIT", + "timerange": "20230601-20250601", + "start": "2023-06-01 00:00:00", + "end": "2025-06-01 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1456.0, + "profit_pct": 45.6, + "market_change_pct": 85.76, + "max_drawdown_pct": 12.18, + "profit_factor": 1.31, + "winrate_pct": 37.2, + "trades": 285, + "calmar": 9.78, + "sharpe": 0.58, + "cagr_pct": 20.63, + "exit_reasons": { + "exit_signal": { + "count": 224, + "profit_abs": 497.36 + }, + "trailing_stop_loss": { + "count": 56, + "profit_abs": 63.21 + }, + "stop_loss": { + "count": 5, + "profit_abs": -104.57 + } + } + }, + "oos": { + "strategy": "TrendRider4hPIT", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1138.05, + "profit_pct": 13.8, + "market_change_pct": -31.1, + "max_drawdown_pct": 9.86, + "profit_factor": 1.33, + "winrate_pct": 38.8, + "trades": 98, + "calmar": 5.39, + "sharpe": 0.39, + "cagr_pct": 9.98, + "exit_reasons": { + "exit_signal": { + "count": 81, + "profit_abs": 152.27 + }, + "trailing_stop_loss": { + "count": 16, + "profit_abs": 0.35 + }, + "stop_loss": { + "count": 1, + "profit_abs": -14.57 + } + } + }, + "stress": { + "strategy": "TrendRider4hPIT", + "timerange": "20250601-20261010", + "start": "2025-06-01 00:00:00", + "end": "2026-10-10 00:00:00", + "starting_balance": 1000.0, + "final_balance": 1117.39, + "profit_pct": 11.74, + "market_change_pct": -31.1, + "max_drawdown_pct": 10.19, + "profit_factor": 1.27, + "winrate_pct": 38.8, + "trades": 98, + "calmar": 4.44, + "sharpe": 0.34, + "cagr_pct": 8.51, + "exit_reasons": { + "exit_signal": { + "count": 81, + "profit_abs": 135.26 + }, + "trailing_stop_loss": { + "count": 16, + "profit_abs": -3.08 + }, + "stop_loss": { + "count": 1, + "profit_abs": -14.79 + } + } + }, + "walkforward": { + "windows": [ + { + "window": "2023-06-01 -> 2023-09-01", + "profit_pct": -4.68, + "trades": 28, + "market_change_pct": -19.69 + }, + { + "window": "2023-09-01 -> 2023-12-01", + "profit_pct": 12.66, + "trades": 41, + "market_change_pct": 54.45 + }, + { + "window": "2023-12-01 -> 2024-03-01", + "profit_pct": 17.04, + "trades": 55, + "market_change_pct": 54.6 + }, + { + "window": "2024-03-01 -> 2024-06-01", + "profit_pct": 14.02, + "trades": 46, + "market_change_pct": 21.27 + }, + { + "window": "2024-06-01 -> 2024-09-01", + "profit_pct": -3.27, + "trades": 25, + "market_change_pct": -37.95 + }, + { + "window": "2024-09-01 -> 2024-12-01", + "profit_pct": 24.01, + "trades": 42, + "market_change_pct": 97.89 + }, + { + "window": "2024-12-01 -> 2025-03-01", + "profit_pct": 15.19, + "trades": 24, + "market_change_pct": -41.03 + }, + { + "window": "2025-03-01 -> 2025-06-01", + "profit_pct": -0.69, + "trades": 21, + "market_change_pct": 3.16 + }, + { + "window": "2025-06-01 -> 2025-09-01", + "profit_pct": 1.66, + "trades": 44, + "market_change_pct": 18.99 + }, + { + "window": "2025-09-01 -> 2025-12-01", + "profit_pct": 0.88, + "trades": 23, + "market_change_pct": -42.56 + }, + { + "window": "2025-12-01 -> 2026-03-01", + "profit_pct": 0.0, + "trades": 0, + "market_change_pct": -31.3 + }, + { + "window": "2026-03-01 -> 2026-06-01", + "profit_pct": 0.0, + "trades": 0, + "market_change_pct": 5.11 + }, + { + "window": "2026-06-01 -> 2026-09-01", + "profit_pct": -0.41, + "trades": 7, + "market_change_pct": -0.76 + }, + { + "window": "2026-09-01 -> 2026-10-10", + "profit_pct": -1.11, + "trades": 22, + "market_change_pct": 20.38 + } + ], + "pct_profitable": 50.0, + "worst_pct": -4.68 + }, + "bias": { + "lookahead_clean": true, + "lookahead_note": "20 signals checked; biased indicators: none", + "recursive_max_diff_pct": 0.0, + "recursive_note": "worst indicator deviation at the strategy's startup candle count or more" + }, + "gates": [ + { + "label": "OOS profit > 0%", + "value": 13.8, + "pass": true + }, + { + "label": "OOS profit factor >= 1.3", + "value": 1.33, + "pass": true + }, + { + "label": "OOS max drawdown <= 20%", + "value": 9.86, + "pass": true + }, + { + "label": "OOS Calmar >= 1.0", + "value": 5.39, + "pass": true + }, + { + "label": "OOS trades >= 100", + "value": 98, + "pass": false + }, + { + "label": "Profitable in >= 60% of walk-forward windows", + "value": 50.0, + "pass": false + }, + { + "label": "OOS still profitable with +50% fees", + "value": 11.74, + "pass": true + }, + { + "label": "No lookahead bias", + "value": true, + "pass": true + }, + { + "label": "Indicators stable vs startup candles (<= 1%)", + "value": 0.0, + "pass": true + } + ], + "eligible_for_paper": false + } + }, + "balance": 1000.0 +} \ No newline at end of file diff --git a/user_data/strategies/TrendRider4hPIT.json b/user_data/strategies/TrendRider4hPIT.json new file mode 100644 index 0000000..c97e3ed --- /dev/null +++ b/user_data/strategies/TrendRider4hPIT.json @@ -0,0 +1,31 @@ +{ + "strategy_name": "TrendRider4hPIT", + "params": { + "roi": { + "0": 100 + }, + "stoploss": { + "stoploss": -0.15 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": null, + "trailing_stop_positive_offset": 0.0, + "trailing_only_offset_is_reached": false + }, + "max_open_trades": { + "max_open_trades": 3 + }, + "buy": { + "adx_threshold": 22, + "breakout_period": 28 + }, + "sell": { + "chandelier_mult": 3.9, + "exit_period": 5, + "use_donchian_exit": true + } + }, + "ft_stratparam_v": 1, + "export_time": "2026-10-15 10:26:42.613575+00:00" +} \ No newline at end of file diff --git a/user_data/strategies/TrendRider4hPIT.py b/user_data/strategies/TrendRider4hPIT.py new file mode 100644 index 0000000..628b3e1 --- /dev/null +++ b/user_data/strategies/TrendRider4hPIT.py @@ -0,0 +1,23 @@ +# TrendRider4hPIT.py +# TrendRider4h re-tuned on point-in-time in-sample pairs (candidate). +# +# Same entry, exit and sizing logic as TrendRider4h. Only the hyperopt +# parameters differ: they live in TrendRider4hPIT.json, produced by +# python scripts/research.py hyperopt -s TrendRider4hPIT +# which tunes on the pairs that were actually the top 25 by volume at the +# start of the in-sample period. TrendRider4h's own parameters were tuned on a +# hand-picked list chosen with today's knowledge, so this is the fairer +# version; judge it on out-of-sample data only. + +import sys +from pathlib import Path + +_HERE = str(Path(__file__).resolve().parent) +if _HERE not in sys.path: + sys.path.insert(0, _HERE) + +from TrendRider4h import TrendRider4h # noqa: E402 + + +class TrendRider4hPIT(TrendRider4h): + pass