""" general_analysis 하모닉 패턴 (Gartley, Bat 근사). """ from __future__ import annotations import numpy as np from deepcoin.analysis.general_analysis_core import find_pivots, ga_col def _ratio(a: float, b: float) -> float: if abs(b) < 1e-12: return 0.0 return abs(a / b) def _near(x: float, target: float, tol: float = 0.08) -> bool: return abs(x - target) <= tol def general_analysis_harmonic_snapshot(win) -> dict[str, object]: """ 최근 5개 피벗으로 Gartley·Bat 유사 비율 검사. Args: win: OHLCV DataFrame. Returns: ga_harmonic_* dict. """ res: dict[str, object] = { "harmonic_gartley": 0, "harmonic_bat": 0, "harmonic_label": "none", } if win is None or len(win) < 30: return {ga_col(k): v for k, v in res.items()} h = win["High"].astype(float).values l = win["Low"].astype(float).values peaks, troughs = find_pivots(h, l, order=2) pivots = sorted([(i, "H", h[i]) for i in peaks] + [(i, "L", l[i]) for i in troughs]) if len(pivots) < 5: return {ga_col(k): v for k, v in res.items()} pts = pivots[-5:] prices = [p[2] for p in pts] xa = abs(prices[1] - prices[0]) ab = abs(prices[2] - prices[1]) bc = abs(prices[3] - prices[2]) cd = abs(prices[4] - prices[3]) if xa < 1e-9: return {ga_col(k): v for k, v in res.items()} r_ab = _ratio(ab, xa) r_bc = _ratio(bc, ab) if ab > 1e-9 else 0.0 r_cd = _ratio(cd, bc) if bc > 1e-9 else 0.0 if _near(r_ab, 0.618) and 0.35 <= r_bc <= 0.95 and 1.1 <= r_cd <= 1.75: res["harmonic_gartley"] = 1 res["harmonic_label"] = "gartley" if _near(r_ab, 0.382) and 0.35 <= r_bc <= 0.95 and 1.5 <= r_cd <= 2.1: res["harmonic_bat"] = 1 res["harmonic_label"] = "bat" return {ga_col(k): v for k, v in res.items()} def general_analysis_harmonic_columns() -> list[str]: return ["harmonic_gartley", "harmonic_bat", "harmonic_label"]