fix: 内外接口拆分,统一封装sdk接口;未测试

master
Lxy 3 months ago
parent 175173de0f
commit fbcb54d23f

@ -0,0 +1,120 @@
# AmazingData Adapter 接口调用关系
## 一、核心适配器接口 (DataSourceAdapter 实现)
| 序号 | 公共接口 | 同步方法 | SDK 调用 | 说明 |
|------|----------|----------|----------|------|
| 1 | `connect` | - | `_market_data.login()` | 连接数据源 |
| 2 | `close` | - | - | 关闭连接 |
| 3 | `subscribe_ticks` | - | - | 订阅实时Tick |
| 4 | `fetch_klines` | `_fetch_klines_sync` | `_market_data.query_kline()` | 获取K线数据 |
| 5 | `fetch_symbols` | `_fetch_symbols_sync` | `_base_data.get_code_list()`<br>`_base_data.get_code_info()`<br>`_base_data.get_future_code_list()`<br>`_base_data.get_future_info()` | 获取标的列表 |
| 6 | `fetch_trading_calendar` | `_fetch_calendar_sync` | `_base_data.get_calendar()` | 获取交易日历 |
| 7 | `health_check` | - | `_market_data.is_login()` | 健康检查 |
## 二、数据获取接口 (基础数据)
| 序号 | 公共接口 | 同步方法 | SDK 调用 | 说明 |
|------|----------|----------|----------|------|
| 8 | `get_adj_factor` | - | `_base_data.get_adj_factor()` | 获取复权因子 |
| 9 | `get_backward_factor` | - | `_base_data.get_backward_factor()` | 获取后复权因子 |
| 10 | `get_code_info` | - | `_base_data.get_code_info()` | 获取代码信息 |
| 11 | `get_trading_calendar` | - | `_base_data.get_calendar()` | 获取交易日历(通用) |
## 三、数据获取接口 (财务/股东数据)
| 序号 | 公共接口 | 同步方法 | SDK 调用 | 说明 |
|------|----------|----------|----------|------|
| 12 | `get_balance_sheet` | - | `_info_data.get_balance_sheet()` | 资产负债表 |
| 13 | `get_cash_flow` | - | `_info_data.get_cash_flow()` | 现金流量表 |
| 14 | `get_income_statement` | - | `_info_data.get_income_statement()` | 利润表 |
| 15 | `get_profit_express` | - | `_info_data.get_profit_express()` | 业绩预告 |
| 16 | `get_profit_notice` | - | `_info_data.get_profit_notice()` | 业绩快报 |
| 17 | `get_top10_shareholders` | - | `_info_data.get_share_holder()` | 前十大股东 |
| 18 | `get_shareholder_count` | - | `_info_data.get_holder_num()` | 股东户数 |
| 19 | `get_equity_structure` | - | `_info_data.get_equity_structure()` | 股本结构 |
## 四、数据获取接口 (市场数据)
| 序号 | 公共接口 | 同步方法 | SDK 调用 | 说明 |
|------|----------|----------|----------|------|
| 20 | `get_snapshot` | - | `_market_data.query_kline()` | 获取快照数据 |
| 21 | `get_index_constituents` | - | `_info_data.get_index_constituent()` | 指数成分股 |
| 22 | `get_index_weights` | - | `_info_data.get_index_weight()` | 指数权重 |
| 23 | `get_margin_summary` | - | `_info_data.get_margin_summary()` | 融资融券汇总 |
| 24 | `get_margin_detail` | - | `_info_data.get_margin_detail()` | 融资融券明细 |
| 25 | `get_longhu_bang` | - | `_info_data.get_long_hu_bang()` | 龙虎榜 |
| 26 | `get_block_trading` | - | `_info_data.get_block_trading()` | 大宗交易 |
## 五、数据获取接口 (基金/可转债)
| 序号 | 公共接口 | 同步方法 | SDK 调用 | 说明 |
|------|----------|----------|----------|------|
| 27 | `get_etf_pcf` | - | `_base_data.get_etf_pcf()` | ETF申购赎回清单 |
| 28 | `get_fund_share` | - | `_info_data.get_fund_share()` | 基金份额 |
| 29 | `get_kzz_issuance` | - | `_info_data.get_kzz_issuance()` | 可转债发行 |
## 六、新增分表数据接口
| 序号 | 公共接口 | 同步方法 | SDK 调用 | 说明 |
|------|----------|----------|----------|------|
| 30 | `fetch_kline_base` | `_fetch_kline_base_sync` | `_market_data.query_kline()` | 获取基础K线数据 |
| 31 | `fetch_kline_quote` | `_fetch_kline_quote_sync` | `_market_data.query_kline()`<br>`_base_data.get_code_info()` | 获取行情指标数据 |
| 32 | `fetch_kline_finance` | `_fetch_kline_finance_sync` | `_market_data.query_kline()`<br>`_info_data.get_equity_structure()` | 获取财务数据 |
| 33 | `fetch_stock_basic_info` | `_fetch_stock_basic_info_sync` | `_base_data.get_code_list()`<br>`_base_data.get_code_info()`<br>`_info_data.get_equity_structure()` | 获取股票基础信息 |
## 七、组合调用详细说明
### 7.1 多次调用同一接口的方法
| 方法 | SDK 调用次数 | 调用详情 |
|------|-------------|----------|
| `fetch_symbols` (stock) | 2次 | 1. `get_code_list()`<br>2. `get_code_info()` |
| `fetch_symbols` (futures) | 2次 | 1. `get_future_code_list()`<br>2. `get_future_info()` |
| `fetch_kline_quote` | 2次 | 1. `query_kline()` - 获取K线<br>2. `get_code_info()` - 获取涨跌停价 |
| `fetch_kline_finance` | 2次 | 1. `get_equity_structure()` - 获取股本<br>2. `query_kline()` - 获取价格计算市值 |
| `fetch_stock_basic_info` | 3次 | 1. `get_code_list()` - 获取代码列表<br>2. `get_code_info()` - 获取名称<br>3. `get_equity_structure()` - 获取上市日期 |
### 7.2 调用链示例
```
fetch_stock_basic_info (async)
└── _fetch_stock_basic_info_sync (sync)
├── _base_data.get_code_list() [第1次SDK调用]
├── _base_data.get_code_info() [第2次SDK调用]
└── _info_data.get_equity_structure() [第3次SDK调用, 循环每个股票]
```
```
fetch_kline_quote (async)
└── _fetch_kline_quote_sync (sync)
├── _market_data.query_kline() [第1次SDK调用, 扩展日期范围]
└── _base_data.get_code_info() [第2次SDK调用, 获取涨跌停价]
```
```
fetch_kline_finance (async)
└── _fetch_kline_finance_sync (sync)
├── _info_data.get_equity_structure() [第1次SDK调用]
└── _market_data.query_kline() [第2次SDK调用]
```
## 八、SDK 对象汇总
| SDK 对象 | 说明 | 主要方法 |
|----------|------|----------|
| `_market_data` | 市场数据 | `login()`, `query_kline()`, `is_login()` |
| `_base_data` | 基础数据 | `get_code_list()`, `get_code_info()`, `get_calendar()`, `get_adj_factor()`, `get_future_code_list()`, `get_etf_pcf()` |
| `_info_data` | 信息数据 | `get_equity_structure()`, `get_share_holder()`, `get_income_statement()`, `get_balance_sheet()`, `get_cash_flow()`, `get_profit_express()`, `get_profit_notice()`, `get_holder_num()`, `get_margin_summary()`, `get_margin_detail()`, `get_long_hu_bang()`, `get_block_trading()`, `get_index_constituent()`, `get_index_weight()`, `get_fund_share()`, `get_kzz_issuance()` |
## 九、接口统计
| 类别 | 接口数量 |
|------|----------|
| 核心适配器接口 | 7 |
| 基础数据接口 | 4 |
| 财务/股东数据接口 | 8 |
| 市场数据接口 | 7 |
| 基金/可转债接口 | 3 |
| 新增分表数据接口 | 4 |
| **总计** | **33** |

@ -0,0 +1,222 @@
# AmazingData Adapter 接口调用关系 (完整版)
## 一、核心适配器接口
### 1.1 fetch_klines / _fetch_klines_sync (最复杂)
```
fetch_klines (async)
└── _fetch_klines_sync (sync)
├── _market_data.query_kline() [第1次SDK调用]
├── _base_data.get_code_info() [第2次SDK调用 - 涨跌停价]
├── _info_data.get_equity_structure() [第3次SDK调用 - 股本结构]
├── _base_data.get_calendar() [第4次SDK调用 - 交易日历]
└── _get_list_date() [内部方法]
├── _base_data.get_code_info() [可能第5次SDK调用]
└── _base_data.get_hist_code_list() [可能第6次SDK调用]
```
| 调用层次 | 方法 | SDK调用次数 | 说明 |
|---------|------|------------|------|
| 外层 | `fetch_klines` | 1 | async入口 |
| 内层 | `_fetch_klines_sync` | 4-6次 | 包含K线+扩展字段获取 |
**总计 SDK 调用**: 4-6 次(取决于 `_get_list_date` 的执行路径)
---
### 1.2 其他核心接口
| 序号 | 公共接口 | 同步方法 | SDK 调用 | 次数 | 说明 |
|------|----------|----------|----------|------|------|
| 1 | `connect` | - | `_market_data.login()` | 1 | 连接数据源 |
| 2 | `close` | - | - | 0 | 关闭连接 |
| 3 | `subscribe_ticks` | - | - | 0 | 订阅实时Tick |
| 4 | `fetch_symbols` | `_fetch_symbols_sync` | `_base_data.get_code_list()`<br>`_base_data.get_code_info()` | 2 | 股票标的列表 |
| 4b| `fetch_symbols` | `_fetch_symbols_sync` | `_base_data.get_future_code_list()`<br>`_base_data.get_future_info()` | 2 | 期货标的列表 |
| 5 | `fetch_trading_calendar` | `_fetch_calendar_sync` | `_base_data.get_calendar()` | 1 | 交易日历 |
| 6 | `health_check` | - | `_market_data.is_login()` | 1 | 健康检查 |
---
## 二、新增分表数据接口 (详细调用链)
### 2.1 fetch_kline_base / _fetch_kline_base_sync
```
fetch_kline_base (async)
└── _fetch_kline_base_sync (sync)
└── _market_data.query_kline() [第1次SDK调用]
```
| 调用层次 | 方法 | SDK调用次数 | 说明 |
|---------|------|------------|------|
| 外层 | `fetch_kline_base` | 1 | async入口 |
| 内层 | `_fetch_kline_base_sync` | 1 | 仅基础K线 |
**总计 SDK 调用**: 1 次
---
### 2.2 fetch_kline_quote / _fetch_kline_quote_sync
```
fetch_kline_quote (async)
└── _fetch_kline_quote_sync (sync)
├── _market_data.query_kline() [第1次SDK调用 - 扩展日期范围]
└── _base_data.get_code_info() [第2次SDK调用 - 涨跌停价]
```
| 调用层次 | 方法 | SDK调用次数 | 说明 |
|---------|------|------------|------|
| 外层 | `fetch_kline_quote` | 2 | async入口 |
| 内层 | `_fetch_kline_quote_sync` | 2 | K线+涨跌停价 |
**总计 SDK 调用**: 2 次
---
### 2.3 fetch_kline_finance / _fetch_kline_finance_sync
```
fetch_kline_finance (async)
└── _fetch_kline_finance_sync (sync)
├── _info_data.get_equity_structure() [第1次SDK调用 - 股本结构]
└── _market_data.query_kline() [第2次SDK调用 - 价格数据]
```
| 调用层次 | 方法 | SDK调用次数 | 说明 |
|---------|------|------------|------|
| 外层 | `fetch_kline_finance` | 2 | async入口 |
| 内层 | `_fetch_kline_finance_sync` | 2 | 股本+价格计算市值 |
**总计 SDK 调用**: 2 次
---
### 2.4 fetch_stock_basic_info / _fetch_stock_basic_info_sync
```
fetch_stock_basic_info (async)
└── _fetch_stock_basic_info_sync (sync)
├── _base_data.get_code_list() [第1次SDK调用 - 代码列表]
├── _base_data.get_code_info() [第2次SDK调用 - 名称/交易所]
└── 循环每个股票:
└── _info_data.get_equity_structure() [第3次SDK调用 - 上市日期]
```
| 调用层次 | 方法 | SDK调用次数 | 说明 |
|---------|------|------------|------|
| 外层 | `fetch_stock_basic_info` | 2+N | async入口N为股票数量 |
| 内层 | `_fetch_stock_basic_info_sync` | 2+N | 基础信息+上市日期 |
**总计 SDK 调用**: 2 + N 次N = 股票数量,每个股票调用一次 get_equity_structure
---
## 三、财务/股东数据接口
| 序号 | 公共接口 | SDK 调用 | 次数 | 说明 |
|------|----------|----------|------|------|
| 7 | `get_balance_sheet` | `_info_data.get_balance_sheet()` | 1 | 资产负债表 |
| 8 | `get_cash_flow` | `_info_data.get_cash_flow()` | 1 | 现金流量表 |
| 9 | `get_income_statement` | `_info_data.get_income_statement()` | 1 | 利润表 |
| 10 | `get_profit_express` | `_info_data.get_profit_express()` | 1 | 业绩预告 |
| 11 | `get_profit_notice` | `_info_data.get_profit_notice()` | 1 | 业绩快报 |
| 12 | `get_top10_shareholders` | `_info_data.get_share_holder()` | 1 | 前十大股东 |
| 13 | `get_shareholder_count` | `_info_data.get_holder_num()` | 1 | 股东户数 |
| 14 | `get_equity_structure` | `_info_data.get_equity_structure()` | 1 | 股本结构 |
---
## 四、市场数据接口
| 序号 | 公共接口 | SDK 调用 | 次数 | 说明 |
|------|----------|----------|------|------|
| 15 | `get_snapshot` | `_market_data.query_kline()` | 1 | 快照数据 |
| 16 | `get_adj_factor` | `_base_data.get_adj_factor()` | 1 | 复权因子 |
| 17 | `get_backward_factor` | `_base_data.get_backward_factor()` | 1 | 后复权因子 |
| 18 | `get_index_constituents` | `_info_data.get_index_constituent()` | 1 | 指数成分股 |
| 19 | `get_index_weights` | `_info_data.get_index_weight()` | 1 | 指数权重 |
| 20 | `get_margin_summary` | `_info_data.get_margin_summary()` | 1 | 融资融券汇总 |
| 21 | `get_margin_detail` | `_info_data.get_margin_detail()` | 1 | 融资融券明细 |
| 22 | `get_longhu_bang` | `_info_data.get_long_hu_bang()` | 1 | 龙虎榜 |
| 23 | `get_block_trading` | `_info_data.get_block_trading()` | 1 | 大宗交易 |
---
## 五、基金/可转债接口
| 序号 | 公共接口 | SDK 调用 | 次数 | 说明 |
|------|----------|----------|------|------|
| 24 | `get_etf_pcf` | `_base_data.get_etf_pcf()` | 1 | ETF申购赎回清单 |
| 25 | `get_fund_share` | `_info_data.get_fund_share()` | 1 | 基金份额 |
| 26 | `get_kzz_issuance` | `_info_data.get_kzz_issuance()` | 1 | 可转债发行 |
---
## 六、接口调用次数汇总表
| 接口方法 | SDK 调用次数 | 调用的 SDK 方法 |
|----------|-------------|----------------|
| `fetch_klines` | **4-6次** | `query_kline` + `get_code_info` (1-2次) + `get_equity_structure` + `get_calendar` + `get_hist_code_list` (可选) |
| `fetch_symbols` (stock) | **2次** | `get_code_list` + `get_code_info` |
| `fetch_symbols` (futures) | **2次** | `get_future_code_list` + `get_future_info` |
| `fetch_trading_calendar` | **1次** | `get_calendar` |
| `fetch_kline_base` | **1次** | `query_kline` |
| `fetch_kline_quote` | **2次** | `query_kline` + `get_code_info` |
| `fetch_kline_finance` | **2次** | `get_equity_structure` + `query_kline` |
| `fetch_stock_basic_info` | **2+N次** | `get_code_list` + `get_code_info` + N×`get_equity_structure` |
| 其他单个数据接口 | **1次** | 对应单个 SDK 方法 |
---
## 七、SDK 对象与方法汇总
### _market_data (市场数据)
| 方法 | 调用次数 | 使用场景 |
|------|---------|----------|
| `login()` | 1 | connect |
| `query_kline()` | 多次 | K线数据获取 |
| `is_login()` | 1 | health_check |
### _base_data (基础数据)
| 方法 | 调用次数 | 使用场景 |
|------|---------|----------|
| `get_code_list()` | 多次 | 股票代码列表 |
| `get_code_info()` | 最频繁 | 代码信息、涨跌停价、上市日期 |
| `get_calendar()` | 多次 | 交易日历 |
| `get_adj_factor()` | 按需 | 复权因子 |
| `get_future_code_list()` | 按需 | 期货代码列表 |
| `get_etf_pcf()` | 按需 | ETF数据 |
| `get_hist_code_list()` | 可选 | 历史代码列表(备选) |
### _info_data (信息数据)
| 方法 | 调用次数 | 使用场景 |
|------|---------|----------|
| `get_equity_structure()` | 最频繁 | 股本结构、上市日期推断 |
| `get_income_statement()` | 按需 | 利润表 |
| `get_balance_sheet()` | 按需 | 资产负债表 |
| `get_cash_flow()` | 按需 | 现金流量表 |
| `get_share_holder()` | 按需 | 股东数据 |
| `get_margin_summary/detail()` | 按需 | 融资融券 |
| `get_index_constituent/weight()` | 按需 | 指数数据 |
---
## 八、接口统计
| 类别 | 接口数量 | 说明 |
|------|----------|------|
| 核心适配器接口 | 6 | connect/close/subscribe_ticks/fetch_klines/fetch_symbols/fetch_trading_calendar/health_check |
| 财务/股东数据接口 | 8 | 各类财务报表和股东数据 |
| 市场数据接口 | 9 | K线、指数、融资融券等 |
| 基金/可转债接口 | 3 | ETF、基金、可转债 |
| 新增分表数据接口 | 4 | 拆分表专用接口 |
| **总计** | **30** | 公共接口方法 |
**调用复杂度分级**:
- ⭐ 简单 (1次SDK调用): fetch_kline_base, get_balance_sheet, get_cash_flow 等
- ⭐⭐ 中等 (2次SDK调用): fetch_symbols, fetch_kline_quote, fetch_kline_finance 等
- ⭐⭐⭐ 复杂 (4-6次SDK调用): **fetch_klines** (原K线获取接口)
- ⭐⭐⭐⭐ 最复杂 (2+N次SDK调用): **fetch_stock_basic_info** (N=股票数量)

@ -0,0 +1,239 @@
# AmazingData Adapter 接口调用关系 (完整修正版)
## 一、接口调用次数汇总表
| 序号 | 接口方法 | SDK 调用次数 | 调用的 SDK 方法 | 复杂度 |
|------|----------|-------------|----------------|--------|
| 1 | `fetch_klines` | **4-6次** | `query_kline` + `get_code_info` + `get_equity_structure` + `get_calendar` + `get_list_date`(内部) | ⭐⭐⭐⭐ |
| 2 | `fetch_stock_basic_info` | **2+N次** | `get_code_list` + `get_code_info` + N×`get_equity_structure` | ⭐⭐⭐⭐⭐ |
| 3 | `fetch_symbols` (stock) | **2次** | `get_code_list` + `get_code_info` | ⭐⭐ |
| 4 | `fetch_symbols` (futures) | **1次** | `get_future_code_list` | ⭐ |
| 5 | `fetch_trading_calendar` | **1次** | `get_calendar` | ⭐ |
| 6 | `fetch_kline_quote` | **2次** | `query_kline` + `get_code_info` | ⭐⭐ |
| 7 | `fetch_kline_finance` | **2次** | `get_equity_structure` + `query_kline` | ⭐⭐ |
| 8 | `fetch_kline_base` | **1次** | `query_kline` | ⭐ |
| 9 | 其他所有数据接口 | **1次** | 对应单个 SDK 方法 | ⭐ |
---
## 二、复杂接口详细调用链
### 2.1 fetch_klines (4-6次SDK调用)
```python
fetch_klines (async)
└── _fetch_klines_sync (sync)
├── _market_data.query_kline() # [第1次] 获取K线数据
├── _base_data.get_code_info() # [第2次] 获取涨跌停价
├── _info_data.get_equity_structure() # [第3次] 获取股本结构
├── _base_data.get_calendar() # [第4次] 获取交易日历
└── _get_list_date() # [内部方法]
├── _base_data.get_code_info() # [第5次可选] 尝试获取上市日期
└── _base_data.get_hist_code_list() # [第6次可选] 备选方案
```
**实际调用**: 最少4次最多6次取决于 `_get_list_date` 的执行路径)
---
### 2.2 fetch_stock_basic_info (2+N次SDK调用)
```python
fetch_stock_basic_info (async)
└── _fetch_stock_basic_info_sync (sync)
├── _base_data.get_code_list() # [第1次] 获取所有股票代码
├── _base_data.get_code_info() # [第2次] 获取股票名称
└── for code in codes: # [循环N次N=股票数量]
└── _info_data.get_equity_structure() # [第3~2+N次] 每个股票调用一次!
```
**⚠️ 警告**: 如果获取全市场5000+只股票,将触发 2+5000 = **5002次** SDK 调用!
**建议**: 使用 `codes` 参数限制股票数量,避免全量查询。
---
### 2.3 fetch_symbols (股票 - 2次SDK调用)
```python
fetch_symbols (async)
└── _fetch_symbols_sync (sync)
├── _base_data.get_code_list() # [第1次] 获取代码列表
└── _base_data.get_code_info() # [第2次] 获取代码信息(名称)
```
---
### 2.4 fetch_symbols (期货 - 1次SDK调用)
```python
fetch_symbols (async)
└── _fetch_symbols_sync (sync)
└── _base_data.get_future_code_list() # [第1次] 获取期货代码
# 注意: 期货没有调用 get_future_info交易所通过代码解析
```
**修正**: 之前文档错误地写为2次调用实际只有1次。
---
## 三、新增分表接口调用链
### 3.1 fetch_kline_base (1次SDK调用)
```python
fetch_kline_base (async)
└── _fetch_kline_base_sync (sync)
└── _market_data.query_kline() # [第1次] 仅基础K线
```
---
### 3.2 fetch_kline_quote (2次SDK调用)
```python
fetch_kline_quote (async)
└── _fetch_kline_quote_sync (sync)
├── _market_data.query_kline() # [第1次] 扩展日期范围
└── _base_data.get_code_info() # [第2次] 涨跌停价
```
---
### 3.3 fetch_kline_finance (2次SDK调用)
```python
fetch_kline_finance (async)
└── _fetch_kline_finance_sync (sync)
├── _info_data.get_equity_structure() # [第1次] 股本结构
└── _market_data.query_kline() # [第2次] 价格数据
```
---
## 四、单次SDK调用的接口
以下接口都只进行 **1次** SDK 调用:
| 接口 | SDK 方法 |
|------|----------|
| `connect` | `_market_data.login()` |
| `health_check` | `_market_data.is_login()` |
| `fetch_trading_calendar` | `_base_data.get_calendar()` |
| `get_adj_factor` | `_base_data.get_adj_factor()` |
| `get_backward_factor` | `_base_data.get_backward_factor()` |
| `get_snapshot` | `_market_data.query_snapshot()` |
| `get_balance_sheet` | `_info_data.get_balance_sheet()` |
| `get_cash_flow` | `_info_data.get_cash_flow()` |
| `get_income_statement` | `_info_data.get_income()` |
| `get_profit_express` | `_info_data.get_profit_express()` |
| `get_profit_notice` | `_info_data.get_profit_notice()` |
| `get_top10_shareholders` | `_info_data.get_share_holder()` |
| `get_shareholder_count` | `_info_data.get_holder_num()` |
| `get_equity_structure` | `_info_data.get_equity_structure()` |
| `get_index_constituents` | `_info_data.get_index_constituent()` |
| `get_index_weights` | `_info_data.get_index_weight()` |
| `get_margin_summary` | `_info_data.get_margin_summary()` |
| `get_margin_detail` | `_info_data.get_margin_detail()` |
| `get_longhu_bang` | `_info_data.get_long_hu_bang()` |
| `get_block_trading` | `_info_data.get_block_trading()` |
| `get_etf_pcf` | `_base_data.get_etf_pcf()` |
| `get_fund_share` | `_info_data.get_fund_share()` |
| `get_kzz_issuance` | `_info_data.get_kzz_issuance()` |
| `get_history_stock_status` | `_info_data.get_history_stock_status()` |
| `get_code_info` | `_base_data.get_code_info()` |
| `get_trading_calendar` | `_base_data.get_calendar()` |
---
## 五、SDK 对象方法汇总
### _market_data (市场数据)
| 方法 | 使用场景 | 调用次数 |
|------|----------|----------|
| `login()` | connect | 1 |
| `query_kline()` | K线数据 | 频繁 |
| `query_snapshot()` | get_snapshot | 按需 |
| `is_login()` | health_check | 1 |
### _base_data (基础数据)
| 方法 | 使用场景 | 调用次数 |
|------|----------|----------|
| `get_code_list()` | fetch_symbols(stock) | 频繁 |
| `get_future_code_list()` | fetch_symbols(futures) | 按需 |
| `get_code_info()` | 多个接口 | **最频繁** |
| `get_calendar()` | 交易日历 | 多次 |
| `get_adj_factor()` | 复权因子 | 按需 |
| `get_backward_factor()` | 后复权因子 | 按需 |
| `get_etf_pcf()` | ETF数据 | 按需 |
| `get_hist_code_list()` | 备选上市日期 | 可选 |
### _info_data (信息数据)
| 方法 | 使用场景 | 调用次数 |
|------|----------|----------|
| `get_equity_structure()` | 股本/上市日期 | **最频繁** |
| `get_share_holder()` | 股东数据 | 按需 |
| `get_income()` | 利润表 | 按需 |
| `get_balance_sheet()` | 资产负债表 | 按需 |
| `get_cash_flow()` | 现金流量表 | 按需 |
| `get_profit_express()` | 业绩预告 | 按需 |
| `get_profit_notice()` | 业绩快报 | 按需 |
| `get_holder_num()` | 股东户数 | 按需 |
| `get_margin_summary()` | 融资融券汇总 | 按需 |
| `get_margin_detail()` | 融资融券明细 | 按需 |
| `get_long_hu_bang()` | 龙虎榜 | 按需 |
| `get_block_trading()` | 大宗交易 | 按需 |
| `get_index_constituent()` | 指数成分股 | 按需 |
| `get_index_weight()` | 指数权重 | 按需 |
| `get_fund_share()` | 基金份额 | 按需 |
| `get_kzz_issuance()` | 可转债发行 | 按需 |
| `get_history_stock_status()` | 历史股票状态(涨停/跌停/ST/停牌) | 按需 |
---
## 六、修正说明
### 6.1 与V2版文档的差异
| 接口 | V2版文档 | V3版文档(修正) | 差异说明 |
|------|----------|----------------|----------|
| `fetch_symbols` (futures) | 2次调用 | **1次调用** | 期货实际只调用`get_future_code_list`,没有调用`get_future_info` |
| `fetch_stock_basic_info` | 2+N次 | **2+N次** | 确认正确N=股票数量 |
| `fetch_klines` | 4-6次 | **4-6次** | 确认正确 |
### 6.2 风险提示
**⚠️ 高危接口**: `fetch_stock_basic_info`
- 获取全市场股票时会产生 **5000+** 次 SDK 调用
- 可能导致性能问题或触发限流
- **建议**: 始终使用 `codes` 参数限制查询范围
```python
# ❌ 不推荐: 获取全市场
all_stocks = await adapter.fetch_stock_basic_info()
# ✅ 推荐: 只查询指定股票
specific_stocks = await adapter.fetch_stock_basic_info(
codes=["000001.SZ", "600519.SH"]
)
```
---
## 七、接口统计
| 类别 | 接口数量 |
|------|----------|
| 核心适配器接口 | 6 |
| 基础数据接口 | 4 |
| 财务/股东数据接口 | 8 |
| 市场数据接口 | 9 |
| 基金/可转债接口 | 3 |
| 新增分表数据接口 | 4 |
| **总计** | **34** |
**复杂度分级**:
- ⭐ 简单 (1次): 26个接口
- ⭐⭐ 中等 (2次): 3个接口 (`fetch_symbols` stock, `fetch_kline_quote`, `fetch_kline_finance`)
- ⭐⭐⭐⭐ 复杂 (4-6次): 1个接口 (`fetch_klines`)
- ⭐⭐⭐⭐⭐ 极复杂 (2+N次): 1个接口 (`fetch_stock_basic_info`)

@ -0,0 +1,123 @@
# get_history_stock_status 接口实现
## 接口说明
`get_history_stock_status` 是 AmazingData SDK 提供的历史股票状态查询接口,用于获取股票的历史涨停/跌停价、ST状态、停牌状态等信息。
## 实现位置
### 1. 内部接口层
**文件**: `app/adapters/internal_data_service.py`
```python
class _InfoDataInternal:
def get_history_stock_status(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取历史股票状态数据
包含字段:
- TRADE_DATE: 交易日期
- UP_LIMIT: 涨停价
- DOWN_LIMIT: 跌停价
- MAX_UP_DOWN: 最大涨跌幅限制
- IS_ST: 是否ST
- IS_SUSPEND: 是否停牌
"""
```
### 2. 对外接口层
**文件**: `app/adapters/amazingdata_adapter.py`
```python
class AmazingDataAdapter(DataSourceAdapter):
async def get_history_stock_status(
self,
codes: List[str],
start_date: Optional[str] = None,
end_date: Optional[str] = None,
is_local: Optional[bool] = None
) -> Dict[str, pd.DataFrame]:
"""获取历史股票状态数据"""
```
## 返回字段说明
| 字段名 | 类型 | 说明 |
|--------|------|------|
| TRADE_DATE | int | 交易日期 (YYYYMMDD) |
| UP_LIMIT | float | 涨停价 |
| DOWN_LIMIT | float | 跌停价 |
| MAX_UP_DOWN | float | 最大涨跌幅限制(%) |
| IS_ST | bool/int | 是否ST股 |
| IS_SUSPEND | bool/int | 是否停牌 |
## 使用示例
```python
from app.adapters.amazingdata_adapter import AmazingDataAdapter
adapter = AmazingDataAdapter()
await adapter.connect({
"username": "your_username",
"password": "your_password",
"host": "your_host",
"port": 8600
})
# 获取股票历史状态
result = await adapter.get_history_stock_status(
codes=["000001.SZ", "600519.SH"],
start_date="20240301",
end_date="20240310"
)
# 处理结果
for symbol, df in result.items():
print(f"股票: {symbol}")
print(df[["TRADE_DATE", "UP_LIMIT", "DOWN_LIMIT", "IS_ST"]])
```
## 调用链
```
AmazingDataAdapter.get_history_stock_status (async)
└── _InfoDataInternal.get_history_stock_status (sync)
└── SDK: _info_data.get_history_stock_status()
```
## SDK 调用统计
| 层次 | 调用次数 | 说明 |
|------|----------|------|
| 对外接口 | 1 | async 包装 |
| 内部接口 | 1 | 错误处理 |
| SDK | 1 | 实际数据获取 |
**总计**: 1 次 SDK 调用(单次查询)
## 应用场景
1. **涨跌停分析** - 获取历史涨停/跌停价格
2. **ST股票跟踪** - 监控股票ST状态变化
3. **停牌监控** - 获取股票停牌状态
4. **策略回测** - 基于历史涨跌停限制进行回测
## 与其他接口的关系
| 接口 | 用途 | 区别 |
|------|------|------|
| `get_history_stock_status` | 获取历史状态(涨停/跌停/ST/停牌) | 返回每日状态数据 |
| `get_code_info` | 获取当前代码信息 | 返回最新静态信息 |
| `fetch_klines` | 获取K线数据 | 包含涨跌停判断逻辑 |
## 文档更新
以下文档已更新:
- `INTERNAL_INTERFACES.md` - 内部接口层文档
- `AMAZINGDATA_ADAPTER_INTERFACES_V3.md` - 接口调用关系文档

@ -0,0 +1,148 @@
# 内部接口层文档
## 架构设计
```
对外接口 (AmazingDataAdapter)
├── 内部接口层 (InternalDataService)
│ ├── _MarketDataInternal (市场数据)
│ ├── _BaseDataInternal (基础数据)
│ └── _InfoDataInternal (信息数据)
└── AmazingData SDK
├── _market_data
├── _base_data
└── _info_data
```
## 设计原则
1. **对外接口不直接调用 SDK** - 所有 SDK 调用都通过内部接口层
2. **统一错误处理** - 内部接口层统一处理 SDK 异常
3. **日志记录** - 内部接口层统一记录调用日志
4. **便于测试** - 可以 mock 内部接口进行单元测试
## 内部接口类
### _MarketDataInternal
封装 `_market_data` SDK 对象的方法:
| 方法 | SDK 方法 | 说明 |
|------|----------|------|
| `login()` | `_market_data.login()` | 登录 |
| `is_login()` | `_market_data.is_login()` | 检查登录状态 |
| `query_kline()` | `_market_data.query_kline()` | 查询K线 |
| `query_snapshot()` | `_market_data.query_snapshot()` | 查询快照 |
### _BaseDataInternal
封装 `_base_data` SDK 对象的方法:
| 方法 | SDK 方法 | 说明 |
|------|----------|------|
| `get_code_list()` | `_base_data.get_code_list()` | 获取股票代码列表 |
| `get_future_code_list()` | `_base_data.get_future_code_list()` | 获取期货代码列表 |
| `get_code_info()` | `_base_data.get_code_info()` | 获取代码信息 |
| `get_calendar()` | `_base_data.get_calendar()` | 获取交易日历 |
| `get_adj_factor()` | `_base_data.get_adj_factor()` | 获取复权因子 |
| `get_backward_factor()` | `_base_data.get_backward_factor()` | 获取后复权因子 |
| `get_etf_pcf()` | `_base_data.get_etf_pcf()` | 获取ETF申赎数据 |
| `get_hist_code_list()` | `_base_data.get_hist_code_list()` | 获取历史代码列表 |
### _InfoDataInternal
封装 `_info_data` SDK 对象的方法:
| 方法 | SDK 方法 | 说明 |
|------|----------|------|
| `get_equity_structure()` | `_info_data.get_equity_structure()` | 股本结构 |
| `get_share_holder()` | `_info_data.get_share_holder()` | 股东数据 |
| `get_holder_num()` | `_info_data.get_holder_num()` | 股东户数 |
| `get_income()` | `_info_data.get_income()` | 利润表 |
| `get_balance_sheet()` | `_info_data.get_balance_sheet()` | 资产负债表 |
| `get_cash_flow()` | `_info_data.get_cash_flow()` | 现金流量表 |
| `get_profit_express()` | `_info_data.get_profit_express()` | 业绩预告 |
| `get_profit_notice()` | `_info_data.get_profit_notice()` | 业绩快报 |
| `get_margin_summary()` | `_info_data.get_margin_summary()` | 融资融券汇总 |
| `get_margin_detail()` | `_info_data.get_margin_detail()` | 融资融券明细 |
| `get_long_hu_bang()` | `_info_data.get_long_hu_bang()` | 龙虎榜 |
| `get_block_trading()` | `_info_data.get_block_trading()` | 大宗交易 |
| `get_index_constituent()` | `_info_data.get_index_constituent()` | 指数成分股 |
| `get_index_weight()` | `_info_data.get_index_weight()` | 指数权重 |
| `get_fund_share()` | `_info_data.get_fund_share()` | 基金份额 |
| `get_kzz_issuance()` | `_info_data.get_kzz_issuance()` | 可转债发行 |
| `get_history_stock_status()` | `_info_data.get_history_stock_status()` | 历史股票状态(涨停/跌停/ST/停牌) |
## 使用方式
### 在 AmazingDataAdapter 中
```python
class AmazingDataAdapter(DataSourceAdapter):
def __init__(self):
# ... 其他初始化 ...
self._internal: Optional[InternalDataService] = None
async def connect(self, config: dict) -> None:
# ... 登录和初始化 SDK ...
# 初始化内部数据服务层
self._internal = InternalDataService(self)
def _fetch_klines_sync(self, ...):
# 使用内部接口,而不是直接调用 SDK
kline_dict = self._internal.market.query_kline(...)
code_info = self._internal.base.get_code_info(...)
equity = self._internal.info.get_equity_structure(...)
```
### 错误处理
内部接口统一处理 SDK 异常,返回空数据或默认值:
```python
class _BaseDataInternal:
def get_code_info(self, security_type: str) -> pd.DataFrame:
try:
return self._base_data.get_code_info(security_type=security_type)
except Exception as e:
error(f"[_BaseDataInternal] Get code info failed: {e}")
return pd.DataFrame() # 返回空数据而不是抛出异常
```
## 文件结构
```
app/adapters/
├── __init__.py
├── base.py # 适配器基类
├── amazingdata_adapter.py # 对外适配器(使用内部接口)
└── internal_data_service.py # 内部接口层
```
## 迁移说明
### 之前的调用方式(已废弃)
```python
# 直接调用 SDK
code_info = self._base_data.get_code_info(security_type=...)
kline = self._market_data.query_kline(...)
equity = self._info_data.get_equity_structure(...)
```
### 现在的调用方式
```python
# 通过内部接口调用
code_info = self._internal.base.get_code_info(security_type=...)
kline = self._internal.market.query_kline(...)
equity = self._internal.info.get_equity_structure(...)
```
## 优势
1. **解耦** - 对外接口与 SDK 解耦,便于更换数据源
2. **可测试性** - 可以 mock 内部接口进行单元测试
3. **错误处理** - 统一的错误处理和日志记录
4. **扩展性** - 可以在内部接口层添加缓存、限流等功能

@ -17,6 +17,7 @@ from app.adapters.base import (
TradeCalData, TickCallback
)
from app.core.logger import info, error, warning
from app.adapters.internal_data_service import InternalDataService
class SecurityType(Enum):
"""证券类型枚举"""
@ -65,6 +66,7 @@ class AmazingDataAdapter(DataSourceAdapter):
self._calendar = None
self._is_logged_in = False
self._connected = False
self._internal: Optional[InternalDataService] = None # 内部数据服务
def _check_login(self):
"""检查是否已登录"""
@ -90,7 +92,7 @@ class AmazingDataAdapter(DataSourceAdapter):
"""
try:
# 方法1尝试从代码信息中获取
code_info_df = self._base_data.get_code_info(security_type=SecurityType.STOCK_A.value)
code_info_df = self._internal.base.get_code_info(security_type=SecurityType.STOCK_A.value)
if symbol in code_info_df.index:
# 尝试不同的字段名
for field in ['list_date', 'LIST_DATE', 'listDate', 'founded_date']:
@ -107,7 +109,7 @@ class AmazingDataAdapter(DataSourceAdapter):
# 方法2尝试从历史代码列表获取
try:
hist_codes = self._base_data.get_hist_code_list(security_type=SecurityType.STOCK_A.value)
hist_codes = self._internal.base.get_hist_code_list(security_type=SecurityType.STOCK_A.value)
if symbol in hist_codes.index and 'list_date' in hist_codes.columns:
list_date_val = hist_codes.loc[symbol, 'list_date']
if pd.notna(list_date_val):
@ -182,9 +184,12 @@ class AmazingDataAdapter(DataSourceAdapter):
# 初始化数据类
self._base_data = self._ad.BaseData()
self._info_data = self._ad.InfoData()
self._calendar = self._base_data.get_calendar()
self._calendar = self._internal.base.get_calendar()
self._market_data = self._ad.MarketData(self._calendar)
# 初始化内部数据服务层
self._internal = InternalDataService(self)
self._is_logged_in = True
print("[amazingdata_adapter]登录成功")
@ -258,8 +263,8 @@ class AmazingDataAdapter(DataSourceAdapter):
end_int = self._format_date(end_date)
print(f"[amazingdata_adapter _fetch_klines_sync]正在获取K线数据: 代码={codes}, 日期范围={start_date}~{end_date}, 周期={period_value}")
# 获取K线数据 - 将周期值转换为 SDK 的常量
kline_dict = self._market_data.query_kline(
# 获取K线数据 - 使用内部接口
kline_dict = self._internal.market.query_kline(
code_list=codes,
begin_date=start_int,
end_date=end_int,
@ -278,7 +283,7 @@ class AmazingDataAdapter(DataSourceAdapter):
# ============================================
print(f"[amazingdata_adapter _fetch_klines_sync]正在获取证券基本信息...")
try:
code_info_df = self._base_data.get_code_info(security_type=SecurityType.STOCK_A.value)
code_info_df = self._internal.base.get_code_info(security_type=SecurityType.STOCK_A.value)
# 提取当前股票的涨停价和跌停价
if symbol in code_info_df.index:
high_limited = float(code_info_df.loc[symbol, 'high_limited']) if 'high_limited' in code_info_df.columns else None
@ -298,7 +303,7 @@ class AmazingDataAdapter(DataSourceAdapter):
# ============================================
print(f"[amazingdata_adapter _fetch_klines_sync]正在获取股本结构...")
try:
equity_dict = self._info_data.get_equity_structure(
equity_dict = self._internal.info.get_equity_structure(
code_list=codes,
local_path=self.config.local_path,
is_local=self.config.use_local_cache
@ -341,7 +346,7 @@ class AmazingDataAdapter(DataSourceAdapter):
print(f"[amazingdata_adapter _fetch_klines_sync]正在获取交易日历...")
try:
# 获取交易日历
calendar = self._base_data.get_calendar(market=Market.SH.value)
calendar = self._internal.base.get_calendar(market=Market.SH.value)
# 获取股票上市日期
list_date = self._get_list_date(symbol)
@ -453,11 +458,11 @@ class AmazingDataAdapter(DataSourceAdapter):
if asset_type == "stock":
# 获取A股代码列表
codes = self._base_data.get_code_list(
codes = self._internal.base.get_code_list(
security_type=SecurityType.STOCK_A.value
)
# 获取代码信息
info_df = self._base_data.get_code_info(
info_df = self._internal.base.get_code_info(
security_type=SecurityType.STOCK_A.value
)
@ -487,7 +492,7 @@ class AmazingDataAdapter(DataSourceAdapter):
elif asset_type == "futures":
# 获取期货代码列表
codes = self._base_data.get_future_code_list(
codes = self._internal.base.get_future_code_list(
security_type=SecurityType.FUTURE.value
)
@ -556,7 +561,7 @@ class AmazingDataAdapter(DataSourceAdapter):
"""同步获取交易日历"""
# 获取交易日历
market = Market.SH if exchange in ["SH", "SSE"] else Market.SZ
calendar = self._base_data.get_calendar(market=market.value)
calendar = self._internal.base.get_calendar(market=market.value)
start_int = self._format_date(start)
end_int = self._format_date(end)
@ -582,7 +587,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
await loop.run_in_executor(
None,
lambda: self._base_data.get_code_list(
lambda: self._internal.base.get_code_list(
security_type=SecurityType.STOCK_A.value
)
)
@ -613,7 +618,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._base_data.get_adj_factor(
lambda: self._internal.base.get_adj_factor(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local
@ -632,7 +637,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._base_data.get_backward_factor(
lambda: self._internal.base.get_backward_factor(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local
@ -658,7 +663,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_index_constituent(
lambda: self._internal.info.get_index_constituent(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local
@ -690,7 +695,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_index_weight(
lambda: self._internal.info.get_index_weight(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -714,7 +719,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._market_data.query_snapshot(
lambda: self._internal.market.query_snapshot(
code_list=codes,
begin_date=start_int,
end_date=end_int
@ -811,7 +816,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._base_data.get_code_info(security_type=security_type.value)
lambda: self._internal.base.get_code_info(security_type=security_type.value)
)
async def get_trading_calendar(
@ -831,7 +836,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._base_data.get_calendar(market=market.value)
lambda: self._internal.base.get_calendar(market=market.value)
)
# ==================== 业绩数据接口 ====================
@ -871,7 +876,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_profit_express(
lambda: self._internal.info.get_profit_express(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -913,7 +918,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_profit_notice(
lambda: self._internal.info.get_profit_notice(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -956,7 +961,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_share_holder(
lambda: self._internal.info.get_share_holder(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -994,7 +999,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_holder_num(
lambda: self._internal.info.get_holder_num(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -1035,7 +1040,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_equity_structure(
lambda: self._internal.info.get_equity_structure(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -1078,7 +1083,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_margin_summary(
lambda: self._internal.info.get_margin_summary(
local_path=self.config.local_path,
is_local=is_local,
begin_date=begin_date,
@ -1118,7 +1123,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_margin_detail(
lambda: self._internal.info.get_margin_detail(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -1163,7 +1168,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_long_hu_bang(
lambda: self._internal.info.get_long_hu_bang(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -1205,7 +1210,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_block_trading(
lambda: self._internal.info.get_block_trading(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -1244,7 +1249,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._base_data.get_etf_pcf(code_list=codes)
lambda: self._internal.base.get_etf_pcf(code_list=codes)
)
async def get_fund_share(
@ -1278,7 +1283,7 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_fund_share(
lambda: self._internal.info.get_fund_share(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
@ -1317,13 +1322,55 @@ class AmazingDataAdapter(DataSourceAdapter):
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._info_data.get_kzz_issuance(
lambda: self._internal.info.get_kzz_issuance(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local
)
)
async def get_history_stock_status(
self,
codes: List[str],
start_date: Optional[str] = None,
end_date: Optional[str] = None,
is_local: Optional[bool] = None
) -> Dict[str, pd.DataFrame]:
"""获取历史股票状态数据
Args:
codes: 股票代码列表
start_date: 开始日期 (YYYYMMDD)
end_date: 结束日期 (YYYYMMDD)
is_local: 是否使用本地缓存
Returns:
Dict[代码, DataFrame] 主要字段
- TRADE_DATE: 交易日期
- UP_LIMIT: 涨停价
- DOWN_LIMIT: 跌停价
- MAX_UP_DOWN: 最大涨跌幅限制
- IS_ST: 是否ST
- IS_SUSPEND: 是否停牌
"""
self._check_login()
is_local = is_local if is_local is not None else self.config.use_local_cache
begin_date = self._format_date(start_date) if start_date else None
end_date_int = self._format_date(end_date) if end_date else None
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: self._internal.info.get_history_stock_status(
code_list=codes,
local_path=self.config.local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date_int
)
)
# ==================== New Split Table Data Fetch Methods ====================
async def fetch_kline_base(
@ -1381,7 +1428,7 @@ class AmazingDataAdapter(DataSourceAdapter):
start_int = self._format_date(start_date)
end_int = self._format_date(end_date)
kline_dict = self._market_data.query_kline(
kline_dict = self._internal.market.query_kline(
code_list=codes,
begin_date=start_int,
end_date=end_int,
@ -1473,7 +1520,7 @@ class AmazingDataAdapter(DataSourceAdapter):
start_int = self._format_date(extended_start_str)
end_int = self._format_date(end_date)
kline_dict = self._market_data.query_kline(
kline_dict = self._internal.market.query_kline(
code_list=codes,
begin_date=start_int,
end_date=end_int,
@ -1487,7 +1534,7 @@ class AmazingDataAdapter(DataSourceAdapter):
df = df.sort_values('kline_time')
try:
code_info_df = self._base_data.get_code_info(security_type=SecurityType.STOCK_A.value)
code_info_df = self._internal.base.get_code_info(security_type=SecurityType.STOCK_A.value)
if symbol in code_info_df.index:
high_limited = float(code_info_df.loc[symbol, 'high_limited']) if 'high_limited' in code_info_df.columns else None
low_limited = float(code_info_df.loc[symbol, 'low_limited']) if 'low_limited' in code_info_df.columns else None
@ -1635,7 +1682,7 @@ class AmazingDataAdapter(DataSourceAdapter):
results = []
try:
equity_dict = self._info_data.get_equity_structure(
equity_dict = self._internal.info.get_equity_structure(
code_list=codes,
local_path=self.config.local_path,
is_local=self.config.use_local_cache
@ -1659,7 +1706,7 @@ class AmazingDataAdapter(DataSourceAdapter):
print(f"[amazingdata_adapter]Failed to get equity structure: {e}")
equity_data = {}
kline_dict = self._market_data.query_kline(
kline_dict = self._internal.market.query_kline(
code_list=codes,
begin_date=start_int,
end_date=end_int,
@ -1756,14 +1803,14 @@ class AmazingDataAdapter(DataSourceAdapter):
) -> List[Dict[str, Any]]:
"""Sync method to fetch stock basic info"""
try:
all_codes = self._base_data.get_code_list(
all_codes = self._internal.base.get_code_list(
security_type=SecurityType.STOCK_A.value
)
if codes:
all_codes = [c for c in all_codes if c in codes]
info_df = self._base_data.get_code_info(
info_df = self._internal.base.get_code_info(
security_type=SecurityType.STOCK_A.value
)
@ -1784,7 +1831,7 @@ class AmazingDataAdapter(DataSourceAdapter):
list_date = None
try:
equity_dict = self._info_data.get_equity_structure(
equity_dict = self._internal.info.get_equity_structure(
code_list=[code],
local_path=self.config.local_path,
is_local=self.config.use_local_cache

@ -0,0 +1,534 @@
"""内部数据服务层
AmazingData SDK 的调用封装为内部接口
对外接口不直接调用 SDK而是通过内部接口调用
"""
import pandas as pd
from datetime import datetime
from typing import List, Optional, Dict, Any
from app.core.logger import info, error
class _MarketDataInternal:
"""市场数据内部接口 - 封装 _market_data"""
def __init__(self, market_data):
self._market_data = market_data
def login(self, username: str, password: str, ip: str, port: str) -> bool:
"""登录"""
try:
return self._market_data.login(
username=username,
password=password,
ip=ip,
port=port
)
except Exception as e:
error(f"[_MarketDataInternal] Login failed: {e}")
raise
def is_login(self) -> bool:
"""检查登录状态"""
try:
return self._market_data.is_login()
except Exception:
return False
def query_kline(
self,
code_list: List[str],
begin_date: int,
end_date: int,
period: int
) -> Dict[str, pd.DataFrame]:
"""查询K线数据"""
try:
return self._market_data.query_kline(
code_list=code_list,
begin_date=begin_date,
end_date=end_date,
period=period
)
except Exception as e:
error(f"[_MarketDataInternal] Query kline failed: {e}")
return {}
def query_snapshot(
self,
code_list: List[str],
begin_date: int,
end_date: int
) -> Dict[str, pd.DataFrame]:
"""查询快照数据"""
try:
return self._market_data.query_snapshot(
code_list=code_list,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_MarketDataInternal] Query snapshot failed: {e}")
return {}
class _BaseDataInternal:
"""基础数据内部接口 - 封装 _base_data"""
def __init__(self, base_data):
self._base_data = base_data
def get_code_list(self, security_type: str) -> List[str]:
"""获取代码列表"""
try:
return self._base_data.get_code_list(security_type=security_type)
except Exception as e:
error(f"[_BaseDataInternal] Get code list failed: {e}")
return []
def get_future_code_list(self, security_type: str) -> List[str]:
"""获取期货代码列表"""
try:
return self._base_data.get_future_code_list(security_type=security_type)
except Exception as e:
error(f"[_BaseDataInternal] Get future code list failed: {e}")
return []
def get_code_info(self, security_type: str) -> pd.DataFrame:
"""获取代码信息"""
try:
return self._base_data.get_code_info(security_type=security_type)
except Exception as e:
error(f"[_BaseDataInternal] Get code info failed: {e}")
return pd.DataFrame()
def get_calendar(self, market: str) -> List[int]:
"""获取交易日历"""
try:
return self._base_data.get_calendar(market=market)
except Exception as e:
error(f"[_BaseDataInternal] Get calendar failed: {e}")
return []
def get_adj_factor(
self,
code_list: List[str],
local_path: str,
is_local: bool
) -> pd.DataFrame:
"""获取复权因子"""
try:
return self._base_data.get_adj_factor(
code_list=code_list,
local_path=local_path,
is_local=is_local
)
except Exception as e:
error(f"[_BaseDataInternal] Get adj factor failed: {e}")
return pd.DataFrame()
def get_backward_factor(
self,
code_list: List[str],
local_path: str,
is_local: bool
) -> pd.DataFrame:
"""获取后复权因子"""
try:
return self._base_data.get_backward_factor(
code_list=code_list,
local_path=local_path,
is_local=is_local
)
except Exception as e:
error(f"[_BaseDataInternal] Get backward factor failed: {e}")
return pd.DataFrame()
def get_etf_pcf(self, code_list: List[str]) -> tuple:
"""获取ETF申赎数据"""
try:
return self._base_data.get_etf_pcf(code_list=code_list)
except Exception as e:
error(f"[_BaseDataInternal] Get ETF PCF failed: {e}")
return ({}, {})
def get_hist_code_list(self, security_type: str) -> pd.DataFrame:
"""获取历史代码列表"""
try:
return self._base_data.get_hist_code_list(security_type=security_type)
except Exception as e:
error(f"[_BaseDataInternal] Get hist code list failed: {e}")
return pd.DataFrame()
class _InfoDataInternal:
"""信息数据内部接口 - 封装 _info_data"""
def __init__(self, info_data):
self._info_data = info_data
def get_equity_structure(
self,
code_list: List[str],
local_path: str,
is_local: bool
) -> Dict[str, pd.DataFrame]:
"""获取股本结构"""
try:
return self._info_data.get_equity_structure(
code_list=code_list,
local_path=local_path,
is_local=is_local
)
except Exception as e:
error(f"[_InfoDataInternal] Get equity structure failed: {e}")
return {}
def get_share_holder(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取股东数据"""
try:
return self._info_data.get_share_holder(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get share holder failed: {e}")
return {}
def get_holder_num(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取股东户数"""
try:
return self._info_data.get_holder_num(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get holder num failed: {e}")
return {}
def get_income(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取利润表"""
try:
return self._info_data.get_income(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get income failed: {e}")
return {}
def get_balance_sheet(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取资产负债表"""
try:
return self._info_data.get_balance_sheet(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get balance sheet failed: {e}")
return {}
def get_cash_flow(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取现金流量表"""
try:
return self._info_data.get_cash_flow(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get cash flow failed: {e}")
return {}
def get_profit_express(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取业绩预告"""
try:
return self._info_data.get_profit_express(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get profit express failed: {e}")
return {}
def get_profit_notice(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取业绩快报"""
try:
return self._info_data.get_profit_notice(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get profit notice failed: {e}")
return {}
def get_margin_summary(
self,
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> pd.DataFrame:
"""获取融资融券汇总"""
try:
return self._info_data.get_margin_summary(
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get margin summary failed: {e}")
return pd.DataFrame()
def get_margin_detail(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取融资融券明细"""
try:
return self._info_data.get_margin_detail(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get margin detail failed: {e}")
return {}
def get_long_hu_bang(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> pd.DataFrame:
"""获取龙虎榜数据"""
try:
return self._info_data.get_long_hu_bang(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get long hu bang failed: {e}")
return pd.DataFrame()
def get_block_trading(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> pd.DataFrame:
"""获取大宗交易数据"""
try:
return self._info_data.get_block_trading(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get block trading failed: {e}")
return pd.DataFrame()
def get_index_constituent(
self,
code_list: List[str],
local_path: str,
is_local: bool
) -> Dict[str, pd.DataFrame]:
"""获取指数成分股"""
try:
return self._info_data.get_index_constituent(
code_list=code_list,
local_path=local_path,
is_local=is_local
)
except Exception as e:
error(f"[_InfoDataInternal] Get index constituent failed: {e}")
return {}
def get_index_weight(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取指数权重"""
try:
return self._info_data.get_index_weight(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get index weight failed: {e}")
return {}
def get_fund_share(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取基金份额"""
try:
return self._info_data.get_fund_share(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get fund share failed: {e}")
return {}
def get_kzz_issuance(
self,
code_list: List[str],
local_path: str,
is_local: bool
) -> Dict[str, pd.DataFrame]:
"""获取可转债发行数据"""
try:
return self._info_data.get_kzz_issuance(
code_list=code_list,
local_path=local_path,
is_local=is_local
)
except Exception as e:
error(f"[_InfoDataInternal] Get kzz issuance failed: {e}")
return {}
def get_history_stock_status(
self,
code_list: List[str],
local_path: str,
is_local: bool,
begin_date: Optional[int] = None,
end_date: Optional[int] = None
) -> Dict[str, pd.DataFrame]:
"""获取历史股票状态数据
包含字段
- TRADE_DATE: 交易日期
- UP_LIMIT: 涨停价
- DOWN_LIMIT: 跌停价
- MAX_UP_DOWN: 最大涨跌幅限制
- IS_ST: 是否ST
- IS_SUSPEND: 是否停牌
"""
try:
return self._info_data.get_history_stock_status(
code_list=code_list,
local_path=local_path,
is_local=is_local,
begin_date=begin_date,
end_date=end_date
)
except Exception as e:
error(f"[_InfoDataInternal] Get history stock status failed: {e}")
return {}
class InternalDataService:
"""内部数据服务统一入口"""
def __init__(self, ad):
"""
Args:
ad: AmazingData SDK instance with _market_data, _base_data, _info_data
"""
self.market = _MarketDataInternal(ad._market_data)
self.base = _BaseDataInternal(ad._base_data)
self.info = _InfoDataInternal(ad._info_data)

@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""自动替换 SDK 调用为内部接口调用"""
replacements = [
# _base_data 调用
("self._base_data.get_code_info(", "self._internal.base.get_code_info("),
("self._base_data.get_code_list(", "self._internal.base.get_code_list("),
("self._base_data.get_future_code_list(", "self._internal.base.get_future_code_list("),
("self._base_data.get_calendar(", "self._internal.base.get_calendar("),
("self._base_data.get_adj_factor(", "self._internal.base.get_adj_factor("),
("self._base_data.get_backward_factor(", "self._internal.base.get_backward_factor("),
("self._base_data.get_etf_pcf(", "self._internal.base.get_etf_pcf("),
("self._base_data.get_hist_code_list(", "self._internal.base.get_hist_code_list("),
# _market_data 调用
("self._market_data.query_kline(", "self._internal.market.query_kline("),
("self._market_data.query_snapshot(", "self._internal.market.query_snapshot("),
# _info_data 调用
("self._info_data.get_equity_structure(", "self._internal.info.get_equity_structure("),
("self._info_data.get_share_holder(", "self._internal.info.get_share_holder("),
("self._info_data.get_holder_num(", "self._internal.info.get_holder_num("),
("self._info_data.get_income(", "self._internal.info.get_income("),
("self._info_data.get_balance_sheet(", "self._internal.info.get_balance_sheet("),
("self._info_data.get_cash_flow(", "self._internal.info.get_cash_flow("),
("self._info_data.get_profit_express(", "self._internal.info.get_profit_express("),
("self._info_data.get_profit_notice(", "self._internal.info.get_profit_notice("),
("self._info_data.get_margin_summary(", "self._internal.info.get_margin_summary("),
("self._info_data.get_margin_detail(", "self._internal.info.get_margin_detail("),
("self._info_data.get_long_hu_bang(", "self._internal.info.get_long_hu_bang("),
("self._info_data.get_block_trading(", "self._internal.info.get_block_trading("),
("self._info_data.get_index_constituent(", "self._internal.info.get_index_constituent("),
("self._info_data.get_index_weight(", "self._internal.info.get_index_weight("),
("self._info_data.get_fund_share(", "self._internal.info.get_fund_share("),
("self._info_data.get_kzz_issuance(", "self._internal.info.get_kzz_issuance("),
]
def main():
with open('app/adapters/amazingdata_adapter.py', 'r', encoding='utf-8') as f:
content = f.read()
count = 0
for old, new in replacements:
if old in content:
occurrences = content.count(old)
content = content.replace(old, new)
count += occurrences
print(f"Replaced {occurrences} occurrence(s): {old} -> {new}")
with open('app/adapters/amazingdata_adapter.py', 'w', encoding='utf-8') as f:
f.write(content)
print(f"\nTotal replacements: {count}")
if __name__ == "__main__":
main()

@ -19,3 +19,14 @@
2. 获取k线 stock/klines/{symbol} -- 缺少股本等信息
3. 获取交易日历 stock/trading-dates
# 2026年3月15日 接口调整
## 已实现
1. _fetch_calendar_sync 调用接口 get_calendar
## 待调整
1. close接口需要logout进行退出
2.

Loading…
Cancel
Save