This commit is contained in:
2026-09-17 00:56:05 +08:00
parent 33693adb66
commit c7d39938a2
14 changed files with 1005 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
"""ETF 专用历史日线适配,不依赖或修改 QMT SDK。"""
from datetime import date, datetime
import math
import re
import httpx
DAILY_URL = 'http://go.apinb.com/a/get_daily'
def daily_bars(client: httpx.Client, code: str, today: date, count: int = 120) -> list[dict]:
"""读取指定证券日线;只使用 code 参数,截取历史窗口在本地完成。"""
response = client.get(DAILY_URL, params={'code': code})
response.raise_for_status()
return parse_daily(response.json(), code, today, count)
def parse_daily(payload: dict, code: str, today: date, count: int = 120) -> list[dict]:
"""校验业务状态、证券归属和 OHLC将 trade_date 转为指标需要的 date。"""
if type(count) is not int or count <= 0:
raise ValueError('日线数量必须为正整数')
if not isinstance(payload, dict) or type(payload.get('code')) is not int or payload['code'] != 0:
raise ValueError(f'日线接口业务失败:{payload.get("message", "状态无效") if isinstance(payload, dict) else "响应非对象"}')
details = payload.get('details')
if not isinstance(details, list) or not details:
raise ValueError(f'{code} 日线接口未返回有效 details 列表')
bars = {}
for row in details:
if not isinstance(row, dict) or row.get('ts_code') != code:
raise ValueError(f'{code} 日线证券代码不一致')
stamp = str(row.get('trade_date', ''))
if not re.fullmatch(r'[0-9]{8}', stamp):
raise ValueError(f'{code} 日线日期无效:{stamp}')
day = datetime.strptime(stamp, '%Y%m%d').date()
# 当前日及未来日线均不可用于盘中指标,先过滤再截取最近 count 根。
if day >= today:
continue
if stamp in bars:
raise ValueError(f'{code} 日线日期重复:{stamp}')
values = {}
for key in ('open', 'high', 'low', 'close'):
value = row.get(key)
if isinstance(value, bool) or not isinstance(value, (int, float, str)):
raise ValueError(f'{code} 日线 {key} 无效')
value = float(value)
if not math.isfinite(value) or value <= 0:
raise ValueError(f'{code} 日线 {key} 非有限正数')
values[key] = value
if not (values['low'] <= values['open'] <= values['high']
and values['low'] <= values['close'] <= values['high']):
raise ValueError(f'{code} 日线 OHLC 关系异常')
bars[stamp] = dict(date=stamp, **values)
return [bars[stamp] for stamp in sorted(bars)[-count:]]