from __future__ import annotations import sys import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) # Python Port of calcAlphaLeadRow_'s lateChaseRisk calculation def calculate_late_chase_risk( close: float, ma20: float, val_surge_pct: float | None, anti_distribution_state: str | None, dart_risk_status: str | None, high_52w: float | None, volume: float | None, avg_volume_5d: float | None ) -> int: close_vs_ma20_pct = (close / ma20 - 1.0) * 100.0 if close > 0 and ma20 > 0 else None late_chase_risk = 0 if close_vs_ma20_pct is not None: if close_vs_ma20_pct > 10.0: late_chase_risk += 60 elif close_vs_ma20_pct > 6.0: late_chase_risk += 25 elif close_vs_ma20_pct > 3.0: late_chase_risk += 10 val_surge_pct = float(val_surge_pct) if val_surge_pct not in (None, "") else None if val_surge_pct is not None: if val_surge_pct >= 60.0: late_chase_risk += 25 elif val_surge_pct >= 35.0: late_chase_risk += 10 if anti_distribution_state == "BLOCK_BUY": late_chase_risk += 40 if dart_risk_status is not None and dart_risk_status != "OK": late_chase_risk += 30 # N2: Volume breakout unconfirmed check (+15) n2_high52w = float(high_52w) if high_52w not in (None, "") and float(high_52w) > 0 else 0.0 n2_vol = float(volume) if volume not in (None, "") else 0.0 n2_avg_vol = float(avg_volume_5d) if avg_volume_5d not in (None, "") else 0.0 if n2_high52w > 0.0 and close > 0.0 and close >= n2_high52w * 0.97: if n2_avg_vol > 0.0 and n2_vol < n2_avg_vol * 1.2: late_chase_risk += 15 return min(100, max(0, late_chase_risk)) class TestLateChaseRiskParity(unittest.TestCase): def test_close_vs_ma20_ranges_parity(self): # close=11100, ma20=10000 -> 11% extension (expected +60) score_11pct = calculate_late_chase_risk( close=11100, ma20=10000, val_surge_pct=0, anti_distribution_state="PASS", dart_risk_status="OK", high_52w=None, volume=None, avg_volume_5d=None ) self.assertEqual(score_11pct, 60) # close=10700, ma20=10000 -> 7% extension (expected +25) score_7pct = calculate_late_chase_risk( close=10700, ma20=10000, val_surge_pct=0, anti_distribution_state="PASS", dart_risk_status="OK", high_52w=None, volume=None, avg_volume_5d=None ) self.assertEqual(score_7pct, 25) def test_multi_factor_late_chase_cap_parity(self): # 11% extension (+60) + Value surge extreme (+25) + Distribution block (+40) = 125 -> capped at 100 score_extreme = calculate_late_chase_risk( close=11100, ma20=10000, val_surge_pct=65.0, anti_distribution_state="BLOCK_BUY", dart_risk_status="OK", high_52w=None, volume=None, avg_volume_5d=None ) self.assertEqual(score_extreme, 100) def test_unconfirmed_volume_breakout_chase_parity(self): # close=9800, high52w=10000 (close >= 97%), volume=100, avg_vol=100 (volume < 1.2*avg_vol -> expected +15) # close=10100, ma20=10000 (1% extension -> 0) score_breakout = calculate_late_chase_risk( close=9800, ma20=10000, val_surge_pct=10.0, anti_distribution_state="PASS", dart_risk_status="OK", high_52w=10000, volume=100, avg_volume_5d=100 ) self.assertEqual(score_breakout, 15) if __name__ == "__main__": unittest.main()