remove the AI market-outlook feature (delivery-side only; books byte-identical)

Owner decision 2026-09-15. Gone: market_outlook.py, the post-H1-scan Telegram
outlook DM, main.py --outlook, the outlooks table DDL/migration/writer in
signal_log.py, the market_outlook config block + MARKET_OUTLOOK_ENABLED, and
test Module 26. Last commit containing the feature: 2836024.

- app_config: "market_outlook" stays in _KNOWN_TOP as a TOLERATED, ignored
  legacy key (oanda.account_id precedent) with one warning, so a live
  config.json that still carries the block keeps booting.
- signal_log: existing DBs keep their orphan outlooks table and rows (no
  DROP TABLE); fresh DBs never create it.
- tests: Module 26's 4-thread concurrent legacy-DB migration test (the
  suite's only one) moved to Module 31; new test for the tolerated block.
  519 -> 509 tests, 34 -> 33 modules, 0 failed.
- docs: CLAUDE.md usage/config/file-map lines and a dated state note.

Verification: W2 books (2024-09-05 -> 2026-09-05, $0.65, NumPy CPU, 0 cache
fallbacks) byte-identical before/after — M5 29120a1ee32979431d062f210d41a9d1
(781 / +146.07R / PF 1.52), M15 d185f099482a07f29502cb5a371a9bbf
(931 / +146.14R / PF 1.43).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Harwinder Sekhon
2026-09-15 19:58:47 -04:00
parent 2836024e9d
commit 931b26548e
10 changed files with 103 additions and 1047 deletions
+12 -35
View File
@@ -84,7 +84,12 @@ _VALID_LOG_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
_SCANNABLE_TFS = ("M5", "M15", "H1", "H4")
_KNOWN_TOP = {"telegram", "data_providers", "scanning", "pairs", "log_level",
"oanda", "deepseek", "agent_panel", "market_outlook",
"oanda", "deepseek", "agent_panel",
# "market_outlook" is TOLERATED but ignored: the AI market
# outlook was removed 2026-09-15 (last commit 2836024). Live
# config.json files still carry the block and must keep
# loading; load_app_config logs one warning when it is there.
"market_outlook",
"violent_watch", "signal_log", "news_archive"}
_KNOWN_OANDA = {
# "account_id" is TOLERATED but ignored: it belonged to the auto-
@@ -102,7 +107,6 @@ _KNOWN_AGENT_PANEL = {
"candles_enabled", "candle_bars_primary", "candle_bars_confirm",
"verdict_source",
}
_KNOWN_MARKET_OUTLOOK = {"enabled", "telegram", "max_per_day", "candles_bars"}
_KNOWN_VIOLENT_WATCH = {"enabled", "alert_line", "headsup",
"max_headsup_per_day", "headsup_min_level"}
_KNOWN_SIGNAL_LOG = {"enabled", "path", "log_blocked_scans"}
@@ -111,13 +115,6 @@ _KNOWN_TELEGRAM = {"token", "chat_id", "channel_id", "alert_min_quality"}
_KNOWN_SCANNING = {"pairs", "timeframes"}
_KNOWN_PAIR_KEYS = {"blocked_hours_utc"}
_MARKET_OUTLOOK_DEFAULTS = {
"enabled": False,
"telegram": False,
"max_per_day": 12,
"candles_bars": 20,
}
# Violent-move watch (display-only heads-up) — dark by default.
_VIOLENT_WATCH_DEFAULTS = {
"enabled": False,
@@ -149,8 +146,6 @@ class AppConfig:
oanda_settings: Dict[str, Any] = field(default_factory=dict)
deepseek_settings: Dict[str, Any] = field(default_factory=dict)
agent_panel_settings: Dict[str, Any] = field(default_factory=dict)
market_outlook_settings: Dict[str, Any] = field(
default_factory=lambda: dict(_MARKET_OUTLOOK_DEFAULTS))
violent_watch_settings: Dict[str, Any] = field(
default_factory=lambda: dict(_VIOLENT_WATCH_DEFAULTS))
signal_log_settings: Dict[str, Any] = field(default_factory=dict)
@@ -341,19 +336,12 @@ def load_app_config(path: str = CONFIG_PATH) -> AppConfig:
'"chair"')
app.agent_panel_settings = dict(panel)
outlook = raw.get("market_outlook", {})
_check_keys(outlook, _KNOWN_MARKET_OUTLOOK, '"market_outlook"')
for flag in ("enabled", "telegram"):
if flag in outlook and not isinstance(outlook[flag], bool):
raise AppConfigError(
f"config.json: market_outlook.{flag} must be true or false")
for key in ("max_per_day", "candles_bars"):
if key in outlook:
value = outlook[key]
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
raise AppConfigError(
f"config.json: market_outlook.{key} must be a positive integer")
app.market_outlook_settings.update(outlook)
if "market_outlook" in raw:
# The AI market-outlook feature was removed 2026-09-15; older
# config.json files still carry its block and must keep loading.
logger.warning(
"config.json: market_outlook block is ignored (feature removed "
"2026-09-15) — delete it")
watch = raw.get("violent_watch", {})
_check_keys(watch, _KNOWN_VIOLENT_WATCH, '"violent_watch"')
@@ -493,17 +481,6 @@ def deepseek_api_key(app: Optional[AppConfig] = None) -> str:
app.deepseek_settings.get("api_key", ""))
# ── AI market-outlook resolution helper ──────────────────────────────
def market_outlook_enabled(app: Optional[AppConfig] = None) -> bool:
"""Whether the advisory outlook feature is enabled (env wins)."""
env = os.environ.get("MARKET_OUTLOOK_ENABLED")
if env:
return env.strip().lower() in ("1", "true", "yes", "on")
app = app or get_app_config()
return bool(app.market_outlook_settings.get("enabled", False))
# ── Violent-move watch resolution helper ─────────────────────────────
def violent_watch_enabled(app: Optional[AppConfig] = None) -> bool: