用Pandas shift()搞定股票数据分析:从日涨跌幅到移动平均线
·
用Pandas shift()搞定股票数据分析:从日涨跌幅到移动平均线
金融数据分析的核心在于从时间序列中提取有价值的信息。Pandas作为Python生态中最强大的数据分析工具,其shift()函数配合diff()、rolling()等方法,能够高效完成从基础涨跌幅计算到复杂技术指标生成的全套流程。本文将手把手带您实现股票数据分析的完整特征工程路径。
1. 构建虚拟股票数据集
任何分析都需要从数据开始。我们先模拟一个包含日期和收盘价的股票数据集:
import pandas as pd
import numpy as np
# 设置随机种子保证可复现
np.random.seed(42)
# 生成180个交易日的虚拟数据
dates = pd.date_range("2023-01-01", periods=180, freq='B') # B表示工作日
prices = np.cumprod(1 + np.random.normal(0.001, 0.02, 180)) * 100 # 几何布朗运动模拟股价
df = pd.DataFrame({
'Date': dates,
'Close': np.round(prices, 2)
}).set_index('Date')
print(df.head())
输出示例:
Close
Date
2023-01-02 100.00
2023-01-03 98.45
2023-01-04 96.33
2023-01-05 94.72
2023-01-06 93.18
提示:实际应用中可直接用
yfinance等库获取真实股票数据,但虚拟数据更适合教学演示。
2. 基础特征工程:涨跌幅计算
2.1 日收益率计算
日收益率是金融分析中最基础的特征,有两种常见计算方式:
- 简单收益率:(今日收盘价 - 昨日收盘价) / 昨日收盘价
- 对数收益率:ln(今日收盘价 / 昨日收盘价)
# 方法一:使用diff()
df['Simple_Return'] = df['Close'].diff() / df['Close'].shift(1)
# 方法二:使用pct_change()
df['Simple_Return_Alt'] = df['Close'].pct_change()
# 对数收益率
df['Log_Return'] = np.log(df['Close'] / df['Close'].shift(1))
print(df[['Close', 'Simple_Return', 'Log_Return']].head(3))
关键点说明:
shift(1)将数据向下移动1行,使昨日收盘价与今日对齐- 首日数据因无前值会产生NaN,这是正常现象
2.2 多周期收益率比较
计算不同时间跨度的收益率可观察不同投资周期的表现:
# 计算5日收益率
df['5d_Return'] = df['Close'].pct_change(5)
# 计算20日收益率(约1个月)
df['20d_Return'] = df['Close'].pct_change(20)
# 可视化对比
df[['Simple_Return', '5d_Return', '20d_Return']].plot(figsize=(12,6), alpha=0.7)
plt.title('不同周期收益率对比')
plt.ylabel('收益率');
3. 技术指标生成:移动平均线
3.1 简单移动平均线(SMA)
移动平均线是平滑价格波动的经典指标,计算窗口内的平均值:
# 计算5日和20日移动平均
df['SMA_5'] = df['Close'].rolling(window=5).mean()
df['SMA_20'] = df['Close'].rolling(window=20).mean()
# 可视化
df[['Close', 'SMA_5', 'SMA_20']].plot(figsize=(12,6))
plt.title('价格与移动平均线');
3.2 指数移动平均线(EMA)
EMA给予近期价格更高权重,对价格变化更敏感:
df['EMA_12'] = df['Close'].ewm(span=12, adjust=False).mean()
df['EMA_26'] = df['Close'].ewm(span=26, adjust=False).mean()
# MACD指标计算
df['MACD'] = df['EMA_12'] - df['EMA_26']
df['Signal'] = df['MACD'].ewm(span=9, adjust=False).mean()
3.3 布林带(Bollinger Bands)
布林带结合移动平均和标准差,反映价格波动范围:
# 计算中轨、上轨和下轨
df['Middle_Band'] = df['Close'].rolling(20).mean()
df['Upper_Band'] = df['Middle_Band'] + 2*df['Close'].rolling(20).std()
df['Lower_Band'] = df['Middle_Band'] - 2*df['Close'].rolling(20).std()
# 可视化
df[['Close', 'Middle_Band', 'Upper_Band', 'Lower_Band']].plot(figsize=(12,6))
plt.fill_between(df.index, df['Lower_Band'], df['Upper_Band'], alpha=0.1)
plt.title('布林带指标');
4. 高级特征工程
4.1 波动率计算
波动率是风险评估的重要指标:
# 历史波动率(20日年化)
df['Volatility_20d'] = df['Log_Return'].rolling(20).std() * np.sqrt(252)
# 实现波动率(过去5日)
df['Realized_Vol_5d'] = df['Log_Return'].rolling(5).std() * np.sqrt(252)
# 可视化
df[['Volatility_20d', 'Realized_Vol_5d']].plot(figsize=(12,4))
plt.title('波动率指标对比');
4.2 相对强弱指数(RSI)
RSI反映超买超卖状态,计算14日RSI:
delta = df['Close'].diff()
gain = delta.where(delta > 0, 0)
loss = -delta.where(delta < 0, 0)
avg_gain = gain.rolling(14).mean()
avg_loss = loss.rolling(14).mean()
rs = avg_gain / avg_loss
df['RSI'] = 100 - (100 / (1 + rs))
# 可视化
df['RSI'].plot(figsize=(12,4), ylim=[0,100])
plt.axhline(70, color='r', linestyle='--')
plt.axhline(30, color='g', linestyle='--')
plt.title('相对强弱指数(RSI)');
4.3 交易信号生成
结合多个指标生成交易信号:
# 金叉/死叉信号
df['SMA_Signal'] = np.where(df['SMA_5'] > df['SMA_20'], 1, -1)
# MACD信号
df['MACD_Signal'] = np.where(df['MACD'] > df['Signal'], 1, -1)
# 布林带信号
df['BB_Signal'] = np.where(df['Close'] < df['Lower_Band'], 1,
np.where(df['Close'] > df['Upper_Band'], -1, 0))
# 综合信号
df['Composite_Signal'] = (df['SMA_Signal'] + df['MACD_Signal'] + df['BB_Signal']) / 3
5. 可视化与分析
5.1 多指标综合视图
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(15,10), gridspec_kw={'height_ratios': [3,1,1]})
# 价格和均线
df[['Close', 'SMA_5', 'SMA_20']].plot(ax=ax1)
ax1.set_title('价格与技术指标')
# MACD
df[['MACD', 'Signal']].plot(ax=ax2)
ax2.axhline(0, color='gray', linestyle='--')
# RSI
df['RSI'].plot(ax=ax3, ylim=[0,100])
ax3.axhline(70, color='r', linestyle='--')
ax3.axhline(30, color='g', linestyle='--')
plt.tight_layout()
5.2 回测简单策略
# 假设当综合信号>0时买入,<0时卖出
df['Strategy_Return'] = df['Simple_Return'] * df['Composite_Signal'].shift(1)
# 计算累计收益
df['Cum_Market'] = (1 + df['Simple_Return']).cumprod()
df['Cum_Strategy'] = (1 + df['Strategy_Return']).cumprod()
# 可视化
df[['Cum_Market', 'Cum_Strategy']].plot(figsize=(12,6))
plt.title('策略回测结果')
plt.ylabel('累计收益率');
6. 实战技巧与优化建议
-
处理缺失值:移动窗口计算会产生前N天的NaN值,可用
dropna()或指定min_periods参数:df['SMA_20'] = df['Close'].rolling(20, min_periods=10).mean() -
性能优化:大数据量时避免重复计算:
# 不好的做法:重复计算rolling df['A'] = df['Close'].rolling(20).mean() df['B'] = df['Close'].rolling(20).std() # 好的做法:先创建rolling对象 roller = df['Close'].rolling(20) df['A'] = roller.mean() df['B'] = roller.std() -
多股票分析:使用groupby处理多只股票:
# 假设df包含'Ticker'列 df.groupby('Ticker')['Close'].rolling(20).mean() -
实时更新:增量计算最新指标:
last_20 = df['Close'].iloc[-20:] current_sma = last_20.mean() -
参数优化:用网格搜索寻找最佳参数组合:
from sklearn.model_selection import ParameterGrid param_grid = { 'sma_short': range(5, 20, 5), 'sma_long': range(20, 60, 10) } for params in ParameterGrid(param_grid): df[f"SMA_{params['sma_short']}"] = df['Close'].rolling(params['sma_short']).mean() df[f"SMA_{params['sma_long']}"] = df['Close'].rolling(params['sma_long']).mean() # 评估策略表现...
通过Pandas这一系列时间序列操作,我们实现了从基础数据预处理到复杂技术指标生成的完整流程。实际应用中,这些技术可扩展到期权定价、风险价值计算等更复杂的金融工程领域。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)