Quant review 2026-09-26: alert cooldown, plain-language alerts, M15 grade-A SELL block, 1R printed targets, panel/breach fixes
Live signals.db review (2026-09-04 → 09-25, 100 unique setups replayed on the scans' stored candles): the stop-hunt claim is refuted (71% of stop-outs ran ≥ 1R past the stop, the same as the books' 70–72%); −25.1R of the −37.8R raw live book were consecutive-bar re-entries the backtester's 5-bar cooldown never takes; the cooldown view sits at the 3rd–5th percentile of the books' three-week windows. Delivery (book-neutral): - telegram_bot: 5-bar alert cooldown per timeframe (_ALERT_COOLDOWN_BARS = backtest.cooldown_bars); setup block prints the trail first as the tested exit, TP1/TP2 as guides with their R, "Planned TP1 RR", and one validated-book line per setup class (agent_panel_base_rates.json); the informational 100-pip TP line is removed (owner request). - AI block rewritten for a new trader: "AI check: the odds look better than / worse than / about usual", the chance "in 100" against the usual odds, one reason for and one against, no verdict word or confidence figure; rendered whenever the verdict carries a probability. agent_panel: persona prompts ban trading jargon (PANEL_PROMPT_VERSION v2.1-2026-09-26); the macro-source verdict now carries its persona's p_win (every live v2 row had panel_probability NULL). Dashboard and guides mirror the wording. - telegram_bot: level-breach rescans skip the settlement-pause window (the 20:45 M15 bar was re-logged at 22:01/22:06/22:11 every evening). Signal (pre-registered rule-1 campaign, CUDA twin paired with gpu_backtest.py): - M15 grade-A SELLs blocked (sell_min_quality ["A+","B"], M15 override 6): W2 813/+154.15R PF 1.53 vs 931/+146.14R PF 1.43, OOS +68.5 vs +60.67, W3 max DD 20.9 vs 19.3R. PASSED a–f. - Printed target 1 floored at 1R in every generator (signals.tp_print_floor_rr), applied AFTER the min-RR and spread gates so no trade changes; mirrored in cuda_signal_kernels.py K2. Label-only re-pin of all four timeframes (only tp1/rr_planned columns move). A floor applied before the gate cost −24.6R and was rejected. - FAILED: M15 min_rr_ratio 1.0 (trade-identical, −2.0R). Owner's call: M5 tp_floor_rr 1.0 (+48.9R W2, max DD 10.1 → 15.9R), not shipped. New pins: M5 W2 e599c8a8…, M15 W2 5b65ecf6…, M15 OOS 09b8a8e7…, H1 W3 a44fd19b…, H4 W3 f3587bae…; twin and reference MD5s equal on every window. Base-rate table rebuilt from the new pins. Suite 558 passed. Evidence: backtest/research/rr_and_sell_campaign_2026-09-26.md (plus the 2026-09-18 loss-anatomy and wrong-direction reports it builds on).
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
# Risk-reward floor and M15 grade-A SELL block — pre-registered campaign, 2026-09-26
|
||||
|
||||
Owner questions (2026-09-26): plans sometimes show a target smaller than the stop (RR 0.75 / 0.5);
|
||||
and "we still lose money". The live review of `logs/signals.db` (2026-09-04 → 2026-09-25, 100
|
||||
unique setups replayed on the scans' own stored candles) found: no stop-hunt signature (52 stop-outs,
|
||||
71% ran ≥ 1R past the stop, the same as the books' 70–72%); −25.1R of the −37.8R raw live book were
|
||||
consecutive-bar re-entries the backtester's 5-bar cooldown never takes; the cooldown view
|
||||
(−12.7R / 57) sits at the 3rd–5th percentile of three-week windows of the two-year books.
|
||||
|
||||
This file was written BEFORE any candidate run. Rules first, results appended below them.
|
||||
|
||||
## Harness
|
||||
|
||||
`venv/bin/python backtest/cudaBasedBacktesting.py --pair XAUUSD --primary-tf <tf> --start <s>
|
||||
--end <e> --spread <0.65|0.30> [--set ...] --export-csv <out>` (CUDA research twin; its parity
|
||||
with `gpu_backtest.py` was re-proven today: the M15 and M5 two-year controls reproduce the pinned
|
||||
MD5s `b4aa07e33e17e23774c486a6900d673b` and `49f8e602fc6ae769713c94ef7b257810`). Factory
|
||||
configuration; `--set` is the only override. A passing candidate is re-run once on
|
||||
`gpu_backtest.py` before it ships and the two MD5s must be equal.
|
||||
|
||||
Windows: W2 2024-09-05 → 2026-09-05 (halves split at 2025-09-05); OOS 2024-06-01 → 2025-03-30
|
||||
(halves at the midpoint 2024-10-30); W3 2023-09-05 → 2026-09-05 (the owner's 3-year maximum,
|
||||
halves at 2025-03-05; used for the max-DD clause). Costs $0.65 (headline) and $0.30.
|
||||
|
||||
## Candidates
|
||||
|
||||
| # | TF | `--set` | Hypothesis |
|
||||
|---|---|---|---|
|
||||
| B1 | M15 | `signals.min_rr_ratio=1.0` | Vetoes the grade-A XAU_ATR plan (RR 0.75 / 0.5); the signal falls through to the GENERIC swing setup whose M15 floor is already 1R. Every M15 plan then shows RR ≥ 1. Two-year book: XAU_ATR 174 / +5.7R / 24% wrong-direction vs GENERIC 757 / +140.5R / 16% |
|
||||
| B2 | M5 | `signals.tp_floor_rr=1.0` | Retest of the 1R target floor on M5 (failed 2026-07-11 on Twelve Data bars at $0.30). Expected to fail again: it lifts low-ATR targets past the $9 spread gate |
|
||||
| B3 | M15 | `xau_strategy.sell_min_quality=["A+","B"]` | Parked cycle-2 candidate: M15 grade-A SELLs 230 / −14.4R, negative in y1, y2, y3 and OOS (cycle_2026-09-10.md). Counter-fact on record: those shorts paid in 2021–22 |
|
||||
| B4 | M15 | B1 + B3 | The shipped combination if both pass alone |
|
||||
|
||||
## Acceptance rule (CLAUDE.md rule 1, written before the runs)
|
||||
|
||||
A candidate PASSES only if, at $0.65, on W2: (a) total R ≥ control + 5R; (b) both halves ≥ the
|
||||
control's halves − 1R (no half may fund the other); (c) PF ≥ 1.3 and ≥ control − 0.02; (d) OOS
|
||||
total R ≥ control − 1R; (e) W3 max drawdown ≤ control + 10%; (f) the same sign of ΔR at $0.30.
|
||||
A candidate that changes fewer than 20 trades on W2 is "no evidence", not a pass. B4 must pass
|
||||
the rule on its own numbers, not by inheritance.
|
||||
|
||||
Judge run-level numbers, never the added/removed bucket alone (the 5-bar cooldown displaces
|
||||
entries). Trade diff keyed on (entry time, direction).
|
||||
|
||||
## Results (all runs on the CUDA twin, `Backend: CuPy GPU (NVIDIA GeForce RTX 4070 SUPER, cupy 14.0.1)`, 0 cache fallbacks; controls reproduce the pinned MD5s)
|
||||
|
||||
| TF | Window | Cost | Candidate | Trades | Total R | H1 | H2 | WR | PF | Max DD | MD5 (8) |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| M15 | W2 | $0.65 | control | 931 | +146.14 | +66.14 | +80.00 | 60.5% | 1.43 | 16.1 | b4aa07e3 |
|
||||
| M15 | W2 | $0.65 | B1 | 931 | +144.16 | +65.09 | +79.07 | 60.3% | 1.42 | 16.1 | 80428e98 |
|
||||
| M15 | W2 | $0.65 | **B3** | 813 | **+154.15** | +70.10 | +84.04 | 62.2% | 1.53 | 15.6 | 497520ef |
|
||||
| M15 | W2 | $0.65 | B4 | 813 | +153.41 | +70.73 | +82.68 | 62.1% | 1.53 | 15.6 | 177b716d |
|
||||
| M15 | W2 | $0.30 | control | 931 | +161.44 | +75.63 | +85.81 | 61.0% | 1.48 | 14.9 | 6bdea010 |
|
||||
| M15 | W2 | $0.30 | B1 | 931 | +158.17 | +74.41 | +83.75 | 60.7% | 1.47 | 14.9 | 21dcfc9e |
|
||||
| M15 | W2 | $0.30 | B3 | 813 | +167.53 | +78.61 | +88.91 | 62.6% | 1.59 | 14.5 | aff050f7 |
|
||||
| M15 | W2 | $0.30 | B4 | 813 | +166.06 | +79.12 | +86.95 | 62.5% | 1.58 | 14.8 | 03a412b8 |
|
||||
| M15 | OOS | $0.65 | control | 355 | +60.67 | +9.14 | +51.52 | 60.8% | 1.49 | 17.1 | 9fd624fb |
|
||||
| M15 | OOS | $0.65 | B1 | 355 | +60.67 | +9.14 | +51.52 | 60.8% | 1.49 | 17.1 | 9fd624fb (identical) |
|
||||
| M15 | OOS | $0.65 | B3 = B4 | 313 | +68.50 | +18.87 | +49.63 | 63.6% | 1.67 | 12.4 | 09b8a8e7 |
|
||||
| M15 | OOS | $0.30 | control | 355 | +69.18 | +13.48 | +55.70 | 61.7% | 1.57 | 14.9 | 30a82817 |
|
||||
| M15 | OOS | $0.30 | B3 = B4 | 313 | +76.21 | +22.92 | +53.29 | 64.5% | 1.77 | 11.1 | 5fc34cef |
|
||||
| M15 | W3 | $0.65 | control | 1273 | +186.61 | +75.87 | +110.74 | 59.4% | 1.39 | 19.3 | 5194e5d4 |
|
||||
| M15 | W3 | $0.65 | B1 | 1273 | +183.78 | +75.83 | +107.95 | 59.2% | 1.39 | 19.3 | 2d5cfc31 |
|
||||
| M15 | W3 | $0.65 | B3 | 1105 | +195.79 | +76.87 | +118.92 | 61.1% | 1.49 | 20.9 | 8bed7199 |
|
||||
| M15 | W3 | $0.65 | B4 | 1105 | +194.19 | +76.00 | +118.18 | 61.0% | 1.48 | 20.9 | 05201726 |
|
||||
| M15 | W3 | $0.30 | control / B1 / B3 / B4 | 1273 / 1273 / 1105 / 1105 | +210.42 / +206.25 / +216.53 / +214.19 | | | | 1.45 / 1.44 / 1.55 / 1.54 | 17.7 / 17.7 / 19.6 / 19.6 | |
|
||||
| M5 | W2 | $0.65 | control | 781 | +146.07 | +26.42 | +119.65 | 52.9% | 1.52 | 10.1 | 49f8e602 |
|
||||
| M5 | W2 | $0.65 | B2 | 1105 | +194.94 | +54.11 | +140.83 | 53.6% | 1.50 | 15.9 | 351f9eee |
|
||||
| M5 | W2 | $0.30 | control / B2 | 781 / 1105 | +162.09 / +220.32 | +31.71 / +65.22 | +130.37 / +155.10 | | 1.60 / 1.58 | 8.9 / 13.7 | |
|
||||
| M5 | OOS | $0.65 | control | 86 | +9.47 | +0.01 | +9.46 | 53.5% | 1.33 | 8.2 | da497d74 |
|
||||
| M5 | OOS | $0.65 | B2 | 235 | +13.17 | −6.01 | +19.18 | 52.3% | 1.17 | 12.3 | 3cece33b |
|
||||
| M5 | OOS | $0.30 | control / B2 | 86 / 235 | +12.42 / +20.66 | +1.65 / −2.04 | +10.77 / +22.69 | | 1.46 / 1.28 | 6.7 / 9.5 | |
|
||||
| M5 | W3 | $0.65 | control | 870 | +153.64 | +16.73 | +136.91 | 53.3% | 1.50 | 10.1 | 408de695 |
|
||||
| M5 | W3 | $0.65 | B2 | 1333 | +223.87 | +47.49 | +176.38 | 53.4% | 1.48 | 15.9 | f89176d6 |
|
||||
| M5 | W3 | $0.30 | control / B2 | 870 / 1333 | +172.40 / +256.20 | | | | 1.57 / 1.56 | 8.9 / 13.7 | |
|
||||
|
||||
Trade diffs on W2 at $0.65 (key = entry time + direction):
|
||||
- B1: 0 added, 0 removed, 931 common. The 158 grade-A XAU_ATR setups become GENERIC (strategy
|
||||
mix 915 / 16 vs 757 / 174); same bars, same direction, a swing-anchored stop instead of 2 × ATR.
|
||||
- B3: 162 removed (−8.11R, all grade-A SELLs), 44 added (−0.11R, cooldown displacement), 769 common.
|
||||
Direction mix 635 BUY / 178 SELL vs 635 / 296.
|
||||
- B2: 392 added (+63.63R), 68 removed (+17.23R), 713 common.
|
||||
|
||||
### Verdicts against the pre-registered rule
|
||||
|
||||
| # | (a) ΔR ≥ +5 W2 | (b) halves | (c) PF | (d) OOS | (e) W3 DD ≤ +10% | (f) $0.30 sign | Verdict |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| B1 | −2.0 ✗ | −1.05 / −0.93 ✓ | 1.42 ✓ | 0 ✓ | 19.3 ✓ | −3.3 ✓ | **FAIL (a)**. Trade-identical; the GENERIC stop is not better than the 2 × ATR stop on the same grade-A signals (W3 −2.8R). Not shipped |
|
||||
| B2 | +48.9 ✓ | +27.7 / +21.2 ✓ | 1.50 ✓ (−0.02) | +3.7 ✓ (but OOS PF 1.17, OOS H1 −6.0) | 15.9 vs 10.1 = +57% ✗ | +58.2 ✓ | **FAIL (e)**. Owner's call, see the ladder below |
|
||||
| B3 | +8.0 ✓ | +4.0 / +4.0 ✓ | 1.53 ✓ | +7.8 ✓ | 20.9 vs 19.3 = +8% ✓ | +6.1 ✓ | **PASS a–f — SHIPPED** |
|
||||
| B4 | +7.3 ✓ | ✓ | 1.53 ✓ | +7.8 ✓ | +8% ✓ | +4.6 ✓ | Passes, but 0.7R below B3 alone on W2 and 1.6R on W3; B1's part adds nothing. Not shipped |
|
||||
|
||||
B3 per year on W3 ($0.65, control → B3): 2023 (Sep–Dec) +13.2 → +10.8; 2024 +52.9 → +53.3;
|
||||
2025 +77.3 → +81.2; 2026 (to Sep 5) +43.2 → +50.5. Removed grade-A SELLs by year: 2023 15 / +1.4R,
|
||||
2024 76 / −1.3R, 2025 79 / −8.6R, 2026 60 / −5.8R. The max-DD episode is the same one in both books
|
||||
(2023-12-01 → 2024-02-23), 19.3R → 20.9R. M15 SELLs remaining under B3 on W3: A+ 19 / +2.6R,
|
||||
B 217 / −8.6R. The 5-year fact on the other side of the scale stands (shorts paid in 2021–22).
|
||||
|
||||
### M5 target-floor ladder (informational, run after B2's verdict; NOT pre-registered, NOT shipped)
|
||||
|
||||
`--set signals.tp_floor_rr=<x>` on M5, $0.65:
|
||||
|
||||
| Floor | W2 trades / R / PF / DD | OOS trades / R / PF / DD | W3 trades / R / PF / DD |
|
||||
|---|---|---|---|
|
||||
| 0.5 (shipped) | 781 / +146.1 / 1.52 / 10.1 | 86 / +9.5 / 1.33 / 8.2 | 870 / +153.6 / 1.50 / 10.1 |
|
||||
| 0.6 | 868 / +159.1 / 1.51 / 10.2 | 105 / +1.5 / 1.04 / 12.5 | 979 / +164.5 / 1.46 / 17.1 |
|
||||
| 0.75 | 975 / +192.4 / 1.57 / 13.4 | 147 / +13.5 / 1.31 / 9.4 | 1133 / +211.6 / 1.54 / 13.4 |
|
||||
| 0.9 | 1053 / +209.3 / 1.58 / 13.5 | 194 / +22.8 / 1.41 / 7.5 | 1252 / +238.7 / 1.55 / 13.5 |
|
||||
| 1.0 (B2) | 1105 / +194.9 / 1.50 / 15.9 | 235 / +13.2 / 1.17 / 12.3 | 1333 / +223.9 / 1.48 / 15.9 |
|
||||
|
||||
Reading: the 2026-07-11 verdict ("a 1R floor hurts M5") was made on Twelve Data bars at $0.30 with
|
||||
the pre-relaxation gates and the pullback entries on; on Oanda bars at $0.65 with today's gates the
|
||||
floor lifts small-ATR targets past the $9 spread gate and those trades earn (B2 added trades on W3:
|
||||
554 / +90.8R, 53% winners, median stop $12 vs $20, 962 of 1,105 plans at exactly RR 1.0; positive
|
||||
in every year; by volatility regime extreme +44.6R, low +32.0R, normal +14.2R). The cost is a deeper
|
||||
drawdown in the same May–July 2026 episode (10.1R → 13.4–15.9R) and a non-monotone ladder (0.9 beats
|
||||
1.0 on every window, 0.6 is worse than 0.5 out of sample), which is why nothing from it ships now.
|
||||
If the owner wants M5 plans at RR ≥ 1 (the original request), the honest candidate is a fresh
|
||||
pre-registered run at ONE value chosen before looking (0.75 or 1.0), with the DD clause waived
|
||||
explicitly as on 2026-07-21, plus the cost ladder ($0.80, $1.00) because the added trades carry
|
||||
$12 stops. Until then M5 plans keep 0.5R guides and the alert says so.
|
||||
|
||||
## Shipped (2026-09-26)
|
||||
|
||||
1. **A1 — alert cooldown** (`telegram_bot.py`): no new alert on a timeframe within 5 bars of the
|
||||
last alerted setup's bar, `_ALERT_COOLDOWN_BARS = 5` = `backtest.cooldown_bars`. Book-neutral.
|
||||
2. **A2 — alert wording** (`telegram_bot.py`, `main.py`, `docs/user_guide.html`): the trail is
|
||||
printed first as the tested exit; TP1/TP2 are guides with their R; "Planned TP1 RR"; one
|
||||
validated-book line per setup class from `agent_panel_base_rates.json`. Book-neutral.
|
||||
3. **B3 — M15 grade-A SELL block** (`pair_signals/xauusd.py`, M15 override 6). Re-pins the M15
|
||||
books (below).
|
||||
4. **XAU_ATR target floor** (`signal_aggregator._generate_xau_atr_trade_setup`, mirrored in
|
||||
`cuda_signal_kernels.py` K2): the gold ATR generator now honours `tp_floor_rr` like the GENERIC
|
||||
one, compared as multipliers so the non-binding case is bit-identical. Binds only on M15 (floor
|
||||
1R): grade-A plans print TP1 at 2 × ATR = 1R instead of 1.5 × ATR = 0.75R. TP1 is advisory in the
|
||||
backtester, so the only book effect is through the $9 spread gate (parity MD5s below).
|
||||
|
||||
## Addendum (same day): printed-target floor extended to every generator
|
||||
|
||||
Owner rule: "make sure TP1 is at least 1:1". Shipped as `signals.tp_print_floor_rr = 1.0`
|
||||
(effective floor `max(tp_floor_rr, tp_print_floor_rr)`), applied after the min-RR and spread gates in
|
||||
the gold ATR, GENERIC and chart-pattern generators (Python and CUDA K2). Label-only by construction
|
||||
and by measurement (twin books vs the previous pins, same code otherwise):
|
||||
|
||||
| Book | Rows | R before → after | Columns that differ | Rows with a new `tp1` | min `rr_planned` before → after |
|
||||
|---|---|---|---|---|---|
|
||||
| M5 W2 | 781 | +146.07 → +146.07 | `tp1`, `rr_planned` | 544 (GENERIC 408, XAU_CP 105, XAU_ATR 31) | 0.5 → 1.0 |
|
||||
| M15 W2 | 813 | +154.15 → +154.15 | none | 0 | 1.0 → 1.0 |
|
||||
| H1 W3 | 253 | +70.02 → +70.02 | `tp1`, `rr_planned` | 252 | 0.5 → 1.0 |
|
||||
| H4 W3 | 114 | +21.68 → +21.68 | `tp1`, `rr_planned` | 108 | 0.5 → 1.0 |
|
||||
|
||||
New pins: M5 W2 `e599c8a8652f95b7fcdf5a7ef2b564f5`, H1 W3 `a44fd19b4067f46e5bd98d50cc3eff92`,
|
||||
H4 W3 `f3587bae3c5dc3eda468ea3588b17dcb`; M15 W2 `5b65ecf6…` and OOS `09b8a8e7…` unchanged. Reference
|
||||
script MD5s: see the parity lines appended below. The gated floor (`tp_floor_rr`, 0.5 on M5 / 1.0 on
|
||||
M15) is untouched: raising it is the B2 question above, a book change, and remains the owner's call.
|
||||
|
||||
## Addendum 2 (same day): consistency checks on `signals.db` — two delivery bugs fixed
|
||||
|
||||
Read-only checks over the 8,453 rows (2026-09-04 → 09-25): scan latency (median 10–15 s after the
|
||||
candle close on every timeframe; 2–3% of rows read the previous bar, all in 18–22 UTC around the
|
||||
Labor Day gap and the settlement pause), `actionable` / setup consistency (clean), gate reasons for
|
||||
actionable grades without a setup (all `SPREAD GATE`, small-ATR M5 bars), outcome states (97 resolved,
|
||||
36 duplicates, 1 unresolvable for a candle gap), scan-outcome labelling (7,054 of 7,634 complete),
|
||||
8 `failed` scans (Oanda "primary timeframe data unavailable", notably 06:11–06:17 UTC on both Sep 15
|
||||
and Sep 16), dashboard-vs-bot disagreement on the same bar (4 bars of 3,300).
|
||||
|
||||
Two bugs, both fixed and tested, books untouched:
|
||||
|
||||
1. **Panel probability never stored under `verdict_source: "macro"`** (the live setting): all 33 v2
|
||||
rows had `panel_probability` NULL although `panel_probs_json` carried the macro persona's `p_win`.
|
||||
Cause: the macro-source `PanelVerdict` carried no `probability`, `_write_fields` wrote 0.0 and the
|
||||
logger's `probability if probability else None` dropped it. Fix: the macro verdict's probability is
|
||||
the macro take's `p_win` (`agent_panel.run_panel`). Without it the planned re-evaluation at 100 /
|
||||
300 outcomes would have had no live probabilities to score.
|
||||
2. **Breach rescans re-log the last pre-pause M15 bar.** Every evening the 20:45 UTC bar was logged
|
||||
again at 22:01, 22:06 and 22:11 (Sep 17–24) by the tick-driven level-breach rescan: the first
|
||||
post-pause bar closes at 22:15, so the frame still ends at 20:45. The candle loop already skips
|
||||
that window (`_closed_bar_in_settlement`); `_handle_breach` now applies the same guard. Cosmetic
|
||||
for alerts (dedup + cooldown), but it polluted the log's per-bar dedup and the parity reports.
|
||||
|
||||
Not bugs, recorded: 380 `blocked` weekend rows on Sep 20 carry no `bar_ts` by design; the 43× repeat of
|
||||
the Sep 7 18:25 M5 bar is the known holiday gap (no holiday calendar); the H4 loop produced no setup
|
||||
in three weeks (its two tradeable close hours).
|
||||
Reference in New Issue
Block a user