22from pathlib import Path
33from types import SimpleNamespace
44
5+ import pandas as pd
6+
57
68ROOT = Path (__file__ ).resolve ().parents [1 ]
79if str (ROOT ) not in sys .path :
810 sys .path .insert (0 , str (ROOT ))
11+ HK_STRATEGIES_SRC = ROOT .parent / "HkEquityStrategies" / "src"
12+ if str (HK_STRATEGIES_SRC ) not in sys .path :
13+ sys .path .insert (0 , str (HK_STRATEGIES_SRC ))
914
1015from application .runtime_strategy_adapters import build_runtime_strategy_adapters
1116
@@ -35,7 +40,9 @@ def build_price_history(self, market_data_port, symbol):
3540 signal_text_fn = lambda icon : f"signal:{ icon } " ,
3641 translator = lambda key , ** _kwargs : key ,
3742 broker_adapters = FakeBrokerAdapters (),
38- calculate_rotation_indicators_fn = lambda * _args , ** _kwargs : (_ for _ in ()).throw (AssertionError ("unexpected fallback" )),
43+ calculate_rotation_indicators_fn = lambda * _args , ** _kwargs : (
44+ _ for _ in ()
45+ ).throw (AssertionError ("unexpected fallback" )),
3946 build_strategy_evaluation_inputs_fn = lambda ** _kwargs : {},
4047 map_strategy_decision_to_plan_fn = lambda * _args , ** _kwargs : {},
4148 )
@@ -52,6 +59,58 @@ def build_price_history(self, market_data_port, symbol):
5259 }
5360
5461
62+ def test_runtime_strategy_adapters_materialize_hk_direct_market_history ():
63+ observed = {}
64+
65+ class FakeBrokerAdapters :
66+ strategy_symbols = ("02800" , "02834" )
67+
68+ def build_market_data_port (self , quote_context ):
69+ observed ["market_data_port_context" ] = quote_context
70+ return "market-data-port"
71+
72+ def build_market_history_loader (self , market_data_port ):
73+ observed ["market_history_loader_port" ] = market_data_port
74+
75+ def load_market_history (_broker_client , symbol ):
76+ observed .setdefault ("history_calls" , []).append (symbol )
77+ return pd .Series (
78+ [10.0 , 11.0 ],
79+ index = pd .to_datetime (["2026-05-29" , "2026-06-01" ], utc = True ),
80+ dtype = float ,
81+ )
82+
83+ return load_market_history
84+
85+ adapters = build_runtime_strategy_adapters (
86+ strategy_runtime = SimpleNamespace (evaluate = lambda ** _kwargs : None ),
87+ strategy_profile = "hk_listed_global_etf_rotation" ,
88+ strategy_runtime_config = {"universe_symbols" : ("02800" , "02834" )},
89+ available_inputs = ("market_history" ,),
90+ benchmark_symbol = "QQQ" ,
91+ signal_text_fn = lambda icon : f"signal:{ icon } " ,
92+ translator = lambda key , ** _kwargs : key ,
93+ broker_adapters = FakeBrokerAdapters (),
94+ calculate_rotation_indicators_fn = lambda * _args , ** _kwargs : (
95+ _ for _ in ()
96+ ).throw (AssertionError ("unexpected fallback" )),
97+ build_strategy_evaluation_inputs_fn = lambda ** _kwargs : {},
98+ map_strategy_decision_to_plan_fn = lambda * _args , ** _kwargs : {},
99+ )
100+
101+ result = adapters .calculate_strategy_indicators ("quote-context" )
102+
103+ assert observed ["market_data_port_context" ] == "quote-context"
104+ assert observed ["market_history_loader_port" ] == "market-data-port"
105+ assert observed ["history_calls" ] == ["02800" , "02834" ]
106+ assert sorted (result ["market_history" ]) == ["02800" , "02834" ]
107+ assert result ["market_history" ]["02800" ][0 ]["date" ] == pd .Timestamp (
108+ "2026-05-29" ,
109+ tz = "UTC" ,
110+ )
111+ assert result ["market_history" ]["02800" ][0 ]["close" ] == 10.0
112+
113+
55114def test_runtime_strategy_adapters_fall_back_to_rotation_indicators ():
56115 observed = {}
57116
0 commit comments