fix: recalibrate FIP path labels to live equity scale
Replace inert ±0.25 bands with ~p25/p75 cutoffs from the prod snapshot (−0.08 / 0.00). Document zero-return dilution and left-skewed distribution.
This commit is contained in:
@@ -408,6 +408,17 @@ def compute_pivot_points(
|
||||
}
|
||||
|
||||
|
||||
# Path labels for display only. Calibrated on the ~505-name prod snapshot
|
||||
# (2026-07, n=502 with full history): empirical p25 ≈ −0.082, p75 ≈ +0.004,
|
||||
# mean ≈ −0.043. Paper-style |ID| ≳ 0.25 almost never appears in live equities
|
||||
# (only ~0.2% of names); real momentum winners cluster around −0.04…−0.12.
|
||||
# Thresholds are therefore ~quartile cutoffs, not ±0.25 textbook extremes.
|
||||
# Distribution is left-skewed (bullish sample → more "continuous" than "discrete"),
|
||||
# so the discrete band is not symmetric.
|
||||
FIP_PATH_CONTINUOUS_MAX = -0.08 # ~p25: smoother quartile
|
||||
FIP_PATH_DISCRETE_MIN = 0.00 # ~p75: less-continuous quartile
|
||||
|
||||
|
||||
def compute_fip_id(closes: list[float], as_of_index: int | None = None) -> dict[str, Any]:
|
||||
"""Da/Gurun/Warachka information discreteness over the 12-1 formation window.
|
||||
|
||||
@@ -418,8 +429,13 @@ def compute_fip_id(closes: list[float], as_of_index: int | None = None) -> dict[
|
||||
ID = sign(PRET) × (%neg − %pos)
|
||||
|
||||
Lower ID ⇒ smoother / more continuous path (for a winner: many small up days).
|
||||
Higher ID ⇒ jumpy / discrete path (few large moves). Zero-return days count
|
||||
in neither numerator but remain in the denominator.
|
||||
Higher ID ⇒ jumpy / discrete path (few large moves).
|
||||
|
||||
Zero-return days count in neither numerator but remain in the denominator
|
||||
(paper definition). Quirk: a flat series with one big jump can still land
|
||||
near zero ("mixed") because zeros dilute %pos/%neg — faithful to the paper
|
||||
and to real equities (exact zero daily returns are rare). Synthetic jump
|
||||
tests assert ordering vs a steady climber, not the discrete label itself.
|
||||
"""
|
||||
i = len(closes) - 1 if as_of_index is None else as_of_index
|
||||
if i < 252 or closes[i - 252] <= 0 or closes[i - 21] <= 0:
|
||||
@@ -448,12 +464,13 @@ def compute_fip_id(closes: list[float], as_of_index: int | None = None) -> dict[
|
||||
else:
|
||||
sign = 0.0
|
||||
fip = sign * (pct_neg - pct_pos)
|
||||
# Map [-1, 1] → score where lower ID (smoother) is higher for display only.
|
||||
# Map observed ID range (~[-0.3, 0.15]) loosely to 0–100 for the card chrome;
|
||||
# lower ID (smoother) → higher score. Display only.
|
||||
score = max(0.0, min(100.0, 50.0 * (1.0 - fip)))
|
||||
if fip <= -0.25:
|
||||
if fip <= FIP_PATH_CONTINUOUS_MAX:
|
||||
path = "continuous"
|
||||
path_label = "smooth grind (continuous information)"
|
||||
elif fip >= 0.25:
|
||||
elif fip >= FIP_PATH_DISCRETE_MIN:
|
||||
path = "discrete"
|
||||
path_label = "jumpy path (discrete information)"
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user