summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/probe_akshare_nav_dates.py81
1 files changed, 81 insertions, 0 deletions
diff --git a/scripts/probe_akshare_nav_dates.py b/scripts/probe_akshare_nav_dates.py
new file mode 100644
index 0000000..255cf91
--- /dev/null
+++ b/scripts/probe_akshare_nav_dates.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+"""Runtime probe for real AKShare NAV dates.
+
+This is the "can the host actually reach AKShare?" check that backs the
+`GET /api/market/nav-dates` endpoint. It talks to AKShare directly (no mocks),
+memoises the akshare frame lookup with functools.lru_cache, and prints a single
+JSON object on success:
+
+ {"code": "000001", "dates": ["2026-09-21", "2026-09-18", ...]}
+
+On any failure (missing dependency, network error, unusable payload) it writes a
+clear message to stderr and exits non-zero so callers can treat it as a skipped /
+unavailable probe instead of inventing data.
+"""
+
+import argparse
+import functools
+import json
+import sys
+
+import akshare as ak
+
+
[email protected]_cache(maxsize=8)
+def fetch_nav_frame(code: str):
+ """Fetch the since-inception unit-NAV frame for a fund (cached per code)."""
+ return ak.fund_open_fund_info_em(
+ symbol=code,
+ indicator="单位净值走势",
+ period="成立来",
+ )
+
+
+def recent_nav_dates(code: str, limit: int = 5):
+ """Return the most recent distinct NAV dates (descending) for a fund code."""
+ if len(code) != 6 or not code.isdigit():
+ raise ValueError("fund code must contain exactly six digits")
+
+ frame = fetch_nav_frame(code)
+ if frame is None or frame.empty:
+ raise RuntimeError(f"AKShare returned no NAV observations for {code}")
+
+ column = "净值日期"
+ if column not in frame.columns:
+ raise RuntimeError(f"AKShare payload for {code} is missing the {column} column")
+
+ dates = []
+ for value in frame[column].tolist():
+ text = str(value).strip()[:10]
+ if len(text) == 10 and text not in dates:
+ dates.append(text)
+
+ dates.sort(reverse=True)
+ return dates[:limit]
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Probe real AKShare NAV dates for a fund code",
+ )
+ parser.add_argument("--code", default="000001")
+ parser.add_argument("--limit", type=int, default=5)
+ args = parser.parse_args()
+
+ try:
+ dates = recent_nav_dates(args.code, args.limit)
+ except Exception as error: # noqa: BLE001 - probe reports the concrete reason
+ print(f"AKShare NAV-date probe failed for {args.code}: {error}", file=sys.stderr)
+ return 2
+
+ if not dates:
+ print(f"AKShare NAV-date probe found no dates for {args.code}", file=sys.stderr)
+ return 2
+
+ json.dump({"code": args.code, "dates": dates}, sys.stdout, ensure_ascii=False)
+ sys.stdout.write("\n")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())