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>
This commit is contained in:
119
archive/tests/ticker_sim.py
Normal file
119
archive/tests/ticker_sim.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""종목 수 확장 시뮬레이션 - 거래량 상위 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()
|
||||
Reference in New Issue
Block a user