From 248b50c8fd057c87a852f8306fc4585905880abc Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 22 Sep 2026 20:42:27 +0800 Subject: Fetch latest fund NAV via ETF endpoint so T-day subscriptions can confirm (3d-38) --- src/FundLab.Api/akshare_collector.py | 61 ++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 17 deletions(-) (limited to 'src') 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}") -- cgit v1.2.3