Ashare - 完整学习教程

Ashare - 完整学习教程

教程级别: 从零到一 预计学习时间: 2-3 小时 前置知识: Python 基础(变量、函数、import)、pandas 基本操作(DataFrame 查看)、A 股市场基本概念(股票代码、K 线)

环境搭建指南

系统要求

  • 操作系统:Windows / macOS / Linux(任意)
  • Python 版本:3.6 及以上
  • 依赖项:pandas、requests(安装 Ashare 时自动安装)

安装步骤

方式一:通过 pip 安装(推荐)

# 使用 pip 安装 ashares 包
pip install ashares

# 如果下载速度慢,可以使用清华镜像源
pip install ashares -i https://pypi.tuna.tsinghua.edu.cn/simple

方式二:直接下载源码文件

# 克隆 GitHub 仓库
git clone https://github.com/mpquant/Ashare.git

# 进入目录
cd Ashare

# 将 Ashare.py 复制到你的项目目录即可使用
cp Ashare.py /你的项目路径/

基于官方 README(master 分支,2025-12-24)

验证安装

# 在 Python 交互环境中验证
from Ashare import *
import pandas as pd

# 验证 get_price 函数可用
print(get_price.__module__)
Ashare

如果输出 Ashare 说明安装成功。如果报错 ModuleNotFoundError,请检查安装方式是否正确。


第一部分:入门篇

1.1 第一次获取股票数据

概念讲解:

Ashare 的核心设计理念是"一个函数解决所有问题"。整个库只暴露一个函数 get_price(),它接受股票代码和查询参数,返回标准的 pandas DataFrame 格式数据。

get_price() 函数签名如下:

get_price(code, end_date='', count=10, frequency='1d', fields=[])

参数说明: - code:股票代码(必填),支持多种格式(详见 1.2 节) - end_date:结束日期(可选),默认为空表示取最新数据 - count:获取的数据条数(可选),默认为 10 - frequency:数据周期(可选),默认为 '1d'(日线) - fields:字段筛选(可选),当前版本未使用该参数

返回值是一个 pandas DataFrame,包含以下列: - open:开盘价 - close:收盘价 - high:最高价 - low:最低价 - volume:成交量

行索引为日期时间(DatetimeIndex)。

代码示例:

# 基于官方 README(master 分支,2025-12-24)

from Ashare import *

# 获取上证指数最近 10 个交易日的日线数据
df = get_price('sh000001', frequency='1d', count=10)
print('上证指数最近 10 日行情:')
print(df)
print()

# 查看数据类型
print('数据类型:')
print(df.dtypes)
print()

# 查看索引类型
print('索引类型:', type(df.index))

执行结果:

上证指数最近 10 日行情:
              open    close     high      low        volume
2025-12-11  3432.56  3450.12  3465.78  3428.90  3.245670e+08
2025-12-12  3451.23  3445.67  3458.90  3440.12  2.987650e+08
2025-12-15  3446.78  3468.34  3475.56  3442.23  3.123450e+08
2025-12-16  3467.89  3456.78  3472.34  3450.67  2.876540e+08
2025-12-17  3455.67  3478.90  3485.12  3452.34  3.345670e+08
2025-12-18  3480.12  3462.45  3488.56  3458.78  3.098760e+08
2025-12-19  3460.34  3485.67  3492.34  3456.12  3.234560e+08
2025-12-22  3486.78  3490.12  3498.56  3482.34  3.156780e+08
2025-12-23  3488.90  3475.67  3495.23  3470.89  2.945670e+08
2025-12-24  3476.23  3482.56  3488.90  3472.12  3.012340e+08

数据类型:
open      float64
close     float64
high      float64
low       float64
volume    float64
dtype: object

索引类型: <class 'pandas.core.indexes.datetimes.DatetimeIndex'>

注:以上输出为模拟结果,实际数据取决于查询时的市场行情。基于官方 README 及源码(master 分支,2025-12-24)

练习题: 1. 获取贵州茅台(代码 sh600519)最近 20 个交易日的日线数据,并打印出来。 2. 获取平安银行(代码 sz000001)最近 5 个交易日的日线数据,并查看收盘价的最大值。


1.2 股票代码格式

概念讲解:

中国 A 股市场的股票代码由 6 位数字组成,但在不同的量化平台中,代码格式有所不同。Ashare 支持以下两种主流格式:

格式类型 示例 适用平台
通达信格式 sh600519sz000001 通达信、东方财富等
同花顺/聚宽格式 600519.XSHG000001.XSHE 同花顺、聚宽(JoinQuant)

格式规则: - 上海证券交易所:前缀 sh 或后缀 .XSHG,代码以 6 开头(如 600519 贵州茅台) - 深圳证券交易所:前缀 sz 或后缀 .XSHE,代码以 0 或 3 开头(如 000001 平安银行、300750 宁德时代)

Ashare 在内部自动完成格式转换,用户无需手动处理。转换逻辑: - 600519.XSHG → 去掉 .XSHG600519 → 加上 shsh600519 - 000001.XSHE → 去掉 .XSHE000001 → 加上 szsz000001 - sh600519 → 保持不变

代码示例:

# 基于源码 Ashare.py(master 分支,2025-12-24)

from Ashare import *

# 三种格式获取同一只股票(贵州茅台)的数据,结果完全一致

# 格式一:通达信格式(推荐,最简洁)
df1 = get_price('sh600519', frequency='1d', count=5)
print('通达信格式获取结果:')
print(df1)
print()

# 格式二:同花顺/聚宽格式
df2 = get_price('600519.XSHG', frequency='1d', count=5)
print('同花顺/聚宽格式获取结果:')
print(df2)
print()

# 验证两种格式返回的数据是否一致
print('两种格式的收盘价是否一致:', df1['close'].equals(df2['close']))

执行结果:

通达信格式获取结果:
              open     close      high       low       volume
2025-12-18  1520.00  1535.56  1540.23  1515.67  1.234567e+07
2025-12-19  1536.78  1542.34  1548.90  1530.12  1.345678e+07
2025-12-22  1543.45  1538.90  1550.23  1535.67  1.456789e+07
2025-12-23  1539.12  1545.67  1552.34  1536.78  1.234567e+07
2025-12-24  1546.23  1550.12  1556.78  1542.34  1.345678e+07

同花顺/聚宽格式获取结果:
              open     close      high       low       volume
2025-12-18  1520.00  1535.56  1540.23  1515.67  1.234567e+07
2025-12-19  1536.78  1542.34  1548.90  1530.12  1.345678e+07
2025-12-22  1543.45  1538.90  1550.23  1535.67  1.456789e+07
2025-12-23  1539.12  1545.67  1552.34  1536.78  1.234567e+07
2025-12-24  1546.23  1550.12  1556.78  1542.34  1.345678e+07

两种格式的收盘价是否一致: True

注:以上输出为模拟结果。基于源码 Ashare.py(master 分支,2025-12-24)

练习题: 1. 分别使用通达信格式和聚宽格式获取宁德时代(300750)最近 10 个交易日的日线数据。 2. 获取上证指数(sh000001)和深证成指(sz399001)最近 5 个交易日的日线数据,对比两个指数的涨跌情况。


1.3 多周期行情数据

概念讲解:

Ashare 支持 8 种数据周期,覆盖了量化分析中最常用的时间维度。通过 frequency 参数指定周期:

参数值 周期 数据源 典型用途
'1d' 日线 新浪(主)+ 腾讯(备) 中长期趋势分析
'1w' 周线 新浪(主)+ 腾讯(备) 中期趋势确认
'1M' 月线 新浪(主)+ 腾讯(备) 长期趋势判断
'60m' 60 分钟线 新浪(主)+ 腾讯(备) 日内波段分析
'30m' 30 分钟线 新浪(主)+ 腾讯(备) 短线交易参考
'15m' 15 分钟线 新浪(主)+ 腾讯(备) 短线交易参考
'5m' 5 分钟线 新浪(主)+ 腾讯(备) 日内策略
'1m' 1 分钟线 仅腾讯 超短线策略

注意:1 分钟线仅由腾讯数据源提供,其他周期支持新浪+腾讯双数据源热备。

代码示例:

# 基于官方 README 及源码(master 分支,2025-12-24)

from Ashare import *

# 日线数据(默认周期)
df_day = get_price('sh600519', frequency='1d', count=10)
print('=== 日线数据(最近 10 日)===')
print(df_day)
print()

# 周线数据
df_week = get_price('sh600519', frequency='1w', count=5)
print('=== 周线数据(最近 5 周)===')
print(df_week)
print()

# 月线数据
df_month = get_price('sh600519', frequency='1M', count=6)
print('=== 月线数据(最近 6 个月)===')
print(df_month)

执行结果:

=== 日线数据(最近 10 日)===
              open     close      high       low       volume
2025-12-15  1532.45  1540.23  1545.67  1528.90  1.234567e+07
...(共 10 行)

=== 周线数据(最近 5 周)===
              open     close      high       low       volume
2025-11-21  1500.00  1525.67  1530.45  1495.23  5.678900e+07
...(共 5 行)

=== 月线数据(最近 6 个月)===
              open     close      high       low       volume
2025-07  1450.23  1520.56  1535.78  1445.12  2.345678e+08
...(共 6 行)

注:以上输出为模拟结果,实际数据取决于查询时的市场行情。基于官方 README 及源码(master 分支,2025-12-24)

练习题: 1. 获取比亚迪(sz002594)的日线、周线、月线数据各 10 条,观察不同周期下数据的时间跨度。 2. 获取某只股票的 5 分钟线和 60 分钟线数据,对比不同分钟线的精度差异。


第二部分:进阶篇

2.1 使用 end_date 参数获取历史数据

概念讲解:

在入门篇中,我们获取的都是最新数据(end_date 为空)。但在实际量化分析中,经常需要获取特定时间段的历史数据,例如回测某个策略在 2024 年的表现。

end_date 参数的工作原理: 1. 对于日线/周线/月线:Ashare 会计算 end_date 到当前日期的天数差,额外增加请求数据量,然后截取 end_date 之前的 count 条数据 2. end_date 可以是字符串格式(如 '2025-12-20')或 datetime.date 对象 3. 如果 end_date 正好是当天,Ashare 会将其置空,获取最新数据

代码示例:

# 基于源码 Ashare.py(master 分支,2025-12-24)

from Ashare import *

# 获取截至 2025-12-20 的最近 10 个交易日日线数据
df = get_price('sh600519', end_date='2025-12-20', count=10, frequency='1d')
print('截至 2025-12-20 的最近 10 日行情:')
print(df)
print()

# 使用 datetime 对象作为 end_date
import datetime
end = datetime.date(2025, 11, 30)
df2 = get_price('sh600519', end_date=end, count=5, frequency='1d')
print('截至 2025-11-30 的最近 5 日行情:')
print(df2)

执行结果:

截至 2025-12-20 的最近 10 日行情:
              open     close      high       low       volume
2025-12-05  1510.23  1520.45  1525.67  1505.89  1.123456e+07
2025-12-08  1521.34  1518.90  1528.45  1515.23  1.234567e+07
2025-12-09  1519.56  1525.78  1530.12  1516.45  1.345678e+07
2025-12-10  1526.23  1532.45  1538.90  1522.34  1.456789e+07
2025-12-11  1533.12  1528.67  1535.45  1525.12  1.234567e+07
2025-12-12  1527.89  1535.23  1540.56  1524.78  1.345678e+07
2025-12-15  1536.45  1542.67  1548.90  1532.34  1.456789e+07
2025-12-16  1543.12  1538.90  1545.67  1535.23  1.234567e+07
2025-12-17  1539.56  1545.23  1550.78  1536.45  1.345678e+07
2025-12-18  1546.23  1550.12  1556.78  1542.34  1.234567e+07

截至 2025-11-30 的最近 5 日行情:
              open     close      high       low       volume
2025-11-24  1490.12  1498.56  1505.23  1485.67  1.123456e+07
...(共 5 行)

注:以上输出为模拟结果。基于源码 Ashare.py(master 分支,2025-12-24)

注意事项: - end_date 参数仅对日线('1d')、周线('1w')、月线('1M')有效。分钟线('5m' 等)的 end_date 参数会被传递但不影响新浪接口的返回结果。 - 如果 end_date 指定的日期不是交易日(如周末、节假日),Ashare 会返回该日期之前最近一个交易日的数据。 - 获取很早的历史数据时,由于新浪 API 的 datalen 参数限制,count 值不宜过大(建议不超过 1000)。

练习题: 1. 获取贵州茅台 2025 年 6 月到 2025 年 12 月的月线数据(end_date='2025-12-31', count=7, frequency='1M')。 2. 获取某只股票在 2025-10-08 到 2025-10-31 之间的日线数据。


2.2 分钟线数据获取

概念讲解:

分钟线数据是量化短线策略的重要基础。Ashare 支持 5 种分钟周期:1 分钟、5 分钟、15 分钟、30 分钟和 60 分钟。

分钟线的内部机制与日线不同: - 1 分钟线('1m'):仅由腾讯数据源提供,通过 get_price_min_tx() 函数获取 - 其他分钟线('5m'/'15m'/'30m'/'60m'):主力为新浪接口,备用为腾讯接口

分钟线数据有一个特殊处理:腾讯分钟线返回的最后一根 K 线的收盘价会通过实时行情(qt)接口进行修正,确保最新价格准确。

代码示例:

# 基于官方 README 及源码(master 分支,2025-12-24)

from Ashare import *

# 获取 5 分钟线数据(最近 20 条)
df_5m = get_price('sh600519', frequency='5m', count=20)
print('=== 5 分钟线数据(最近 20 条)===')
print(df_5m)
print()

# 获取 60 分钟线数据(最近 10 条)
df_60m = get_price('sh600519', frequency='60m', count=10)
print('=== 60 分钟线数据(最近 10 条)===')
print(df_60m)
print()

# 获取 1 分钟线数据(最近 10 条,仅腾讯支持)
df_1m = get_price('sh600519', frequency='1m', count=10)
print('=== 1 分钟线数据(最近 10 条)===')
print(df_1m)

执行结果:

=== 5 分钟线数据(最近 20 条)===
                        open     close      high       low   volume
2025-12-24 14:30:00  1548.23  1550.45  1552.67  1546.12  23456.0
2025-12-24 14:35:00  1550.67  1548.90  1553.45  1547.23  19876.0
...(共 20 行)

=== 60 分钟线数据(最近 10 条)===
                        open     close      high       low    volume
2025-12-23 10:30:00  1540.12  1545.67  1548.90  1538.45  123456.0
...(共 10 行)

=== 1 分钟线数据(最近 10 条)===
                        open     close      high       low  volume
2025-12-24 14:51:00  1549.23  1550.12  1550.56  1548.89  3456.0
...(共 10 行)

注:以上输出为模拟结果。分钟线数据仅在交易时段有效。基于官方 README 及源码(master 分支,2025-12-24)

注意事项: - 分钟线数据仅在交易时段内有效。非交易时段调用可能返回上一个交易日或不完整的数据。 - 分钟线的 count 参数建议不超过 500,过大的值可能导致请求超时。 - 1 分钟线仅由腾讯提供,如果腾讯接口异常,1 分钟线将无法获取(无备用数据源)。 - 分钟线的时间索引包含时分秒信息(如 2025-12-24 14:30:00),日线的时间索引仅包含日期(如 2025-12-24)。

练习题: 1. 分别获取贵州茅台的 5 分钟线和 60 分钟线各 30 条,对比两种周期下同一时间段内的 K 线数量。 2. 尝试获取 1 分钟线数据,验证时间索引的格式是否包含时分秒。


2.3 DataFrame 数据处理技巧

概念讲解:

Ashare 返回的是标准的 pandas DataFrame,因此可以直接使用 pandas 的全部功能进行数据处理和分析。本节介绍几个常用的数据处理模式。

代码示例:

# 基于官方 README 及源码(master 分支,2025-12-24)

from Ashare import *

# 获取 30 日日线数据
df = get_price('sh600519', frequency='1d', count=30)

# 1. 计算涨跌幅
df['pct_change'] = df['close'].pct_change() * 100
print('=== 最近 5 日涨跌幅 ===')
print(df[['close', 'pct_change']].tail(5))
print()

# 2. 计算移动平均线
df['ma5'] = df['close'].rolling(window=5).mean()
df['ma10'] = df['close'].rolling(window=10).mean()
print('=== 最近 5 日收盘价与均线 ===')
print(df[['close', 'ma5', 'ma10']].tail(5))
print()

# 3. 计算波动率(20 日标准差)
df['volatility'] = df['pct_change'].rolling(window=20).std()
print('=== 最近波动率 ===')
print(f"20 日波动率: {df['volatility'].iloc[-1]:.4f}%")
print()

# 4. 筛选涨幅超过 2% 的交易日
big_up_days = df[df['pct_change'] > 2]
print(f'涨幅超过 2% 的交易日数量: {len(big_up_days)}')

执行结果:

=== 最近 5 日涨跌幅 ===
              close  pct_change
2025-12-18  1550.12     0.32
2025-12-19  1542.34    -0.50
2025-12-22  1538.90    -0.22
2025-12-23  1545.67     0.44
2025-12-24  1550.12     0.29

=== 最近 5 日收盘价与均线 ===
              close      ma5     ma10
2025-12-18  1550.12  1542.67  1535.23
2025-12-19  1542.34  1543.45  1536.78
2025-12-22  1538.90  1543.12  1537.90
2025-12-23  1545.67  1543.78  1539.12
2025-12-24  1550.12  1545.43  1540.56

=== 最近波动率 ===
20 日波动率: 1.2345%

涨幅超过 2% 的交易日数量: 3

注:以上输出为模拟结果。基于 pandas 标准用法及 Ashare 返回格式

练习题: 1. 获取某只股票 60 日日线数据,计算 20 日均线和 60 日均线,找出"金叉"(短期均线从下方穿越长期均线)的日期。 2. 计算某只股票最近 30 日的日均成交量和成交量的标准差。


第三部分:高级篇

3.1 双数据源热备机制详解

概念讲解:

Ashare 的核心创新是内置了新浪财经和腾讯股票两个数据源,通过 try-except 机制实现自动故障切换。理解这个机制有助于排查数据获取问题和选择合适的参数。

切换策略总结:

frequency 主力数据源 备用数据源 切换机制
'1d'/'1w'/'1M' 新浪 get_price_sina() 腾讯 get_price_day_tx() try-except 自动切换
'5m'/'15m'/'30m'/'60m' 新浪 get_price_sina() 腾讯 get_price_min_tx() try-except 自动切换
'1m' 腾讯 get_price_min_tx() 无备用

用户无法控制使用哪个数据源,也无法在切换时得到通知。这意味着在极端情况下,同一参数的两次调用可能返回来自不同数据源的数据。

代码示例:

# 基于源码 Ashare.py(master 分支,2025-12-24)
# 演示如何手动模拟双数据源行为

from Ashare import *

# 日线数据:先尝试新浪,失败自动切换腾讯
df = get_price('sh600519', frequency='1d', count=10)
print('日线数据获取成功(来源:新浪或腾讯):')
print(df.head(3))
print()

# 1 分钟线:仅腾讯支持
df_min = get_price('sh600519', frequency='1m', count=10)
print('1 分钟线数据获取成功(来源:仅腾讯):')
print(df_min.head(3))

执行结果:

日线数据获取成功(来源:新浪或腾讯):
              open     close      high       low       volume
2025-12-18  1546.23  1550.12  1556.78  1542.34  1.345678e+07
2025-12-19  1548.56  1542.34  1552.12  1540.23  1.234567e+07
2025-12-22  1543.12  1538.90  1548.56  1536.45  1.456789e+07

1 分钟线数据获取成功(来源:仅腾讯):
                        open     close      high       low  volume
2025-12-24 14:51:00  1549.23  1550.12  1550.56  1548.89  3456.0
2025-12-24 14:52:00  1550.34  1549.78  1551.12  1549.23  3123.0
2025-12-24 14:53:00  1549.56  1550.45  1550.89  1549.12  2890.0

注:以上输出为模拟结果。基于源码 Ashare.py(master 分支,2025-12-24)

注意事项: - 用户无法感知当前使用的是哪个数据源。如果需要确认数据来源,可以通过检查 DataFrame 的列顺序来推断(新浪返回的列顺序是 day/open/high/low/close/volume,腾讯日线是 time/open/close/high/low/volume)。 - 双数据源返回的数据可能存在微小差异(如复权方式不同),在对精度要求高的场景下需要注意。 - 新浪接口使用 HTTP(非 HTTPS),数据传输未加密。腾讯部分接口也是 HTTP。


3.2 批量获取多只股票数据

概念讲解:

Ashare 每次调用只获取一只股票的数据。在实际量化分析中,通常需要同时获取多只股票的数据。本节介绍如何通过循环批量获取,以及相关的注意事项。

代码示例:

# 基于 Ashare API 设计及 Python 标准库

from Ashare import *
import time

# 定义要获取的股票列表(通达信格式)
stocks = {
    'sh600519': '贵州茅台',
    'sz000001': '平安银行',
    'sz002594': '比亚迪',
    'sh601318': '中国平安',
    'sz300750': '宁德时代'
}

# 批量获取日线数据
results = {}
for code, name in stocks.items():
    try:
        df = get_price(code, frequency='1d', count=30)
        results[code] = df
        # 计算最近 30 日涨跌幅
        latest = df['close'].iloc[-1]
        earliest = df['close'].iloc[0]
        change_pct = (latest - earliest) / earliest * 100
        print(f'{name}({code}):30 日涨跌幅 {change_pct:+.2f}%')
        # 每次请求后短暂等待,避免触发上游 API 的频率限制
        time.sleep(0.5)
    except Exception as e:
        print(f'{name}({code})获取失败: {e}')

print()

# 对比所有股票的最近收盘价
print('=== 各股票最新收盘价 ===')
for code, name in stocks.items():
    if code in results:
        print(f'{name}({code}): {results[code]["close"].iloc[-1]:.2f}')

执行结果:

贵州茅台(sh600519):30 日涨跌幅 +3.45%
平安银行(sz000001):30 日涨跌幅 -1.23%
比亚迪(sz002594):30 日涨跌幅 +5.67%
中国平安(sh601318):30 日涨跌幅 +2.34%
宁德时代(sz300750):30 日涨跌幅 -0.89%

=== 各股票最新收盘价 ===
贵州茅台(sh600519): 1550.12
平安银行(sz000001): 15.23
比亚迪(sz002594): 265.45
中国平安(sh601318): 52.67
宁德时代(sz300750): 198.34

注:以上输出为模拟结果。基于 Ashare API 设计

注意事项: - 必须添加 time.sleep():Ashare 底层访问的是公开 API,连续高频请求可能触发频率限制或 IP 封禁。建议每次请求间隔至少 0.3 秒。 - 异常处理不可省略:网络波动、上游接口变更等都可能导致单只股票获取失败。使用 try-except 确保一只股票失败不会中断整个批量任务。 - 不建议单次批量超过 50 只股票:过多的请求可能导致 IP 被临时封禁。如需获取全市场数据,建议使用 Tushare 或 AKShare。


3.3 数据质量验证与缓存

概念讲解:

Ashare 返回的数据未经过完整性校验。在实际使用中,建议对返回数据做基本的质量验证,并对频繁使用的数据添加缓存机制,避免重复请求。

代码示例:

# 基于 Ashare API 设计及 Python 标准库

from Ashare import *
import pandas as pd
import time

def get_price_safe(code, end_date='', count=10, frequency='1d', max_retries=3):
    """
    带重试和数据验证的安全获取函数。

    参数:
        code: 股票代码
        end_date: 结束日期
        count: 数据条数
        frequency: 数据周期
        max_retries: 最大重试次数
    """
    for attempt in range(max_retries):
        try:
            df = get_price(code, end_date=end_date, count=count, frequency=frequency)

            # 验证 1:检查是否为空
            if df.empty:
                print(f'警告:{code} 返回空数据,重试 {attempt + 1}/{max_retries}')
                time.sleep(1)
                continue

            # 验证 2:检查必需列是否存在
            required_cols = ['open', 'close', 'high', 'low', 'volume']
            missing_cols = [col for col in required_cols if col not in df.columns]
            if missing_cols:
                print(f'警告:{code} 缺少列 {missing_cols}')
                continue

            # 验证 3:检查缺失值
            if df[required_cols].isnull().any().any():
                null_count = df[required_cols].isnull().sum().sum()
                print(f'警告:{code} 存在 {null_count} 个缺失值')

            # 验证 4:检查价格合理性(收盘价应在最高价和最低价之间)
            price_invalid = (df['close'] > df['high']) | (df['close'] < df['low'])
            if price_invalid.any():
                print(f'警告:{code} 存在 {price_invalid.sum()} 条价格异常数据')

            return df

        except Exception as e:
            print(f'错误:{code} 第 {attempt + 1} 次请求失败: {e}')
            if attempt < max_retries - 1:
                time.sleep(2)

    print(f'错误:{code} 获取失败,已达最大重试次数 {max_retries}')
    return pd.DataFrame()


# 简单内存缓存
_cache = {}

def get_price_cached(code, end_date='', count=10, frequency='1d'):
    """
    带缓存的数据获取函数。同一参数组合只请求一次。
    注意:缓存是内存级别的,进程退出后失效。
    """
    cache_key = f'{code}_{end_date}_{count}_{frequency}'
    if cache_key in _cache:
        return _cache[cache_key]

    df = get_price_safe(code, end_date=end_date, count=count, frequency=frequency)
    if not df.empty:
        _cache[cache_key] = df
    return df


# 使用示例
df = get_price_cached('sh600519', count=30, frequency='1d')
print(f'获取到 {len(df)} 条数据')
print(f'日期范围:{df.index[0]} 至 {df.index[-1]}')

执行结果:

获取到 30 条数据
日期范围:2025-11-12 00:00:00 至 2025-12-24 00:00:00

注:以上输出为模拟结果。基于 Ashare API 设计

注意事项: - 重试间隔应逐步增加:生产环境中建议使用指数退避策略(如 1s、2s、4s),避免在接口故障时造成雪崩效应。 - 缓存的有效期:股票数据是实时变化的,日线数据的缓存可以保留到当日收盘后,分钟线数据缓存有效期应很短(如 5 分钟)。 - 价格验证逻辑close <= high and close >= low 是最基本的合理性检查,更严格的验证可以包括成交量是否为正、涨跌停判断等。


第四部分:实战项目

项目需求

构建一个"多股票技术分析面板"脚本,综合运用以下知识点: 1. 多周期数据获取(知识点 1.3):同时获取日线和周线数据 2. 多股票批量获取(知识点 3.2):获取多只股票的数据 3. DataFrame 数据处理(知识点 2.3):计算技术指标(均线、涨跌幅、波动率) 4. 数据质量验证(知识点 3.3):对获取的数据进行基本验证

功能要求: - 输入一组股票代码,自动获取日线数据 - 计算 5 日均线、10 日均线、涨跌幅和波动率 - 生成汇总报告,按涨跌幅排序 - 对异常数据进行标记

项目设计

stock_analysis_panel.py
│
├── get_stock_data()       # 批量获取股票数据
├── calc_indicators()      # 计算技术指标
├── validate_data()        # 数据验证
├── generate_report()      # 生成汇总报告
└── main()                 # 主函数

完整实现代码

# 基于 Ashare API 设计、pandas 标准用法及源码(master 分支,2025-12-24)

from Ashare import *
import pandas as pd
import time

# ===== 配置区域 =====
# 要分析的股票列表(通达信格式)
STOCKS = {
    'sh600519': '贵州茅台',
    'sz000001': '平安银行',
    'sz002594': '比亚迪',
    'sh601318': '中国平安',
    'sz300750': '宁德时代',
    'sh600036': '招商银行',
    'sz000858': '五粮液',
    'sh601012': '隆基绿能',
}

# 分析参数
DATA_COUNT = 30         # 获取的交易日数量
REQUEST_INTERVAL = 0.5  # 请求间隔(秒)


def validate_data(df, code):
    """
    数据验证函数。
    检查数据的完整性和合理性。

    参数:
        df: pandas DataFrame,Ashare 返回的行情数据
        code: str,股票代码

    返回:
        list: 异常信息列表,空列表表示数据正常
    """
    issues = []

    # 检查是否为空
    if df.empty:
        issues.append(f'{code}: 返回数据为空')
        return issues

    # 检查数据行数是否足够
    if len(df) < 10:
        issues.append(f'{code}: 数据行数不足(仅 {len(df)} 行)')

    # 检查缺失值
    required_cols = ['open', 'close', 'high', 'low', 'volume']
    null_count = df[required_cols].isnull().sum().sum()
    if null_count > 0:
        issues.append(f'{code}: 存在 {null_count} 个缺失值')

    # 检查价格合理性
    price_invalid = (df['close'] > df['high']) | (df['close'] < df['low'])
    if price_invalid.any():
        issues.append(f'{code}: 存在 {price_invalid.sum()} 条价格异常数据')

    # 检查成交量是否为负
    if (df['volume'] < 0).any():
        issues.append(f'{code}: 存在负成交量')

    return issues


def calc_indicators(df):
    """
    计算技术指标。
    包括:5 日均线、10 日均线、涨跌幅、20 日波动率。

    参数:
        df: pandas DataFrame,Ashare 返回的行情数据

    返回:
        pandas DataFrame:添加了技术指标列的数据
    """
    df = df.copy()

    # 计算涨跌幅(百分比)
    df['pct_change'] = df['close'].pct_change() * 100

    # 计算移动平均线
    df['ma5'] = df['close'].rolling(window=5).mean()
    df['ma10'] = df['close'].rolling(window=10).mean()

    # 计算 20 日波动率(涨跌幅的标准差)
    df['volatility'] = df['pct_change'].rolling(window=20).std()

    return df


def get_stock_data(stocks, count=30):
    """
    批量获取股票数据并进行验证。

    参数:
        stocks: dict,股票代码到名称的映射
        count: int,获取的交易日数量

    返回:
        dict: 股票代码到(DataFrame, 验证信息)的映射
    """
    results = {}

    for code, name in stocks.items():
        try:
            # 获取日线数据
            df = get_price(code, frequency='1d', count=count)

            # 数据验证
            issues = validate_data(df, code)
            if issues:
                for issue in issues:
                    print(f'  [警告] {issue}')

            # 计算技术指标
            if not df.empty:
                df = calc_indicators(df)

            results[code] = {
                'name': name,
                'data': df,
                'issues': issues
            }

            print(f'  {name}({code})获取成功,{len(df)} 条数据')

        except Exception as e:
            print(f'  {name}({code})获取失败: {e}')
            results[code] = {
                'name': name,
                'data': pd.DataFrame(),
                'issues': [f'{code}: 获取失败 - {e}']
            }

        # 请求间隔,避免触发频率限制
        time.sleep(REQUEST_INTERVAL)

    return results


def generate_report(results):
    """
    生成汇总分析报告。

    参数:
        results: dict,get_stock_data() 的返回值

    返回:
        pandas DataFrame:汇总报告表
    """
    report_data = []

    for code, info in results.items():
        df = info['data']
        name = info['name']

        if df.empty:
            report_data.append({
                '代码': code,
                '名称': name,
                '最新价': None,
                '涨跌幅(%)': None,
                'MA5': None,
                'MA10': None,
                '20日波动率(%)': None,
                '30日涨跌幅(%)': None,
                '日均成交量': None,
                '状态': '获取失败'
            })
            continue

        latest = df.iloc[-1]
        first = df.iloc[0]

        # 判断均线趋势
        ma_status = ''
        if pd.notna(latest['ma5']) and pd.notna(latest['ma10']):
            if latest['ma5'] > latest['ma10']:
                ma_status = '多头'
            else:
                ma_status = '空头'

        # 计算 30 日总涨跌幅
        total_change = (latest['close'] - first['close']) / first['close'] * 100

        report_data.append({
            '代码': code,
            '名称': name,
            '最新价': round(latest['close'], 2),
            '涨跌幅(%)': round(latest.get('pct_change', 0), 2) if pd.notna(latest.get('pct_change')) else None,
            'MA5': round(latest['ma5'], 2) if pd.notna(latest['ma5']) else None,
            'MA10': round(latest['ma10'], 2) if pd.notna(latest['ma10']) else None,
            '20日波动率(%)': round(latest['volatility'], 2) if pd.notna(latest['volatility']) else None,
            '30日涨跌幅(%)': round(total_change, 2),
            '日均成交量': round(df['volume'].mean(), 0),
            '状态': ma_status
        })

    report_df = pd.DataFrame(report_data)
    # 按 30 日涨跌幅降序排列
    report_df = report_df.sort_values('30日涨跌幅(%)', ascending=False)
    return report_df


def main():
    """
    主函数:执行完整的多股票技术分析流程。
    """
    print('=' * 60)
    print('多股票技术分析面板')
    print('=' * 60)
    print()

    # 第一步:批量获取数据
    print('[1/3] 正在获取股票数据...')
    results = get_stock_data(STOCKS, count=DATA_COUNT)
    print()

    # 第二步:生成报告
    print('[2/3] 正在生成分析报告...')
    report = generate_report(results)
    print()

    # 第三步:输出结果
    print('[3/3] 分析结果:')
    print('=' * 60)
    print(report.to_string(index=False))
    print('=' * 60)

    # 输出统计摘要
    valid_report = report.dropna(subset=['30日涨跌幅(%)'])
    if not valid_report.empty:
        print()
        print('=== 统计摘要 ===')
        print(f'分析股票数量:{len(report)}')
        print(f'获取成功:{len(valid_report)}')
        print(f'获取失败:{len(report) - len(valid_report)}')

        best = valid_report.iloc[0]
        worst = valid_report.iloc[-1]
        print(f'30 日涨幅最大:{best["名称"]}({best["30日涨跌幅(%)"]:+.2f}%)')
        print(f'30 日跌幅最大:{worst["名称"]}({worst["30日涨跌幅(%)"]:+.2f}%)')


if __name__ == '__main__':
    main()

代码解析

知识点运用说明:

  1. 多周期数据获取(1.3 节):get_price(code, frequency='1d', count=count) 使用日线周期获取数据。frequency='1d' 是默认值,确保获取的是日线数据。

  2. 多股票批量获取(3.2 节):get_stock_data() 函数通过循环遍历股票列表,每次调用 get_price() 获取一只股票的数据,并在每次请求后使用 time.sleep(REQUEST_INTERVAL) 控制请求频率。

  3. DataFrame 数据处理(2.3 节):calc_indicators() 函数使用 pandas 的 pct_change() 计算涨跌幅、rolling().mean() 计算移动平均线、rolling().std() 计算波动率。

  4. 数据质量验证(3.3 节):validate_data() 函数检查数据是否为空、行数是否足够、是否存在缺失值、价格是否合理(收盘价在最高价和最低价之间)、成交量是否为负。

扩展挑战

  1. 增加更多技术指标:在 calc_indicators() 中添加 MACD(指数平滑移动平均线)、RSI(相对强弱指标)、布林带等常用技术指标的计算。提示:可以使用 mpquant 开源的 MyTT 库(https://github.com/mpquant/MyTT),它与 Ashare 出自同一作者。

  2. 增加数据导出功能:将分析报告导出为 CSV 文件或 Excel 文件,方便后续查看和分享。提示:使用 df.to_csv()df.to_excel() 方法。

  3. 增加可视化功能:使用 matplotlib 或 plotly 绘制 K 线图和均线图。提示:搜索结果中掘金文章的 plot_draw_kline 函数是一个参考起点。


第五部分:常见问题与排查指南

常见错误及解决方案

错误信息 原因 解决方案
ModuleNotFoundError: No module named 'Ashare' 未安装 ashares 包,或安装了错误的包名 执行 pip install ashares(注意包名是 ashares,不是 ashare)
JSONDecodeError: Expecting value 新浪或腾讯 API 返回了非 JSON 内容(如 HTML 错误页面) 检查网络连接;如果持续出现可能是 API 接口变更,关注 GitHub Issues
KeyError: 'qfqday' 腾讯 API 对指数和股票返回不同的字段名 这是 Ashare 内部已处理的问题。如果仍然出现,说明你使用的版本较旧,更新到最新版即可
Empty DataFrame 传入的股票代码格式不正确,或该股票已退市 确认股票代码格式正确(如 sh600519,不是 600519);检查股票是否仍在交易
ConnectionError 网络无法连接到新浪或腾讯的 API 服务器 检查网络连接;如果在国内使用代理,确认代理配置正确
返回的数据行数少于请求的 count count 超过了上游 API 的最大返回限制,或该股票上市时间不够长 减小 count 值;对于次新股,先查询上市日期再确定合理的 count
get_price('600519') 返回空数据 股票代码缺少交易所前缀 使用 sh600519(上海)或 sz000001(深圳)格式,或使用 600519.XSHG 格式
分钟线数据与实际行情不一致 腾讯分钟线返回的最后一根 K 线使用了实时行情修正 这是正常行为。Ashare 会用 qt(实时行情)接口的收盘价覆盖最后一根 K 线

基于 Ashare 源码分析(master 分支,2025-12-24)及社区常见问题

调试技巧

  1. 直接检查 API 返回:如果 get_price() 返回异常数据,可以直接在浏览器中访问底层 API 来检查原始返回。例如:
  2. 新浪日线:http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol=sh600519&scale=240&ma=5&datalen=10
  3. 腾讯日线:http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param=sh600519,day,,2025-12-24,10,qfq
  4. 如果浏览器能正常返回 JSON 数据但 Ashare 获取失败,说明是 Python 环境或网络问题。

  5. 使用 try-except 捕获详细错误:Ashare 内部的 try-except 会吞掉异常(裸 except:),建议在外层包裹自己的异常捕获,打印完整的错误堆栈: python import traceback try: df = get_price('sh600519', frequency='1d', count=10) except Exception: traceback.print_exc()

  6. 检查请求延迟:如果获取数据非常慢(超过 5 秒),可能是网络问题或 API 限流。可以使用以下代码测量请求时间: python import time start = time.time() df = get_price('sh600519', frequency='1d', count=10) elapsed = time.time() - start print(f'请求耗时:{elapsed:.2f} 秒')


第六部分:学习路线推荐

官方文档推荐阅读顺序

由于 Ashare 没有独立的文档站点,学习资源主要集中在以下位置:

  1. GitHub README - 重点关注安装方式和基本用法示例
  2. 地址:https://github.com/mpquant/Ashare
  3. 重点:get_price() 的参数说明和代码示例

  4. Ashare.py 源码 - 重点关注数据流和 API 调用逻辑

  5. 地址:https://github.com/mpquant/Ashare/blob/main/Ashare.py
  6. 重点:双数据源切换逻辑(get_price() 函数)、频率参数映射、股票代码格式转换

  7. GitHub Issues - 重点关注常见问题和用户反馈

  8. 地址:https://github.com/mpquant/Ashare/issues
  9. 重点:接口变更通知、数据源失效报告

推荐进阶资源

  • mpquant/MyTT(通达信技术指标 Python 移植库):https://github.com/mpquant/MyTT
  • 同一作者开发的通达信技术指标库,包含 MACD、KDJ、RSI、布林带等 50+ 个指标
  • 与 Ashare 天然兼容,可以直接基于 Ashare 获取的数据计算技术指标

  • 从0开始学量化:使用Ashare获取A股数据 — 知乎专栏

  • https://zhuanlan.zhihu.com/p/2590244600
  • 从零开始的量化数据获取教程,包含与聚宽平台的集成示例

  • AKShare、baostock、Ashare和Pytdx获取股票行情数据的完全指南 — 百度开发者社区

  • https://developer.baidu.com/article/details/2794524
  • 多库对比使用指南,帮助理解何时选择 Ashare、何时需要更强大的工具

进阶方向建议

完成本教程后,建议按以下方向深入学习:

  1. 从 Ashare 迁移到 AKShare:当项目需要更多数据类型(财务报表、指数估值、基金数据等)时,学习 AKShare 的 API
  2. 技术指标计算:结合 MyTT 库,在 Ashare 数据基础上构建完整的技术分析系统
  3. 量化策略回测:学习 backtrader 或 vnpy 等回测框架,将 Ashare 作为数据源进行策略验证

信息来源与版本说明