add etf
This commit is contained in:
55
py-client/strategy/etf/data.py
Normal file
55
py-client/strategy/etf/data.py
Normal 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:]]
|
||||
Reference in New Issue
Block a user