第1章:流动性数据获取——从零开始抓取LOF实时行情

大家好,我是你们的LOF套利实战导师。今天咱们正式开讲《LOF流动性管理从零开始》的第一章。

做LOF套利,说白了就是跟流动性打交道。没有数据,一切都是空谈。我见过太多新手,上来就搞策略、写模型,结果连行情数据都拿不到——嗯,那真是白忙活。

这一章,我带大家搞定三件事:实时行情、盘口深度、历史成交。这三样东西,是LOF套利的“三驾马车”。

核心观点:流动性数据是LOF套利的“眼睛”。没有数据,你就是在黑夜里开车。

1.1 数据源选择:tushare vs akshare

我个人习惯用两个库:tushareakshare。它们各有千秋,我分别说说。

对比项 tushare akshare
数据覆盖 全面,含LOF实时行情 更广,含盘口深度
使用门槛 需要注册获取token 免费,无需注册
稳定性 高,有付费版 中等,依赖源网站
盘口深度 不支持 支持(东方财富源)
历史数据 支持,按天获取 支持,可获取分钟级

我的建议是:两个都装。tushare做主力,akshare做补充。尤其是盘口深度数据,tushare目前不支持,得靠akshare。

小技巧:我在项目中遇到过tushare token过期的情况。建议把token写在配置文件里,定期检查。别问我怎么知道的——有一次凌晨跑策略,突然报错,那叫一个酸爽。

1.2 实时行情获取:tushare实战

先上代码。这是我最常用的实时行情获取函数:

import tushare as ts
import pandas as pd
from datetime import datetime

# 初始化tushare
ts.set_token('你的token')
pro = ts.pro_api()

def get_lof_realtime(ticker):
    """
    获取LOF实时行情
    :param ticker: LOF代码,如 '501018'
    :return: DataFrame
    """
    # 获取实时行情
    df = pro.realtime_tick(ts_code=f'{ticker}.SZ')
    
    if df.empty:
        print(f'警告:{ticker} 无数据,可能未上市或停牌')
        return None
    
    # 提取关键字段
    result = {
        '代码': ticker,
        '最新价': df['price'].iloc[0],
        '涨跌幅': df['pct_chg'].iloc[0],
        '成交量': df['vol'].iloc[0],
        '成交额': df['amount'].iloc[0],
        '时间': datetime.now().strftime('%H:%M:%S')
    }
    
    return pd.DataFrame([result])

# 测试
if __name__ == '__main__':
    df = get_lof_realtime('501018')
    print(df)

这里有个坑,我提醒一下:LOF代码后面要加交易所后缀。深交所的LOF用.SZ,上交所的用.SH。我曾经因为这个后缀写错过,结果查了半天数据都是空的——后来才发现是代码写成了.SS

注意:tushare的realtime_tick接口在交易时段外返回空数据。如果你在非交易时间测试,别慌,不是代码问题。

1.3 盘口深度数据:akshare上场

盘口深度,说白了就是买卖五档的挂单情况。这对LOF套利至关重要——你想想看,如果买一和卖一之间价差很大,说明流动性差,套利空间可能被吃掉。

akshare从东方财富源获取盘口数据,代码很简单:

import akshare as ak

def get_lof_depth(ticker):
    """
    获取LOF盘口深度数据
    :param ticker: LOF代码,如 '501018'
    :return: dict
    """
    # 获取盘口数据
    df = ak.stock_individual_info_em(symbol=ticker)
    
    # 获取买卖五档
    bid_ask = ak.stock_bid_ask_em(symbol=ticker)
    
    # 整理输出
    depth = {
        '买五': bid_ask['买五价'].iloc[0] if '买五价' in bid_ask.columns else None,
        '买四': bid_ask['买四价'].iloc[0] if '买四价' in bid_ask.columns else None,
        '买三': bid_ask['买三价'].iloc[0] if '买三价' in bid_ask.columns else None,
        '买二': bid_ask['买二价'].iloc[0] if '买二价' in bid_ask.columns else None,
        '买一': bid_ask['买一价'].iloc[0] if '买一价' in bid_ask.columns else None,
        '卖一': bid_ask['卖一价'].iloc[0] if '卖一价' in bid_ask.columns else None,
        '卖二': bid_ask['卖二价'].iloc[0] if '卖二价' in bid_ask.columns else None,
        '卖三': bid_ask['卖三价'].iloc[0] if '卖三价' in bid_ask.columns else None,
        '卖四': bid_ask['卖四价'].iloc[0] if '卖四价' in bid_ask.columns else None,
        '卖五': bid_ask['卖五价'].iloc[0] if '卖五价' in bid_ask.columns else None,
    }
    
    # 计算价差
    if depth['买一'] and depth['卖一']:
        depth['价差'] = depth['卖一'] - depth['买一']
        depth['价差率'] = depth['价差'] / depth['买一'] * 100
    else:
        depth['价差'] = None
        depth['价差率'] = None
    
    return depth

# 测试
depth = get_lof_depth('501018')
print(f"买一: {depth['买一']}, 卖一: {depth['卖一']}")
print(f"价差: {depth['价差']:.4f}, 价差率: {depth['价差率']:.2f}%")

为什么价差这么重要?我举个例子。有一次我盯着一只LOF,买一1.200,卖一1.205,价差0.005元。看起来不大,但换算成年化收益率,如果每天做一次套利,年化能到15%以上。后来流动性变差,价差扩大到0.02元,套利空间直接被吃掉了。

核心指标:价差率 < 0.5% 算流动性好,0.5%-1% 算一般,> 1% 算差。这是我个人经验,你可以根据自己策略调整。

1.4 历史成交数据:构建回测基础

历史数据是回测的基础。没有历史数据,你怎么知道策略在过去表现如何?

我用tushare获取日线历史数据:

def get_lof_history(ticker, start_date, end_date):
    """
    获取LOF历史日线数据
    :param ticker: LOF代码
    :param start_date: 起始日期 '20240101'
    :param end_date: 结束日期 '20241231'
    :return: DataFrame
    """
    df = pro.fund_daily(
        ts_code=f'{ticker}.SZ',
        start_date=start_date,
        end_date=end_date,
        fields='trade_date,open,high,low,close,vol,amount'
    )
    
    # 排序
    df = df.sort_values('trade_date')
    df = df.reset_index(drop=True)
    
    # 计算一些常用指标
    df['涨跌幅'] = df['close'].pct_change() * 100
    df['换手率'] = df['vol'] / df['vol'].rolling(20).mean()
    
    return df

# 获取2024年全年数据
hist = get_lof_history('501018', '20240101', '20241231')
print(hist.tail())

这里有个细节:换手率是我自己加的。换手率突然放大,往往意味着有资金在搞事情。我在做套利时,会特别关注换手率超过均值2倍以上的日子——那通常是套利机会出现的时候。

1.5 知识体系总览

为了让你更直观地理解本章的知识结构,我画了一张图:

LOF流动性数据获取知识体系 流动性数据获取 实时行情数据 盘口深度数据 历史成交数据 tushare: realtime_tick 字段:最新价/涨跌幅/成交量 注意:交易时段外返回空 akshare: stock_bid_ask_em 买卖五档 + 价差计算 价差率 < 0.5% 为流动性好 tushare: fund_daily 字段:开高低收/成交量/额 换手率放大 = 套利机会 三驾马车:实时行情 + 盘口深度 + 历史成交 tushare做主力,akshare做补充

1.6 实战中的避坑指南

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

  • 数据延迟问题:tushare的实时数据有1-3秒延迟。做高频套利时,这个延迟可能致命。我一般会用akshare的websocket接口做补充。
  • 停牌处理:LOF也会停牌。停牌期间所有数据接口返回空。我写了个自动检测函数,如果连续5分钟无数据,就发邮件报警。
  • 复权问题:历史数据默认是未复权的。做回测时一定要用复权数据,否则分红除权后你的策略曲线会失真。
  • 数据存储:别每次都从接口拉数据。我习惯每天收盘后把数据存到本地SQLite,第二天直接用本地数据,快很多。

我的习惯:每天早上开盘前,我会跑一遍所有数据获取脚本,检查数据是否正常。如果发现某只LOF数据异常,立刻人工排查。这个习惯帮我避免了好几次因为数据问题导致的策略误判。

好了,第一章的内容就到这里。数据获取是基础中的基础,但也是最容易出问题的地方。把这三样东西搞定了,后面的流动性分析、套利策略才有根基。

记住:数据质量决定策略质量。别嫌麻烦,把数据源、存储、监控都做好,后面你会感谢自己的。


专注资料整理