Files
Bithumb/deepcoin/analysis/general_analysis_align.py
dsyoon b52d61b777 WLD DeepCoin 단계별 구조 재편 및 설정·문서 통합
로고스/루트 레거시를 제거하고 deepcoin 패키지·scripts 01~05 CLI·docs/reference로
데이터·GT·분석·매칭·운영 단계를 정리했다. config와 .env 기반 설정, trade_anaysis.html 동기화 포함.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-30 22:58:25 +09:00

154 lines
4.4 KiB
Python

"""
general_analysis MTF 합성·정렬 점수.
"""
from __future__ import annotations
from typing import Any
import pandas as pd
from config import (
ALIGN_BB_POS_HIGH,
ALIGN_BB_POS_LOW,
ALIGN_RSI_CONFLICT_TIMING_HIGH,
ALIGN_RSI_CONFLICT_TIMING_LOW,
ALIGN_RSI_CONFLICT_TREND_HIGH,
ALIGN_RSI_CONFLICT_TREND_LOW,
ALIGN_RSI_OVERBOUGHT,
ALIGN_RSI_OVERSOLD,
)
from deepcoin.analysis.general_analysis_config import TIMING_INTERVALS, TREND_INTERVALS
from deepcoin.analysis.general_analysis_core import ga_col, interval_tf_prefix
def general_analysis_mtf_scores(
prefixed_row: dict[str, Any],
) -> dict[str, float | int | str]:
"""
간격 접두사가 붙은 스냅샷 행에서 MTF 합성 점수 계산.
Args:
prefixed_row: m3_ga_rsi 형태 flat dict.
Returns:
ga_align_* 점수.
"""
rsi_oversold = 0
rsi_overbought = 0
trend_up = 0
trend_down = 0
n_timing = 0
n_trend = 0
conflict = 0
for interval in TIMING_INTERVALS:
p = interval_tf_prefix(interval)
rk = f"{p}_RSI"
if rk in prefixed_row and prefixed_row[rk] is not None:
n_timing += 1
rsi = float(prefixed_row[rk])
if rsi < ALIGN_RSI_OVERSOLD:
rsi_oversold += 1
if rsi > ALIGN_RSI_OVERBOUGHT:
rsi_overbought += 1
for interval in TREND_INTERVALS:
p = interval_tf_prefix(interval)
sk = f"{p}_{ga_col('struct_trend')}"
if sk in prefixed_row:
n_trend += 1
t = prefixed_row[sk]
if t == "up":
trend_up += 1
elif t == "down":
trend_down += 1
m3_rsi = prefixed_row.get("m3_RSI")
d1_rsi = prefixed_row.get("d1_RSI")
if m3_rsi is not None and d1_rsi is not None:
if (
float(m3_rsi) < ALIGN_RSI_CONFLICT_TIMING_LOW
and float(d1_rsi) > ALIGN_RSI_CONFLICT_TREND_HIGH
):
conflict = 1
if (
float(m3_rsi) > ALIGN_RSI_CONFLICT_TIMING_HIGH
and float(d1_rsi) < ALIGN_RSI_CONFLICT_TREND_LOW
):
conflict = 1
timing_buy_align = rsi_oversold / max(len(TIMING_INTERVALS), 1)
timing_sell_align = rsi_overbought / max(len(TIMING_INTERVALS), 1)
return {
"ga_align_rsi_oversold_tf": rsi_oversold,
"ga_align_rsi_overbought_tf": rsi_overbought,
"ga_align_trend_up_tf": trend_up,
"ga_align_trend_down_tf": trend_down,
"ga_align_timing_buy_score": round(timing_buy_align, 3),
"ga_align_timing_sell_score": round(timing_sell_align, 3),
"ga_align_trend_score": round(
(trend_up - trend_down) / max(n_trend, 1), 3
),
"ga_align_mtf_conflict": conflict,
}
def general_analysis_mtf_vote_latest(
frames_enriched: dict[int, pd.DataFrame],
) -> dict[str, float | int | str]:
"""
각 TF 최신 완성봉 지표로 TF 가중 투표·필터 점수 산출.
Args:
frames_enriched: interval → enrich된 DataFrame.
Returns:
ga_vote_* 점수 (접두사 없음, ga_col로 감쌀 것).
"""
votes_buy = 0
votes_sell = 0
trend_ok = 0
n = 0
for interval in TIMING_INTERVALS:
df = frames_enriched.get(interval)
if df is None or df.empty:
continue
row = df.iloc[-1]
n += 1
rsi = row.get("RSI")
if rsi is not None and not pd.isna(rsi):
if float(rsi) < ALIGN_RSI_OVERSOLD:
votes_buy += 1
if float(rsi) > ALIGN_RSI_OVERBOUGHT:
votes_sell += 1
bb_pos = row.get("bb_pos")
if bb_pos is not None and float(bb_pos) < ALIGN_BB_POS_LOW:
votes_buy += 1
if bb_pos is not None and float(bb_pos) > ALIGN_BB_POS_HIGH:
votes_sell += 1
for interval in TREND_INTERVALS:
df = frames_enriched.get(interval)
if df is None or df.empty:
continue
row = df.iloc[-1]
st = row.get(ga_col("struct_trend"), "range")
if st == "up":
trend_ok += 1
elif st == "down":
trend_ok -= 1
return {
"vote_timing_buy": votes_buy,
"vote_timing_sell": votes_sell,
"vote_trend_score": trend_ok,
"vote_tf_used": n,
}
def general_analysis_vote_columns() -> list[str]:
return ["vote_timing_buy", "vote_timing_sell", "vote_trend_score", "vote_tf_used"]