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(独立项目)
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"""同步任务:六大指数日 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 上证 → .SH;399xxx 深证 → .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,
|
||||
}
|
||||
Reference in New Issue
Block a user