auto: 일일 백업 2026-06-09 02:00
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"""기술지표 — 순수 함수. 일봉 리스트(시간 오름차순)나 종가 리스트를 받아 계산만 한다.
|
||||
|
||||
입력 candle dict 키: open/high/low/close/volume (daily_candles_cache.get_candles 형식).
|
||||
모든 함수는 데이터 부족 시 None 을 반환한다 (raise X).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def sma(values: list[float], period: int) -> float | None:
|
||||
"""최근 period 개 단순이동평균."""
|
||||
if period <= 0 or len(values) < period:
|
||||
return None
|
||||
return sum(values[-period:]) / period
|
||||
|
||||
|
||||
def rsi(closes: list[float], period: int = 14) -> float | None:
|
||||
"""Wilder RSI. closes 오름차순, period+1 개 이상 필요."""
|
||||
if len(closes) < period + 1:
|
||||
return None
|
||||
gains = losses = 0.0
|
||||
for i in range(1, period + 1):
|
||||
diff = closes[i] - closes[i - 1]
|
||||
if diff >= 0:
|
||||
gains += diff
|
||||
else:
|
||||
losses -= diff
|
||||
avg_gain = gains / period
|
||||
avg_loss = losses / period
|
||||
for i in range(period + 1, len(closes)):
|
||||
diff = closes[i] - closes[i - 1]
|
||||
gain = diff if diff > 0 else 0.0
|
||||
loss = -diff if diff < 0 else 0.0
|
||||
avg_gain = (avg_gain * (period - 1) + gain) / period
|
||||
avg_loss = (avg_loss * (period - 1) + loss) / period
|
||||
if avg_loss == 0:
|
||||
return 100.0
|
||||
rs = avg_gain / avg_loss
|
||||
return 100.0 - (100.0 / (1.0 + rs))
|
||||
|
||||
|
||||
def atr(candles: list[dict], period: int = 14) -> float | None:
|
||||
"""Average True Range (Wilder). candles 오름차순, period+1 개 이상 필요."""
|
||||
if len(candles) < period + 1:
|
||||
return None
|
||||
trs: list[float] = []
|
||||
for i in range(1, len(candles)):
|
||||
h = candles[i]['high']
|
||||
lo = candles[i]['low']
|
||||
prev_close = candles[i - 1]['close']
|
||||
trs.append(max(h - lo, abs(h - prev_close), abs(lo - prev_close)))
|
||||
if len(trs) < period:
|
||||
return None
|
||||
atr_val = sum(trs[:period]) / period
|
||||
for tr in trs[period:]:
|
||||
atr_val = (atr_val * (period - 1) + tr) / period
|
||||
return atr_val
|
||||
|
||||
|
||||
def recent_high(candles: list[dict], period: int, exclude_last: bool = True) -> float | None:
|
||||
"""최근 period 봉 최고가. exclude_last 면 마지막(오늘) 봉 제외 → 돌파 판정용."""
|
||||
series = candles[:-1] if exclude_last else candles
|
||||
window = series[-period:]
|
||||
if not window:
|
||||
return None
|
||||
return max(c['high'] for c in window)
|
||||
|
||||
|
||||
def recent_low(candles: list[dict], period: int, exclude_last: bool = True) -> float | None:
|
||||
"""최근 period 봉 최저가 — 손절선 후보."""
|
||||
series = candles[:-1] if exclude_last else candles
|
||||
window = series[-period:]
|
||||
if not window:
|
||||
return None
|
||||
return min(c['low'] for c in window)
|
||||
|
||||
|
||||
def volume_avg(candles: list[dict], period: int, exclude_last: bool = True) -> float | None:
|
||||
"""최근 period 봉 평균 거래량. exclude_last 면 오늘 제외."""
|
||||
series = candles[:-1] if exclude_last else candles
|
||||
window = series[-period:]
|
||||
if len(window) < period:
|
||||
return None
|
||||
return sum(c['volume'] for c in window) / period
|
||||
Reference in New Issue
Block a user