|
|
|
|
|
# 市场数据服务接口文档
|
|
|
|
|
|
|
|
|
|
|
|
## 架构概览
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ HTTP 客户端 │
|
|
|
|
|
|
│ (浏览器/Postman/其他服务) │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ API 路由层 (HTTP) │
|
|
|
|
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
|
|
|
|
|
│ │ /stock/* │ │ /futures/* │ │ /admin/* │ │ /stream (WS) │ │
|
|
|
|
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────────┘ │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 服务层 (Service) │
|
|
|
|
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
|
|
|
|
|
│ │ StockService │ │FuturesService│ │ AdminService │ │ TestService │ │
|
|
|
|
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────────┘ │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 适配器服务层 (Adapter) │
|
|
|
|
|
|
│ ┌──────────────────────────────────┐ │
|
|
|
|
|
|
│ │ AdapterService (单例) │ │
|
|
|
|
|
|
│ │ ┌────────────────────────────┐ │ │
|
|
|
|
|
|
│ │ │ AmazingDataAdapter │ │ │
|
|
|
|
|
|
│ │ │ ┌──────────────────────┐ │ │ │
|
|
|
|
|
|
│ │ │ │ _internal: │ │ │ │
|
|
|
|
|
|
│ │ │ │ InternalDataService │ │ │ │
|
|
|
|
|
|
│ │ │ └──────────────────────┘ │ │ │
|
|
|
|
|
|
│ │ └────────────────────────────┘ │ │
|
|
|
|
|
|
│ └──────────────────────────────────┘ │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 内部数据服务层 (Internal) │
|
|
|
|
|
|
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
|
|
|
|
|
|
│ │ _MarketDataInternal│ │ _BaseDataInternal │ │ _InfoDataInternal │ │
|
|
|
|
|
|
│ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────────────┐ │ │
|
|
|
|
|
|
│ │ │ query_kline │ │ │ │get_code_list │ │ │ │ get_equity_structure │ │ │
|
|
|
|
|
|
│ │ │query_snapshot│ │ │ │ get_calendar │ │ │ │ get_share_holder │ │ │
|
|
|
|
|
|
│ │ └──────────────┘ │ │ │get_adj_factor│ │ │ │ get_income │ │ │
|
|
|
|
|
|
│ │ │ │ │ get_etf_pcf │ │ │ │ get_balance_sheet │ │ │
|
|
|
|
|
|
│ │ │ │ └──────────────┘ │ │ │ get_cash_flow │ │ │
|
|
|
|
|
|
│ │ │ │ │ │ │ get_margin_* │ │ │
|
|
|
|
|
|
│ │ │ │ │ │ │ get_index_* │ │ │
|
|
|
|
|
|
│ │ │ │ │ │ │ get_fund_share │ │ │
|
|
|
|
|
|
│ └──────────────────┘ └──────────────────┘ │ └──────────────────────┘ │ │
|
|
|
|
|
|
│ └──────────────────────────┘ │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ SDK 层 (AmazingData) │
|
|
|
|
|
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
|
|
|
|
│ │ login │ │BaseData │ │MarketData│ │ InfoData │ │
|
|
|
|
|
|
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
|
|
|
|
|
│ (银河证券星耀数智) │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 一、对外接口 (HTTP API)
|
|
|
|
|
|
|
|
|
|
|
|
对外接口是提供给外部系统调用的 RESTful API,路径以 `/v1` 为前缀。
|
|
|
|
|
|
|
|
|
|
|
|
### 1.1 股票接口
|
|
|
|
|
|
|
|
|
|
|
|
| 接口 | 方法 | 路径 | 说明 |
|
|
|
|
|
|
|------|------|------|------|
|
|
|
|
|
|
| 查询股票K线 | GET | `/v1/stock/klines/{symbol}` | 获取指定股票的历史K线数据 |
|
|
|
|
|
|
| 查询股票列表 | GET | `/v1/stock/symbols` | 获取所有股票代码列表 |
|
|
|
|
|
|
| 查询股票基本信息 | GET | `/v1/stock/basic/{symbol}` | 获取股票的基本信息 |
|
|
|
|
|
|
| 批量查询K线 | POST | `/v1/stock/klines/batch` | 批量获取多只股票的K线 |
|
|
|
|
|
|
|
|
|
|
|
|
**调用链示例:**
|
|
|
|
|
|
```
|
|
|
|
|
|
GET /v1/stock/klines/000001.SZ
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
routes.py:query_stock_klines()
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
StockService.query_klines()
|
|
|
|
|
|
│
|
|
|
|
|
|
├── 1. 先查数据库 (StockRepository)
|
|
|
|
|
|
│ └── 有数据 → 直接返回
|
|
|
|
|
|
│
|
|
|
|
|
|
└── 2. 无数据 → 从适配器获取
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AdapterService.get_active_adapter("stock")
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AmazingDataAdapter.fetch_klines()
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
InternalDataService.market.query_kline()
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AmazingData.MarketData.query_kline()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 1.2 期货接口
|
|
|
|
|
|
|
|
|
|
|
|
| 接口 | 方法 | 路径 | 说明 |
|
|
|
|
|
|
|------|------|------|------|
|
|
|
|
|
|
| 查询期货K线 | GET | `/v1/futures/klines/{symbol}` | 获取期货合约K线数据 |
|
|
|
|
|
|
| 查询期货列表 | GET | `/v1/futures/symbols` | 获取所有期货合约列表 |
|
|
|
|
|
|
|
|
|
|
|
|
### 1.3 管理接口
|
|
|
|
|
|
|
|
|
|
|
|
| 接口 | 方法 | 路径 | 说明 |
|
|
|
|
|
|
|------|------|------|------|
|
|
|
|
|
|
| 健康检查 | GET | `/v1/admin/health` | 检查服务健康状态 |
|
|
|
|
|
|
| 数据源状态 | GET | `/v1/admin/source/status` | 查看数据源连接状态 |
|
|
|
|
|
|
| 适配器列表 | GET | `/v1/admin/adapters` | 查看所有适配器配置 |
|
|
|
|
|
|
|
|
|
|
|
|
### 1.4 测试接口 (Admin)
|
|
|
|
|
|
|
|
|
|
|
|
| 接口 | 方法 | 路径 | 说明 |
|
|
|
|
|
|
|------|------|------|------|
|
|
|
|
|
|
| 获取API测试列表 | GET | `/v1/admin/tests/api` | 获取对外接口测试列表 |
|
|
|
|
|
|
| 执行API测试 | POST | `/v1/admin/tests/api/run` | 执行指定的API测试 |
|
|
|
|
|
|
| **获取内部接口测试列表** | **GET** | **`/v1/admin/tests/internal`** | **获取对内SDK接口测试列表** |
|
|
|
|
|
|
| **执行内部接口测试** | **POST** | **`/v1/admin/tests/internal/run`** | **执行指定的对内接口测试** |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 二、对内接口 (Internal SDK 封装层)
|
|
|
|
|
|
|
|
|
|
|
|
对内接口是直接封装 AmazingData SDK 的内部方法,通过 `InternalDataService` 统一暴露。
|
|
|
|
|
|
|
|
|
|
|
|
### 2.1 访问路径
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# 获取 adapter
|
|
|
|
|
|
adapter = AdapterService.get_active_adapter("stock")
|
|
|
|
|
|
|
|
|
|
|
|
# 访问内部接口
|
|
|
|
|
|
adapter._internal.market.query_kline(...)
|
|
|
|
|
|
adapter._internal.base.get_code_list(...)
|
|
|
|
|
|
adapter._internal.info.get_equity_structure(...)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2.2 接口清单 (23个)
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.2.1 市场数据接口 (_MarketDataInternal)
|
|
|
|
|
|
|
|
|
|
|
|
| 序号 | 方法名 | 说明 | 对应 SDK |
|
|
|
|
|
|
|------|--------|------|----------|
|
|
|
|
|
|
| 1 | `query_kline()` | 查询K线数据 | `MarketData.query_kline()` |
|
|
|
|
|
|
| 2 | `query_snapshot()` | 查询快照数据 | `MarketData.query_snapshot()` |
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.2.2 基础数据接口 (_BaseDataInternal)
|
|
|
|
|
|
|
|
|
|
|
|
| 序号 | 方法名 | 说明 | 对应 SDK |
|
|
|
|
|
|
|------|--------|------|----------|
|
|
|
|
|
|
| 3 | `get_code_list()` | 获取股票代码列表 | `BaseData.get_code_list()` |
|
|
|
|
|
|
| 4 | `get_future_code_list()` | 获取期货代码列表 | `BaseData.get_future_code_list()` |
|
|
|
|
|
|
| 5 | `get_code_info()` | 获取代码详细信息 | `BaseData.get_code_info()` |
|
|
|
|
|
|
| 6 | `get_calendar()` | 获取交易日历 | `BaseData.get_calendar()` |
|
|
|
|
|
|
| 7 | `get_adj_factor()` | 获取复权因子 | `BaseData.get_adj_factor()` |
|
|
|
|
|
|
| 8 | `get_etf_pcf()` | 获取ETF申赎数据 | `BaseData.get_etf_pcf()` |
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.2.3 股本股东接口 (_InfoDataInternal)
|
|
|
|
|
|
|
|
|
|
|
|
| 序号 | 方法名 | 说明 | 对应 SDK | 特殊处理 |
|
|
|
|
|
|
|------|--------|------|----------|----------|
|
|
|
|
|
|
| 9 | `get_equity_structure()` | 获取股本结构 | `InfoData.get_equity_structure()` | - |
|
|
|
|
|
|
| 10 | `get_share_holder()` | 获取股东数据 | `InfoData.get_share_holder()` | **每次创建新实例** |
|
|
|
|
|
|
| 11 | `get_holder_num()` | 获取股东户数 | `InfoData.get_holder_num()` | - |
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.2.4 财务报表接口 (_InfoDataInternal)
|
|
|
|
|
|
|
|
|
|
|
|
| 序号 | 方法名 | 说明 | 对应 SDK |
|
|
|
|
|
|
|------|--------|------|----------|
|
|
|
|
|
|
| 12 | `get_income()` | 获取利润表 | `InfoData.get_income()` |
|
|
|
|
|
|
| 13 | `get_balance_sheet()` | 获取资产负债表 | `InfoData.get_balance_sheet()` |
|
|
|
|
|
|
| 14 | `get_cash_flow()` | 获取现金流量表 | `InfoData.get_cash_flow()` |
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.2.5 市场状态接口 (_InfoDataInternal)
|
|
|
|
|
|
|
|
|
|
|
|
| 序号 | 方法名 | 说明 | 对应 SDK |
|
|
|
|
|
|
|------|--------|------|----------|
|
|
|
|
|
|
| 15 | `get_history_stock_status()` | 获取历史股票状态 | `InfoData.get_history_stock_status()` |
|
|
|
|
|
|
| 16 | `get_margin_summary()` | 获取融资融券汇总 | `InfoData.get_margin_summary()` |
|
|
|
|
|
|
| 17 | `get_margin_detail()` | 获取融资融券明细 | `InfoData.get_margin_detail()` |
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.2.6 特色数据接口 (_InfoDataInternal)
|
|
|
|
|
|
|
|
|
|
|
|
| 序号 | 方法名 | 说明 | 对应 SDK |
|
|
|
|
|
|
|------|--------|------|----------|
|
|
|
|
|
|
| 18 | `get_long_hu_bang()` | 获取龙虎榜数据 | `InfoData.get_long_hu_bang()` |
|
|
|
|
|
|
| 19 | `get_block_trading()` | 获取大宗交易数据 | `InfoData.get_block_trading()` |
|
|
|
|
|
|
| 20 | `get_index_constituent()` | 获取指数成分股 | `InfoData.get_index_constituent()` |
|
|
|
|
|
|
| 21 | `get_index_weight()` | 获取指数权重 | `InfoData.get_index_weight()` |
|
|
|
|
|
|
|
|
|
|
|
|
#### 2.2.7 基金可转债接口 (_InfoDataInternal)
|
|
|
|
|
|
|
|
|
|
|
|
| 序号 | 方法名 | 说明 | 对应 SDK |
|
|
|
|
|
|
|------|--------|------|----------|
|
|
|
|
|
|
| 22 | `get_fund_share()` | 获取基金份额 | `InfoData.get_fund_share()` |
|
|
|
|
|
|
| 23 | `get_kzz_issuance()` | 获取可转债发行 | `InfoData.get_kzz_issuance()` |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 三、调用链详解
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 完整调用链示例:查询股票K线
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第1层:HTTP 客户端请求 │
|
|
|
|
|
|
│ GET http://localhost:8080/v1/stock/klines/000001.SZ?start=20240101&... │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第2层:API 路由层 (app/api/routes.py) │
|
|
|
|
|
|
│ @router.get("/stock/klines/{symbol}") │
|
|
|
|
|
|
│ def query_stock_klines(symbol, start, end, freq, adjust, db, api_key) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 职责:参数校验、认证、调用服务层 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第3层:服务层 (app/services/stock_service.py) │
|
|
|
|
|
|
│ class StockService: │
|
|
|
|
|
|
│ def query_klines(self, req: KLineQueryRequest) -> KLineData: │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 职责:业务逻辑、数据聚合、缓存策略 │
|
|
|
|
|
|
│ 步骤: │
|
|
|
|
|
|
│ 1. 先查询数据库 (StockRepository) │
|
|
|
|
|
|
│ 2. 如无数据,调用适配器获取 │
|
|
|
|
|
|
│ 3. 保存到数据库 │
|
|
|
|
|
|
│ 4. 返回格式化数据 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│ (如无缓存)
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第4层:适配器服务层 (app/services/adapter_service.py) │
|
|
|
|
|
|
│ class AdapterService: # 单例模式 │
|
|
|
|
|
|
│ def get_active_adapter(self, asset_class: str) -> DataSourceAdapter: │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 职责:管理适配器生命周期、连接管理、配置管理 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第5层:适配器实现 (app/adapters/amazingdata_adapter.py) │
|
|
|
|
|
|
│ class AmazingDataAdapter(DataSourceAdapter): │
|
|
|
|
|
|
│ async def fetch_klines(self, symbol, start, end, freq) -> List[KLine] │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 职责:适配器具体实现、数据格式转换、调用内部接口 │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 内部调用: │
|
|
|
|
|
|
│ self._internal.market.query_kline( │
|
|
|
|
|
|
│ code_list=[symbol], │
|
|
|
|
|
|
│ begin_date=int(start), │
|
|
|
|
|
|
│ end_date=int(end), │
|
|
|
|
|
|
│ period=10000 # SDK只支持日线 │
|
|
|
|
|
|
│ ) │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第6层:内部数据服务层 (app/adapters/internal_data_service.py) │
|
|
|
|
|
|
│ class _MarketDataInternal: │
|
|
|
|
|
|
│ def query_kline(self, code_list, begin_date, end_date, period): │
|
|
|
|
|
|
│ return self._market_data.query_kline(...) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 职责:SDK 封装、异常处理、日志记录 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第7层:SDK 层 (AmazingData) │
|
|
|
|
|
|
│ from AmazingData import MarketData │
|
|
|
|
|
|
│ market_data = MarketData(calendar) │
|
|
|
|
|
|
│ market_data.query_kline(code_list, begin_date, end_date, period) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 职责:与银河证券星耀数智服务器通信 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 内部接口测试调用链
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第1层:管理后台点击"测试"按钮 │
|
|
|
|
|
|
│ POST /v1/admin/tests/internal/run │
|
|
|
|
|
|
│ Body: { "id": "internal_market_query_kline", "params": {...} } │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第2层:Admin 路由 (app/api/admin_routes.py) │
|
|
|
|
|
|
│ @admin_router.post("/admin/tests/internal/run") │
|
|
|
|
|
|
│ async def run_internal_test(req: APITestRequest, token): │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 职责:获取 adapter、确保连接、调用测试服务 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第3层:测试服务 (app/services/test_service.py) │
|
|
|
|
|
|
│ class TestService: │
|
|
|
|
|
|
│ async def run_internal_test(self, adapter, req: APITestRequest): │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 职责:根据 test_id 分发到对应的内部接口调用 │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 例如 req.id == "internal_market_query_kline": │
|
|
|
|
|
|
│ adapter._internal.market.query_kline(...) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 例如 req.id == "internal_info_get_share_holder": │
|
|
|
|
|
|
│ adapter._internal.info.get_share_holder(...) # 创建新 InfoData 实例 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第4层:内部数据服务层 (app/adapters/internal_data_service.py) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 标准调用流程: │
|
|
|
|
|
|
│ def query_kline(self, ...): │
|
|
|
|
|
|
│ return self._market_data.query_kline(...) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 特殊处理(get_share_holder): │
|
|
|
|
|
|
│ def get_share_holder(self, ...): │
|
|
|
|
|
|
│ # 创建新的 InfoData 实例以避免 SDK 内部状态问题 │
|
|
|
|
|
|
│ import AmazingData as ad │
|
|
|
|
|
|
│ info_data = ad.InfoData() # <-- 新实例 │
|
|
|
|
|
|
│ return info_data.get_share_holder(...) │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 第5层:SDK 层 (AmazingData) │
|
|
|
|
|
|
│ 与银河证券星耀数智服务器通信 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 四、关键代码片段
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 内部数据服务初始化
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# app/adapters/amazingdata_adapter.py
|
|
|
|
|
|
class AmazingDataAdapter(DataSourceAdapter):
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
self._internal: Optional[InternalDataService] = None
|
|
|
|
|
|
|
|
|
|
|
|
def _do_login(self):
|
|
|
|
|
|
# 初始化 SDK 数据类
|
|
|
|
|
|
self._base_data = self._ad.BaseData()
|
|
|
|
|
|
self._info_data = self._ad.InfoData()
|
|
|
|
|
|
self._calendar = self._base_data.get_calendar()
|
|
|
|
|
|
self._market_data = self._ad.MarketData(self._calendar)
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化内部数据服务层(关键!)
|
|
|
|
|
|
self._internal = InternalDataService(self)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 内部数据服务封装
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# app/adapters/internal_data_service.py
|
|
|
|
|
|
class InternalDataService:
|
|
|
|
|
|
"""内部数据服务统一入口"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, ad):
|
|
|
|
|
|
"""
|
|
|
|
|
|
Args:
|
|
|
|
|
|
ad: AmazingDataAdapter 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)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 特殊处理:get_share_holder
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# app/adapters/internal_data_service.py
|
|
|
|
|
|
class _InfoDataInternal:
|
|
|
|
|
|
def get_share_holder(self, code_list, local_path, is_local, begin_date=None, end_date=None):
|
|
|
|
|
|
"""获取股东数据"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 创建新的 InfoData 实例以避免 SDK 内部状态问题
|
|
|
|
|
|
import AmazingData as ad
|
|
|
|
|
|
info_data = ad.InfoData()
|
|
|
|
|
|
return 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 {}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 五、接口测试清单
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 对外接口测试 (18个)
|
|
|
|
|
|
|
|
|
|
|
|
| 分类 | 测试项 | 路径 |
|
|
|
|
|
|
|------|--------|------|
|
|
|
|
|
|
| 股票接口 | 查询股票K线 | GET /v1/stock/klines/{symbol} |
|
|
|
|
|
|
| 股票接口 | 查询股票列表 | GET /v1/stock/symbols |
|
|
|
|
|
|
| 期货接口 | 查询期货K线 | GET /v1/futures/klines/{symbol} |
|
|
|
|
|
|
| 期货接口 | 查询期货列表 | GET /v1/futures/symbols |
|
|
|
|
|
|
| 管理接口 | 健康检查 | GET /v1/admin/health |
|
|
|
|
|
|
| ... | ... | ... |
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 对内接口测试 (23个)
|
|
|
|
|
|
|
|
|
|
|
|
详见第二节接口清单,每个内部接口都有对应的测试用例。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 六、配置说明
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 数据源配置 (config.json)
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"sources": {
|
|
|
|
|
|
"stock": {
|
|
|
|
|
|
"active": "amazingdata",
|
|
|
|
|
|
"list": {
|
|
|
|
|
|
"amazingdata": {
|
|
|
|
|
|
"type": "sdk",
|
|
|
|
|
|
"config": {
|
|
|
|
|
|
"username": "your_username",
|
|
|
|
|
|
"password": "your_password",
|
|
|
|
|
|
"host": "140.206.44.234",
|
|
|
|
|
|
"port": "8600",
|
|
|
|
|
|
"local_path": "./amazing_data_cache/",
|
|
|
|
|
|
"use_local_cache": "true"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 七、总结
|
|
|
|
|
|
|
|
|
|
|
|
1. **对外接口**:HTTP RESTful API,供外部系统调用
|
|
|
|
|
|
2. **对内接口**:SDK 封装层,通过 `adapter._internal` 访问
|
|
|
|
|
|
3. **调用链**:外部请求 → 路由 → 服务 → 适配器服务 → 适配器 → 内部接口 → SDK
|
|
|
|
|
|
4. **特殊处理**:`get_share_holder` 每次创建新的 `InfoData` 实例以避免 SDK 状态问题
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 八、数据流向与缓存策略
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 数据流向图
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 数据流向架构 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
|
|
|
|
|
|
外部请求
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────┐ ┌─────────┐ ┌─────────┐
|
|
|
|
|
|
│ API │────▶│ Service│────▶│Repository│
|
|
|
|
|
|
│ Layer │ │ Layer │ │ Layer │
|
|
|
|
|
|
└────┬────┘ └────┬────┘ └────┬────┘
|
|
|
|
|
|
│ │ │
|
|
|
|
|
|
│ │ ▼
|
|
|
|
|
|
│ │ ┌─────────┐
|
|
|
|
|
|
│ │ │ MySQL │
|
|
|
|
|
|
│ │ │ Database│
|
|
|
|
|
|
│ │ └────┬────┘
|
|
|
|
|
|
│ │ │
|
|
|
|
|
|
│ ▼ │
|
|
|
|
|
|
│ ┌─────────┐ │
|
|
|
|
|
|
│ │ Adapter │ │
|
|
|
|
|
|
│ │ Service │ │
|
|
|
|
|
|
│ └────┬────┘ │
|
|
|
|
|
|
│ │ │
|
|
|
|
|
|
▼ ▼ ▼
|
|
|
|
|
|
┌─────────────────────────────────────────┐
|
|
|
|
|
|
│ AmazingDataAdapter │
|
|
|
|
|
|
│ ┌─────────────────────────────────┐ │
|
|
|
|
|
|
│ │ InternalDataService │ │
|
|
|
|
|
|
│ │ ┌─────────┐ ┌─────────┐ │ │
|
|
|
|
|
|
│ │ │ _Market │ │ _Base │ ... │ │
|
|
|
|
|
|
│ │ │ _Data │ │ _Data │ │ │
|
|
|
|
|
|
│ │ └────┬────┘ └────┬────┘ │ │
|
|
|
|
|
|
│ └───────┼───────────┼────────────┘ │
|
|
|
|
|
|
└──────────┼───────────┼─────────────────┘
|
|
|
|
|
|
│ │
|
|
|
|
|
|
▼ ▼
|
|
|
|
|
|
┌─────────────────────────────────────────┐
|
|
|
|
|
|
│ AmazingData SDK │
|
|
|
|
|
|
│ MarketData BaseData InfoData │
|
|
|
|
|
|
└─────────────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────┐
|
|
|
|
|
|
│ 银河证券星耀数智服务器 │
|
|
|
|
|
|
└─────────────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 8.2 缓存策略
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# 数据获取优先级
|
|
|
|
|
|
1. 内存缓存 (Redis) - 最高优先级,实时数据
|
|
|
|
|
|
2. 数据库 (MySQL) - 持久化存储,历史数据
|
|
|
|
|
|
3. SDK 实时获取 - 兜底方案,网络请求
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**缓存规则:**
|
|
|
|
|
|
|
|
|
|
|
|
| 数据类型 | 缓存位置 | 缓存时间 | 更新策略 |
|
|
|
|
|
|
|---------|---------|---------|---------|
|
|
|
|
|
|
| K线数据 | MySQL + Redis | 永久 + 5分钟 | 懒加载 |
|
|
|
|
|
|
| 股票列表 | MySQL | 永久 | 每日同步 |
|
|
|
|
|
|
| 实时行情 | Redis | 1分钟 | 实时推送 |
|
|
|
|
|
|
| 财务数据 | MySQL | 永久 | 季度更新 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 九、错误处理机制
|
|
|
|
|
|
|
|
|
|
|
|
### 9.1 错误处理层级
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 错误处理流程 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
|
|
|
|
|
|
SDK 错误
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────┐
|
|
|
|
|
|
│ Internal Layer │ 捕获异常,记录日志,返回默认值
|
|
|
|
|
|
│ try/except │
|
|
|
|
|
|
└────────┬────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────┐
|
|
|
|
|
|
│ Adapter Layer │ 转换数据格式,处理空值
|
|
|
|
|
|
│ │
|
|
|
|
|
|
└────────┬────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────┐
|
|
|
|
|
|
│ Service Layer │ 业务逻辑判断,降级策略
|
|
|
|
|
|
│ │
|
|
|
|
|
|
└────────┬────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────┐
|
|
|
|
|
|
│ API Layer │ 包装错误响应,HTTP 状态码
|
|
|
|
|
|
│ │
|
|
|
|
|
|
└────────┬────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
客户端收到错误响应
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 9.2 错误码定义
|
|
|
|
|
|
|
|
|
|
|
|
| 错误码 | 说明 | 处理方式 |
|
|
|
|
|
|
|-------|------|---------|
|
|
|
|
|
|
| 0 | 成功 | - |
|
|
|
|
|
|
| 400 | 请求参数错误 | 检查参数格式 |
|
|
|
|
|
|
| 401 | 未授权 | 检查 API Key |
|
|
|
|
|
|
| 404 | 数据不存在 | 尝试从 SDK 获取 |
|
|
|
|
|
|
| 500 | 服务器内部错误 | 查看日志,联系管理员 |
|
|
|
|
|
|
| 503 | 数据源不可用 | 检查适配器连接状态 |
|
|
|
|
|
|
|
|
|
|
|
|
### 9.3 内部接口错误处理示例
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# app/adapters/internal_data_service.py
|
|
|
|
|
|
class _MarketDataInternal:
|
|
|
|
|
|
def query_kline(self, code_list, begin_date, end_date, period):
|
|
|
|
|
|
"""查询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}")
|
|
|
|
|
|
error(f" Params: code_list={code_list}, period={period}")
|
|
|
|
|
|
# 返回空结果而非抛出异常,保证服务可用性
|
|
|
|
|
|
return {}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 十、WebSocket 实时数据接口
|
|
|
|
|
|
|
|
|
|
|
|
### 10.1 WebSocket 连接
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
ws://localhost:8080/v1/stream
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 10.2 消息类型
|
|
|
|
|
|
|
|
|
|
|
|
| 消息类型 | 方向 | 说明 |
|
|
|
|
|
|
|---------|------|------|
|
|
|
|
|
|
| subscribe | C→S | 订阅标的 |
|
|
|
|
|
|
| unsubscribe | C→S | 取消订阅 |
|
|
|
|
|
|
| tick | S→C | 实时 tick 数据 |
|
|
|
|
|
|
| kline | S→C | 实时 K线数据 |
|
|
|
|
|
|
| error | S→C | 错误通知 |
|
|
|
|
|
|
|
|
|
|
|
|
### 10.3 订阅示例
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// 客户端订阅
|
|
|
|
|
|
const ws = new WebSocket('ws://localhost:8080/v1/stream');
|
|
|
|
|
|
|
|
|
|
|
|
ws.onopen = () => {
|
|
|
|
|
|
// 订阅股票
|
|
|
|
|
|
ws.send(JSON.stringify({
|
|
|
|
|
|
action: 'subscribe',
|
|
|
|
|
|
symbols: ['000001.SZ', '000002.SZ']
|
|
|
|
|
|
}));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
ws.onmessage = (event) => {
|
|
|
|
|
|
const data = JSON.parse(event.data);
|
|
|
|
|
|
console.log('收到数据:', data);
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 10.4 WebSocket 调用链
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
客户端订阅
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
WebSocket Endpoint
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
StreamService
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AdapterService.get_adapter("stock")
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AmazingDataAdapter.subscribe_tick(symbols, callback)
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AmazingData.MarketData.subscribe(...) # SDK 订阅
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
数据推送回调
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
WebSocket 发送给客户端
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 十一、数据同步机制
|
|
|
|
|
|
|
|
|
|
|
|
### 11.1 同步任务类型
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ 数据同步架构 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
|
|
|
|
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
|
|
|
|
│ 股票K线同步 │ │ 期货K线同步 │ │ 股票列表同步│ │ 财务数据同步│
|
|
|
|
|
|
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
└────────────────┴────────────────┴────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────┐
|
|
|
|
|
|
│ Sync Service │
|
|
|
|
|
|
└────────┬────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
┌────────────┼────────────┐
|
|
|
|
|
|
▼ ▼ ▼
|
|
|
|
|
|
┌─────────┐ ┌─────────┐ ┌─────────┐
|
|
|
|
|
|
│ Daily │ │ Weekly │ │ Manual │
|
|
|
|
|
|
│ Sync │ │ Sync │ │ Sync │
|
|
|
|
|
|
└─────────┘ └─────────┘ └─────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 11.2 同步接口
|
|
|
|
|
|
|
|
|
|
|
|
| 接口 | 方法 | 路径 | 说明 |
|
|
|
|
|
|
|------|------|------|------|
|
|
|
|
|
|
| 手动同步 | POST | `/v1/admin/data/sync` | 触发指定类型的数据同步 |
|
|
|
|
|
|
|
|
|
|
|
|
### 11.3 同步调用链
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
POST /v1/admin/data/sync
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AdminService.sync_data(sync_type)
|
|
|
|
|
|
│
|
|
|
|
|
|
├── sync_type = "base" → 同步基础K线
|
|
|
|
|
|
├── sync_type = "quote" → 同步行情指标
|
|
|
|
|
|
├── sync_type = "finance" → 同步财务数据
|
|
|
|
|
|
└── sync_type = "full" → 全量同步
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AmazingDataAdapter.fetch_stock_basic_info()
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
InternalDataService.base.get_code_list()
|
|
|
|
|
|
InternalDataService.base.get_code_info()
|
|
|
|
|
|
InternalDataService.info.get_equity_structure()
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
保存到 MySQL
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 十二、数据库表结构
|
|
|
|
|
|
|
|
|
|
|
|
### 12.1 表清单
|
|
|
|
|
|
|
|
|
|
|
|
| 表名 | 说明 | 数据来源 |
|
|
|
|
|
|
|------|------|---------|
|
|
|
|
|
|
| `stock_symbols` | 股票基础信息 | SDK BaseData |
|
|
|
|
|
|
| `stock_klines_1d_base` | 股票日线基础数据 | SDK MarketData |
|
|
|
|
|
|
| `stock_klines_1d_quote` | 股票日线行情指标 | SDK MarketData |
|
|
|
|
|
|
| `stock_klines_1d_finance` | 股票日线财务数据 | SDK InfoData |
|
|
|
|
|
|
| `futures_klines_1d_base` | 期货日线基础数据 | SDK MarketData |
|
|
|
|
|
|
| `futures_klines_1d_quote` | 期货日线行情指标 | SDK MarketData |
|
|
|
|
|
|
| `realtime_quotes` | 实时行情数据 | SDK MarketData |
|
|
|
|
|
|
| `sync_tasks` | 同步任务记录 | 系统生成 |
|
|
|
|
|
|
|
|
|
|
|
|
### 12.2 分表策略
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
stock_klines_*
|
|
|
|
|
|
├── stock_klines_1d_base # 日线基础K线 (OHLCV)
|
|
|
|
|
|
├── stock_klines_1d_quote # 日线行情指标 (均线、MACD等)
|
|
|
|
|
|
├── stock_klines_1d_finance # 日线财务数据 (市值、PE等)
|
|
|
|
|
|
├── stock_klines_1m_base # 分钟线基础数据
|
|
|
|
|
|
└── stock_klines_5m_base # 5分钟线基础数据
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 十三、部署与运维
|
|
|
|
|
|
|
|
|
|
|
|
### 13.1 启动流程
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 1. 启动服务
|
|
|
|
|
|
python -m uvicorn app.main:app --host 0.0.0.0 --port 8080
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 服务初始化流程
|
|
|
|
|
|
main.py
|
|
|
|
|
|
│
|
|
|
|
|
|
├── 加载配置 (config.json)
|
|
|
|
|
|
│
|
|
|
|
|
|
├── 初始化数据库连接
|
|
|
|
|
|
│
|
|
|
|
|
|
├── 初始化 Redis 连接
|
|
|
|
|
|
│
|
|
|
|
|
|
└── 启动 FastAPI 服务
|
|
|
|
|
|
│
|
|
|
|
|
|
└── 首次请求时懒加载适配器
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 13.2 适配器连接流程
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
首次请求
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AdapterService.get_active_adapter("stock")
|
|
|
|
|
|
│
|
|
|
|
|
|
├── 检查 active_adapters 缓存
|
|
|
|
|
|
│ ├── 存在且已连接 → 直接返回
|
|
|
|
|
|
│ └── 不存在或断开 → 创建新连接
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
AmazingDataAdapter.connect(config)
|
|
|
|
|
|
│
|
|
|
|
|
|
├── 登录 SDK
|
|
|
|
|
|
│ AmazingData.login(username, password, host, port)
|
|
|
|
|
|
│
|
|
|
|
|
|
├── 初始化数据类
|
|
|
|
|
|
│ BaseData(), InfoData(), MarketData()
|
|
|
|
|
|
│
|
|
|
|
|
|
└── 初始化 InternalDataService
|
|
|
|
|
|
│ self._internal = InternalDataService(self)
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
保存到 active_adapters 缓存
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 13.3 健康检查
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 健康检查接口
|
|
|
|
|
|
GET /v1/admin/health
|
|
|
|
|
|
|
|
|
|
|
|
# 响应示例
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 0,
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"status": "healthy",
|
|
|
|
|
|
"database": "connected",
|
|
|
|
|
|
"redis": "connected",
|
|
|
|
|
|
"adapters": {
|
|
|
|
|
|
"amazingdata": "connected"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 十四、开发指南
|
|
|
|
|
|
|
|
|
|
|
|
### 14.1 新增对外接口
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# 1. 在 routes.py 添加路由
|
|
|
|
|
|
@router.get("/stock/new_endpoint/{param}")
|
|
|
|
|
|
def new_endpoint(param: str, db: Session = Depends(get_db)):
|
|
|
|
|
|
service = StockService(db)
|
|
|
|
|
|
return service.new_method(param)
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 在 service 实现业务逻辑
|
|
|
|
|
|
class StockService:
|
|
|
|
|
|
def new_method(self, param):
|
|
|
|
|
|
# 业务逻辑
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 如需 SDK 数据,在 adapter 添加方法
|
|
|
|
|
|
class AmazingDataAdapter:
|
|
|
|
|
|
async def fetch_new_data(self, param):
|
|
|
|
|
|
return self._internal.xxx.method(param)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 14.2 新增对内接口
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
# 1. 在 internal_data_service.py 添加方法
|
|
|
|
|
|
class _InfoDataInternal:
|
|
|
|
|
|
def new_internal_method(self, param):
|
|
|
|
|
|
try:
|
|
|
|
|
|
return self._info_data.sdk_method(param)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
error(f"[_InfoDataInternal] new method failed: {e}")
|
|
|
|
|
|
return default_value
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 在 test_service.py 添加测试用例
|
|
|
|
|
|
APITestCase(
|
|
|
|
|
|
id="internal_info_new_method",
|
|
|
|
|
|
name="SDK: new_method",
|
|
|
|
|
|
description="...",
|
|
|
|
|
|
method="INTERNAL",
|
|
|
|
|
|
path="AmazingDataAdapter._internal.info.new_internal_method"
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 14.3 调用关系速查
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
需要调用 SDK 的接口:
|
|
|
|
|
|
↓
|
|
|
|
|
|
adapter._internal.{category}.{method}
|
|
|
|
|
|
|
|
|
|
|
|
category 可选值:
|
|
|
|
|
|
- market: 市场数据 (K线、快照)
|
|
|
|
|
|
- base: 基础数据 (代码列表、日历、复权因子)
|
|
|
|
|
|
- info: 信息数据 (财务、股本、融资融券等)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 十五、附录
|
|
|
|
|
|
|
|
|
|
|
|
### 15.1 SDK Period 参数对照表
|
|
|
|
|
|
|
|
|
|
|
|
| 周期 | SDK 参数值 | 说明 |
|
|
|
|
|
|
|------|-----------|------|
|
|
|
|
|
|
| 日线 | 10000 | 仅支持此值 |
|
|
|
|
|
|
| 1分钟 | - | SDK 不支持 |
|
|
|
|
|
|
| 5分钟 | - | SDK 不支持 |
|
|
|
|
|
|
|
|
|
|
|
|
> 注意:当前 SDK 版本 `query_kline` 仅支持 `period=10000`(日线),其他值会导致 `'NoneType' object cannot be interpreted as an integer` 错误。
|
|
|
|
|
|
|
|
|
|
|
|
### 15.2 文件结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
app/
|
|
|
|
|
|
├── api/
|
|
|
|
|
|
│ ├── routes.py # 对外接口路由
|
|
|
|
|
|
│ └── admin_routes.py # 管理后台路由
|
|
|
|
|
|
├── services/
|
|
|
|
|
|
│ ├── stock_service.py # 股票业务服务
|
|
|
|
|
|
│ ├── futures_service.py # 期货业务服务
|
|
|
|
|
|
│ ├── adapter_service.py # 适配器管理服务
|
|
|
|
|
|
│ └── test_service.py # 测试服务
|
|
|
|
|
|
├── adapters/
|
|
|
|
|
|
│ ├── amazingdata_adapter.py # 星耀数智适配器
|
|
|
|
|
|
│ ├── internal_data_service.py # 内部数据服务层
|
|
|
|
|
|
│ └── base.py # 适配器基类
|
|
|
|
|
|
├── models/ # 数据模型
|
|
|
|
|
|
├── repositories/ # 数据访问层
|
|
|
|
|
|
└── core/ # 核心工具
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 15.3 关键配置项
|
|
|
|
|
|
|
|
|
|
|
|
| 配置项 | 说明 | 默认值 |
|
|
|
|
|
|
|-------|------|--------|
|
|
|
|
|
|
| `sources.stock.active` | 当前激活的股票数据源 | `amazingdata` |
|
|
|
|
|
|
| `sources.stock.list.amazingdata.config.local_path` | 本地缓存路径 | `./amazing_data_cache/` |
|
|
|
|
|
|
| `sources.stock.list.amazingdata.config.use_local_cache` | 是否使用本地缓存 | `true` |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
**文档版本**: 1.0
|
|
|
|
|
|
**最后更新**: 2026-03-15
|
|
|
|
|
|
**适用版本**: Python Market Data Service
|