feat(mcp-macro): add compute_percentile + classify_extreme pure helpers
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
"""Pure-logic helpers per COT report parsing e analytics.
|
||||
|
||||
Niente HTTP qui — orchestrazione fetch sta in fetchers.py. Tutto testabile
|
||||
in isolamento.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
ExtremeSignal = Literal["extreme_short", "extreme_long", "neutral"]
|
||||
|
||||
|
||||
def compute_percentile(value: float, history: list[float]) -> float | None:
|
||||
"""Percentile di `value` rispetto ad `history` (0-100, inclusive).
|
||||
|
||||
Restituisce None se history vuoto. Clipped a [0, 100] se value fuori range.
|
||||
"""
|
||||
if not history:
|
||||
return None
|
||||
n = len(history)
|
||||
below_or_eq = sum(1 for h in history if h <= value)
|
||||
pct = 100.0 * below_or_eq / n
|
||||
return max(0.0, min(100.0, pct))
|
||||
|
||||
|
||||
def classify_extreme(percentile: float | None, threshold: float = 5.0) -> ExtremeSignal:
|
||||
"""Classifica un percentile come estremo short/long o neutral.
|
||||
|
||||
threshold default 5 → flagga ≤ 5 come short, ≥ 100-5=95 come long.
|
||||
"""
|
||||
if percentile is None:
|
||||
return "neutral"
|
||||
if percentile <= threshold:
|
||||
return "extreme_short"
|
||||
if percentile >= 100.0 - threshold:
|
||||
return "extreme_long"
|
||||
return "neutral"
|
||||
@@ -0,0 +1,44 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from mcp_macro.cot import classify_extreme, compute_percentile
|
||||
|
||||
|
||||
def test_compute_percentile_basic():
|
||||
history = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
|
||||
assert compute_percentile(50, history) == 50.0
|
||||
assert compute_percentile(10, history) == 10.0
|
||||
assert compute_percentile(100, history) == 100.0
|
||||
|
||||
|
||||
def test_compute_percentile_value_below_min():
|
||||
history = [10, 20, 30]
|
||||
assert compute_percentile(5, history) == 0.0
|
||||
|
||||
|
||||
def test_compute_percentile_value_above_max():
|
||||
history = [10, 20, 30]
|
||||
assert compute_percentile(40, history) == 100.0
|
||||
|
||||
|
||||
def test_compute_percentile_empty_history():
|
||||
assert compute_percentile(50, []) is None
|
||||
|
||||
|
||||
def test_classify_extreme_below_threshold():
|
||||
assert classify_extreme(3.0) == "extreme_short"
|
||||
assert classify_extreme(5.0) == "extreme_short" # boundary inclusive
|
||||
|
||||
|
||||
def test_classify_extreme_above_threshold():
|
||||
assert classify_extreme(96.0) == "extreme_long"
|
||||
assert classify_extreme(95.0) == "extreme_long" # boundary inclusive
|
||||
|
||||
|
||||
def test_classify_extreme_neutral():
|
||||
assert classify_extreme(50.0) == "neutral"
|
||||
assert classify_extreme(94.99) == "neutral"
|
||||
assert classify_extreme(5.01) == "neutral"
|
||||
|
||||
|
||||
def test_classify_extreme_none_input():
|
||||
assert classify_extreme(None) == "neutral"
|
||||
Reference in New Issue
Block a user