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:
@@ -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")]
|
||||
|
||||
Reference in New Issue
Block a user