feat: show FIP path-smoothness in ticker technicals
Deploy / lint (push) Successful in 9s
Deploy / test (push) Successful in 1m20s
Deploy / deploy (push) Successful in 41s

Display-only Da/Gurun/Warachka information discreteness on the ticker
indicator panel. Shared compute with the backtest harness; not wired into
gate or rank.
This commit is contained in:
2026-07-18 19:22:02 +02:00
parent a71dd4adb7
commit 19d674ed62
4 changed files with 159 additions and 37 deletions
+6 -31
View File
@@ -819,39 +819,14 @@ def _realized_vol_6m(closes: list[float], i: int) -> float | None:
def _fip_id(closes: list[float], i: int) -> float | None:
"""Da/Gurun/Warachka information discreteness over the 12-1 formation window.
"""Point-in-time FIP ID for the signal harness; delegates to indicator_service."""
from app.services.indicator_service import compute_fip_id
from app.exceptions import ValidationError
Formation matches ``mom_12_1``: cumulative return from close[i-252] to
close[i-21] (231 daily returns ending one month before as-of).
ID = sign(PRET) × (%neg %pos)
where %pos / %neg are fractions of up / down days over the formation window
(zero-return days count in neither numerator, but remain in the denominator).
Lower ID = smoother / more continuous path → expect negative cross-sectional
IC (continuous-information winners outperform).
"""
if i - 252 < 0 or closes[i - 252] <= 0 or closes[i - 21] <= 0:
try:
return float(compute_fip_id(closes, as_of_index=i)["fip_id"])
except (ValidationError, KeyError, TypeError, ValueError):
return None
pret = closes[i - 21] / closes[i - 252] - 1.0
rets: list[float] = []
for k in range(i - 251, i - 20):
prev = closes[k - 1]
if prev <= 0:
return None
rets.append(closes[k] / prev - 1.0)
if len(rets) < 200:
return None
n = len(rets)
pct_pos = sum(1 for r in rets if r > 0) / n
pct_neg = sum(1 for r in rets if r < 0) / n
if pret > 0:
sign = 1.0
elif pret < 0:
sign = -1.0
else:
sign = 0.0
return sign * (pct_neg - pct_pos)
def _signal_values(