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 安装包含 requests、pandas、thsdk、mootdx、lxml 等 provider 运行所需包;如果某个真实 provider 在本机不可用,测试仍应通过 fake/mock provider 覆盖网关和 schema 行为。
设计目标
- 对外 API 必须显式,例如
get_kline()、get_quote()、get_stock_news(),不使用gateway.get(capability="...")这种动态入口。 - 每个方法内部可以有多个 provider,并按策略自动降级。
- 自动降级只允许发生在语义等价或兼容的数据源之间。
- 每次降级必须记录
fallback_chain,不能静默换源。 - 所有返回结果使用统一
GatewayResult包裹,包含数据、provider、schema、warnings、耗时和错误。 - 常用数据统一字段 schema,保留
raw或 provider 原始结果以便调试。 - 东方财富等有风控的数据源统一限流,不允许并发打接口。
- 可以分期实现,首期覆盖高频行情,后续逐步接入资金、公告、财务、研报、板块和问财。
非目标
- 首版不做动态插件市场。
- 首版不做跨进程缓存服务。
- 首版不做异步任务队列。
- 首版不把所有数据强行合并成一个大 schema。
- 首版不把不等价数据做静默替代,例如“个股新闻”不自动降级到“全市场快讯”。
- 不接入
iwencai OpenAPI,不要求 API Key 或X-Claw-*头;问财能力统一通过thsdk的wencai_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_klineget_quoteget_depthget_intradayget_tickget_big_orderget_call_auctionscan_call_auction_anomalyget_index_quoteget_index_klineget_index_listget_sector_listget_sector_quoteget_sector_constituentsget_eps_forecast的动态问财 fallbackquery_wencaiquery_wencai_baseget_global_news- 多市场 quote/list 方法暂缓接入
MootdxProvider
支持:
get_klineget_quoteget_depthget_intradayget_transactionget_finance_snapshotget_f10get_latest_notice_summaryget_index_kline
TencentProvider
支持:
get_quoteget_index_quoteget_stock_info的部分字段get_valuation
EastmoneyProvider
支持:
get_stock_newsget_global_newsget_fund_flowget_fund_flow_120dget_marginget_block_tradeget_holder_numget_dividendget_lockup_expiryget_dragon_tigerget_daily_dragon_tigerget_industry_rankget_stock_infoget_reportsget_eps_forecastdownload_reportget_valuation
所有 Eastmoney 请求必须经过限流器。
SinaProvider
支持:
get_quoteget_financial_statementget_chart_image
BaiduProvider
支持:
get_klineget_concept_blocksget_sector_constituents的部分 fallback
SohuProvider
支持:
get_kline的历史日线 fallback
CninfoProvider
支持:
get_announcements
JiuyangongsheProvider
支持:
get_stock_newsfallback
XuanGuBaoProvider
支持:
get_global_newsfallback
Jin10Provider
支持:
get_global_newsfallback,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:基础框架
目标:建立可扩展骨架。
实现:
GatewayResultSymbolResolverGatewayError错误体系FallbackRunnerRateLimiterProviderHealthBaseProvider- 基础测试框架
验收:
- 可以实例化
StockDataGateway。 - 可以注册 provider。
- 可以模拟 provider 失败并进入 fallback。
Phase 1:高频行情 MVP
目标:支撑日常看盘和分析。
实现:
get_klineget_quoteget_depthget_intradayget_transactionget_chart_image
Provider:
ThsdkProviderMootdxProviderTencentProviderSinaProviderBaiduProviderSohuProvider
验收:
gateway.get_kline("300498", interval="1m")可返回标准 K线 schema。- thsdk 异常时可降级到 mootdx。
gateway.get_quote("300498")优先腾讯,失败后降级。- 1m K线降级到 mootdx 时包含 09:30 差异 warning。
Phase 2:新闻、公告、资金
目标:覆盖个股研究最常用信息面。
实现:
get_stock_newsget_global_newsget_announcementsget_fund_flowget_fund_flow_120dget_finance_snapshotget_stock_info
当前实现状态(2026-06-04):
- 已实现
get_stock_news、get_global_news、get_announcements、get_fund_flow、get_fund_flow_120d、get_finance_snapshot、get_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:
EastmoneyProviderCninfoProviderMootdxProviderJiuyangongsheProvider
验收:
- 东财请求走限流器。
- 个股新闻不默认降级到全市场快讯。
- 公告全文优先巨潮,F10 fallback 必须带 warning。
Phase 3:资金面、筹码、事件
目标:补齐 a-stock-data 独有事件数据。
实现:
get_marginget_block_tradeget_holder_numget_dividendget_lockup_expiryget_dragon_tigerget_daily_dragon_tiger
当前实现状态(2026-06-04):
- 已实现
get_margin、get_block_trade、get_holder_num、get_dividend、get_lockup_expiry、get_dragon_tiger、get_daily_dragon_tiger。 - 东财 datacenter 请求统一经
EastmoneyProvider._get(),复用 provider limiter。 get_dragon_tiger与get_daily_dragon_tiger默认链路为eastmoney -> thsdk;thsdk 仅作为 wencai 动态字段 fallback,gateway 必须返回 warning。get_daily_dragon_tiger在非交易日或盘后未更新时返回空列表和 note,不把无数据当作异常。- 金额字段统一保留为元,展示层按需换算万元/亿元。
Provider:
EastmoneyProviderThsdkProvider的 wencai fallback。
验收:
- 金额字段统一为元。
- 展示层可自行换算万元/亿元。
- 非交易日无龙虎榜不算异常。
Phase 4:板块、指数、题材
目标:支持行业轮动、概念研究和成分股分析。
实现:
get_index_quoteget_index_klineget_index_listget_sector_listget_sector_quoteget_sector_constituentsget_industry_rankget_concept_blocksget_hot_reason
Provider:
ThsdkProviderEastmoneyProviderBaiduProviderThsHotProvider
当前实现状态(2026-06-05):
- 已实现
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。 get_index_quote/get_index_kline会把常见指数代码映射到 thsdk 真实内码,例如000001 -> USHI1A0001、000300 -> USHI1B0300、399001 -> 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 -> thsdk;ths_hot有独立串行 limiter,thsdk仅作为 wencai 动态字段 fallback,gateway 必须返回 warning。
验收:
- 板块成分股优先 THS。
- 行业排名优先东财。
- 个股概念归属优先百度;百度不可用时允许 thsdk 问财动态 fallback,并显式 warning。
Phase 5:研报、估值、财报
目标:支持基本面研究。
实现:
get_reportsdownload_reportget_eps_forecastget_financial_statementget_valuation
Provider:
EastmoneyProviderThsdkProviderSinaProviderTencentProvider
当前实现状态(2026-06-05):
- 已实现
download_report、get_eps_forecast、get_financial_statement、get_valuation,并补充get_reports的 EPS 预测字段。 get_reports、download_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_wencaiquery_wencai_basescreen_stockscompare_stocksnormalize_trendcorrelation_matrix
Provider:
ThsdkProvider
当前实现状态(2026-06-05):
- 已实现
query_wencai、query_wencai_base、screen_stocks、compare_stocks、normalize_trend、correlation_matrix。 query_wencai调用thsdk.wencai_nlp;query_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_stocks、normalize_trend、correlation_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_quoteget_us_quoteget_uk_quoteget_forex_quoteget_future_quoteget_bond_quoteget_fund_quoteget_option_datalist_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。
风险与约束
- thsdk 权限可能限制某些专业数据。
- mootdx 在海外网络可能超时。
- 东方财富接口有风控,必须限流。
- 问财返回字段动态,不适合强 schema。
- 不同 provider 的复权、分钟边界、成交量单位可能不同,必须通过 warnings 暴露。
- 图表图片是资源接口,不等同于结构化 K线。
- 全市场快讯不能冒充个股新闻。
开放问题
- 是否需要默认缓存?首版建议不做持久缓存,只保留 provider health。
- 是否需要异步版本?首版建议同步接口,后续可加
AsyncStockDataGateway。 - 是否需要 CLI?首版不需要。
- 是否需要把所有 provider 原始数据落盘?首版仅返回
raw,不自动落盘。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
686c64b688681e42bf95034a4e46af358f40ca1399fb00881e42e4f8dc55f144
|
|
| MD5 |
4fbcb75789337e24195699c5ba8e62fd
|
|
| BLAKE2b-256 |
7a6106c0ae74d195475211bbd8412c69a6b0714471ed89b97af0b4c4ab504d7f
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db287646a14a0c07fbbb57f0713c1615c029d556d57aabc9fcf34303e3a31390
|
|
| MD5 |
dceed74dcc527891797dfb1f1ab9c473
|
|
| BLAKE2b-256 |
6a74cbc361c8673e9225b2b6ddc5b48e6a327eace91366c126b8e1d13a358383
|