summaryrefslogtreecommitdiff
path: root/src/FundLab.Api/akshare_collector.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/FundLab.Api/akshare_collector.py')
-rw-r--r--src/FundLab.Api/akshare_collector.py112
1 files changed, 94 insertions, 18 deletions
diff --git a/src/FundLab.Api/akshare_collector.py b/src/FundLab.Api/akshare_collector.py
index f9a0f06..2fb837e 100644
--- a/src/FundLab.Api/akshare_collector.py
+++ b/src/FundLab.Api/akshare_collector.py
@@ -162,14 +162,12 @@ def bond_maturity_date(code):
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")
-
+def bond_quote_from_spot(code):
+ # Realtime convertible-bond snapshot (sina). Gives name/trade but no date or
+ # accrued-interest fields; returns None when the code carries no valid price.
frame = ak.bond_zh_hs_cov_spot()
if frame is None or frame.empty:
- raise ValueError(f"no valid quote for {code}")
+ return None
prefix_rank = {"sh": 0, "sz": 1, "bj": 2}
candidates = []
@@ -188,25 +186,103 @@ def bond_quote(code):
candidates.append((prefix_rank.get(prefix, 3), symbol, text(row.get("name")), price))
if not candidates:
- raise ValueError(f"no valid quote for {code}")
+ return None
- _, _, name, price = sorted(candidates, key=lambda item: (item[0], item[1]))[0]
+ _, symbol, name, price = sorted(candidates, key=lambda item: (item[0], item[1]))[0]
+ revision = source_revision()
return {
- "schema_version": SCHEMA_VERSION,
- "operation": "bond-quote",
"source": "akshare",
- "source_revision": source_revision(),
- "collected_at": collected_at(),
- "instrument": {"code": code},
- "quote": {
- "name": name,
- "price": price,
- "maturity_date": bond_maturity_date(code),
- },
+ "source_revision": f"{revision}/hs-cov-spot",
+ "name": name,
+ "price": price,
+ "clean_price": price,
+ "accrued_interest": None,
+ "date": None,
+ "maturity_date": bond_maturity_date(code),
+ "symbol": symbol,
}
+def bond_quote_from_daily(code):
+ # Exchange bond daily history (sina). The venue is not derivable from the
+ # six-digit code alone, so try both prefixes; the first that returns rows
+ # wins. Gives a settled close and the trade date but no accrued-interest
+ # breakdown.
+ for prefix in ("sh", "sz"):
+ symbol = f"{prefix}{code}"
+
+ try:
+ frame = ak.bond_zh_hs_daily(symbol=symbol)
+ except Exception:
+ continue
+
+ if frame is None or frame.empty or "date" not in frame.columns or "close" not in frame.columns:
+ continue
+
+ rows = []
+
+ for _, row in frame.iterrows():
+ row_date = date_text(row.get("date"))
+ close = decimal_text(row.get("close"))
+ if row_date is None or close is None or Decimal(close) <= 0:
+ continue
+ rows.append((row_date, close))
+
+ if not rows:
+ continue
+
+ rows.sort(key=lambda item: item[0])
+ row_date, close = rows[-1]
+ revision = source_revision()
+
+ return {
+ "source": "akshare",
+ "source_revision": f"{revision}/hs-bond-daily",
+ "name": None,
+ "price": close,
+ "clean_price": close,
+ "accrued_interest": None,
+ "date": row_date,
+ "maturity_date": bond_maturity_date(code),
+ "symbol": symbol,
+ }
+
+ 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")
+
+ for attempt in (bond_quote_from_spot, bond_quote_from_daily):
+ try:
+ quote = attempt(code)
+ except Exception:
+ quote = None
+
+ if quote is not None:
+ return {
+ "schema_version": SCHEMA_VERSION,
+ "operation": "bond-quote",
+ "source": quote["source"],
+ "source_revision": quote["source_revision"],
+ "collected_at": collected_at(),
+ "instrument": {"code": code},
+ "quote": {
+ "name": quote["name"],
+ "price": quote["price"],
+ "clean_price": quote["clean_price"],
+ "accrued_interest": quote["accrued_interest"],
+ "date": quote["date"],
+ "maturity_date": quote["maturity_date"],
+ },
+ }
+
+ raise ValueError(f"no valid bond quote for {code}")
+
+
def first_positive_decimal(*values):
for value in values:
price = decimal_text(value)