Files
market_sync/app/tasks/task_kline_index.py
T
gao 05635b76b9 chore: 重构前基线 — 9 个 sync task 全部 ok + akshare 移除 + mairui 资金流接入
状态:
- 9 个 sync task(stock_basic / kline_daily / kline_index / kline_5min /
  moneyflow / industry_sector / sector_features / share_snapshot / market_regime)
- 数据源:baostock + mairui + 雪球(pysnowball) + 新浪(4 个)
- 项目级约束:永远不用 akshare(已落实)
- kline_5min 改用 DB 快照统一全量/增量逻辑
- 零后端 Chrome 扩展 xueqiu_sync(独立项目)
2026-06-15 15:36:09 +08:00

110 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""同步任务:六大指数日 K 线。
数据流:新浪指数日K接口 → 写 kline_index + indices 表。"""
from __future__ import annotations
from datetime import datetime
from typing import Any
from app.core.db import ops as db_ops
from app.core.datasource.base import registry as ds_registry
from app.core.datasource.registry import is_source_ready
from app.core.sync.base import SyncTask, _is_market_closed
from app.core.sync.registry import mark_sync_blocked
from app.core.utils.logging import get_logger
logger = get_logger("sync.kline_index")
MAJOR_INDICES = {
"000001": {"name": "上证指数", "market": "CN", "category": "broad_market"},
"399001": {"name": "深证成指", "market": "CN", "category": "broad_market"},
"399006": {"name": "创业板指", "market": "CN", "category": "broad_market"},
"000300": {"name": "沪深300", "market": "CN", "category": "broad_market"},
"000905": {"name": "中证500", "market": "CN", "category": "broad_market"},
"000852": {"name": "中证1000", "market": "CN", "category": "broad_market"},
}
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")
if primary is None:
return {"status": "error", "message": "指数数据源均不可用(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)} 个指数...")
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:
# 000xxx 上证 → .SH399xxx 深证 → .SZ
if code.startswith("399"):
suffix = ".SZ"
else:
suffix = ".SH"
fetch_code = f"{code}{suffix}"
else:
fetch_code = code
df = primary.fetch_index_daily(fetch_code, start="1990-01-01", end="2099-12-31")
if df is None or df.empty:
raise RuntimeError("返回空数据")
# 盘中未收盘则去掉当天
if not _is_market_closed():
today = datetime.now().strftime("%Y-%m-%d")
df = df[df["trade_date"].dt.strftime("%Y-%m-%d") < today]
rows = [
{
"index_code": code,
"trade_date": r["trade_date"].strftime("%Y-%m-%d"),
"open": float(r["open"]),
"high": float(r["high"]),
"low": float(r["low"]),
"close": float(r["close"]),
"volume": float(r["volume"]),
}
for _, r in df.iterrows()
]
db_ops.upsert_kline_index(rows)
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,
)
ok_cnt += 1
results[code] = {
"rows": len(rows),
"date_min": rows[0]["trade_date"] if rows else "",
"date_max": rows[-1]["trade_date"] if rows else "",
}
except Exception as e:
fail_cnt += 1
results[code] = {"error": str(e)}
logger.warning(f"[index {code}] 失败: {e}")
self._progress(
message=f"指数 {i}/{total} 完成 ({ok_cnt}{fail_cnt}败)",
current=i, total=total, current_step=code,
)
msg = f"六大指数 {ok_cnt}{fail_cnt}"
return {
"status": "ok" if fail_cnt == 0 else "warning",
"message": msg,
"ok": ok_cnt, "fail": fail_cnt,
"details": results,
}