第4章:盘口数据获取实战

做量化交易,数据就是你的眼睛。没有数据,再好的策略也是瞎猜。

这一章,我们来点实在的。我会带你用 akshare 这个库,把ETF的实时盘口数据抓下来。然后解析买卖十档、算价差、算深度。嗯,都是做市商每天要看的东西。

4.1 为什么选 akshare?

说实话,国内能免费拿到实时行情的库不多。akshare 算是一个良心之作。它封装了新浪、腾讯等数据源,接口统一,用起来顺手。

我个人习惯是:开发阶段用 akshare 做数据验证,实盘阶段再切到券商API。为什么?因为免费接口有频率限制,你想想看,高频交易肯定不行。但做策略研究、回测、甚至低频做市,完全够用。

小提示: akshare 的实时行情数据,延迟大概在 1-3 秒。对于做市策略的盘口分析来说,这个延迟可以接受。

4.2 安装与导入

先装库,这个不用我多说了吧:

pip install akshare --upgrade

然后导入:

import akshare as ak
import pandas as pd
import time

嗯,这里要注意。akshare 更新比较频繁,建议每次运行前都升级一下。我曾经遇到过因为版本太旧,接口返回格式变了,导致解析报错。折腾了半天才发现是版本问题。

4.3 获取ETF实时行情

akshare 获取ETF实时行情,用的是 fund_etf_spot_em 这个接口。它会返回全市场ETF的实时数据。

# 获取所有ETF实时行情
df = ak.fund_etf_spot_em()
print(df.head())

返回的字段很多,我们重点关注这几个:

字段名 含义 说明
代码 ETF代码 如 510050
名称 ETF名称 如 上证50ETF
最新价 当前成交价 单位:元
涨跌幅 当日涨跌百分比 正数为涨
成交量 当日累计成交量 单位:手
成交额 当日累计成交金额 单位:万元

如果你想只看某一只ETF,比如 510050:

# 筛选指定ETF
etf_code = '510050'
row = df[df['代码'] == etf_code]
print(row[['代码', '名称', '最新价', '涨跌幅']])

4.4 解析买卖十档数据

实时行情只能看到最新价,但做市商真正关心的是盘口深度。也就是买卖十档的挂单情况。

akshare 提供了 fund_etf_quote_em 接口,可以获取ETF的盘口数据:

# 获取指定ETF的盘口数据
quote_df = ak.fund_etf_quote_em(symbol="510050")
print(quote_df)

返回的数据结构是这样的:

字段 含义
买一价 ~ 买十价 买方各档位的挂单价格
买一量 ~ 买十量 买方各档位的挂单数量(单位:手)
卖一价 ~ 卖十价 卖方各档位的挂单价格
卖一量 ~ 卖十量 卖方各档位的挂单数量(单位:手)

我一般会这样解析:

# 提取买卖十档数据
buy_prices = [quote_df[f'买{i}价'].values[0] for i in range(1, 11)]
buy_volumes = [quote_df[f'买{i}量'].values[0] for i in range(1, 11)]
sell_prices = [quote_df[f'卖{i}价'].values[0] for i in range(1, 11)]
sell_volumes = [quote_df[f'卖{i}量'].values[0] for i in range(1, 11)]

print("买方挂单:")
for i in range(10):
    print(f"  买{i+1}: 价格 {buy_prices[i]:.3f}, 数量 {buy_volumes[i]}手")
print("卖方挂单:")
for i in range(10):
    print(f"  卖{i+1}: 价格 {sell_prices[i]:.3f}, 数量 {sell_volumes[i]}手")
注意: 有些ETF流动性差,可能只有几档有数据,其他档位是0。解析时要做空值处理,否则会报错。

4.5 计算盘口价差

盘口价差(Spread),就是卖一价和买一价的差值。这是做市商的核心利润来源。

# 计算盘口价差
bid1_price = quote_df['买一价'].values[0]
ask1_price = quote_df['卖一价'].values[0]
spread = ask1_price - bid1_price
spread_pct = spread / ((ask1_price + bid1_price) / 2) * 100

print(f"买一价: {bid1_price:.3f}")
print(f"卖一价: {ask1_price:.3f}")
print(f"绝对价差: {spread:.4f} 元")
print(f"相对价差: {spread_pct:.2f}%")

为什么这个指标重要?说白了,价差就是你的利润空间。你做市,就是在买一价买入,卖一价卖出,赚中间的差价。如果价差太小,扣除手续费后可能还亏钱。

我在项目中遇到过这样的情况:某只ETF平时价差在0.01元左右,但遇到市场剧烈波动时,价差能扩大到0.05元以上。这时候做市风险很大,我会选择缩小报价量,甚至暂停做市。

4.6 计算盘口深度

盘口深度,衡量的是市场在某个价格水平上的承接能力。简单说,就是你想买/卖大单时,市场能不能接住。

我一般会计算两个指标:

  1. 各档位累计深度:从买一累加到买十,看不同价格水平的总挂单量
  2. 加权平均深度:考虑价格因素,计算深度加权价格
# 计算累计深度
cum_buy_volume = 0
cum_sell_volume = 0

print("买方深度(累计):")
for i in range(10):
    cum_buy_volume += buy_volumes[i]
    print(f"  买{i+1}档累计: {cum_buy_volume}手")

print("\n卖方深度(累计):")
for i in range(10):
    cum_sell_volume += sell_volumes[i]
    print(f"  卖{i+1}档累计: {cum_sell_volume}手")

# 计算加权平均深度价格
weighted_bid_price = sum(buy_prices[i] * buy_volumes[i] for i in range(10)) / sum(buy_volumes)
weighted_ask_price = sum(sell_prices[i] * sell_volumes[i] for i in range(10)) / sum(sell_volumes)

print(f"\n买方加权平均价格: {weighted_bid_price:.3f}")
print(f"卖方加权平均价格: {weighted_ask_price:.3f}")

你想想看,如果买一到买五的深度很厚,但买六到买十几乎没单,说明市场只在当前价格附近有承接力。一旦价格跌破买五,可能就会快速下探。这就是所谓的「深度断层」。

实战经验: 我一般会设置一个深度阈值。比如,如果买一档的挂单量小于100手,我会认为这个ETF流动性不足,不适合做市。如果累计深度小于1000手,我会降低单笔报单量。

4.7 完整代码示例

把上面的代码整合一下,写成一个完整的函数:

import akshare as ak
import pandas as pd

def get_etf_market_depth(etf_code):
    """
    获取ETF盘口深度数据
    :param etf_code: ETF代码,如 '510050'
    :return: 盘口深度字典
    """
    # 获取盘口数据
    quote_df = ak.fund_etf_quote_em(symbol=etf_code)
    
    # 解析买卖十档
    buy_prices = [quote_df[f'买{i}价'].values[0] for i in range(1, 11)]
    buy_volumes = [quote_df[f'买{i}量'].values[0] for i in range(1, 11)]
    sell_prices = [quote_df[f'卖{i}价'].values[0] for i in range(1, 11)]
    sell_volumes = [quote_df[f'卖{i}量'].values[0] for i in range(1, 11)]
    
    # 计算价差
    spread = sell_prices[0] - buy_prices[0]
    mid_price = (sell_prices[0] + buy_prices[0]) / 2
    spread_pct = spread / mid_price * 100
    
    # 计算深度
    total_bid_volume = sum(buy_volumes)
    total_ask_volume = sum(sell_volumes)
    
    # 返回结果
    result = {
        'code': etf_code,
        'bid_prices': buy_prices,
        'bid_volumes': buy_volumes,
        'ask_prices': sell_prices,
        'ask_volumes': sell_volumes,
        'spread': spread,
        'spread_pct': spread_pct,
        'total_bid_depth': total_bid_volume,
        'total_ask_depth': total_ask_volume
    }
    
    return result

# 使用示例
depth = get_etf_market_depth('510050')
print(f"ETF: {depth['code']}")
print(f"价差: {depth['spread']:.4f} ({depth['spread_pct']:.2f}%)")
print(f"买方总深度: {depth['total_bid_depth']}手")
print(f"卖方总深度: {depth['total_ask_depth']}手")

4.8 盘口分析框架图

下面这张图,是我做盘口分析时常用的框架。你可以把它当作一个检查清单:

ETF盘口分析框架 数据获取层 akshare → fund_etf_spot_em (实时行情) + fund_etf_quote_em (盘口数据) 数据解析层 提取买卖十档价格 + 提取买卖十档数量 + 空值处理 指标计算层 盘口价差(Spread) + 累计深度 + 加权平均价格 + 深度断层检测 策略应用层 做市报价决策 | 流动性评估 | 风险控制

这个框架,说白了就是四个步骤:拿数据 → 拆数据 → 算指标 → 做决策。每一步都有坑,我都在代码里加了注释,你跑一遍就明白了。

4.9 避坑指南

最后,分享几个我踩过的坑:

  • 频率限制:akshare 的免费接口,建议每秒最多请求1次。我曾经写了个循环每秒请求10次,结果IP被临时封了半小时。
  • 数据缺失:有些ETF在开盘初期或收盘前,盘口数据可能不完整。解析时一定要做异常处理。
  • 价格精度:ETF的价格精度一般是3位小数,但有些是4位。计算价差时要注意精度问题,避免浮点数误差。
  • 节假日:A股有节假日,非交易日调用接口会返回空数据。建议加个交易日判断。

好了,这一章的内容就到这里。代码你直接复制就能跑,跑通了再往下看。数据是交易的基础,把这一步做扎实了,后面的策略才能站得住脚。


蓝海数据掘金营,专注资料整理