diff --git a/workflows/exp14-optstop-v2/a_topk_baseline.yaml b/workflows/exp14-optstop-v2/a_topk_baseline.yaml new file mode 100644 index 0000000..0cce859 --- /dev/null +++ b/workflows/exp14-optstop-v2/a_topk_baseline.yaml @@ -0,0 +1,136 @@ +# ----------------------------------------------------------------------------- +# EXP 14 - Strategy A (baseline): reference model + TopkDropout. +# +# Model = RankICEnsembleLGBModel (5-seed RankIC-early-stopped LGB), the class +# wired by the tac-rd-rank-ensemble-isolated reference (run 0cea66d9...). +# Strategy = TopkDropout topk=10 n_drop=2 risk_degree=0.95 (the reference's own +# recorded backtest strategy), so this run reproduces the reference baseline on +# the same 50-ETF SP-5d panel. +# +# Run: +# rd_run_workflow config_path=experiments/workflows/exp14-optstop-v2/a_topk_baseline.yaml \ +# experiment_name=tac-rd-optstop-v2 +# ----------------------------------------------------------------------------- +{%- set LAKE = TAC_LAKE_DIR %} +{%- set UNIVERSE = "SPY,QQQ,DIA,IWM,MDY,VTI,VOO,VEA,VWO,VT,EFA,EEM,TLT,IEF,SHY,AGG,BND,LQD,HYG,JNK,EMB,GLD,SLV,USO,UNG,DBA,DBC,XLK,XLF,XLE,XLV,XLI,XLY,XLP,XLU,XLB,XLRE,ARKK,SMH,SOXX,IBB,XBI,ITA,XAR,ICLN,TAN,FDN,IGV,ESPO,REM" %} +{%- set SP_FIELDS = "sp_ret,sp_jump_ratio,sp_jump_flag,sp_jump_tail,sp_max_move,sp_rv1,sp_rv5,sp_rv22,sp_vol_ratio_5_22,sp_vol_ratio_1_22,sp_trend_slope_5,sp_trend_slope_20,sp_trend_slope_60,sp_logp,sp_hurst_exponent,sp_sig_level1_lead,sp_sig_level1_lag,sp_sig_level2_lead_lag,sp_sig_level2_lag_lead" %} + +qlib_init: + provider_uri: "{{ LAKE }}" + region: us + expression_cache: null + dataset_cache: null + + calendar_provider: + class: tac_qlib.data.providers.LakeCalendarProvider + kwargs: + lake_root: "{{ LAKE }}" + market: US + instrument_provider: + class: tac_qlib.data.providers.LakeInstrumentProvider + kwargs: + lake_root: "{{ LAKE }}" + market: US + markets: {} + feature_provider: + class: tac_qlib.data.providers.LakeFeatureProvider + kwargs: + lake_root: "{{ LAKE }}" + market: US + + exp_manager: + class: MLflowExpManager + module_path: qlib.workflow.expm + kwargs: + uri: "sqlite:///{{ LAKE }}/mlruns.db" + default_exp_name: "tac-rd-optstop-v2" + +task: + model: + class: RankICEnsembleLGBModel + module_path: tac_qlib.contrib.model.rank_ensemble + kwargs: + loss: mse + learning_rate: 0.02 + num_leaves: 31 + n_estimators: 3000 + num_boost_round: 3000 + early_stopping_rounds: 200 + min_data_in_leaf: 20 + lambda_l2: 0.5 + colsample_bytree: 0.8 + subsample: 0.8 + subsample_freq: 1 + reg_alpha: 0.1 + reg_lambda: 1.0 + seeds: "42,7,2026,99,123" + + dataset: + class: DatasetH + module_path: qlib.data.dataset + kwargs: + handler: + class: TACHandler + module_path: tac_qlib.contrib.data.handler + kwargs: + instruments: "{{ UNIVERSE }}" + start_time: 2015-01-03 + end_time: 2026-08-14 + fit_start_time: 2016-01-04 + fit_end_time: 2025-09-01 + freq: day + lake_root: "{{ LAKE }}" + market: US + label: "Ref($close,-6)/Ref($close,-1)-1" + feature_fields: "$open,$high,$low,$close,$vwap,$volume,{{ SP_FIELDS }}" + infer_processors: + - class: DropAllNaN + kwargs: {} + - class: ProcessInf + kwargs: {} + - class: CSRankNorm + kwargs: {} + - class: ZScoreNorm + kwargs: {} + - class: Fillna + kwargs: {} + segments: + train: [2016-01-04, 2025-09-01] + valid: [2025-09-03, 2026-01-03] + test: [2026-01-04, 2026-08-10] + + record: + - class: SignalRecord + module_path: qlib.workflow.record_temp + kwargs: {} + - class: SigAnaRecord + module_path: qlib.workflow.record_temp + kwargs: + ana_long_short: true + ann_scaler: 252 + - class: PortAnaRecord + module_path: qlib.workflow.record_temp + kwargs: + config: + strategy: + class: TopkDropoutStrategy + module_path: qlib.contrib.strategy + kwargs: + signal: "" + topk: 10 + n_drop: 2 + only_tradable: true + risk_degree: 0.95 + backtest: + start_time: 2026-01-04 + end_time: 2026-08-10 + account: 1000000 + benchmark: SPY + exchange_kwargs: + codes: "{{ UNIVERSE }}" + deal_price: $close + freq: day + open_cost: 0.0005 + close_cost: 0.0015 + min_cost: 5.0 + risk_analysis_freq: 1d diff --git a/workflows/exp14-optstop-v2/b_optstop_v2.yaml b/workflows/exp14-optstop-v2/b_optstop_v2.yaml new file mode 100644 index 0000000..9b84b06 --- /dev/null +++ b/workflows/exp14-optstop-v2/b_optstop_v2.yaml @@ -0,0 +1,149 @@ +# ----------------------------------------------------------------------------- +# EXP 14 - Strategy B (enhanced): reference model + OptimalStopControlV2. +# +# Model = RankICEnsembleLGBModel (5-seed RankIC-early-stopped LGB), identical to +# Strategy A. Strategy = OptimalStopControlV2 (tac_qlib.contrib.strategy. +# optimal_stop_v2) with the controls that address OptimalStopControl's documented +# weaknesses: +# - turnover / cost control: rebalance_band=0.05 (skip small rebalances), +# cooldown_days=3 (no whipsaw re-entries), max_turnover=0.30 (cap daily +# traded notional, priority exits > opens > rebalances) +# - robust thresholds (no valid-window overfit): entry 0.85 / exit 0.70 / +# max_hold 10 / min_hold 2 / sl -0.08 +# Sizing = equal-weight control (risk_degree fraction of total value split +# across targets) - the "proper allocation" that replaces cash-heuristic sizing. +# +# Run: +# rd_run_workflow config_path=experiments/workflows/exp14-optstop-v2/b_optstop_v2.yaml \ +# experiment_name=tac-rd-optstop-v2 +# ----------------------------------------------------------------------------- +{%- set LAKE = TAC_LAKE_DIR %} +{%- set UNIVERSE = "SPY,QQQ,DIA,IWM,MDY,VTI,VOO,VEA,VWO,VT,EFA,EEM,TLT,IEF,SHY,AGG,BND,LQD,HYG,JNK,EMB,GLD,SLV,USO,UNG,DBA,DBC,XLK,XLF,XLE,XLV,XLI,XLY,XLP,XLU,XLB,XLRE,ARKK,SMH,SOXX,IBB,XBI,ITA,XAR,ICLN,TAN,FDN,IGV,ESPO,REM" %} +{%- set SP_FIELDS = "sp_ret,sp_jump_ratio,sp_jump_flag,sp_jump_tail,sp_max_move,sp_rv1,sp_rv5,sp_rv22,sp_vol_ratio_5_22,sp_vol_ratio_1_22,sp_trend_slope_5,sp_trend_slope_20,sp_trend_slope_60,sp_logp,sp_hurst_exponent,sp_sig_level1_lead,sp_sig_level1_lag,sp_sig_level2_lead_lag,sp_sig_level2_lag_lead" %} + +qlib_init: + provider_uri: "{{ LAKE }}" + region: us + expression_cache: null + dataset_cache: null + + calendar_provider: + class: tac_qlib.data.providers.LakeCalendarProvider + kwargs: + lake_root: "{{ LAKE }}" + market: US + instrument_provider: + class: tac_qlib.data.providers.LakeInstrumentProvider + kwargs: + lake_root: "{{ LAKE }}" + market: US + markets: {} + feature_provider: + class: tac_qlib.data.providers.LakeFeatureProvider + kwargs: + lake_root: "{{ LAKE }}" + market: US + + exp_manager: + class: MLflowExpManager + module_path: qlib.workflow.expm + kwargs: + uri: "sqlite:///{{ LAKE }}/mlruns.db" + default_exp_name: "tac-rd-optstop-v2" + +task: + model: + class: RankICEnsembleLGBModel + module_path: tac_qlib.contrib.model.rank_ensemble + kwargs: + loss: mse + learning_rate: 0.02 + num_leaves: 31 + n_estimators: 3000 + num_boost_round: 3000 + early_stopping_rounds: 200 + min_data_in_leaf: 20 + lambda_l2: 0.5 + colsample_bytree: 0.8 + subsample: 0.8 + subsample_freq: 1 + reg_alpha: 0.1 + reg_lambda: 1.0 + seeds: "42,7,2026,99,123" + + dataset: + class: DatasetH + module_path: qlib.data.dataset + kwargs: + handler: + class: TACHandler + module_path: tac_qlib.contrib.data.handler + kwargs: + instruments: "{{ UNIVERSE }}" + start_time: 2015-01-03 + end_time: 2026-08-14 + fit_start_time: 2016-01-04 + fit_end_time: 2025-09-01 + freq: day + lake_root: "{{ LAKE }}" + market: US + label: "Ref($close,-6)/Ref($close,-1)-1" + feature_fields: "$open,$high,$low,$close,$vwap,$volume,{{ SP_FIELDS }}" + infer_processors: + - class: DropAllNaN + kwargs: {} + - class: ProcessInf + kwargs: {} + - class: CSRankNorm + kwargs: {} + - class: ZScoreNorm + kwargs: {} + - class: Fillna + kwargs: {} + segments: + train: [2016-01-04, 2025-09-01] + valid: [2025-09-03, 2026-01-03] + test: [2026-01-04, 2026-08-10] + + record: + - class: SignalRecord + module_path: qlib.workflow.record_temp + kwargs: {} + - class: SigAnaRecord + module_path: qlib.workflow.record_temp + kwargs: + ana_long_short: true + ann_scaler: 252 + - class: PortAnaRecord + module_path: qlib.workflow.record_temp + kwargs: + config: + strategy: + class: OptimalStopControlV2 + module_path: tac_qlib.contrib.strategy.optimal_stop_v2 + kwargs: + signal: "" + topk: 10 + entry_pct: 0.85 + exit_pct: 0.70 + max_hold_days: 10 + min_hold_days: 2 + sl: -0.08 + risk_degree: 0.95 + notional: 20000 + rebalance_band: 0.05 + cooldown_days: 3 + max_turnover: 0.30 + backtest: + start_time: 2026-01-04 + end_time: 2026-08-10 + account: 1000000 + benchmark: SPY + exchange_kwargs: + codes: "{{ UNIVERSE }}" + deal_price: $close + freq: day + open_cost: 0.0005 + close_cost: 0.0015 + min_cost: 5.0 + risk_analysis_freq: 1d