Files
upbit-trader/archive/tests/ticker_sim.py
joungmin 6e0c4508fa refactor: MVC 구조 분리 + 미사용 파일 archive 정리
- tick_trader.py를 Controller로 축소, 로직을 3개 모듈로 분리:
  - core/signal.py: 시그널 감지, 지표 계산 (calc_vr, calc_atr, detect_signal)
  - core/order.py: Upbit 주문 실행 (매수/매도/취소/조회)
  - core/position_manager.py: 포지션 관리, DB sync, 복구, 청산 조건
- type hints, Google docstring, 구체적 예외 타입 적용
- 50줄 초과 함수 분리 (process_signal, restore_positions)
- 미사용 파일 58개 archive/ 폴더로 이동
- README.md 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:46:47 +09:00

120 lines
4.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""종목 수 확장 시뮬레이션 - 거래량 상위 N개 종목별 vol-lead 전략 비교."""
import os
import pickle
import sys
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
import pandas as pd
import pyupbit
# vol_lead_sim.py의 공통 파라미터/함수 재사용
sys.path.insert(0, str(Path(__file__).parent))
from vol_lead_sim import (
STOP_LOSS_PCT, TIME_STOP_HOURS, TIME_STOP_MIN_PCT,
FEE, LOCAL_VOL_HOURS, VOL_MULT, PRICE_QUIET_PCT, SIGNAL_TIMEOUT_H,
FROM_DATE, simulate_pos, run_trend, run_vol_lead_thresh,
)
CACHE_FILE = Path("vol_lead_cache_30.pkl")
TOP30_FILE = Path("top30_tickers.pkl")
DAYS = 46.0
def load_data() -> dict:
return pickle.load(open(CACHE_FILE, "rb"))
def run_subset(data: dict, tickers: list, thresh: float) -> dict:
agg = {"total": 0, "wins": 0, "pnl": 0.0, "per_ticker": []}
for t in tickers:
if t not in data:
continue
trades = run_vol_lead_thresh(data[t], thresh)
n = len(trades)
w = sum(1 for x in trades if x[0])
p = sum(x[1] for x in trades)
agg["total"] += n
agg["wins"] += w
agg["pnl"] += p
agg["per_ticker"].append((t, n, w, p))
agg["wr"] = agg["wins"] / agg["total"] * 100 if agg["total"] else 0
return agg
def main() -> None:
data = load_data()
top30 = pickle.load(open(TOP30_FILE, "rb"))
# 데이터 충분한 종목만 (400봉 이상 = 16일 이상)
valid = [t for t in top30 if t in data and len(data[t]) >= 400]
n_max = len(valid)
print(f"유효 종목: {n_max}")
print(f"기간: 46일 (2026-01-15 ~ 2026-03-02)\n")
# ── A 현행 기준선 (9종목) ─────────────────────────
orig9 = ["KRW-DKA","KRW-LAYER","KRW-SIGN","KRW-SOL","KRW-ETH",
"KRW-XRP","KRW-HOLO","KRW-OM","KRW-ORBS"]
orig9_valid = [t for t in orig9 if t in data]
a_agg = {"total": 0, "wins": 0, "pnl": 0.0}
for t in orig9_valid:
trades = run_trend(data[t])
a_agg["total"] += len(trades)
a_agg["wins"] += sum(1 for x in trades if x[0])
a_agg["pnl"] += sum(x[1] for x in trades)
a_wr = a_agg["wins"] / a_agg["total"] * 100 if a_agg["total"] else 0
print(f"[기준: A 현행 9종목] {a_agg['total']}건 | 승률={a_wr:.0f}% | 누적={a_agg['pnl']:+.2f}%\n")
# ── 종목수별 비교 (임계값 4.8% 고정) ────────────────
THRESH = 4.8
subset_ns = [9, 15, 20, n_max]
print(f"임계값 +{THRESH}% | 종목 수 확장 효과")
print(f"{'종목수':>6}{'총거래':>6} {'일평균':>7} {'월환산':>7}{'승률':>5} {'누적PnL':>10}")
print("" * 56)
for n in subset_ns:
s = run_subset(data, valid[:n], THRESH)
pdm = s["total"] / DAYS
pmm = pdm * 30
marker = " ← 현재설정" if n == 9 else ""
print(f"{n:>5}종목 │ {s['total']:>6}{pdm:>6.2f}회/일 {pmm:>6.1f}회/월 │ "
f"{s['wr']:>4.0f}% {s['pnl']:>+9.2f}%{marker}")
# ── 임계값 × 종목수 매트릭스 ─────────────────────
thresholds = [3.6, 4.0, 4.4, 4.8]
col_ns = [9, 15, 20, n_max]
print(f"\n임계값 × 종목수 매트릭스 (건수 / 승률 / 누적PnL)")
col_w = 20
header = f"{'임계값':>6}"
for n in col_ns:
header += f" {f'{n}종목':^{col_w}}"
print(header)
print("" * (10 + col_w * len(col_ns)))
for thresh in thresholds:
row = f"+{thresh:.1f}% │"
for n in col_ns:
s = run_subset(data, valid[:n], thresh)
wr = s["wins"] / s["total"] * 100 if s["total"] else 0
cell = f"{s['total']}{wr:.0f}% {s['pnl']:+.1f}%"
row += f" {cell:<{col_w}}"
print(row)
# ── 전체 종목별 기여도 (4.8%) ────────────────────
print(f"\n종목별 기여도 ({n_max}종목, +4.8%)")
print(f"{'종목':<16} {'거래':>5} {'승률':>6} {'누적PnL':>10} {'평균PnL/거래':>12}")
print("" * 55)
s = run_subset(data, valid, THRESH)
s["per_ticker"].sort(key=lambda x: x[3], reverse=True)
for t, n, w, p in s["per_ticker"]:
wr = w / n * 100 if n else 0
avg = p / n if n else 0
print(f"{t:<16} {n:>5}{wr:>5.0f}% {p:>+9.2f}% {avg:>+10.2f}%/건")
if __name__ == "__main__":
main()