本文目录导读:

这是一个非常专业且实际的量化投资问题,在Python中结合伤停信息(伤病与停赛)进行调仓,主要应用于体育竞技类基金(如足球、篮球俱乐部股票)或体育博彩/预测模型中,但在传统股票市场中,伤停信息通常指上市公司高管或核心技术人员的突发状况(如生病、意外)。
最常见的场景是体育博彩赔率预测或基于球队表现的量化投资策略(例如投资表现强劲的球队母公司股票,并在核心球员伤停时卖出)。
下面我提供一个完整的、可运行的Python案例,模拟一个基于足球俱乐部核心球员伤停信息的调仓策略,核心逻辑是:当核心球员伤停时,降低该俱乐部股票仓位;当伤愈复出时,增加仓位。
案例设计思路
- 数据来源:模拟足球俱乐部的股价数据以及核心球员的伤停状态(0=健康/可上场, 1=受伤/停赛)。
- 策略规则:
- 如果核心球员持续伤停(状态为1),则仓位减半或清仓。
- 如果核心球员复出(状态从1变成0),则补回仓位。
- 持仓调整时考虑信号强度(例如连续伤停天数 > 阈值)。
- 目标:生成一个动态调仓信号,并进行回测。
完整代码实现
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()} 天")
关键逻辑解释
-
伤停信号生成 (
generate_signal函数):- 使用滚动窗口(
rolling)检测最近几天的伤停状态。 - 清仓信号 (-1):当球员连续伤停达到阈值(
lookback=3天),立即清仓该股票。 - 加仓信号 (+1):当球员昨天受伤、今天恢复,即触发买入。
- 维持信号 (0):其他情况。
- 使用滚动窗口(
-
调仓执行逻辑:
- 在每日循环中,根据伤停信号调整两只股票的仓位比例。
- 实际应用中,可改成“减半仓位”而非完全清仓,或加入更复杂的风险管理。
-
收益计算:
- 使用
shift(1)确保用前一天的仓位来计算当天的收益(避免未来函数)。 - 比较了“动态伤停策略”和“传统静态50/50”的表现。
- 使用
进阶优化方向(可根据实际需求修改)
-
多球员多标的:将单只股票扩展为多个球队/资产,用矩阵操作代替循环。
-
信号加权:根据球员重要性(如进球数、身价)给伤停信号加权。
-
阈值优化:
lookback参数可通过网格搜索确定最优值。 -
真实数据接入:
- 伤停数据:从体育新闻API(如Sportradar, ESPN API)获取。
- 股价数据:从
yfinance获取对应俱乐部股票(曼联MANU、多特蒙德BVB等)。
-
改进调仓方式:
# 更精细的仓位调整(例如按比例减少) 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股(核心技术人员离职/被调查等),如果你有具体的股票或行业需求,我可以帮你定制修改。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播