|
|
@@ -88,6 +88,72 @@ class SignalLayerTests(unittest.TestCase):
|
|
|
self.assertTrue(pd.isna(signals.loc["chinext50", "selection_rank"]))
|
|
|
self.assertGreater(signals.loc["sse50", "final_score"], signals.loc["hs300", "final_score"])
|
|
|
|
|
|
+ def test_custom_risk_penalty_can_change_selection_order(self) -> None:
|
|
|
+ frame = pd.DataFrame(
|
|
|
+ [
|
|
|
+ {
|
|
|
+ "instrument": "high_beta",
|
|
|
+ "trade_date": pd.Timestamp("2020-01-10"),
|
|
|
+ "close": 110,
|
|
|
+ "daily_return": 0.01,
|
|
|
+ "ret_5d": 0.12,
|
|
|
+ "ret_10d": 0.12,
|
|
|
+ "ret_20d": 0.12,
|
|
|
+ "ret_60d": 0.12,
|
|
|
+ "ma_20": 100,
|
|
|
+ "ma_60": 95,
|
|
|
+ "vol_10d": 0.30,
|
|
|
+ "vol_20d": 0.30,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "instrument": "defensive",
|
|
|
+ "trade_date": pd.Timestamp("2020-01-10"),
|
|
|
+ "close": 108,
|
|
|
+ "daily_return": 0.01,
|
|
|
+ "ret_5d": 0.11,
|
|
|
+ "ret_10d": 0.11,
|
|
|
+ "ret_20d": 0.11,
|
|
|
+ "ret_60d": 0.11,
|
|
|
+ "ma_20": 100,
|
|
|
+ "ma_60": 95,
|
|
|
+ "vol_10d": 0.05,
|
|
|
+ "vol_20d": 0.05,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "instrument": "filler_a",
|
|
|
+ "trade_date": pd.Timestamp("2020-01-10"),
|
|
|
+ "close": 90,
|
|
|
+ "daily_return": -0.01,
|
|
|
+ "ret_5d": 0.02,
|
|
|
+ "ret_10d": 0.02,
|
|
|
+ "ret_20d": 0.02,
|
|
|
+ "ret_60d": 0.02,
|
|
|
+ "ma_20": 100,
|
|
|
+ "ma_60": 101,
|
|
|
+ "vol_10d": 0.10,
|
|
|
+ "vol_20d": 0.10,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "instrument": "filler_b",
|
|
|
+ "trade_date": pd.Timestamp("2020-01-10"),
|
|
|
+ "close": 89,
|
|
|
+ "daily_return": -0.01,
|
|
|
+ "ret_5d": 0.01,
|
|
|
+ "ret_10d": 0.01,
|
|
|
+ "ret_20d": 0.01,
|
|
|
+ "ret_60d": 0.01,
|
|
|
+ "ma_20": 100,
|
|
|
+ "ma_60": 101,
|
|
|
+ "vol_10d": 0.11,
|
|
|
+ "vol_20d": 0.11,
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ )
|
|
|
+ low_penalty = build_signal_panel(frame, top_n=1, risk_penalty_multiplier=0.0).set_index("instrument")
|
|
|
+ high_penalty = build_signal_panel(frame, top_n=1, risk_penalty_multiplier=1.0).set_index("instrument")
|
|
|
+ self.assertEqual(low_penalty.loc["high_beta", "selection_rank"], 1)
|
|
|
+ self.assertEqual(high_penalty.loc["defensive", "selection_rank"], 1)
|
|
|
+
|
|
|
def test_top1_top2_and_empty_allocation(self) -> None:
|
|
|
base_signals = build_signal_panel(make_signal_input(), top_n=2)
|
|
|
|