4. 数据获取与清洗

在金融数据分析Agent的完整链路中,数据获取与清洗是决定后续所有分析可信度的地基。无论Agent后续要做因子挖掘、组合回测还是风险监控,只要原始数据存在缺失、重复、口径不一致或复权错误,模型再强也会输出错误结论。本节聚焦三类主流数据接口——Tushare、AkShare、yfinance,讲清它们的定位差异、获取流程、清洗标准与常见陷阱。

一、核心原理:三类接口的定位与数据边界

金融数据接口的本质是“把交易所、数据商或公开网页上的原始记录,转换为结构化、可编程访问的数据对象”。不同接口的差异,主要体现在数据覆盖范围、授权方式、更新频率和字段规范上。

  • Tushare:面向A股、基金、期货、宏观等中文市场,字段规范、积分制授权,适合需要稳定、标准化数据的生产场景。
  • AkShare:开源免费,覆盖股票、基金、期货、债券、外汇及另类数据,接口数量多、更新快,适合快速验证与补充数据。
  • yfinance:主要覆盖、港股、ETF、外汇与加密货币,基于Yahoo Finance,适合海外资产与跨市场对比。

三者并非互斥,而是互补。一个成熟的Agent通常以Tushare为主数据源,AkShare为补充源,yfinance为海外映射源,并在清洗层统一字段口径。

数据获取与清洗标准化链路 Tushare A股/基金/期货 积分授权·字段规范 AkShare 全品类·开源免费 接口多·更新快 yfinance /港股/ETF 海外映射·跨市场 统一清洗层 去重·缺失·复权·时区·字段映射 标准化数据表 date·code·open·high·low·close·volume

二、方法流程:从接口调用到标准化数据表

无论使用哪个接口,标准流程都可以归纳为五步:初始化连接、拉取原始数据、字段对齐、质量清洗、落地存储。下面以A股日线为例,展示Tushare与AkShare的典型调用方式。

# Tushare 获取A股日线
import tushare as ts
pro = ts.pro_api("你的")
df = pro.daily(ts_code="000001.SZ",
               start_date="20240101",
               end_date="20241231")

# AkShare 获取A股日线
import akshare as ak
df = ak.stock_zh_a_hist(symbol="000001",
                        period="daily",
                        start_date="20240101",
                        end_date="20241231",
                        adjust="qfq")

# yfinance 获取日线
import yfinance as yf
df = yf.download("AAPL", start="2024-01-01", end="2024-12-31")

拿到原始数据后,进入清洗环节。核心动作包括:统一列名、转换日期类型、处理缺失值与重复行、复权处理、时区对齐、异常值检测。建议将清洗逻辑封装为函数,保证不同数据源输出同一套字段结构。

def standardize(df, source):
    df = df.rename(columns={
        "trade_date": "date", "日期": "date",
        "ts_code": "code", "股票代码": "code",
        "vol": "volume", "成交量": "volume"
    })
    df["date"] = pd.to_datetime(df["date"])
    df = df.drop_duplicates(subset=["date", "code"])
    df = df.sort_values(["code", "date"]).reset_index(drop=True)
    df["source"] = source
    return df[["date","code","open","high","low","close","volume","source"]]

三、案例场景:多源数据合并与复权处理

假设Agent需要同时分析A股银行股与银行ETF的相关性。Tushare提供A股日线,yfinance提供ETF数据。两者日期格式、时区、复权方式均不同。此时需要先分别标准化,再按日期对齐,最后合并为宽表。

清洗维度 Tushare AkShare yfinance
日期字段 trade_date(字符串) 日期(字符串) Date(索引)
复权方式 需手动计算或使用adj_factor adjust参数可选qfq/hfq auto_adjust参数控制
缺失值 停牌日无记录 停牌日可能为空 非交易日无记录
时区 北京时间 北京时间 美东时间
字段命名 英文缩写 中文为主 英文标准
复权是清洗中最容易被忽视的环节。前复权适合回测,后复权适合长期收益对比,不复权仅适合观察真实成交价。Agent应根据分析目标自动选择复权方式,并在元数据中记录。

四、误区与复盘:常见陷阱与排查清单

实际项目中,数据问题往往不是“拿不到”,而是“拿到了但不可用”。以下是最常见的五类误区:

  1. 忽略停牌与退市:停牌日无数据,直接dropna会丢失交易日结构,建议用交易日历reindex后填充或标记。
  2. 混用复权口径:同一张表中部分股票前复权、部分不复权,导致收益率计算完全失真。
  3. 时区未对齐:与A股日期直接merge,会出现大量错配,需统一为UTC或分别对齐交易日。
  4. 字段类型错误:成交量、成交额被读成字符串,参与计算时静默出错。
  5. 接口限流与重试:Tushare有积分与频率限制,AkShare依赖网页结构,需加缓存与重试机制。
清洗后的数据必须保留“数据血缘”:记录来源接口、拉取时间、复权方式、清洗版本。否则当Agent输出异常时,无法快速定位是数据问题还是模型问题。

建议在Agent中内置一张数据质量检查表,每次拉取后自动执行:行数是否合理、日期是否连续、价格是否为正、成交量是否非负、涨跌幅是否超过阈值。只有通过检查的数据才进入下游分析。

def quality_check(df):
    assert df["close"].gt(0).all(), "存在非正价格"
    assert df["volume"].ge(0).all(), "存在负成交量"
    assert df["date"].is_monotonic_increasing, "日期未排序"
    assert df["code"].nunique() > 0, "无有效代码"
    return True

把获取与清洗做成可复用、可审计、可扩展的模块,Agent才能在面对股票、基金、期货等多品种时保持稳定输出。数据层越干净,上层分析越可信,这也是金融数据分析Agent区别于普通脚本的关键工程能力。