Files
big-qmt/py-client/strategy/etf/indicators.py
2026-09-17 00:56:05 +08:00

56 lines
2.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""仅用已收盘日线计算指标,避免把盘中未完成的日线混入信号。"""
from dataclasses import dataclass
from datetime import date, datetime
from decimal import Decimal, ROUND_CEILING
import math
from statistics import fmean, pstdev
from .config import ETFConfig
@dataclass(frozen=True)
class Indicators:
day: str
ma60: float
atr: float
lower: float
middle: float
upper: float
grid: float
def calculate(rows: list[dict], today: date, cfg: ETFConfig) -> Indicators:
"""MA60 + Wilder ATR + BOLL总体标准差格距向上取整到 0.001 元。"""
bars = {}
for row in rows:
day = datetime.strptime(str(row['date']), '%Y%m%d').date()
if day >= today:
continue
if day in bars:
raise ValueError('日线包含重复日期')
high, low, close = (float(row[key]) for key in ('high', 'low', 'close'))
if not all(math.isfinite(v) and v > 0 for v in (high, low, close)) or not low <= close <= high:
raise ValueError('日线价格无效')
bars[day] = (high, low, close)
days = sorted(bars)
if len(days) < max(60, cfg.atr_period + 1, cfg.boll_period):
raise ValueError('已收盘日线不足,至少需要 60 根且能计算 ATR')
# 长期停牌或历史缓存未补齐时不使用过期信号;春节等长假允许 15 个自然日。
if (today - days[-1]).days > 15:
raise ValueError('最近日线超过 15 个自然日,需补齐行情')
values = [bars[d] for d in days]
closes = [v[2] for v in values]
tr = [max(h - l, abs(h - closes[i - 1]), abs(l - closes[i - 1]))
for i, (h, l, _) in enumerate(values) if i > 0]
n = cfg.atr_period
atr = fmean(tr[:n])
for value in tr[n:]:
atr = (atr * (n - 1) + value) / n
ma = fmean(closes[-60:])
window = closes[-cfg.boll_period:]
middle, width = fmean(window), cfg.boll_std * pstdev(window)
raw_grid = max(atr * cfg.atr_multiplier, ma * cfg.min_grid_pct / 100, 0.001)
grid = float(Decimal(str(raw_grid)).quantize(Decimal('0.001'), rounding=ROUND_CEILING))
return Indicators(days[-1].strftime('%Y%m%d'), ma, atr, middle - width, middle, middle + width, grid)