Final review fixes: scrub tracked credentials, trim scannable TFs, absolute parity pin, scanning type-checks
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VPNF37MP9Kuv62B1VBrZ1b
This commit is contained in:
@@ -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/__init__.py` | 52 | `PAIR_PRODUCERS` registry + `produce_signal()` |
|
||||||
| `pair_signals/xauusd.py` | 71 | XAU config factory + per-timeframe overrides (docstring = validation record) |
|
| `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` |
|
| `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/gpu_backtest.py` | ~2231 | Bar-replay backtester (CuPy→NumPy fallback) using the real `SignalAggregator` |
|
||||||
| `backtest/diagnose_filters.py` | 609 | Filter-diagnosis tooling |
|
| `backtest/diagnose_filters.py` | 609 | Filter-diagnosis tooling |
|
||||||
| `backtest/research/` | — | Calendar research infra (`ff_calendar_scraper.py`, `calendar_store.py`) + result CSVs |
|
| `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
|
## 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.**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -37,8 +37,9 @@ CONFIG_PATH = os.path.join(
|
|||||||
|
|
||||||
_VALID_QUALITIES = ("A+", "A", "B", "C")
|
_VALID_QUALITIES = ("A+", "A", "B", "C")
|
||||||
_VALID_LOG_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
|
_VALID_LOG_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
|
||||||
# Timeframes the bot's candle-boundary scheduler can run (W1 has no loop).
|
# Timeframes the bot's candle-boundary scheduler supports (must have
|
||||||
_SCANNABLE_TFS = ("M5", "M15", "M30", "H1", "H4", "D1")
|
# AUTOSCAN_CONFIRMATIONS entries in telegram_bot.py).
|
||||||
|
_SCANNABLE_TFS = ("M5", "M15", "H1", "H4")
|
||||||
|
|
||||||
_KNOWN_TOP = {"telegram", "data_providers", "scanning", "pairs", "log_level"}
|
_KNOWN_TOP = {"telegram", "data_providers", "scanning", "pairs", "log_level"}
|
||||||
_KNOWN_TELEGRAM = {"token", "chat_id", "channel_id", "alert_min_quality"}
|
_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"')
|
_check_keys(scanning, _KNOWN_SCANNING, '"scanning"')
|
||||||
if "pairs" in scanning:
|
if "pairs" in scanning:
|
||||||
pairs = scanning["pairs"]
|
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]
|
bad = [p for p in pairs if p not in PAIR_INFO]
|
||||||
if bad:
|
if bad:
|
||||||
raise AppConfigError(
|
raise AppConfigError(
|
||||||
@@ -135,6 +138,9 @@ def load_app_config(path: str = CONFIG_PATH) -> AppConfig:
|
|||||||
app.scan_pairs = list(pairs)
|
app.scan_pairs = list(pairs)
|
||||||
if "timeframes" in scanning:
|
if "timeframes" in scanning:
|
||||||
names = scanning["timeframes"]
|
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]
|
bad = [n for n in names if n not in _SCANNABLE_TFS]
|
||||||
if bad:
|
if bad:
|
||||||
raise AppConfigError(
|
raise AppConfigError(
|
||||||
|
|||||||
@@ -644,7 +644,7 @@ Expected: `✗ config.py: hardcoded twelve_data_api_key field is gone` (plus the
|
|||||||
|
|
||||||
- [ ] **Step 3: Implement**
|
- [ ] **Step 3: Implement**
|
||||||
|
|
||||||
`config.py`: delete line 346 (`twelve_data_api_key: str = "f563b22ffdfd4924955decbe1a8c8701"`).
|
`config.py`: delete line 346 (`twelve_data_api_key: str = "<TWELVE_DATA_API_KEY>"`).
|
||||||
|
|
||||||
`data_provider.py` — add to the local imports:
|
`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'
|
cat > /home/harwinder/Development/ForexBot2/config.json <<'EOF'
|
||||||
{
|
{
|
||||||
"telegram": {
|
"telegram": {
|
||||||
"token": "8228251480:AAFW2OAyQ2idMicql2t_CIXk1r9-qfRTfNE",
|
"token": "<TELEGRAM_BOT_TOKEN>",
|
||||||
"chat_id": "6025899022",
|
"chat_id": "<OWNER_CHAT_ID>",
|
||||||
"channel_id": "-1003942265371",
|
"channel_id": "<CHANNEL_ID>",
|
||||||
"alert_min_quality": "C"
|
"alert_min_quality": "C"
|
||||||
},
|
},
|
||||||
"data_providers": {
|
"data_providers": {
|
||||||
"active": "twelve_data",
|
"active": "twelve_data",
|
||||||
"twelve_data": { "api_key": "f563b22ffdfd4924955decbe1a8c8701" }
|
"twelve_data": { "api_key": "<TWELVE_DATA_API_KEY>" }
|
||||||
},
|
},
|
||||||
"scanning": {
|
"scanning": {
|
||||||
"pairs": ["XAU/USD"],
|
"pairs": ["XAU/USD"],
|
||||||
@@ -932,6 +932,8 @@ EOF
|
|||||||
git status --short # config.json must NOT appear (gitignored)
|
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)**
|
- [ ] **Step 2: Boot smoke test (no network needed)**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
+13
@@ -2630,6 +2630,8 @@ def _():
|
|||||||
{"scanning": {"pairs": ["EUR/USD"]}}, # unknown pair
|
{"scanning": {"pairs": ["EUR/USD"]}}, # unknown pair
|
||||||
{"scanning": {"timeframes": ["M7"]}}, # bad TF name
|
{"scanning": {"timeframes": ["M7"]}}, # bad TF name
|
||||||
{"scanning": {"timeframes": ["W1"]}}, # unscannable TF
|
{"scanning": {"timeframes": ["W1"]}}, # unscannable TF
|
||||||
|
{"scanning": {"pairs": 5}}, # pairs not a list
|
||||||
|
{"scanning": {"timeframes": 5}}, # timeframes not a list
|
||||||
{"data_providers": {"active": "polygon",
|
{"data_providers": {"active": "polygon",
|
||||||
"twelve_data": {"api_key": "k"}}}, # no block
|
"twelve_data": {"api_key": "k"}}}, # no block
|
||||||
{"telegram": {"alert_min_quality": "Z"}}, # bad quality
|
{"telegram": {"alert_min_quality": "Z"}}, # bad quality
|
||||||
@@ -2803,6 +2805,17 @@ def _():
|
|||||||
assert "credentials" in str(e)
|
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
|
# SUMMARY
|
||||||
# ══════════════════════════════════════════════════════════════════════
|
# ══════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
+3
-1
@@ -10,7 +10,9 @@ import time
|
|||||||
|
|
||||||
from twelvedata import TDClient
|
from twelvedata import TDClient
|
||||||
|
|
||||||
API_KEY = "f563b22ffdfd4924955decbe1a8c8701"
|
from app_config import provider_api_key
|
||||||
|
|
||||||
|
API_KEY = provider_api_key()
|
||||||
SYMBOL = "XAU/USD"
|
SYMBOL = "XAU/USD"
|
||||||
DURATION = 30
|
DURATION = 30
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user