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.py58
1 files changed, 49 insertions, 9 deletions
diff --git a/src/FundLab.Api/akshare_collector.py b/src/FundLab.Api/akshare_collector.py
index 2fb837e..9234d81 100644
--- a/src/FundLab.Api/akshare_collector.py
+++ b/src/FundLab.Api/akshare_collector.py
@@ -150,16 +150,34 @@ 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.
+def bond_profile(code):
+ # Convertible-bond reference data (eastmoney). Best-effort enrichment: any
+ # failure leaves every descriptive field null rather than fabricating one.
+ # Plain treasury/policy bonds are not covered by this endpoint, so those
+ # fields legitimately stay null for them.
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"))
+ return {}
+ row = frame.iloc[0]
+ coupon_ir = decimal_text(row.get("COUPON_IR"))
+ coupon_rate = None
+ if coupon_ir is not None:
+ coupon_rate = format(Decimal(coupon_ir) / Decimal(100), "f")
+ return {
+ "par_value": decimal_text(row.get("PAR_VALUE")),
+ "issue_price": decimal_text(row.get("ISSUE_PRICE")),
+ "value_date": date_text(row.get("VALUE_DATE")),
+ "listing_date": date_text(row.get("LISTING_DATE")),
+ "maturity_date": date_text(row.get("EXPIRE_DATE")),
+ "pay_interest_day": text(row.get("PAY_INTEREST_DAY")),
+ "coupon_rate": coupon_rate,
+ "coupon_rate_explain": text(row.get("INTEREST_RATE_EXPLAIN")),
+ "bond_expire_years": text(row.get("BOND_EXPIRE")),
+ "rating": text(row.get("RATING")),
+ }
except Exception:
- return None
+ return {}
def bond_quote_from_spot(code):
@@ -199,7 +217,6 @@ def bond_quote_from_spot(code):
"clean_price": price,
"accrued_interest": None,
"date": None,
- "maturity_date": bond_maturity_date(code),
"symbol": symbol,
}
@@ -244,7 +261,6 @@ def bond_quote_from_daily(code):
"clean_price": close,
"accrued_interest": None,
"date": row_date,
- "maturity_date": bond_maturity_date(code),
"symbol": symbol,
}
@@ -263,6 +279,19 @@ def bond_quote(code):
quote = None
if quote is not None:
+ profile = bond_profile(code)
+ has_price = quote["price"] is not None
+ has_profile = profile.get("value_date") is not None or profile.get("maturity_date") is not None
+
+ if has_price and has_profile:
+ data_status = "complete"
+ elif has_price:
+ data_status = "price_only"
+ elif has_profile:
+ data_status = "profile_only"
+ else:
+ data_status = "missing"
+
return {
"schema_version": SCHEMA_VERSION,
"operation": "bond-quote",
@@ -276,7 +305,18 @@ def bond_quote(code):
"clean_price": quote["clean_price"],
"accrued_interest": quote["accrued_interest"],
"date": quote["date"],
- "maturity_date": quote["maturity_date"],
+ "publish_date": quote["date"],
+ "par_value": profile.get("par_value"),
+ "issue_price": profile.get("issue_price"),
+ "value_date": profile.get("value_date"),
+ "listing_date": profile.get("listing_date"),
+ "maturity_date": profile.get("maturity_date"),
+ "pay_interest_day": profile.get("pay_interest_day"),
+ "coupon_rate": profile.get("coupon_rate"),
+ "coupon_rate_explain": profile.get("coupon_rate_explain"),
+ "bond_expire_years": profile.get("bond_expire_years"),
+ "rating": profile.get("rating"),
+ "data_status": data_status,
},
}