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)) from tools.build_distribution_risk_score_v2 import calculate_distribution_risk class TestDistributionRiskParity(unittest.TestCase): def test_distribution_risk_parity_scenarios(self): # Scenario 1: Smart Money Outflow only row_1 = { "close": 10000, "ma20": 10000, "frg_5d": -100, "inst_5d": -200, } res_1 = calculate_distribution_risk(row_1, kospi_ret_5d=0.0) self.assertEqual(res_1["distribution_risk_score"], 30) self.assertIn("smart_money_outflow", res_1["reason_codes"]) self.assertEqual(res_1["anti_distribution_state"], "PASS") # Scenario 2: High upper wick and low flow credit under priceAboveMa20 row_2 = { "close": 12000, "ma20": 10000, # priceAboveMa20 = True "high": 15000, "low": 10000, # upperWickRatio = (15000-12000)/5000 = 3000/5000 = 0.60 >= 0.45 "flow_credit": 0.35, # flow_credit < 0.40 } res_2 = calculate_distribution_risk(row_2, kospi_ret_5d=0.0) self.assertIn("upper_wick_distribution", res_2["reason_codes"]) self.assertIn("flow_credit_low", res_2["reason_codes"]) # score = 15 (upper wick) + 20 (flow credit low) = 35 self.assertEqual(res_2["distribution_risk_score"], 35) # Scenario 3: Trim Review threshold (score >= 55) row_3 = { "close": 10000, "ma20": 10000, "frg_5d": -100, "inst_5d": -200, # +30 "flow_credit": 0.30, # +20 "volume": 70, "avg_volume_5d": 100, # volume < 80% of avg_vol_5d -> +20 } res_3 = calculate_distribution_risk(row_3, kospi_ret_5d=0.0) # score = 30 + 20 + 20 = 70 (BLOCK_BUY) self.assertEqual(res_3["distribution_risk_score"], 70) self.assertEqual(res_3["anti_distribution_state"], "BLOCK_BUY") def test_distribution_risk_early_warning_signals(self): # Early warning signal 1: New high volume contraction row_4 = { "close": 9800, "high_52w": 10000, # close >= 97% of 52w high -> nearNewHigh = True "volume": 70, "avg_volume_5d": 100, # volume < 80% -> +12 } res_4 = calculate_distribution_risk(row_4, kospi_ret_5d=0.0) self.assertIn("new_high_volume_contraction", res_4["reason_codes"]) self.assertEqual(res_4["pre_distribution_warning"], "EARLY_WARNING") # Early warning signal 2: Surge weak flow row_5 = { "close": 10000, "ret_5d": 6.0, # ret5d >= 5 "flow_credit": 0.40, # flow_credit < 0.45 -> +10 } res_5 = calculate_distribution_risk(row_5, kospi_ret_5d=0.0) self.assertIn("surge_weak_flow", res_5["reason_codes"]) self.assertEqual(res_5["pre_distribution_warning"], "EARLY_WARNING") if __name__ == "__main__": unittest.main()