65 lines
2.1 KiB
Python
65 lines
2.1 KiB
Python
import math
|
|
|
|
from scoring import engine
|
|
|
|
|
|
def _complete_metrics():
|
|
return {
|
|
"fear_greed": {"value": 10, "classification": "Extreme Fear"},
|
|
"puell_multiple": {"value": 0.3},
|
|
"mvrv_zscore": {"value": -0.1},
|
|
"drawdown": {"value": 60.0, "ath": 250.0},
|
|
"price": {"price": 100.0},
|
|
"200w_sma": {"value": 120.0},
|
|
"reserve_risk": {"value": 0.001},
|
|
"rhodl_ratio": {"value": 50.0},
|
|
"nupl": {"value": -0.1},
|
|
"lth_realized_price": {"value": 120.0},
|
|
"hash_ribbons": {"buy_signal": False},
|
|
}
|
|
|
|
|
|
def test_score_all_ml_normalizes_displayed_weights_and_contributions(monkeypatch):
|
|
monkeypatch.setattr(
|
|
engine,
|
|
"load_ml_weights",
|
|
lambda: {
|
|
"fear_greed": 0.40,
|
|
"puell_multiple": 0.20,
|
|
"mvrv_zscore": 0.15,
|
|
"drawdown": 0.10,
|
|
"pct_above_200w_sma": 0.05,
|
|
"reserve_risk": 0.04,
|
|
"rhodl_ratio": 0.03,
|
|
"nupl": 0.02,
|
|
"pct_above_lth_rp": 0.01,
|
|
},
|
|
)
|
|
|
|
scored = engine.score_all_ml(_complete_metrics())
|
|
|
|
assert scored["ml_mode"] is True
|
|
valid_metrics = [m for m in scored["metrics"] if m["score"] is not None]
|
|
assert scored["ml_weight_total"] == 1.01 # trained weights + small hash-ribbons fallback
|
|
assert math.isclose(sum(m["ml_weight"] for m in valid_metrics), 1.0, abs_tol=0.001)
|
|
assert math.isclose(
|
|
sum(m["ml_contribution"] for m in valid_metrics),
|
|
scored["composite_score"],
|
|
abs_tol=0.05,
|
|
)
|
|
|
|
hash_ribbons = next(m for m in valid_metrics if m["key"] == "hash_ribbons")
|
|
assert hash_ribbons["ml_raw_weight"] == 0.01
|
|
assert hash_ribbons["ml_weight"] == round(0.01 / 1.01, 4)
|
|
|
|
|
|
def test_score_all_ml_preserves_classic_fallback_when_weights_missing(monkeypatch):
|
|
monkeypatch.setattr(engine, "load_ml_weights", lambda: {})
|
|
|
|
scored = engine.score_all_ml(_complete_metrics())
|
|
|
|
assert scored["ml_mode"] is False
|
|
assert scored["ml_error"]
|
|
assert scored["ml_artifact"]["valid"] is False
|
|
assert "classic_score" not in scored
|