feat(HEL-421): 回补历史日历和指数并标记区间不完整
Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
25ff6bbe06
commit
a836cda1b2
@@ -0,0 +1,130 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Iterable
|
||||
|
||||
from datahub.db import HubDB
|
||||
from datahub.timeutil import iter_yyyymmdd, yyyymmdd
|
||||
|
||||
MISSING_SAMPLE_LIMIT = 10
|
||||
|
||||
|
||||
def coverage_payload(
|
||||
*,
|
||||
kind: str,
|
||||
start: str,
|
||||
end: str,
|
||||
expected: Iterable[str],
|
||||
available: Iterable[str],
|
||||
extra: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
start = yyyymmdd(start)
|
||||
end = yyyymmdd(end)
|
||||
expected_list = sorted({yyyymmdd(item) for item in expected if item})
|
||||
available_set = {yyyymmdd(item) for item in available if item}
|
||||
missing = [item for item in expected_list if item not in available_set]
|
||||
payload: dict[str, Any] = {
|
||||
"kind": kind,
|
||||
"complete": not missing,
|
||||
"requested_from": start,
|
||||
"requested_to": end,
|
||||
"available_from": min(available_set) if available_set else None,
|
||||
"available_to": max(available_set) if available_set else None,
|
||||
"expected_count": len(expected_list),
|
||||
"available_count": len(available_set),
|
||||
"missing_count": len(missing),
|
||||
"missing_sample": missing[:MISSING_SAMPLE_LIMIT],
|
||||
}
|
||||
if extra:
|
||||
payload.update(extra)
|
||||
return payload
|
||||
|
||||
|
||||
def calendar_coverage(db: HubDB, start: str, end: str, exchange: str = "SSE") -> dict[str, Any]:
|
||||
start = yyyymmdd(start)
|
||||
end = yyyymmdd(end)
|
||||
expected = list(iter_yyyymmdd(start, end))
|
||||
rows = db.fetchall(
|
||||
"SELECT cal_date FROM trade_calendar WHERE exchange = ? AND cal_date >= ? AND cal_date <= ?",
|
||||
(exchange, start, end),
|
||||
)
|
||||
return coverage_payload(
|
||||
kind="calendar",
|
||||
start=start,
|
||||
end=end,
|
||||
expected=expected,
|
||||
available=(row["cal_date"] for row in rows),
|
||||
extra={"exchange": exchange},
|
||||
)
|
||||
|
||||
|
||||
def published_range_coverage(
|
||||
db: HubDB,
|
||||
dataset: str,
|
||||
start: str,
|
||||
end: str,
|
||||
ts_code: str = "",
|
||||
table: str = "",
|
||||
) -> dict[str, Any]:
|
||||
start = yyyymmdd(start)
|
||||
end = yyyymmdd(end)
|
||||
calendar = calendar_coverage(db, start, end)
|
||||
open_rows = db.fetchall(
|
||||
"""
|
||||
SELECT cal_date FROM trade_calendar
|
||||
WHERE exchange = 'SSE' AND is_open = 1 AND cal_date >= ? AND cal_date <= ?
|
||||
ORDER BY cal_date
|
||||
""",
|
||||
(start, end),
|
||||
)
|
||||
expected_open = [row["cal_date"] for row in open_rows]
|
||||
pubs = db.fetchall(
|
||||
"""
|
||||
SELECT trade_date, active_batch FROM publications
|
||||
WHERE dataset = ? AND trade_date >= ? AND trade_date <= ?
|
||||
ORDER BY trade_date
|
||||
""",
|
||||
(dataset, start, end),
|
||||
)
|
||||
published_dates = [row["trade_date"] for row in pubs]
|
||||
available = list(published_dates)
|
||||
extra: dict[str, Any] = {
|
||||
"dataset": dataset,
|
||||
"calendar_complete": calendar["complete"],
|
||||
"calendar_missing_count": calendar["missing_count"],
|
||||
}
|
||||
if ts_code and table and pubs:
|
||||
present_code: list[str] = []
|
||||
for pub in pubs:
|
||||
hit = db.fetchone(
|
||||
f"SELECT 1 AS ok FROM {table} WHERE trade_date = ? AND batch_id = ? AND ts_code = ? LIMIT 1",
|
||||
(pub["trade_date"], pub["active_batch"], ts_code),
|
||||
)
|
||||
if hit:
|
||||
present_code.append(pub["trade_date"])
|
||||
available = present_code
|
||||
extra["code"] = ts_code
|
||||
payload = coverage_payload(
|
||||
kind="published_range",
|
||||
start=start,
|
||||
end=end,
|
||||
expected=expected_open,
|
||||
available=available,
|
||||
extra=extra,
|
||||
)
|
||||
if not calendar["complete"]:
|
||||
payload["complete"] = False
|
||||
payload["calendar_missing_sample"] = calendar["missing_sample"]
|
||||
return payload
|
||||
|
||||
|
||||
def point_coverage(trade_date: str, dataset: str = "") -> dict[str, Any]:
|
||||
day = yyyymmdd(trade_date)
|
||||
payload = coverage_payload(
|
||||
kind="point",
|
||||
start=day,
|
||||
end=day,
|
||||
expected=[day],
|
||||
available=[day],
|
||||
extra={"dataset": dataset} if dataset else None,
|
||||
)
|
||||
return payload
|
||||
Reference in New Issue
Block a user