""" general_analysis 차트 유형 (캔들·선·바·Renko·P&F). """ from __future__ import annotations import numpy as np import pandas as pd from deepcoin.analysis.general_analysis_core import ga_col def _renko_direction_series(close: pd.Series, brick: pd.Series) -> pd.Series: """ATR 기반 브릭 크기로 Renko 방향 (+1/-1/0) 시계열.""" n = len(close) direction = pd.Series(0, index=close.index, dtype=int) if n < 2: return direction price = float(close.iloc[0]) for i in range(1, n): b = float(brick.iloc[i]) if not np.isnan(brick.iloc[i]) else float(close.diff().abs().median()) if b < 1e-9: b = 1e-9 c = float(close.iloc[i]) if c >= price + b: steps = int((c - price) // b) direction.iloc[i] = 1 price += steps * b elif c <= price - b: steps = int((price - c) // b) direction.iloc[i] = -1 price -= steps * b return direction def general_analysis_apply_chart_bars(df: pd.DataFrame) -> pd.DataFrame: """ 봉 단위 차트 파생 컬럼 (선 기울기, Renko, P&F). Args: df: OHLCV (+ ga_atr_14 권장). Returns: ga_chart_* 시계열 컬럼 추가. """ out = df.copy() c = out["Close"].astype(float) h = out["High"].astype(float) l = out["Low"].astype(float) out[ga_col("chart_line_slope_1")] = c.diff() out[ga_col("chart_bar_range_pct")] = (h - l) / c.replace(0, np.nan) * 100 from config import GA_ATR_PERIOD brick = ( out[ga_col("atr_14")] if ga_col("atr_14") in out.columns else (h - l).rolling(GA_ATR_PERIOD).mean() ) brick = brick.fillna(c.diff().abs().rolling(20).median()).replace(0, np.nan).bfill() renko_dir = _renko_direction_series(c, brick) out[ga_col("chart_renko_dir")] = renko_dir out[ga_col("chart_renko_up")] = (renko_dir == 1).astype(int) box = brick.fillna(1.0) pnf = pd.Series(0, index=out.index, dtype=int) col = 0 for i in range(1, len(c)): b = float(box.iloc[i]) move = c.iloc[i] - c.iloc[i - 1] if move >= b: col += 1 pnf.iloc[i] = 1 elif move <= -b: col -= 1 pnf.iloc[i] = -1 out[ga_col("chart_pnf_col")] = pnf if "Volume" in out.columns: v = out["Volume"].astype(float) out[ga_col("chart_vol_spike")] = (v > v.rolling(20).mean() * 1.8).astype(int) if ga_col("ha_trend_up") in out.columns: out[ga_col("chart_ha_trend")] = out[ga_col("ha_trend_up")] elif ga_col("ha_bull") in out.columns: out[ga_col("chart_ha_trend")] = out[ga_col("ha_bull")] return out def general_analysis_chart_metrics(df: pd.DataFrame) -> dict[str, object]: """ lookback 구간 차트 요약 (마지막 봉 스냅샷용). Returns: ga_chart_* dict. """ res: dict[str, object] = { "chart_type_candle": 1, "chart_line_slope": 0.0, "chart_bar_range_pct": 0.0, "chart_ha_trend": 0, "chart_renko_brick_up_ratio": 0.5, "chart_renko_dir": 0, "chart_pnf_col": 0, "chart_vol_spike": 0, } if df is None or len(df) < 5: return {ga_col(k): v for k, v in res.items()} row = df.iloc[-1] c = df["Close"].astype(float) res["chart_line_slope"] = float((c.iloc[-1] - c.iloc[0]) / max(len(c) - 1, 1)) res["chart_bar_range_pct"] = float( (df["High"].iloc[-1] - df["Low"].iloc[-1]) / max(c.iloc[-1], 1e-9) * 100 ) if ga_col("chart_renko_dir") in df.columns: rd = df[ga_col("chart_renko_dir")].astype(float) up = (rd == 1).sum() down = (rd == -1).sum() res["chart_renko_brick_up_ratio"] = round(up / max(up + down, 1), 3) res["chart_renko_dir"] = int(rd.iloc[-1]) else: diff = c.diff().fillna(0) up = (diff > 0).sum() down = (diff < 0).sum() res["chart_renko_brick_up_ratio"] = round(up / max(up + down, 1), 3) if ga_col("chart_pnf_col") in df.columns: res["chart_pnf_col"] = int(df[ga_col("chart_pnf_col")].iloc[-1]) if ga_col("chart_ha_trend") in df.columns: res["chart_ha_trend"] = int(row[ga_col("chart_ha_trend")]) elif ga_col("ha_trend_up") in df.columns: res["chart_ha_trend"] = int(row[ga_col("ha_trend_up")]) if ga_col("chart_vol_spike") in df.columns: res["chart_vol_spike"] = int(row[ga_col("chart_vol_spike")]) elif "Volume" in df.columns: v = df["Volume"].astype(float) res["chart_vol_spike"] = int(v.iloc[-1] > v.iloc[-20:].mean() * 1.8) return {ga_col(k): v for k, v in res.items()} def general_analysis_chart_columns() -> list[str]: return [ "chart_type_candle", "chart_line_slope", "chart_line_slope_1", "chart_bar_range_pct", "chart_ha_trend", "chart_renko_brick_up_ratio", "chart_renko_dir", "chart_renko_up", "chart_pnf_col", "chart_vol_spike", ]