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:
2026-05-30 22:58:25 +09:00
parent e631a5701f
commit b52d61b777
76 changed files with 11552 additions and 4567 deletions

300
config.py
View File

@@ -1,92 +1,264 @@
"""
전역 설정 (WLD 월드코인, 3분 BB MTF 전략).
전역 설정 (WLD). 값은 PROJECT_ROOT/.env → OS 환경 변수 순으로 읽습니다.
"""
from __future__ import annotations
import os
try:
from dotenv import load_dotenv
from deepcoin.env_loader import load_project_env
load_project_env()
def _getenv(key: str, default: str = "") -> str:
return os.getenv(key, default)
def _getenv_int(key: str, default: str) -> int:
return int(_getenv(key, default))
def _getenv_float(key: str, default: str) -> float:
return float(_getenv(key, default))
def _parse_int_tuple(env_key: str, default: str) -> tuple[int, ...]:
raw = _getenv(env_key, default)
return tuple(int(x.strip()) for x in raw.split(",") if x.strip())
def _parse_int_set(env_key: str, default: str) -> frozenset[int]:
return frozenset(_parse_int_tuple(env_key, default))
def _parse_interval_map(env_key: str, default: str) -> dict[int, int]:
"""
'3:120,5:100'{3: 120, 5: 100}.
"""
raw = _getenv(env_key, default)
out: dict[int, int] = {}
for part in raw.split(","):
part = part.strip()
if ":" not in part:
continue
k, v = part.split(":", 1)
out[int(k.strip())] = int(v.strip())
return out
def _parse_str_map(env_key: str, default: str) -> dict[int, str]:
"""'3:m3,1440:d1'{3: 'm3', 1440: 'd1'}."""
raw = _getenv(env_key, default)
out: dict[int, str] = {}
for part in raw.split(","):
part = part.strip()
if ":" not in part:
continue
k, v = part.split(":", 1)
out[int(k.strip())] = v.strip()
return out
load_dotenv()
except ImportError:
pass
# --- API / 알림 ---
COIN_TELEGRAM_BOT_TOKEN = os.getenv("COIN_TELEGRAM_BOT_TOKEN", "")
COIN_TELEGRAM_CHAT_ID = os.getenv("COIN_TELEGRAM_CHAT_ID", "")
BITHUMB_ACCESS_KEY = _getenv("BITHUMB_ACCESS_KEY")
BITHUMB_SECRET_KEY = _getenv("BITHUMB_SECRET_KEY")
BITHUMB_API_URL = _getenv("BITHUMB_API_URL", "https://api.bithumb.com")
BITHUMB_API_CANDLE_COUNT = _getenv_int("BITHUMB_API_CANDLE_COUNT", "200")
BITHUMB_MINUTE_INTERVALS = _parse_int_set(
"BITHUMB_MINUTE_INTERVALS", "1,3,5,10,15,30,60,240"
)
HTS_API_RETRY_SLEEP_SEC = _getenv_float("HTS_API_RETRY_SLEEP_SEC", "0.5")
COIN_TELEGRAM_BOT_TOKEN = _getenv("COIN_TELEGRAM_BOT_TOKEN")
COIN_TELEGRAM_CHAT_ID = _getenv("COIN_TELEGRAM_CHAT_ID")
# --- 거래 대상 ---
SYMBOL = "WLD"
COIN_NAME = "월드코인"
KR_COINS: dict[str, str] = {
SYMBOL: COIN_NAME,
}
SYMBOL = _getenv("SYMBOL", "WLD")
COIN_NAME = _getenv("COIN_NAME", "월드코인")
KR_COINS: dict[str, str] = {SYMBOL: COIN_NAME}
# --- 타임프레임 (분) ---
TREND_INTERVAL_1H = 60
TREND_INTERVAL_1D = 1440
DAILY_INTERVAL_MIN = _getenv_int("DAILY_INTERVAL_MIN", "1440")
ENTRY_INTERVAL = _getenv_int("ENTRY_INTERVAL", "3")
TREND_INTERVAL_1H = _getenv_int("TREND_INTERVAL_1H", "60")
TREND_INTERVAL_1D = _getenv_int("TREND_INTERVAL_1D", "1440")
# --- 쿨다운(초) — 3분봉: 기본 30분/15분 (빈번 체결 완화) ---
BUY_COOLDOWN_SEC = int(os.getenv("BUY_COOLDOWN_SEC", "1800"))
SELL_COOLDOWN_SEC = int(os.getenv("SELL_COOLDOWN_SEC", "900"))
BUY_MINUTE_LIMIT = BUY_COOLDOWN_SEC
ALL_INTERVALS: tuple[int, ...] = _parse_int_tuple(
"ALL_INTERVALS", "1,3,5,10,15,30,60,240,1440"
)
DOWNLOAD_INTERVALS: tuple[int, ...] = _parse_int_tuple(
"DOWNLOAD_INTERVALS",
",".join(str(x) for x in ALL_INTERVALS),
)
GENERAL_ANALYSIS_INTERVALS: tuple[int, ...] = _parse_int_tuple(
"GENERAL_ANALYSIS_INTERVALS", "3,5,10,15,30,60,240,1440"
)
TIMING_INTERVALS: tuple[int, ...] = _parse_int_tuple(
"TIMING_INTERVALS", "3,5,10,15"
)
TREND_INTERVALS: tuple[int, ...] = _parse_int_tuple(
"TREND_INTERVALS", "60,240,1440"
)
# 매수·매도 신호는 조건이 False→True로 바뀐 봉에서만 (연속 참 방지)
SIGNAL_EDGE_ONLY = os.getenv("SIGNAL_EDGE_ONLY", "true").lower() in ("1", "true", "yes")
INTERVAL_PREFIX: dict[int, str] = _parse_str_map(
"INTERVAL_PREFIX",
"1:m1,3:m3,5:m5,10:m10,15:m15,30:m30,60:m60,240:m240,1440:d1",
)
# 체결(매수·매도 공통) 후 최소 대기 봉 수 (3분봉 5봉 = 15분)
TRADE_MIN_GAP_BARS = int(os.getenv("TRADE_MIN_GAP_BARS", "5"))
# --- 볼린저 / RSI ---
BB_PERIOD = _getenv_int("BB_PERIOD", "20")
BB_STD = _getenv_float("BB_STD", "2")
BB_MIN_WIDTH_PCT = _getenv_float("BB_MIN_WIDTH_PCT", "0.8")
RSI_PERIOD = _getenv_int("RSI_PERIOD", "14")
# 규칙 탐색 시 거래 횟수 패널티 (학습 구간)
DISCOVER_MAX_TRADES = int(os.getenv("DISCOVER_MAX_TRADES", "120"))
DISCOVER_TRADE_PENALTY_PCT = float(os.getenv("DISCOVER_TRADE_PENALTY_PCT", "0.03"))
# --- 이격도 ---
DISPARITY_PERIODS: tuple[int, ...] = _parse_int_tuple("DISPARITY_PERIODS", "5,20,60")
DISPARITY_OVERBOUGHT = _getenv_float("DISPARITY_OVERBOUGHT", "105")
DISPARITY_OVERSOLD = _getenv_float("DISPARITY_OVERSOLD", "95")
# 3분 BB 위치: 이 값 미만에서 상단돌파 매도 차단 (저점 익절 방지)
SELL_MIN_BB_POS = float(os.getenv("SELL_MIN_BB_POS", "0.4"))
# --- MACD / Stochastic ---
MACD_FAST = _getenv_int("MACD_FAST", "12")
MACD_SLOW = _getenv_int("MACD_SLOW", "26")
MACD_SIGNAL = _getenv_int("MACD_SIGNAL", "9")
STOCH_K_PERIOD = _getenv_int("STOCH_K_PERIOD", "14")
STOCH_D_PERIOD = _getenv_int("STOCH_D_PERIOD", "3")
STOCH_SMOOTH_K = _getenv_int("STOCH_SMOOTH_K", "3")
STOCH_OVERSOLD = _getenv_float("STOCH_OVERSOLD", "20")
STOCH_OVERBOUGHT = _getenv_float("STOCH_OVERBOUGHT", "80")
# 3분 BB 위치: 이 값 이상이면 단독 상단구간 매수 차단 (고점 추격 방지)
BUY_MAX_BB_POS_CHASE = float(os.getenv("BUY_MAX_BB_POS_CHASE", "0.55"))
# --- 추세 ---
TREND_RANGE_MA_GAP_PCT = _getenv_float("TREND_RANGE_MA_GAP_PCT", "0.5")
# --- 볼린저 (3분봉, 20, 2σ) ---
BB_PERIOD = 20
BB_STD = 2
BB_MIN_WIDTH_PCT = float(os.getenv("BB_MIN_WIDTH_PCT", "0.8"))
# --- MTF 합성·정렬 ---
ALIGN_RSI_OVERSOLD = _getenv_float("ALIGN_RSI_OVERSOLD", "35")
ALIGN_RSI_OVERBOUGHT = _getenv_float("ALIGN_RSI_OVERBOUGHT", "65")
ALIGN_RSI_CONFLICT_TIMING_LOW = _getenv_float("ALIGN_RSI_CONFLICT_TIMING_LOW", "40")
ALIGN_RSI_CONFLICT_TIMING_HIGH = _getenv_float("ALIGN_RSI_CONFLICT_TIMING_HIGH", "65")
ALIGN_RSI_CONFLICT_TREND_LOW = _getenv_float("ALIGN_RSI_CONFLICT_TREND_LOW", "40")
ALIGN_RSI_CONFLICT_TREND_HIGH = _getenv_float("ALIGN_RSI_CONFLICT_TREND_HIGH", "65")
ALIGN_BB_POS_LOW = _getenv_float("ALIGN_BB_POS_LOW", "0.2")
ALIGN_BB_POS_HIGH = _getenv_float("ALIGN_BB_POS_HIGH", "0.8")
# --- RSI / 거래량 (조합 필터) ---
RSI_PERIOD = 14
RSI_BUY_MAX = float(os.getenv("RSI_BUY_MAX", "42"))
VOLUME_BUY_RATIO = float(os.getenv("VOLUME_BUY_RATIO", "1.0"))
# --- 다운로드 / DB ---
DOWNLOAD_MONTHS = _getenv_int("DOWNLOAD_MONTHS", "12")
DOWNLOAD_MONTHS_1M = _getenv_int("DOWNLOAD_MONTHS_1M", "6")
INCREMENTAL_OVERLAP_BARS = _getenv_int("INCREMENTAL_OVERLAP_BARS", "3")
DOWNLOAD_BACKFILL_EXTRA_BARS = _getenv_int("DOWNLOAD_BACKFILL_EXTRA_BARS", "200")
DOWNLOAD_MIN_INCREMENTAL_BARS = _getenv_int("DOWNLOAD_MIN_INCREMENTAL_BARS", "50")
DOWNLOAD_DAILY_EXTRA_DAYS = _getenv_int("DOWNLOAD_DAILY_EXTRA_DAYS", "20")
DB_READ_LIMIT_DEFAULT = _getenv_int("DB_READ_LIMIT_DEFAULT", "7000")
DB_ROW_WARMUP_BARS = _getenv_int("DB_ROW_WARMUP_BARS", "200")
DB_ROW_MIN_DAILY_BARS = _getenv_int("DB_ROW_MIN_DAILY_BARS", "100")
DB_ROW_DAILY_PADDING_DAYS = _getenv_int("DB_ROW_DAILY_PADDING_DAYS", "30")
# --- 추세 / 레짐 ---
TREND_RANGE_MA_GAP_PCT = 0.5
# --- 주문 ---
DEFAULT_BUY_KRW = int(os.getenv("DEFAULT_BUY_KRW", "30000"))
RANGE_BUY_KRW = int(os.getenv("RANGE_BUY_KRW", "15000"))
def _paths():
from deepcoin.paths import (
ANALYSIS_CAPABILITY_HTML,
ANALYSIS_LATEST_DIR,
ANALYSIS_REPORT_HTML,
ANALYSIS_TRADES_CSV,
resolve_db_path,
resolve_ground_truth_file,
)
# --- 수수료 (매수·매도 각각 적용, 시뮬레이션) ---
TRADING_FEE_RATE = float(os.getenv("TRADING_FEE_RATE", "0.0005"))
return (
resolve_db_path(),
resolve_ground_truth_file(),
ANALYSIS_TRADES_CSV,
ANALYSIS_REPORT_HTML,
ANALYSIS_CAPABILITY_HTML,
ANALYSIS_LATEST_DIR,
)
# --- coins.db (downloader.py 적재 간격, 분) ---
# 빗썸 분봉 API: 1,3,5,10,15,30,60,240 / 일봉 1440
ALL_INTERVALS: tuple[int, ...] = (1, 3, 5, 10, 15, 30, 60, 240, 1440)
DOWNLOAD_INTERVALS: tuple[int, ...] = ALL_INTERVALS
DOWNLOAD_MONTHS = int(os.getenv("DOWNLOAD_MONTHS", "6"))
# 1분봉은 용량·API 부담으로 기본 2개월 (환경변수로 조정)
DOWNLOAD_MONTHS_1M = int(os.getenv("DOWNLOAD_MONTHS_1M", "2"))
DB_PATH = "coins.db"
# 규칙 탐색·조합 분석 기준 타임라인
ENTRY_INTERVAL = 3
_db, _gt, _a_csv, _a_html, _a_cap, _a_latest = _paths()
DB_PATH = _getenv("DB_PATH", str(_db))
GROUND_TRUTH_PATH = _gt
REPORTS_ANALYSIS_TRADES_CSV = _a_csv
REPORTS_ANALYSIS_REPORT_HTML = _a_html
REPORTS_ANALYSIS_CAPABILITY_HTML = _a_cap
REPORTS_ANALYSIS_LATEST_DIR = _a_latest
GROUND_TRUTH_FILE = _getenv("GROUND_TRUTH_FILE", str(_gt))
# 실시간: discovered_rules + 전 봉 BB·일목 조합 (False면 mtf_bb_policy)
USE_DISCOVERED_LIVE = os.getenv("USE_DISCOVERED_LIVE", "true").lower() in ("1", "true", "yes")
# --- 차트 ---
CHART_LOOKBACK_DAYS = _getenv_int("CHART_LOOKBACK_DAYS", "365")
GT_UNLIMITED_CHRONOLOGICAL_DAYS = _getenv_int("GT_UNLIMITED_CHRONOLOGICAL_DAYS", "300")
# --- 시뮬레이션 ---
SIM_INITIAL_CASH_KRW = int(os.getenv("SIM_INITIAL_CASH_KRW", "200000"))
SIM_MIN_ORDER_KRW = int(os.getenv("SIM_MIN_ORDER_KRW", "5000"))
# --- Ground Truth ---
GT_MIN_SWING_PCT = _getenv_float("GT_MIN_SWING_PCT", "4.0")
GT_PIVOT_ORDER = _getenv_int("GT_PIVOT_ORDER", "20")
GT_MIN_BARS_BETWEEN = _getenv_int("GT_MIN_BARS_BETWEEN", "30")
GT_MAX_ROUND_TRIPS = _getenv_int("GT_MAX_ROUND_TRIPS", "24")
GT_SELECTION_MODE = _getenv("GT_SELECTION_MODE", "split_buy_peak_sell")
GT_MIN_LEG_PCT = _getenv_float("GT_MIN_LEG_PCT", "8.0")
GT_BUY_MIN_SWING_PCT = _getenv_float("GT_BUY_MIN_SWING_PCT", "3.0")
GT_BUY_BB_MAX = _getenv_float("GT_BUY_BB_MAX", "0.45")
GT_BUY_MIN_BARS = _getenv_int("GT_BUY_MIN_BARS", "24")
GT_MAX_BUYS_PER_LEG = _getenv_int("GT_MAX_BUYS_PER_LEG", "12")
GT_MAX_SELLS_PER_LEG = _getenv_int("GT_MAX_SELLS_PER_LEG", "2")
GT_SELL_SPLIT_GAP_PCT = _getenv_float("GT_SELL_SPLIT_GAP_PCT", "2.5")
GT_MARKER_SIZE_MIN = _getenv_int("GT_MARKER_SIZE_MIN", "10")
GT_MARKER_SIZE_MAX = _getenv_int("GT_MARKER_SIZE_MAX", "32")
GT_INITIAL_CASH_KRW = _getenv_int("GT_INITIAL_CASH_KRW", "1000000")
TRADING_FEE_RATE = _getenv_float("TRADING_FEE_RATE", "0.0005")
# --- 실행 ---
MONITOR_LOOP_SLEEP_SEC = 10
COOLDOWN_FILE = "coins_buy_time.json"
# --- 모니터 / API 수집 ---
MONITOR_LOOP_SLEEP_SEC = _getenv_int("MONITOR_LOOP_SLEEP_SEC", "10")
MONITOR_POOL_WORKERS = _getenv_int("MONITOR_POOL_WORKERS", "12")
MONITOR_DEFAULT_INTERVAL = _getenv_int("MONITOR_DEFAULT_INTERVAL", "60")
MONITOR_API_RETRIES = _getenv_int("MONITOR_API_RETRIES", "3")
MONITOR_API_BONG_COUNT = _getenv_int("MONITOR_API_BONG_COUNT", "3000")
MONITOR_SLEEP_AFTER_REQUEST_SEC = _getenv_float("MONITOR_SLEEP_AFTER_REQUEST_SEC", "0.5")
MONITOR_SLEEP_RATE_LIMIT_SEC = _getenv_float("MONITOR_SLEEP_RATE_LIMIT_SEC", "5")
MONITOR_SLEEP_BETWEEN_CHUNKS_SEC = _getenv_float("MONITOR_SLEEP_BETWEEN_CHUNKS_SEC", "0.3")
MONITOR_API_CHUNK_BARS = _getenv_int("MONITOR_API_CHUNK_BARS", "200")
MONITOR_MA_WINDOWS: tuple[int, ...] = _parse_int_tuple(
"MONITOR_MA_WINDOWS", "5,20,40,120,200,240,720,1440"
)
MONITOR_NORM_WINDOW = _getenv_int("MONITOR_NORM_WINDOW", "20")
MONITOR_TELEGRAM_BATCH_SIZE = _getenv_int("MONITOR_TELEGRAM_BATCH_SIZE", "20")
# --- general_analysis ---
GA_COL_PREFIX = _getenv("GA_COL_PREFIX", "ga_")
LOOKBACK_BARS: dict[int, int] = _parse_interval_map(
"LOOKBACK_BARS",
"3:120,5:100,10:80,15:60,30:50,60:40,240:30,1440:60",
)
CONTEXT_TAIL_ROWS: dict[int, int] = _parse_interval_map(
"CONTEXT_TAIL_ROWS",
"3:6000,5:5000,10:4000,15:3000,30:2000,60:1500,240:800,1440:500",
)
GA_DEFAULT_TAIL_EXPORT = _getenv_int("GA_DEFAULT_TAIL_EXPORT", "200")
GA_PATTERN_TOLERANCE_PCT = _getenv_float("GA_PATTERN_TOLERANCE_PCT", "2.5")
GA_VP_BINS = _getenv_int("GA_VP_BINS", "30")
GA_VP_VALUE_AREA_PCT = _getenv_float("GA_VP_VALUE_AREA_PCT", "0.70")
GA_HV_ROLLING_BARS = _getenv_int("GA_HV_ROLLING_BARS", "20")
GA_HV_PERCENTILE_WINDOW = _getenv_int("GA_HV_PERCENTILE_WINDOW", "120")
GA_HV_ANNUALIZE_SQRT = _getenv_float("GA_HV_ANNUALIZE_SQRT", "339.41148133")
GA_DIVERGENCE_LOOKBACK = _getenv_int("GA_DIVERGENCE_LOOKBACK", "10")
GA_SMA_PERIODS: tuple[int, ...] = _parse_int_tuple("GA_SMA_PERIODS", "5,20,60,120")
GA_EMA_SPANS: tuple[int, ...] = _parse_int_tuple("GA_EMA_SPANS", "12,26")
GA_ATR_PERIOD = _getenv_int("GA_ATR_PERIOD", "14")
GA_KELTNER_ATR_MULT = _getenv_float("GA_KELTNER_ATR_MULT", "2")
GA_AO_FAST = _getenv_int("GA_AO_FAST", "5")
GA_AO_SLOW = _getenv_int("GA_AO_SLOW", "34")
GA_LINREG_WINDOW = _getenv_int("GA_LINREG_WINDOW", "20")
GA_ADX_PERIOD = _getenv_int("GA_ADX_PERIOD", "14")
GA_ADX_TREND_THRESHOLD = _getenv_float("GA_ADX_TREND_THRESHOLD", "25")
GA_SUPERTREND_ATR_MULT = _getenv_float("GA_SUPERTREND_ATR_MULT", "3")
GA_VOL_SPIKE_MULT = _getenv_float("GA_VOL_SPIKE_MULT", "1.8")
GA_VOL_MA_WINDOW = _getenv_int("GA_VOL_MA_WINDOW", "20")
GA_CCI_PERIOD = _getenv_int("GA_CCI_PERIOD", "20")
GA_WILLIAMS_PERIOD = _getenv_int("GA_WILLIAMS_PERIOD", "14")
GA_ROC_PERIOD = _getenv_int("GA_ROC_PERIOD", "10")
GA_MFI_PERIOD = _getenv_int("GA_MFI_PERIOD", "14")
GA_CMF_PERIOD = _getenv_int("GA_CMF_PERIOD", "20")
GA_DONCHIAN_PERIOD = _getenv_int("GA_DONCHIAN_PERIOD", "20")
GA_BB_SQUEEZE_WINDOW = _getenv_int("GA_BB_SQUEEZE_WINDOW", "50")
GA_BB_SQUEEZE_QUANTILE = _getenv_float("GA_BB_SQUEEZE_QUANTILE", "0.2")
GA_PIVOT_ORDER = _getenv_int("GA_PIVOT_ORDER", "3")
GA_PSAR_AF_START = _getenv_float("GA_PSAR_AF_START", "0.02")
GA_PSAR_AF_STEP = _getenv_float("GA_PSAR_AF_STEP", "0.02")
GA_PSAR_AF_MAX = _getenv_float("GA_PSAR_AF_MAX", "0.2")