From 1b5f4d1e76fa63cbc76965a0986dc9899b62a40d Mon Sep 17 00:00:00 2001 From: zhaoli Date: Fri, 14 Aug 2026 22:39:44 +0000 Subject: [PATCH] exp 15: fractional-Kelly sizing A/B (A TopkDropout baseline, B KellyWeightStrategy) with parallel RankICEnsembleLGBModel --- .../exp15-kelly-size/a_topk_baseline.yaml | 136 +++++++++++++++++ .../exp15-kelly-size/b_kelly_weight.yaml | 143 ++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 workflows/exp15-kelly-size/a_topk_baseline.yaml create mode 100644 workflows/exp15-kelly-size/b_kelly_weight.yaml diff --git a/workflows/exp15-kelly-size/a_topk_baseline.yaml b/workflows/exp15-kelly-size/a_topk_baseline.yaml new file mode 100644 index 0000000..b79049f --- /dev/null +++ b/workflows/exp15-kelly-size/a_topk_baseline.yaml @@ -0,0 +1,136 @@ +# ----------------------------------------------------------------------------- +# EXP 15 - Strategy A (baseline): parallel reference model + TopkDropout. +# +# Model = RankICEnsembleLGBModel with PARALLEL=5 (thread-pool seed training, +# rank_ensemble.py) - the speedup means the 5-seed 3000-round ensemble trains in +# ~1/5th the wall time of the serial reference. Strategy = TopkDropout topk=10 +# n_drop=2 risk_degree=0.95 (the reference's recorded strategy). +# +# Run: +# rd_run_workflow config_path=experiments/workflows/exp15-kelly-size/a_topk_baseline.yaml \ +# experiment_name=tac-rd-kelly-size +# ----------------------------------------------------------------------------- +{%- 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-kelly-size" + +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" + parallel: 5 + + 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/exp15-kelly-size/b_kelly_weight.yaml b/workflows/exp15-kelly-size/b_kelly_weight.yaml new file mode 100644 index 0000000..f67de51 --- /dev/null +++ b/workflows/exp15-kelly-size/b_kelly_weight.yaml @@ -0,0 +1,143 @@ +# ----------------------------------------------------------------------------- +# EXP 15 - Strategy B: parallel reference model + KellyWeightStrategy. +# +# Model = RankICEnsembleLGBModel PARALLEL=5 (same as A). Strategy = +# KellyWeightStrategy (tac_qlib.contrib.strategy.kelly_weight): applies the +# Kelly criterion to position SIZING - +# f* = kelly_fraction * mu_i / var_i (Gaussian Kelly, mu_i > 0) +# with per-name mu/var estimated from the rolling signal history (no +# lookahead), floored/capped and normalized to the risk_degree leverage budget. +# This is the piece the reference's equal-weight TopkDropout never tunes: it +# weights names by edge/risk instead of equal-weight top-k. +# +# Run: +# rd_run_workflow config_path=experiments/workflows/exp15-kelly-size/b_kelly_weight.yaml \ +# experiment_name=tac-rd-kelly-size +# ----------------------------------------------------------------------------- +{%- 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-kelly-size" + +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" + parallel: 5 + + 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: KellyWeightStrategy + module_path: tac_qlib.contrib.strategy.kelly_weight + kwargs: + signal: "" + lookback: 20 + min_obs: 10 + kelly_fraction: 0.5 + max_weight: 0.15 + min_weight: 0.0 + risk_degree: 0.95 + 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