Python案例如何结合伤停信息调仓?

wen python案例 4

本文目录导读:

Python案例如何结合伤停信息调仓?

  1. 案例设计思路
  2. 完整代码实现
  3. 关键逻辑解释
  4. 进阶优化方向(可根据实际需求修改)
  5. 实际应用提醒

这是一个非常专业且实际的量化投资问题,在Python中结合伤停信息(伤病与停赛)进行调仓,主要应用于体育竞技类基金(如足球、篮球俱乐部股票)或体育博彩/预测模型中,但在传统股票市场中,伤停信息通常指上市公司高管或核心技术人员的突发状况(如生病、意外)。

最常见的场景是体育博彩赔率预测基于球队表现的量化投资策略(例如投资表现强劲的球队母公司股票,并在核心球员伤停时卖出)。

下面我提供一个完整的、可运行的Python案例,模拟一个基于足球俱乐部核心球员伤停信息的调仓策略,核心逻辑是:当核心球员伤停时,降低该俱乐部股票仓位;当伤愈复出时,增加仓位。


案例设计思路

  1. 数据来源:模拟足球俱乐部的股价数据以及核心球员的伤停状态(0=健康/可上场, 1=受伤/停赛)。
  2. 策略规则
    • 如果核心球员持续伤停(状态为1),则仓位减半或清仓。
    • 如果核心球员复出(状态从1变成0),则补回仓位。
    • 持仓调整时考虑信号强度(例如连续伤停天数 > 阈值)。
  3. 目标:生成一个动态调仓信号,并进行回测。

完整代码实现

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# ---------------------------- 1. 构建模拟数据 ----------------------------
np.random.seed(42)
dates = pd.date_range(start='2024-01-01', end='2024-06-01', freq='D')
n = len(dates)
# 模拟两只球队(股票)的价格
price_a = 100 + np.cumsum(np.random.randn(n) * 0.5)
price_b = 100 + np.cumsum(np.random.randn(n) * 0.6)
# 模拟核心球员伤停状态(0=健康, 1=受伤)
# 假设球员A在某个时间段受伤
injury_status_a = np.zeros(n)
injury_period = (dates >= '2024-02-01') & (dates <= '2024-02-20')
injury_status_a[injury_period] = 1
# 假设球员B从未受伤
injury_status_b = np.zeros(n)
# 创建DataFrame
df = pd.DataFrame({
    'date': dates,
    'price_A': price_a,
    'price_B': price_b,
    'injury_A': injury_status_a,
    'injury_B': injury_status_b
})
df.set_index('date', inplace=True)
# ---------------------------- 2. 定义调仓策略 ----------------------------
def generate_signal(injury_series, lookback=3):
    """
    根据伤停状态生成调仓信号
    - 信号 = -1: 清仓(连续伤停超过lookback天)
    - 信号 = 0: 维持仓位
    - 信号 = 1: 加仓(复出后第一天)
    """
    # 滚动窗口:最近lookback天伤停天数
    injury_sum = injury_series.rolling(window=lookback, min_periods=1).sum()
    # 信号生成
    signal = pd.Series(0, index=injury_series.index)
    # 条件1:最近lookback天全部受伤 → 清仓
    signal.loc[injury_sum >= lookback] = -1
    # 条件2:昨天受伤,今天不受伤 → 复出加仓
    yesterday_injured = injury_series.shift(1) == 1
    today_healthy = injury_series == 0
    signal.loc[yesterday_injured & today_healthy] = 1
    return signal
# 生成每只球队的调仓信号
df['signal_A'] = generate_signal(df['injury_A'])
df['signal_B'] = generate_signal(df['injury_B'])
# ---------------------------- 3. 模拟调仓与仓位变化 ----------------------------
# 初始仓位:各50%
position_a = 0.5
position_b = 0.5
position_history = []
for idx, row in df.iterrows():
    # 根据信号调整仓位
    if row['signal_A'] == -1:
        # 清仓股票A,资金全部分配给B
        position_a = 0.0
        position_b = 1.0
    elif row['signal_A'] == 1:
        # 复出,恢复初始仓位
        position_a = 0.5
        position_b = 0.5
    # 如果B也受伤(本案例中B一直健康,但保留逻辑)
    if row['signal_B'] == -1:
        position_a = 1.0
        position_b = 0.0
    elif row['signal_B'] == 1:
        position_a = 0.5
        position_b = 0.5
    position_history.append([position_a, position_b])
# 添加到DataFrame
df[['pos_A', 'pos_B']] = position_history
# ---------------------------- 4. 计算策略收益 vs 基准 ----------------------------
# 基准:静态50/50持仓
df['return_A'] = df['price_A'].pct_change()
df['return_B'] = df['price_B'].pct_change()
df['基准收益'] = 0.5 * df['return_A'] + 0.5 * df['return_B']
# 策略收益:根据每日仓位计算
df['策略收益'] = (df['pos_A'].shift(1) * df['return_A'] + 
                    df['pos_B'].shift(1) * df['return_B'])
# 累计收益
df['基准累计'] = (1 + df['基准收益']).cumprod()
df['策略累计'] = (1 + df['策略收益']).cumprod()
# 去除第一行NaN
df_clean = df.dropna()
# ---------------------------- 5. 可视化结果 ----------------------------
fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)
# 子图1:股价与伤停标记
axes[0].plot(df_clean.index, df_clean['price_A'], label='股票A(核心球员)', color='blue')
axes[0].plot(df_clean.index, df_clean['price_B'], label='股票B(参考标的)', color='orange')
# 标记伤停区间
injury_dates = df_clean[df_clean['injury_A'] == 1].index
for d in injury_dates:
    axes[0].axvspan(d, d + timedelta(days=1), alpha=0.2, color='red')
axes[0].set_ylabel('价格')
axes[0].legend()
axes[0].set_title('股价走势与核心球员伤停区间(红色阴影)')
# 子图2:仓位变化
axes[1].plot(df_clean.index, df_clean['pos_A'], label='股票A仓位', color='blue')
axes[1].plot(df_clean.index, df_clean['pos_B'], label='股票B仓位', color='orange')
axes[1].axhline(0.5, color='gray', linestyle='--', alpha=0.5)
axes[1].set_ylabel('仓位比例')
axes[1].set_ylim(-0.1, 1.1)
axes[1].legend()
axes[1].set_title('动态调仓过程')
# 子图3:收益对比
axes[2].plot(df_clean.index, df_clean['基准累计'], label='静态50/50基准', color='gray')
axes[2].plot(df_clean.index, df_clean['策略累计'], label='基于伤停调仓策略', color='green')
axes[2].set_ylabel('累计收益')
axes[2].legend()
axes[2].set_title('策略表现对比')
plt.tight_layout()
plt.show()
# ---------------------------- 6. 输出统计指标 ----------------------------
strategy_return = df_clean['策略累计'].iloc[-1] - 1
benchmark_return = df_clean['基准累计'].iloc[-1] - 1
print(f"策略累计收益: {strategy_return:.2%}")
print(f"基准累计收益: {benchmark_return:.2%}")
print(f"超额收益: {strategy_return - benchmark_return:.2%}")
print(f"\n最大仓位调整次数: {len(df_clean[df_clean['signal_A'].abs() > 0])} 次")
print(f"伤停总天数: {df_clean['injury_A'].sum()} 天")

关键逻辑解释

  1. 伤停信号生成 (generate_signal函数)

    • 使用滚动窗口rolling)检测最近几天的伤停状态。
    • 清仓信号 (-1):当球员连续伤停达到阈值(lookback=3天),立即清仓该股票。
    • 加仓信号 (+1):当球员昨天受伤、今天恢复,即触发买入。
    • 维持信号 (0):其他情况。
  2. 调仓执行逻辑

    • 在每日循环中,根据伤停信号调整两只股票的仓位比例。
    • 实际应用中,可改成“减半仓位”而非完全清仓,或加入更复杂的风险管理。
  3. 收益计算

    • 使用 shift(1) 确保用前一天的仓位来计算当天的收益(避免未来函数)。
    • 比较了“动态伤停策略”和“传统静态50/50”的表现。

进阶优化方向(可根据实际需求修改)

  1. 多球员多标的:将单只股票扩展为多个球队/资产,用矩阵操作代替循环。

  2. 信号加权:根据球员重要性(如进球数、身价)给伤停信号加权。

  3. 阈值优化lookback 参数可通过网格搜索确定最优值。

  4. 真实数据接入

    • 伤停数据:从体育新闻API(如Sportradar, ESPN API)获取。
    • 股价数据:从yfinance获取对应俱乐部股票(曼联MANU、多特蒙德BVB等)。
  5. 改进调仓方式

    # 更精细的仓位调整(例如按比例减少)
    def adjust_position(injury_series, base_weight=0.5, decay_factor=0.5):
        # 受伤天数越长,仓位越低
        injury_days = injury_series.rolling(window=5).sum()
        weight = base_weight * (decay_factor ** injury_days)
        return np.clip(weight, 0, base_weight)

实际应用提醒

  • 流动性风险:基于新闻的事件驱动策略,需要确保能在事件发生时及时交易。
  • 信息时效性:伤停信息往往是新闻发布后才能获取,回测时要考虑滞后。
  • 过拟合:不要过度优化参数,建议进行样本外测试。

这个框架可以轻松扩展到美股体育板块(如耐克、Under Armour因代言球星受伤调仓)或A股(核心技术人员离职/被调查等),如果你有具体的股票或行业需求,我可以帮你定制修改。

抱歉,评论功能暂时关闭!