summaryrefslogtreecommitdiff
path: root/src/FundLab.Api
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-22 20:42:27 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-22 20:42:27 +0800
commit248b50c8fd057c87a852f8306fc4585905880abc (patch)
tree254102f8891e2fa6c62a2bef31bf13be720c5a91 /src/FundLab.Api
parent75523a0f99ad86fd5cd01ef36f2091e6b670bf9d (diff)
downloadfund-lab-248b50c8fd057c87a852f8306fc4585905880abc.tar.gz
Fetch latest fund NAV via ETF endpoint so T-day subscriptions can confirm (3d-38)
Diffstat (limited to 'src/FundLab.Api')
-rw-r--r--src/FundLab.Api/akshare_collector.py61
1 files changed, 44 insertions, 17 deletions
diff --git a/src/FundLab.Api/akshare_collector.py b/src/FundLab.Api/akshare_collector.py
index aed053a..6534511 100644
--- a/src/FundLab.Api/akshare_collector.py
+++ b/src/FundLab.Api/akshare_collector.py
@@ -2,7 +2,9 @@
"""Small raw-data adapter for the AKShare endpoints used by Fund Lab."""
import argparse
+import contextlib
import datetime as dt
+import io
import json
import re
import sys
@@ -113,28 +115,53 @@ def nav(code):
if not re.fullmatch(r"\d{6}", code):
raise ValueError("fund code must contain exactly six digits")
- frame = ak.fund_open_fund_info_em(
+ observations = []
+ seen = set()
+
+ def append_observations(frame):
+ for _, row in frame.iterrows():
+ nav_date = date_text(row.get("净值日期"))
+ nav_value = decimal_text(row.get("单位净值"))
+ if nav_date is None or nav_value is None or nav_date in seen:
+ continue
+
+ seen.add(nav_date)
+ observations.append(
+ {
+ "nav_date": nav_date,
+ "published_at": None,
+ "nav": nav_value,
+ "accumulated_nav": None,
+ "daily_return": decimal_text(row.get("日增长率")),
+ }
+ )
+
+ # Long history via the unit-NAV trend endpoint. It can lag the latest
+ # trading day, so the recent window below is merged in on top of it.
+ history = ak.fund_open_fund_info_em(
symbol=code,
indicator="单位净值走势",
period="成立来",
)
- observations = []
-
- for _, row in frame.iterrows():
- nav_date = date_text(row.get("净值日期"))
- nav_value = decimal_text(row.get("单位净值"))
- if nav_date is None or nav_value is None:
- continue
+ append_observations(history)
- observations.append(
- {
- "nav_date": nav_date,
- "published_at": None,
- "nav": nav_value,
- "accumulated_nav": None,
- "daily_return": decimal_text(row.get("日增长率")),
- }
- )
+ # Recent window from the ETF/LOF NAV endpoint, which publishes the newest
+ # trading day earlier than the trend endpoint (this is what makes T-day
+ # subscription confirmations possible). Only missing dates are appended, so
+ # historical values are never overwritten.
+ try:
+ today = dt.date.today()
+ with contextlib.redirect_stderr(io.StringIO()):
+ recent = ak.fund_etf_fund_info_em(
+ fund=code,
+ start_date=(today - dt.timedelta(days=30)).strftime("%Y%m%d"),
+ end_date=today.strftime("%Y%m%d"),
+ )
+ if recent is not None and not recent.empty:
+ append_observations(recent.sort_values("净值日期"))
+ except Exception:
+ # Best-effort: a failure here leaves the trend history untouched.
+ pass
if not observations:
raise ValueError(f"AKShare returned no usable NAV observations for {code}")