sql-integration
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2cc5915b00 |
Daily-trend veto on M15 + lot-size / already-open lines (campaign 2026-09-30), and the 2026-10-01 whole-codebase bug review
Two bodies of work that touch the same files, so one commit. Direction and setup-size campaign (2026-09-30 → 10-01, owner's RISK-FIRST rule, written before the runs): - SIGNAL: M15 daily-trend veto (factory override 7, xau_strategy.daily_trend_veto "both"). A finished setup against a clear daily trend (closed D1 bars, Sunday stubs dropped; close vs its 50-bar EMA AND vs the close 60 kept days earlier) is removed as the last step of the core. One definition (signal_aggregator.daily_trend_series) for live, the bar replay and the twin's host side; D1 is a required frame of the M15 scan; mirrored in cuda_signal_kernels.py K2. W3 824 / +205.12R / PF 1.73 / max DD 12.4R vs 1105 / +195.79R / 1.49 / 20.9R; 2019 → 2026 1473 / +223.33R / 1.42 / 41.5R vs 2091 / +192.53R / 1.24 / 64.8R; unseen holdout −4.62R vs −6.63R. - FAILED, not shipped: the veto on M5 (W3 drawdown 10.1R → 15.2R) and on H1 (−9.7R), buy-only and sell-only on M15, the wide-stop cap (xau_strategy.wide_stop_cap_atr stays 0.0, a research knob). - New M15 pins: W2 dfc647ee…, OOS e87b6de1…, W3 ad3b0dbd…. M5 / H1 / H4 pins unchanged. --set xau_strategy.daily_trend_veto=off reproduces the previous M15 pins byte for byte. Twin and reference script MD5-equal on every window; recorder and feature-export parity re-proved; base-rate table rebuilt. - DISPLAY (books byte-identical): exposure.py — the lot size for trade_size.risk_per_trade (new config.json block, off by default, private to the owner) and the count of alerted same-direction plans still open (signals.alerted_at, read-only open_alerted_setups); the daily trend in words on Telegram, the CLI and the dashboard; signals.daily_trend logged. - providers/oanda: a date window with no bars is an empty page, not an error (a D1 pad starting on a Saturday could not be fetched). - Reports only, nothing shipped: seven context screens (no state nominated) and eight outside data sources (none adds a forecast to gold's own daily trend). - Independent verification (report section 5.13): every figure reproduced on both scripts, scoring identical to the previous commit on all four timeframes, no look-ahead. Caveats recorded there: this is a risk filter, not a more accurate direction call; nine tenths of the 2019 → 2026 gain is 2022; the neighbour clause is thin (a simple 100-day average misses the R line by 1.2R); "never adds a trade" is an observation, not a guarantee. Bug review of the whole codebase (2026-10-01). Signal path clean. 17 defects elsewhere, plus 4 found by an independent review of the fixes; all fixed, each with a test; no trade book changed: - telegram_bot: one _deliver_alert for the candle scan and the level break. The row is stamped alerted_at BEFORE the send, on a one-worker alert-db pool, and un-stamped when no chat received it or the task is cancelled mid-stamp, so two alerts at one candle close count each other and no SQLite write runs on the event loop. The 5-bar cooldown counts bars of the scan's own frame (== gpu_backtest._apply_cooldown; clock time released the 22:00 UTC bar after the settlement pause) and is restored from signals.db at start-up. The lot-size line goes to the owner's own chat only (a group the owner ran /autoscan in used to receive it). /status gains a news-model line and escapes failure details. - signal_log: mark_alerted returns the stamp it wrote; unmark_alerted; last_alerted_bars (read-only). - advisory_outcome: candle continuity is judged bar by bar inside the walk; a hole after a trade's exit no longer voids it. The stored-candle replay resolves 108 of 111 logged setups instead of 99, all equal to the live resolver's R. - providers/oanda: an empty date window is accepted only when it is 5 days or shorter and no malformed candle was skipped; a longer one is retried, then refused, instead of being cached as history with a hole. - news_engine: blank RSS titles skipped; one unreadable headline no longer costs the local model its whole batch (checked on the real bundle); model health recorded; no DeepSeek audit call in a process that does not archive; event-direction cache keyed per release; peek_week_events never fetches. - llm_client: chat_json never raises and never logs the API key (requests quotes the Authorization value in InvalidHeader; error bodies are redacted). - signal_aggregator: the scan's news block is guarded; the replay path (use_news=False) assigns exactly the values it did before. - agent_panel: PANEL_PROMPT_VERSION v2.2 — same prompt text, corrected input (event timing over the cached calendar week, headlines newest first). scripts/panel_replay.py replays the logged payload per persona; scripts/label_scan_outcomes.py counts with an ISO cutoff. - test_all.py: no ALTER TABLE … DROP COLUMN (the live box has SQLite 3.34); tests that write Parquet print a notice and return without pyarrow. Verification after the last code change: suite 632 passed, 0 failed (also with both Parquet engines blocked and FB2_BACKEND=cpu, the live box's view); the five reference MD5s reproduce on both backtesters, 0 cache fallbacks; Module 37 green; static Python 3.9 / SQLite 3.34 check clean on every touched file. Evidence: backtest/research/daily_trend_campaign_2026-09-30.md, context_screens_2026-10.md, new_inputs_2026-10.md; CLAUDE.md sections 2, 4, 6, 8, 9, 10 and 11; pair_signals/xauusd.py docstring (M15 override 7). |
||
|
|
7936c2fe6e |
audit 2026-09-13: eleven delivery/measurement fixes; books byte-identical
Signal path
- A shipped setup always names a direction: grade C needs confidence
>= 45 (gate 6c grants B at >= 45) while BUY/SELL needs >= 50, so C/B
reports reached the alert gate as NEUTRAL with a full setup. The
backtester traded them (the replay never reads final_signal) while
Telegram rejected NEUTRAL first — 5/91 trades, +3.7R of +22.2R, in
the H1 12-month book. _attach_core_setup stamps the setup's direction
and repairs the pre-gate action text; no-setup reports stay NEUTRAL.
- analyze_pair fails the scan when ANY required frame is missing
("DATA UNAVAILABLE:", scan_kind='failed'); a lost confirmation frame
used to run with an unearned -15 MTF penalty.
Outcomes and labels
- advisory_outcome.resolve_locked_floor: a gap the database cannot fill
resolves at the locked trailing stop (GAP_LOCKED, result.floor) when
the stop had crossed the entry; the rebuild fires on both gap reasons
(rows 915/920: +1.14R / +0.84R floors; resolved sum -2.46R -> -0.48R).
- label_scan_outcomes: SCAN_LABEL_MAX_AGE_H = 72 h, stale rows never
anchor the shared bar stream, labeled_at bumped on re-examination,
_stream_since warns at its row cap (the Labor-Day pile would have
blinded scan_outcomes around 2026-09-14).
Providers and scheduling
- The daily settlement pause (Mon-Thu 17:00 New York) is a scheduled
closure for _has_latest_closed_bar (whole-span label walk-back) and
_is_data_fresh (closed seconds are not age); bot and dashboard skip
scans whose just-closed label sits in the pause (61 false stale rows
in three nights).
- fetch_range retries a failed page in place (three failures on one
window raise) and logs an ERROR for interior gaps of >3 unscheduled
bars.
- pd.Timestamp.utcnow() replaced in providers/base.py and engine.py;
requirements pin pandas<4, numpy<3.
- telegram_bot: boundary loops share one scan per bar with the breach
handler (_remember_scan keys on the analysed bar; breach defers 20 s
after an M15 boundary); the rotating file handler attaches in main()
only (the test suite used to write into logs/telegram.log).
- dashboard_publisher: catch_up_timeframes re-scans boundaries missed
while a cycle overran or the process was down.
Backtester and engine guards
- _replay_span returns the per-span GPU cache-miss count; run_backtest
sums, logs it (must be 0) and writes cache_fallbacks to the manifest.
- regime_weight_table raises on a partial regime_weights_override.
Verification: 519 tests; M5/M15 2-yr, M15 OOS, H1/H4 3-yr books
byte-identical before and after; live signals.db untouched (checked on
a copy). CLAUDE.md carries the dated entry and the blocked-hours union
semantics that every book was validated under.
Claude-Session: https://claude.ai/code/session_0139xz1J6YccndA2be6yFnEo
|
||
|
|
bed8b549ac |
audit: fix 9 defects from the 2026-09-04 hidden-bug sweep
None of these touch the signal path. The 12-month M15 book (2024-03-31..2025-03-30, $0.65, cached Oanda bars) is byte-identical before and after (md5 c59d3e7e685c3c880b614640adfea74b, 375 trades), and signal/indicator logging to logs/signals.db is unchanged — verified end to end: per-TF indicators for all 3 TFs, 14 candles each, MTF and reasons all still persisted. agent_panel: the >=2-take quorum guards the CHAIR's synthesizer spend, but ran BEFORE the macro branch — so the shipped default (verdict_source="macro", which never calls the synthesizer) silently dropped a good macro verdict whenever the other three personas were slow. Quorum moved below the macro path. analyze_signal_log: _forward_return clamped to the last available bar when the horizon had not elapsed, reporting a near-zero "forward move" for every row logged within `hours` of the end of the data — on every run, blended into the hit-rate/mean-move aggregate. Now returns None. analyze_signal_log: persona_scorecard iterated raw rows through an index that maps EVERY member row of an event, so a re-logged scan appended the same event R twice. One vote per event, matching news_outcome_buckets. Measured inflation on the live DB: 1.03x. signal_log: dedup required a non-negative logged_at gap, but logged_at is stamped before the row wins the write lock (busy_timeout 5 s), so two processes logging one setup at a boundary can invert timestamp order against id order and seed a second event. Now abs(gap); same in backfill_seeds. Zero occurrences in the current 366 setup rows. advisory_outcome: the catch-all turned real evaluator faults into the same UNRESOLVABLE used for legitimate weekend gaps, with nothing in the log. Still fail-safe, now logged. llm_client: only the "cap" warn-once flag was ever reset, so in a daemon the second and every later network/http/parse outage logged at DEBUG only. Cleared on UTC-day roll and re-armed by a recovered call. dashboard.js: renderCards() redraws the selector for every timeframe but was gated on the SELECTED one changing, leaving the other buttons stale (up to 4 h on H4) while renderHero's rail already showed the new value. news_summary.php: atomic_write treated a short write (disk full) as success, publishing a truncated body the cache-first branch then served forever; and the daily cap was charged before the no-key check and before the stampede lock re-checked the cache, so a click burst could exhaust the 150/day budget with zero LLM calls. migrate_signal_log_news: logged_at is ISO with 'T' while SQLite's datetime() uses a space, so string-comparing them put the whole cutoff calendar day on the wrong side and it never pruned. NOTE: this now deletes one day more than before — it only runs when invoked by hand. Tests 414 -> 422 (new Module 28, one per fix). The existing "<2 valid takes" test was retargeted at the chair — the case it actually protects — rather than deleted, with new coverage for macro-alone. Not fixed, recorded for the owner: - Gate 6c's boost condition is textually identical to the C-grade definition, so every C in a "transitioning" regime is promoted to B before gate 6e reads it, making sell_min_quality's exclusion of "C" a no-op on M15/H4 (H1 and M5 are immune by other means). The backtester runs the same code, so every pinned book already includes this — changing it is a rule-1 revalidation event. - Safe-haven/geopolitical headlines score exactly 0.00 in the keyword fallback and are now dropped entirely by the ±0.1 neutral filter, contradicting the platform's own gold doctrine. Bites only when DeepSeek is capped/disabled/failing. Claude-Session: https://claude.ai/code/session_01Ah5EB1goi5inQmSd37Ne6a |
||
|
|
83ffd290ca | agent_panel: harden evidence and track outcomes |