@@ -820,6 +820,120 @@ def run_exit_prediction_v8_command(
820820 click .echo (json .dumps (summary , indent = 2 ))
821821
822822
823+ @main .command ("run-live-forward-snapshot" )
824+ @click .option (
825+ "--base-config" ,
826+ default = "configs/base.yaml" ,
827+ show_default = True ,
828+ type = click .Path (exists = True ),
829+ )
830+ @click .option (
831+ "--data-config" ,
832+ default = "configs/data.yaml" ,
833+ show_default = True ,
834+ type = click .Path (exists = True ),
835+ )
836+ @click .option (
837+ "--experiment-config" ,
838+ default = "configs/experiments/sparse_rank_v3_fnspid_2018_2023.yaml" ,
839+ show_default = True ,
840+ type = click .Path (exists = True ),
841+ )
842+ @click .option (
843+ "--training-dataset" ,
844+ default = "data/processed/residual_rank_v2_repaired/features.parquet" ,
845+ show_default = True ,
846+ type = click .Path (exists = True ),
847+ )
848+ @click .option (
849+ "--membership-file" ,
850+ default = "data/raw/sp500_ticker_start_end_source.csv" ,
851+ show_default = True ,
852+ type = click .Path (exists = True ),
853+ )
854+ @click .option (
855+ "--sector-file" ,
856+ default = "data/processed/residual_rank_v2_repaired/inferred_sector_history.csv" ,
857+ show_default = True ,
858+ type = click .Path (exists = True ),
859+ )
860+ @click .option ("--output-root" , default = "reports/live_forward" , show_default = True )
861+ @click .option ("--as-of" , default = None , help = "Decision date, defaults to today." )
862+ @click .option ("--holdings" , default = 40 , show_default = True , type = int )
863+ @click .option (
864+ "--label-horizon-sessions" ,
865+ default = 1 ,
866+ show_default = True ,
867+ type = int ,
868+ help = "Forward sessions for the live proxy label; 1 collects evidence fastest." ,
869+ )
870+ @click .option ("--recent-training-start" , default = "2024-01-01" , show_default = True )
871+ @click .option (
872+ "--use-static-training-dataset" ,
873+ is_flag = True ,
874+ help = "Use the old repaired weekly parquet instead of rebuilding recent daily proxy labels." ,
875+ )
876+ @click .option (
877+ "--skip-hyperparameter-tuning" ,
878+ is_flag = True ,
879+ help = "Train fixed config parameters only; intended for debugging, not evidence runs." ,
880+ )
881+ @click .option ("--validation-days" , default = 42 , show_default = True , type = int )
882+ @click .option ("--selection-metric" , default = "top40_raw_excess" , show_default = True )
883+ def run_live_forward_snapshot_command (
884+ base_config : str ,
885+ data_config : str ,
886+ experiment_config : str ,
887+ training_dataset : str ,
888+ membership_file : str ,
889+ sector_file : str ,
890+ output_root : str ,
891+ as_of : str | None ,
892+ holdings : int ,
893+ label_horizon_sessions : int ,
894+ recent_training_start : str ,
895+ use_static_training_dataset : bool ,
896+ skip_hyperparameter_tuning : bool ,
897+ validation_days : int ,
898+ selection_metric : str ,
899+ ) -> None :
900+ """Create a frozen paper-trading snapshot from latest yfinance data."""
901+ import json
902+
903+ from .experiments .live_forward_test import run_live_forward_snapshot
904+
905+ cfg = load_config (base_config , data_config )
906+ result = run_live_forward_snapshot (
907+ cfg ,
908+ experiment_config ,
909+ training_dataset = training_dataset ,
910+ membership_file = membership_file ,
911+ sector_file = sector_file ,
912+ output_root = output_root ,
913+ as_of = as_of ,
914+ holdings = holdings ,
915+ label_horizon_sessions = label_horizon_sessions ,
916+ recent_training_start = recent_training_start ,
917+ use_static_training_dataset = use_static_training_dataset ,
918+ tune_hyperparameters = not skip_hyperparameter_tuning ,
919+ validation_days = validation_days ,
920+ selection_metric = selection_metric ,
921+ )
922+ click .echo (json .dumps ({
923+ "decision_date" : result ["decision_date" ],
924+ "latest_price_date" : result ["latest_price_date" ],
925+ "training_mode" : result ["training_mode" ],
926+ "training_end" : result ["training_end" ],
927+ "label_horizon_sessions" : result ["label_horizon_sessions" ],
928+ "model" : result ["model" ],
929+ "model_params" : result ["model_params" ],
930+ "model_selection" : result ["model_selection" ],
931+ "live_scored_names" : result ["live_scored_names" ],
932+ "holdings" : result ["holdings" ],
933+ "output" : result ["output" ],
934+ }, indent = 2 ))
935+
936+
823937@main .command ("run-sparse-sentiment-ablation" )
824938@click .option (
825939 "--v2-base-config" , default = "configs/experiments/residual_rank_v2.yaml" ,
0 commit comments