diff --git a/AMAZINGDATA_ADAPTER_INTERFACES.md b/AMAZINGDATA_ADAPTER_INTERFACES.md
new file mode 100644
index 0000000..931c41e
--- /dev/null
+++ b/AMAZINGDATA_ADAPTER_INTERFACES.md
@@ -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()`
`_base_data.get_code_info()`
`_base_data.get_future_code_list()`
`_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()`
`_base_data.get_code_info()` | 获取行情指标数据 |
+| 32 | `fetch_kline_finance` | `_fetch_kline_finance_sync` | `_market_data.query_kline()`
`_info_data.get_equity_structure()` | 获取财务数据 |
+| 33 | `fetch_stock_basic_info` | `_fetch_stock_basic_info_sync` | `_base_data.get_code_list()`
`_base_data.get_code_info()`
`_info_data.get_equity_structure()` | 获取股票基础信息 |
+
+## 七、组合调用详细说明
+
+### 7.1 多次调用同一接口的方法
+
+| 方法 | SDK 调用次数 | 调用详情 |
+|------|-------------|----------|
+| `fetch_symbols` (stock) | 2次 | 1. `get_code_list()`
2. `get_code_info()` |
+| `fetch_symbols` (futures) | 2次 | 1. `get_future_code_list()`
2. `get_future_info()` |
+| `fetch_kline_quote` | 2次 | 1. `query_kline()` - 获取K线
2. `get_code_info()` - 获取涨跌停价 |
+| `fetch_kline_finance` | 2次 | 1. `get_equity_structure()` - 获取股本
2. `query_kline()` - 获取价格计算市值 |
+| `fetch_stock_basic_info` | 3次 | 1. `get_code_list()` - 获取代码列表
2. `get_code_info()` - 获取名称
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** |
diff --git a/AMAZINGDATA_ADAPTER_INTERFACES_V2.md b/AMAZINGDATA_ADAPTER_INTERFACES_V2.md
new file mode 100644
index 0000000..80324b8
--- /dev/null
+++ b/AMAZINGDATA_ADAPTER_INTERFACES_V2.md
@@ -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()`
`_base_data.get_code_info()` | 2 | 股票标的列表 |
+| 4b| `fetch_symbols` | `_fetch_symbols_sync` | `_base_data.get_future_code_list()`
`_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=股票数量)
diff --git a/AMAZINGDATA_ADAPTER_INTERFACES_V3.md b/AMAZINGDATA_ADAPTER_INTERFACES_V3.md
new file mode 100644
index 0000000..1d36b7e
--- /dev/null
+++ b/AMAZINGDATA_ADAPTER_INTERFACES_V3.md
@@ -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`)
diff --git a/GET_HISTORY_STOCK_STATUS.md b/GET_HISTORY_STOCK_STATUS.md
new file mode 100644
index 0000000..666de5d
--- /dev/null
+++ b/GET_HISTORY_STOCK_STATUS.md
@@ -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` - 接口调用关系文档
diff --git a/INTERNAL_INTERFACES.md b/INTERNAL_INTERFACES.md
new file mode 100644
index 0000000..bd51adf
--- /dev/null
+++ b/INTERNAL_INTERFACES.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. **扩展性** - 可以在内部接口层添加缓存、限流等功能
diff --git a/app/adapters/__pycache__/amazingdata_adapter.cpython-311.pyc b/app/adapters/__pycache__/amazingdata_adapter.cpython-311.pyc
index ec0b1d5..cc15fd8 100644
Binary files a/app/adapters/__pycache__/amazingdata_adapter.cpython-311.pyc and b/app/adapters/__pycache__/amazingdata_adapter.cpython-311.pyc differ
diff --git a/app/adapters/__pycache__/internal_data_service.cpython-311.pyc b/app/adapters/__pycache__/internal_data_service.cpython-311.pyc
new file mode 100644
index 0000000..7826012
Binary files /dev/null and b/app/adapters/__pycache__/internal_data_service.cpython-311.pyc differ
diff --git a/app/adapters/amazingdata_adapter.py b/app/adapters/amazingdata_adapter.py
index ce6c494..ab0f8bb 100644
--- a/app/adapters/amazingdata_adapter.py
+++ b/app/adapters/amazingdata_adapter.py
@@ -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,8 +184,11 @@ 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
diff --git a/app/adapters/internal_data_service.py b/app/adapters/internal_data_service.py
new file mode 100644
index 0000000..4596666
--- /dev/null
+++ b/app/adapters/internal_data_service.py
@@ -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)
diff --git a/scripts/replace_sdk_calls.py b/scripts/replace_sdk_calls.py
new file mode 100644
index 0000000..daa2e1a
--- /dev/null
+++ b/scripts/replace_sdk_calls.py
@@ -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()
diff --git a/todo_20260311.md b/todo_20260311.md
index e6f4638..7a5b038 100644
--- a/todo_20260311.md
+++ b/todo_20260311.md
@@ -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.
+