Merge branch 'main' of http://192.168.123.100:8418/KimJaeHyun/myfinance
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
WBS-9.3: ATR20 자동 충전 절차
|
||||
|
||||
조건: atr20 IS NULL AND close_price IS NOT NULL
|
||||
"""
|
||||
|
||||
def calculate_atr20(closes: list[float], period: int = 20) -> float:
|
||||
"""
|
||||
Calculate ATR (Average True Range) for 20 days.
|
||||
|
||||
입력: close 가격 리스트 (최소 20일)
|
||||
출력: ATR20 (숫자)
|
||||
"""
|
||||
if len(closes) < period:
|
||||
return None
|
||||
|
||||
# True Range 계산 (간소화 버전: 현재 close 기반)
|
||||
trs = []
|
||||
for i in range(1, len(closes)):
|
||||
high = closes[i]
|
||||
low = closes[i]
|
||||
prev_close = closes[i - 1]
|
||||
|
||||
tr = max(
|
||||
high - low,
|
||||
abs(high - prev_close),
|
||||
abs(low - prev_close)
|
||||
)
|
||||
trs.append(tr)
|
||||
|
||||
if not trs:
|
||||
return None
|
||||
|
||||
# ATR = SMA of True Range (20일)
|
||||
atr = sum(trs[-period:]) / min(period, len(trs))
|
||||
return round(atr, 2)
|
||||
|
||||
|
||||
def auto_fill_atr20(row: dict, historical_closes: list[float] = None) -> dict:
|
||||
"""
|
||||
자동 충전: atr20 필드
|
||||
|
||||
입력:
|
||||
row: 현재 행 (atr20 = None)
|
||||
historical_closes: 과거 close 가격 (최소 20일)
|
||||
|
||||
출력:
|
||||
row (atr20 채워짐) 또는 원본 (실패시)
|
||||
"""
|
||||
if not historical_closes or row.get("atr20") is not None:
|
||||
return row
|
||||
|
||||
atr20 = calculate_atr20(historical_closes)
|
||||
if atr20 is not None:
|
||||
row["atr20"] = atr20
|
||||
row["_fill_source"] = "auto_fill_atr20_v1"
|
||||
|
||||
return row
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
WBS-9.3: RSI14 자동 충전 절차
|
||||
|
||||
조건: rsi_14 IS NULL AND close_price IS NOT NULL
|
||||
"""
|
||||
|
||||
def calculate_rsi14(closes: list[float], period: int = 14) -> float:
|
||||
"""
|
||||
Calculate RSI (Relative Strength Index) for 14 days.
|
||||
|
||||
입력: close 가격 리스트 (최소 14일)
|
||||
출력: RSI14 (0-100)
|
||||
"""
|
||||
if len(closes) < period:
|
||||
return None
|
||||
|
||||
# 가격 변화 계산
|
||||
deltas = [closes[i] - closes[i - 1] for i in range(1, len(closes))]
|
||||
|
||||
# Gains/Losses 분리
|
||||
gains = [d if d > 0 else 0 for d in deltas]
|
||||
losses = [abs(d) if d < 0 else 0 for d in deltas]
|
||||
|
||||
# 평균 계산 (간단한 이동평균)
|
||||
avg_gain = sum(gains[-period:]) / period if period <= len(gains) else sum(gains) / len(gains)
|
||||
avg_loss = sum(losses[-period:]) / period if period <= len(losses) else sum(losses) / len(losses)
|
||||
|
||||
if avg_loss == 0:
|
||||
return 100 if avg_gain > 0 else 50
|
||||
|
||||
rs = avg_gain / avg_loss
|
||||
rsi = 100 - (100 / (1 + rs))
|
||||
|
||||
return round(rsi, 2)
|
||||
|
||||
|
||||
def auto_fill_rsi14(row: dict, historical_closes: list[float] = None) -> dict:
|
||||
"""
|
||||
자동 충전: rsi_14 필드
|
||||
|
||||
입력:
|
||||
row: 현재 행 (rsi_14 = None)
|
||||
historical_closes: 과거 close 가격 (최소 14일)
|
||||
|
||||
출력:
|
||||
row (rsi_14 채워짐) 또는 원본 (실패시)
|
||||
"""
|
||||
if not historical_closes or row.get("rsi_14") is not None:
|
||||
return row
|
||||
|
||||
rsi14 = calculate_rsi14(historical_closes)
|
||||
if rsi14 is not None:
|
||||
row["rsi_14"] = rsi14
|
||||
row["_fill_source"] = "auto_fill_rsi14_v1"
|
||||
|
||||
return row
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
WBS-9.3: Stop Price 자동 충전 절차
|
||||
|
||||
조건: stop_price IS NULL AND atr20 IS NOT NULL
|
||||
"""
|
||||
|
||||
def calculate_stop_price(
|
||||
entry_price: float = None,
|
||||
close_price: float = None,
|
||||
atr20: float = None,
|
||||
multiplier: float = 2.0,
|
||||
fallback_pct: float = -5.0
|
||||
) -> float:
|
||||
"""
|
||||
Calculate stop loss price.
|
||||
|
||||
전략 (우선순위):
|
||||
1. entry_price 있으면: entry - (atr20 × multiplier)
|
||||
2. entry_price 없으면: close - (atr20 × multiplier)
|
||||
3. atr20 없으면: price × (1 + fallback_pct/100)
|
||||
|
||||
입력:
|
||||
entry_price: 진입가 (선택)
|
||||
close_price: 현재가 (필수 또는 entry_price)
|
||||
atr20: ATR20 (권장)
|
||||
multiplier: ATR 배수 (default: 2.0)
|
||||
fallback_pct: ATR 미사용시 폴백 (default: -5%)
|
||||
|
||||
출력:
|
||||
stop_price (숫자) 또는 None (실패시)
|
||||
"""
|
||||
# ATR 기반 계산 (권장)
|
||||
if atr20 is not None and atr20 > 0:
|
||||
base_price = entry_price if entry_price is not None else close_price
|
||||
if base_price is not None and base_price > 0:
|
||||
stop_price = base_price - (atr20 * multiplier)
|
||||
return max(round(stop_price, 0), base_price * 0.8) # 최소 20% 하방선
|
||||
|
||||
# Fallback: 단순 백분율 기반
|
||||
if close_price is not None and close_price > 0:
|
||||
fallback_stop = close_price * (1 + fallback_pct / 100)
|
||||
return max(round(fallback_stop, 0), close_price * 0.7) # 최소 30% 하방선
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def auto_fill_stop_price(
|
||||
row: dict,
|
||||
multiplier: float = 2.0,
|
||||
fallback_pct: float = -5.0
|
||||
) -> dict:
|
||||
"""
|
||||
자동 충전: stop_price 필드
|
||||
|
||||
입력:
|
||||
row: 현재 행 (stop_price = None)
|
||||
multiplier: ATR 배수 (default: 2.0)
|
||||
fallback_pct: 폴백 백분율 (default: -5%)
|
||||
|
||||
출력:
|
||||
row (stop_price 채워짐) 또는 원본 (실패시)
|
||||
"""
|
||||
if row.get("stop_price") is not None:
|
||||
return row
|
||||
|
||||
stop_price = calculate_stop_price(
|
||||
entry_price=row.get("entry_price"),
|
||||
close_price=row.get("close_price"),
|
||||
atr20=row.get("atr20"),
|
||||
multiplier=multiplier,
|
||||
fallback_pct=fallback_pct
|
||||
)
|
||||
|
||||
if stop_price is not None:
|
||||
row["stop_price"] = stop_price
|
||||
row["_fill_source"] = "auto_fill_stop_price_v1"
|
||||
row["_stop_price_basis"] = (
|
||||
"atr20" if row.get("atr20") is not None else "fallback_pct"
|
||||
)
|
||||
|
||||
return row
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
WBS-9.3: Velocity 자동 충전 절차
|
||||
|
||||
조건: velocity_1d IS NULL AND (close_price AND previous_close_price) IS NOT NULL
|
||||
"""
|
||||
|
||||
def calculate_velocity_1d(close_price: float, previous_close: float) -> float:
|
||||
"""
|
||||
Calculate 1-day velocity (percentage change).
|
||||
|
||||
입력: close_price, previous_close
|
||||
출력: velocity_1d (%, e.g., 2.5 = +2.5%)
|
||||
"""
|
||||
if previous_close is None or previous_close == 0:
|
||||
return None
|
||||
|
||||
velocity = ((close_price - previous_close) / previous_close) * 100
|
||||
return round(velocity, 2)
|
||||
|
||||
|
||||
def calculate_velocity_5d(closes: list[float]) -> float:
|
||||
"""
|
||||
Calculate 5-day velocity (5-period momentum).
|
||||
|
||||
입력: close 가격 리스트 (최소 5개)
|
||||
출력: velocity_5d (%, e.g., 5.2 = +5.2%)
|
||||
"""
|
||||
if not closes or len(closes) < 5:
|
||||
return None
|
||||
|
||||
current = closes[-1]
|
||||
five_days_ago = closes[-5]
|
||||
|
||||
if five_days_ago is None or five_days_ago == 0:
|
||||
return None
|
||||
|
||||
velocity = ((current - five_days_ago) / five_days_ago) * 100
|
||||
return round(velocity, 2)
|
||||
|
||||
|
||||
def auto_fill_velocity_1d(row: dict) -> dict:
|
||||
"""
|
||||
자동 충전: velocity_1d 필드
|
||||
|
||||
입력:
|
||||
row: 현재 행 (velocity_1d = None)
|
||||
필수: close_price, previous_close_price
|
||||
|
||||
출력:
|
||||
row (velocity_1d 채워짐) 또는 원본 (실패시)
|
||||
"""
|
||||
if row.get("velocity_1d") is not None:
|
||||
return row
|
||||
|
||||
close = row.get("close_price")
|
||||
prev_close = row.get("previous_close_price")
|
||||
|
||||
if close is None or prev_close is None:
|
||||
return row
|
||||
|
||||
velocity_1d = calculate_velocity_1d(close, prev_close)
|
||||
if velocity_1d is not None:
|
||||
row["velocity_1d"] = velocity_1d
|
||||
row["_fill_source"] = "auto_fill_velocity_v1"
|
||||
|
||||
return row
|
||||
|
||||
|
||||
def auto_fill_velocity_5d(row: dict, historical_closes: list[float] = None) -> dict:
|
||||
"""
|
||||
자동 충전: velocity_5d 필드
|
||||
|
||||
입력:
|
||||
row: 현재 행
|
||||
historical_closes: 과거 close 가격 (최소 5일)
|
||||
|
||||
출력:
|
||||
row (velocity_5d 채워짐) 또는 원본 (실패시)
|
||||
"""
|
||||
if row.get("velocity_5d") is not None:
|
||||
return row
|
||||
|
||||
if not historical_closes:
|
||||
return row
|
||||
|
||||
velocity_5d = calculate_velocity_5d(historical_closes)
|
||||
if velocity_5d is not None:
|
||||
row["velocity_5d"] = velocity_5d
|
||||
row["_fill_source"] = "auto_fill_velocity_v1"
|
||||
|
||||
return row
|
||||
Reference in New Issue
Block a user