You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

957 lines
47 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 市场数据服务接口文档
## 架构概览
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 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