Skip to content

Commit ced5db4

Browse files
authored
Add IBKR HK low-vol dividend switch plan
* Enable HK low-vol dividend switch plan * Update HK strategy live-enable dependency * Add HK low-vol dividend notification names * Pin HK low-vol strategy release
1 parent fdab0a4 commit ced5db4

5 files changed

Lines changed: 60 additions & 6 deletions

File tree

notifications/telegram.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@
140140
"strategy_name_mega_cap_leader_rotation_top50_balanced": "Mega Cap Top50 平衡龙头轮动",
141141
"strategy_name_nasdaq_sp500_smart_dca": "纳斯达克 / 标普智能定投",
142142
"strategy_name_hk_listed_global_etf_rotation": "港股上市全球 ETF 轮动",
143+
"strategy_name_hk_high_dividend_low_vol_trend": "港股高股息低波趋势",
144+
"strategy_name_hk_low_vol_dividend_quality": "港股低波股息质量",
143145
"strategy_name_tqqq_growth_income": "TQQQ 增长收益",
144146
"strategy_name_soxl_soxx_trend_income": "SOXL/SOXX 半导体趋势收益",
145147
},
@@ -279,6 +281,8 @@
279281
"strategy_name_mega_cap_leader_rotation_top50_balanced": "Mega Cap Leader Rotation Top50 Balanced",
280282
"strategy_name_nasdaq_sp500_smart_dca": "Nasdaq/S&P 500 Smart DCA",
281283
"strategy_name_hk_listed_global_etf_rotation": "HK-listed Global ETF Rotation",
284+
"strategy_name_hk_high_dividend_low_vol_trend": "HK High Dividend Low-Volatility Trend",
285+
"strategy_name_hk_low_vol_dividend_quality": "HK Low-Volatility Dividend Quality",
282286
"strategy_name_tqqq_growth_income": "TQQQ Growth Income",
283287
"strategy_name_soxl_soxx_trend_income": "SOXL/SOXX Semiconductor Trend Income",
284288
},

requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
flask
22
gunicorn
3-
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@v0.7.35
4-
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@v0.7.49
5-
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@71141ce9e8a343cec8e2140994071eea66422bc6
3+
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@v0.7.36
4+
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@v0.7.50
5+
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@hk-low-vol-dividend-quality-20260603
66
pandas
77
numpy
88
requests

scripts/print_strategy_switch_env_plan.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ def _should_add_local_src(candidate: Path) -> bool:
8181
]
8282

8383

84+
def _feature_snapshot_filenames(profile: str, snapshot_contract_version: str | None) -> tuple[str, str]:
85+
suffix = "factor_snapshot" if ".factor_snapshot." in str(snapshot_contract_version or "") else "feature_snapshot"
86+
snapshot_filename = f"{profile}_{suffix}_latest.csv"
87+
return snapshot_filename, f"{snapshot_filename}.manifest.json"
88+
89+
8490
def build_switch_plan(
8591
profile: str,
8692
*,
@@ -223,10 +229,12 @@ def build_switch_plan(
223229

224230
hints: dict[str, str] = {}
225231
if requires_feature_snapshot:
226-
hints["feature_snapshot_filename"] = f"{definition.profile}_feature_snapshot_latest.csv"
227-
hints["feature_snapshot_manifest_filename"] = (
228-
f"{definition.profile}_feature_snapshot_latest.csv.manifest.json"
232+
snapshot_filename, manifest_filename = _feature_snapshot_filenames(
233+
definition.profile,
234+
runtime_requirements.get("snapshot_contract_version"),
229235
)
236+
hints["feature_snapshot_filename"] = snapshot_filename
237+
hints["feature_snapshot_manifest_filename"] = manifest_filename
230238
if artifact_paths.bundled_config_path is not None:
231239
hints["bundled_strategy_config_path"] = str(artifact_paths.bundled_config_path)
232240

tests/test_notifications.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ def test_strategy_display_name_translates_new_live_profiles():
8686
assert en_name("nasdaq_sp500_smart_dca") == "Nasdaq/S&P 500 Smart DCA"
8787
assert zh_name("hk_listed_global_etf_rotation") == "港股上市全球 ETF 轮动"
8888
assert en_name("hk_listed_global_etf_rotation") == "HK-listed Global ETF Rotation"
89+
assert zh_name("hk_high_dividend_low_vol_trend") == "港股高股息低波趋势"
90+
assert en_name("hk_high_dividend_low_vol_trend") == "HK High Dividend Low-Volatility Trend"
91+
assert zh_name("hk_low_vol_dividend_quality") == "港股低波股息质量"
92+
assert en_name("hk_low_vol_dividend_quality") == "HK Low-Volatility Dividend Quality"
8993

9094

9195
def test_supported_strategy_profiles_have_translated_names():

tests/test_runtime_config_support.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@
6363
"soxl_soxx_trend_income",
6464
"tech_communication_pullback_enhancement",
6565
"tqqq_growth_income",
66+
"hk_high_dividend_low_vol_trend",
6667
"hk_listed_global_etf_rotation",
68+
"hk_low_vol_dividend_quality",
6769
}
6870
)
6971
HK_DISABLED_PROFILES = frozenset(
@@ -1058,6 +1060,42 @@ def test_print_strategy_switch_env_plan_for_mega_cap_top50_balanced_profile():
10581060
assert plan["hints"]["feature_snapshot_filename"] == "mega_cap_leader_rotation_top50_balanced_feature_snapshot_latest.csv"
10591061

10601062

1063+
def test_print_strategy_switch_env_plan_for_hk_low_vol_dividend_quality_profile():
1064+
result = subprocess.run(
1065+
[
1066+
sys.executable,
1067+
str(SWITCH_PLAN_SCRIPT_PATH),
1068+
"--profile",
1069+
"hk_low_vol_dividend_quality",
1070+
"--dry-run-only",
1071+
"--deployment-selector",
1072+
"hk-verify",
1073+
"--account-scope",
1074+
"HK",
1075+
"--service-name",
1076+
"interactive-brokers-hk-verify-service",
1077+
"--json",
1078+
],
1079+
check=True,
1080+
capture_output=True,
1081+
text=True,
1082+
)
1083+
1084+
plan = json.loads(result.stdout)
1085+
assert plan["canonical_profile"] == "hk_low_vol_dividend_quality"
1086+
assert plan["enabled"] is True
1087+
assert plan["profile_group"] == "snapshot_backed"
1088+
assert plan["input_mode"] == "feature_snapshot"
1089+
assert plan["snapshot_contract_version"] == "hk_low_vol_dividend_quality.factor_snapshot.v1"
1090+
assert plan["set_env"]["IBKR_DRY_RUN_ONLY"] == "true"
1091+
assert plan["set_env"]["IBKR_FEATURE_SNAPSHOT_PATH"] == "<required>"
1092+
assert plan["set_env"]["IBKR_FEATURE_SNAPSHOT_MANIFEST_PATH"] == "<required>"
1093+
assert plan["hints"]["feature_snapshot_filename"] == "hk_low_vol_dividend_quality_factor_snapshot_latest.csv"
1094+
assert plan["hints"]["feature_snapshot_manifest_filename"] == (
1095+
"hk_low_vol_dividend_quality_factor_snapshot_latest.csv.manifest.json"
1096+
)
1097+
1098+
10611099
@pytest.mark.parametrize("profile", sorted(HK_DISABLED_PROFILES))
10621100
def test_print_strategy_switch_env_plan_rejects_hk_disabled_profiles(profile):
10631101
result = subprocess.run(

0 commit comments

Comments
 (0)