Skip to content

获取股票数据

数据是量化投资的原材料。没有数据,再好的策略也只是纸上谈兵。本章教你如何用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
沪深300sh000300
上证50sh000016
中证500sh000905
创业板指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需要安装 pyarrowfastparquet

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是社区维护的开源项目,接口偶尔会调整。遇到接口变化时:

  1. 查看AKShare官方文档:akshare.akfamily.xyz
  2. 在AKShare的GitHub Issues中搜索相关问题
  3. 更新到最新版本:pip install akshare --upgrade

数据使用的注意事项

  1. 数据仅供学习研究:通过免费接口获取的数据可能存在延迟或错误,不构成任何投资建议。
  2. 注意数据版权:部分数据源有使用限制,大量商业使用前需确认授权。
  3. 免费接口有限制:AKShare等免费接口的数据质量和及时性不如付费数据源(如Wind),做实盘决策时需注意。
  4. 定期更新本地数据:保存到本地的数据会过时,建议定期(如每周)重新获取最新数据。
  5. 交叉验证:对关键数据,可以用多个数据源交叉验证,确保数据准确。

小结

  • 使用AKShare可以免费获取A股的日线行情、周线月线、财务数据、指数数据等。
  • 获取数据后要做基本检查:查看数据量、时间范围、缺失值。
  • 常见数据处理操作:缺失值处理、日期对齐、类型转换、计算衍生指标。
  • 养成保存到本地的习惯,避免重复获取。
  • 数据是量化投资的基础,数据质量直接影响分析结果的可靠性。

下一步

现在你已经掌握了获取和处理股票数据的方法。有了数据基础,接下来就可以开始学习如何构建和回测量化策略了。

返回量化投资目录

仅供学习交流,不构成任何投资建议