Skip to content

Commit 0690a9f

Browse files
Pigbibicodex
andauthored
feat(risk): represent split-adjusted price basis (#298)
Co-authored-by: Codex <noreply@openai.com>
1 parent 0922836 commit 0690a9f

3 files changed

Lines changed: 16 additions & 1 deletion

File tree

docs/qsl_long_horizon_risk_observation_v2.zh-CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ v2 复用已验证的 v1 计算器,**仅**在以下范围输出 `ADVISORY_RECO
7272
| 多策略组合 | `PORTFOLIO_COMPOSER_REQUIRED` | 必须重算相关性、边际风险贡献和组合净收益 |
7373
| DCA 或外部现金流 | `CASHFLOW_COMPOSER_REQUIRED` | 必须使用现金流匹配、时间一致的路径 |
7474
| 混合、现金或绝对收益基准 | `BENCHMARK_POLICY_COMPOSER_REQUIRED` | 不能伪装为权益无杠杆基准 |
75-
| 非净成本总收益基准口径 | `BENCHMARK_RETURN_BASIS_COMPOSER_REQUIRED` | 当前回撤比较不具可比性 |
75+
| 非净成本总收益基准口径(包括 `SPLIT_ADJUSTED_PRICE_RETURN`| `BENCHMARK_RETURN_BASIS_COMPOSER_REQUIRED` | 当前回撤比较不具可比性;拆分复权收盘价不得伪称总收益 |
7676

7777
`PARKED` 没有尺度、最大回撤或前沿;它不是失败后的默认继续运行,更不能触发实盘动作。
7878

python/scripts/long_horizon_risk_composer_v2.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@
105105
_RETURN_BASES = {
106106
"TOTAL_RETURN_NET_OF_COST",
107107
"TIME_WEIGHTED_TOTAL_RETURN",
108+
# Some verified research lanes intentionally retain only split-adjusted
109+
# closes. They must be representable without being mislabeled as a total
110+
# return series; the generic v1-compatible composer still parks them.
111+
"SPLIT_ADJUSTED_PRICE_RETURN",
108112
"CASHFLOW_MATCHED_RETURN",
109113
}
110114
_RISK_FACTORS = {

python/tests/test_long_horizon_risk_observation_v2.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,17 @@ def test_cashflow_matched_strategies_cannot_be_coerced_to_time_weighted_linear_m
168168
with self.assertRaisesRegex(composer.LongHorizonRiskComposerError, "requires CASHFLOW_MATCHED_RETURN"):
169169
composer_v2.validate_long_horizon_risk_observation_v2(invalid)
170170

171+
def test_split_adjusted_price_benchmark_is_represented_but_not_mislabeled_as_total_return(self):
172+
observation = self._observation()
173+
observation["benchmark_policy"]["return_basis"] = "SPLIT_ADJUSTED_PRICE_RETURN"
174+
observation["observation_sha256"] = composer_v2.calculate_risk_observation_v2_sha256(observation)
175+
176+
recommendation = composer_v2.compose_long_horizon_risk_recommendation_v2(observation, self._profile())
177+
178+
self.assertEqual(recommendation["status"], "PARKED")
179+
self.assertEqual(recommendation["reason_codes"], ["BENCHMARK_RETURN_BASIS_COMPOSER_REQUIRED"])
180+
self.assertIsNone(recommendation["recommended_scale_bps"])
181+
171182

172183
if __name__ == "__main__":
173184
unittest.main()

0 commit comments

Comments
 (0)