From 44eef95718c22957297c86006792330abf1a11de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=A5=E5=BE=AE?= Date: Mon, 22 Jun 2026 23:53:31 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=8C=87=E6=95=B0K=E7=BA=BF=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fetch_kline 修复两个bug: 1. _market_prefix 不认识 sh/sz/hk 开头指数代码 2. 指数code自带前缀,API key不要重复拼接 3. fqkline端点同样支持指数,不需要mkline 4. refresh_mtf_cache 加入6大指数自动缓存 上证指数(4163) MA5=4110 MA10=4053 MA20=4070 周线横盘 月线震荡上升 --- __pycache__/multi_timeframe.cpython-312.pyc | Bin 25699 -> 26461 bytes data/multi_tf_cache.json | 1257 +++++++++++++++++++ multi_timeframe.py | 19 +- 3 files changed, 1273 insertions(+), 3 deletions(-) diff --git a/__pycache__/multi_timeframe.cpython-312.pyc b/__pycache__/multi_timeframe.cpython-312.pyc index ecc9704a62b4413b9512d2d28a121a095a5d0f04..bc1a9c43f0dd1d0a5531abcf22b88bf81883d1ca 100644 GIT binary patch delta 3678 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 str: """根据代码确定市场前缀""" raw = str(code).split("_")[0] + # 指数代码:sh/sz/hk开头 + if raw.startswith("sh"): + return "sh" + if raw.startswith("sz"): + return "sz" + if raw.startswith("hk"): + return "hk" if len(raw) == 5 and raw.isdigit(): return "hk" if raw.startswith("6") or raw.startswith("5"): @@ -138,8 +145,11 @@ def fetch_kline(code: str, period: str = "day", count: int = 120) -> list: return cached_klines market = _market_prefix(code) - url = KLINE_URL.format(market=market, code=code, - period=period, count=count) + is_index = any(code.startswith(p) for p in ["sh", "sz", "hk"]) + + # 指数代码已经自带前缀,API直接用code;普通股票需要加market前缀 + api_code = code if is_index else f"{market}{code}" + url = f"http://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param={api_code},{period},,,{count},qfq" try: req = urllib.request.Request(url, headers=_user_agent()) @@ -155,7 +165,10 @@ def fetch_kline(code: str, period: str = "day", count: int = 120) -> list: if not isinstance(api_data, dict): return {"error": f"data field is {type(api_data).__name__}", "raw": str(api_data)[:200]} - stock_key = f"{market}{code}" + # 指数代码已经自带前缀(sh000001/sz399001),直接用 + # 普通股票代码需要加market前缀(sh600036/sz300750) + is_index = any(code.startswith(p) for p in ["sh", "sz", "hk"]) + stock_key = code if is_index else f"{market}{code}" stock_data = api_data.get(stock_key, {}) # 腾讯API的K线字段名: qfqday, qfqweek, qfqmonth