目录
在金融量化分析中,时间序列数据处理是核心基础 —— 无论是股票 K 线、期货合约还是加密货币行情,所有策略逻辑的实现都依赖于对数据的高效清洗、转换与指标计算。本文基于 Pandas 库,从数据预处理到技术指标落地,手把手讲解量化分析中的关键操作,最终通过 “均线金叉死叉” 案例展示策略逻辑的实现,适合量化入门者与数据分析师参考。
一、基础准备:数据与环境
在开始前,需确保环境已安装核心库。金融数据常用 Tushare(A 股)、Yahoo Finance(美股)或聚宽 API 获取,本文以股票日度数据为例(含date
、open
、close
、high
、low
等字段)。
1.1 环境安装
# 安装核心库
pip install pandas numpy matplotlib tushare # tushare用于获取A股数据
1.2 数据获取示例(Tushare)
import pandas as pd
import tushare as ts
# 设置Tushare token(需注册账号获取:https://tushare.pro/)
ts.set_token("你的Tushare Token")
pro = ts.pro_api()
# 获取贵州茅台(600519.SH)2020-2024年日度数据
df = pro.daily(ts_code="600519.SH", start_date="20200101", end_date="20240531")
print("原始数据结构:")
print(df.head())
原始数据中,date
字段为字符串格式(如20200102
),无法直接用于时间序列分析,需先进行类型转换与索引设置。
二、核心操作 1:时间序列数据预处理
时间序列的核心是 “时间有序性”,因此第一步必须确保数据按时间排序,并将时间字段设为索引 —— 这是后续重采样、滑动窗口计算的前提。
2.1 列类型转换(字符串→时间格式)
Pandas 的to_datetime()
可将字符串格式的日期转换为datetime64
类型,支持多种格式(如20200102
、2020-01-02
)。
# 1. 转换日期格式:字符串→datetime
df["date"] = pd.to_datetime(df["date"]) # 自动识别"20200102"为2020-01-02
# 2. 按日期升序排序(避免数据乱序)
df = df.sort_values("date").reset_index(drop=True)
# 3. 设置date为索引(方便后续按时间筛选、重采样)
df.set_index("date", inplace=True) # inplace=True:直接修改原DataFrame
print("处理后的数据结构:")
print(df.head())
print("索引类型:", df.index.dtype) # 输出:datetime64[ns]
2.2 数据移位:shift () 实现 “前后对比”
在量化分析中,经常需要对比 “当前值” 与 “前 N 期值”(如当日开盘价与昨日收盘价的差值),shift()
方法可实现数据的上移 / 下移。
方法 | 效果 | 应用场景示例 |
---|---|---|
shift(1) | 数据下移 1 行(取前 1 期) | 当日开盘价 - 昨日收盘价 |
shift(-1) | 数据上移 1 行(取后 1 期) | 当日收盘价 - 明日开盘价 |
shift(5) | 数据下移 5 行(取前 5 期) | 计算 5 期前的均线值 |
实战示例:筛选 “开盘价较昨日收盘价下跌≥2%” 的日期
# 计算:(当日开盘价 - 昨日收盘价) / 昨日收盘价 ≤ -2%
# shift(1):获取昨日收盘价(close列下移1行)
price_drop_condition = (df["open"] - df["close"].shift(1)) / df["close"].shift(1) <= -0.02
# 筛选满足条件的日期索引
drop_dates = df[price_drop_condition].index
print("开盘价较昨日收盘价下跌≥2%的日期:")
print(drop_dates[:10]) # 输出前10个日期
三、核心操作 2:时间序列重采样(resample)
高频数据(如分钟级)往往噪声较多,需通过重采样转换为低频数据(如日度、周度),或按业务需求生成周期性报表(如月度第一个交易日、年度最后一个交易日)。
Pandas 的resample()
方法支持灵活的频率设置,核心逻辑是:按指定频率分组,对组内数据聚合(如取首值、尾值、均值)。
3.1 常用频率编码表(量化必背)
频率类型 | 频率编码 | 含义 | 应用场景示例 |
---|---|---|---|
高频 | 10S | 每 10 秒 | 传感器实时温度数据 |
30min | 每 30 分钟 | 股票分时数据、交通流量 | |
1H | 每 1 小时 | 服务器负载监控 | |
BH | 每个工作日的每小时 | 工作时间客服咨询量 | |
中频 | 1D | 每天 | 日销售额、DAU(日活用户) |
BD | 每个工作日(周一至周五) | 工作日订单量、考勤数据 | |
W-SUN | 每周日(默认周结) | 周销售额、WAU(周活用户) | |
W-MON | 每周一(自定义周结) | 企业周一结上周报表 | |
低频 | BMS | 每月第一个工作日 | 月初业绩目标、月度计划 |
BM | 每月最后一个工作日 | 月度财务结账 | |
QS-JAN | 季度初(1/4/7/10 月 1 日) | 季度预算规划 | |
BA | 每年最后一个工作日 | 年度财务总结 |
3.2 重采样实战:筛选买卖点日期
在量化策略中,常需按 “月度第一个交易日买入”“年度最后一个交易日卖出” 的逻辑设置买卖点,resample()
+ 聚合函数可高效实现。
# 假设用"收盘价"作为信号列,筛选买卖点日期
con = df["close"] # 信号列(实际可替换为策略信号,如均线交叉)
# 1. 买入点:每月第一个工作日(BMS)的第一个数据(即当日收盘价)
buy_dates = con.resample("BMS").first()
print("月度买入日期及收盘价:")
print(buy_dates.head())
# 2. 卖出点:每年最后一个工作日(BA)的最后一个数据,排除最后一年(避免未结束周期)
sell_dates = con.resample("BA").last()[:-1]
print("\n年度卖出日期及收盘价:")
print(sell_dates.head())
四、核心操作 3:滑动窗口与均线计算(rolling)
技术分析中,移动平均线(MA) 是最基础的指标(如 MA5、MA30),其本质是 “固定窗口内数据的均值”。Pandas 的rolling()
方法可快速实现滑动窗口计算,支持均值、求和、最大值等聚合操作。
4.1 均线计算实战
# 1. 计算5日均线(MA5):窗口大小=5,聚合函数=均值
df["MA5"] = df["close"].rolling(window=5).mean()
# 2. 计算30日均线(MA30):窗口大小=30,聚合函数=均值
df["MA30"] = df["close"].rolling(window=30).mean()
# 3. 查看结果(前35行,MA30需30个数据才会有值,前29行为NaN)
print("收盘价与均线数据:")
print(df[["close", "MA5", "MA30"]].head(35))
4.2 窗口计算扩展
除均值外,rolling()
还支持多种聚合函数,满足不同分析需求:
# 1. 5日最高价(窗口内最大值)
df["MA5_high"] = df["high"].rolling(5).max()
# 2. 30日成交量总和(窗口内求和)
df["MA30_volume"] = df["vol"].rolling(30).sum()
# 3. 5日收盘价标准差(窗口内波动率)
df["MA5_std"] = df["close"].rolling(5).std()
五、实战案例:均线金叉死叉策略实现
“金叉” 与 “死叉” 是均线策略的核心信号:
- 金叉:短期均线上穿长期均线(如 MA5 从下向上穿过 MA30),视为买入信号;
- 死叉:短期均线下穿长期均线(如 MA5 从上向下穿过 MA30),视为卖出信号。
5.1 信号逻辑定义
基于 MA5 与 MA30 的位置关系,定义两个中间条件,再通过 “前后对比” 筛选出交叉点:
s1
:MA5 < MA30(短期均线在长期均线下方);s2
:MA5 > MA30(短期均线在长期均线上方);- 死叉:当日
s1
为 True,且前一日s2
为 True(MA5 从上方下穿 MA30); - 金叉:当日
s2
为 True,且前一日s1
为 True(MA5 从下方上穿 MA30)。
5.2 代码实现与结果验证
# 1. 确保MA5和MA30已计算(参考第四章)
# 2. 定义中间条件
df["s1"] = df["MA5"] < df["MA30"] # MA5在MA30下方
df["s2"] = df["MA5"] > df["MA30"] # MA5在MA30上方
# 3. 计算死叉信号(当日s1=True,前一日s2=True)
df["death_cross"] = df["s1"] & df["s2"].shift(1)
# 4. 计算金叉信号(当日s2=True,前一日s1=True)
df["gold_cross"] = df["s2"] & df["s1"].shift(1)
# 5. 筛选金叉和死叉日期
gold_dates = df[df["gold_cross"]].index
death_dates = df[df["death_cross"]].index
print("2023年以来的金叉日期:")
print(gold_dates[gold_dates >= "2023-01-01"])
print("\n2023年以来的死叉日期:")
print(death_dates[death_dates >= "2023-01-01"])
5.3 结果可视化(直观验证)
通过 Matplotlib 绘制收盘价、均线及交叉点,直观查看策略信号:
import matplotlib.pyplot as plt
# 设置中文字体(避免乱码)
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# 选取2023年数据绘图
plot_data = df.loc["2023-01-01":"2023-12-31"]
# 创建画布
fig, ax = plt.subplots(figsize=(12, 6))
# 绘制收盘价、MA5、MA30
ax.plot(plot_data.index, plot_data["close"], label="收盘价", color="blue", alpha=0.6)
ax.plot(plot_data.index, plot_data["MA5"], label="MA5", color="red", linewidth=2)
ax.plot(plot_data.index, plot_data["MA30"], label="MA30", color="green", linewidth=2)
# 标记金叉(绿色上三角)和死叉(红色下三角)
gold_points = plot_data[plot_data["gold_cross"]]
death_points = plot_data[plot_data["death_cross"]]
ax.scatter(gold_points.index, gold_points["close"], marker="^", color="green", s=100, label="金叉")
ax.scatter(death_points.index, death_points["close"], marker="v", color="red", s=100, label="死叉")
# 设置图表属性
ax.set_title("2023年贵州茅台收盘价与均线金叉死叉", fontsize=14)
ax.set_xlabel("日期", fontsize=12)
ax.set_ylabel("价格(元)", fontsize=12)
ax.legend()
plt.xticks(rotation=45)
plt.tight_layout()
# 保存图片
plt.savefig("gold_death_cross_2023.png", dpi=300)
plt.show()
运行代码后,会生成包含 “收盘价、MA5、MA30、金叉、死叉” 的可视化图,可直观看到策略信号的触发时机。
六、总结与扩展
本文从 Pandas 基础操作出发,覆盖了金融量化的核心数据处理流程:
- 数据预处理:日期类型转换、索引设置、数据移位;
- 重采样:按业务需求生成周期性数据(如月度买入点);
- 滑动窗口:计算均线等技术指标;
- 策略实现:基于均线的金叉死叉信号筛选。
扩展方向
- 策略优化:加入成交量、MACD 等其他指标,避免单一均线信号的误判;
- 回测框架:结合
Backtrader
或VectorBT
,对金叉死叉策略进行回测(计算收益率、最大回撤等); - 高频策略:将日度数据替换为分钟级数据,利用
resample("5min")
实现高频策略逻辑。