Appearance
获取股票数据
数据是量化投资的原材料。没有数据,再好的策略也只是纸上谈兵。本章教你如何用Python获取A股的行情数据、财务数据和指数数据,并进行基本的清洗和处理。
准备工作
安装AKShare
本章主要使用AKShare库来获取数据。如果还没有安装,请先安装:
bash
pip install akshare --upgrade--upgrade 确保安装最新版本,因为AKShare更新频繁,接口偶尔会调整。
导入必要的库
每次开始数据分析前,先导入需要的库:
python
import akshare as ak
import pandas as pd习惯上,pandas会缩写为 pd,这是约定俗成的写法,几乎所有Python数据分析代码都这样做。
获取股票日线行情
日K线数据
日K线(日线)是最基础的行情数据,包含每个交易日的开盘价、最高价、最低价、收盘价、成交量等信息。
python
import akshare as ak
# 获取贵州茅台(600519)2024年全年日K线
df = ak.stock_zh_a_hist(
symbol="600519",
period="daily",
start_date="20240101",
end_date="20241231",
adjust="qfq" # 前复权
)
print(df.head()) # 查看前5行
print(df.shape) # 查看数据行数和列数
print(df.columns.tolist()) # 查看所有列名参数说明:
| 参数 | 含义 | 常用值 |
|---|---|---|
| symbol | 股票代码 | "600519"、"000858"等 |
| period | 周期 | "daily"(日线)、"weekly"(周线)、"monthly"(月线) |
| start_date | 开始日期 | "YYYYMMDD"格式 |
| end_date | 结束日期 | "YYYYMMDD"格式 |
| adjust | 复权方式 | "qfq"(前复权)、"hfq"(后复权)、""(不复权) |
什么是复权
复权是对股票价格进行调整,消除因送股、配股、分红等除权事件造成的价格跳空,使价格走势具有连续可比性。
假设一只股票价格100元,每10股送10股后,价格变成50元。如果不复权,K线图上会出现一个巨大的向下跳空缺口,看起来像暴跌了50%,但实际上你的总资产没变。
- 前复权(qfq):以当前价格为基准,调整历史价格。K线图看起来连续平滑。这是技术分析中最常用的方式。
- 后复权(hfq):以上市首日价格为基准,调整后续价格。适合看长期的真实收益。
- 不复权:原始价格,保留除权缺口。
做量化分析时,绝大多数情况应该使用前复权数据。
数据格式说明
获取到的数据通常包含以下列(列名可能因AKShare版本略有不同):
| 列名 | 含义 |
|---|---|
| 日期 | 交易日期 |
| 开盘 | 开盘价 |
| 收盘 | 收盘价 |
| 最高 | 最高价 |
| 最低 | 最低价 |
| 成交量 | 成交量(股) |
| 成交额 | 成交金额(元) |
| 振幅 | 当日振幅百分比 |
| 涨跌幅 | 当日涨跌幅百分比 |
| 涨跌额 | 当日涨跌金额 |
| 换手率 | 当日换手率百分比 |
查看数据概况
获取数据后,先做一些基本检查:
python
# 数据量
print(f"共 {len(df)} 条记录")
# 时间范围
print(f"时间范围: {df['日期'].min()} 至 {df['日期'].max()}")
# 检查缺失值
print("\n各列缺失值统计:")
print(df.isnull().sum())
# 数据类型
print("\n数据类型:")
print(df.dtypes)
# 基本统计
print("\n收盘价统计:")
print(df["收盘"].describe())养成良好的习惯:每次拿到新数据,先做这些基本检查,确认数据没有明显问题。
获取周线和月线数据
只需修改 period 参数:
python
# 周线数据
df_weekly = ak.stock_zh_a_hist(
symbol="600519",
period="weekly",
start_date="20240101",
end_date="20241231",
adjust="qfq"
)
# 月线数据
df_monthly = ak.stock_zh_a_hist(
symbol="600519",
period="monthly",
start_date="20240101",
end_date="20241231",
adjust="qfq"
)周线和月线数据量更少,适合分析中长期趋势。
批量获取多只股票数据
实际分析中,经常需要同时获取多只股票的数据。
python
# 定义股票池
stock_list = {
"600519": "贵州茅台",
"000858": "五粮液",
"601318": "中国平安",
"000333": "美的集团",
"600036": "招商银行",
}
# 批量获取
stock_data = {}
for code, name in stock_list.items():
try:
df = ak.stock_zh_a_hist(
symbol=code,
period="daily",
start_date="20240101",
end_date="20241231",
adjust="qfq"
)
stock_data[code] = df
print(f"已获取 {name}({code}),共 {len(df)} 条记录")
except Exception as e:
print(f"获取 {name}({code}) 失败: {e}")几点说明:
try/except用于捕获异常,避免某只股票获取失败导致整个程序中断stock_data字典的键是股票代码,值是对应的DataFrame- 批量获取时建议加入适当的延时(如
time.sleep(0.5)),避免请求过于频繁
加入延时的完整版本:
python
import time
for code, name in stock_list.items():
try:
df = ak.stock_zh_a_hist(
symbol=code,
period="daily",
start_date="20240101",
end_date="20241231",
adjust="qfq"
)
stock_data[code] = df
print(f"已获取 {name}({code}),共 {len(df)} 条记录")
time.sleep(0.5) # 暂停0.5秒,避免请求过快
except Exception as e:
print(f"获取 {name}({code}) 失败: {e}")
time.sleep(1) # 失败后多等一会获取财务数据
除了行情数据,基本面分析还需要财务数据。
个股财务指标
python
# 获取个股的主要财务指标
df_financial = ak.stock_financial_abstract_ths(
symbol="600519",
indicator="按报告期"
)
print(df_financial.head())
print(df_financial.columns.tolist())返回的数据通常包括每股收益、净资产收益率、营业收入、净利润等关键指标。
获取市盈率、市净率等估值数据
python
# 获取个股的估值指标
df_valuation = ak.stock_a_indicator_lg(symbol="600519")
print(df_valuation.tail()) # 查看最近几条记录返回的数据可能包含:市盈率(PE)、市净率(PB)、股息率等估值指标。
注意事项
- 财务数据更新频率不同于行情数据,通常是季度更新(一季报、中报、三季报、年报)
- 不同数据源的财务指标名称和计算口径可能有差异,使用前先确认
- 某些AKShare接口可能需要指定
indicator参数来选择报告类型
获取指数数据
指数数据(如沪深300、上证50)对于判断市场整体走势非常重要。
python
# 获取沪深300指数数据
df_index = ak.stock_zh_index_daily(symbol="sh000300")
# 数据量可能很大,只看最近的数据
print(df_index.tail(10))常见A股指数代码:
| 指数名称 | 代码 |
|---|---|
| 上证指数 | sh000001 |
| 深证成指 | sz399001 |
| 沪深300 | sh000300 |
| 上证50 | sh000016 |
| 中证500 | sh000905 |
| 创业板指 | sz399006 |
注意:AKShare的接口可能随版本更新而变化。如果以上代码运行报错,可以到AKShare官方文档查看最新接口。
数据处理技巧
处理缺失值
获取到的数据可能会有缺失值(NaN),需要处理。
python
# 检查缺失值
print("缺失值统计:")
print(df.isnull().sum())
# 方法一:删除包含缺失值的行(数据量充足时)
df_clean = df.dropna()
# 方法二:前向填充(用前一个有效值填充)
df_filled = df.fillna(method="ffill")
# 方法三:后向填充(用后一个有效值填充)
df_bfilled = df.fillna(method="bfill")
# 方法四:用特定值填充
df_zero_filled = df.fillna(0)选择哪种方法取决于具体情况:
- 如果缺失值很少(少于总数据量的1%),直接删除即可
- 如果是价格数据出现缺失,通常用前向填充(
ffill),因为前一天的价格是最合理的估计 - 成交量缺失时,可以用0填充,表示当天没有成交
日期对齐
当你同时分析多只股票或多个数据源时,它们可能覆盖不同的日期范围(比如某只股票停牌期间没有数据)。需要做日期对齐。
python
# 假设有两只股票的数据
df_a = stock_data["600519"] # 贵州茅台
df_b = stock_data["000858"] # 五粮液
# 确保日期列是日期类型
df_a["日期"] = pd.to_datetime(df_a["日期"])
df_b["日期"] = pd.to_datetime(df_b["日期"])
# 方法一:取两只股票共有的交易日
common_dates = set(df_a["日期"]) & set(df_b["日期"])
df_a_aligned = df_a[df_a["日期"].isin(common_dates)]
df_b_aligned = df_b[df_b["日期"].isin(common_dates)]
# 方法二:以日期为索引后,用pandas的对齐功能
df_a = df_a.set_index("日期")
df_b = df_b.set_index("日期")
# 合并,自动对齐日期(缺失值填充为NaN)
combined = pd.DataFrame({
"茅台收盘": df_a["收盘"],
"五粮液收盘": df_b["收盘"],
})
combined = combined.dropna() # 删除没有对齐的日期
print(combined.head())计算衍生指标
获取到原始数据后,通常需要计算一些技术分析指标。
python
# 确保"日期"是日期类型并设为索引
df["日期"] = pd.to_datetime(df["日期"])
df = df.set_index("日期")
# 移动平均线
df["MA5"] = df["收盘"].rolling(window=5).mean()
df["MA10"] = df["收盘"].rolling(window=10).mean()
df["MA20"] = df["收盘"].rolling(window=20).mean()
# 每日涨跌幅
df["涨跌幅"] = df["收盘"].pct_change()
# 波动率(20日标准差,年化)
df["波动率"] = df["涨跌幅"].rolling(window=20).std() * (252 ** 0.5)
# 252是A股大约的年交易日数,开平方是日度到年度的转换
# 成交量移动平均
df["成交量MA5"] = df["成交量"].rolling(window=5).mean()
# 量比(当日成交量 / 5日平均成交量)
df["量比"] = df["成交量"] / df["成交量MA5"]
print(df[["收盘", "MA5", "MA20", "涨跌幅", "量比"]].tail(10))数据类型转换
获取的数据有时列类型不正确(比如数字被存为字符串),需要转换。
python
# 查看数据类型
print(df.dtypes)
# 如果收盘价是字符串,转换为浮点数
df["收盘"] = pd.to_numeric(df["收盘"], errors="coerce")
# 如果成交量是字符串,转换为整数
df["成交量"] = pd.to_numeric(df["成交量"], errors="coerce")errors="coerce" 的意思是:如果转换失败(比如数据中混入了文字),就设为NaN而不是报错。
保存数据到本地
每次从网络获取数据都有时间成本,而且频繁请求可能被限制。养成保存到本地的习惯。
保存为CSV
python
# 保存单只股票数据
df.to_csv("maotai_daily.csv", encoding="utf-8-sig")
# 读取已保存的数据
df = pd.read_csv("maotai_daily.csv", index_col=0, parse_dates=True)encoding="utf-8-sig" 确保中文在Excel中正常显示。index_col=0 表示第一列作为索引,parse_dates=True 自动解析日期。
批量保存股票池数据
python
import os
# 创建数据目录
os.makedirs("stock_data", exist_ok=True)
# 批量保存
for code, df in stock_data.items():
filename = f"stock_data/{code}.csv"
df.to_csv(filename, encoding="utf-8-sig")
print(f"已保存: {filename}")保存为更高效的格式
CSV是通用格式,但读写速度较慢,特别是数据量大时。可以考虑使用parquet格式:
python
# 保存为parquet格式(读写更快,文件更小)
df.to_parquet("maotai_daily.parquet")
# 读取parquet文件
df = pd.read_parquet("maotai_daily.parquet")使用parquet需要安装 pyarrow 或 fastparquet:
bash
pip install pyarrow完整示例:获取并处理一只股票的数据
以下是一个完整的示例,从获取数据到处理再到保存,一步到位:
python
import akshare as ak
import pandas as pd
import os
import time
def get_and_process_stock(symbol, name, start_date, end_date):
"""获取并处理单只股票的数据"""
print(f"正在获取 {name}({symbol}) 的数据...")
# 1. 获取数据
df = ak.stock_zh_a_hist(
symbol=symbol,
period="daily",
start_date=start_date,
end_date=end_date,
adjust="qfq"
)
# 2. 基本处理
df["日期"] = pd.to_datetime(df["日期"])
df = df.set_index("日期")
# 3. 确保数值类型正确
for col in ["开盘", "收盘", "最高", "最低", "成交量", "成交额"]:
if col in df.columns:
df[col] = pd.to_numeric(df[col], errors="coerce")
# 4. 处理缺失值
df = df.dropna(subset=["收盘"]) # 收盘价为空的行直接删除
# 5. 计算常用指标
df["MA5"] = df["收盘"].rolling(window=5).mean().round(2)
df["MA20"] = df["收盘"].rolling(window=20).mean().round(2)
df["涨跌幅"] = (df["收盘"].pct_change() * 100).round(2)
df["成交量MA5"] = df["成交量"].rolling(window=5).mean().round(0)
print(f" 获取完成,共 {len(df)} 个交易日")
return df
# 定义股票池
stock_pool = {
"600519": "贵州茅台",
"000858": "五粮液",
"601318": "中国平安",
}
# 创建保存目录
os.makedirs("stock_data", exist_ok=True)
# 批量获取、处理、保存
for code, name in stock_pool.items():
try:
df = get_and_process_stock(code, name, "20230101", "20241231")
filepath = f"stock_data/{code}.csv"
df.to_csv(filepath, encoding="utf-8-sig")
print(f" 已保存至 {filepath}\n")
time.sleep(0.5)
except Exception as e:
print(f" 处理 {name}({code}) 时出错: {e}\n")
print("全部完成!")运行后,你会在 stock_data 目录下看到三个CSV文件,每个文件包含对应股票两年的日线行情数据和计算好的技术指标。
常见问题与解决
Q:获取数据时报错"ConnectionError"
网络连接问题。检查网络是否正常,或者稍后再试。如果是公司网络,可能需要配置代理。
Q:获取数据时报错"No data"
可能的原因:
- 股票代码写错了(检查是否多写或少写了数字)
- 日期范围内该股票尚未上市
- AKShare接口更新了,查看官方文档确认最新用法
Q:获取到的数据列名是英文
不同版本的AKShare可能返回中文或英文列名。如果需要统一,可以重命名:
python
# 重命名列
df = df.rename(columns={
"date": "日期",
"open": "开盘",
"close": "收盘",
"high": "最高",
"low": "最低",
"volume": "成交量",
})Q:数据太多,获取很慢
可以分批获取,每次只取需要的时间段。另外,获取后立即保存到本地,后续分析直接读本地文件,速度会快很多。
Q:AKShare接口变了怎么办
AKShare是社区维护的开源项目,接口偶尔会调整。遇到接口变化时:
- 查看AKShare官方文档:akshare.akfamily.xyz
- 在AKShare的GitHub Issues中搜索相关问题
- 更新到最新版本:
pip install akshare --upgrade
数据使用的注意事项
- 数据仅供学习研究:通过免费接口获取的数据可能存在延迟或错误,不构成任何投资建议。
- 注意数据版权:部分数据源有使用限制,大量商业使用前需确认授权。
- 免费接口有限制:AKShare等免费接口的数据质量和及时性不如付费数据源(如Wind),做实盘决策时需注意。
- 定期更新本地数据:保存到本地的数据会过时,建议定期(如每周)重新获取最新数据。
- 交叉验证:对关键数据,可以用多个数据源交叉验证,确保数据准确。
小结
- 使用AKShare可以免费获取A股的日线行情、周线月线、财务数据、指数数据等。
- 获取数据后要做基本检查:查看数据量、时间范围、缺失值。
- 常见数据处理操作:缺失值处理、日期对齐、类型转换、计算衍生指标。
- 养成保存到本地的习惯,避免重复获取。
- 数据是量化投资的基础,数据质量直接影响分析结果的可靠性。
下一步
现在你已经掌握了获取和处理股票数据的方法。有了数据基础,接下来就可以开始学习如何构建和回测量化策略了。
→ 返回量化投资目录