Skip to main content

A-share data aggregation SDK with explicit gateway APIs.

Project description

StockDataGateway 设计文档

背景

当前项目已经整理了多类 A 股数据源:

  • mootdx / 通达信 SDK:K线、实时行情、盘口、分笔成交、财务快照、F10。
  • thsdk:K线、分时、盘口、Tick、大单、竞价、板块、指数、问财;多市场行情暂列低优先级。
  • 腾讯财经:实时行情、PE/PB、市值、换手率、涨跌停、指数/ETF。
  • 东方财富:个股新闻、资金流、龙虎榜、解禁、融资融券、大宗交易、股东户数、分红、行业排名、研报。
  • 新浪:实时行情、财报三表、图表图片。
  • 百度股市通:K线带均线、个股概念/行业/地域归属。
  • 巨潮:公告全文检索。
  • 选股宝、金十、韭研公社、timor.tech 等辅助数据源。

这些数据源的字段、代码格式、稳定性、限流规则都不同。目标是封装一个统一网关,让上层业务调用明确方法,而不是关心底层 provider。

运行依赖由 pyproject.toml 声明,默认 SDK 安装包含 requestspandasthsdkmootdxlxml 等 provider 运行所需包;如果某个真实 provider 在本机不可用,测试仍应通过 fake/mock provider 覆盖网关和 schema 行为。

设计目标

  1. 对外 API 必须显式,例如 get_kline()get_quote()get_stock_news(),不使用 gateway.get(capability="...") 这种动态入口。
  2. 每个方法内部可以有多个 provider,并按策略自动降级。
  3. 自动降级只允许发生在语义等价或兼容的数据源之间。
  4. 每次降级必须记录 fallback_chain,不能静默换源。
  5. 所有返回结果使用统一 GatewayResult 包裹,包含数据、provider、schema、warnings、耗时和错误。
  6. 常用数据统一字段 schema,保留 raw 或 provider 原始结果以便调试。
  7. 东方财富等有风控的数据源统一限流,不允许并发打接口。
  8. 可以分期实现,首期覆盖高频行情,后续逐步接入资金、公告、财务、研报、板块和问财。

非目标

  • 首版不做动态插件市场。
  • 首版不做跨进程缓存服务。
  • 首版不做异步任务队列。
  • 首版不把所有数据强行合并成一个大 schema。
  • 首版不把不等价数据做静默替代,例如“个股新闻”不自动降级到“全市场快讯”。
  • 不接入 iwencai OpenAPI,不要求 API Key 或 X-Claw-* 头;问财能力统一通过 thsdkwencai_nlp / wencai_base 模式承载。
  • 多市场行情能力暂缓,不作为近期阶段的默认建设目标。

对外 API

行情与交易数据

class StockDataGateway:
    def get_kline(
        self,
        code: str,
        interval: str = "day",
        count: int | None = None,
        start: str | None = None,
        end: str | None = None,
        adjust: str | None = None,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_quote(
        self,
        code: str | list[str],
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_depth(
        self,
        code: str,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_intraday(
        self,
        code: str,
        date: str | None = None,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_transaction(
        self,
        code: str,
        date: str | None = None,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_tick(
        self,
        code: str,
        mode: str = "level1",
        date: str | None = None,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_big_order(
        self,
        code: str,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_call_auction(
        self,
        code: str,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def scan_call_auction_anomaly(
        self,
        market: str = "USZA",
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

资金、筹码与交易异动

    def get_fund_flow(
        self,
        code: str,
        period: str = "minute",
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_fund_flow_120d(
        self,
        code: str,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_margin(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_block_trade(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_holder_num(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_dividend(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...

    def get_lockup_expiry(
        self,
        code: str,
        forward_days: int = 90,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_dragon_tiger(
        self,
        code: str,
        trade_date: str | None = None,
        look_back: int = 30,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_daily_dragon_tiger(
        self,
        trade_date: str | None = None,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

指数、板块与题材

    def get_index_quote(self, index_code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_index_kline(self, index_code: str, interval: str = "day", provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_index_list(self, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...

    def get_sector_list(
        self,
        sector_type: str = "industry",
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_sector_quote(self, sector_code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_sector_constituents(self, sector_code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_industry_rank(self, top_n: int = 20, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_concept_blocks(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_hot_reason(self, date: str | None = None, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...

新闻、公告与资讯

    def get_stock_news(
        self,
        code: str,
        limit: int = 20,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_global_news(
        self,
        limit: int = 50,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_announcements(
        self,
        code: str,
        limit: int = 30,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_f10(
        self,
        code: str,
        section: str = "公司概况",
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_latest_notice_summary(
        self,
        code: str,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

财务、估值与研报

    def get_finance_snapshot(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_stock_info(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...

    def get_financial_statement(
        self,
        code: str,
        report_type: str = "income",
        limit: int = 8,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def get_eps_forecast(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_reports(self, code: str, max_pages: int = 2, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def download_report(
        self,
        record: dict,
        target_dir: str | None = None,
        filename: str | None = None,
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...
    def get_valuation(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...

问财与自然语言

    def query_wencai(self, query: str, provider: str = "auto") -> GatewayResult: ...
    def query_wencai_base(self, query: str, provider: str = "auto") -> GatewayResult: ...
    def screen_stocks(self, query: str, provider: str = "auto") -> GatewayResult: ...
    def compare_stocks(self, codes: list[str], interval: str = "day", count: int = 30, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def normalize_trend(self, codes: list[str], interval: str = "day", count: int = 30, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def correlation_matrix(self, codes: list[str], interval: str = "day", count: int = 30, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...

多市场

    def get_hk_quote(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_us_quote(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_uk_quote(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_forex_quote(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_future_quote(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_bond_quote(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_fund_quote(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def get_option_data(self, code: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def list_market(self, market: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...

图片与辅助

    def get_chart_image(
        self,
        code: str,
        chart_type: str = "daily",
        provider: str = "auto",
        fallback: bool = True,
    ) -> GatewayResult: ...

    def search_symbol(self, keyword: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...
    def normalize_symbol(self, code: str) -> dict: ...
    def is_trading_day(self, date: str, provider: str = "auto", fallback: bool = True) -> GatewayResult: ...

统一返回模型

from dataclasses import dataclass
from typing import Any

@dataclass
class GatewayResult:
    ok: bool
    data: Any
    provider: str | None
    fallback_chain: list[dict]
    warnings: list[str]
    schema: list[str]
    symbol: str | None = None
    normalized_symbol: dict | None = None
    latency_ms: float | None = None
    error: str | None = None
    raw: Any | None = None

fallback_chain 记录每个 provider 的调用情况:

[
    {
        "provider": "thsdk",
        "status": "failed",
        "error_type": "timeout",
        "error": "connect timeout",
        "latency_ms": 3000,
    },
    {
        "provider": "mootdx",
        "status": "success",
        "latency_ms": 126,
    },
]

SymbolResolver

SymbolResolver 负责把用户输入归一化为不同 provider 所需格式。

输入支持:

输入 归一化
300498 纯 6 位代码
SZ300498 / sz300498 纯 6 位代码
300498.SZ 纯 6 位代码
USZA300498 THSCODE
sz300498 腾讯/新浪格式

输出示例:

{
    "raw": "300498",
    "code": "300498",
    "market": "sz",
    "ths": "USZA300498",
    "tencent": "sz300498",
    "sina": "sz300498",
    "eastmoney_secid": "0.300498",
    "mootdx": "300498",
}

指数、港股、美股、外汇、期货等使用独立转换规则。

Provider 设计

每个 provider 是一个适配器,只实现自己支持的方法。

class BaseProvider:
    name: str

    def health(self) -> dict: ...

ThsdkProvider

支持:

  • get_kline
  • get_quote
  • get_depth
  • get_intraday
  • get_tick
  • get_big_order
  • get_call_auction
  • scan_call_auction_anomaly
  • get_index_quote
  • get_index_kline
  • get_index_list
  • get_sector_list
  • get_sector_quote
  • get_sector_constituents
  • get_eps_forecast 的动态问财 fallback
  • query_wencai
  • query_wencai_base
  • get_global_news
  • 多市场 quote/list 方法暂缓接入

MootdxProvider

支持:

  • get_kline
  • get_quote
  • get_depth
  • get_intraday
  • get_transaction
  • get_finance_snapshot
  • get_f10
  • get_latest_notice_summary
  • get_index_kline

TencentProvider

支持:

  • get_quote
  • get_index_quote
  • get_stock_info 的部分字段
  • get_valuation

EastmoneyProvider

支持:

  • get_stock_news
  • get_global_news
  • get_fund_flow
  • get_fund_flow_120d
  • get_margin
  • get_block_trade
  • get_holder_num
  • get_dividend
  • get_lockup_expiry
  • get_dragon_tiger
  • get_daily_dragon_tiger
  • get_industry_rank
  • get_stock_info
  • get_reports
  • get_eps_forecast
  • download_report
  • get_valuation

所有 Eastmoney 请求必须经过限流器。

SinaProvider

支持:

  • get_quote
  • get_financial_statement
  • get_chart_image

BaiduProvider

支持:

  • get_kline
  • get_concept_blocks
  • get_sector_constituents 的部分 fallback

SohuProvider

支持:

  • get_kline 的历史日线 fallback

CninfoProvider

支持:

  • get_announcements

JiuyangongsheProvider

支持:

  • get_stock_news fallback

XuanGuBaoProvider

支持:

  • get_global_news fallback

Jin10Provider

支持:

  • get_global_news fallback,WebSocket 形态,首版可先不实现实时长连接。

TimorProvider

支持:

  • is_trading_day

降级策略

降级策略按方法写死,不对外暴露动态 capability。

行情与 K线

方法 默认 provider 顺序 等价性 说明
get_kline(interval="1m") thsdk -> mootdx compatible_with_warning mootdx 可能合并 09:30 bar
get_kline(interval="5m") thsdk -> mootdx compatible_with_warning 分钟边界可能略不同
get_kline(interval="day") thsdk -> mootdx -> baidu -> sohu equivalent/compatible 复权方式需标记
get_kline(interval="week") thsdk -> mootdx equivalent
get_kline(interval="month") thsdk -> mootdx equivalent
get_quote tencent -> thsdk -> mootdx -> sina compatible_with_warning 各源估值字段覆盖不同
get_depth thsdk -> mootdx compatible_with_warning mootdx 普通五档,THS 可更细
get_intraday thsdk -> mootdx compatible_with_warning 历史覆盖范围不同
get_transaction mootdx not_supported 暂无自动 fallback
get_tick thsdk not_supported 暂无自动 fallback
get_big_order thsdk not_supported 不等同于资金流
get_call_auction thsdk not_supported
scan_call_auction_anomaly thsdk not_supported

新闻、公告、资金

方法 默认 provider 顺序 等价性 说明
get_stock_news eastmoney -> jiuyangongshe compatible_with_warning 不降级到全市场快讯
get_global_news thsdk -> eastmoney_global -> xuan_gu_bao -> jin10 compatible_with_warning 来源差异大
get_announcements cninfo -> mootdx_f10 compatible_with_warning F10 是摘要,不是全文
get_fund_flow eastmoney not_supported THS 大单不等价;东财 provider 内部可从 push2 同源切到 push2delay,仍复用 limiter
get_fund_flow_120d eastmoney not_supported 东财 provider 内部可从 push2his daykline 切到 push2delay kline(klt=101),仍复用 limiter

筹码与事件

方法 默认 provider 顺序 等价性 说明
get_margin eastmoney not_supported
get_block_trade eastmoney not_supported
get_holder_num eastmoney not_supported
get_dividend eastmoney not_supported
get_lockup_expiry eastmoney not_supported
get_dragon_tiger eastmoney -> thsdk_wencai compatible_with_warning thsdk 问财字段动态
get_daily_dragon_tiger eastmoney -> thsdk_wencai compatible_with_warning

指数、板块、题材、财务、研报

方法 默认 provider 顺序 等价性 说明
get_index_quote thsdk not_supported 指数使用 market_data_index
get_index_kline thsdk not_supported 指数代码使用 USHI / USZI 前缀
get_index_list thsdk not_supported
get_sector_list thsdk -> eastmoney compatible_with_warning 东财 fallback 仅覆盖板块列表基础字段
get_sector_quote thsdk not_supported
get_sector_constituents thsdk not_supported 板块成分股优先 THS
get_industry_rank eastmoney not_supported 东财适合排名,必须限流;provider 内部可切 push2delay 同源备用域
get_concept_blocks baidu -> thsdk_wencai compatible_with_warning 百度优先;百度 403/不可用时 thsdk 问财返回动态字段和 raw,必须 warning
get_hot_reason ths_hot -> thsdk_wencai compatible_with_warning thsdk 问财 fallback 字段动态
get_finance_snapshot mootdx -> eastmoney compatible_with_warning 字段不同
get_stock_info eastmoney -> tencent compatible_with_warning 腾讯缺上市日期等字段
get_financial_statement sina not_supported 新浪三表按真实报表表格展平为 period/item/value/raw,不混入财务快照
get_eps_forecast eastmoney -> thsdk_wencai compatible_with_warning 东财研报 EPS 字段优先;thsdk 问财 fallback 字段动态,必须 warning;无机构覆盖返回空,不算异常
get_reports eastmoney not_supported
download_report eastmoney not_supported 默认只返回 PDF bytes;传 target_dir 才落盘,避免提交下载产物
get_valuation tencent -> eastmoney compatible_with_warning 腾讯 quote 估值字段优先;东财 push2 补充
query_wencai / query_wencai_base thsdk not_supported 只走 thsdk.wencai_nlp / thsdk.wencai_base,不需要 API Key;query_wencai_base 在 thsdk base 查询失败时可退回 NLP 动态结果
screen_stocks thsdk not_supported 业务包装,保留原始问财返回
compare_stocks / normalize_trend / correlation_matrix 复用 get_quote / get_kline compatible_with_warning 按单只显式方法循环拉取,记录子调用 fallback_chain
search_reports 不进入当前路线 not_supported 不接入 iwencai OpenAPI;主题检索优先通过 query_wencai 动态探索

错误处理

触发 fallback:

  • 网络超时。
  • TCP 连接失败。
  • HTTP 403 / 429。
  • provider 返回明确错误。
  • 返回字段缺失。
  • 数据校验失败。
  • 权限不足。

不一定触发 fallback:

  • 非交易时间返回空分笔。
  • 没有机构覆盖导致 EPS 预期为空。
  • 非交易日没有当日龙虎榜。
  • 查询条件本身无结果。

错误分类:

class GatewayError(Exception): ...
class ProviderUnavailable(GatewayError): ...
class ProviderTimeout(GatewayError): ...
class ProviderRateLimited(GatewayError): ...
class ProviderPermissionDenied(GatewayError): ...
class EmptyData(GatewayError): ...
class SchemaValidationError(GatewayError): ...
class UnsupportedProvider(GatewayError): ...

限流与健康状态

RateLimiter

东方财富默认:

  • 串行。
  • 最小间隔 1 秒。
  • 加随机抖动。
  • 批量任务可配置为 1.5 到 2 秒。

其他 HTTP provider:

  • 默认轻量限流。
  • 支持 provider 级别配置。

ProviderHealth

记录:

  • 最近成功时间。
  • 最近失败时间。
  • 连续失败次数。
  • 平均耗时。
  • 是否处于冷却期。
  • 最近错误类型。

如果 provider 触发 403 / 429,进入冷却期,自动跳过一段时间。

标准 Schema

K线

KLINE_SCHEMA = [
    "time",
    "open",
    "high",
    "low",
    "close",
    "volume",
    "amount",
]

实时行情

QUOTE_SCHEMA = [
    "symbol",
    "name",
    "price",
    "prev_close",
    "open",
    "high",
    "low",
    "change",
    "change_pct",
    "volume",
    "amount",
    "turnover_rate",
    "volume_ratio",
    "pe_ttm",
    "pb",
    "market_cap",
    "float_market_cap",
    "limit_up",
    "limit_down",
]

盘口

DEPTH_SCHEMA = [
    "symbol",
    "bid1_price", "bid1_volume",
    "bid2_price", "bid2_volume",
    "bid3_price", "bid3_volume",
    "bid4_price", "bid4_volume",
    "bid5_price", "bid5_volume",
    "ask1_price", "ask1_volume",
    "ask2_price", "ask2_volume",
    "ask3_price", "ask3_volume",
    "ask4_price", "ask4_volume",
    "ask5_price", "ask5_volume",
]

个股新闻

STOCK_NEWS_SCHEMA = [
    "time",
    "source",
    "title",
    "summary",
    "url",
    "symbols",
    "relevance",
]

资金流

FUND_FLOW_SCHEMA = [
    "time",
    "main_net",
    "super_net",
    "large_net",
    "mid_net",
    "small_net",
]

公告

ANNOUNCEMENT_SCHEMA = [
    "date",
    "title",
    "type",
    "url",
]

资金面、筹码与事件

MARGIN_SCHEMA = [
    "date",
    "rzye",
    "rzmre",
    "rzche",
    "rqye",
    "rqmcl",
    "rqchl",
    "rzrqye",
]

BLOCK_TRADE_SCHEMA = [
    "date",
    "price",
    "close",
    "premium_pct",
    "vol",
    "amount",
    "buyer",
    "seller",
]

HOLDER_NUM_SCHEMA = [
    "date",
    "holder_num",
    "change_num",
    "change_ratio",
    "avg_shares",
]

DIVIDEND_SCHEMA = [
    "date",
    "bonus_rmb",
    "transfer_ratio",
    "bonus_ratio",
    "plan",
]

LOCKUP_EXPIRY_SCHEMA = [
    "history",
    "upcoming",
]

DRAGON_TIGER_SCHEMA = [
    "records",
    "seats",
    "institution",
]

DAILY_DRAGON_TIGER_SCHEMA = [
    "date",
    "total_records",
    "stocks",
    "note",
]

金额字段统一保留为元,不在 SDK provider 层换算成万元或亿元;展示层可自行换算。

目录结构

stock_gateway/
  __init__.py
  gateway.py
  models.py
  resolver.py
  errors.py
  fallback.py
  health.py
  rate_limit.py

  providers/
    __init__.py
    base.py
    thsdk_provider.py
    mootdx_provider.py
    tencent_provider.py
    eastmoney_provider.py
    sina_provider.py
    baidu_provider.py
    sohu_provider.py
    cninfo_provider.py
    jiuyangongshe_provider.py
    xuan_gu_bao_provider.py
    jin10_provider.py
    timor_provider.py

  normalizers/
    __init__.py
    kline.py
    quote.py
    depth.py
    intraday.py
    news.py
    announcement.py
    fund_flow.py
    finance.py
    sector.py

  policies/
    __init__.py
    kline_policy.py
    quote_policy.py
    news_policy.py
    finance_policy.py

  tests/
    test_gateway_kline.py
    test_gateway_quote.py
    test_fallback.py
    test_normalizers.py
    test_stock_news_policy.py

分期实现计划

Phase 0:基础框架

目标:建立可扩展骨架。

实现:

  • GatewayResult
  • SymbolResolver
  • GatewayError 错误体系
  • FallbackRunner
  • RateLimiter
  • ProviderHealth
  • BaseProvider
  • 基础测试框架

验收:

  • 可以实例化 StockDataGateway
  • 可以注册 provider。
  • 可以模拟 provider 失败并进入 fallback。

Phase 1:高频行情 MVP

目标:支撑日常看盘和分析。

实现:

  • get_kline
  • get_quote
  • get_depth
  • get_intraday
  • get_transaction
  • get_chart_image

Provider:

  • ThsdkProvider
  • MootdxProvider
  • TencentProvider
  • SinaProvider
  • BaiduProvider
  • SohuProvider

验收:

  • gateway.get_kline("300498", interval="1m") 可返回标准 K线 schema。
  • thsdk 异常时可降级到 mootdx。
  • gateway.get_quote("300498") 优先腾讯,失败后降级。
  • 1m K线降级到 mootdx 时包含 09:30 差异 warning。

Phase 2:新闻、公告、资金

目标:覆盖个股研究最常用信息面。

实现:

  • get_stock_news
  • get_global_news
  • get_announcements
  • get_fund_flow
  • get_fund_flow_120d
  • get_finance_snapshot
  • get_stock_info

当前实现状态(2026-06-04):

  • 已实现 get_stock_newsget_global_newsget_announcementsget_fund_flowget_fund_flow_120dget_finance_snapshotget_stock_info
  • 已额外接入 get_reports 的东财研报列表基础能力,满足个股研究入口;PDF 下载仍留在 Phase 5,iwencai OpenAPI 不进入当前路线。
  • 东财 provider 使用统一限流入口,所有东财 HTTP 请求先经过 limiter。
  • 个股新闻默认链路只在 eastmoney -> jiuyangongshe 内降级,不降级到 get_global_news
  • 公告默认链路为 cninfo -> mootdx;mootdx 仅作为 F10 摘要 fallback,gateway 必须返回 warning。
  • get_finance_snapshot 默认 mootdx -> eastmoney;东财 fallback 只提供基本面兼容字段,gateway 必须返回 warning。

Provider:

  • EastmoneyProvider
  • CninfoProvider
  • MootdxProvider
  • JiuyangongsheProvider

验收:

  • 东财请求走限流器。
  • 个股新闻不默认降级到全市场快讯。
  • 公告全文优先巨潮,F10 fallback 必须带 warning。

Phase 3:资金面、筹码、事件

目标:补齐 a-stock-data 独有事件数据。

实现:

  • get_margin
  • get_block_trade
  • get_holder_num
  • get_dividend
  • get_lockup_expiry
  • get_dragon_tiger
  • get_daily_dragon_tiger

当前实现状态(2026-06-04):

  • 已实现 get_marginget_block_tradeget_holder_numget_dividendget_lockup_expiryget_dragon_tigerget_daily_dragon_tiger
  • 东财 datacenter 请求统一经 EastmoneyProvider._get(),复用 provider limiter。
  • get_dragon_tigerget_daily_dragon_tiger 默认链路为 eastmoney -> thsdk;thsdk 仅作为 wencai 动态字段 fallback,gateway 必须返回 warning。
  • get_daily_dragon_tiger 在非交易日或盘后未更新时返回空列表和 note,不把无数据当作异常。
  • 金额字段统一保留为元,展示层按需换算万元/亿元。

Provider:

  • EastmoneyProvider
  • ThsdkProvider 的 wencai fallback。

验收:

  • 金额字段统一为元。
  • 展示层可自行换算万元/亿元。
  • 非交易日无龙虎榜不算异常。

Phase 4:板块、指数、题材

目标:支持行业轮动、概念研究和成分股分析。

实现:

  • get_index_quote
  • get_index_kline
  • get_index_list
  • get_sector_list
  • get_sector_quote
  • get_sector_constituents
  • get_industry_rank
  • get_concept_blocks
  • get_hot_reason

Provider:

  • ThsdkProvider
  • EastmoneyProvider
  • BaiduProvider
  • ThsHotProvider

当前实现状态(2026-06-05):

  • 已实现 get_index_quoteget_index_klineget_index_listget_sector_listget_sector_quoteget_sector_constituentsget_industry_rankget_concept_blocksget_hot_reason
  • get_index_quote / get_index_kline 会把常见指数代码映射到 thsdk 真实内码,例如 000001 -> USHI1A0001000300 -> USHI1B0300399001 -> USZI399001
  • get_sector_constituents 默认只走 thsdk,避免把“个股归属”静默替代为“不完整成分股”。
  • get_industry_rank 默认只走 eastmoney,东财 push2 请求统一经 EastmoneyProvider._get(),复用 provider limiter;当前网络下 push2 断连时可切换同源 push2delay
  • get_concept_blocks 默认链路为 baidu -> thsdk;百度返回 403 或不可用时,thsdk 问财仅作为动态字段 fallback,返回行业、概念、地域、concept_tags 和 raw,gateway 必须返回 warning。
  • get_hot_reason 默认链路为 ths_hot -> thsdkths_hot 有独立串行 limiter,thsdk 仅作为 wencai 动态字段 fallback,gateway 必须返回 warning。

验收:

  • 板块成分股优先 THS。
  • 行业排名优先东财。
  • 个股概念归属优先百度;百度不可用时允许 thsdk 问财动态 fallback,并显式 warning。

Phase 5:研报、估值、财报

目标:支持基本面研究。

实现:

  • get_reports
  • download_report
  • get_eps_forecast
  • get_financial_statement
  • get_valuation

Provider:

  • EastmoneyProvider
  • ThsdkProvider
  • SinaProvider
  • TencentProvider

当前实现状态(2026-06-05):

  • 已实现 download_reportget_eps_forecastget_financial_statementget_valuation,并补充 get_reports 的 EPS 预测字段。
  • get_reportsdownload_report、东财估值补充都复用 EastmoneyProvider._get(),因此继续经过 provider limiter。
  • download_report 默认不写文件,只返回 PDF bytes;只有调用者显式传 target_dir 时才落盘。
  • get_eps_forecast 默认链路为 eastmoney -> thsdk;东财研报无 EPS 字段或无机构覆盖时可返回空列表,不视为异常。thsdk 仅作为问财动态字段 fallback,gateway 必须返回 warning。
  • get_financial_statement 默认只走 sina,三表返回 report_type/rows/raw,并将新浪横向期间表展平成 period/item/value/raw 行,不与 get_finance_snapshot 混合。
  • get_valuation 默认链路为 tencent -> eastmoney,返回 PE/PB/市值/换手等 quote 派生估值字段并保留 raw。

验收:

  • 不引入 iwencai OpenAPI,不读取 API Key,不实现依赖 X-Claw-* 头的 provider。
  • 主题研报探索如需自然语言入口,走 query_wencai 返回动态字段和 raw,不作为强 schema 研报搜索接口。
  • 一致预期没有机构覆盖时返回空结果,不算异常。
  • 三表字段不强行和财务快照混合。

Phase 6:问财与组合分析

目标:提供无 API Key 的自然语言和批量分析入口。

实现:

  • query_wencai
  • query_wencai_base
  • screen_stocks
  • compare_stocks
  • normalize_trend
  • correlation_matrix

Provider:

  • ThsdkProvider

当前实现状态(2026-06-05):

  • 已实现 query_wencaiquery_wencai_basescreen_stockscompare_stocksnormalize_trendcorrelation_matrix
  • query_wencai 调用 thsdk.wencai_nlpquery_wencai_base 优先调用 thsdk.wencai_base,如 thsdk base 模式返回失败,可在同一 provider 内退回 wencai_nlp 动态字段;不接入 iwencai OpenAPI,不读取 API Key,不构造 X-Claw-* 头。
  • ThsdkProvider 对问财调用做串行限速,避免连续 wencai_nlp / wencai_base 触发 thsdk 官方 250ms 间隔限制。
  • 问财返回使用动态 schema 标记,并保留 raw;screen_stocks 仅包装自然语言选股结果,不把动态字段强行固定。
  • compare_stocksnormalize_trendcorrelation_matrix 基于已有 get_quote / get_kline 显式方法逐只调用,聚合子调用的 fallback_chain 和 warnings,避免把沪深市场混入 THS 单次批量请求。

验收:

  • 问财只走 thsdk.wencai_nlp / thsdk.wencai_base,不接入 iwencai OpenAPI。
  • 问财字段动态,不能硬编码 schema。
  • screen_stocks 是业务包装,保留原始问财返回。
  • thsdk 不可用时返回明确 provider 不可用错误,不要求用户配置 API Key。

交易日历

当前实现状态(2026-06-05):

  • 已实现 is_trading_day(date, provider="auto"),默认 provider 为 timor
  • TimorProvider 调用 https://timor.tech/api/holiday/info/{date} 判断中国节假日,返回 date/is_trading_day/type/name/week/holiday/raw
  • is_trading_day 只把普通周一至周五工作日判为 A 股交易日;周末、法定节假日和调休上班日均不直接视为 A 股开市日,避免把中国工作日口径误当交易所日历。
  • 真实数据源 demo 已加入 is_trading_day,使用 --date 参数控制查询日期。

验收:

  • timor 返回失败或被风控时,gateway 返回明确 provider 失败,不用本地 weekday 规则冒充交易所日历。
  • 返回保留 raw,便于核对节假日 API 原始口径。

Phase 7:多市场(低优先级,暂缓)

目标:把 thsdk 多市场能力纳入;当前优先级低于 A 股指数、板块、题材、财务和问财能力。

实现:

  • get_hk_quote
  • get_us_quote
  • get_uk_quote
  • get_forex_quote
  • get_future_quote
  • get_bond_quote
  • get_fund_quote
  • get_option_data
  • list_market

Provider:

  • ThsdkProvider

验收:

  • 多市场统一返回行情 schema。
  • 保留 raw 字段。
  • 没有 fallback 的能力明确标记 provider 唯一。

示例

gateway = StockDataGateway()

kline = gateway.get_kline("300498", interval="1m", count=260)
quote = gateway.get_quote("300498")
depth = gateway.get_depth("300498")
news = gateway.get_stock_news("300498", limit=10)
flow = gateway.get_fund_flow("300498", period="minute")
anns = gateway.get_announcements("300498", limit=20)

指定 provider:

kline = gateway.get_kline("300498", interval="1m", provider="thsdk", fallback=False)
quote = gateway.get_quote("300498", provider="tencent")

查看降级链:

result = gateway.get_kline("300498", interval="1m")
print(result.provider)
print(result.warnings)
print(result.fallback_chain)

测试策略

单元测试

  • SymbolResolver 各种代码格式转换。
  • normalizer 字段转换。
  • fallback runner 成功/失败路径。
  • rate limiter 是否生效。

集成测试

  • get_kline("300498", interval="1m")
  • get_quote("300498")
  • get_depth("300498")
  • get_stock_news("300498")
  • get_announcements("300498")

降级测试

  • 模拟 thsdk 超时,确认降级到 mootdx。
  • 模拟东财 429,确认 provider 冷却。
  • 模拟个股新闻东财失败,确认降级到韭研公社,不降级到全市场快讯。
  • 模拟巨潮失败,确认 F10 fallback 有 warning。

数据校验

  • K线字段必须包含标准 schema。
  • time 必须可解析。
  • open/high/low/close 必须为数值。
  • volume/amount 必须为数值。
  • quote 至少包含 symbol/name/price

风险与约束

  1. thsdk 权限可能限制某些专业数据。
  2. mootdx 在海外网络可能超时。
  3. 东方财富接口有风控,必须限流。
  4. 问财返回字段动态,不适合强 schema。
  5. 不同 provider 的复权、分钟边界、成交量单位可能不同,必须通过 warnings 暴露。
  6. 图表图片是资源接口,不等同于结构化 K线。
  7. 全市场快讯不能冒充个股新闻。

开放问题

  1. 是否需要默认缓存?首版建议不做持久缓存,只保留 provider health。
  2. 是否需要异步版本?首版建议同步接口,后续可加 AsyncStockDataGateway
  3. 是否需要 CLI?首版不需要。
  4. 是否需要把所有 provider 原始数据落盘?首版仅返回 raw,不自动落盘。

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stock_gateway-0.1.0.tar.gz (67.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stock_gateway-0.1.0-py3-none-any.whl (39.7 kB view details)

Uploaded Python 3

File details

Details for the file stock_gateway-0.1.0.tar.gz.

File metadata

  • Download URL: stock_gateway-0.1.0.tar.gz
  • Upload date:
  • Size: 67.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for stock_gateway-0.1.0.tar.gz
Algorithm Hash digest
SHA256 686c64b688681e42bf95034a4e46af358f40ca1399fb00881e42e4f8dc55f144
MD5 4fbcb75789337e24195699c5ba8e62fd
BLAKE2b-256 7a6106c0ae74d195475211bbd8412c69a6b0714471ed89b97af0b4c4ab504d7f

See more details on using hashes here.

File details

Details for the file stock_gateway-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: stock_gateway-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 39.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for stock_gateway-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 db287646a14a0c07fbbb57f0713c1615c029d556d57aabc9fcf34303e3a31390
MD5 dceed74dcc527891797dfb1f1ab9c473
BLAKE2b-256 6a74cbc361c8673e9225b2b6ddc5b48e6a327eace91366c126b8e1d13a358383

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page