WLD DeepCoin 단계별 구조 재편 및 설정·문서 통합
로고스/루트 레거시를 제거하고 deepcoin 패키지·scripts 01~05 CLI·docs/reference로 데이터·GT·분석·매칭·운영 단계를 정리했다. config와 .env 기반 설정, trade_anaysis.html 동기화 포함. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
159
deepcoin/analysis/general_analysis_chart.py
Normal file
159
deepcoin/analysis/general_analysis_chart.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
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",
|
||||
]
|
||||
Reference in New Issue
Block a user