7 Commits
Author SHA1 Message Date
gavindiazandClaude Code 1a7b0111ad fix(startup): build asyncio socketpair over AF_UNIX - unblocks .bat console freeze
The run_bot.bat console path froze for minutes after two log lines with 0 CPU:
asyncio.run() -> ProactorEventLoop.__init__ -> socket.socketpair() -> Python
3.12 _fallback_socketpair() blocks forever in accept() on the loopback TCP
handshake (security software filters 127.0.0.1 in this interactive console +
xau-trader.exe session). Git-bash redirect runs worked 4/4, proving it is
environment, not interpreter.

Windows 10 17063+ supports AF_UNIX sockets, which need no loopback TCP at all.
Monkey-patch socket.socketpair on win32 to self-pair over AF_UNIX via ctypes
(ws2_32; python.org builds don't expose the AF_UNIX constant), falling back to
the stock socketpair if AF_UNIX is unavailable. socket.socket(fileno=N) treats
N as a raw SOCKET handle on Windows, so the raw handles wrap cleanly.

Verified: smoke test raw pair family=1 + full asyncio.run Proactor loop; the
exe boot went from hang to 'Main loop started' in ~10s.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-21 20:58:08 +08:00
gavindiazandClaude Code f38fa944ea fix(exec): repair the six live loss root causes - cooldown, M5-ATR trailing, honest learner, persisted snapshots
Phase-3 execution-layer fixes, config values are the phase-2 gate winners
(conf=55/breakeven 0.7/atr_trail 1.5/trigger 1.2 - scalping 5-month sweep PF
2.11 at conf=55, baseline PF 1.50):

- trailing_stop: trail now arms only after trail_trigger_atr x ATR unrealized
  gain and never trails past TP (tick-capped). Old logic trailed on H1-ATR
  (~6) while SL/TP were M5-ATR-calibrated (~-6) - breakeven at +1.85 was
  unreachable, every scalp exited at SL
- bot.py: entry cooldown per (strategy, direction) from the cooldown: YAML
  block (scalping 1800s/day_trading 3600s/swing 7200s) - signals were re-firing
  every 30s cycle and piling same-direction positions; Step 4 AI prediction on
  df_m15 (model trains on M15); Step 8 trails on M5 ATR; removed duplicate
  on_trade_closed hook in Step 9 (each external close was learned twice,
  strat_scalping multiplier decayed 0.527->0.41 on garbage inputs);
  performance_snapshots now trigger on wall clock (boot + every >=30min) so
  restarts can't reset the cycle counter to 0
- trade_executor: execute_signal threads ai_score/regime_score into signal
  records; close_trade + sync_positions share _learner_context built from the
  DB Trade row (was: hardcoded regime=RANGING/confluence=75/sentiment=0)
- trading_params.yaml: moderate scalping 50->55, breakeven 0.7, trail 1.5,
  trail_trigger_atr 1.2, cooldown + backtest sections
- tests: scalping moderate confluence assertion 50.0 -> 55.0

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-21 20:57:42 +08:00
gavindiazandClaude Code 7e50a0fef6 feat(llm): provider-agnostic OpenAI-compatible client; FinBERT warm-up off the event loop
Replace the Google Gemini lock-in with a unified chat client usable with any
OpenAI-compatible /v1 endpoint (OpenAI, OpenRouter, DeepSeek, Moonshot, Qwen,
Ollama, Gemini-via-compat...). Switch provider purely via AI_BASE_URL/AI_MODEL.

- ai/llm_client.py (new): lazy openai SDK, per-candidate 429/503/timeout
  failover across AI_MODEL_FAILOVER, robust JSON extraction, reasoning-budget
  retry, image analysis via base64 data URI, singleton get_llm_client()
- settings.py: GeminiSettings -> LLMSettings (api_key/base_url/model/failover/
  vision_model), keeps GEMINI_API_KEY fallback so existing .env keeps working
- news_analyzer.py: analyze_with_gemini -> analyze_with_llm (same output keys);
  FinBERT now loads in a daemon warm-up thread with lexicon fallback - the
  transformers first import was blocking the asyncio main loop 15-45s
  (importlib.metadata full package scan); HF_ENDPOINT hf-mirror setdefault
- telegram_bot.py: news digest + AI-vision chart analysis via the LLM client
- requirements: google-genai -> openai>=1.0; docs/.env.example/README updated
  for the new provider-agnostic setup; dashboard meta updated

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-21 20:56:52 +08:00
gavindiazandClaude Code 886de825fa feat(backtest): real-data gate runners for scalping and day_trading + grid results
The prove-before-live gate tooling. Both runners fetch real MT5 history
(M5/H1 for simulation, M15 for AI, H4 for regime), build zero-lookahead
time-aligned lookups, run the live-parity engine, append config->metrics rows
to grid_results.csv and print the gate verdict:

  expectancy > 0 AND profit_factor >= 1.2 AND max_drawdown_pct <= 15 AND total_trades >= 60

- run_real_backtest.py: scalping gate mirroring live moderate params
- backtest_day_trading_gate.py: H1-frame gate for the day_trading family
  (isolation loop disables scalping/swing), compares conf=live(55) vs
  conf=raw(70); 5-month run: conf=55 PASS (72 trades, WR 70.8%, PF 3.45,
  exp +47.91, maxDD 2.25%), conf=70 FAIL (0 trades - threshold unbeatable)
- grid_results.csv: full scan history incl. the 5-month all-configs sweep
  whose baseline (50/0/2.0: 2214 trades, PF 1.50) proved live losses were
  the execution layer, not the entry spec

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-21 20:56:27 +08:00
gavindiazandClaude Code d9b0b19c02 feat(backtest): live-parity backtest engine with zero-lookahead AI/regime lookups
Phase 1 of the prove-before-live alignment. The backtest engine now mirrors
the live signal path instead of hardcoding HOLD/NEUTRAL:

- predict_batch (signal_classifier): vectorized per-bar AI signals reusing the
  exact live predict() pipeline (class-index mapping, conf<0.5 -> HOLD)
- backtester: per-bar time-aligned AI lookup (M15 close = signal-available
  time, searchsorted, no lookahead), exogenous H4 regime plan with dynamic
  confluence-key deltas, AttentionMarketScorer alignment, per-strategy entry
  cooldown, max-hold timeout exit, breakeven + trailing capped at TP, running-
  equity pnl_pct (was: static initial balance)
- fix: store direction.value on entry (Enum vs str compare silently disabled
  every exit - no trade ever closed)
- fix: _check_mode_sync only clears override on genuine mode transition (grid
  threshold injection was wiped on first read)
- SignalAggregator(use_adaptive_weights=True): backtest uses pure config
  weights so garbage live adaptive weights can't contaminate gate scores

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-21 20:55:45 +08:00
gavindiazandClaude Code 661fd8d941 fix(learner): scope mistake-guard thresholds to each strategy's own confluence base
The guard previously hardcoded the scalping base for every strategy, so
day_trading and swing_trading signals were screened against scalping's
numbers. New _strategy_base_confluence() resolves each strategy to its own
settings section (day_trading_params / swing_trading_params), mirroring
BaseStrategy.min_confluence_score routing; unknown names fall back to
scalping (legacy). Moderate-mode parity verified by smoke test: penalty
override 55+6=61, memory veto 55+8=63 for both day_trading and scalping.
Full suite: 39 tests pass.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-21 20:47:10 +08:00
gavindiazandClaude Code 239f4108df fix(learner): unblock Mistake Guard deadlock — conditional veto + time decay
The guard vetoed UNCONDITIONALLY whenever a strategy multiplier fell below
0.65x, never comparing the confluence the '+6' message claimed. A stale 0.408x
SCALPING multiplier (garbage-era lessons incl. one double-learned duplicate)
then locked the bot out permanently: no trades → no lessons → no recovery.

Now the veto is conditional on confluence >= base + 6 (penalty still applied
to effective_score downstream), and multipliers time-decay back toward 1.0
(~24h constant, persisted without touching win/loss counts).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-16 17:13:12 +08:00