add etf
This commit is contained in:
55
py-client/strategy/etf/indicators.py
Normal file
55
py-client/strategy/etf/indicators.py
Normal file
@@ -0,0 +1,55 @@
|
||||
"""仅用已收盘日线计算指标,避免把盘中未完成的日线混入信号。"""
|
||||
|
||||
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)
|
||||
Reference in New Issue
Block a user