feat: temporal params (lag/diffusion/decay) sur aretes causales
- CausalEdge: lag_min, diffusion_min, decay_days - SVG: labels +Xm (jaune, source) et decay (gris, cible) sur les aretes - Editeur: champs lag/diffusion/decay dans add-edge et edit-edge - Bibliotheque: section Parametres temporels avec grille editable + sauvegarde PATCH - Backend: _drift_metrics utilise lag_min pour decaler fenetre intraday 5min - analyze: calcule effective_lag depuis aretes → market_asset, retourne dans resultat Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -134,24 +134,25 @@ def _fetch_prices(event_date_str: str, instruments: list[str]) -> dict:
|
||||
return out
|
||||
|
||||
|
||||
def _drift_metrics(prices: dict, event_date_str: str, inst: str) -> dict:
|
||||
def _drift_metrics(prices: dict, event_date_str: str, inst: str, lag_min: int = 0) -> dict:
|
||||
series = prices.get(inst, [])
|
||||
mode = prices.get("mode", "none")
|
||||
edate = event_date_str[:10]
|
||||
|
||||
empty = {"pre_pips": None, "post_pips": None, "drift_ratio": None, "leak": "unknown"}
|
||||
empty = {"pre_pips": None, "post_pips": None, "drift_ratio": None, "leak": "unknown", "lag_min": lag_min}
|
||||
if not series:
|
||||
return empty
|
||||
|
||||
mult = 10000 if inst in ("EURUSD",) else 10
|
||||
if mode == "intraday_5m":
|
||||
n = len(series)
|
||||
if n < 8:
|
||||
return empty
|
||||
mid = n // 2
|
||||
# Convertir en pips (×10000 pour FX, ×10 pour or/pétrole/SP500)
|
||||
mult = 10000 if inst in ("EURUSD",) else 10
|
||||
pre_pips = round((series[mid - 1]["c"] - series[0]["c"]) * mult)
|
||||
post_pips = round((series[-1]["c"] - series[mid]["c"]) * mult)
|
||||
# Décale le mid en avant selon le lag (chaque barre = 5 min)
|
||||
lag_bars = max(0, round(lag_min / 5))
|
||||
mid = min(n // 2 + lag_bars, n - 2)
|
||||
pre_pips = round((series[mid - 1]["c"] - series[0]["c"]) * mult)
|
||||
post_pips = round((series[-1]["c"] - series[mid]["c"]) * mult)
|
||||
else:
|
||||
pre = [b for b in series if b["t"] < edate]
|
||||
same = [b for b in series if b["t"] == edate]
|
||||
@@ -760,6 +761,15 @@ def analyze_event(body: AnalyzeRequest):
|
||||
# Évaluation du graphe
|
||||
node_values = evaluate_graph(graph, inputs, body.coef_overrides or {})
|
||||
|
||||
# Lag effectif : max des lag_min sur toutes les arêtes menant à un market_asset
|
||||
edges = graph.get("edges", [])
|
||||
nodes_map = {n["id"]: n for n in graph.get("nodes", [])}
|
||||
output_ids = {n["id"] for n in graph.get("nodes", []) if n.get("type") in ("market_asset", "output")}
|
||||
effective_lag = max(
|
||||
(e.get("lag_min", 0) or 0 for e in edges if e.get("to") in output_ids),
|
||||
default=0,
|
||||
)
|
||||
|
||||
# Prix réels
|
||||
instruments = list({body.instrument} | set(tmpl.get("instruments", [body.instrument])))
|
||||
prices = _fetch_prices(event["start_date"], instruments)
|
||||
@@ -768,7 +778,7 @@ def analyze_event(body: AnalyzeRequest):
|
||||
actual_moves: dict = {}
|
||||
drift_by_inst: dict = {}
|
||||
for inst in instruments:
|
||||
drift = _drift_metrics(prices, event["start_date"], inst)
|
||||
drift = _drift_metrics(prices, event["start_date"], inst, lag_min=effective_lag)
|
||||
drift_by_inst[inst] = drift
|
||||
if drift.get("post_pips") is not None:
|
||||
actual_moves[inst] = drift["post_pips"]
|
||||
@@ -786,18 +796,19 @@ def analyze_event(body: AnalyzeRequest):
|
||||
analyzed_at = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
result = {
|
||||
"event": event,
|
||||
"template_id": body.template_id,
|
||||
"template_name": tmpl["name"],
|
||||
"instrument": body.instrument,
|
||||
"inputs": inputs,
|
||||
"node_values": node_values,
|
||||
"actual_moves": actual_moves,
|
||||
"yields": yields,
|
||||
"activation": activation,
|
||||
"drift": drift_by_inst.get(body.instrument, {}),
|
||||
"prices_mode": prices.get("mode", "none"),
|
||||
"analyzed_at": analyzed_at,
|
||||
"event": event,
|
||||
"template_id": body.template_id,
|
||||
"template_name": tmpl["name"],
|
||||
"instrument": body.instrument,
|
||||
"inputs": inputs,
|
||||
"node_values": node_values,
|
||||
"actual_moves": actual_moves,
|
||||
"yields": yields,
|
||||
"activation": activation,
|
||||
"drift": drift_by_inst.get(body.instrument, {}),
|
||||
"prices_mode": prices.get("mode", "none"),
|
||||
"effective_lag_min": effective_lag,
|
||||
"analyzed_at": analyzed_at,
|
||||
}
|
||||
|
||||
# Persistance
|
||||
|
||||
Reference in New Issue
Block a user