From aa4dba4f70749b459c3922ce68610c4d78e644e6 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 22 Sep 2026 01:35:29 +0800 Subject: Fix bond quote probe to use live convertible-bond spot data (3d-15) --- src/FundLab.Api/akshare_collector.py | 43 ++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/FundLab.Api/akshare_collector.py b/src/FundLab.Api/akshare_collector.py index 6543923..652f32d 100644 --- a/src/FundLab.Api/akshare_collector.py +++ b/src/FundLab.Api/akshare_collector.py @@ -150,22 +150,47 @@ def nav(code): } +def bond_maturity_date(code): + # Maturity is a best-effort enrichment: the quote endpoint has no expiry + # date, so a failure here only means maturity_date stays null. + try: + frame = ak.bond_zh_cov_info(symbol=code, indicator="基本信息") + if frame is None or frame.empty: + return None + return date_text(frame.iloc[0].get("EXPIRE_DATE")) + except Exception: + return None + + def bond_quote(code): code = text(code) if code is None or not re.fullmatch(r"\d{6}", code): raise ValueError("bond code must contain exactly six digits") - frame = ak.bond_zh_cov_info(symbol=code, indicator="基本信息") + frame = ak.bond_zh_hs_cov_spot() if frame is None or frame.empty: - raise ValueError(f"AKShare returned no bond info for {code}") + raise ValueError(f"no valid quote for {code}") + + prefix_rank = {"sh": 0, "sz": 1, "bj": 2} + candidates = [] + + for _, row in frame.iterrows(): + symbol = text(row.get("symbol")) or "" + row_code = text(row.get("code")) or "" + if row_code != code and not symbol.endswith(code): + continue + + price = decimal_text(row.get("trade")) + if price is None or Decimal(price) <= 0: + continue + + prefix = symbol[:2].lower() + candidates.append((prefix_rank.get(prefix, 3), symbol, text(row.get("name")), price)) - row = frame.iloc[0] - name = text(row.get("SECURITY_NAME_ABBR")) - price = decimal_text(row.get("CURRENT_BOND_PRICE")) - maturity_date = date_text(row.get("EXPIRE_DATE")) + if not candidates: + raise ValueError(f"no valid quote for {code}") - if name is None and price is None and maturity_date is None: - raise ValueError(f"AKShare returned no usable bond quote fields for {code}") + _, _, name, price = sorted(candidates, key=lambda item: (item[0], item[1]))[0] return { "schema_version": SCHEMA_VERSION, @@ -177,7 +202,7 @@ def bond_quote(code): "quote": { "name": name, "price": price, - "maturity_date": maturity_date, + "maturity_date": bond_maturity_date(code), }, } -- cgit v1.2.3