Metadata-Version: 2.4
Name: stock-gateway
Version: 0.2.0
Summary: A-share data aggregation SDK with explicit gateway APIs.
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: pandas>=1.3.0
Requires-Dist: requests>=2.31.0
Requires-Dist: thsdk>=1.7.18
Requires-Dist: mootdx>=0.11.7
Requires-Dist: lxml>=6.1.1
Provides-Extra: docs
Requires-Dist: mkdocs>=1.6.0; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.25.0; extra == "docs"

# Stock Gateway

A 股数据采集、聚合、研究和复盘辅助 Python SDK。项目对外提供显式网关方法，例如 `get_quote()`、`get_kline()`、`get_depth()`，调用方无需直接关心底层数据源 provider。

> 本 SDK 用于数据获取和研究辅助，不提供投资建议，也不是自动交易系统。

## 安装

```bash
pip install stock-gateway
```

指定版本：

```bash
pip install stock-gateway==0.2.0
```

运行环境：

- Python `>=3.11`
- 默认依赖：`pandas`、`requests`、`thsdk`、`mootdx`、`lxml`
- 使用 `thsdk` provider 时建议配置 `THS_USERNAME`、`THS_PASSWORD`；可选配置 `THS_MAC`

## 快速开始

```python
from stock_gateway import StockGateway

gateway = StockGateway()

quote = gateway.get_quote("300498")
print(quote.ok, quote.provider, quote.error)
print(quote.data_type)          # quote_list
print(quote.data[0]["price"])   # 实体支持 dict 风格访问
print(quote.data.to_frame())    # 需要 DataFrame 时显式转换

kline = gateway.get_kline("300498", interval="day", count=120)
print(kline.ok, kline.provider, kline.error)
print(kline.data.to_frame())
```

使用完真实 provider 后，可以关闭 provider 持有的连接：

```python
for provider in gateway.providers.values():
    close = getattr(provider, "close", None) or getattr(provider, "disconnect", None)
    if callable(close):
        close()
```

## 返回值

所有接口返回 `GatewayResult`：

| 字段 | 含义 |
| --- | --- |
| `ok` | 请求是否成功 |
| `data` | 标准化后的实体、实体列表或实体表格 |
| `data_type` | 实体类型，例如 `quote_list`、`kline_series` |
| `provider` | 实际命中的数据源 |
| `schema` | 标准化字段 schema |
| `warnings` | 警告信息，例如兼容降级说明 |
| `fallback_chain` | provider 尝试和降级链路 |
| `error` | 失败时的错误信息 |
| `raw` | provider 原始结果，便于调试 |
| `symbol` / `normalized_symbol` | 输入代码和标准化代码信息 |
| `latency_ms` | 最终命中 provider 的耗时 |

示例：

```python
result = gateway.get_quote(["600519", "300498"])

if result.ok:
    print(result.data[0]["price"])
    print(result.data.to_frame())
else:
    print(result.error)

for attempt in result.fallback_chain:
    print(attempt.provider, attempt.status, attempt.error_type, attempt.error)
```

列表型结果支持 `result.data[0]["field"]`、`result.data.to_dicts()` 和 `result.data.to_frame()`；对象型结果支持 `result.data["field"]` 和 `result.data.to_dict()`；组合分析矩阵支持 `result.data.to_frame()`。`raw` 始终保留实际命中 provider 的原始或半解析结果。

## 股票代码

常用接口可直接传 6 位 A 股代码：

```python
gateway.get_quote("600519")
gateway.get_kline("300498", interval="1m", count=260)
```

SDK 会在内部转换为各 provider 需要的代码格式，例如腾讯、新浪、东方财富、同花顺和通达信格式。

## Provider 与降级

默认 `provider="auto"`，SDK 会按接口策略选择 provider，并在语义兼容时自动降级。降级过程会写入 `fallback_chain`，不会静默换源。

```python
result = gateway.get_kline("300498", interval="day")
print(result.provider)
print([(item.provider, item.status) for item in result.fallback_chain])
```

也可以指定 provider 并关闭降级：

```python
result = gateway.get_quote("300498", provider="tencent", fallback=False)
```

常见 provider 名称包括：

- `thsdk`
- `mootdx`
- `tencent`
- `sina`
- `baidu`
- `eastmoney`
- `cninfo`
- `ths_hot`
- `jiuyangongshe`
- `timor`

## 接口参考

所有方法都返回 `GatewayResult`。成功时 `result.ok=True`，标准化结果放在 `result.data`；失败时 `result.ok=False`，错误摘要放在 `result.error`，已尝试的数据源链路放在 `result.fallback_chain`。

实体都支持 dict 风格访问：

```python
result = gateway.get_quote("300498")

if result.ok:
    first = result.data[0]
    print(first["code"], first["price"])
    print(first.to_dict())
```

列表型实体支持：

```python
result.data[0]           # 第一条实体
result.data.to_dicts()   # list[dict]
result.data.to_frame()   # pandas.DataFrame
```

对象型实体支持：

```python
result.data["field"]
result.data.to_dict()
```

矩阵或表格型实体支持：

```python
result.data.to_frame()
```

### 通用参数

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | A 股股票代码，通常传 6 位代码即可，例如 `"300498"`、`"600519"`。 |
| `codes` | `str | list[str]` | 单个股票代码或股票代码列表。 |
| `provider` | `str` | 指定数据源；默认 `"auto"`，由网关按方法选择默认链路。 |
| `fallback` | `bool` | 是否启用兼容 provider 降级；默认 `True`。指定 provider 且希望只跑该源时设为 `False`。 |
| `interval` | `str` | K 线周期，常用值：`"1m"`、`"5m"`、`"15m"`、`"30m"`、`"60m"`、`"day"`、`"week"`、`"month"`。 |
| `count` | `int` | 返回条数；部分 provider 可能按自身接口能力解释。 |
| `start` / `end` | `str` | 起止日期，建议使用 `YYYY-MM-DD`。 |
| `adjust` | `str` | 复权方式，由 provider 支持情况决定，未指定时使用 provider 默认口径。 |

### 行情与交易数据

| 方法 | 调用示例 | `data_type` | `result.data` | 字段定义 |
| --- | --- | --- | --- | --- |
| `get_quote(codes, provider="auto", fallback=True)` | `gateway.get_quote(["300498", "600519"])` | `quote_list` | `EntityList[Quote]` | 见 `Quote` |
| `get_kline(code, interval="day", count=None, start=None, end=None, adjust=None, provider="auto", fallback=True)` | `gateway.get_kline("300498", interval="day", count=120)` | `kline_series` | `EntityList[KlineBar]` | 见 `KlineBar` |
| `get_depth(code, provider="auto", fallback=True)` | `gateway.get_depth("300498")` | `depth_quote` | `DepthQuote` | 见 `DepthQuote`、`PriceLevel` |
| `get_intraday(code, date=None, provider="auto", fallback=True)` | `gateway.get_intraday("300498", date="2026-06-14")` | `intraday_series` | `EntityList[IntradayPoint]` | 见 `IntradayPoint` |
| `get_transaction(code, date=None, provider="auto", fallback=True)` | `gateway.get_transaction("300498")` | `transaction_ticks` | `EntityList[TransactionTick]` | 见 `TransactionTick` |
| `get_chart_image(code, period="daily", provider="auto", fallback=True)` | `gateway.get_chart_image("300498", period="daily")` | `chart_image` | `ChartImage` | 见 `ChartImage` |

默认 provider 链路：

| 方法 | 默认链路 |
| --- | --- |
| `get_quote` | `tencent -> thsdk -> mootdx -> sina` |
| `get_kline` 分钟线 | `thsdk -> mootdx` |
| `get_kline` 日/周/月线 | `thsdk -> mootdx -> baidu` |
| `get_depth` | `thsdk -> mootdx` |
| `get_intraday` | `thsdk -> mootdx` |
| `get_transaction` | `mootdx -> thsdk` |
| `get_chart_image` | `sina` |

### 新闻、公告与资讯

| 方法 | 调用示例 | `data_type` | `result.data` | 字段定义 |
| --- | --- | --- | --- | --- |
| `get_stock_news(code, limit=20, provider="auto", fallback=True)` | `gateway.get_stock_news("300498", limit=10)` | `stock_news_list` | `EntityList[NewsItem]` | 见 `NewsItem` |
| `get_global_news(limit=50, provider="auto", fallback=True)` | `gateway.get_global_news(limit=20)` | `global_news_list` | `EntityList[NewsItem]` | 见 `NewsItem` |
| `get_announcements(code, limit=30, provider="auto", fallback=True)` | `gateway.get_announcements("300498", limit=10)` | `announcement_list` | `EntityList[Announcement]` | 见 `Announcement` |

默认 provider 链路：

| 方法 | 默认链路 | 说明 |
| --- | --- | --- |
| `get_stock_news` | `eastmoney -> jiuyangongshe` | `jiuyangongshe` 当前为预留 provider。 |
| `get_global_news` | `thsdk -> eastmoney` | 全市场快讯与个股新闻不是完全相同语义。 |
| `get_announcements` | `cninfo -> mootdx` | `mootdx` 是 F10 摘要 fallback，不等价于巨潮全文公告，命中时会写入 `warnings`。 |

### 资金、筹码与交易异动

| 方法 | 调用示例 | `data_type` | `result.data` | 字段定义 |
| --- | --- | --- | --- | --- |
| `get_fund_flow(code, period="minute", provider="auto", fallback=True)` | `gateway.get_fund_flow("300498", period="minute")` | `fund_flow_series` | `EntityList[FundFlowPoint]` | 见 `FundFlowPoint` |
| `get_fund_flow_120d(code, provider="auto", fallback=True)` | `gateway.get_fund_flow_120d("300498")` | `fund_flow_series` | `EntityList[FundFlowPoint]` | 见 `FundFlowPoint` |
| `get_margin(code, provider="auto", fallback=True)` | `gateway.get_margin("300498")` | `margin_records` | `EntityList[MarginRecord]` | 见 `MarginRecord` |
| `get_block_trade(code, provider="auto", fallback=True)` | `gateway.get_block_trade("300498")` | `block_trade_records` | `EntityList[BlockTradeRecord]` | 见 `BlockTradeRecord` |
| `get_holder_num(code, provider="auto", fallback=True)` | `gateway.get_holder_num("300498")` | `holder_num_records` | `EntityList[HolderNumRecord]` | 见 `HolderNumRecord` |
| `get_dividend(code, provider="auto", fallback=True)` | `gateway.get_dividend("300498")` | `dividend_records` | `EntityList[DividendRecord]` | 见 `DividendRecord` |
| `get_lockup_expiry(code, forward_days=90, provider="auto", fallback=True)` | `gateway.get_lockup_expiry("300498", forward_days=180)` | `lockup_expiry_result` | `LockupExpiryResult` | 见 `LockupExpiryResult`、`LockupRecord` |
| `get_dragon_tiger(code, trade_date=None, look_back=30, provider="auto", fallback=True)` | `gateway.get_dragon_tiger("300498", look_back=30)` | `dragon_tiger_result` | `DragonTigerResult` | 见 `DragonTigerResult`、`DragonTigerRecord`、`DragonTigerSeat`、`DragonTigerInstitution` |
| `get_daily_dragon_tiger(trade_date=None, provider="auto", fallback=True)` | `gateway.get_daily_dragon_tiger(trade_date="2026-06-14")` | `daily_dragon_tiger_result` | `DailyDragonTigerResult` | 见 `DailyDragonTigerResult`、`DailyDragonTigerStock` |

默认 provider 链路：

| 方法 | 默认链路 |
| --- | --- |
| `get_fund_flow` / `get_fund_flow_120d` | `eastmoney` |
| `get_margin` / `get_block_trade` / `get_holder_num` / `get_dividend` / `get_lockup_expiry` | `eastmoney` |
| `get_dragon_tiger` / `get_daily_dragon_tiger` | `eastmoney -> thsdk` |

`thsdk` 作为龙虎榜 fallback 时来自问财动态字段，命中时会写入 `warnings`，下游如需严格字段请同时检查 `raw`。

### 指数、板块与题材

| 方法 | 调用示例 | `data_type` | `result.data` | 字段定义 |
| --- | --- | --- | --- | --- |
| `get_index_quote(index_code, provider="auto", fallback=True)` | `gateway.get_index_quote("000001")` | `index_quote_list` | `EntityList[IndexQuote]` | 见 `IndexQuote` |
| `get_index_kline(index_code, interval="day", provider="auto", fallback=True)` | `gateway.get_index_kline("000001", interval="day")` | `index_kline_series` | `EntityList[KlineBar]` | 见 `KlineBar` |
| `get_index_list(provider="auto", fallback=True)` | `gateway.get_index_list()` | `index_info_list` | `EntityList[IndexInfo]` | 见 `IndexInfo` |
| `get_sector_list(sector_type="industry", provider="auto", fallback=True)` | `gateway.get_sector_list(sector_type="concept")` | `sector_info_list` | `EntityList[SectorInfo]` | 见 `SectorInfo` |
| `get_sector_quote(sector_code, provider="auto", fallback=True)` | `gateway.get_sector_quote("URFI883404")` | `sector_quote` | `SectorQuote` | 见 `SectorQuote` |
| `get_sector_constituents(sector_code, provider="auto", fallback=True)` | `gateway.get_sector_constituents("URFI883404")` | `sector_constituent_list` | `EntityList[SectorConstituent]` | 见 `SectorConstituent` |
| `get_industry_rank(top_n=20, provider="auto", fallback=True)` | `gateway.get_industry_rank(top_n=20)` | `industry_rank_result` | `IndustryRankResult` | 见 `IndustryRankResult`、`IndustryRankItem` |
| `get_concept_blocks(code, provider="auto", fallback=True)` | `gateway.get_concept_blocks("300498")` | `concept_blocks_result` | `ConceptBlocksResult` | 见 `ConceptBlocksResult`、`ConceptBlock` |
| `get_hot_reason(date=None, provider="auto", fallback=True)` | `gateway.get_hot_reason(date="2026-06-14")` | `hot_reason_result` | `HotReasonResult` | 见 `HotReasonResult`、`HotReasonStock` |

默认 provider 链路：

| 方法 | 默认链路 | 说明 |
| --- | --- | --- |
| `get_index_quote` / `get_index_kline` / `get_index_list` | `thsdk` | 指数代码会在 provider 层转为同花顺内码。 |
| `get_sector_list` | `thsdk -> eastmoney` | 东财 fallback 只覆盖板块基础列表。 |
| `get_sector_quote` / `get_sector_constituents` | `thsdk` | 成分股优先使用 THS 板块成分能力。 |
| `get_industry_rank` | `eastmoney` | 行业涨跌排行。 |
| `get_concept_blocks` | `baidu -> thsdk` | `thsdk` fallback 来自问财动态字段，命中时会写入 `warnings`。 |
| `get_hot_reason` | `ths_hot -> thsdk` | `ths_hot` 有串行限流；`thsdk` fallback 来自问财动态字段。 |

### 交易日历

| 方法 | 调用示例 | `data_type` | `result.data` | 字段定义 |
| --- | --- | --- | --- | --- |
| `is_trading_day(date, provider="auto", fallback=True)` | `gateway.is_trading_day("2026-06-14")` | `trading_day` | `TradingDay` | 见 `TradingDay` |

默认 provider 链路：`timor`。

### 财务、估值与研报

| 方法 | 调用示例 | `data_type` | `result.data` | 字段定义 |
| --- | --- | --- | --- | --- |
| `get_finance_snapshot(code, provider="auto", fallback=True)` | `gateway.get_finance_snapshot("300498")` | `finance_snapshot` | `FinanceSnapshot` | 见 `FinanceSnapshot` |
| `get_stock_info(code, provider="auto", fallback=True)` | `gateway.get_stock_info("300498")` | `stock_info` | `StockInfo` | 见 `StockInfo` |
| `get_reports(code, max_pages=2, provider="auto", fallback=True)` | `gateway.get_reports("300498", max_pages=1)` | `report_list` | `EntityList[ResearchReport]` | 见 `ResearchReport` |
| `download_report(record, target_dir=None, filename=None, provider="auto", fallback=True)` | `gateway.download_report(report.data[0], target_dir="reports")` | `report_download` | `ReportDownload` | 见 `ReportDownload` |
| `get_eps_forecast(code, provider="auto", fallback=True)` | `gateway.get_eps_forecast("300498")` | `eps_forecast_list` | `EntityList[EPSForecast]` | 见 `EPSForecast` |
| `get_financial_statement(code, report_type="income", limit=8, provider="auto", fallback=True)` | `gateway.get_financial_statement("300498", report_type="income", limit=8)` | `financial_statement` | `FinancialStatement` | 见 `FinancialStatement`、`FinancialStatementRow` |
| `get_valuation(code, provider="auto", fallback=True)` | `gateway.get_valuation("300498")` | `valuation` | `Valuation` | 见 `Valuation` |

默认 provider 链路：

| 方法 | 默认链路 | 说明 |
| --- | --- | --- |
| `get_finance_snapshot` | `mootdx -> eastmoney` | 东财 fallback 来自基础资料字段，命中时会写入 `warnings`。 |
| `get_stock_info` | `eastmoney -> tencent` | 腾讯 fallback 主要来自行情扩展字段，命中时会写入 `warnings`。 |
| `get_reports` / `download_report` | `eastmoney` | `ResearchReport.pdf_url` 可由东财 `infoCode` 派生。 |
| `get_eps_forecast` | `eastmoney -> thsdk` | `thsdk` fallback 来自问财动态字段。 |
| `get_financial_statement` | `sina` | `report_type` 常用值：`"income"`、`"balance"`、`"cashflow"`。 |
| `get_valuation` | `tencent -> eastmoney` | provider 字段覆盖度不同，部分字段可能留空。 |

### 问财与组合分析

| 方法 | 调用示例 | `data_type` | `result.data` | 字段定义 |
| --- | --- | --- | --- | --- |
| `query_wencai(query, provider="auto")` | `gateway.query_wencai("300498 所属行业 市盈率")` | `dynamic_query_result` | `DynamicQueryResult` | 见 `DynamicQueryResult`、`DynamicRecord` |
| `query_wencai_base(query, provider="auto")` | `gateway.query_wencai_base("300498 所属行业")` | `dynamic_query_result` | `DynamicQueryResult` | 见 `DynamicQueryResult`、`DynamicRecord` |
| `screen_stocks(query, provider="auto")` | `gateway.screen_stocks("今日涨停，非ST")` | `dynamic_query_result` | `DynamicQueryResult` | 见 `DynamicQueryResult`、`DynamicRecord` |
| `compare_stocks(codes, interval="day", count=30, provider="auto", fallback=True)` | `gateway.compare_stocks(["300498", "600519"], count=30)` | `compare_stocks_result` | `CompareStocksResult` | 见 `CompareStocksResult` |
| `normalize_trend(codes, interval="day", count=30, provider="auto", fallback=True)` | `gateway.normalize_trend(["300498", "600519"], count=30)` | `trend_result` | `TrendResult` | 见 `TrendResult` |
| `correlation_matrix(codes, interval="day", count=30, provider="auto", fallback=True)` | `gateway.correlation_matrix(["300498", "600519"], count=30)` | `correlation_matrix` | `CorrelationMatrix` | 见 `CorrelationMatrix` |

默认 provider 链路：

| 方法 | 默认链路 | 说明 |
| --- | --- | --- |
| `query_wencai` / `query_wencai_base` / `screen_stocks` | `thsdk` | 问财字段由查询语句决定，`DynamicRecord.fields` 保留动态列。 |
| `compare_stocks` | 先调用 `get_quote`，再逐个调用 `get_kline` | 返回行情、收盘价矩阵、归一化走势和相关系数矩阵。 |
| `normalize_trend` / `correlation_matrix` | 逐个调用 `get_kline` | 使用收盘价序列计算。 |

## 返回实体字段

字段表里的类型表示字段有值时的基础类型。由于不同 provider 字段覆盖度不一致，除特别说明外，实体字段在 provider 未返回、字段不可解析或该接口语义不包含该字段时都可能留空。

所有实体都继承通用元数据字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `raw` | `Any` | 命中 provider 的原始行或原始结果片段，用于调试字段变化。 |
| `source_provider` | `str` | 产生该实体的 provider 名称。 |
| `source_semantics` | `str` | 降级后语义不完全一致时的标记，例如 `dynamic_wencai`。 |

### GatewayResult

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `ok` | `bool` | 调用是否成功。 |
| `data` | `Any` | 标准化后的实体、实体列表或实体表格。 |
| `provider` | `str` | 最终成功命中的 provider；失败时通常留空。 |
| `fallback_chain` | `list[ProviderAttempt]` | provider 尝试链路，包含成功和失败记录。 |
| `warnings` | `list[str]` | 降级、语义差异或字段动态性的提示。 |
| `schema` | `list[str] | dict` | 方法声明的稳定字段 schema。 |
| `symbol` | `str` | 调用方传入的股票代码、指数代码或代码列表字符串。 |
| `normalized_symbol` | `NormalizedSymbol` | 单只股票接口的标准化代码对象。 |
| `latency_ms` | `float` | 最终成功 provider 的耗时，单位毫秒。 |
| `raw` | `Any` | 最终成功 provider 的原始输出。 |
| `error` | `str` | 失败时的错误摘要。 |
| `data_type` | `str` | 标准化结果类型标识。 |

### ProviderAttempt

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `provider` | `str` | 本次尝试的数据源名称。 |
| `status` | `str` | 尝试状态，例如 `"success"` 或 `"failed"`。 |
| `latency_ms` | `float` | 本次 provider 调用耗时。 |
| `error_type` | `str` | 失败异常类型。 |
| `error` | `str` | 失败详情。 |
| `warnings` | `list[str]` | provider 尝试级别的提示。 |

### NormalizedSymbol

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `raw` | `str` | 用户传入的原始代码。 |
| `code` | `str` | 6 位标准股票代码。 |
| `market` | `str` | 市场标记，常见值为 `sh`、`sz`、`bj`。 |
| `ths` / `ths_code` | `str` | 同花顺 provider 使用的代码格式。 |
| `tencent` / `tencent_code` | `str` | 腾讯接口使用的代码格式。 |
| `sina` / `sina_code` | `str` | 新浪接口使用的代码格式。 |
| `eastmoney_secid` | `str` | 东方财富 secid 格式。 |
| `mootdx` / `tdx_code` | `str` | mootdx / 通达信使用的代码格式。 |

### 行情实体

#### Quote

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 6 位股票代码。 |
| `market` | `str` | 市场标记。 |
| `name` | `str` | 股票名称。 |
| `price` | `float` | 最新价。 |
| `pre_close` | `float` | 昨收价。 |
| `open` | `float` | 开盘价。 |
| `high` | `float` | 最高价。 |
| `low` | `float` | 最低价。 |
| `volume` | `float` | 成交量，单位取决于 provider 原始口径。 |
| `amount` | `float` | 成交额，单位取决于 provider 原始口径。 |
| `datetime` | `str` | 行情时间。 |
| `change` | `float` | 涨跌额。 |
| `change_pct` | `float` | 涨跌幅百分值。 |
| `turnover_pct` | `float` | 换手率百分值。 |
| `pe_ttm` | `float` | 滚动市盈率。 |
| `pb` | `float` | 市净率。 |
| `mcap` | `float` | 总市值。 |
| `float_mcap` | `float` | 流通市值。 |

#### KlineBar

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `datetime` | `str` | K 线时间，日线通常为日期，分钟线通常为日期时间。 |
| `open` | `float` | 开盘价。 |
| `high` | `float` | 最高价。 |
| `low` | `float` | 最低价。 |
| `close` | `float` | 收盘价。 |
| `volume` | `float` | 成交量。 |
| `amount` | `float` | 成交额。 |
| `interval` | `str` | 请求的 K 线周期。 |
| `adjust` | `str` | 复权方式。 |
| `code` | `str` | 股票或指数代码。 |
| `market` | `str` | 市场标记。 |

#### DepthQuote 与 PriceLevel

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `DepthQuote.code` | `str` | 股票代码。 |
| `DepthQuote.market` | `str` | 市场标记。 |
| `DepthQuote.bids` | `list[PriceLevel]` | 买盘档位，通常按买一、买二顺序排列。 |
| `DepthQuote.asks` | `list[PriceLevel]` | 卖盘档位，通常按卖一、卖二顺序排列。 |
| `DepthQuote.datetime` | `str` | 盘口时间。 |
| `PriceLevel.price` | `float` | 档位价格。 |
| `PriceLevel.volume` | `float` | 档位挂单量。 |
| `PriceLevel.orders` | `int` | 档位订单数；provider 不提供时留空。 |

#### IntradayPoint

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `time` | `str` | 分时时间。 |
| `price` | `float` | 分时价格。 |
| `volume` | `float` | 分时成交量。 |
| `amount` | `float` | 分时成交额。 |
| `avg_price` | `float` | 均价。 |
| `leading_indicator` | `float` | 领先指标；provider 不提供时留空。 |

#### TransactionTick

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `time` | `str` | 成交时间。 |
| `price` | `float` | 成交价格。 |
| `volume` | `float` | 成交量。 |
| `amount` | `float` | 成交额。 |
| `side` | `str` | 买卖方向或 provider 原始方向标记。 |
| `trade_count` | `int` | 成交笔数；provider 不提供时留空。 |

#### ChartImage

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 股票代码。 |
| `market` | `str` | 市场标记。 |
| `period` | `str` | 图像周期。 |
| `content` | `bytes` | 图像二进制内容。 |
| `mime_type` | `str` | MIME 类型，默认 `image/gif`。 |
| `url` | `str` | 图像来源 URL；provider 未返回时留空。 |

### 新闻与公告实体

#### NewsItem

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `time` | `str` | 新闻或快讯时间。 |
| `source` | `str` | 来源媒体或 provider 来源名。 |
| `title` | `str` | 标题。 |
| `summary` | `str` | 摘要。 |
| `url` | `str` | 正文链接。 |
| `symbols` | `list[str]` | 关联股票代码。 |
| `relevance` | `float` | 相关度；provider 不提供时留空。 |
| `category` | `str` | 新闻分类。 |

#### Announcement

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `date` | `str` | 公告日期。 |
| `title` | `str` | 公告标题。 |
| `type` | `str` | 公告类型。 |
| `url` | `str` | 公告详情 URL；巨潮公告可由 `announcementId` 派生。 |
| `summary` | `str` | 摘要；F10 fallback 可能只提供摘要。 |
| `is_full_text` | `bool` | 是否是全文公告来源；巨潮通常为 `True`，mootdx F10 摘要通常为 `False`，未知时留空。 |

### 资金与事件实体

#### FundFlowPoint

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `time` | `str` | 分钟级资金流时间。 |
| `date` | `str` | 日级资金流日期。 |
| `period_type` | `str` | 周期类型，例如分钟或日级。 |
| `main_net` | `float` | 主力净流入。 |
| `super_net` | `float` | 超大单净流入。 |
| `large_net` | `float` | 大单净流入。 |
| `mid_net` | `float` | 中单净流入。 |
| `small_net` | `float` | 小单净流入。 |

#### MarginRecord

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `date` | `str` | 交易日期。 |
| `rzye` | `float` | 融资余额。 |
| `rzmre` | `float` | 融资买入额。 |
| `rzche` | `float` | 融资偿还额。 |
| `rqye` | `float` | 融券余额。 |
| `rqmcl` | `float` | 融券卖出量。 |
| `rqchl` | `float` | 融券偿还量。 |
| `rzrqye` | `float` | 融资融券余额。 |

#### BlockTradeRecord

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `date` | `str` | 交易日期。 |
| `price` | `float` | 大宗交易成交价。 |
| `close` | `float` | 当日收盘价。 |
| `premium_pct` | `float` | 溢价率百分值；provider 未提供时留空。 |
| `volume` | `float` | 成交量。 |
| `amount` | `float` | 成交额。 |
| `buyer` | `str` | 买方营业部。 |
| `seller` | `str` | 卖方营业部。 |

#### HolderNumRecord

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `date` | `str` | 截止日期。 |
| `holder_num` | `float` | 股东户数。 |
| `change_num` | `float` | 户数变化。 |
| `change_ratio` | `float` | 户数变化比例。 |
| `avg_shares` | `float` | 户均持股数。 |

#### DividendRecord

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `date` | `str` | 除权除息日期或方案日期。 |
| `bonus_rmb` | `float` | 税前现金分红金额。 |
| `transfer_ratio` | `float` | 转增比例。 |
| `bonus_ratio` | `float` | 送股比例。 |
| `plan` | `str` | 分红送转方案进度或摘要。 |

#### LockupExpiryResult 与 LockupRecord

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `LockupExpiryResult.history` | `EntityList[LockupRecord]` | 历史解禁记录。 |
| `LockupExpiryResult.upcoming` | `EntityList[LockupRecord]` | 未来窗口内的解禁记录。 |
| `LockupRecord.date` | `str` | 解禁日期。 |
| `LockupRecord.type` | `str` | 限售股类型。 |
| `LockupRecord.shares` | `float` | 解禁股数。 |
| `LockupRecord.ratio` | `float` | 解禁比例。 |

#### DragonTigerResult

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `records` | `EntityList[DragonTigerRecord]` | 股票上榜记录。 |
| `seats` | `dict[str, EntityList[DragonTigerSeat]]` | 席位明细，常用键为 `buy` 和 `sell`。 |
| `institution` | `DragonTigerInstitution | dict` | 机构买卖汇总。 |

#### DragonTigerRecord

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `date` | `str` | 上榜日期。 |
| `reason` | `str` | 上榜原因。 |
| `net_buy` | `float` | 龙虎榜净买入额。 |
| `turnover` | `float` | 换手率或成交相关比例。 |

#### DragonTigerSeat

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `name` | `str` | 营业部或席位名称。 |
| `buy_amt` | `float` | 买入金额。 |
| `sell_amt` | `float` | 卖出金额。 |
| `net` | `float` | 净买入额。 |

#### DragonTigerInstitution

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `buy_amt` | `float` | 机构买入金额。 |
| `sell_amt` | `float` | 机构卖出金额。 |
| `net_amt` | `float` | 机构净买入金额。 |

#### DailyDragonTigerResult 与 DailyDragonTigerStock

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `DailyDragonTigerResult.date` | `str` | 龙虎榜日期。 |
| `DailyDragonTigerResult.total_records` | `int` | 返回股票数量。 |
| `DailyDragonTigerResult.stocks` | `EntityList[DailyDragonTigerStock]` | 当日上榜股票列表。 |
| `DailyDragonTigerResult.note` | `str` | 非交易日、未更新或 provider 说明。 |
| `DailyDragonTigerStock.code` | `str` | 股票代码。 |
| `DailyDragonTigerStock.name` | `str` | 股票名称。 |
| `DailyDragonTigerStock.reason` | `str` | 上榜原因。 |
| `DailyDragonTigerStock.close` | `float` | 收盘价。 |
| `DailyDragonTigerStock.change_pct` | `float` | 涨跌幅百分值。 |
| `DailyDragonTigerStock.net_buy` | `float` | 净买入额。 |
| `DailyDragonTigerStock.buy` | `float` | 买入额。 |
| `DailyDragonTigerStock.sell` | `float` | 卖出额。 |
| `DailyDragonTigerStock.turnover_pct` | `float` | 换手率百分值。 |

#### HotReasonResult 与 HotReasonStock

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `HotReasonResult.date` | `str` | 热点原因日期。 |
| `HotReasonResult.total_records` | `int` | 返回股票数量。 |
| `HotReasonResult.stocks` | `EntityList[HotReasonStock]` | 热点或涨停原因股票列表。 |
| `HotReasonStock.date` | `str` | 日期。 |
| `HotReasonStock.code` | `str` | 股票代码。 |
| `HotReasonStock.name` | `str` | 股票名称。 |
| `HotReasonStock.reason` | `str` | 热点、涨停或异动原因。 |
| `HotReasonStock.close` | `float` | 收盘价或最新价。 |
| `HotReasonStock.change_pct` | `float` | 涨跌幅百分值。 |
| `HotReasonStock.amount` | `float` | 成交额。 |
| `HotReasonStock.volume` | `float` | 成交量。 |
| `HotReasonStock.turnover_pct` | `float` | 换手率百分值。 |
| `HotReasonStock.big_order_net` | `float` | 大单净额。 |
| `HotReasonStock.market` | `str` | 市场标记。 |

### 指数、板块与题材实体

#### IndexQuote

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 指数代码。 |
| `name` | `str` | 指数名称。 |
| `price` | `float` | 最新点位。 |
| `change` | `float` | 涨跌点数。 |
| `change_pct` | `float` | 涨跌幅百分值。 |
| `volume` | `float` | 成交量。 |
| `amount` | `float` | 成交额。 |
| `high` | `float` | 最高点位。 |
| `low` | `float` | 最低点位。 |

#### IndexInfo

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 指数代码或 THS 指数内码。 |
| `name` | `str` | 指数名称。 |
| `market` | `str` | 市场标记，可由 THS 内码推断。 |

#### SectorInfo

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 板块代码。 |
| `name` | `str` | 板块名称。 |
| `type` | `str` | 板块类型，例如行业或概念。 |

#### SectorQuote

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 板块代码。 |
| `name` | `str` | 板块名称。 |
| `price` | `float` | 板块行情价格或点位。 |
| `change_pct` | `float` | 板块涨跌幅百分值。 |
| `volume` | `float` | 成交量。 |
| `amount` | `float` | 成交额。 |
| `total_mcap` | `float` | 板块总市值。 |
| `float_mcap` | `float` | 板块流通市值。 |
| `up_count` | `int` | 上涨家数。 |
| `down_count` | `int` | 下跌家数。 |
| `leader` | `str` | 领涨股名称。 |
| `leader_change` | `float` | 领涨股涨跌幅。 |

#### SectorConstituent

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 标准股票代码，可由 THS 代码派生。 |
| `name` | `str` | 股票名称。 |
| `market` | `str` | 市场标记，可由 THS 代码派生。 |
| `ths_code` | `str` | THS 成分股代码。 |

#### IndustryRankResult 与 IndustryRankItem

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `IndustryRankResult.top` | `EntityList[IndustryRankItem]` | 涨幅靠前行业。 |
| `IndustryRankResult.bottom` | `EntityList[IndustryRankItem]` | 涨幅靠后行业。 |
| `IndustryRankResult.total` | `int` | 行业总数。 |
| `IndustryRankItem.rank` | `int` | 排名。 |
| `IndustryRankItem.code` | `str` | 行业代码。 |
| `IndustryRankItem.name` | `str` | 行业名称。 |
| `IndustryRankItem.change_pct` | `float` | 行业涨跌幅百分值。 |
| `IndustryRankItem.up_count` | `int` | 上涨家数。 |
| `IndustryRankItem.down_count` | `int` | 下跌家数。 |
| `IndustryRankItem.leader` | `str` | 领涨股名称。 |
| `IndustryRankItem.leader_change` | `float` | 领涨股涨跌幅。 |

#### ConceptBlocksResult 与 ConceptBlock

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `ConceptBlocksResult.industry` | `EntityList[ConceptBlock]` | 所属行业标签。 |
| `ConceptBlocksResult.concept` | `EntityList[ConceptBlock]` | 所属概念标签。 |
| `ConceptBlocksResult.region` | `EntityList[ConceptBlock]` | 地域标签。 |
| `ConceptBlocksResult.concept_tags` | `list[str]` | 便于筛选的概念名称列表。 |
| `ConceptBlock.name` | `str` | 标签名称。 |
| `ConceptBlock.change_pct` | `float | str` | 标签涨跌幅；动态来源可能保留字符串。 |
| `ConceptBlock.desc` | `str` | 标签说明。 |
| `ConceptBlock.type` | `str` | 标签类型，例如 `industry`、`concept`、`region`。 |

### 交易日历实体

#### TradingDay

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `date` | `str` | 日期。 |
| `is_trading_day` | `bool` | 是否为交易日。 |
| `type` | `str` | 日期类型，取决于 provider。 |
| `name` | `str` | 节假日或日期名称。 |
| `week` | `int` | 星期。 |
| `holiday` | `dict` | 节假日详情。 |

### 财务、估值与研报实体

#### FinanceSnapshot

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 股票代码。 |
| `eps` | `float` | 每股收益。 |
| `roe` | `float` | 净资产收益率。 |
| `profit` | `float` | 净利润或 provider 对应利润字段。 |
| `income` | `float` | 营业收入或 provider 对应收入字段。 |

#### StockInfo

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 股票代码。 |
| `name` | `str` | 股票名称。 |
| `industry` | `str` | 所属行业。 |
| `total_shares` | `float` | 总股本。 |
| `float_shares` | `float` | 流通股本。 |
| `mcap` | `float` | 总市值。 |
| `float_mcap` | `float` | 流通市值。 |
| `list_date` | `str` | 上市日期。 |
| `price` | `float` | 最新价或 provider 基础资料中的价格字段。 |

#### ResearchReport

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `title` | `str` | 研报标题。 |
| `publish_date` | `str` | 发布日期。 |
| `org` | `str` | 机构简称。 |
| `rating` | `str` | 评级。 |
| `predict_this_year_eps` | `float` | 当年 EPS 预测。 |
| `predict_next_year_eps` | `float` | 下一年 EPS 预测。 |
| `predict_next_two_year_eps` | `float` | 下两年 EPS 预测。 |
| `pdf_url` | `str` | PDF 下载地址；东财来源可由 `infoCode` 派生。 |

#### ReportDownload

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `url` | `str` | PDF 下载地址。 |
| `filename` | `str` | 文件名。 |
| `path` | `str` | 本地保存路径；未指定 `target_dir` 时留空。 |
| `content` | `bytes` | PDF 二进制内容。 |
| `content_type` | `str` | 内容类型，默认 `application/pdf`。 |

#### EPSForecast

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `year` | `str` | 预测年份。 |
| `institution_count` | `int` | 参与预测机构数。 |
| `mean` | `float` | EPS 预测均值。 |
| `min` | `float` | EPS 预测最小值。 |
| `max` | `float` | EPS 预测最大值。 |

#### FinancialStatement 与 FinancialStatementRow

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `FinancialStatement.code` | `str` | 股票代码。 |
| `FinancialStatement.report_type` | `str` | 报表类型。 |
| `FinancialStatement.rows` | `EntityList[FinancialStatementRow]` | 报表项目行。 |
| `FinancialStatementRow.period` | `str` | 报告期。 |
| `FinancialStatementRow.item` | `str` | 报表项目名称。 |
| `FinancialStatementRow.value` | `float` | 项目金额或数值。 |

#### Valuation

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `str` | 股票代码。 |
| `price` | `float` | 最新价。 |
| `pe_ttm` | `float` | 滚动市盈率。 |
| `pb` | `float` | 市净率。 |
| `mcap` | `float` | 总市值。 |
| `float_mcap` | `float` | 流通市值。 |
| `turnover_pct` | `float` | 换手率百分值。 |

### 问财与组合分析实体

#### DynamicQueryResult 与 DynamicRecord

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `DynamicQueryResult.query` | `str` | 查询语句。 |
| `DynamicQueryResult.mode` | `str` | 查询模式，例如 `screen`。 |
| `DynamicQueryResult.records` | `EntityList[DynamicRecord]` | 动态查询结果行。 |
| `DynamicRecord.fields` | `dict` | provider 返回的动态字段映射。 |

`DynamicRecord` 支持直接用动态列名读取：

```python
result = gateway.query_wencai("300498 所属行业")
first = result.data.records[0]
print(first.fields)
print(first.get("所属行业"))
```

#### CompareStocksResult

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `quotes` | `EntityList[Quote]` | 多只股票实时行情。 |
| `closes` | `EntityTable` | 对齐后的收盘价矩阵，列为股票代码。 |
| `normalized_trend` | `EntityTable` | 以首个有效收盘价为 100 的归一化走势矩阵。 |
| `correlation_matrix` | `EntityTable` | 收盘价收益率相关系数矩阵。 |

#### TrendResult 与 CorrelationMatrix

| 实体 | 类型 | 说明 |
| --- | --- | --- |
| `TrendResult` | `EntityTable` | 多只股票归一化走势表，可通过 `to_frame()` 转为 DataFrame。 |
| `CorrelationMatrix` | `EntityTable` | 多只股票相关系数矩阵，可通过 `to_frame()` 转为 DataFrame。 |

## 环境变量

如果使用默认 `StockGateway()`，部分接口会优先或降级调用 `thsdk`。`thsdk` 连接同花顺行情服务时建议配置账号密码：

```bash
export THS_USERNAME="你的同花顺账号"
export THS_PASSWORD="你的同花顺密码"
```

如果需要固定设备标识，也可以配置：

```bash
export THS_MAC="你的 MAC 地址或 thsdk 要求的设备标识"
```

注意：

- 环境变量名大小写敏感，`thsdk` 默认读取 `THS_USERNAME`、`THS_PASSWORD`、`THS_MAC`。
- 如果部署平台使用 `ths_username`、`ths_password` 作为 secret 名称，请在启动进程前映射为大写环境变量。
- 本 SDK 不接入同花顺问财 OpenAPI，不读取 `API_KEY`，也不构造 `X-Claw-*` 请求头。
- 不要把真实账号、密码、Cookie、Token、Webhook 或本地行情缓存提交到代码仓库。
- 如果上层项目需要代理、缓存目录或日志配置，请在上层项目自行管理。

## 使用注意

- 行情、新闻、公告、财务和研报数据具有时效性，请在使用前重新拉取，并记录数据时间和 provider。
- 东方财富、同花顺热点等数据源可能触发风控，SDK 内部对相关 provider 做了串行限流；上层项目不要并发猛打接口。
- `thsdk`、`mootdx` 和网页数据源可能受本机环境、网络、交易时段和源站可用性影响；失败时优先查看 `fallback_chain` 和 `error`。
- 自动降级只发生在语义等价或兼容的数据源之间；不等价数据不会被静默替代。
- 问财相关接口通过 `thsdk` 承载，返回字段可能随查询语句和源站变化，建议保留 `raw` 便于排查。
