finish experiment 57 (exp/57-signal-quality-gate-gate-trades-based-on)
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
window,gate,start,end,trade_dates,gate_open,gate_closed,trip_rate,base_ann,base_sharpe,base_maxDD,gated_ann,gated_sharpe,gated_maxDD
|
||||
2026,hitrate_5d_0.50,2026-01-04,2026-08-19,157,92,65,0.414,0.255023,1.4465,-0.080671,0.649911,5.7351,-0.031795
|
||||
2026,hitrate_5d_0.60,2026-01-04,2026-08-19,157,49,108,0.6879,0.255023,1.4465,-0.080671,0.489313,6.0111,-0.014864
|
||||
2026,hitrate_5d_0.70,2026-01-04,2026-08-19,157,15,142,0.9045,0.255023,1.4465,-0.080671,0.229523,4.3611,-0.002029
|
||||
2026,hitrate_10d_0.50,2026-01-04,2026-08-19,157,109,48,0.3057,0.255023,1.4465,-0.080671,0.335822,2.6391,-0.060142
|
||||
2026,hitrate_10d_0.60,2026-01-04,2026-08-19,157,36,121,0.7707,0.255023,1.4465,-0.080671,0.251108,3.8579,-0.026842
|
||||
2026,hitrate_10d_0.70,2026-01-04,2026-08-19,157,9,148,0.9427,0.255023,1.4465,-0.080671,0.039314,1.3415,-0.012061
|
||||
2026,hitrate_20d_0.40,2026-01-04,2026-08-19,157,157,0,0.0,0.255023,1.4465,-0.080671,0.255023,1.4465,-0.080671
|
||||
2026,hitrate_20d_0.50,2026-01-04,2026-08-19,157,118,39,0.2484,0.255023,1.4465,-0.080671,0.343819,2.5185,-0.06259
|
||||
2026,hitrate_20d_0.60,2026-01-04,2026-08-19,157,28,129,0.8217,0.255023,1.4465,-0.080671,0.020214,0.3601,-0.028489
|
||||
2025,hitrate_5d_0.50,2025-01-02,2025-12-31,250,153,97,0.388,0.177515,0.8573,-0.217417,0.72055,6.9599,-0.048495
|
||||
2025,hitrate_5d_0.60,2025-01-02,2025-12-31,250,90,160,0.64,0.177515,0.8573,-0.217417,0.48564,5.3623,-0.046673
|
||||
2025,hitrate_5d_0.70,2025-01-02,2025-12-31,250,39,211,0.844,0.177515,0.8573,-0.217417,0.249221,4.9448,-0.009619
|
||||
2025,hitrate_10d_0.50,2025-01-02,2025-12-31,250,170,80,0.32,0.177515,0.8573,-0.217417,0.435847,3.5614,-0.055776
|
||||
2025,hitrate_10d_0.60,2025-01-02,2025-12-31,250,66,184,0.736,0.177515,0.8573,-0.217417,0.324574,5.2279,-0.016028
|
||||
2025,hitrate_10d_0.70,2025-01-02,2025-12-31,250,14,236,0.944,0.177515,0.8573,-0.217417,0.043002,1.4635,-0.010154
|
||||
2025,hitrate_20d_0.40,2025-01-02,2025-12-31,250,247,3,0.012,0.177515,0.8573,-0.217417,0.202949,0.9896,-0.217417
|
||||
2025,hitrate_20d_0.50,2025-01-02,2025-12-31,250,177,73,0.292,0.177515,0.8573,-0.217417,0.340772,2.8303,-0.071731
|
||||
2025,hitrate_20d_0.60,2025-01-02,2025-12-31,250,48,202,0.808,0.177515,0.8573,-0.217417,0.266028,4.1749,-0.021527
|
||||
2024,hitrate_5d_0.50,2024-01-02,2024-12-31,253,139,114,0.4506,0.08229,0.5594,-0.10685,0.30351,2.4275,-0.088219
|
||||
2024,hitrate_5d_0.60,2024-01-02,2024-12-31,253,71,182,0.7194,0.08229,0.5594,-0.10685,0.265365,2.4908,-0.078304
|
||||
2024,hitrate_5d_0.70,2024-01-02,2024-12-31,253,19,234,0.9249,0.08229,0.5594,-0.10685,0.143125,3.377,-0.003364
|
||||
2024,hitrate_10d_0.50,2024-01-02,2024-12-31,253,157,96,0.3794,0.08229,0.5594,-0.10685,0.277421,2.5767,-0.043146
|
||||
2024,hitrate_10d_0.60,2024-01-02,2024-12-31,253,37,216,0.8538,0.08229,0.5594,-0.10685,0.207161,3.6083,-0.011122
|
||||
2024,hitrate_10d_0.70,2024-01-02,2024-12-31,253,7,246,0.9723,0.08229,0.5594,-0.10685,0.031785,1.7408,-0.002083
|
||||
2024,hitrate_20d_0.40,2024-01-02,2024-12-31,253,247,6,0.0237,0.08229,0.5594,-0.10685,0.078007,0.5342,-0.10685
|
||||
2024,hitrate_20d_0.50,2024-01-02,2024-12-31,253,177,76,0.3004,0.08229,0.5594,-0.10685,0.210672,1.8469,-0.056207
|
||||
2024,hitrate_20d_0.60,2024-01-02,2024-12-31,253,13,240,0.9486,0.08229,0.5594,-0.10685,0.005953,0.3039,-0.014443
|
||||
2023,hitrate_5d_0.50,2023-01-03,2023-12-29,250,139,111,0.444,-0.047644,-0.2738,-0.197856,0.546654,4.2124,-0.035676
|
||||
2023,hitrate_5d_0.60,2023-01-03,2023-12-29,250,79,171,0.684,-0.047644,-0.2738,-0.197856,0.556291,5.1026,-0.025449
|
||||
2023,hitrate_5d_0.70,2023-01-03,2023-12-29,250,34,216,0.864,-0.047644,-0.2738,-0.197856,0.404269,4.4477,-0.013842
|
||||
2023,hitrate_10d_0.50,2023-01-03,2023-12-29,250,148,102,0.408,-0.047644,-0.2738,-0.197856,0.459869,3.5211,-0.046921
|
||||
2023,hitrate_10d_0.60,2023-01-03,2023-12-29,250,62,188,0.752,-0.047644,-0.2738,-0.197856,0.368232,4.0148,-0.022983
|
||||
2023,hitrate_10d_0.70,2023-01-03,2023-12-29,250,13,237,0.948,-0.047644,-0.2738,-0.197856,0.091481,2.1324,-0.010866
|
||||
2023,hitrate_20d_0.40,2023-01-03,2023-12-29,250,237,13,0.052,-0.047644,-0.2738,-0.197856,0.06639,0.3895,-0.146704
|
||||
2023,hitrate_20d_0.50,2023-01-03,2023-12-29,250,149,101,0.404,-0.047644,-0.2738,-0.197856,0.366016,2.5969,-0.063998
|
||||
2023,hitrate_20d_0.60,2023-01-03,2023-12-29,250,40,210,0.84,-0.047644,-0.2738,-0.197856,0.149844,2.2645,-0.032267
|
||||
2021,hitrate_5d_0.50,2021-01-04,2021-12-31,252,145,107,0.4246,0.18367,1.0979,-0.102651,0.556615,5.9167,-0.028062
|
||||
2021,hitrate_5d_0.60,2021-01-04,2021-12-31,252,68,184,0.7302,0.18367,1.0979,-0.102651,0.351867,5.8468,-0.011638
|
||||
2021,hitrate_5d_0.70,2021-01-04,2021-12-31,252,26,226,0.8968,0.18367,1.0979,-0.102651,0.148417,3.9593,-0.0041
|
||||
2021,hitrate_10d_0.50,2021-01-04,2021-12-31,252,163,89,0.3532,0.18367,1.0979,-0.102651,0.465023,4.2342,-0.040569
|
||||
2021,hitrate_10d_0.60,2021-01-04,2021-12-31,252,51,201,0.7976,0.18367,1.0979,-0.102651,0.21802,4.7978,-0.011134
|
||||
2021,hitrate_10d_0.70,2021-01-04,2021-12-31,252,13,239,0.9484,0.18367,1.0979,-0.102651,0.06108,2.7055,-0.000262
|
||||
2021,hitrate_20d_0.40,2021-01-04,2021-12-31,252,252,0,0.0,0.18367,1.0979,-0.102651,0.18367,1.0979,-0.102651
|
||||
2021,hitrate_20d_0.50,2021-01-04,2021-12-31,252,166,86,0.3413,0.18367,1.0979,-0.102651,0.289756,2.5787,-0.049244
|
||||
2021,hitrate_20d_0.60,2021-01-04,2021-12-31,252,38,214,0.8492,0.18367,1.0979,-0.102651,0.104685,2.394,-0.015084
|
||||
|
@@ -0,0 +1,722 @@
|
||||
[
|
||||
{
|
||||
"window": "2026",
|
||||
"gate": "hitrate_5d_0.50",
|
||||
"start": "2026-01-04",
|
||||
"end": "2026-08-19",
|
||||
"trade_dates": 157,
|
||||
"gate_open": 92,
|
||||
"gate_closed": 65,
|
||||
"trip_rate": 0.414,
|
||||
"base_ann": 0.255023,
|
||||
"base_sharpe": 1.4465,
|
||||
"base_maxDD": -0.080671,
|
||||
"gated_ann": 0.649911,
|
||||
"gated_sharpe": 5.7351,
|
||||
"gated_maxDD": -0.031795
|
||||
},
|
||||
{
|
||||
"window": "2026",
|
||||
"gate": "hitrate_5d_0.60",
|
||||
"start": "2026-01-04",
|
||||
"end": "2026-08-19",
|
||||
"trade_dates": 157,
|
||||
"gate_open": 49,
|
||||
"gate_closed": 108,
|
||||
"trip_rate": 0.6879,
|
||||
"base_ann": 0.255023,
|
||||
"base_sharpe": 1.4465,
|
||||
"base_maxDD": -0.080671,
|
||||
"gated_ann": 0.489313,
|
||||
"gated_sharpe": 6.0111,
|
||||
"gated_maxDD": -0.014864
|
||||
},
|
||||
{
|
||||
"window": "2026",
|
||||
"gate": "hitrate_5d_0.70",
|
||||
"start": "2026-01-04",
|
||||
"end": "2026-08-19",
|
||||
"trade_dates": 157,
|
||||
"gate_open": 15,
|
||||
"gate_closed": 142,
|
||||
"trip_rate": 0.9045,
|
||||
"base_ann": 0.255023,
|
||||
"base_sharpe": 1.4465,
|
||||
"base_maxDD": -0.080671,
|
||||
"gated_ann": 0.229523,
|
||||
"gated_sharpe": 4.3611,
|
||||
"gated_maxDD": -0.002029
|
||||
},
|
||||
{
|
||||
"window": "2026",
|
||||
"gate": "hitrate_10d_0.50",
|
||||
"start": "2026-01-04",
|
||||
"end": "2026-08-19",
|
||||
"trade_dates": 157,
|
||||
"gate_open": 109,
|
||||
"gate_closed": 48,
|
||||
"trip_rate": 0.3057,
|
||||
"base_ann": 0.255023,
|
||||
"base_sharpe": 1.4465,
|
||||
"base_maxDD": -0.080671,
|
||||
"gated_ann": 0.335822,
|
||||
"gated_sharpe": 2.6391,
|
||||
"gated_maxDD": -0.060142
|
||||
},
|
||||
{
|
||||
"window": "2026",
|
||||
"gate": "hitrate_10d_0.60",
|
||||
"start": "2026-01-04",
|
||||
"end": "2026-08-19",
|
||||
"trade_dates": 157,
|
||||
"gate_open": 36,
|
||||
"gate_closed": 121,
|
||||
"trip_rate": 0.7707,
|
||||
"base_ann": 0.255023,
|
||||
"base_sharpe": 1.4465,
|
||||
"base_maxDD": -0.080671,
|
||||
"gated_ann": 0.251108,
|
||||
"gated_sharpe": 3.8579,
|
||||
"gated_maxDD": -0.026842
|
||||
},
|
||||
{
|
||||
"window": "2026",
|
||||
"gate": "hitrate_10d_0.70",
|
||||
"start": "2026-01-04",
|
||||
"end": "2026-08-19",
|
||||
"trade_dates": 157,
|
||||
"gate_open": 9,
|
||||
"gate_closed": 148,
|
||||
"trip_rate": 0.9427,
|
||||
"base_ann": 0.255023,
|
||||
"base_sharpe": 1.4465,
|
||||
"base_maxDD": -0.080671,
|
||||
"gated_ann": 0.039314,
|
||||
"gated_sharpe": 1.3415,
|
||||
"gated_maxDD": -0.012061
|
||||
},
|
||||
{
|
||||
"window": "2026",
|
||||
"gate": "hitrate_20d_0.40",
|
||||
"start": "2026-01-04",
|
||||
"end": "2026-08-19",
|
||||
"trade_dates": 157,
|
||||
"gate_open": 157,
|
||||
"gate_closed": 0,
|
||||
"trip_rate": 0.0,
|
||||
"base_ann": 0.255023,
|
||||
"base_sharpe": 1.4465,
|
||||
"base_maxDD": -0.080671,
|
||||
"gated_ann": 0.255023,
|
||||
"gated_sharpe": 1.4465,
|
||||
"gated_maxDD": -0.080671
|
||||
},
|
||||
{
|
||||
"window": "2026",
|
||||
"gate": "hitrate_20d_0.50",
|
||||
"start": "2026-01-04",
|
||||
"end": "2026-08-19",
|
||||
"trade_dates": 157,
|
||||
"gate_open": 118,
|
||||
"gate_closed": 39,
|
||||
"trip_rate": 0.2484,
|
||||
"base_ann": 0.255023,
|
||||
"base_sharpe": 1.4465,
|
||||
"base_maxDD": -0.080671,
|
||||
"gated_ann": 0.343819,
|
||||
"gated_sharpe": 2.5185,
|
||||
"gated_maxDD": -0.06259
|
||||
},
|
||||
{
|
||||
"window": "2026",
|
||||
"gate": "hitrate_20d_0.60",
|
||||
"start": "2026-01-04",
|
||||
"end": "2026-08-19",
|
||||
"trade_dates": 157,
|
||||
"gate_open": 28,
|
||||
"gate_closed": 129,
|
||||
"trip_rate": 0.8217,
|
||||
"base_ann": 0.255023,
|
||||
"base_sharpe": 1.4465,
|
||||
"base_maxDD": -0.080671,
|
||||
"gated_ann": 0.020214,
|
||||
"gated_sharpe": 0.3601,
|
||||
"gated_maxDD": -0.028489
|
||||
},
|
||||
{
|
||||
"window": "2025",
|
||||
"gate": "hitrate_5d_0.50",
|
||||
"start": "2025-01-02",
|
||||
"end": "2025-12-31",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 153,
|
||||
"gate_closed": 97,
|
||||
"trip_rate": 0.388,
|
||||
"base_ann": 0.177515,
|
||||
"base_sharpe": 0.8573,
|
||||
"base_maxDD": -0.217417,
|
||||
"gated_ann": 0.72055,
|
||||
"gated_sharpe": 6.9599,
|
||||
"gated_maxDD": -0.048495
|
||||
},
|
||||
{
|
||||
"window": "2025",
|
||||
"gate": "hitrate_5d_0.60",
|
||||
"start": "2025-01-02",
|
||||
"end": "2025-12-31",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 90,
|
||||
"gate_closed": 160,
|
||||
"trip_rate": 0.64,
|
||||
"base_ann": 0.177515,
|
||||
"base_sharpe": 0.8573,
|
||||
"base_maxDD": -0.217417,
|
||||
"gated_ann": 0.48564,
|
||||
"gated_sharpe": 5.3623,
|
||||
"gated_maxDD": -0.046673
|
||||
},
|
||||
{
|
||||
"window": "2025",
|
||||
"gate": "hitrate_5d_0.70",
|
||||
"start": "2025-01-02",
|
||||
"end": "2025-12-31",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 39,
|
||||
"gate_closed": 211,
|
||||
"trip_rate": 0.844,
|
||||
"base_ann": 0.177515,
|
||||
"base_sharpe": 0.8573,
|
||||
"base_maxDD": -0.217417,
|
||||
"gated_ann": 0.249221,
|
||||
"gated_sharpe": 4.9448,
|
||||
"gated_maxDD": -0.009619
|
||||
},
|
||||
{
|
||||
"window": "2025",
|
||||
"gate": "hitrate_10d_0.50",
|
||||
"start": "2025-01-02",
|
||||
"end": "2025-12-31",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 170,
|
||||
"gate_closed": 80,
|
||||
"trip_rate": 0.32,
|
||||
"base_ann": 0.177515,
|
||||
"base_sharpe": 0.8573,
|
||||
"base_maxDD": -0.217417,
|
||||
"gated_ann": 0.435847,
|
||||
"gated_sharpe": 3.5614,
|
||||
"gated_maxDD": -0.055776
|
||||
},
|
||||
{
|
||||
"window": "2025",
|
||||
"gate": "hitrate_10d_0.60",
|
||||
"start": "2025-01-02",
|
||||
"end": "2025-12-31",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 66,
|
||||
"gate_closed": 184,
|
||||
"trip_rate": 0.736,
|
||||
"base_ann": 0.177515,
|
||||
"base_sharpe": 0.8573,
|
||||
"base_maxDD": -0.217417,
|
||||
"gated_ann": 0.324574,
|
||||
"gated_sharpe": 5.2279,
|
||||
"gated_maxDD": -0.016028
|
||||
},
|
||||
{
|
||||
"window": "2025",
|
||||
"gate": "hitrate_10d_0.70",
|
||||
"start": "2025-01-02",
|
||||
"end": "2025-12-31",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 14,
|
||||
"gate_closed": 236,
|
||||
"trip_rate": 0.944,
|
||||
"base_ann": 0.177515,
|
||||
"base_sharpe": 0.8573,
|
||||
"base_maxDD": -0.217417,
|
||||
"gated_ann": 0.043002,
|
||||
"gated_sharpe": 1.4635,
|
||||
"gated_maxDD": -0.010154
|
||||
},
|
||||
{
|
||||
"window": "2025",
|
||||
"gate": "hitrate_20d_0.40",
|
||||
"start": "2025-01-02",
|
||||
"end": "2025-12-31",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 247,
|
||||
"gate_closed": 3,
|
||||
"trip_rate": 0.012,
|
||||
"base_ann": 0.177515,
|
||||
"base_sharpe": 0.8573,
|
||||
"base_maxDD": -0.217417,
|
||||
"gated_ann": 0.202949,
|
||||
"gated_sharpe": 0.9896,
|
||||
"gated_maxDD": -0.217417
|
||||
},
|
||||
{
|
||||
"window": "2025",
|
||||
"gate": "hitrate_20d_0.50",
|
||||
"start": "2025-01-02",
|
||||
"end": "2025-12-31",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 177,
|
||||
"gate_closed": 73,
|
||||
"trip_rate": 0.292,
|
||||
"base_ann": 0.177515,
|
||||
"base_sharpe": 0.8573,
|
||||
"base_maxDD": -0.217417,
|
||||
"gated_ann": 0.340772,
|
||||
"gated_sharpe": 2.8303,
|
||||
"gated_maxDD": -0.071731
|
||||
},
|
||||
{
|
||||
"window": "2025",
|
||||
"gate": "hitrate_20d_0.60",
|
||||
"start": "2025-01-02",
|
||||
"end": "2025-12-31",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 48,
|
||||
"gate_closed": 202,
|
||||
"trip_rate": 0.808,
|
||||
"base_ann": 0.177515,
|
||||
"base_sharpe": 0.8573,
|
||||
"base_maxDD": -0.217417,
|
||||
"gated_ann": 0.266028,
|
||||
"gated_sharpe": 4.1749,
|
||||
"gated_maxDD": -0.021527
|
||||
},
|
||||
{
|
||||
"window": "2024",
|
||||
"gate": "hitrate_5d_0.50",
|
||||
"start": "2024-01-02",
|
||||
"end": "2024-12-31",
|
||||
"trade_dates": 253,
|
||||
"gate_open": 139,
|
||||
"gate_closed": 114,
|
||||
"trip_rate": 0.4506,
|
||||
"base_ann": 0.08229,
|
||||
"base_sharpe": 0.5594,
|
||||
"base_maxDD": -0.10685,
|
||||
"gated_ann": 0.30351,
|
||||
"gated_sharpe": 2.4275,
|
||||
"gated_maxDD": -0.088219
|
||||
},
|
||||
{
|
||||
"window": "2024",
|
||||
"gate": "hitrate_5d_0.60",
|
||||
"start": "2024-01-02",
|
||||
"end": "2024-12-31",
|
||||
"trade_dates": 253,
|
||||
"gate_open": 71,
|
||||
"gate_closed": 182,
|
||||
"trip_rate": 0.7194,
|
||||
"base_ann": 0.08229,
|
||||
"base_sharpe": 0.5594,
|
||||
"base_maxDD": -0.10685,
|
||||
"gated_ann": 0.265365,
|
||||
"gated_sharpe": 2.4908,
|
||||
"gated_maxDD": -0.078304
|
||||
},
|
||||
{
|
||||
"window": "2024",
|
||||
"gate": "hitrate_5d_0.70",
|
||||
"start": "2024-01-02",
|
||||
"end": "2024-12-31",
|
||||
"trade_dates": 253,
|
||||
"gate_open": 19,
|
||||
"gate_closed": 234,
|
||||
"trip_rate": 0.9249,
|
||||
"base_ann": 0.08229,
|
||||
"base_sharpe": 0.5594,
|
||||
"base_maxDD": -0.10685,
|
||||
"gated_ann": 0.143125,
|
||||
"gated_sharpe": 3.377,
|
||||
"gated_maxDD": -0.003364
|
||||
},
|
||||
{
|
||||
"window": "2024",
|
||||
"gate": "hitrate_10d_0.50",
|
||||
"start": "2024-01-02",
|
||||
"end": "2024-12-31",
|
||||
"trade_dates": 253,
|
||||
"gate_open": 157,
|
||||
"gate_closed": 96,
|
||||
"trip_rate": 0.3794,
|
||||
"base_ann": 0.08229,
|
||||
"base_sharpe": 0.5594,
|
||||
"base_maxDD": -0.10685,
|
||||
"gated_ann": 0.277421,
|
||||
"gated_sharpe": 2.5767,
|
||||
"gated_maxDD": -0.043146
|
||||
},
|
||||
{
|
||||
"window": "2024",
|
||||
"gate": "hitrate_10d_0.60",
|
||||
"start": "2024-01-02",
|
||||
"end": "2024-12-31",
|
||||
"trade_dates": 253,
|
||||
"gate_open": 37,
|
||||
"gate_closed": 216,
|
||||
"trip_rate": 0.8538,
|
||||
"base_ann": 0.08229,
|
||||
"base_sharpe": 0.5594,
|
||||
"base_maxDD": -0.10685,
|
||||
"gated_ann": 0.207161,
|
||||
"gated_sharpe": 3.6083,
|
||||
"gated_maxDD": -0.011122
|
||||
},
|
||||
{
|
||||
"window": "2024",
|
||||
"gate": "hitrate_10d_0.70",
|
||||
"start": "2024-01-02",
|
||||
"end": "2024-12-31",
|
||||
"trade_dates": 253,
|
||||
"gate_open": 7,
|
||||
"gate_closed": 246,
|
||||
"trip_rate": 0.9723,
|
||||
"base_ann": 0.08229,
|
||||
"base_sharpe": 0.5594,
|
||||
"base_maxDD": -0.10685,
|
||||
"gated_ann": 0.031785,
|
||||
"gated_sharpe": 1.7408,
|
||||
"gated_maxDD": -0.002083
|
||||
},
|
||||
{
|
||||
"window": "2024",
|
||||
"gate": "hitrate_20d_0.40",
|
||||
"start": "2024-01-02",
|
||||
"end": "2024-12-31",
|
||||
"trade_dates": 253,
|
||||
"gate_open": 247,
|
||||
"gate_closed": 6,
|
||||
"trip_rate": 0.0237,
|
||||
"base_ann": 0.08229,
|
||||
"base_sharpe": 0.5594,
|
||||
"base_maxDD": -0.10685,
|
||||
"gated_ann": 0.078007,
|
||||
"gated_sharpe": 0.5342,
|
||||
"gated_maxDD": -0.10685
|
||||
},
|
||||
{
|
||||
"window": "2024",
|
||||
"gate": "hitrate_20d_0.50",
|
||||
"start": "2024-01-02",
|
||||
"end": "2024-12-31",
|
||||
"trade_dates": 253,
|
||||
"gate_open": 177,
|
||||
"gate_closed": 76,
|
||||
"trip_rate": 0.3004,
|
||||
"base_ann": 0.08229,
|
||||
"base_sharpe": 0.5594,
|
||||
"base_maxDD": -0.10685,
|
||||
"gated_ann": 0.210672,
|
||||
"gated_sharpe": 1.8469,
|
||||
"gated_maxDD": -0.056207
|
||||
},
|
||||
{
|
||||
"window": "2024",
|
||||
"gate": "hitrate_20d_0.60",
|
||||
"start": "2024-01-02",
|
||||
"end": "2024-12-31",
|
||||
"trade_dates": 253,
|
||||
"gate_open": 13,
|
||||
"gate_closed": 240,
|
||||
"trip_rate": 0.9486,
|
||||
"base_ann": 0.08229,
|
||||
"base_sharpe": 0.5594,
|
||||
"base_maxDD": -0.10685,
|
||||
"gated_ann": 0.005953,
|
||||
"gated_sharpe": 0.3039,
|
||||
"gated_maxDD": -0.014443
|
||||
},
|
||||
{
|
||||
"window": "2023",
|
||||
"gate": "hitrate_5d_0.50",
|
||||
"start": "2023-01-03",
|
||||
"end": "2023-12-29",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 139,
|
||||
"gate_closed": 111,
|
||||
"trip_rate": 0.444,
|
||||
"base_ann": -0.047644,
|
||||
"base_sharpe": -0.2738,
|
||||
"base_maxDD": -0.197856,
|
||||
"gated_ann": 0.546654,
|
||||
"gated_sharpe": 4.2124,
|
||||
"gated_maxDD": -0.035676
|
||||
},
|
||||
{
|
||||
"window": "2023",
|
||||
"gate": "hitrate_5d_0.60",
|
||||
"start": "2023-01-03",
|
||||
"end": "2023-12-29",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 79,
|
||||
"gate_closed": 171,
|
||||
"trip_rate": 0.684,
|
||||
"base_ann": -0.047644,
|
||||
"base_sharpe": -0.2738,
|
||||
"base_maxDD": -0.197856,
|
||||
"gated_ann": 0.556291,
|
||||
"gated_sharpe": 5.1026,
|
||||
"gated_maxDD": -0.025449
|
||||
},
|
||||
{
|
||||
"window": "2023",
|
||||
"gate": "hitrate_5d_0.70",
|
||||
"start": "2023-01-03",
|
||||
"end": "2023-12-29",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 34,
|
||||
"gate_closed": 216,
|
||||
"trip_rate": 0.864,
|
||||
"base_ann": -0.047644,
|
||||
"base_sharpe": -0.2738,
|
||||
"base_maxDD": -0.197856,
|
||||
"gated_ann": 0.404269,
|
||||
"gated_sharpe": 4.4477,
|
||||
"gated_maxDD": -0.013842
|
||||
},
|
||||
{
|
||||
"window": "2023",
|
||||
"gate": "hitrate_10d_0.50",
|
||||
"start": "2023-01-03",
|
||||
"end": "2023-12-29",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 148,
|
||||
"gate_closed": 102,
|
||||
"trip_rate": 0.408,
|
||||
"base_ann": -0.047644,
|
||||
"base_sharpe": -0.2738,
|
||||
"base_maxDD": -0.197856,
|
||||
"gated_ann": 0.459869,
|
||||
"gated_sharpe": 3.5211,
|
||||
"gated_maxDD": -0.046921
|
||||
},
|
||||
{
|
||||
"window": "2023",
|
||||
"gate": "hitrate_10d_0.60",
|
||||
"start": "2023-01-03",
|
||||
"end": "2023-12-29",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 62,
|
||||
"gate_closed": 188,
|
||||
"trip_rate": 0.752,
|
||||
"base_ann": -0.047644,
|
||||
"base_sharpe": -0.2738,
|
||||
"base_maxDD": -0.197856,
|
||||
"gated_ann": 0.368232,
|
||||
"gated_sharpe": 4.0148,
|
||||
"gated_maxDD": -0.022983
|
||||
},
|
||||
{
|
||||
"window": "2023",
|
||||
"gate": "hitrate_10d_0.70",
|
||||
"start": "2023-01-03",
|
||||
"end": "2023-12-29",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 13,
|
||||
"gate_closed": 237,
|
||||
"trip_rate": 0.948,
|
||||
"base_ann": -0.047644,
|
||||
"base_sharpe": -0.2738,
|
||||
"base_maxDD": -0.197856,
|
||||
"gated_ann": 0.091481,
|
||||
"gated_sharpe": 2.1324,
|
||||
"gated_maxDD": -0.010866
|
||||
},
|
||||
{
|
||||
"window": "2023",
|
||||
"gate": "hitrate_20d_0.40",
|
||||
"start": "2023-01-03",
|
||||
"end": "2023-12-29",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 237,
|
||||
"gate_closed": 13,
|
||||
"trip_rate": 0.052,
|
||||
"base_ann": -0.047644,
|
||||
"base_sharpe": -0.2738,
|
||||
"base_maxDD": -0.197856,
|
||||
"gated_ann": 0.06639,
|
||||
"gated_sharpe": 0.3895,
|
||||
"gated_maxDD": -0.146704
|
||||
},
|
||||
{
|
||||
"window": "2023",
|
||||
"gate": "hitrate_20d_0.50",
|
||||
"start": "2023-01-03",
|
||||
"end": "2023-12-29",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 149,
|
||||
"gate_closed": 101,
|
||||
"trip_rate": 0.404,
|
||||
"base_ann": -0.047644,
|
||||
"base_sharpe": -0.2738,
|
||||
"base_maxDD": -0.197856,
|
||||
"gated_ann": 0.366016,
|
||||
"gated_sharpe": 2.5969,
|
||||
"gated_maxDD": -0.063998
|
||||
},
|
||||
{
|
||||
"window": "2023",
|
||||
"gate": "hitrate_20d_0.60",
|
||||
"start": "2023-01-03",
|
||||
"end": "2023-12-29",
|
||||
"trade_dates": 250,
|
||||
"gate_open": 40,
|
||||
"gate_closed": 210,
|
||||
"trip_rate": 0.84,
|
||||
"base_ann": -0.047644,
|
||||
"base_sharpe": -0.2738,
|
||||
"base_maxDD": -0.197856,
|
||||
"gated_ann": 0.149844,
|
||||
"gated_sharpe": 2.2645,
|
||||
"gated_maxDD": -0.032267
|
||||
},
|
||||
{
|
||||
"window": "2021",
|
||||
"gate": "hitrate_5d_0.50",
|
||||
"start": "2021-01-04",
|
||||
"end": "2021-12-31",
|
||||
"trade_dates": 252,
|
||||
"gate_open": 145,
|
||||
"gate_closed": 107,
|
||||
"trip_rate": 0.4246,
|
||||
"base_ann": 0.18367,
|
||||
"base_sharpe": 1.0979,
|
||||
"base_maxDD": -0.102651,
|
||||
"gated_ann": 0.556615,
|
||||
"gated_sharpe": 5.9167,
|
||||
"gated_maxDD": -0.028062
|
||||
},
|
||||
{
|
||||
"window": "2021",
|
||||
"gate": "hitrate_5d_0.60",
|
||||
"start": "2021-01-04",
|
||||
"end": "2021-12-31",
|
||||
"trade_dates": 252,
|
||||
"gate_open": 68,
|
||||
"gate_closed": 184,
|
||||
"trip_rate": 0.7302,
|
||||
"base_ann": 0.18367,
|
||||
"base_sharpe": 1.0979,
|
||||
"base_maxDD": -0.102651,
|
||||
"gated_ann": 0.351867,
|
||||
"gated_sharpe": 5.8468,
|
||||
"gated_maxDD": -0.011638
|
||||
},
|
||||
{
|
||||
"window": "2021",
|
||||
"gate": "hitrate_5d_0.70",
|
||||
"start": "2021-01-04",
|
||||
"end": "2021-12-31",
|
||||
"trade_dates": 252,
|
||||
"gate_open": 26,
|
||||
"gate_closed": 226,
|
||||
"trip_rate": 0.8968,
|
||||
"base_ann": 0.18367,
|
||||
"base_sharpe": 1.0979,
|
||||
"base_maxDD": -0.102651,
|
||||
"gated_ann": 0.148417,
|
||||
"gated_sharpe": 3.9593,
|
||||
"gated_maxDD": -0.0041
|
||||
},
|
||||
{
|
||||
"window": "2021",
|
||||
"gate": "hitrate_10d_0.50",
|
||||
"start": "2021-01-04",
|
||||
"end": "2021-12-31",
|
||||
"trade_dates": 252,
|
||||
"gate_open": 163,
|
||||
"gate_closed": 89,
|
||||
"trip_rate": 0.3532,
|
||||
"base_ann": 0.18367,
|
||||
"base_sharpe": 1.0979,
|
||||
"base_maxDD": -0.102651,
|
||||
"gated_ann": 0.465023,
|
||||
"gated_sharpe": 4.2342,
|
||||
"gated_maxDD": -0.040569
|
||||
},
|
||||
{
|
||||
"window": "2021",
|
||||
"gate": "hitrate_10d_0.60",
|
||||
"start": "2021-01-04",
|
||||
"end": "2021-12-31",
|
||||
"trade_dates": 252,
|
||||
"gate_open": 51,
|
||||
"gate_closed": 201,
|
||||
"trip_rate": 0.7976,
|
||||
"base_ann": 0.18367,
|
||||
"base_sharpe": 1.0979,
|
||||
"base_maxDD": -0.102651,
|
||||
"gated_ann": 0.21802,
|
||||
"gated_sharpe": 4.7978,
|
||||
"gated_maxDD": -0.011134
|
||||
},
|
||||
{
|
||||
"window": "2021",
|
||||
"gate": "hitrate_10d_0.70",
|
||||
"start": "2021-01-04",
|
||||
"end": "2021-12-31",
|
||||
"trade_dates": 252,
|
||||
"gate_open": 13,
|
||||
"gate_closed": 239,
|
||||
"trip_rate": 0.9484,
|
||||
"base_ann": 0.18367,
|
||||
"base_sharpe": 1.0979,
|
||||
"base_maxDD": -0.102651,
|
||||
"gated_ann": 0.06108,
|
||||
"gated_sharpe": 2.7055,
|
||||
"gated_maxDD": -0.000262
|
||||
},
|
||||
{
|
||||
"window": "2021",
|
||||
"gate": "hitrate_20d_0.40",
|
||||
"start": "2021-01-04",
|
||||
"end": "2021-12-31",
|
||||
"trade_dates": 252,
|
||||
"gate_open": 252,
|
||||
"gate_closed": 0,
|
||||
"trip_rate": 0.0,
|
||||
"base_ann": 0.18367,
|
||||
"base_sharpe": 1.0979,
|
||||
"base_maxDD": -0.102651,
|
||||
"gated_ann": 0.18367,
|
||||
"gated_sharpe": 1.0979,
|
||||
"gated_maxDD": -0.102651
|
||||
},
|
||||
{
|
||||
"window": "2021",
|
||||
"gate": "hitrate_20d_0.50",
|
||||
"start": "2021-01-04",
|
||||
"end": "2021-12-31",
|
||||
"trade_dates": 252,
|
||||
"gate_open": 166,
|
||||
"gate_closed": 86,
|
||||
"trip_rate": 0.3413,
|
||||
"base_ann": 0.18367,
|
||||
"base_sharpe": 1.0979,
|
||||
"base_maxDD": -0.102651,
|
||||
"gated_ann": 0.289756,
|
||||
"gated_sharpe": 2.5787,
|
||||
"gated_maxDD": -0.049244
|
||||
},
|
||||
{
|
||||
"window": "2021",
|
||||
"gate": "hitrate_20d_0.60",
|
||||
"start": "2021-01-04",
|
||||
"end": "2021-12-31",
|
||||
"trade_dates": 252,
|
||||
"gate_open": 38,
|
||||
"gate_closed": 214,
|
||||
"trip_rate": 0.8492,
|
||||
"base_ann": 0.18367,
|
||||
"base_sharpe": 1.0979,
|
||||
"base_maxDD": -0.102651,
|
||||
"gated_ann": 0.104685,
|
||||
"gated_sharpe": 2.394,
|
||||
"gated_maxDD": -0.015084
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,293 @@
|
||||
"""Signal-quality gate walk-forward backtest.
|
||||
|
||||
Gates trades based on whether the model's recent topk predictions were correct
|
||||
(hit rate). This is a retrospective gate — it measures prediction accuracy,
|
||||
not market state.
|
||||
|
||||
Usage:
|
||||
cd /app && .venv/bin/python book/scripts/signal_quality_gate_bt.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
LAKE_ROOT = "/home/data/lake"
|
||||
MARKET = "US"
|
||||
OUT_DIR = pathlib.Path("/app/experiments/book/data/signal_quality_gate")
|
||||
|
||||
WINDOWS = [
|
||||
{"label": "2026", "start": "2026-01-04", "end": "2026-08-19",
|
||||
"pred": f"{LAKE_ROOT}/mlruns/52/9f98ea5c550a409f87b56a6cd8fee343/artifacts/pred.pkl"},
|
||||
{"label": "2025", "start": "2025-01-02", "end": "2025-12-31",
|
||||
"pred": f"{LAKE_ROOT}/mlruns/52/fe96741654df4780957a3a949999ae6a/artifacts/pred.pkl"},
|
||||
{"label": "2024", "start": "2024-01-02", "end": "2024-12-31",
|
||||
"pred": f"{LAKE_ROOT}/mlruns/52/71ed5bfa9984490f8bba8b222f7acc39/artifacts/pred.pkl"},
|
||||
{"label": "2023", "start": "2023-01-03", "end": "2023-12-29",
|
||||
"pred": f"{LAKE_ROOT}/mlruns/56/8ca46e554311444c9a42637a788226e8/artifacts/pred.pkl"},
|
||||
{"label": "2021", "start": "2021-01-04", "end": "2021-12-31",
|
||||
"pred": f"{LAKE_ROOT}/mlruns/56/4e0700ddab2a4e108b46efece7346ee3/artifacts/pred.pkl"},
|
||||
]
|
||||
|
||||
# Signal-quality gate configs: (lookback_days, threshold, name)
|
||||
SIGNAL_GATE_CONFIGS = [
|
||||
(5, 0.50, "hitrate_5d_0.50"),
|
||||
(5, 0.60, "hitrate_5d_0.60"),
|
||||
(5, 0.70, "hitrate_5d_0.70"),
|
||||
(10, 0.50, "hitrate_10d_0.50"),
|
||||
(10, 0.60, "hitrate_10d_0.60"),
|
||||
(10, 0.70, "hitrate_10d_0.70"),
|
||||
(20, 0.40, "hitrate_20d_0.40"),
|
||||
(20, 0.50, "hitrate_20d_0.50"),
|
||||
(20, 0.60, "hitrate_20d_0.60"),
|
||||
]
|
||||
|
||||
|
||||
def load_pred(path: str) -> pd.Series:
|
||||
df = pd.read_pickle(path)
|
||||
if isinstance(df, pd.DataFrame):
|
||||
if "score" in df.columns:
|
||||
s = df["score"]
|
||||
else:
|
||||
s = df.iloc[:, 0]
|
||||
else:
|
||||
s = df
|
||||
idx = s.index
|
||||
new_dt = pd.to_datetime(idx.get_level_values(0)).normalize()
|
||||
s.index = pd.MultiIndex.from_arrays([new_dt, idx.get_level_values(1)], names=idx.names)
|
||||
return s
|
||||
|
||||
|
||||
def load_bars_for_window(start: str, end: str) -> pd.DataFrame:
|
||||
from tac_qlib.data.config import LakeConfig, resolve_lake_root
|
||||
cfg = LakeConfig(resolve_lake_root(LAKE_ROOT), MARKET)
|
||||
sp = cfg.lake_root / "symbols.parquet"
|
||||
if sp.exists():
|
||||
syms = pd.read_parquet(sp)
|
||||
col = "symbol" if "symbol" in syms.columns else syms.columns[0]
|
||||
symbols = sorted(syms[col].astype(str).str.upper().tolist())
|
||||
else:
|
||||
return pd.DataFrame()
|
||||
closes = {}
|
||||
for sym in symbols:
|
||||
p = cfg.bar_path("1d", sym)
|
||||
if not p.exists():
|
||||
continue
|
||||
try:
|
||||
df = pd.read_parquet(p)
|
||||
except Exception:
|
||||
continue
|
||||
if not len(df):
|
||||
continue
|
||||
tcol = df["t"] if "t" in df.columns else df["date"]
|
||||
ts = pd.to_datetime(tcol)
|
||||
df = df.assign(_t=ts).set_index("_t").sort_index()
|
||||
warmup_start = pd.Timestamp(start) - pd.Timedelta(days=60)
|
||||
df = df.loc[warmup_start:end]
|
||||
if len(df) >= 22:
|
||||
closes[sym] = df["c"]
|
||||
return pd.DataFrame(closes)
|
||||
|
||||
|
||||
def compute_hit_rate_series(
|
||||
pred: pd.Series, ret_df: pd.DataFrame, topk: int = 10, lookback: int = 10,
|
||||
) -> pd.Series:
|
||||
dt_idx = pred.index.get_level_values(0)
|
||||
trade_dates = sorted(dt_idx.unique())
|
||||
hit_rates = {}
|
||||
for i in range(1, len(trade_dates)):
|
||||
prev_date = trade_dates[i - 1]
|
||||
curr_date = trade_dates[i]
|
||||
try:
|
||||
prev_scores = pred.loc[prev_date]
|
||||
except KeyError:
|
||||
continue
|
||||
if isinstance(prev_scores, pd.DataFrame):
|
||||
prev_scores = prev_scores.iloc[:, 0]
|
||||
prev_scores = prev_scores.dropna().sort_values(ascending=False)
|
||||
topk_syms = list(prev_scores.index[:topk])
|
||||
if curr_date not in ret_df.index:
|
||||
continue
|
||||
today_ret = ret_df.loc[curr_date]
|
||||
topk_rets = today_ret.reindex(topk_syms).dropna()
|
||||
if len(topk_rets) > 0:
|
||||
hit_rate = (topk_rets > 0).mean()
|
||||
hit_rates[curr_date] = hit_rate
|
||||
hit_series = pd.Series(hit_rates)
|
||||
if len(hit_series) == 0:
|
||||
return hit_series
|
||||
rolling_hr = hit_series.rolling(lookback, min_periods=max(1, lookback // 2)).mean()
|
||||
return rolling_hr
|
||||
|
||||
|
||||
def run_backtest(pred, hit_rate, close_df, start, end, topk=10, threshold=0.5):
|
||||
if not isinstance(pred.index, pd.MultiIndex):
|
||||
return {"error": "pred must have MultiIndex"}
|
||||
ret_df = close_df.pct_change()
|
||||
ret_df.index = pd.to_datetime(ret_df.index).normalize()
|
||||
dt_idx = pred.index.get_level_values(0)
|
||||
window_mask = (dt_idx >= pd.Timestamp(start)) & (dt_idx <= pd.Timestamp(end))
|
||||
window_pred = pred.loc[window_mask]
|
||||
if len(window_pred) == 0:
|
||||
return {"error": "no pred data in window"}
|
||||
trade_dates = sorted(dt_idx[window_mask].unique())
|
||||
gate_open = {}
|
||||
for d in trade_dates:
|
||||
known = hit_rate[hit_rate.index <= d]
|
||||
if len(known) > 0 and not pd.isna(known.iloc[-1]):
|
||||
gate_open[d] = bool(known.iloc[-1] >= threshold)
|
||||
else:
|
||||
gate_open[d] = True
|
||||
n_total = len(trade_dates)
|
||||
n_open = sum(1 for v in gate_open.values() if v)
|
||||
n_closed = n_total - n_open
|
||||
holdings_base = []
|
||||
holdings_gated = []
|
||||
equity_gated = 1_000_000.0
|
||||
equity_base = 1_000_000.0
|
||||
prev_week = None
|
||||
prev_scores = None
|
||||
daily_gated = []
|
||||
daily_base = []
|
||||
ret_by_date = {rd: ret_df.loc[rd] for rd in ret_df.index}
|
||||
for d in trade_dates:
|
||||
try:
|
||||
day_scores = window_pred.loc[d]
|
||||
except KeyError:
|
||||
daily_gated.append(equity_gated)
|
||||
daily_base.append(equity_base)
|
||||
prev_scores = None
|
||||
continue
|
||||
if isinstance(day_scores, pd.DataFrame):
|
||||
day_scores = day_scores.iloc[:, 0]
|
||||
day_scores = day_scores.dropna().sort_values(ascending=False)
|
||||
if len(day_scores) == 0:
|
||||
daily_gated.append(equity_gated)
|
||||
daily_base.append(equity_base)
|
||||
prev_scores = None
|
||||
continue
|
||||
ret_row = ret_by_date.get(d)
|
||||
if ret_row is None:
|
||||
daily_gated.append(equity_gated)
|
||||
daily_base.append(equity_base)
|
||||
prev_scores = day_scores
|
||||
continue
|
||||
cur_week = (d.isocalendar()[0], d.isocalendar()[1]) if hasattr(d, 'isocalendar') else None
|
||||
gate_val = gate_open.get(d, True)
|
||||
if cur_week != prev_week or not holdings_base:
|
||||
if prev_scores is not None:
|
||||
holdings_base = list(prev_scores.index[:topk])
|
||||
if holdings_base:
|
||||
base_rets = ret_row.reindex(holdings_base).dropna()
|
||||
if len(base_rets) > 0:
|
||||
equity_base *= (1 + base_rets.mean())
|
||||
if gate_val:
|
||||
if cur_week != prev_week or not holdings_gated:
|
||||
if prev_scores is not None:
|
||||
holdings_gated = list(prev_scores.index[:topk])
|
||||
if holdings_gated:
|
||||
hold_rets = ret_row.reindex(holdings_gated).dropna()
|
||||
if len(hold_rets) > 0:
|
||||
equity_gated *= (1 + hold_rets.mean())
|
||||
else:
|
||||
holdings_gated = []
|
||||
prev_week = cur_week
|
||||
prev_scores = day_scores
|
||||
daily_gated.append(equity_gated)
|
||||
daily_base.append(equity_base)
|
||||
g_series = pd.Series(daily_gated, index=trade_dates)
|
||||
b_series = pd.Series(daily_base, index=trade_dates)
|
||||
def _metrics(eq):
|
||||
if len(eq) < 2:
|
||||
return {"ann_return": 0, "sharpe": 0, "maxDD": 0}
|
||||
rets = eq.pct_change().dropna()
|
||||
ann_ret = float((eq.iloc[-1] / eq.iloc[0]) ** (252 / max(len(eq), 1)) - 1)
|
||||
vol = float(rets.std() * (252 ** 0.5)) if len(rets) > 1 else 0
|
||||
sharpe = ann_ret / vol if vol > 0 else 0
|
||||
peak = eq.cummax()
|
||||
dd = (eq - peak) / peak
|
||||
maxDD = float(dd.min())
|
||||
return {"ann_return": round(ann_ret, 6), "sharpe": round(sharpe, 4), "maxDD": round(maxDD, 6)}
|
||||
base_m = _metrics(b_series)
|
||||
gated_m = _metrics(g_series)
|
||||
return {
|
||||
"trade_dates": n_total,
|
||||
"gate_open_days": n_open,
|
||||
"gate_closed_days": n_closed,
|
||||
"trip_rate": round(n_closed / n_total, 4) if n_total else 0,
|
||||
"base": base_m,
|
||||
"gated": gated_m,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
full_start = "2015-01-03"
|
||||
full_end = "2026-08-19"
|
||||
print("Loading lake bars...")
|
||||
close_df = load_bars_for_window(full_start, full_end)
|
||||
print(f" {close_df.shape[1]} symbols, {close_df.shape[0]} days")
|
||||
ret_df = close_df.pct_change()
|
||||
ret_df.index = pd.to_datetime(ret_df.index).normalize()
|
||||
results = []
|
||||
for window in WINDOWS:
|
||||
wl, ws, we = window["label"], window["start"], window["end"]
|
||||
pred_path = window["pred"]
|
||||
print(f"\n=== Window {wl} ({ws} to {we}) ===")
|
||||
pred = load_pred(pred_path)
|
||||
print(f" pred shape: {pred.shape}")
|
||||
hit_rates = {}
|
||||
for lookback, _, name in SIGNAL_GATE_CONFIGS:
|
||||
if lookback not in hit_rates:
|
||||
hr = compute_hit_rate_series(pred, ret_df, topk=10, lookback=lookback)
|
||||
hit_rates[lookback] = hr
|
||||
print(f" lookback={lookback}: {len(hr)} days with hit rates")
|
||||
for lookback, threshold, name in SIGNAL_GATE_CONFIGS:
|
||||
hr = hit_rates[lookback]
|
||||
bt = run_backtest(pred, hr, close_df, ws, we, topk=10, threshold=threshold)
|
||||
if "error" in bt:
|
||||
print(f" {name}: {bt['error']}")
|
||||
continue
|
||||
row = {
|
||||
"window": wl,
|
||||
"gate": name,
|
||||
"start": ws,
|
||||
"end": we,
|
||||
"trade_dates": bt["trade_dates"],
|
||||
"gate_open": bt["gate_open_days"],
|
||||
"gate_closed": bt["gate_closed_days"],
|
||||
"trip_rate": bt["trip_rate"],
|
||||
"base_ann": bt["base"]["ann_return"],
|
||||
"base_sharpe": bt["base"]["sharpe"],
|
||||
"base_maxDD": bt["base"]["maxDD"],
|
||||
"gated_ann": bt["gated"]["ann_return"],
|
||||
"gated_sharpe": bt["gated"]["sharpe"],
|
||||
"gated_maxDD": bt["gated"]["maxDD"],
|
||||
}
|
||||
results.append(row)
|
||||
print(f" {name}: trip={bt['trip_rate']:.1%}, "
|
||||
f"base={bt['base']['ann_return']:+.1%} (Sharpe {bt['base']['sharpe']:.2f}), "
|
||||
f"gated={bt['gated']['ann_return']:+.1%} (Sharpe {bt['gated']['sharpe']:.2f})")
|
||||
df = pd.DataFrame(results)
|
||||
out_path = OUT_DIR / "signal_quality_gate_results.csv"
|
||||
df.to_csv(out_path, index=False)
|
||||
with open(OUT_DIR / "signal_quality_gate_results.json", "w") as f:
|
||||
json.dump(df.to_dict(orient="records"), f, indent=2, default=str)
|
||||
print(f"\nSaved to {out_path}")
|
||||
print("\n=== Summary: Gated Return by Window ===")
|
||||
for gate_name in df["gate"].unique():
|
||||
gdf = df[df["gate"] == gate_name]
|
||||
print(f"\n{gate_name}:")
|
||||
for _, r in gdf.iterrows():
|
||||
print(f" {r['window']}: base={r['base_ann']:+.1%}, gated={r['gated_ann']:+.1%}, "
|
||||
f"trip={r['trip_rate']:.0%}, diff={r['gated_ann']-r['base_ann']:+.1%}pp")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user