7171 "cvar_loss_72h" ,
7272]
7373
74+ HOURS_PER_YEAR = 24.0 * 365.25
75+
7476
7577@dataclass (frozen = True )
7678class PackConfig :
@@ -87,6 +89,11 @@ class PackConfig:
8789 min_recent_ret_24h : float
8890 min_recent_ret_72h : float
8991 max_recent_vol_72h : float
92+ regime_cs_skew_min : float
93+ vol_target_ann : float
94+ inv_vol_target_ann : float
95+ inv_vol_floor : float
96+ inv_vol_cap : float
9097 max_positions : int
9198 max_pending_entries : int
9299 entry_ttl_hours : int
@@ -437,6 +444,32 @@ def build_actions_and_bars(
437444 if "vol_72h" in rows .columns
438445 else np .zeros (len (rows ), dtype = np .float64 )
439446 )
447+ rows ["_recent_ret_24h_for_regime" ] = recent_ret_24h
448+ rows ["_recent_vol_72h_for_scale" ] = recent_vol_72h
449+ regime_cs_skew_24h = (
450+ rows .groupby ("timestamp" )["_recent_ret_24h_for_regime" ]
451+ .transform (lambda values : values .skew ())
452+ .replace ([np .inf , - np .inf ], np .nan )
453+ .fillna (0.0 )
454+ .to_numpy (dtype = np .float64 )
455+ )
456+ market_vol_72h = (
457+ rows .groupby ("timestamp" )["_recent_vol_72h_for_scale" ]
458+ .transform ("median" )
459+ .replace ([np .inf , - np .inf ], np .nan )
460+ .fillna (0.0 )
461+ .to_numpy (dtype = np .float64 )
462+ )
463+ recent_vol_ann = recent_vol_72h * float (np .sqrt (HOURS_PER_YEAR ))
464+ market_vol_ann = market_vol_72h * float (np .sqrt (HOURS_PER_YEAR ))
465+ inv_vol_scale = np .ones (len (rows ), dtype = np .float64 )
466+ if float (cfg .inv_vol_target_ann ) > 0.0 :
467+ inv_vol_scale = float (cfg .inv_vol_target_ann ) / np .maximum (recent_vol_ann , 1e-9 )
468+ inv_vol_scale = np .clip (inv_vol_scale , float (cfg .inv_vol_floor ), float (cfg .inv_vol_cap ))
469+ market_vol_scale = np .ones (len (rows ), dtype = np .float64 )
470+ if float (cfg .vol_target_ann ) > 0.0 :
471+ market_vol_scale = float (cfg .vol_target_ann ) / np .maximum (market_vol_ann , 1e-9 )
472+ market_vol_scale = np .clip (market_vol_scale , float (cfg .inv_vol_floor ), float (cfg .inv_vol_cap ))
440473
441474 upside = np .maximum .reduce ([pred_high , pred_close , np .zeros_like (pred_high )])
442475 downside = np .maximum (- pred_low , 0.0 )
@@ -465,6 +498,9 @@ def build_actions_and_bars(
465498 )
466499 if float (cfg .max_recent_vol_72h ) > 0.0 :
467500 active &= recent_vol_72h <= float (cfg .max_recent_vol_72h )
501+ if float (cfg .regime_cs_skew_min ) > - 1e8 :
502+ active &= regime_cs_skew_24h >= float (cfg .regime_cs_skew_min )
503+ amount = amount * inv_vol_scale * market_vol_scale
468504 amount = np .where (active , amount , 0.0 )
469505
470506 rows ["buy_price" ] = buy_price
@@ -480,6 +516,11 @@ def build_actions_and_bars(
480516 rows ["recent_ret_24h" ] = recent_ret_24h
481517 rows ["recent_ret_72h" ] = recent_ret_72h
482518 rows ["recent_vol_72h" ] = recent_vol_72h
519+ rows ["recent_vol_ann" ] = recent_vol_ann
520+ rows ["regime_cs_skew_24h" ] = regime_cs_skew_24h
521+ rows ["market_vol_ann" ] = market_vol_ann
522+ rows ["inv_vol_scale" ] = inv_vol_scale
523+ rows ["market_vol_scale" ] = market_vol_scale
483524 rows ["watch_entry_gap_bps" ] = entry_gap * 10_000.0
484525 rows ["watch_exit_gap_bps" ] = exit_gap * 10_000.0
485526 rows [f"predicted_high_p50_h{ label_horizon } " ] = ref * (1.0 + pred_high )
@@ -506,6 +547,11 @@ def build_actions_and_bars(
506547 "recent_ret_24h" ,
507548 "recent_ret_72h" ,
508549 "recent_vol_72h" ,
550+ "recent_vol_ann" ,
551+ "regime_cs_skew_24h" ,
552+ "market_vol_ann" ,
553+ "inv_vol_scale" ,
554+ "market_vol_scale" ,
509555 "watch_entry_gap_bps" ,
510556 "watch_exit_gap_bps" ,
511557 f"predicted_high_p50_h{ label_horizon } " ,
@@ -1020,6 +1066,11 @@ def iter_pack_configs(args: argparse.Namespace) -> list[PackConfig]:
10201066 _parse_float_list (args .min_recent_ret_24h_grid ),
10211067 _parse_float_list (args .min_recent_ret_72h_grid ),
10221068 _parse_float_list (args .max_recent_vol_72h_grid ),
1069+ _parse_float_list (args .regime_cs_skew_min_grid ),
1070+ _parse_float_list (args .vol_target_ann_grid ),
1071+ _parse_float_list (args .inv_vol_target_ann_grid ),
1072+ _parse_float_list (args .inv_vol_floor_grid ),
1073+ _parse_float_list (args .inv_vol_cap_grid ),
10231074 _parse_int_list (args .max_positions_grid ),
10241075 _parse_int_list (args .max_pending_entries_grid ),
10251076 _parse_int_list (args .entry_ttl_hours_grid ),
@@ -1071,6 +1122,11 @@ def main() -> int:
10711122 parser .add_argument ("--min-recent-ret-24h-grid" , default = "-1.0" )
10721123 parser .add_argument ("--min-recent-ret-72h-grid" , default = "-1.0" )
10731124 parser .add_argument ("--max-recent-vol-72h-grid" , default = "0.0" )
1125+ parser .add_argument ("--regime-cs-skew-min-grid" , default = "-1000000000.0" )
1126+ parser .add_argument ("--vol-target-ann-grid" , default = "0.0" )
1127+ parser .add_argument ("--inv-vol-target-ann-grid" , default = "0.0" )
1128+ parser .add_argument ("--inv-vol-floor-grid" , default = "0.05" )
1129+ parser .add_argument ("--inv-vol-cap-grid" , default = "3.0" )
10741130 parser .add_argument ("--max-positions-grid" , default = "5,8" )
10751131 parser .add_argument ("--max-pending-entries-grid" , default = "12,24" )
10761132 parser .add_argument ("--entry-ttl-hours-grid" , default = "3,6" )
0 commit comments