feat: Phase 3 — indicateurs techniques calibrés par horizon option

- technical_indicators.py (nouveau) : compute_indicators() calcule RSI, MA fast/slow,
  Bollinger Bands, ATR — périodes calibrées automatiquement selon horizon_days
- config.py : endpoints GET/PUT /config/tech-indicators (activé, liste, auto-calibration)
- useApi.ts : useTechIndicatorsConfig + useSaveTechIndicatorsConfig hooks
- Config.tsx : carte "Indicateurs techniques" dans Options—Paramètres avec toggles
- auto_cycle.py : compute top-5 tickers à chaque cycle si tech_indicators_enabled=true
- ai_analyzer.py : tech_indicators_block injecté dans suggestion + scoring prompts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-20 16:41:42 +02:00
parent d5e31bc897
commit 50ba75e468
6 changed files with 357 additions and 2 deletions

View File

@@ -376,12 +376,43 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
# Apply decay to news before suggestion (adds decayed_score + age_hours)
from services.ai_analyzer import apply_news_decay as _apply_decay
news = _apply_decay(news)
# ── Tech indicators for top tickers ──────────────────────────────
_tech_block = ""
try:
_ti_enabled = (get_config("tech_indicators_enabled") or "true").lower() == "true"
if _ti_enabled:
from services.technical_indicators import compute_indicators, format_indicators_for_prompt
_ti_list = [s.strip() for s in (get_config("tech_indicators_list") or "rsi,ma,bollinger,atr").split(",") if s.strip()]
_horizon_days = 45 # default mid-range if no trade horizon context yet
_ti_lines = []
# Pick up to 5 tickers from quotes (one per asset class)
_seen_tickers: set = set()
for _cls, _qs in quotes.items():
for _q in _qs[:1]:
_sym = _q.get("symbol", "")
if _sym and _sym not in _seen_tickers:
_seen_tickers.add(_sym)
_ind = compute_indicators(_sym, _horizon_days, enabled_indicators=_ti_list)
_blk = format_indicators_for_prompt(_ind)
if _blk:
_ti_lines.append(_blk)
if len(_seen_tickers) >= 5:
break
if len(_seen_tickers) >= 5:
break
if _ti_lines:
_tech_block = "\n".join(_ti_lines)
except Exception as _te:
logger.warning(f"[Cycle] Tech indicators failed (non-blocking): {_te}")
suggestions = suggest_patterns_from_market_context(
news, quotes, calendar, macro_regime=macro_regime, geo_score=geo_score_obj,
portfolio_lessons=portfolio_lessons,
reliability_map=_reliability_map or None,
iv_context=iv_context,
cycle_meta=cycle_meta,
tech_indicators_block=_tech_block,
)
except Exception as e:
logger.warning(f"[Cycle] Suggestion step failed: {e}")
@@ -505,6 +536,7 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
iv_context=iv_context,
risk_context=risk_cluster_context,
cycle_meta=cycle_meta,
tech_indicators_block=_tech_block,
)
scored_with_id = [s for s in scored if s.get("pattern_id")]
scored_without_id = [s for s in scored if not s.get("pattern_id")]