股票配资专业平台|北京股票配资网|杠杆公司|配资平台app

Python量化交易:多股票组合策略搭建与风险控制实战指南(pdf)

股票交易技术+实战快速+pdf_Python量化交易多股票组合回测_分散投资风险控制策略

大家好,我是量化老王。作为《量化交易开发100篇》的第8篇,咱们在上一篇已经完成了“全行情适配+风险控制”的单股票组合策略搭建,解决了“单一策略风险高”“止损止盈不精细”的问题。但量化交易的另一大核心原则是“不要把鸡蛋放在一个篮子里”——单只股票哪怕策略再好,也可能因个股黑天鹅(如业绩暴雷、突发利空)导致大幅亏损。

今天第8篇,咱们就实现“多股票组合回测”:构建一个包含5-10只股票的量化组合,通过“分散投资+合理权重分配”Python量化交易:多股票组合策略搭建与风险控制实战指南(pdf),进一步降低单一股票的非系统性风险,同时保留策略的盈利能力。全程代码落地,整合前文的趋势/震荡识别、动态仓位、精细化止损止盈,形成完整的多股票量化交易体系。

一、核心逻辑:多股票组合的优势与搭建原则1. 为什么要做多股票组合?

单股票交易的风险主要来自两方面:一是系统性风险(大盘下跌带动个股下跌,所有策略都难规避);二是非系统性风险(个股自身问题,如财务造假、行业政策利空)。多股票组合的核心作用是“分散非系统性风险”——不同行业、不同风格的股票走势不完全同步,一只股票亏损,可能被其他股票的盈利抵消,让组合收益更平稳。

2. 多股票组合的搭建原则(新手直接套用)

为了让组合既分散风险又有盈利能力,咱们制定3个可落地的原则:

二、第一步:股票筛选与数据获取(实现)

咱们筛选5只不同行业的龙头股(兼顾流动性和稳定性),具体标的及行业如下:

股票代码

股票名称

所属行业

筛选理由

.SH

贵州茅台

消费(白酒)

行业龙头,业绩稳定,抗跌性强

.SZ

比亚迪

新能源

赛道龙头,成长属性强,波动适中

.SZ

迈瑞医疗

医疗健康

医疗器械龙头,刚需属性,稳定性好

.SZ

五 粮 液

消费(白酒)

次高端龙头,与茅台形成消费双配

.SH

中国平安

金融(保险)

金融龙头,估值低,分红稳定

代码:批量获取多股票数据

# 导入必备库(复用前文基础库)
import tushare as ts
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')  # 忽略警告,让代码更整洁
# 解决中文乱码
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 基础配置:Tushare Token+股票列表
ts.set_token('你的Tushare Token')
pro = ts.pro_api()
# 筛选的5只股票(代码+名称,便于后续标注)
stock_list = [
    ('600519.SH', '贵州茅台'),
    ('002594.SZ', '比亚迪'),
    ('300760.SZ', '迈瑞医疗'),
    ('000858.SZ', '五粮液'),
    ('601318.SH', '中国平安')
]
start_date = '20230101'
end_date = '20240630'  # 回测周期与前文一致,便于对比
# 2. 批量获取多股票日线数据(封装成函数)
def get_multi_stock_data(stock_list, start_date, end_date):
    """
    批量获取多股票日线数据
    参数:
    - stock_list: 股票代码+名称列表
    - start_date/end_date: 起止日期
    返回:
    - stock_data_dict: 字典,key=股票名称,value=对应DataFrame
    """
    stock_data_dict = {}
    for ts_code, name in stock_list:
        # 获取单股票数据(复用前文的预处理逻辑)
        df = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date)
        # 数据预处理
        df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
        df = df.sort_values('trade_date').set_index('trade_date').dropna()
        # 保存到字典
        stock_data_dict[name] = df
        print(f"已获取{name}数据,形状:{df.shape}")
    return stock_data_dict
# 执行函数,获取数据
stock_data = get_multi_stock_data(stock_list, start_date, end_date)
# 查看数据是否获取成功(以贵州茅台为例)
print("\n贵州茅台数据前3行:")
print(stock_data['贵州茅台'][['open', 'high', 'low', 'close', 'vol']].head(3))

第二步:整合前文核心策略(行情识别+风险控制)

将第6篇的“行情识别”、第7篇的“动态仓位”“精细化止损止盈”封装成统一函数,用于后续批量处理每只股票。

# 3. 整合核心策略函数(复用前文逻辑,统一封装)
def apply_strategy_with_risk_control(df):
    """
    给单只股票的DataFrame应用完整策略(行情识别+动态仓位+精细化止损止盈)
    参数:df 单只股票的日线DataFrame
    返回:df 含策略信号、仓位、收益率的DataFrame
    """
    # 3.1 行情识别(ATR+标准差)
    def calculate_atr(data, period=14):
        data['tr1'] = data['high'] - data['low']
        data['tr2'] = abs(data['high'] - data['close'].shift(1))
        data['tr3'] = abs(data['low'] - data['close'].shift(1))
        data['tr'] = data[['tr1', 'tr2', 'tr3']].max(axis=1)
        return data['tr'].rolling(window=period).mean()
    
    df['ATR14'] = calculate_atr(df)
    df['ATR_std20'] = df['ATR14'].rolling(window=20).std()
    df['market_type'] = 'unknown'
    df.loc[df['ATR_std20'] < 0.5, 'market_type'] = 'shock'  # 震荡行情
    df.loc[df['ATR_std20'] >= 0.5, 'market_type'] = 'trend'  # 趋势行情
    
    # 3.2 多均线+RSI策略信号
    # 多均线策略(趋势用)
    df['MA_short'] = df['close'].rolling(window=5).mean()
    df['MA_mid'] = df['close'].rolling(window=10).mean()
    df['MA_long'] = df['close'].rolling(window=20).mean()
    df['ma_buy'] = ((df['MA_short'] > df['MA_mid']) & (df['MA_mid'] > df['MA_long'])) & (
        ~((df['MA_short'].shift(1) > df['MA_mid'].shift(1)) & (df['MA_mid'].shift(1) > df['MA_long'].shift(1))))
    df['ma_sell'] = ((df['MA_short'] < df['MA_mid']) & (df['MA_mid'] < df['MA_long'])) & (
        ~((df['MA_short'].shift(1) < df['MA_mid'].shift(1)) & (df['MA_mid'].shift(1) < df['MA_long'].shift(1))))
    
    # RSI策略(震荡用)
    delta = df['close'].diff()
    gain = delta.where(delta > 0, 0)
    loss = -delta.where(delta < 0, 0)
    avg_gain = gain.rolling(window=14).mean()
    avg_loss = loss.rolling(window=14).mean()
    rs = avg_gain / avg_loss
    df['RSI'] = 100 - (100 / (1 + rs)).fillna(100)
    df['rsi_buy'] = (df['RSI'] < 30) & (df['RSI'].shift(1) >= 30)
    df['rsi_sell'] = (df['RSI'] > 70) & (df['RSI'].shift(1) <= 70)
    
    # 3.3 动态仓位管理
    def calculate_position(data, risk_tolerance=0.02, max_loss_pct=0.05):
        data['base_position'] = 0.7  # 趋势基础仓位70%
        data.loc[data['market_type'] == 'shock', 'base_position'] = 0.3  # 震荡30%
        data['position_ratio'] = (risk_tolerance / max_loss_pct) * data['base_position']
        return data['position_ratio'].clip(lower=0.1, upper=1.0)  # 仓位限制
    
    df['position_ratio'] = calculate_position(df)
    
    # 3.4 精细化止损止盈
    df['stop_loss_price'] = 0.0
    df['take_profit_price'] = 0.0
    df['entry_price'] = 0.0
    df['final_buy'] = False
    df['final_sell'] = False
    
    entry_price = 0.0
    position = 0  # 0空仓,1持仓
    
    for date in df.index:

分散投资风险控制策略_Python量化交易多股票组合回测_股票交易技术+实战快速+pdf

market_type = df.loc[date, 'market_type'] close = df.loc[date, 'close'] atr = df.loc[date, 'ATR14'] # 买入信号(空仓时) if (df.loc[date, 'ma_buy'] and market_type == 'trend') or (df.loc[date, 'rsi_buy'] and market_type == 'shock'): if position == 0: entry_price = close df.loc[date, 'entry_price'] = entry_price df.loc[date, 'final_buy'] = True position = 1 # 持仓时计算止损止盈 if position == 1: profit_pct = (close - entry_price) / entry_price atr_multiplier = 2.0 if market_type == 'trend' else 1.5 stop_loss = close - atr_multiplier * atr df.loc[date, 'stop_loss_price'] = stop_loss # 移动止盈逻辑 if profit_pct < 0.1: take_profit = entry_price * 1.2 elif 0.1 <= profit_pct < 0.2: take_profit = close - 1.0 * atr else: take_profit = close - 0.5 * atr df.loc[date, 'take_profit_price'] = take_profit # 卖出信号 if close < stop_loss or close > take_profit: df.loc[date, 'final_sell'] = True position = 0 entry_price = 0.0 # 3.5 计算单只股票的策略收益率 df['daily_return'] = 0.0 position = 0 pos_ratio = 0.0 for date in df.index: if df.loc[date, 'final_buy']: position = 1 pos_ratio = df.loc[date, 'position_ratio'] elif df.loc[date, 'final_sell']: position = 0 pos_ratio = 0.0 if position == 1: df.loc[date, 'daily_return'] = pos_ratio * (df.loc[date, 'close'].shift(-1) / df.loc[date, 'close'] - 1) # 计算单只股票累计收益率 df['cum_return'] = (1 + df['daily_return']).cumprod() return df # 4. 批量给每只股票应用策略 stock_strategy_data = {} for name, df in stock_data.items(): stock_strategy_data[name] = apply_strategy_with_risk_control(df.copy()) print(f"\n{name}策略应用完成,最终累计收益率:{(stock_strategy_data[name]['cum_return'].iloc[-2]-1)*100:.2f}%")

第三步:多股票组合构建与回测(核心步骤)

核心是“等权分配权重”+“组合收益加权计算”,同时对比“单股票策略”和“组合策略”的风险收益,验证组合的优势。

# 5. 多股票组合回测(等权分配)
# 5.1 统一所有股票的索引(确保日期对齐)
all_dates = pd.date_range(start=start_date, end=end_date, freq='B')  # 交易日索引
weight = 1 / len(stock_list)  # 等权权重(5只股票,每只20%)
# 5.2 计算组合每日收益率和累计收益率
combo_daily_return = pd.Series(0.0, index=all_dates)  # 组合每日收益率
stock_cum_returns = {}  # 存储每只股票的累计收益率(用于后续对比)
for name, df in stock_strategy_data.items():
    # 对齐日期,缺失值填充为0
    daily_return_aligned = df['daily_return'].reindex(all_dates, fill_value=0.0)
    # 组合收益率 = 各股票收益率 × 权重 求和
    combo_daily_return += daily_return_aligned * weight
    # 保存单股票累计收益率(对齐日期)
    stock_cum_returns[name] = (1 + daily_return_aligned).cumprod()
# 计算组合累计收益率
combo_cum_return = (1 + combo_daily_return).cumprod()
# 5.3 计算组合的核心评估指标(对比单股票)
def calculate_metrics(cum_return, daily_return):
    """计算策略核心指标:累计收益率、最大回撤、夏普比率"""
    final_return = (cum_return.iloc[-1] - 1) * 100
    # 最大回撤
    cum_max = cum_return.cummax()
    drawdown = (cum_return - cum_max) / cum_max
    max_drawdown = drawdown.min() * 100
    # 夏普比率(年化,无风险利率3%)
    annual_return = final_return / 1.5  # 回测1.5年
    daily_vol = daily_return.std() * (240 ** 0.5)  # 年化波动率
    sharpe = (annual_return - 3) / daily_vol if daily_vol != 0 else 0
    return {
        '累计收益率(%)': round(final_return, 2),
        '最大回撤(%)': round(max_drawdown, 2),
        '夏普比率': round(sharpe, 2)
    }
# 计算组合指标
combo_metrics = calculate_metrics(combo_cum_return, combo_daily_return)
# 计算每只股票的指标
stock_metrics = {}
for name, cum_ret in stock_cum_returns.items():
    daily_ret = stock_strategy_data[name]['daily_return'].reindex(all_dates, fill_value=0.0)
    stock_metrics[name] = calculate_metrics(cum_ret, daily_ret)
# 5.4 输出对比结果
print("\n=== 多股票组合 vs 单股票策略核心指标对比 ===")
print(f"组合策略(等权5只股票):{combo_metrics}")
for name, metrics in stock_metrics.items():
    print(f"{name}:{metrics}")

第四步:专业可视化(组合 vs 单股票效果对比)

用2张子图直观展示组合优势:① 组合累计收益率 vs 各单股票;② 组合最大回撤 vs 各单股票。

# 6. 可视化组合效果
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), gridspec_kw={'height_ratios': [3, 2]})
# 子图1:组合 vs 单股票累计收益率
ax1.plot(combo_cum_return.index, combo_cum_return, label='等权组合策略', color='red', linewidth=2.0)
for name, cum_ret in stock_cum_returns.items():
    ax1.plot(cum_ret.index, cum_ret, label=name, linewidth=1.2, alpha=0.7)
ax1.set_title('多股票组合 vs 单股票累计收益率对比(2023-2024)', fontsize=14, pad=15)
ax1.set_ylabel('累计收益(单位:1)', fontsize=12)
ax1.legend(fontsize=10)
ax1.grid(True, alpha=0.3)
# 子图2:组合 vs 单股票最大回撤对比(柱状图)
names = list(stock_metrics.keys()) + ['组合策略']
max_drawdowns = [stock_metrics[name]['最大回撤(%)'] for name in names[:-1]] + [combo_metrics['最大回撤(%)']]
# 柱状图颜色(组合用红色突出)
colors = ['lightblue'] * len(names[:-1]) + ['red']
ax2.bar(names, max_drawdowns, color=colors, alpha=0.7)
ax2.set_title('多股票组合 vs 单股票最大回撤对比', fontsize=14, pad=15)
ax2.set_ylabel('最大回撤(%)', fontsize=12)
ax2.set_xlabel('策略/股票', fontsize=12)
ax2.tick_params(axis='x', rotation=45)  # 旋转x轴标签,避免重叠
# 在柱状图上标注数值
for i, v in enumerate(max_drawdowns):
    ax2.text(i, v + 0.2, f'{v:.2f}%', ha='center', fontsize=10)
plt.tight_layout()
plt.show()

可视化结果解读

1. 子图1中,组合策略的累计收益率可能不是最高的股票交易技术+实战快速+pdf,但走势最平稳——避免了单股票(如比亚迪)的大幅波动,哪怕某只股票短期亏损,其他股票的盈利能对冲风险;

2. 子图2中,组合策略的最大回撤明显低于大多数单股票——这是组合分散非系统性风险的核心体现,比如贵州茅台最大回撤可能15%Python量化交易:多股票组合策略搭建与风险控制实战指南(pdf),组合回撤可能只有8%,大幅提升了策略的抗风险能力。

三、新手常见问题解答

1. 为什么选5只股票?选越多越好吗?—— 5-10只是新手最优选择:太少达不到分散效果,太多会增加交易成本和管理难度,且超过10只后,分散风险的边际效益会递减;

2. 等权分配之外,还有其他权重方式吗?—— 有,比如“市值加权”(按股票市值分配权重)、“波动率加权”(波动小的股票权重高),后续会专门讲解动态权重策略;

3. 组合策略的收益率变低了,正常吗?—— 正常!组合的核心目标是“稳定盈利”,而非“追求极致收益”。用部分收益率换取更低的回撤股票交易技术+实战快速+pdf,是长期量化交易的理性选择。