From f24009a7843438efe1725c3dce452a9955647f4a Mon Sep 17 00:00:00 2001 From: Harwinder Sekhon Date: Tue, 21 Jul 2026 13:42:00 -0400 Subject: [PATCH] Final review fixes: scrub tracked credentials, trim scannable TFs, absolute parity pin, scanning type-checks Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VPNF37MP9Kuv62B1VBrZ1b --- CLAUDE.md | 4 ++-- app_config.py | 10 ++++++++-- .../plans/2026-07-21-centralized-config.md | 12 +++++++----- test_all.py | 13 +++++++++++++ test_ws_dual.py | 4 +++- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7e70c34..9ae17a0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,7 +24,7 @@ This file is the authoritative developer/agent reference. The human-facing docs | `pair_signals/__init__.py` | 52 | `PAIR_PRODUCERS` registry + `produce_signal()` | | `pair_signals/xauusd.py` | 71 | XAU config factory + per-timeframe overrides (docstring = validation record) | | `main.py` | ~348 | CLI: `python main.py --pair XAUUSD --timeframe H1 --detail/--json` | -| `test_all.py` | ~2100 | QA suite: **199 tests in 13 modules**, no live API needed. Must stay green. | +| `test_all.py` | ~2100 | QA suite: **200 tests in 13 modules**, no live API needed. Must stay green. | | `backtest/gpu_backtest.py` | ~2231 | Bar-replay backtester (CuPy→NumPy fallback) using the real `SignalAggregator` | | `backtest/diagnose_filters.py` | 609 | Filter-diagnosis tooling | | `backtest/research/` | — | Calendar research infra (`ff_calendar_scraper.py`, `calendar_store.py`) + result CSVs | @@ -320,7 +320,7 @@ Data: paginated TD fetches (5000-bar chunks) cached as parquet in `backtest/cach ## test_all.py — QA suite -`python test_all.py` — **199 tests, 13 modules** (config, data_provider, engine, news_engine, signal_aggregator, main, telegram_bot, integration, pair_signals registry + engine-bug regressions, 2026-07-16 audit-fix regressions, chart patterns, app_config). No network needed (mocked). Uses a custom `@test("name")` decorator that runs immediately (NOT pytest — don't grep for `def test_`). Exit code non-zero on failure. Includes: the xauusd-factory ≡ inline-config equivalence test, engine bug-fix regression tests (Module 9b), a live-vs-GPU engine parity sweep incl. the EMA-ribbon path (Module 11), and config.json loading/precedence/validation coverage (Module 13). **Any signal-logic change must keep this green and should add tests.** +`python test_all.py` — **200 tests, 13 modules** (config, data_provider, engine, news_engine, signal_aggregator, main, telegram_bot, integration, pair_signals registry + engine-bug regressions, 2026-07-16 audit-fix regressions, chart patterns, app_config). No network needed (mocked). Uses a custom `@test("name")` decorator that runs immediately (NOT pytest — don't grep for `def test_`). Exit code non-zero on failure. Includes: the xauusd-factory ≡ inline-config equivalence test, engine bug-fix regression tests (Module 9b), a live-vs-GPU engine parity sweep incl. the EMA-ribbon path (Module 11), and config.json loading/precedence/validation coverage (Module 13). **Any signal-logic change must keep this green and should add tests.** --- diff --git a/app_config.py b/app_config.py index 6adb3e7..1cb9e19 100644 --- a/app_config.py +++ b/app_config.py @@ -37,8 +37,9 @@ CONFIG_PATH = os.path.join( _VALID_QUALITIES = ("A+", "A", "B", "C") _VALID_LOG_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL") -# Timeframes the bot's candle-boundary scheduler can run (W1 has no loop). -_SCANNABLE_TFS = ("M5", "M15", "M30", "H1", "H4", "D1") +# Timeframes the bot's candle-boundary scheduler supports (must have +# AUTOSCAN_CONFIRMATIONS entries in telegram_bot.py). +_SCANNABLE_TFS = ("M5", "M15", "H1", "H4") _KNOWN_TOP = {"telegram", "data_providers", "scanning", "pairs", "log_level"} _KNOWN_TELEGRAM = {"token", "chat_id", "channel_id", "alert_min_quality"} @@ -127,6 +128,8 @@ def load_app_config(path: str = CONFIG_PATH) -> AppConfig: _check_keys(scanning, _KNOWN_SCANNING, '"scanning"') if "pairs" in scanning: pairs = scanning["pairs"] + if not isinstance(pairs, list): + raise AppConfigError('config.json: scanning.pairs must be a list') bad = [p for p in pairs if p not in PAIR_INFO] if bad: raise AppConfigError( @@ -135,6 +138,9 @@ def load_app_config(path: str = CONFIG_PATH) -> AppConfig: app.scan_pairs = list(pairs) if "timeframes" in scanning: names = scanning["timeframes"] + if not isinstance(names, list): + raise AppConfigError( + 'config.json: scanning.timeframes must be a list') bad = [n for n in names if n not in _SCANNABLE_TFS] if bad: raise AppConfigError( diff --git a/docs/superpowers/plans/2026-07-21-centralized-config.md b/docs/superpowers/plans/2026-07-21-centralized-config.md index 5d82bf5..972ae45 100644 --- a/docs/superpowers/plans/2026-07-21-centralized-config.md +++ b/docs/superpowers/plans/2026-07-21-centralized-config.md @@ -644,7 +644,7 @@ Expected: `✗ config.py: hardcoded twelve_data_api_key field is gone` (plus the - [ ] **Step 3: Implement** -`config.py`: delete line 346 (`twelve_data_api_key: str = "f563b22ffdfd4924955decbe1a8c8701"`). +`config.py`: delete line 346 (`twelve_data_api_key: str = ""`). `data_provider.py` — add to the local imports: @@ -910,14 +910,14 @@ These are the exact values being deleted from source (from pre-change `telegram_ cat > /home/harwinder/Development/ForexBot2/config.json <<'EOF' { "telegram": { - "token": "8228251480:AAFW2OAyQ2idMicql2t_CIXk1r9-qfRTfNE", - "chat_id": "6025899022", - "channel_id": "-1003942265371", + "token": "", + "chat_id": "", + "channel_id": "", "alert_min_quality": "C" }, "data_providers": { "active": "twelve_data", - "twelve_data": { "api_key": "f563b22ffdfd4924955decbe1a8c8701" } + "twelve_data": { "api_key": "" } }, "scanning": { "pairs": ["XAU/USD"], @@ -932,6 +932,8 @@ EOF git status --short # config.json must NOT appear (gitignored) ``` +(Real values were scrubbed from this doc 2026-07-21 — take them from the previously deployed telegram_bot.py/config.py in git history, and rotate both credentials after merge.) + - [ ] **Step 2: Boot smoke test (no network needed)** ```bash diff --git a/test_all.py b/test_all.py index 331ce7d..841c791 100644 --- a/test_all.py +++ b/test_all.py @@ -2630,6 +2630,8 @@ def _(): {"scanning": {"pairs": ["EUR/USD"]}}, # unknown pair {"scanning": {"timeframes": ["M7"]}}, # bad TF name {"scanning": {"timeframes": ["W1"]}}, # unscannable TF + {"scanning": {"pairs": 5}}, # pairs not a list + {"scanning": {"timeframes": 5}}, # timeframes not a list {"data_providers": {"active": "polygon", "twelve_data": {"api_key": "k"}}}, # no block {"telegram": {"alert_min_quality": "Z"}}, # bad quality @@ -2803,6 +2805,17 @@ def _(): assert "credentials" in str(e) +@test("app_config: absolute parity — default overlay leaves tuned values untouched") +def _(): + from pair_signals import PAIR_PRODUCERS + cfg = PAIR_PRODUCERS[XAUUSD](Timeframe.H1, None) + assert cfg.xau_strategy.blocked_hours_utc == [0, 1, 2, 10, 11, 13, 16, 21] + assert cfg.backtest.blocked_hours_utc == [0, 1, 2, 10, 11, 13, 16, 21] + assert cfg.scan_pairs == [XAUUSD] + assert cfg.scan_timeframes == [Timeframe.M5, Timeframe.M15, + Timeframe.H1, Timeframe.H4] + + # ══════════════════════════════════════════════════════════════════════ # SUMMARY # ══════════════════════════════════════════════════════════════════════ diff --git a/test_ws_dual.py b/test_ws_dual.py index 09458e9..0e97674 100644 --- a/test_ws_dual.py +++ b/test_ws_dual.py @@ -10,7 +10,9 @@ import time from twelvedata import TDClient -API_KEY = "f563b22ffdfd4924955decbe1a8c8701" +from app_config import provider_api_key + +API_KEY = provider_api_key() SYMBOL = "XAU/USD" DURATION = 30