2026年5月7日,A股市场迎来"五一"后首个交易日,芯片板块集体爆发,寒武纪股价首次突破1900元,科创板芯片指数单日暴涨5.92%。这样的行情数据背后,藏着大量值得分析的信息。
今天这篇文章,船长用Python的pandas库,手把手带你从零开始处理真实的股票行情数据。学完这篇,你就能自己动手分析任意一只股票的历史走势。
一、环境准备
本文使用pandas + matplotlib,数据来源为AKShare免费行情接口。先安装依赖:
pip install pandas matplotlib akshare
如果你的网络无法访问PyPI,可以使用国内镜像:
pip install pandas matplotlib akshare -i https://pypi.tuna.tsinghua.edu.cn/simple
二、获取股票数据
使用AKShare获取寒武纪(688256)的历史行情数据:
import pandas as pd
import matplotlib.pyplot as plt
import akshare as ak
# 获取寒武纪(688256)近一年日线数据
df = ak.stock_zh_a_hist(symbol="688256", period="daily",
start_date="20250508", end_date="20260508",
adjust="qfq")
print(f"获取到 {len(df)} 条数据")
print(df.head())
print(df.dtypes)
输出字段说明:
-
日期:交易日期 -
开盘:当日开盘价 -
收盘:当日收盘价 -
最高:当日最高价 -
最低:当日最低价 -
成交量:当日成交股数 -
成交额:当日成交金额(元) -
涨跌幅:当日涨跌百分比
三、数据清洗基础
原始数据往往存在缺失值、异常值,需要先清洗再分析。
3.1 检查缺失值
# 检查每列缺失值数量
print(df.isnull().sum())
# 查看有缺失值的行
missing_rows = df[df.isnull().any(axis=1)]
print(f"存在缺失值的行数: {len(missing_rows)}")
如果数据中有缺失值,用前值填充或直接删除:
# 方法1:删除缺失行
df_clean = df.dropna()
# 方法2:用前值填充
df_clean = df.fillna(method='ffill')
print(f"清洗前: {len(df)} 行, 清洗后: {len(df_clean)} 行")
3.2 转换日期格式
# 将日期列转换为datetime类型,方便后续按时间筛选
df_clean['日期'] = pd.to_datetime(df_clean['日期'])
# 按日期升序排序
df_clean = df_clean.sort_values('日期').reset_index(drop=True)
print(f"数据时间范围: {df_clean['日期'].min()} 至 {df_clean['日期'].max()}")
3.3 识别异常值
# 用IQR方法识别单日涨跌幅异常
Q1 = df_clean['涨跌幅'].quantile(0.25)
Q3 = df_clean['涨跌幅'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 3 * IQR # 用3倍IQR,更宽松
upper_bound = Q3 + 3 * IQR
outliers = df_clean[(df_clean['涨跌幅'] upper_bound)]
print(f"异常涨跌幅阈值: [{lower_bound:.2f}%, {upper_bound:.2f}%]")
print(f"异常值数量: {len(outliers)}")
if len(outliers) > 0:
print(outliers[['日期', '涨跌幅']])
四、核心指标计算
4.1 日收益率与累计收益率
# 计算单日收益率(Close to Close)
df_clean['日收益率'] = df_clean['收盘'].pct_change() * 100
# 计算累计收益率(买入持有策略)
df_clean['累计收益率'] = (1 + df_clean['日收益率'] / 100).cumprod() - 1
df_clean['累计收益率'] = df_clean['累计收益率'] * 100
print(f"最高单日涨幅: {df_clean['日收益率'].max():.2f}%")
print(f"最高单日跌幅: {df_clean['日收益率'].min():.2f}%")
print(f"区间累计收益率: {df_clean['累计收益率'].iloc[-1]:.2f}%")
4.2 移动平均线(MA5 / MA20 / MA60)
# 计算简单移动平均
df_clean['MA5'] = df_clean['收盘'].rolling(window=5).mean()
df_clean['MA20'] = df_clean['收盘'].rolling(window=20).mean()
df_clean['MA60'] = df_clean['收盘'].rolling(window=60).mean()
# 查看最新数据
print(df_clean[['日期', '收盘', 'MA5', 'MA20', 'MA60']].tail())
4.3 波动率(Volatility)
# 计算20日年化波动率
df_clean['日波动率'] = df_clean['日收益率'].rolling(window=20).std()
df_clean['年化波动率'] = df_clean['日波动率'] * (252 ** 0.5) # 年化
print(f"最新年化波动率: {df_clean['年化波动率'].iloc[-1]:.2f}%")
print(f"平均年化波动率: {df_clean['年化波动率'].mean():.2f}%")
4.4 成交量异常检测(量比)
# 计算量比:当日成交量 / 过去20日平均成交量
df_clean['量比'] = df_clean['成交量'] / df_clean['成交量'].rolling(window=20).mean()
# 放量标准:量比 > 2
surge_days = df_clean[df_clean['量比'] > 2]
print(f"放量日(量比>2): {len(surge_days)} 天")
if len(surge_days) > 0:
print(surge_days[['日期', '成交量', '涨跌幅', '量比']].tail())
五、可视化实战
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
fig, axes = plt.subplots(3, 1, figsize=(14, 10),
gridspec_kw={'height_ratios': [3, 1, 1]})
# 图1:股价 + 移动平均线
ax1 = axes[0]
ax1.plot(df_clean['日期'], df_clean['收盘'], label='收盘价', linewidth=1.5)
ax1.plot(df_clean['日期'], df_clean['MA5'], label='MA5', alpha=0.7, linewidth=1)
ax1.plot(df_clean['日期'], df_clean['MA20'], label='MA20', alpha=0.7, linewidth=1)
ax1.plot(df_clean['日期'], df_clean['MA60'], label='MA60', alpha=0.7, linewidth=1)
ax1.set_title('寒武纪(688256)股价走势与移动平均线', fontsize=14)
ax1.legend(loc='upper left')
ax1.grid(True, alpha=0.3)
# 图2:成交量柱状图
ax2 = axes[1]
colors = ['red' if c >= 0 else 'green'
for c in df_clean['涨跌幅'].fillna(0)]
ax2.bar(df_clean['日期'], df_clean['成交量'] / 1e8, color=colors, alpha=0.7)
ax2.set_title('成交量(亿元)', fontsize=12)
ax2.grid(True, alpha=0.3)
# 图3:累计收益率
ax3 = axes[2]
ax3.fill_between(df_clean['日期'], df_clean['累计收益率'],
where=df_clean['累计收益率'] >= 0,
color='red', alpha=0.3)
ax3.fill_between(df_clean['日期'], df_clean['累计收益率'],
where=df_clean['累计收益率'] < 0,
color='green', alpha=0.3)
ax3.axhline(y=0, color='black', linewidth=0.5)
ax3.plot(df_clean['日期'], df_clean['累计收益率'], color='blue', linewidth=1)
ax3.set_title('累计收益率(%)', fontsize=12)
ax3.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('hanwu Trend.png', dpi=150)
plt.show()
print("图表已保存为 hanwu_trend.png")
六、完整分析脚本
以下是今天的实战完整代码,将上述所有步骤整合在一起:
import pandas as pd
import matplotlib.pyplot as plt
import akshare as ak
# ========== 1. 获取数据 ==========
df = ak.stock_zh_a_hist(symbol="688256", period="daily",
start_date="20250508", end_date="20260508",
adjust="qfq")
# ========== 2. 数据清洗 ==========
df['日期'] = pd.to_datetime(df['日期'])
df = df.sort_values('日期').reset_index(drop=True)
df = df.dropna() # 删除缺失值
# ========== 3. 指标计算 ==========
df['日收益率'] = df['收盘'].pct_change() * 100
df['累计收益率'] = (1 + df['日收益率'] / 100).cumprod() - 1
df['累计收益率'] = df['累计收益率'] * 100
df['MA5'] = df['收盘'].rolling(window=5).mean()
df['MA20'] = df['收盘'].rolling(window=20).mean()
df['MA60'] = df['收盘'].rolling(window=60).mean()
df['量比'] = df['成交量'] / df['成交量'].rolling(window=20).mean()
# ========== 4. 输出关键数据 ==========
latest = df.iloc[-1]
print(f"=== 寒武纪(688256) 最新行情 ===")
print(f"日期: {latest['日期'].strftime('%Y-%m-%d')}")
print(f"收盘价: {latest['收盘']:.2f} 元")
print(f"单日涨幅: {latest['涨跌幅']:.2f}%")
print(f"量比: {latest['量比']:.2f}")
print(f"20日均线: {latest['MA20']:.2f}")
print(f"区间累计收益: {latest['累计收益率']:.2f}%")
七、总结
本文覆盖了pandas金融数据分析的核心流程:
-
数据获取:使用AKShare免费接口获取真实行情数据
-
数据清洗:缺失值处理、日期格式转换、异常值识别
-
指标计算:日收益率、累计收益率、移动平均线、波动率、量比
-
可视化:股价走势、成交量、累计收益三图联动
学会了这些基础,你就能分析任意一只A股或港股的走势。船长建议你用这个脚本跑一跑自己关注的股票,感受数据说话的力量。
【数据来源】AKShare免费行情接口 | 【时间】2026年5月
作者:CaptainTalk 数据分析系列