로고스/루트 레거시를 제거하고 deepcoin 패키지·scripts 01~05 CLI·docs/reference로 데이터·GT·분석·매칭·운영 단계를 정리했다. config와 .env 기반 설정, trade_anaysis.html 동기화 포함. Co-authored-by: Cursor <cursoragent@cursor.com>
93 lines
2.5 KiB
Python
93 lines
2.5 KiB
Python
"""
|
|
general_analysis Volume Profile (POC, VAH, VAL).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
from config import GA_VP_BINS, GA_VP_VALUE_AREA_PCT
|
|
from deepcoin.analysis.general_analysis_core import ga_col
|
|
|
|
|
|
def general_analysis_volume_profile(
|
|
win: pd.DataFrame,
|
|
bins: int | None = None,
|
|
value_area_pct: float | None = None,
|
|
) -> dict[str, float | int]:
|
|
"""
|
|
lookback 구간 가격-거래량 분포에서 POC·VAH·VAL 계산.
|
|
|
|
Args:
|
|
win: OHLCV.
|
|
bins: 가격 구간 수.
|
|
value_area_pct: value area 누적 비율 (기본 70%).
|
|
|
|
Returns:
|
|
ga_vp_* 키 dict (접두사 없음).
|
|
"""
|
|
res: dict[str, float | int] = {
|
|
"vp_poc": 0.0,
|
|
"vp_vah": 0.0,
|
|
"vp_val": 0.0,
|
|
"vp_close_vs_poc_pct": 0.0,
|
|
"vp_in_value_area": 0,
|
|
}
|
|
if bins is None:
|
|
bins = GA_VP_BINS
|
|
if value_area_pct is None:
|
|
value_area_pct = GA_VP_VALUE_AREA_PCT
|
|
|
|
if win is None or len(win) < 10 or "Volume" not in win.columns:
|
|
return res
|
|
|
|
h = win["High"].astype(float).values
|
|
l = win["Low"].astype(float).values
|
|
c = win["Close"].astype(float).values
|
|
v = win["Volume"].astype(float).values
|
|
tp = (h + l + c) / 3.0
|
|
|
|
lo, hi = float(l.min()), float(h.max())
|
|
if hi <= lo:
|
|
return res
|
|
|
|
edges = np.linspace(lo, hi, bins + 1)
|
|
hist = np.zeros(bins, dtype=float)
|
|
for i in range(len(tp)):
|
|
idx = int(np.clip(np.digitize(tp[i], edges) - 1, 0, bins - 1))
|
|
hist[idx] += v[i]
|
|
|
|
if hist.sum() <= 0:
|
|
return res
|
|
|
|
poc_idx = int(np.argmax(hist))
|
|
poc = float((edges[poc_idx] + edges[poc_idx + 1]) / 2)
|
|
res["vp_poc"] = poc
|
|
|
|
order = np.argsort(hist)[::-1]
|
|
cum = 0.0
|
|
selected: list[int] = []
|
|
total = hist.sum()
|
|
for idx in order:
|
|
selected.append(int(idx))
|
|
cum += hist[idx]
|
|
if cum >= total * value_area_pct:
|
|
break
|
|
sel_min, sel_max = min(selected), max(selected)
|
|
res["vp_val"] = float(edges[sel_min])
|
|
res["vp_vah"] = float(edges[sel_max + 1])
|
|
res["vp_close_vs_poc_pct"] = float((c[-1] / poc - 1) * 100) if poc else 0.0
|
|
res["vp_in_value_area"] = int(res["vp_val"] <= c[-1] <= res["vp_vah"])
|
|
|
|
return res
|
|
|
|
|
|
def general_analysis_volume_columns() -> list[str]:
|
|
return ["vp_poc", "vp_vah", "vp_val", "vp_close_vs_poc_pct", "vp_in_value_area"]
|
|
|
|
|
|
def general_analysis_volume_snapshot(win: pd.DataFrame) -> dict[str, object]:
|
|
"""Volume profile → ga_vp_*."""
|
|
return {ga_col(k): v for k, v in general_analysis_volume_profile(win).items()}
|