feat: QMT Bridge 设为主数据源,新增指数日K支持及数据源文档

- QmtBridgeSource: provides 增加 index_daily,新增 fetch_index_daily 方法
- task_kline_index: 优先级改为 qmt_bridge → mairui → sina 三级降级
- task_kline_5min: 优先用 qmt_bridge,不可用时降级 mairui
- task_kline_daily: 优先级加入 qmt_bridge(首位)
- config: 新增 qmt_bridge_url 配置项
- registry: qmt_bridge 注册信息同步更新
- docs: 新增 DATASETS_AND_SOURCES.md,完整说明数据集与数据源依赖关系
- AGENTS.md: 同步更新指数数据源描述
This commit is contained in:
gao
2026-07-23 23:39:59 +08:00
parent 4516f6d75a
commit daef609e8b
9 changed files with 448 additions and 21 deletions
+26 -12
View File
@@ -1,6 +1,6 @@
"""同步任务:六大指数日 K 线。
数据流:新浪指数日K接口 → 写 kline_index + indices 表。"""
数据流:QMT Bridge(主)→ 麦蕊 → 新浪 逐级 fallback → 写 kline_index + indices 表。"""
from __future__ import annotations
from datetime import datetime
@@ -24,32 +24,46 @@ MAJOR_INDICES = {
"000852": {"name": "中证1000", "market": "CN", "category": "broad_market"},
}
# 数据源优先级:qmt_bridge(免限速免凭证)→ mairui → sina
SOURCE_PRIORITY = [
("datasource_qmt_bridge", True, "qmt_bridge_index_daily"),
("datasource_mairui", True, "mairui_index_daily"),
("datasource_xinlang", False, "sina_index_daily"),
]
class SyncKlineIndex(SyncTask):
dataset_id = "kline_index"
def _run(self, *, trigger_source: str = "manual", **kwargs) -> dict[str, Any]:
# 优先 mairui(指数无单日空窗问题),sina 作为 fallback
primary = ds_registry.get("datasource_mairui")
use_mairui = primary is not None and is_source_ready(primary.key)[0]
if not use_mairui:
primary = ds_registry.get("datasource_xinlang")
# 优先级选择第一个就绪的数据源
primary = None
use_suffix = True
source_label = ""
for key, suffix, label in SOURCE_PRIORITY:
src = ds_registry.get(key)
if src is not None and is_source_ready(src.key)[0]:
primary = src
use_suffix = suffix
source_label = label
break
if primary is None:
return {"status": "error", "message": "指数数据源均不可用(mairui + 新浪)"}
return {"status": "error", "message": "指数数据源均不可用(qmt_bridge + mairui + 新浪)"}
ok, reason = is_source_ready(primary.key)
if not ok:
mark_sync_blocked(self.dataset_id, message=f"{primary.key} 未就绪: {reason}")
return {"status": "blocked", "message": f"{primary.key} 未就绪: {reason}"}
self._progress(message=f"开始同步 {len(MAJOR_INDICES)} 个指数...")
self._progress(message=f"开始同步 {len(MAJOR_INDICES)} 个指数,数据源: {source_label}...")
total = len(MAJOR_INDICES)
ok_cnt = fail_cnt = 0
results = {}
for i, (code, info) in enumerate(MAJOR_INDICES.items(), 1):
try:
# mairui 需带交易所后缀(如 000300.SH),sina 用纯6位
if use_mairui:
# qmt_bridge / mairui 需带交易所后缀(如 000300.SH),sina 用纯6位
if use_suffix:
# 000xxx 上证 → .SH399xxx 深证 → .SZ
if code.startswith("399"):
suffix = ".SZ"
@@ -82,7 +96,7 @@ class SyncKlineIndex(SyncTask):
db_ops.upsert_index(
index_code=code, index_name=info["name"],
market=info["market"], category=info["category"],
source="mairui_index_daily" if use_mairui else "sina_index_daily", enabled=True,
source=source_label, enabled=True,
)
ok_cnt += 1
results[code] = {
@@ -100,7 +114,7 @@ class SyncKlineIndex(SyncTask):
current=i, total=total, current_step=code,
)
msg = f"六大指数 {ok_cnt}{fail_cnt}"
msg = f"六大指数 {ok_cnt}{fail_cnt}{source_label}"
return {
"status": "ok" if fail_cnt == 0 else "warning",
"message": msg,