第四章:数据获取——用tushare和akshare搭建LOF数据管道

做LOF套利,数据就是你的眼睛。没有准确、及时的数据,再好的策略也是空中楼阁。

我个人习惯把数据获取分成两条线:一条是行情数据,一条是申赎数据。行情数据告诉你价格在动,申赎数据告诉你套利空间在哪。两条线缺一不可。

4.1 为什么需要两套数据源?

你想想看,LOF这个品种特殊在哪?它既能像股票一样在二级市场买卖,又能像基金一样在一级市场申赎。两个市场的价格经常不一致,这就是套利机会的来源。

但问题来了——行情数据和申赎数据,通常不在同一个地方。tushare擅长提供二级市场的行情快照,而akshare则能拿到一级市场的申赎清单。我刚开始做的时候,也想过只用一套数据源搞定,后来发现根本行不通。

核心逻辑:行情数据 + 申赎数据 = 套利机会识别

行情数据:实时价格、成交量、涨跌幅

申赎数据:基金净值、申购费率、赎回费率、申赎状态

说白了,你要算套利空间,就得同时知道「二级市场卖多少钱」和「一级市场值多少钱」。这两个价格一对比,溢价还是折价,一目了然。

4.2 使用tushare获取LOF行情数据

tushare是我用得比较多的数据源之一。它的行情数据接口稳定,字段也全。不过要注意,tushare需要注册获取token,免费版有调用频率限制。

4.2.1 安装与初始化

# 安装tushare
pip install tushare

# 初始化
import tushare as ts

# 设置token(记得替换成你自己的)
ts.set_token('你的token')
pro = ts.pro_api()

小提示:token可以在tushare官网的个人中心获取。免费版每分钟最多调用200次,做回测够用了,但实盘的话建议升级付费版。

4.2.2 获取LOF日线行情

我最常用的接口是fund_daily,它能拿到LOF基金的日线数据。包括开盘价、收盘价、最高价、最低价、成交量这些基础字段。

# 获取某只LOF的日线行情
df = pro.fund_daily(
    ts_code='501018.SH',  # 南方原油LOF
    start_date='20240101',
    end_date='20240301',
    fields='ts_code,trade_date,open,high,low,close,vol,amount'
)

print(df.head())

输出结果大概长这样:

ts_code trade_date open high low close vol
501018.SH 20240301 1.234 1.245 1.228 1.240 1234567
501018.SH 20240229 1.220 1.238 1.215 1.232 987654

注意:tushare的基金行情数据,默认只返回上市交易的LOF和ETF。如果你查不到某只基金,先确认它是不是在交易所挂牌了。

4.2.3 获取实时行情

做套利需要盯盘,日线数据显然不够。我一般用fund_realtime接口拿实时快照。

# 获取实时行情
df_realtime = pro.fund_realtime(
    ts_code='501018.SH',
    fields='ts_code,name,price,high,low,volume,amount,pre_close'
)

print(df_realtime)

这个接口返回的是当前交易日的实时数据。嗯,这里要注意——tushare的实时数据有5秒左右的延迟,做高频套利的话可能不够用,但做日频套利完全没问题。

4.3 使用akshare获取LOF申赎数据

akshare是我后来才接触的库,说实话,一开始我对它有点偏见——觉得免费的东西质量不行。但用了一段时间后发现,它在基金申赎数据这块确实有两把刷子。

4.3.1 安装与基础用法

# 安装akshare
pip install akshare

# 基础导入
import akshare as ak

akshare不需要注册,直接就能用。这一点比tushare方便不少。

4.3.2 获取LOF申赎清单

我最常用的接口是fund_etf_fund_info_em,它能拿到LOF基金的申赎相关信息,包括净值、申购费率、赎回费率、申赎状态等。

# 获取LOF申赎数据
df_lof = ak.fund_etf_fund_info_em(
    fund='501018',  # 基金代码(不需要后缀)
    start_date='20240101',
    end_date='20240301'
)

print(df_lof.head())

返回的字段包括:

  • 净值日期:基金净值的公布日期
  • 单位净值:基金的实际净值
  • 累计净值:考虑分红的净值
  • 申购状态:开放/暂停
  • 赎回状态:开放/暂停
  • 申购费率:申购时需要支付的费率
  • 赎回费率:赎回时需要支付的费率

关键点:申赎状态直接影响套利能否执行。如果基金暂停申购,那溢价套利就做不了。我曾在2022年原油LOF溢价的时候,因为没检查申赎状态,白白错过了机会。

4.3.3 获取实时净值估算

做套利时,净值是实时变化的。akshare提供了净值估算接口,可以拿到盘中估算净值。

# 获取实时净值估算
df_estimate = ak.fund_etf_spot_em()

# 筛选LOF基金
df_lof_estimate = df_estimate[df_estimate['基金类型'].str.contains('LOF')]

print(df_lof_estimate.head())

这个接口返回的是全市场所有ETF和LOF的实时估算数据。数据量比较大,我建议只筛选你关注的几只基金,别全量拉取。

4.4 数据整合:构建套利数据管道

数据拿到手了,下一步就是把它们整合起来。我个人习惯写一个数据管道类,把tushare和akshare的数据统一管理。

class LOFDataPipeline:
    """LOF套利数据管道"""
    
    def __init__(self, tushare_token):
        import tushare as ts
        ts.set_token(tushare_token)
        self.pro = ts.pro_api()
        
    def get_market_data(self, ts_code, start_date, end_date):
        """获取行情数据(tushare)"""
        df = self.pro.fund_daily(
            ts_code=ts_code,
            start_date=start_date,
            end_date=end_date
        )
        return df
    
    def get_redemption_data(self, fund_code, start_date, end_date):
        """获取申赎数据(akshare)"""
        import akshare as ak
        df = ak.fund_etf_fund_info_em(
            fund=fund_code,
            start_date=start_date,
            end_date=end_date
        )
        return df
    
    def merge_data(self, ts_code, fund_code, start_date, end_date):
        """合并行情和申赎数据"""
        market_df = self.get_market_data(ts_code, start_date, end_date)
        redeem_df = self.get_redemption_data(fund_code, start_date, end_date)
        
        # 按日期合并
        merged = pd.merge(
            market_df, 
            redeem_df, 
            left_on='trade_date', 
            right_on='净值日期',
            how='inner'
        )
        return merged

避坑指南:我曾经在合并数据时踩过一个坑——tushare的日期格式是'YYYYMMDD',而akshare的日期格式是'YYYY-MM-DD'。合并前一定要统一格式,否则会匹配不上。

4.5 数据质量检查

数据拿到手,别急着用。先做一轮质量检查。我一般检查这几个方面:

  1. 缺失值检查:净值、价格这些关键字段不能为空
  2. 日期连续性:交易日数据不能有跳空
  3. 异常值检查:价格不能为负,净值不能为0
  4. 申赎状态检查:确认基金是否正常开放申赎
def check_data_quality(df):
    """数据质量检查"""
    issues = []
    
    # 检查缺失值
    if df['close'].isnull().any():
        issues.append('收盘价存在缺失值')
    
    # 检查异常值
    if (df['close'] <= 0).any():
        issues.append('收盘价存在非正值')
    
    # 检查申赎状态
    if '申购状态' in df.columns:
        if (df['申购状态'] == '暂停').any():
            issues.append('存在暂停申购的交易日')
    
    return issues

4.6 本章核心知识图谱

下面这张图,是我做数据获取时的核心思路。你可以把它当成一个路线图。

LOF套利数据获取架构 tushare 行情数据 akshare 申赎数据 数据整合层 价格 + 净值 + 费率 套利机会识别 行情:开高低收、成交量 申赎:净值、费率、状态 合并:溢价率、套利空间 数据管道:tushare → 数据整合 → akshare → 套利信号

这张图的核心逻辑很简单:tushare提供行情数据,akshare提供申赎数据,两者在数据整合层合并,最终输出套利信号。我建议你在实际项目中,也按照这个架构来搭建数据管道。

重要提醒:数据获取只是第一步。下一章我们会讲如何计算溢价率、识别套利机会。但如果你连数据都拿不准,后面的分析全是白搭。所以,花点时间把数据管道搭稳了,后面会省很多事。

好了,数据获取这块就讲到这里。代码我都贴出来了,你直接复制就能用。如果遇到问题,欢迎在课程群里交流。


公众号:蓝海资料掘金营,微信deep3321