chore: remove dead functions and write-only state (no behaviour change)
Zero callers anywhere (tests included), verified by grep/AST: - dashboard_publisher.DashboardPublisher.publish_all (run_cycle inlines both publishes) - signal_log.SignalLogger.advance_with_bars (the backfill script uses backfill_seeds / resolve_from_store / reseed_unresolvable) and the never-read SignalLogger.path property plus its write-only self._path - llm_client.reset_llm_client (tests reset the singleton by assignment) - market_outlook._last_run_ts, residue of the old "now - last < 3600" dedup; _last_run_hour (the UTC hour bucket) is the live key - news_engine FundamentalContext.event_impact_on_pairs: written once, read by nothing; its only feeder NewsEngine._get_affected_pairs and that helper's unit test go with it - position_sizing.LIVE_DEPTH_BARS (referenced only by its own docstring, which now states the depths inline), the unused Iterable import and the unused atr_ok parameter of _fill_geometry - analyze_signal_log: unused `field` import and the unused `rows` parameter of persona_scorecard / news_outcome_buckets (both read the DB directly) The two backfill/label scripts' --db help text now names the config.json setting it actually falls back to. Suite: 463 passed, 0 failed (one test removed with its subject). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0156YhogLytzj9P2iH1skGbL
This commit is contained in:
@@ -67,7 +67,7 @@ import math
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Dict, List, Optional, Sequence, Tuple
|
||||
|
||||
@@ -754,8 +754,7 @@ def _fmt_r(r: Optional[float]) -> str:
|
||||
return " n/a " if r is None else f"{r:+.2f}R"
|
||||
|
||||
|
||||
def persona_scorecard(rows: Sequence[LogRow], events: Sequence[SignalEvent],
|
||||
db_path: str) -> List[dict]:
|
||||
def persona_scorecard(events: Sequence[SignalEvent], db_path: str) -> List[dict]:
|
||||
"""Per persona × stance: n, mean R and win rate of the underlying
|
||||
event (needs panel_takes_json — read straight from the DB)."""
|
||||
by_row = _row_index(events)
|
||||
@@ -921,8 +920,7 @@ def news_extremes(rows: Sequence[LogRow], streams: Dict[str, List[dict]],
|
||||
}
|
||||
|
||||
|
||||
def news_outcome_buckets(rows: Sequence[LogRow],
|
||||
events: Sequence[SignalEvent],
|
||||
def news_outcome_buckets(events: Sequence[SignalEvent],
|
||||
db_path: str) -> List[dict]:
|
||||
"""Resolved events bucketed by (direction, news bucket, macro stance).
|
||||
|
||||
@@ -1306,13 +1304,13 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||
print_report(rows, events, panel, hit)
|
||||
print_stored_books(events)
|
||||
print("\n── Persona scorecard (stance × event outcome) " + "─" * 22)
|
||||
for p in persona_scorecard(rows, events, args.db):
|
||||
for p in persona_scorecard(events, args.db):
|
||||
print(f" {p['persona']:11} {p['stance']:8} n={p['n']:3d} "
|
||||
f"mean {p['mean_r']:+.3f}R win {100 * p['win_rate']:.0f}%")
|
||||
if args.news:
|
||||
print_news(news_lead_lag(rows, streams),
|
||||
news_extremes(rows, streams),
|
||||
news_outcome_buckets(rows, events, args.db))
|
||||
news_outcome_buckets(events, args.db))
|
||||
print_parity(parity_report(rows))
|
||||
if args.csv:
|
||||
export_csv(events, args.csv)
|
||||
|
||||
@@ -432,10 +432,6 @@ class DashboardPublisher:
|
||||
if self.news_payload is not None:
|
||||
publish(self.news_payload, os.path.join(self.out_dir, NEWS_FILE))
|
||||
|
||||
def publish_all(self) -> None:
|
||||
self.publish_snapshot()
|
||||
self.publish_news()
|
||||
|
||||
def run_cycle(self, tfs: List[Timeframe]) -> None:
|
||||
for tf in tfs: # sequential — see module docstring (cache dedup)
|
||||
self.scan_tf(tf)
|
||||
|
||||
@@ -198,10 +198,3 @@ def get_llm_client() -> DeepSeekClient:
|
||||
enabled=settings.get("enabled", True) is not False,
|
||||
)
|
||||
return _client
|
||||
|
||||
|
||||
def reset_llm_client() -> None:
|
||||
"""Drop the singleton (tests / config reloads)."""
|
||||
global _client
|
||||
with _client_lock:
|
||||
_client = None
|
||||
|
||||
+2
-5
@@ -329,7 +329,6 @@ def _collect_payload(settings: Dict[str, Any]) -> Dict[str, Any]:
|
||||
# ── Orchestration, hourly dedup, and request budget ──────────────────
|
||||
|
||||
_state_lock = threading.Lock()
|
||||
_last_run_ts: Optional[float] = None
|
||||
_last_run_hour: Optional[int] = None # UTC hour bucket of the last attempt
|
||||
_outlook_day: Optional[_dt.date] = None
|
||||
_outlook_count = 0
|
||||
@@ -344,10 +343,9 @@ def _skip(reason: str) -> None:
|
||||
|
||||
def reset_outlook_state() -> None:
|
||||
"""Drop hourly and daily state (tests / deployment config reloads)."""
|
||||
global _last_run_ts, _last_run_hour, _outlook_day, _outlook_count
|
||||
global _last_run_hour, _outlook_day, _outlook_count
|
||||
global _warned_budget
|
||||
with _state_lock:
|
||||
_last_run_ts = None
|
||||
_last_run_hour = None
|
||||
_outlook_day = None
|
||||
_outlook_count = 0
|
||||
@@ -360,7 +358,7 @@ def _begin_attempt(cap: int, force: bool) -> bool:
|
||||
Hourly dedup is by UTC hour BUCKET, not ``now - last < 3600``: the
|
||||
H1 loop calls this at boundary+10 s+scan time, so consecutive hours
|
||||
were routinely 3,590 s apart and every other hour was dropped."""
|
||||
global _last_run_ts, _last_run_hour, _outlook_day, _outlook_count
|
||||
global _last_run_hour, _outlook_day, _outlook_count
|
||||
global _warned_budget
|
||||
now = time.time()
|
||||
hour_bucket = int(now // 3600)
|
||||
@@ -383,7 +381,6 @@ def _begin_attempt(cap: int, force: bool) -> bool:
|
||||
_skip("daily budget exhausted")
|
||||
return False
|
||||
_outlook_count += 1
|
||||
_last_run_ts = now
|
||||
_last_run_hour = hour_bucket
|
||||
return True
|
||||
|
||||
|
||||
@@ -223,7 +223,6 @@ class FundamentalContext:
|
||||
warning_message: str = ""
|
||||
news_sentiment: Dict[str, float] = field(default_factory=dict) # currency -> sentiment
|
||||
news_headlines: List[NewsSentiment] = field(default_factory=list)
|
||||
event_impact_on_pairs: Dict[str, str] = field(default_factory=dict)
|
||||
calendar_available: bool = False # True if live calendar data was fetched
|
||||
llm_summary: str = "" # DeepSeek market-context blurb (display-only)
|
||||
|
||||
@@ -415,15 +414,6 @@ class NewsEngine:
|
||||
)
|
||||
break
|
||||
|
||||
# Map upcoming high-impact events to affected pairs
|
||||
for event in context.events_upcoming_1h:
|
||||
if event.is_high_impact:
|
||||
affected = self._get_affected_pairs(event.currency)
|
||||
for pair in affected:
|
||||
context.event_impact_on_pairs[pair] = (
|
||||
f"{event.currency} {event.event} in {event.minutes_until:.0f}min"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Economic calendar fetch failed: {e}")
|
||||
|
||||
@@ -1149,11 +1139,3 @@ class NewsEngine:
|
||||
except ValueError:
|
||||
continue
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _get_affected_pairs(currency: str) -> List[str]:
|
||||
_MAP = {
|
||||
"USD": ["XAU/USD"],
|
||||
"XAU": ["XAU/USD"],
|
||||
}
|
||||
return _MAP.get(currency, [])
|
||||
|
||||
+8
-19
@@ -10,11 +10,11 @@ inputs (rule 4: live == backtest parity).
|
||||
Design contract
|
||||
---------------
|
||||
* **Bounded lookbacks.** No feature may read further back than the live
|
||||
fetch depth of its timeframe (``LIVE_DEPTH_BARS``). The primary frame
|
||||
is hard-capped at ``MAX_LOOKBACK_BARS`` rows, confirmation frames at
|
||||
60 rows, and the burst scan at 200 rows, so a feature computed on a
|
||||
full backtest frame equals the same feature computed on the shorter
|
||||
live tail.
|
||||
fetch depth of its timeframe (5000 bars on M5/M15, 1460 on H1, 380 on
|
||||
H4). The primary frame is hard-capped at ``MAX_LOOKBACK_BARS`` rows,
|
||||
confirmation frames at 60 rows, and the burst scan at 200 rows, so a
|
||||
feature computed on a full backtest frame equals the same feature
|
||||
computed on the shorter live tail.
|
||||
* **Closed bars only.** The frames handed in are exactly the frames
|
||||
``analyze_from_dataframes`` received: UTC tz-aware, oldest-first,
|
||||
capitalized OHLCV columns, forming candle already dropped. The last
|
||||
@@ -46,7 +46,7 @@ import json
|
||||
import logging
|
||||
import math
|
||||
from pathlib import Path
|
||||
from typing import Dict, Iterable, Optional, Sequence, Tuple
|
||||
from typing import Dict, Optional, Sequence, Tuple
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
@@ -60,17 +60,6 @@ logger = logging.getLogger(__name__)
|
||||
#: A trained artifact carrying a different value is ignored.
|
||||
FEATURE_VERSION = 1
|
||||
|
||||
#: Bars each timeframe actually holds in LIVE mode. Any feature whose
|
||||
#: lookback exceeds its timeframe's depth here would be unreproducible
|
||||
#: live, so the windows below stay far inside these numbers.
|
||||
LIVE_DEPTH_BARS: Dict[Timeframe, int] = {
|
||||
Timeframe.M5: 5000,
|
||||
Timeframe.M15: 5000,
|
||||
Timeframe.H1: 1460,
|
||||
Timeframe.H4: 380,
|
||||
Timeframe.D1: 300,
|
||||
}
|
||||
|
||||
#: Hard cap on any primary-timeframe window used in this module.
|
||||
MAX_LOOKBACK_BARS = 500
|
||||
|
||||
@@ -338,7 +327,7 @@ def _fill_time(out: Dict[str, float], dt: pd.Timestamp) -> None:
|
||||
|
||||
|
||||
def _fill_geometry(out: Dict[str, float], P: pd.DataFrame, setup,
|
||||
atr: float, atr_ok: bool, norm, d: float) -> None:
|
||||
atr: float, norm, d: float) -> None:
|
||||
out["f_atr"] = max(0.0, _finite(atr))
|
||||
ranges = (P["High"] - P["Low"]).astype(float)
|
||||
last_range = _finite(ranges.iloc[-1])
|
||||
@@ -566,7 +555,7 @@ def build_features(
|
||||
|
||||
for step, args in (
|
||||
(_fill_time, (out, decision_time(P, primary_tf))),
|
||||
(_fill_geometry, (out, P, setup, atr, atr_ok, norm, d)),
|
||||
(_fill_geometry, (out, P, setup, atr, norm, d)),
|
||||
(_fill_exhaustion, (out, report, KL, px, d, norm)),
|
||||
(_fill_quality, (out, report, setup, IND, d)),
|
||||
(_fill_mtf, (out, report, primary_tf, confirmation_tfs, d)),
|
||||
|
||||
@@ -41,7 +41,7 @@ def _status_counts(con: sqlite3.Connection) -> dict:
|
||||
|
||||
def main(argv=None) -> int:
|
||||
ap = argparse.ArgumentParser(description=__doc__.splitlines()[1])
|
||||
ap.add_argument("--db", default=None, help="defaults to signal_log.path")
|
||||
ap.add_argument("--db", default=None, help="defaults to the signal_log.path setting in config.json")
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
ap.add_argument("--reseed-unresolvable", action="store_true")
|
||||
ap.add_argument("--symbol", default="XAU/USD")
|
||||
|
||||
@@ -60,7 +60,7 @@ def _counts(con: sqlite3.Connection, symbol: str,
|
||||
|
||||
def main(argv=None) -> int:
|
||||
ap = argparse.ArgumentParser(description=__doc__.splitlines()[1])
|
||||
ap.add_argument("--db", default=None, help="defaults to signal_log.path")
|
||||
ap.add_argument("--db", default=None, help="defaults to the signal_log.path setting in config.json")
|
||||
ap.add_argument("--dry-run", action="store_true",
|
||||
help="read-only candidate counts, writes nothing")
|
||||
ap.add_argument("--budget", type=int, default=100000,
|
||||
|
||||
@@ -806,7 +806,6 @@ class SignalLogger:
|
||||
the daemon's thread pool."""
|
||||
|
||||
def __init__(self, path: str):
|
||||
self._path = path
|
||||
parent = os.path.dirname(path)
|
||||
if parent:
|
||||
os.makedirs(parent, exist_ok=True)
|
||||
@@ -860,10 +859,6 @@ class SignalLogger:
|
||||
raise
|
||||
time.sleep(delay * (attempt + 1))
|
||||
|
||||
@property
|
||||
def path(self) -> str:
|
||||
return self._path
|
||||
|
||||
def _migrate_signal_columns(self) -> None:
|
||||
"""Add newer signal columns to pre-existing tables (idempotent)."""
|
||||
existing = {r[1] for r in self._conn.execute("PRAGMA table_info(signals)")}
|
||||
@@ -1291,27 +1286,6 @@ class SignalLogger:
|
||||
rowid, exc_info=True)
|
||||
return written
|
||||
|
||||
def advance_with_bars(self, rowid: int, bars: Iterable[Dict[str, Any]],
|
||||
evidence: str = "external_bars") -> str:
|
||||
"""Advance ONE pending outcome from caller-supplied bars (backfill
|
||||
from provider history). Returns the resulting status."""
|
||||
with self._lock:
|
||||
row = self._conn.execute(
|
||||
"SELECT advisory_outcome_status, advisory_outcome_json"
|
||||
" FROM signals WHERE id=?", (rowid,)).fetchone()
|
||||
if row is None or row[0] != "PENDING" or not row[1]:
|
||||
return str(row[0]) if row else "MISSING"
|
||||
state = advance_outcome(json.loads(row[1]), list(bars))
|
||||
state["evidence"] = evidence
|
||||
status = state.get("status", "UNRESOLVABLE")
|
||||
self._conn.execute(
|
||||
"UPDATE signals SET advisory_outcome_status=?,"
|
||||
" advisory_r_multiple=?, advisory_outcome_json=?"
|
||||
" WHERE id=? AND advisory_outcome_status='PENDING'",
|
||||
(status, canonical_r(state), _dumps(state), rowid))
|
||||
self._conn.commit()
|
||||
return status
|
||||
|
||||
def backfill_seeds(self, limit: Optional[int] = None) -> Dict[str, int]:
|
||||
"""Assign setup_key / advisory_event_id to legacy setup rows and
|
||||
seed the canonical ones that carry no outcome yet. Existing
|
||||
|
||||
+2
-14
@@ -881,18 +881,6 @@ def _():
|
||||
assert NewsEngine._parse_rss_date("not-a-date") is None
|
||||
|
||||
|
||||
@test("NewsEngine._get_affected_pairs")
|
||||
def _():
|
||||
pairs = NewsEngine._get_affected_pairs("USD")
|
||||
assert "XAU/USD" in pairs
|
||||
|
||||
pairs_xau = NewsEngine._get_affected_pairs("XAU")
|
||||
assert "XAU/USD" in pairs_xau
|
||||
|
||||
pairs_unknown = NewsEngine._get_affected_pairs("ARS")
|
||||
assert pairs_unknown == []
|
||||
|
||||
|
||||
@test("EconomicEvent properties")
|
||||
def _():
|
||||
future_time = datetime.now(timezone.utc) + timedelta(minutes=30)
|
||||
@@ -8174,7 +8162,7 @@ def _():
|
||||
"stance": "support"}])))
|
||||
con.commit()
|
||||
con.close()
|
||||
buckets = asl.news_outcome_buckets(rows, events, path)
|
||||
buckets = asl.news_outcome_buckets(events, path)
|
||||
finally:
|
||||
os.remove(path)
|
||||
assert sum(b["n"] for b in buckets) == 3
|
||||
@@ -9117,7 +9105,7 @@ def _():
|
||||
ev = _types.SimpleNamespace(
|
||||
rows=[_types.SimpleNamespace(id=1), _types.SimpleNamespace(id=2)],
|
||||
r_headline=2.0)
|
||||
out = asl.persona_scorecard([], [ev], path)
|
||||
out = asl.persona_scorecard([ev], path)
|
||||
assert len(out) == 1, out
|
||||
assert out[0]["n"] == 1, f"two rows of ONE event must count once: {out}"
|
||||
assert out[0]["mean_r"] == 2.0
|
||||
|
||||
Reference in New Issue
Block a user