diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-22 08:58:52 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-22 08:58:52 +0800 |
| commit | 72f466b9b3ea6ece726e6b182cfc36bbd3f6fa15 (patch) | |
| tree | 8d1bf9285314eb37ce47525e4d0c052fcdf08ca8 /src/FundLab.Api/akshare_collector.py | |
| parent | d4b0c26b396bfcf1be029d8db3a1c0fc033a6765 (diff) | |
| download | fund-lab-72f466b9b3ea6ece726e6b182cfc36bbd3f6fa15.tar.gz | |
Add stock probe market/adjust/suspension fields and StockRules (3d-30 B1-B2)
Diffstat (limited to 'src/FundLab.Api/akshare_collector.py')
| -rw-r--r-- | src/FundLab.Api/akshare_collector.py | 61 |
1 files changed, 47 insertions, 14 deletions
diff --git a/src/FundLab.Api/akshare_collector.py b/src/FundLab.Api/akshare_collector.py index 9234d81..aed053a 100644 --- a/src/FundLab.Api/akshare_collector.py +++ b/src/FundLab.Api/akshare_collector.py @@ -333,17 +333,26 @@ def first_positive_decimal(*values): def stock_price_from_bid_ask(code): # Single-symbol realtime snapshot (eastmoney). Returns None when the venue - # rejects the symbol or carries no valid last price. + # rejects the symbol or carries no valid last price. Volume is returned so + # callers can flag a suspended session (no trades => zero volume). frame = ak.stock_bid_ask_em(symbol=code) if frame is None or frame.empty: return None + price = None + volume = None + for _, row in frame.iterrows(): item = text(row.get("item")) if item in ("最新", "最新价"): - return first_positive_decimal(row.get("value")) + price = first_positive_decimal(row.get("value")) + elif item in ("总量", "成交量", "总手"): + volume = decimal_text(row.get("value")) - return None + if price is None: + return None + + return price, volume def stock_from_snapshot(code): @@ -407,17 +416,22 @@ def stock_daily_observations(frame, date_key, close_key, volume_key, amount_key) return rows -def stock_market_symbol(code): +def stock_market(code): + # A-share exchange mapping from the six-digit code prefix. if code[0] in ("6", "9"): - return f"sh{code}" + return "sh" if code[0] in ("0", "3", "2"): - return f"sz{code}" + return "sz" if code[0] in ("4", "8"): - return f"bj{code}" - return f"sh{code}" + return "bj" + return "sh" -def stock_daily(code, days_text): +def stock_market_symbol(code): + return f"{stock_market(code)}{code}" + + +def stock_daily(code, days_text, adjust_text=None): code = text(code) if code is None or not re.fullmatch(r"\d{6}", code): raise ValueError("stock code must contain exactly six digits") @@ -433,10 +447,12 @@ def stock_daily(code, days_text): if days < 1 or days > 30: raise ValueError("days must be an integer between 1 and 30") + adjust = adjust_text if adjust_text in ("qfq", "hfq") else "" + symbol = stock_market_symbol(code) providers = [ - ((ak.stock_zh_a_hist, {"symbol": code, "period": "daily", "adjust": ""}), "日期", "收盘", "成交量", "成交额"), - ((ak.stock_zh_a_daily, {"symbol": symbol, "adjust": ""}), "date", "close", "volume", "amount"), + ((ak.stock_zh_a_hist, {"symbol": code, "period": "daily", "adjust": adjust}), "日期", "收盘", "成交量", "成交额"), + ((ak.stock_zh_a_daily, {"symbol": symbol, "adjust": adjust}), "date", "close", "volume", "amount"), ((ak.stock_zh_a_hist_tx, {"symbol": symbol}), "date", "close", "volume", "amount"), ] @@ -460,6 +476,8 @@ def stock_daily(code, days_text): "source_revision": source_revision(), "collected_at": collected_at(), "instrument": {"code": code}, + "market": stock_market(code), + "adjust": adjust or "none", "observations": observations[-days:], } @@ -473,11 +491,15 @@ def stock_quote(code): name = None price = None + volume = None try: - price = stock_price_from_bid_ask(code) + direct = stock_price_from_bid_ask(code) except Exception: - price = None + direct = None + + if direct is not None: + price, volume = direct if price is None: found = stock_from_snapshot(code) @@ -485,6 +507,13 @@ def stock_quote(code): raise ValueError(f"no valid quote for {code}") name, price = found + suspended = False + if volume is not None: + try: + suspended = Decimal(volume) == 0 + except Exception: + suspended = False + return { "schema_version": SCHEMA_VERSION, "operation": "stock-quote", @@ -496,6 +525,9 @@ def stock_quote(code): "name": name, "price": price, "currency": "CNY", + "market": stock_market(code), + "adjust": "none", + "suspended": suspended, }, } @@ -506,6 +538,7 @@ def main(): parser.add_argument("--query") parser.add_argument("--code") parser.add_argument("--days") + parser.add_argument("--adjust", choices=("none", "qfq", "hfq")) args = parser.parse_args() try: @@ -518,7 +551,7 @@ def main(): elif args.operation == "stock-quote": payload = stock_quote(args.code) else: - payload = stock_daily(args.code, args.days) + payload = stock_daily(args.code, args.days, args.adjust) json.dump(payload, sys.stdout, ensure_ascii=False, separators=(",", ":")) sys.stdout.write("\n") |
