36 lines
1.1 KiB
Python
36 lines
1.1 KiB
Python
"""Canonical score version, brackets, and assessment semantics."""
|
|
|
|
SCORE_VERSION = "accumulation-score-v2"
|
|
|
|
# Half-open intervals [low, high), except the final bracket includes 100.
|
|
# Keep labels canonical because they are persisted in live and backtest output.
|
|
SCORE_BRACKETS = [
|
|
(0, 20, "EXTREME CAUTION"),
|
|
(20, 35, "CAUTION — OVERHEATED"),
|
|
(35, 50, "NEUTRAL"),
|
|
(50, 65, "MODERATE OPPORTUNITY"),
|
|
(65, 80, "STRONG ACCUMULATION ZONE"),
|
|
(80, 100, "EXTREME ACCUMULATION ZONE"),
|
|
]
|
|
|
|
|
|
def score_in_bracket(score, bracket):
|
|
"""Return whether a 0-100 score belongs to a canonical bracket."""
|
|
low, high, _ = bracket
|
|
if not 0 <= score <= 100:
|
|
return False
|
|
return low <= score < high or (high == 100 and score == 100)
|
|
|
|
|
|
def bracket_for_score(score):
|
|
"""Return the one canonical bracket for a 0-100 score."""
|
|
for bracket in SCORE_BRACKETS:
|
|
if score_in_bracket(score, bracket):
|
|
return bracket
|
|
raise ValueError(f"score must be between 0 and 100, got {score!r}")
|
|
|
|
|
|
def assessment_for_score(score):
|
|
"""Return the canonical assessment label for a score."""
|
|
return bracket_for_score(score)[2]
|