로고스/루트 레거시를 제거하고 deepcoin 패키지·scripts 01~05 CLI·docs/reference로 데이터·GT·분석·매칭·운영 단계를 정리했다. config와 .env 기반 설정, trade_anaysis.html 동기화 포함. Co-authored-by: Cursor <cursoragent@cursor.com>
154 lines
4.4 KiB
Python
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"]
|