本文使用 Pandas 做股票行情数据清洗、指标计算、回测前置分析,包含行情读取、均线、MACD、RSI、布林带、收益率、滚动波动率、相关性、可视化,适合A股/美股日线数据,延续整套教程风格。
前置依赖
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
一、示例数据说明
一般股票日线字段标准:date, open, high, low, close, volume(日期、开盘、最高、最低、收盘、成交量)
你可以用 tushare / akshare 获取真实A股数据;下面先构造模拟行情用于演示。
# 构造模拟日线数据
np.random.seed(42)
dates = pd.date_range(start="2025-01-01", end="2025-06-30", freq="D")
n = len(dates)
base_price = 10
close = base_price + np.cumsum(np.random.randn(n)*0.2)
df = pd.DataFrame({
"date": dates,
"close": close,
"open": close + np.random.randn(n)*0.15,
"high": close + np.abs(np.random.randn(n)*0.3),
"low": close - np.abs(np.random.randn(n)*0.3),
"volume": np.random.randint(100000, 500000, size=n)
})
# 日期设为索引,时序分析必备
df = df.set_index("date")
df = df.sort_index()
真实数据获取示例(akshare)
# pip install akshare
# import akshare as ak
# df = ak.stock_zh_a_hist(symbol="600036", period="daily", start_date="20250101", end_date="20250630")
# df = df[["date","open","high","low","close","volume"]].set_index("date")
二、基础收益率计算
# 单日涨跌幅(简单收益率)
df["pct_change"] = df["close"].pct_change()
# 对数收益率(金融统计常用,可累加)
df["log_return"] = np.log(df["close"] / df["close"].shift(1))
# 累计净值
df["net_value"] = (1 + df["pct_change"]).cumprod()
pct_change():后值 / 前值 -1,就是日常看到的涨跌幅- 对数收益率:适合做统计、波动率、相关性
三、移动平均线 MA(均线)
# 5日、10日、20日、60日均线
df["ma5"] = df["close"].rolling(window=5).mean()
df["ma10"] = df["close"].rolling(window=10).mean()
df["ma20"] = df["close"].rolling(window=20).mean()
df["ma60"] = df["close"].rolling(window=60).mean()
# 金叉死叉标记:5日均线上穿10日均线=金叉
df["golden_cross"] = np.where(
(df["ma5"] > df["ma10"]) & (df["ma5"].shift(1) <= df["ma10"].shift(1)),
1, 0
)
df["death_cross"] = np.where(
(df["ma5"] < df["ma10"]) & (df["ma5"].shift(1) >= df["ma10"].shift(1)),
1, 0
)
四、布林带 BOLL
布林带 = 中轨(MA20) + 上下轨(±2倍标准差)
df["boll_mid"] = df["close"].rolling(20).mean()
df["boll_std"] = df["close"].rolling(20).std()
df["boll_upper"] = df["boll_mid"] + 2 * df["boll_std"]
df["boll_lower"] = df["boll_mid"] - 2 * df["boll_std"]
- 价格上轨上方:超买
- 价格下轨下方:超卖
五、RSI 相对强弱指标
def calc_rsi(series, period=14):
delta = series.diff()
gain = delta.where(delta > 0, 0)
loss = -delta.where(delta < 0, 0)
avg_gain = gain.rolling(window=period).mean()
avg_loss = loss.rolling(window=period).mean()
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi
df["rsi14"] = calc_rsi(df["close"], 14)
- RSI >70:超买
- RSI <30:超卖
六、MACD 指标
def calc_macd(series, fast=12, slow=26, signal=9):
ema_fast = series.ewm(span=fast, adjust=False).mean()
ema_slow = series.ewm(span=slow, adjust=False).mean()
dif = ema_fast - ema_slow
dea = dif.ewm(span=signal, adjust=False).mean()
macd_bar = (dif - dea) * 2
return dif, dea, macd_bar
df["dif"], df["dea"], df["macd_bar"] = calc_macd(df["close"])
- DIF上穿DEA:MACD金叉
- DIF下穿DEA:MACD死叉
七、波动率、最大回撤(风险指标)
# 20日滚动波动率(年化,交易日按250)
df["vol_20d"] = df["log_return"].rolling(20).std() * np.sqrt(250)
# 最大回撤
cummax = df["net_value"].cummax()
df["drawdown"] = df["net_value"] / cummax - 1
max_drawdown = df["drawdown"].min()
print(f"最大回撤:{max_drawdown:.2%}")
八、多标的相关性(多股票对比)
如果有多只股票 close 合并到一张表,可以直接算相关系数矩阵
# 示例:df_multi 每一列是一只股票的收盘价
# corr = df_multi.pct_change().corr()
# sns.heatmap(corr, annot=True, cmap="coolwarm")
九、绘图:K线辅助图(均线、布林带)
简易价格+均线图(K线需要 mplfinance,下面是pandas原生绘图)
plt.figure(figsize=(12,6))
plt.plot(df["close"], label="收盘价", color="#222222")
plt.plot(df["ma5"], label="MA5", color="#f44336")
plt.plot(df["ma20"], label="MA20", color="#2196f3")
plt.plot(df["boll_upper"], label="BOLL上轨", ls="--", color="#999999")
plt.plot(df["boll_lower"], label="BOLL下轨", ls="--", color="#999999")
plt.title("股价、均线与布林带")
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
K线图:

mplfinance 专业K线(扩展)
# pip install mplfinance
# import mplfinance as mpf
# mpf.plot(df, type="candle", mav=(5,20), volume=True, show_nontrading=False)
十、简单策略回测模板(金叉买入、死叉卖出)
# 初始化仓位
df["pos"] = 0
df.loc[df["golden_cross"] == 1, "pos"] = 1
df.loc[df["death_cross"] == 1, "pos"] = 0
# 仓位向前填充(持仓保持)
df["pos"] = df["pos"].replace(0, np.nan).ffill().fillna(0)
# 策略收益 = 持仓 * 当日收益率
df["strategy_return"] = df["pos"].shift(1) * df["pct_change"]
df["strategy_net"] = (1 + df["strategy_return"]).cumprod()
# 对比基准净值
plt.figure(figsize=(10,5))
plt.plot(df["net_value"], label="持有基准")
plt.plot(df["strategy_net"], label="均线策略")
plt.legend()
plt.title("策略净值对比")
plt.show()
⚠️ 简化模型,未考虑手续费、滑点、涨跌停、T+1规则,仅用于学习Pandas指标计算。

十一、常用指标速查表
| 指标 | Pandas核心函数 |
|---|---|
| 涨跌幅 | pct_change() |
| 对数收益 | np.log(close / close.shift(1)) |
| 均线MA | rolling().mean() |
| 布林带 | rolling().mean() + rolling().std() |
| RSI | 滚动涨跌均值 |
| MACD | ewm().mean() 指数移动平均 |
| 滚动波动率 | rolling().std() |
| 最大回撤 | cummax() |
| 累计净值 | cumprod() |
十二、实战避坑清单
- 交易日过滤:原始数据包含周末节假日,要先剔除空行情行
shift时序对齐:信号是当天收盘产生,次日才能交易,仓位要shift(1)- EWM 指数均线
adjust=False是金融标准写法,不要省略 - 复权!A股必须用前复权价格计算指标,不复权会因为分红除权造成指标失真
- 滚动窗口
rolling前面N行是NaN,回测时注意跳过 - 不要用未来函数:指标计算不能用到未来数据
- 多标的合并时,注意日期对齐,缺失行情用
reindex补全
十三、拓展方向
- 用
merge_asof匹配基本面、公告、财报数据 - 分行业分组、行业中性超额收益
- 因子IC、IR、单因子分析
- 对接Backtrader、VectorBT做专业回测
0 条笔记