Files
upbit-trader/ticker_sim.py
joungmin 324d69dde0 feat: volume-lead strategy with compounding, WF filter, and DB-backed simulation
- core/strategy.py: replace trend strategy with volume-lead accumulation
  (vol spike + 2h quiet → signal, +4.8% rise → entry)
- core/trader.py: compound budget adjusts on both profit and loss (floor 30%)
- core/notify.py: add accumulation signal telegram notification
- ohlcv_db.py: Oracle ADB OHLCV cache (insert, load, incremental update)
- sim_365.py: 365-day compounding simulation loading from DB
- krw_sim.py: KRW-based simulation with MAX_POSITIONS constraint
- ticker_sim.py: ticker count expansion comparison
- STRATEGY.md: full strategy documentation
- .gitignore: exclude *.pkl cache files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 01:46:03 +09:00

120 lines
4.4 KiB
Python
Raw 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()