VectorBT量化入门系列:第六章 VectorBT实战案例:机器学习预测策略
VectorBT量化入门系列:第六章 VectorBT实战案例:机器学习预测策略
本教程专为中高级开发者设计,系统讲解VectorBT技术在量化交易中的应用。通过结合Tushare数据源和TA-Lib技术指标,深度探索策略开发、回测优化与风险评估的核心方法。从数据获取到策略部署,全面提升量化交易能力,助力开发者构建高效、稳健的交易系统。
文中内容仅限技术学习与代码实践参考,市场存在不确定性,技术分析需谨慎验证,不构成任何投资建议。适合量化新手建立系统认知,为策略开发打下基础。
学习对象
- 中高级水平的开发者和数据分析师
- 具备 Python 编程基础和一定的数据分析能力
- 对量化交易和金融数据处理有一定了解
- 熟悉 A 股市场,了解 Tushare 数据源和 TA-Lib 技术指标
教程目标
- 系统学习 VectorBT 技术,掌握其在量化交易中的应用
- 熟练使用 Tushare 数据源获取 A 股市场数据,并使用 Parquet 文件存储
- 掌握基于 VectorBT 的策略开发、回测和性能评估流程
- 学会使用 TA-Lib 计算技术指标,并将其应用于交易策略
- 理解并实现多因子策略、机器学习策略等高级策略
- 掌握策略优化、风险管理以及策略组合的方法
- 能够独立构建和评估量化交易策略,并部署到生产环境
教程目录
第一章 VectorBT基础与环境搭建
1.1 VectorBT简介与应用场景
1.2 环境搭建与依赖安装
1.3 数据源与Tushare集成
1.4 数据存储与Parquet文件格式
第二章 VectorBT核心功能与数据处理
2.1 数据加载与预处理
2.2 时间序列数据处理
2.3 技术指标计算与TA-Lib集成
2.4 数据可视化与探索性分析
第三章 VectorBT策略回测基础
3.1 策略定义与实现
3.2 回测流程与关键参数
3.3 性能评估指标与解读
3.4 策略优化与参数调整
第四章 高级策略开发与优化
4.1 多因子策略开发
4.2 机器学习策略集成
4.3 风险管理与交易成本模拟
4.4 策略组合与资产配置
第五章 VectorBT性能评估与分析
5.1 性能评估框架
5.2 统计指标与回测报告
5.3 敏感性分析与压力测试
5.4 策略对比与选择标准
第六章 VectorBT实战案例
6.1 策略逻辑
6.2 实现步骤
6.3 代码执行
6.4 结果分析
第六章 VectorBT实战案例:机器学习预测策略
本章将通过具体案例深入学习VectorBT
的应用。我们将使用机器学习模型预测股票价格的走势。实现一个完整的股票交易策略流程,从数据预处理、特征工程、模型训练与验证、信号生成与调试到策略回测和过拟合检测。通过使用多种技术指标和LightGBM
分类器,该策略能够生成有效的交易信号,并通过回测验证其性能。
6.1 策略逻辑
- 使用历史数据训练机器学习模型。
- 根据模型预测结果生成交易信号。
核心流程:
数据加载 -> 特征工程 -> 标签生成 -> 模型训练 -> 信号生成 -> 风险管理 -> 回测验证
数学公式:
持有期回报率
R t + 1 → t + H O L D I N G _ P E R I O D = C l o s e t + H O L D I N G _ P E R I O D O p e n t + 1 − 1 R_{t+1→t+HOLDING\_PERIOD} = \frac{Close_{t+HOLDING\_PERIOD}}{Open_{t+1}} - 1 Rt+1→t+HOLDING_PERIOD=Opent+1Closet+HOLDING_PERIOD−1
示例设置:
# 全局种子设置
np.random.seed(42)
random.seed(42)# 配置参数
HOLDING_PERIOD = 2 # 持有到 t+HOLDING_PERIOD 日收盘(跨交易日持仓)
LOOKBACK_WINDOW = 20 # 回看窗口
6.2 实现步骤
- 加载数据
def load_data(filepath):"""加载并预处理数据"""data = pd.read_parquet(filepath)print(f"Data Stock: {len(data)}")return data
- 特征工程
def create_features(data):"""为给定的数据集创建技术指标特征。使用closed="left"确保滚动窗口不包含当前数据滞后特征避免未来信息泄漏:param data: 包含OHLCV数据的DataFrame:return: 包含新增技术指标特征的DataFrame"""data = data.assign(returns=data["close"].pct_change().shift(1),volatility_5=lambda x: x["returns"].rolling(5, closed="left").std().shift(1),volume_change=lambda x: x["vol"].pct_change(3).shift(1),)# 有效技术指标(经实证检验的因子)data["macd"], data["macd_signal"], _ = talib.MACD(data["close"])data["macd"] = data["macd"].shift(1)data["obv"] = talib.OBV(data["close"], data["vol"]).shift(1)# 布林带特征upper, mid, lower = talib.BBANDS(data["close"], timeperiod=20)data["bb_width"] = ((upper - lower) / mid).shift(1)data["bb_position"] = ((data["close"] - lower) / (upper - lower)).shift(1)# 量价背离指标price_high = data["high"].rolling(5).mean().shift(1)volume_high = data["vol"].rolling(5).mean().shift(1)data["pv_divergence"] = (price_high.diff() * volume_high.diff() < 0).astype(int)return data.dropna() # 删除残余缺失
- 标签工程
def create_labels(data):"""标签生成逻辑(严格时间对齐):- 预测时间:`t`日- 买入时间:`t+1`开盘- 卖出时间:`t+HOLDING_PERIOD`收盘- 持仓天数:`HOLDING_PERIOD`- 时间轴: t t+1 t+HOLDING_PERIOD预测 买入 卖出- 持有期回报率:$ R_{t+1→t+HOLDING\_PERIOD} = \frac{Close_{t+HOLDING\_PERIOD}}{Open_{t+1}} - 1 $"""# 计算持有期收益(严格避免未来信息)buy_price = data["open"].shift(-1) # t+1开盘价sell_price = data["close"].shift(-HOLDING_PERIOD) # t+HOLDING_PERIOD收盘价future_returns = sell_price / buy_price - 1 # 持有期回报率# 生成预测概率标签data["target"] = np.where(future_returns > 0, 1, 0)return data.dropna() # 删除含有NaN值的行
- 特征选择
def select_features(X_train, y_train):"""时间序列特征选择"""estimator = RandomForestClassifier(n_estimators=100,n_jobs=1,random_state=42,max_features="sqrt", # 增加确定性)selector = RFE(estimator, n_features_to_select=5)selector.fit(X_train, y_train)return selector
- 模型训练
def train_model(X, y):"""训练模型并进行特征选择"""# 时间序列交叉验证tscv = TimeSeriesSplit(n_splits=5)callbacks = [lgb.early_stopping(stopping_rounds=50, first_metric_only=True, verbose=False), # 早停回调lgb.log_evaluation(period=0), # 禁用日志输出]def objective(trial):params = {"objective": "binary","metric": "auc","boosting_type": "goss","num_leaves": trial.suggest_int("num_leaves", 16, 64),"learning_rate": trial.suggest_float("lr", 0.02, 0.2, log=True),"max_depth": trial.suggest_int("max_depth", 3, 7),"min_child_samples": trial.suggest_int("min_child_samples", 20, 200),"reg_alpha": trial.suggest_float("reg_alpha", 1e-8, 10.0),"reg_lambda": trial.suggest_float("reg_lambda", 1e-8, 10.0),"feature_fraction": trial.suggest_float("feature_frac", 0.6, 0.95),"bagging_freq": trial.suggest_int("bagging_freq", 3, 7),"verbosity": -1,"force_col_wise": True,"random_state": 42,"num_threads": 1,"deterministic": True,}fold_scores = [] # 保存每一折的AUCfold_iterations = [] # 保存每个fold的最佳迭代次数for train_idx, valid_idx in tscv.split(X):# 分割数据X_train_fold, X_valid_fold = (X.iloc[train_idx],X.iloc[valid_idx],)y_train_fold, y_valid_fold = (y.iloc[train_idx],y.iloc[valid_idx],)# 特征选择selector = select_features(X_train_fold, y_train_fold)X_train_selected = selector.transform(X_train_fold)X_valid_selected = selector.transform(X_valid_fold)# 数据标准化scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train_selected)X_valid_scaled = scaler.transform(X_valid_selected)# 转换为LightGBM Datasettrain_data = lgb.Dataset(X_train_scaled, label=y_train_fold)valid_data = lgb.Dataset(X_valid_scaled, label=y_valid_fold, reference=train_data)# 训练模型(启用早停)model = lgb.train(params,train_data,valid_sets=[valid_data],callbacks=callbacks,num_boost_round=1000,)# 记录最佳迭代的AUCfold_scores.append(model.best_score["valid_0"]["auc"])# 记录最佳迭代次数fold_iterations.append(model.best_iteration)# 记录交叉验证的平均迭代次数trial.set_user_attr("avg_best_iteration", int(np.mean(fold_iterations)))# 返回平均AUC作为Optuna优化目标return np.mean(fold_scores)# 创建Optuna study并优化sampler = TPESampler(seed=42) # 使用TPE采样器study = optuna.create_study(direction="maximize",sampler=sampler,pruner=optuna.pruners.MedianPruner(), # n_warmup_steps=10)study.optimize(objective, n_trials=10, n_jobs=1, timeout=3600, show_progress_bar=True) # 优化超参数 n_jobs=1, 禁用并行试验,保证结果可以复现best_auc = study.best_value # 获取最优 AUCbest_params = study.best_params # 获取最优参数best_params.update({"objective": "binary","metric": "auc","boosting_type": "goss","verbosity": -1,"force_col_wise": True,"random_state": 42,"num_threads": 1,"deterministic": True,})# 输出最佳参数print("最佳参数:", best_params)print("最佳AUC:", best_auc)# 划分训练集和测试集split_idx = int(len(X) * 0.8)X_train, X_test = X.iloc[:split_idx], X.iloc[split_idx:]y_train, y_test = y.iloc[:split_idx], y.iloc[split_idx:]best_selector = select_features(X_train, y_train)X_train = best_selector.transform(X_train)X_test = best_selector.transform(X_test)# 标准化:仅在训练数据上拟合,避免泄露scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)# 训练最终模型(可关闭早停,使用更多迭代)final_model = lgb.train(best_params,lgb.Dataset(X_train_scaled, label=y_train),num_boost_round=study.best_trial.user_attrs["avg_best_iteration"],)# 选择特征print(f"Selected features: {best_selector.feature_names_in_}")# 过拟合检查print("\n过拟合检测:")print(f"Train AUC: {roc_auc_score(y_train, final_model.predict(X_train)):.4f}")print(f"Test AUC: {roc_auc_score(y_test, final_model.predict(X_test)):.4f}")return final_model, best_selector, scaler
- 调试信号生成
def debug_signal_generation(data):"""打印关键指标的统计信息,用于调试信号生成过程。:param data: 包含预测概率和其他指标的DataFrame"""# 打印关键指标分布print("\n信号生成关键指标统计:")print(f"预测概率 > 0.5 的比例: {len(data[data['pred_proba'] > 0.5])/len(data):.2%}")print(f"成交量过滤触发比例: {len(data[data['vol'] > data['vol'].rolling(20).mean()])/len(data):.2%}")print(f"处于200日均线上方比例: {len(data[data['close'] > data['close'].rolling(200).mean()])/len(data):.2%}")
- 交易信号生成
def generate_signals(model, selector, scaler, data):"""生成带持仓周期管理的交易信号"""data = data.copy()# 特征处理X = selector.transform(data[selector.feature_names_in_])X_scaled = scaler.transform(X)data["pred_proba"] = model.predict(X_scaled)print("\n预测概率分布:")print(data["pred_proba"].describe())# 调试信号生成debug_signal_generation(data)# 生成交易信号:简单实现示例data["entry_signal"] = np.where(data["pred_proba"] > data["pred_proba"].mean(), 1, 0)# 遍历数据,为每个买入信号设置对应的卖出信号,t+HOLDING_PERIOD收盘卖出data["exit_signal"] = 0valid_dates = data.index # 确保索引已排序for entry_idx in data[data["entry_signal"] == 1].index:current_pos = valid_dates.get_loc(entry_idx)exit_pos = current_pos + HOLDING_PERIODif exit_pos < len(valid_dates):exit_idx = valid_dates[exit_pos]data.loc[exit_idx, "exit_signal"] = 1# 信号可视化检查print("\n信号统计:")print(f"做多信号数量: {data['entry_signal'].sum()}")print(f"做空信号数量: {data['exit_signal'].sum()}")return data
- 风险控制
def apply_risk_management(data):"""简单波动率仓位控制实现逻辑:1. 计算最近20个交易日的波动率中位数2. 当当前波动率超过中位数时,仓位降至50%3. 否则保持80%仓位"""data = data.copy()# 计算波动率阈值(使用滞后数据避免未来信息)data["vol_threshold"] = (data["volatility_5"].shift(1) # 使用t-1日数据计算.rolling(20, min_periods=5).median() # 20日波动率中位数)# 动态仓位设置data["position_size"] = np.where(data["volatility_5"] > data["vol_threshold"],0.5, # 高波动时50%仓位0.8, # 正常情况80%仓位)# 强制仓位范围限制data["position_size"] = data["position_size"].clip(0.3, 0.8)return data
- 回测引擎
def backtest_strategy(data):"""执行回测"""pf = vbt.Portfolio.from_signals(close=data["close"],open=data["open"], # 使用开盘价作为买入价格entries=data["entry_signal"] == 1,exits=data["exit_signal"] == 1,freq="1D",init_cash=1e5, # 初始资金 10万fees=0.0005, # 佣金万五slippage=0.0015, # 滑点千1.5size_type="percent",size=data["position_size"], # 动态仓位大小sl_stop=0.05, # 固定止损)return pf
- 结果分析
def plot_results(pf):"""可视化策略绩效结果"""pf.plot(subplots=["orders", "trade_pnl", "cum_returns", "drawdowns"]).show()
- 主程序
if __name__ == "__main__":# 数据准备data = load_data("./data/600000.SH.parquet")data = create_features(data)data = create_labels(data)# 按时间分割数据集split_date = pd.to_datetime("2024-01-01")train_data = data[data.index < split_date]test_data = data[data.index >= split_date]# 检查标签分布print(f"Trant Data: {len(train_data)}")print(train_data["target"].value_counts())print(f"Test Data: {len(test_data)}")print(test_data["target"].value_counts())# 特征指标features = ["volatility_5","volume_change","macd","obv","bb_width","bb_position","pv_divergence",]# 模型训练model, selector, scaler = train_model(train_data[features], train_data["target"])# 信号生成data = generate_signals(model, selector, scaler, test_data)data = apply_risk_management(data)# 回测执行pf = backtest_strategy(data)# 结果分析print("\n策略绩效:")print(pf.stats())# 可视化plot_results(pf)
6.3 代码执行
import random
import lightgbm as lgb
import numpy as np
import optuna
import pandas as pd
import talib
import vectorbt as vbt
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import RFE
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import TimeSeriesSplit
from sklearn.preprocessing import StandardScaler
from optuna.samplers import TPESampler# 该策略采用机器学习驱动的量化交易框架,核心流程如下:
# 数据加载 -> 特征工程 -> 标签生成 -> 模型训练 -> 信号生成 -> 风险管理 -> 回测验证# 全局种子设置
np.random.seed(42)
random.seed(42)# 配置参数
HOLDING_PERIOD = 2 # 持有到 t+HOLDING_PERIOD 日收盘(跨交易日持仓)
LOOKBACK_WINDOW = 20 # 回看窗口# ==================== 加载数据 ====================
def load_data(filepath):"""加载并预处理数据"""data = pd.read_parquet(filepath)print(f"Data Stock: {len(data)}")return data# ==================== 特征工程 ====================
def create_features(data):"""为给定的数据集创建技术指标特征。使用closed="left"确保滚动窗口不包含当前数据滞后特征避免未来信息泄漏:param data: 包含OHLCV数据的DataFrame:return: 包含新增技术指标特征的DataFrame"""data = data.assign(returns=data["close"].pct_change().shift(1),volatility_5=lambda x: x["returns"].rolling(5, closed="left").std().shift(1),volume_change=lambda x: x["vol"].pct_change(3).shift(1),)# 有效技术指标(经实证检验的因子)data["macd"], data["macd_signal"], _ = talib.MACD(data["close"])data["macd"] = data["macd"].shift(1)data["obv"] = talib.OBV(data["close"], data["vol"]).shift(1)# 布林带特征upper, mid, lower = talib.BBANDS(data["close"], timeperiod=20)data["bb_width"] = ((upper - lower) / mid).shift(1)data["bb_position"] = ((data["close"] - lower) / (upper - lower)).shift(1)# 量价背离指标price_high = data["high"].rolling(5).mean().shift(1)volume_high = data["vol"].rolling(5).mean().shift(1)data["pv_divergence"] = (price_high.diff() * volume_high.diff() < 0).astype(int)return data.dropna() # 删除残余缺失# ==================== 标签工程 ====================
def create_labels(data):"""标签生成逻辑(严格时间对齐):- 预测时间:`t`日- 买入时间:`t+1`开盘- 卖出时间:`t+HOLDING_PERIOD`收盘- 持仓天数:`HOLDING_PERIOD`- 时间轴: t t+1 t+HOLDING_PERIOD预测 买入 卖出- 持有期回报率:$ R_{t+1→t+HOLDING\_PERIOD} = \frac{Close_{t+HOLDING\_PERIOD}}{Open_{t+1}} - 1 $"""# 计算持有期收益(严格避免未来信息)buy_price = data["open"].shift(-1) # t+1开盘价sell_price = data["close"].shift(-HOLDING_PERIOD) # t+HOLDING_PERIOD收盘价future_returns = sell_price / buy_price - 1 # 持有期回报率# 生成预测概率标签data["target"] = np.where(future_returns > 0, 1, 0)return data.dropna() # 删除含有NaN值的行# ==================== 特征选择 ====================
def select_features(X_train, y_train):"""时间序列特征选择"""estimator = RandomForestClassifier(n_estimators=100,n_jobs=1,random_state=42,max_features="sqrt", # 增加确定性)selector = RFE(estimator, n_features_to_select=5)selector.fit(X_train, y_train)return selector# ==================== 模型训练 ====================
def train_model(X, y):"""训练模型并进行特征选择"""# 时间序列交叉验证tscv = TimeSeriesSplit(n_splits=5)callbacks = [lgb.early_stopping(stopping_rounds=50, first_metric_only=True, verbose=False), # 早停回调lgb.log_evaluation(period=0), # 禁用日志输出]def objective(trial):params = {"objective": "binary","metric": "auc","boosting_type": "goss","num_leaves": trial.suggest_int("num_leaves", 16, 64),"learning_rate": trial.suggest_float("lr", 0.02, 0.2, log=True),"max_depth": trial.suggest_int("max_depth", 3, 7),"min_child_samples": trial.suggest_int("min_child_samples", 20, 200),"reg_alpha": trial.suggest_float("reg_alpha", 1e-8, 10.0),"reg_lambda": trial.suggest_float("reg_lambda", 1e-8, 10.0),"feature_fraction": trial.suggest_float("feature_frac", 0.6, 0.95),"bagging_freq": trial.suggest_int("bagging_freq", 3, 7),"verbosity": -1,"force_col_wise": True,"random_state": 42,"num_threads": 1,"deterministic": True,}fold_scores = [] # 保存每一折的AUCfold_iterations = [] # 保存每个fold的最佳迭代次数for train_idx, valid_idx in tscv.split(X):# 分割数据X_train_fold, X_valid_fold = (X.iloc[train_idx],X.iloc[valid_idx],)y_train_fold, y_valid_fold = (y.iloc[train_idx],y.iloc[valid_idx],)# 特征选择selector = select_features(X_train_fold, y_train_fold)X_train_selected = selector.transform(X_train_fold)X_valid_selected = selector.transform(X_valid_fold)# 数据标准化scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train_selected)X_valid_scaled = scaler.transform(X_valid_selected)# 转换为LightGBM Datasettrain_data = lgb.Dataset(X_train_scaled, label=y_train_fold)valid_data = lgb.Dataset(X_valid_scaled, label=y_valid_fold, reference=train_data)# 训练模型(启用早停)model = lgb.train(params,train_data,valid_sets=[valid_data],callbacks=callbacks,num_boost_round=1000,)# 记录最佳迭代的AUCfold_scores.append(model.best_score["valid_0"]["auc"])# 记录最佳迭代次数fold_iterations.append(model.best_iteration)# 记录交叉验证的平均迭代次数trial.set_user_attr("avg_best_iteration", int(np.mean(fold_iterations)))# 返回平均AUC作为Optuna优化目标return np.mean(fold_scores)# 创建Optuna study并优化sampler = TPESampler(seed=42) # 使用TPE采样器study = optuna.create_study(direction="maximize",sampler=sampler,pruner=optuna.pruners.MedianPruner(), # n_warmup_steps=10)study.optimize(objective, n_trials=10, n_jobs=1, timeout=3600, show_progress_bar=True) # 优化超参数 n_jobs=1, 禁用并行试验,保证结果可以复现best_auc = study.best_value # 获取最优 AUCbest_params = study.best_params # 获取最优参数best_params.update({"objective": "binary","metric": "auc","boosting_type": "goss","verbosity": -1,"force_col_wise": True,"random_state": 42,"num_threads": 1,"deterministic": True,})# 输出最佳参数print("最佳参数:", best_params)print("最佳AUC:", best_auc)# 划分训练集和测试集split_idx = int(len(X) * 0.8)X_train, X_test = X.iloc[:split_idx], X.iloc[split_idx:]y_train, y_test = y.iloc[:split_idx], y.iloc[split_idx:]best_selector = select_features(X_train, y_train)X_train = best_selector.transform(X_train)X_test = best_selector.transform(X_test)# 标准化:仅在训练数据上拟合,避免泄露scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)# 训练最终模型(可关闭早停,使用更多迭代)final_model = lgb.train(best_params,lgb.Dataset(X_train_scaled, label=y_train),num_boost_round=study.best_trial.user_attrs["avg_best_iteration"],)# 选择特征print(f"Selected features: {best_selector.feature_names_in_}")# 过拟合检查print("\n过拟合检测:")print(f"Train AUC: {roc_auc_score(y_train, final_model.predict(X_train)):.4f}")print(f"Test AUC: {roc_auc_score(y_test, final_model.predict(X_test)):.4f}")return final_model, best_selector, scaler# ==================== 调试信号生成 ====================
def debug_signal_generation(data):"""打印关键指标的统计信息,用于调试信号生成过程。:param data: 包含预测概率和其他指标的DataFrame"""# 打印关键指标分布print("\n信号生成关键指标统计:")print(f"预测概率 > 0.5 的比例: {len(data[data['pred_proba'] > 0.5])/len(data):.2%}")print(f"成交量过滤触发比例: {len(data[data['vol'] > data['vol'].rolling(20).mean()])/len(data):.2%}")print(f"处于200日均线上方比例: {len(data[data['close'] > data['close'].rolling(200).mean()])/len(data):.2%}")# ==================== 交易信号生成 ====================
def generate_signals(model, selector, scaler, data):"""生成带持仓周期管理的交易信号"""data = data.copy()# 特征处理X = selector.transform(data[selector.feature_names_in_])X_scaled = scaler.transform(X)data["pred_proba"] = model.predict(X_scaled)print("\n预测概率分布:")print(data["pred_proba"].describe())# 调试信号生成debug_signal_generation(data)# 生成交易信号:简单实现示例data["entry_signal"] = np.where(data["pred_proba"] > data["pred_proba"].mean(), 1, 0)# 遍历数据,为每个买入信号设置对应的卖出信号,t+HOLDING_PERIOD收盘卖出data["exit_signal"] = 0valid_dates = data.index # 确保索引已排序for entry_idx in data[data["entry_signal"] == 1].index:current_pos = valid_dates.get_loc(entry_idx)exit_pos = current_pos + HOLDING_PERIODif exit_pos < len(valid_dates):exit_idx = valid_dates[exit_pos]data.loc[exit_idx, "exit_signal"] = 1# 信号可视化检查print("\n信号统计:")print(f"做多信号数量: {data['entry_signal'].sum()}")print(f"做空信号数量: {data['exit_signal'].sum()}")return data# ==================== 风险控制 ====================
def apply_risk_management(data):"""简单波动率仓位控制实现逻辑:1. 计算最近20个交易日的波动率中位数2. 当当前波动率超过中位数时,仓位降至50%3. 否则保持80%仓位"""data = data.copy()# 计算波动率阈值(使用滞后数据避免未来信息)data["vol_threshold"] = (data["volatility_5"].shift(1) # 使用t-1日数据计算.rolling(20, min_periods=5).median() # 20日波动率中位数)# 动态仓位设置data["position_size"] = np.where(data["volatility_5"] > data["vol_threshold"],0.5, # 高波动时50%仓位0.8, # 正常情况80%仓位)# 强制仓位范围限制data["position_size"] = data["position_size"].clip(0.3, 0.8)return data# ==================== 回测引擎 ====================
def backtest_strategy(data):"""执行回测"""pf = vbt.Portfolio.from_signals(close=data["close"],open=data["open"], # 使用开盘价作为买入价格entries=data["entry_signal"] == 1,exits=data["exit_signal"] == 1,freq="1D",init_cash=1e5, # 初始资金 10万fees=0.0005, # 佣金万五slippage=0.0015, # 滑点千1.5size_type="percent",size=data["position_size"], # 动态仓位大小sl_stop=0.05, # 固定止损)return pf# ==================== 结果分析 ====================
def plot_results(pf):"""可视化策略绩效结果"""pf.plot(subplots=["orders", "trade_pnl", "cum_returns", "drawdowns"]).show()# ==================== 主程序 ====================
if __name__ == "__main__":# 数据准备# 000858.SZ# 600000.SH# 600036.SH# 600519.SHdata = load_data("./data/600000.SH.parquet")data = create_features(data)data = create_labels(data)# 按时间分割数据集split_date = pd.to_datetime("2024-01-01")train_data = data[data.index < split_date]test_data = data[data.index >= split_date]# 检查标签分布print(f"Trant Data: {len(train_data)}")print(train_data["target"].value_counts())print(f"Test Data: {len(test_data)}")print(test_data["target"].value_counts())features = ["volatility_5","volume_change","macd","obv","bb_width","bb_position","pv_divergence",]# 模型训练model, selector, scaler = train_model(train_data[features], train_data["target"])# 信号生成data = generate_signals(model, selector, scaler, test_data)data = apply_risk_management(data)# 回测执行pf = backtest_strategy(data)# 结果分析print("\n策略绩效:")print(pf.stats())# 可视化plot_results(pf)
6.4 结果分析
Data Stock: 3596
Trant Data: 3320
target
0 1765
1 1555
Name: count, dtype: int64
Test Data: 242
target
1 129
0 113
Name: count, dtype: int64最佳参数: {'num_leaves': 30, 'lr': 0.06695552617031866, 'max_depth': 5, 'min_child_samples': 72, 'reg_alpha': 6.118528951105265, 'reg_lambda': 1.3949386151254795, 'feature_frac': 0.7022506269873263, 'bagging_freq': 4, 'objective': 'binary', 'metric': 'auc', 'boosting_type': 'goss', 'verbosity': -1, 'force_col_wise': True, 'random_state': 42, 'num_threads': 1, 'deterministic': True}
最佳AUC: 0.5476021776135598
Selected features: ['volatility_5' 'volume_change' 'macd' 'obv' 'bb_width' 'bb_position''pv_divergence']过拟合检测:
Train AUC: 0.4930
Test AUC: 0.4848预测概率分布:
count 242.000000
mean 0.426774
std 0.095937
min 0.211996
25% 0.356984
50% 0.419012
75% 0.491315
max 0.729235
Name: pred_proba, dtype: float64信号生成关键指标统计:
预测概率 > 0.5 的比例: 23.14%
成交量过滤触发比例: 39.67%
处于200日均线上方比例: 17.77%信号统计:
做多信号数量: 115
做空信号数量: 115策略绩效:
Start 2024-01-02 00:00:00
End 2024-12-31 00:00:00
Period 242 days 00:00:00
Start Value 100000.0
End Value 115254.345896
Total Return [%] 15.254346
Benchmark Return [%] 55.909091
Max Gross Exposure [%] 80.670607
Total Fees Paid 1845.762877
Max Drawdown [%] 5.532785
Max Drawdown Duration 58 days 00:00:00
Total Trades 24
Total Closed Trades 24
Total Open Trades 0
Open Trade PnL 0.0
Win Rate [%] 54.166667
Best Trade [%] 6.547092
Worst Trade [%] -4.102633
Avg Winning Trade [%] 3.123334
Avg Losing Trade [%] -1.189157
Avg Winning Trade Duration 7 days 01:50:46.153846153
Avg Losing Trade Duration 2 days 06:32:43.636363636
Profit Factor 2.304125
Expectancy 635.597746
Sharpe Ratio 1.954995
Calmar Ratio 4.315798
Omega Ratio 1.457148
Sortino Ratio 3.287022
dtype: object
总结
通过本章,你已经学习了以下内容:
-
数据加载与预处理
-
数据加载: 使用Parquet格式高效读取数据,支持列式存储优化。
-
特征工程: 集成TA-Lib技术指标(MACD、OBV、布林带等),并严格通过
shift(1)
避免未来信息泄露。例如:data["macd"] = data["macd"].shift(1) # 滞后一期
-
标签生成: 基于持有期收益的二分类标签,确保时间对齐:
buy_price = data["open"].shift(-1) # t+1日开盘价买入 sell_price = data["close"].shift(-HOLDING_PERIOD) # t+HOLDING_PERIOD收盘卖出
-
-
模型训练与优化
- 特征选择: 采用递归特征消除(RFE)筛选5个关键特征,降低过拟合风险。
- 超参数优化: 使用Optuna框架进行贝叶斯优化,结合时间序列交叉验证(TSCV)确保时序依赖性。
- 模型选择: LightGBM分类器,兼顾效率与精度,启用
deterministic=True
保证可复现性。
-
信号生成与风控
- 信号逻辑: 基于预测概率动态生成买卖信号,并严格限制持仓周期。
- 仓位管理: 波动率驱动的动态仓位调整(50%-80%),结合固定止损(5%)控制下行风险。
-
回测验证
- 实盘细节: 考虑交易成本(佣金0.05%+滑点0.15%),使用
vectorbt
进行事件驱动回测。
- 实盘细节: 考虑交易成本(佣金0.05%+滑点0.15%),使用
风险提示与免责声明
本文内容基于公开信息研究整理,不构成任何形式的投资建议。历史表现不应作为未来收益保证,市场存在不可预见的波动风险。投资者需结合自身财务状况及风险承受能力独立决策,并自行承担交易结果。作者及发布方不对任何依据本文操作导致的损失承担法律责任。市场有风险,投资须谨慎。
相关文章:
VectorBT量化入门系列:第六章 VectorBT实战案例:机器学习预测策略
VectorBT量化入门系列:第六章 VectorBT实战案例:机器学习预测策略 本教程专为中高级开发者设计,系统讲解VectorBT技术在量化交易中的应用。通过结合Tushare数据源和TA-Lib技术指标,深度探索策略开发、回测优化与风险评估的核心方法…...
5G网络下客户端数据业务掉线频繁
MCPTT(Mission Critical Push-to-Talk)客户端的日志,和界面在待机状态下(即没有做通话等业务操作),会频繁提示“离线”。 主要先看有没有丢网,UL BLER有没有问题。确认没有问题。看到业务信道释…...
CPU(中央处理器)
一、CPU的定义与核心作用 CPU 是计算机的核心部件,负责 解释并执行指令、协调各硬件资源 以及 完成数据处理,其性能直接影响计算机的整体效率。 核心功能: 从内存中读取指令并译码。执行算术逻辑运算。控制数据在寄存器、内存和I/O设备间的…...
Java从入门到“放弃”(精通)之旅——程序逻辑控制④
Java从入门到“放弃”(精通)之旅🚀:程序逻辑的完美理解 一、开篇:程序员的"人生选择" 曾经的我,生活就像一段顺序执行的代码: System.out.println("早上8:00起床"); Syste…...
[Dify] 基于明道云实现金融业务中的Confirmation生成功能
在金融业务的日常流程中,交易记录的处理不仅涉及数据录入、流程审批,更重要的是其最终输出形式——交易确认函(Confirmation)。本文将介绍如何通过明道云的打印模板功能,快速、准确地生成符合业务需求的交易Confirmation,提升工作效率与合规性。 为什么需要Confirmation?…...
Qt安卓设备上怎么安装两个不同的Qt应用?
在安卓设备上安装两个不同的Qt应用时,需要确保这两个应用在安卓系统中被视为独立的应用程序。以下是详细的步骤和注意事项,帮助你实现这一目标: 一、修改应用的包名 安卓系统通过应用的包名(package属性)来区分不同的…...
Prompt工程提示词(1-6章)
White graces:个人主页 🐹今日诗词:怅望千秋一洒泪,萧条异代不同时🐹 ⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏 ⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏 目录 🚀 第…...
0基础 | 硬件滤波 C、RC、LC、π型
一、滤波概念 (一)滤波定义 滤波是将信号中特定波段频率滤除的操作,是抑制和防止干扰的重要措施。通过滤波器实现对特定频率成分的筛选,确保目标信号的纯净度,提升系统稳定性。 (二)滤波器分…...
C++ 编程指南34 - C++ 中 ABI 不兼容的典型情形
一:概述 ABI(Application Binary Interface)是二进制层面的接口规范。如果一个库的 ABI 发生了变化,那么基于旧 ABI 编译的代码可能在运行时与新库不兼容(即使接口名字都一样也不行)。那么在C++中编程中,哪些情形会导致ABI不兼容呢?下面逐一列举一下。 二:C++ 中 ABI…...
【动态规划】深入动态规划:背包问题
文章目录 前言01背包例题一、01背包二、分割等和子集三、目标和四、最后一块石头的重量|| 完全背包例题一、完全背包二、 零钱兑换三、零钱兑换||四、完全平方数 前言 什么是背包问题,怎么解决算法中的背包问题呢? 背包问题 (Knapsack problem) 是⼀种组…...
NVIDIA AI Aerial
NVIDIA AI Aerial 适用于无线研发的 NVIDIA AI Aerial 基础模组Aerial CUDA 加速 RANAerial Omniverse 数字孪生Aerial AI 无线电框架 用例构建商业 5G 网络加速 5G生成式 AI 和 5G 数据中心 加速 6G 研究基于云的工具 优势100% 软件定义通过部署在数字孪生中进行测试6G 标准化…...
计算机视觉6——相机基础
一、数字相机基本工作原理 (一)像素概念 数字相机生成二维图像,图像最小单元是像素。 每个像素对应三维世界中某个特定方向,像素值衡量某一时刻来自该方向的光照强度/颜色 ,即相机度量每个像素的光照情况并保存到对…...
入门到精通,C语言十大经典程序
以下是十个经典的C语言程序示例,这些程序涵盖了从基础到稍复杂的应用场景,适合初学者和有一定基础的开发者学习和参考。 1. Hello, World! 这是每个初学者学习编程时的第一个程序,用于验证开发环境是否正确配置。 #include <stdio.h>…...
【毕设】Python构建基于TMDB电影推荐系统
个性化电影推荐系统 这是一个基于FastAPI开发的现代化电影推荐系统,结合了协同过滤和深度学习技术,为用户提供个性化的电影推荐服务。 主要功能 🎯 个性化电影推荐🔍 电影搜索与浏览⭐ 电影评分系统💝 收藏夹功能&a…...
嵌入式常见概念的介绍
目录 一、MCU、MPU、ARM (一)MCU(微控制器) (二)MPU(微处理器) (三)ARM(架构) 二、DSP (一)数字信号处理…...
富兴号:拨云见日,打造普洱品质典范
在高端普洱茶市场的混沌格局中,价格与品质的天平严重失衡,消费者往往深陷 “高价却难觅好茶” 的困境。而新兴品牌富兴号强势崛起,奋力冲破这一迷局,致力于重塑 “号级茶” 的卓越品质,为茶叶赋予珍贵的品鉴与收藏价值…...
【WORD】批量将doc转为docx
具体步骤进行: 打开Word文档,按下AltF11快捷键,打开VBA编辑器。在VBA编辑器中,左侧的“项目资源管理器”窗口会显示当前打开的Word文档相关项目。找到您要添加代码的文档项目(通常以文档名称命名)…...
Linux内存管理架构(1)
0.内存空间架构 1.用户空间 在 Linux 系统中,应用程序通过 malloc() 申请内存,并通过 free() 释放内存时,底层的内存管理是由 glibc(GNU C Library)中的内存分配器实现的。glibc 的内存分配器负责与操作系统的内核交互…...
Ubuntu 各个常见长期支持历史版本与代号
文章目录 1. Ubuntu 历史版本与代号2. 查看当前系统版本 在 Ubuntu 操作系统里,每个版本都有一个别具特色的名字。该名字由一个形容词与一个动物名称构成,且形容词和动物名称的首字母是一样的。例如 “Warty Warthog(长疣的疣猪)”…...
信息安全管理与评估2021年国赛正式卷答案截图以及十套国赛卷
2021年全国职业院校技能大赛高职组 “信息安全管理与评估”赛项 任务书1 赛项时间 共计X小时。 赛项信息 赛项内容 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一阶段 平台搭建与安全设备配置防护 任务1 网络平台搭建 任务2 网络安全设备配置与防护 第二…...
在线上定位1G日志文件中的异常信息时,我这样做合适吗
1G级线上日志文件 的异常定位系统性方案 一、快速定位流程 import datetime import randomdef generate_springboot_log(file_name, file_size_gb):# 模拟Spring Boot日志内容log_levels ["INFO", "DEBUG", "WARNING", "ERROR"]cla…...
Transformer模型中的两种掩码
模型训练通常使用 批处理batch来提升训练效率。而实际中Transformer的输入序列(如句子、文本片段)长度往往不一致。为了让这些样本可以组成一个统一的形状 [B, T] 的张量给GPU进行并行计算提高效率,需要将较短的序列填充(pad&…...
FastAPI-MPC正式发布,新的AI敏捷开发利器
FastAPI-MCP发布:零配置构建微服务控制平台的革命性实践 引言 在微服务架构日益复杂的今天,如何快速实现API接口的标准化管理与可视化控制成为开发者面临的核心挑战。近日,FastAPI-MCP工具的发布引发了技术社区广泛关注,其宣称能…...
Spring Boot 项目基于责任链模式实现复杂接口的解耦和动态编排!
全文目录: 开篇语前言一、责任链模式概述责任链模式的组成部分: 二、责任链模式的核心优势三、使用责任链模式解耦复杂接口1. 定义 Handler 接口2. 实现具体的 Handler3. 创建订单对象4. 在 Spring Boot 中使用责任链模式5. 配置责任链6. 客户端调用 四、…...
学习笔记八——内存管理相关
📘 目录 内存结构基础:栈、堆、数据段Rust 的内存管理机制(对比 C/C、Java)Drop:Rust 的自动清理机制Deref:为什么 *x 能访问结构体内部值Rc:多个变量“共享一个资源”怎么办?Weak&…...
Deepseek Bart模型相比Bert的优势
BART(Bidirectional and Auto-Regressive Transformers)与BERT(Bidirectional Encoder Representations from Transformers)虽然均基于Transformer架构,但在模型设计、任务适配性和应用场景上存在显著差异。以下是BART…...
Python在糖尿病分类问题上寻找具有最佳 ROC AUC 分数和 PR AUC 分数(决策树、逻辑回归、KNN、SVM)
Python在糖尿病分类问题上寻找具有最佳 ROC AUC 分数和 PR AUC 分数(决策树、逻辑回归、KNN、SVM) 问题模板解题思路1. 导入必要的库2. 加载数据3. 划分训练集和测试集4. 数据预处理5. 定义算法及其参数6. 存储算法和对应指标7. 训练模型并计算指标8. 找…...
达梦数据库-学习-20-慢SQL优化之CTE等价改写
目录 一、环境信息 二、介绍 三、优化过程 1、原始SQL 2、源SQL执行时间 3、原始SQL执行计划 4、拆分问题 5、过滤性 6、统计信息收集 7、改写思路一 8、改写SQL一 9、改写SQL一的执行计划 10、改写思路二 11、改写SQL二 12、改写SQL二的执行计划 一、环境信息…...
软件生命周期模型:瀑布模型、螺旋模型、迭代模型、敏捷开发、增量模型、快速原型模型
目录 1.软件生命周期 2.软件生命周期模型 2.1瀑布模型 缺点【存在的问题】: 优点: 2.2 螺旋模型 特点: 2.3 迭代模型 优点: 2.4 敏捷开发 2.5 增量模型 增量模型一般和迭代模型一起使用: 2.6 快速原型模型…...
AI agents系列之全面介绍
随着大型语言模型(LLMs)的出现,人工智能(AI)取得了巨大的飞跃。这些强大的系统彻底改变了自然语言处理,但当它们与代理能力结合时,才真正释放出潜力——能够自主地推理、规划和行动。这就是LLM代理大显身手的地方,它们代表了我们与AI交互以及利用AI的方式的范式转变。 …...
Ubuntu 下通过 Docker 部署 WordPress 服务器
最近想恢复写私人博客的习惯,准备搭建一个wordpress。 在这篇博客中,我将记录如何在 Ubuntu 环境下通过 Docker 部署一个 WordPress 服务器。WordPress 是一个流行的内容管理系统(CMS),它让用户能够轻松地创建和管理网…...
Elasticsearch生态
目录 Elasticsearch核心概念 Elasticsearch实现全文检索的原理 Elasticsearch打分规则 常用的查询语法 ES数据同步方案 Elasticsearch生态非常丰富,包含了一系列工具和功能,帮助用户处理、分析和可视化数据,Elastic Stack是其核心部分&a…...
idea配置spring MVC项目启动(maven配置完后)
springmvc项目在idea中配置启动总结,下面的内容是在maven配置好后进行的。 配置 Tomcat 服务器 添加 Tomcat 到 IDEA: File → Settings → Build, Execution, Deployment → Application Servers → 点击 → 选择 Tomcat Server。 指定 Tomcat 安装目…...
大模型微调数据集怎么搞?基于easydataset实现文档转换问答对json数据集!
微调的难点之一在与数据集。本文介绍一种将文档转换为问答数据集的方法,超级快! 上图左侧是我的原文档,右侧是我基于文档生成的数据集。 原理是通过将文档片段发送给ollama本地模型,然后本地模型生成有关问题,并基于文…...
【排序算法】快速排序
目录 一、递归版本 1.1 hoare版本 问题1:为什么left 和 right指定的数据和key值相等时不能交换? 问题2:为什么跳出循环后right位置的值⼀定不⼤于key? 1.2 挖坑法 1.3 lomuto前后指针版本 二、快排优化 2.1 时间复杂度的计算 2.1.…...
爬虫:IP代理
什么是代理 代理服务器 代理服务器的作用 就是用来转发请求和响应 在爬虫中为何需要使用代理? 有些时候,需要对网站服务器发起高频的请求,网站的服务器会检测到这样的异常现象,则会讲请求对应机器的ip地址加入黑名单ÿ…...
JUC.atomic原子操作类原理分析
摘要 多线程场景下共享变量原子性操作除了可以使用Java自带的synchronized关键字以及AQS锁实现线程同步外,java.util.concurrent.atomic 包下提供了对基本类型封装类(AtomicBoolean|AtomicLong|AtomicReference|AtomicBoolean) 以及对应的数组封装。对于已有的包含…...
【XCP实战】AUTOSAR架构下XCP从0到1开发配置实践
目录 前言 正文 1.CAN功能开发 1.1 DBC的制作及导入 1.2 CanTrcv模块配置 1.3 Can Controller模块配置 1.4 CanIf模块配置 2.XCP模块集成配置配置 2.1.XCP模块配置 2.2.XCP模块的Task Mapping 2.3.XCP模块的初始化 3.在链接文件中定义标定段 4.编写标定相关的测试…...
【STM32】STemWin库,使用template API
目录 CubeMX配置 工程文件配置 Keil配置 STemwin配置 GUIConf.c LCDConf.c 打点函数 修改屏幕分辨率 GUI_X.c 主函数 添加区域填充函数 移植过程中需要一些参考手册,如下 STemwin使用指南 emWin User Guide & Reference Manual CubeMX配置 参考驱…...
Web开发-JavaEE应用动态接口代理原生反序列化危险Invoke重写方法利用链
知识点: 1、安全开发-JavaEE-动态代理&序列化&反序列化 2、安全开发-JavaEE-readObject&toString方法 一、演示案例-WEB开发-JavaEE-动态代理 动态代理 代理模式Java当中最常用的设计模式之一。其特征是代理类与委托类有同样的接口,代理类…...
C语言中while的相关题目
一、题目引入 以下程序中,while循环的循环次数是多少次? 二、代码分析 首先要明确的一点 while循环是当循环条件为真 就会一直循环 不会停止 while中i是小于10的 说明i可以取到0 1 2 3 4 5 6 7 8 9 进入第一个if判断i小于1为真时执行continue i0是为真的 执行continue 后…...
在Ubuntu下交叉编译 Qt 应用程序(完整步骤)
1、下载交叉编译器下: st-example-image-qt wayland-openstlinux-weston-stm32mp1-x86_64-toolchain-3.1-snapshot.sh 通过网盘分享的文件:STM32项目 链接: https://pan.baidu.com/s/1hTvJT2r6czWCrKSuNEZCuw?pwdth7t 提取码: th7t --来自百度网盘超级…...
深入剖析 Axios 的 POST 请求:何时使用 qs 处理数据
在前端开发中,Axios 是一个广泛使用的用于发送 HTTP 请求的库,特别是在处理 POST 请求时,数据的处理方式会直接影响到请求能否正确被后端接收和处理。其中,使用 qs 库对数据进行处理是一个常见的操作点,本文将深入探讨…...
Python中NumPy的随机操作
在数据科学、机器学习和科学计算中,随机数的生成和操作是不可或缺的一部分。NumPy作为Python中强大的数值计算库,提供了丰富的随机数生成工具,能够满足从简单随机数生成到复杂概率分布模拟的多种需求。本文将深入探讨NumPy的随机操作功能&…...
从代码学习深度学习 - 多头注意力 PyTorch 版
文章目录 前言一、多头注意力机制介绍1.1 工作原理1.2 优势1.3 代码实现概述二、代码解析2.1 导入依赖序列掩码函数2.2 掩码 Softmax 函数2.3 缩放点积注意力2.4 张量转换函数2.5 多头注意力模块2.6 测试代码总结前言 在深度学习领域,注意力机制(Attention Mechanism)是自然…...
通过扣子平台工作流将数据写入飞书多维表格
1. 进入扣子平台,并创建工作流扣子 扣子是新一代 AI 大模型智能体开发平台。整合了插件、长短期记忆、工作流、卡片等丰富能力,扣子能帮你低门槛、快速搭建个性化或具备商业价值的智能体,并发布到豆包、飞书等各个平台。https://www.coze.cn…...
python专题2-----用python生成多位,值均是数字的随机数
有很多方法可以用 Python 生成 多位随机数。我将向您介绍几个常用的方法,并解释它们的优缺点(此处以4位随机数为例): 1. 使用 random.randint() 这是最简单直接的方法: import randomrandom_number random.randint…...
Mybatis的简单介绍
文章目录 MyBatis 简介 1. MyBatis 核心特点2. MyBatis 核心组件3. MyBatis 基本使用示例(1) 依赖引入(Maven)(2) 定义 Mapper 接口(3) 定义实体类(4) 在 Service 层调用 4. MyBatis 与 JPA/Hibernate 对比 MyBatis 简介 MyBatis 是一款优秀的 持久层框…...
山东大学软件学院创新项目实训(11)之springboot+vue项目接入deepseekAPI
因为该阶段是前后端搭建阶段,所以没有进大模型的专项训练,所以先用老师给的deepseek接口进行代替 且因为前端设计部分非本人负责且还没有提交到github上,所以目前只能先编写一个简易的界面进行功能的测试 首先进行创建model类 然后创建Cha…...
Qt绘图事件
目录 1.绘图事件 1.1绘图事件 1.2声明一个画家对象 2.画线、画圆、画矩形、画文字 2.1画线 编辑 2.2画圆 2.3画矩形 2.4画文字 3.设置画笔 3.1设置画笔颜色 3.2设置画笔宽度 3.3设置画笔风格 4.设置画刷 4.1填色 4.2设置画刷风格 5.绘图高级设置 5.1设置抗锯…...