"""
WLD 3분 BB 시뮬레이션.
기본: 하단 상향 돌파 매수, 상단 상향 돌파 매도.
수수료 반영, 레짐/필터 조합 비교 지원.
python simulation.py # analyze → discover → HTML (탐색 규칙 매수·매도)
python simulation.py analyze # (고급) 조합 분석만
python simulation.py discover # (고급) 규칙 탐색만
python simulation.py compare # (고급) 9종 프리셋 비교
python simulation.py mtf # (고급) 구 MTF BB 정책
"""
from __future__ import annotations
import sys
import webbrowser
from dataclasses import dataclass
from pathlib import Path
import pandas as pd
import plotly.graph_objs as go
from plotly.subplots import make_subplots
from config import (
BUY_COOLDOWN_SEC,
COIN_NAME,
ENTRY_INTERVAL,
SELL_COOLDOWN_SEC,
SIM_INITIAL_CASH_KRW,
SIM_MIN_ORDER_KRW,
SYMBOL,
TRADING_FEE_RATE,
TREND_INTERVAL_1D,
TREND_INTERVAL_1H,
)
from monitor import Monitor
import strategy
REPORT_DIR = Path(__file__).resolve().parent / "reports"
OUTPUT_HTML = REPORT_DIR / "wld_bb_simulation.html"
def interval_chart_label(interval_min: int) -> str:
"""차트 제목용 봉 라벨."""
if interval_min >= 1440:
return "일봉"
return f"{interval_min}분봉"
def _add_trade_markers(fig, trades: list["SimTrade"], row: int = 1) -> None:
"""
매수·매도 마커·라벨 (Scatter trace만 사용, 범례와 함께 토글).
simulate_mtf.py 와 동일 스타일.
"""
for action, color, symbol, label, text_pos in [
("매수", "#16a34a", "triangle-up", "매수", "top center"),
("매도", "#dc2626", "triangle-down", "매도", "bottom center"),
]:
pts = [t for t in trades if t.action == action]
if not pts:
continue
fig.add_trace(
go.Scatter(
x=[t.dt for t in pts],
y=[t.price for t in pts],
mode="markers+text",
name=label,
legendgroup=label,
text=[label] * len(pts),
textposition=text_pos,
textfont=dict(
size=12,
color=color,
family="Malgun Gothic, Arial, sans-serif",
),
marker=dict(
symbol=symbol,
size=16,
color=color,
line=dict(width=2, color="#111"),
),
hovertext=[
f"{label} 체결
{t.signal}
₩{t.price:,.2f}
₩{t.krw:,.0f}"
for t in pts
],
hovertemplate="%{hovertext}",
),
row=row,
col=1,
)
def build_simulation_html(
df: pd.DataFrame,
result: SimResult,
trend: str,
interval_min: int = ENTRY_INTERVAL,
note: str = "",
) -> str:
"""simulate_mtf.py 와 동일 레이아웃의 HTML 리포트."""
df = strategy.prepare_entry_df(df.copy())
iv_label = interval_chart_label(interval_min)
buy_n = sum(1 for t in result.trades if t.action == "매수")
sell_n = sum(1 for t in result.trades if t.action == "매도")
pnl_krw = result.final_asset - result.initial_cash
summary = {
"config_name": result.config_name,
"period_start": str(df.index[0]),
"period_end": str(df.index[-1]),
"interval_label": iv_label,
"trend": trend,
"signal_count": len(result.trades),
"buy_signal_count": buy_n,
"sell_signal_count": sell_n,
"total_trades": result.trade_count,
"pnl_krw": round(pnl_krw, 0),
"pnl_pct": round(result.total_return_pct, 2),
"total_fees": round(result.total_fees, 0),
"win_count": result.win_count,
"note": note,
}
fig = make_subplots(
rows=3,
cols=1,
shared_xaxes=True,
vertical_spacing=0.05,
row_heights=[0.58, 0.2, 0.22],
subplot_titles=(
f"{COIN_NAME} ({SYMBOL}) {iv_label} — {result.config_name}",
"RSI (14)",
"거래량",
),
)
fig.add_trace(
go.Candlestick(
x=df.index,
open=df["Open"],
high=df["High"],
low=df["Low"],
close=df["Close"],
name=f"{iv_label} 캔들",
increasing_line_color="#ef4444",
decreasing_line_color="#3b82f6",
),
row=1,
col=1,
)
if "MA" in df.columns:
fig.add_trace(
go.Scatter(
x=df.index,
y=df["MA"],
name="BB 중심",
line=dict(color="#64748b", width=1, dash="dot"),
),
row=1,
col=1,
)
if "Upper" in df.columns:
fig.add_trace(
go.Scatter(
x=df.index,
y=df["Upper"],
name="BB 상단",
line=dict(color="#94a3b8", width=1),
),
row=1,
col=1,
)
if "Lower" in df.columns:
fig.add_trace(
go.Scatter(
x=df.index,
y=df["Lower"],
name="BB 하단",
line=dict(color="#94a3b8", width=1),
),
row=1,
col=1,
)
_add_trade_markers(fig, result.trades, row=1)
if not result.trades:
fig.add_annotation(
text=(
f"이 기간에는 체결 신호가 없습니다.
"
f"전략: {result.config_name} | 추세: {trend}"
),
xref="paper",
yref="paper",
x=0.5,
y=0.88,
showarrow=False,
font=dict(size=14, color="#b45309"),
bgcolor="#fffbeb",
bordercolor="#f59e0b",
borderwidth=1,
)
if "RSI" in df.columns:
fig.add_trace(
go.Scatter(
x=df.index,
y=df["RSI"],
name="RSI",
line=dict(color="#7c3aed"),
),
row=2,
col=1,
)
fig.add_hline(y=70, line_dash="dot", line_color="#9ca3af", row=2, col=1)
fig.add_hline(y=50, line_dash="dot", line_color="#d1d5db", row=2, col=1)
fig.add_trace(
go.Bar(
x=df.index,
y=df["Volume"],
name="Volume",
marker_color="#cbd5e1",
),
row=3,
col=1,
)
fig.update_layout(
height=920,
template="plotly_white",
xaxis_rangeslider_visible=False,
legend=dict(orientation="h", y=1.05, x=0),
margin=dict(l=60, r=30, t=90, b=40),
)
fig.update_yaxes(title_text="가격 (KRW)", row=1, col=1)
fig.update_yaxes(title_text="RSI", row=2, col=1, range=[0, 100])
chart_html = fig.to_html(full_html=False, include_plotlyjs="cdn")
trade_rows = ""
for t in result.trades:
cls = "buy" if t.action == "매수" else "sell"
pnl = f"{t.pnl:+,.0f}" if t.pnl is not None else "-"
trade_rows += f"""
| {t.dt} |
{t.action} |
체결 |
₩{t.price:,.2f} |
{t.signal} |
₩{t.krw:,.0f} |
₩{t.fee:,.0f} |
{pnl} |
"""
if not trade_rows:
trade_rows = '| 신호 없음 |
'
note_html = f"{summary['note']}
" if summary.get("note") else ""
sells = summary["sell_signal_count"]
win_rate = (
summary["win_count"] / sells * 100 if sells else 0.0
)
return f"""
{SYMBOL} BB 시뮬레이션
{COIN_NAME} ({SYMBOL}) BB 시뮬레이션
전략: {summary['config_name']} | 추세: {summary['trend']} | 기간: {summary['period_start']} ~ {summary['period_end']}
{note_html}
▲ 매수 범례 클릭 시 마커·라벨 함께 숨김
▼ 매도 동일
체결{summary['total_trades']} (매수 {summary['buy_signal_count']} / 매도 {summary['sell_signal_count']})
손익₩{summary['pnl_krw']:+,.0f} ({summary['pnl_pct']:+.2f}%)
수수료₩{summary['total_fees']:,.0f}
승률(매도 기준){win_rate:.1f}%
{chart_html}
신호·체결 내역
| 시각 | 구분 | 상태 | 가격 | 신호 | 금액 | 수수료 | 손익 |
{trade_rows}
"""
@dataclass
class SimTrade:
dt: pd.Timestamp
action: str
signal: str
price: float
krw: float
fee: float
quantity: float
pnl: float | None
cash_after: float
total_asset: float
@dataclass
class SimResult:
config_name: str
trades: list[SimTrade]
initial_cash: float
final_cash: float
final_coin_qty: float
final_price: float
realized_pnl: float
total_fees: float
final_asset: float
total_return_pct: float
trade_count: int
win_count: int
def run_backtest(
df_3m: pd.DataFrame,
df_1d: pd.DataFrame,
df_1h: pd.DataFrame,
config_name: str = "",
initial_cash: float = SIM_INITIAL_CASH_KRW,
min_order_krw: float = SIM_MIN_ORDER_KRW,
fee_rate: float = TRADING_FEE_RATE,
) -> SimResult:
"""신호 순서대로 현물 매수/매도 시뮬레이션 (수수료 차감)."""
cash = float(initial_cash)
coin_qty = 0.0
cost_basis = 0.0
realized_pnl = 0.0
total_fees = 0.0
win_count = 0
trades: list[SimTrade] = []
last_buy_ts: pd.Timestamp | None = None
last_sell_ts: pd.Timestamp | None = None
signals = df_3m[df_3m["point"] == 1].sort_index()
for ts, row in signals.iterrows():
price = float(row["Close"])
action = str(row.get("action", ""))
signal_name = str(row.get("signal", ""))
if price <= 0:
continue
trend_at = str(row.get("trend", "")) or strategy.get_trend_at(df_1d, df_1h, ts)
if trend_at not in ("up", "down", "range"):
trend_at = strategy.get_trend_at(df_1d, df_1h, ts)
if action == "buy":
if last_buy_ts is not None:
if (ts - last_buy_ts).total_seconds() < BUY_COOLDOWN_SEC:
continue
buy_krw = float(
strategy.get_buy_amount(SYMBOL, signal_name, price, trend_at)
)
buy_krw = max(min_order_krw, min(buy_krw, cash))
fee = buy_krw * fee_rate
total_cost = buy_krw + fee
if buy_krw < min_order_krw or cash < total_cost:
continue
qty = buy_krw / price
cash -= total_cost
total_fees += fee
cost_basis += buy_krw
coin_qty += qty
last_buy_ts = ts
trades.append(
SimTrade(
dt=ts,
action="매수",
signal=signal_name,
price=price,
krw=buy_krw,
fee=fee,
quantity=qty,
pnl=None,
cash_after=cash,
total_asset=cash + coin_qty * price,
)
)
continue
if action == "sell":
if coin_qty <= 0:
continue
if last_sell_ts is not None:
if (ts - last_sell_ts).total_seconds() < SELL_COOLDOWN_SEC:
continue
ratio = strategy.get_sell_ratio(SYMBOL, signal_name)
sell_qty = min(coin_qty * ratio, coin_qty)
sell_krw = sell_qty * price
if sell_krw < min_order_krw:
if coin_qty * price < min_order_krw:
continue
sell_qty = coin_qty
sell_krw = sell_qty * price
fee = sell_krw * fee_rate
net = sell_krw - fee
avg_cost = cost_basis / coin_qty
sold_cost = avg_cost * sell_qty
pnl = net - sold_cost
cash += net
total_fees += fee
cost_basis -= sold_cost
coin_qty -= sell_qty
realized_pnl += pnl
if pnl > 0:
win_count += 1
if coin_qty < 1e-12:
coin_qty = 0.0
cost_basis = 0.0
last_sell_ts = ts
trades.append(
SimTrade(
dt=ts,
action="매도",
signal=signal_name,
price=price,
krw=sell_krw,
fee=fee,
quantity=sell_qty,
pnl=pnl,
cash_after=cash,
total_asset=cash + coin_qty * price,
)
)
final_price = float(df_3m["Close"].iloc[-1])
final_asset = cash + coin_qty * final_price
sell_trades = sum(1 for t in trades if t.action == "매도")
return SimResult(
config_name=config_name,
trades=trades,
initial_cash=initial_cash,
final_cash=cash,
final_coin_qty=coin_qty,
final_price=final_price,
realized_pnl=realized_pnl,
total_fees=total_fees,
final_asset=final_asset,
total_return_pct=(final_asset - initial_cash) / initial_cash * 100
if initial_cash > 0
else 0.0,
trade_count=len(trades),
win_count=win_count if sell_trades else 0,
)
def print_backtest_report(result: SimResult) -> None:
fee_pct = TRADING_FEE_RATE * 100
print("\n" + "=" * 80)
print(
f"[{result.config_name}] 시작 {result.initial_cash:,.0f}원 | "
f"최소주문 {SIM_MIN_ORDER_KRW:,.0f}원 | 수수료 {fee_pct:.3f}%/쪽"
)
print("=" * 80)
if not result.trades:
print("체결 없음")
else:
print(
f"{'일시':<18} {'구분':<4} {'신호':<22} {'가격':>9} {'금액':>10} "
f"{'수수료':>8} {'수익':>10}"
)
print("-" * 80)
for t in result.trades:
pnl_s = f"{t.pnl:+,.0f}" if t.pnl is not None else "-"
print(
f"{t.dt.strftime('%Y-%m-%d %H:%M'):<18} {t.action:<4} {t.signal:<22} "
f"{t.price:>9,.2f} {t.krw:>10,.0f} {t.fee:>8,.0f} {pnl_s:>10}"
)
print("-" * 80)
sells = sum(1 for t in result.trades if t.action == "매도")
win_rate = result.win_count / sells * 100 if sells else 0.0
print(f"거래 횟수: {result.trade_count} (매도 {sells}회) | 승률: {win_rate:.1f}%")
print(f"수수료 합계: {result.total_fees:,.0f}원")
print(f"실현 손익(수수료 반영): {result.realized_pnl:+,.0f}원")
print(
f"최종 자산: {result.final_asset:,.0f}원 | "
f"총수익: {result.final_asset - result.initial_cash:+,.0f}원 "
f"({result.total_return_pct:+.2f}%)"
)
print("=" * 80)
def run_comparison(df_1d: pd.DataFrame, df_1h: pd.DataFrame, df_3m: pd.DataFrame) -> None:
"""기법 조합별 수익률 비교 (수수료 포함)."""
print(f"\n{'='*80}")
print(f"전략 조합 비교 — {SYMBOL} 3분 | {df_3m.index[0]} ~ {df_3m.index[-1]}")
print(f"시작 {SIM_INITIAL_CASH_KRW:,}원 | 수수료 {TRADING_FEE_RATE*100:.3f}%/매수·매도")
print(f"{'='*80}")
print(
f"{'순위':<4} {'조합':<22} {'수익률':>9} {'최종자산':>12} "
f"{'거래':>6} {'승률':>7} {'수수료':>10}"
)
print("-" * 80)
rows: list[tuple[SimResult, strategy.StrategyConfig]] = []
for cfg in strategy.comparison_presets():
df_sig = strategy.annotate_signals(
SYMBOL,
df_3m.copy(),
simulation=True,
df_1h=df_1h,
df_1d=df_1d,
config=cfg,
)
res = run_backtest(df_sig, df_1d, df_1h, config_name=cfg.name)
rows.append((res, cfg))
rows.sort(key=lambda x: x[0].total_return_pct, reverse=True)
for rank, (res, cfg) in enumerate(rows, 1):
sells = sum(1 for t in res.trades if t.action == "매도")
wr = res.win_count / sells * 100 if sells else 0.0
print(
f"{rank:<4} {res.config_name:<22} {res.total_return_pct:>+8.2f}% "
f"{res.final_asset:>12,.0f} {res.trade_count:>6} {wr:>6.1f}% "
f"{res.total_fees:>10,.0f}"
)
best_res, best_cfg = rows[0]
print("-" * 80)
print(f"1위: {best_cfg.name} ({best_res.total_return_pct:+.2f}%)")
print(
"실거래 적용: strategy.ACTIVE_CONFIG 를 1위 조합으로 맞추세요 "
"(현재 ACTIVE_CONFIG.name=%s)" % strategy.ACTIVE_CONFIG.name
)
print(f"{'='*80}\n")
class Simulation:
def __init__(self) -> None:
self.monitor = Monitor(cooldown_file=None)
def load_mtf(self, symbol: str):
df_1d = self.monitor.get_coin_some_data(symbol, TREND_INTERVAL_1D)
df_1h = self.monitor.get_coin_some_data(symbol, TREND_INTERVAL_1H)
df_3m = self.monitor.get_coin_some_data(symbol, ENTRY_INTERVAL)
if df_1d is None or df_1d.empty:
df_1d = self.monitor.get_coin_more_data(symbol, TREND_INTERVAL_1D, bong_count=500)
if df_1h is None or df_1h.empty:
df_1h = self.monitor.get_coin_more_data(symbol, TREND_INTERVAL_1H, bong_count=5000)
if df_3m is None or df_3m.empty:
df_3m = self.monitor.get_coin_more_data(
symbol, ENTRY_INTERVAL, bong_count=90000, verbose=True
)
df_1d = self.monitor.calculate_technical_indicators(df_1d)
df_1h = self.monitor.calculate_technical_indicators(df_1h)
df_3m = self.monitor.calculate_technical_indicators(df_3m)
return df_1d, df_1h, df_3m
def render_plotly(
self,
df_plot: pd.DataFrame,
trend: str,
result: SimResult,
interval_min: int = ENTRY_INTERVAL,
note: str = "",
open_browser: bool = True,
) -> Path:
"""HTML 리포트 저장 (simulate_mtf.py 동일 스타일)."""
html = build_simulation_html(
df_plot, result, trend, interval_min=interval_min, note=note
)
REPORT_DIR.mkdir(parents=True, exist_ok=True)
OUTPUT_HTML.write_text(html, encoding="utf-8")
print(f"HTML: {OUTPUT_HTML}")
if open_browser:
webbrowser.open(OUTPUT_HTML.resolve().as_uri())
return OUTPUT_HTML
def load_all_frames(self) -> dict[int, pd.DataFrame]:
"""discovered 규칙용 전 간격 로드."""
from mtf_bb import load_frames_from_db
return load_frames_from_db(self.monitor, SYMBOL)
def _run_one_strategy(
self,
name: str,
df_1d: pd.DataFrame,
df_1h: pd.DataFrame,
df_3m: pd.DataFrame,
cfg: strategy.StrategyConfig,
frames: dict | None = None,
) -> tuple[pd.DataFrame, SimResult, int]:
"""한 전략으로 신호·백테스트. 반환: (df, result, 신호수)."""
df_sig = strategy.annotate_signals(
SYMBOL,
df_3m.copy(),
simulation=True,
df_1h=df_1h,
df_1d=df_1d,
config=cfg,
frames=frames,
)
n_sig = int((df_sig["point"] == 1).sum())
res = run_backtest(df_sig, df_1d, df_1h, config_name=name)
return df_sig, res, n_sig
def _frames_to_mtf(
self, frames: dict[int, pd.DataFrame]
) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
"""전 간격 frames에서 1d/1h/3m 추출."""
df_3m = frames.get(ENTRY_INTERVAL)
if df_3m is None or df_3m.empty:
raise ValueError(f"{ENTRY_INTERVAL}분봉 데이터 없음")
df_1d = frames.get(TREND_INTERVAL_1D)
if df_1d is None or df_1d.empty:
df_1d = df_3m
df_1h = frames.get(TREND_INTERVAL_1H)
if df_1h is None or df_1h.empty:
df_1h = df_3m
return df_1d, df_1h, df_3m
def run_discovered_chart(
self,
frames: dict[int, pd.DataFrame],
rules=None,
) -> SimResult:
"""
discovered_rules 매수·매도 규칙만 백테스트하고 HTML에 표시합니다.
차트 마커 = 해당 규칙으로 발생한 매수·매도 체결.
"""
from rule_discovery import DiscoveredRules, load_rules, rules_have_buy
rule_set = rules or load_rules()
if rule_set is None or not rules_have_buy(rule_set):
raise FileNotFoundError(
"discovered_rules.json 이 없거나 매수 규칙이 비어 있습니다."
)
df_1d, df_1h, df_3m = self._frames_to_mtf(frames)
trend = strategy.get_trend(df_1d, df_1h)
print(f"추세(최신): {trend}")
print(f"3분: {df_3m.index[0]} ~ {df_3m.index[-1]} ({len(df_3m)}봉)")
print(f"\n[적용 규칙] {rule_set.name}")
print(f" 매수 AND: {rule_set.buy_all}")
if rule_set.buy_any:
print(f" 매수 OR: {rule_set.buy_any}")
print(f" 매도 AND: {rule_set.sell_all}")
if rule_set.sell_stop:
print(f" 손절: {rule_set.sell_stop}")
df_sig = strategy.annotate_discovered_signals(
SYMBOL, frames, df_1d, df_1h, rules=rule_set, data=df_3m
)
n_sig = int((df_sig["point"] == 1).sum())
buy_sig = int((df_sig["action"] == "buy").sum())
sell_sig = int((df_sig["action"] == "sell").sum())
print(f"\n규칙 신호: {n_sig} (매수 {buy_sig} / 매도 {sell_sig})")
result = run_backtest(df_sig, df_1d, df_1h, config_name=rule_set.name)
print_backtest_report(result)
note = (
f"매수 규칙: {rule_set.buy_all}"
+ (f" | OR {rule_set.buy_any}" if rule_set.buy_any else "")
+ f" | 매도: {rule_set.sell_all}"
)
self.render_plotly(df_sig, trend, result, note=note)
return result
def run_mtf_analysis() -> None:
"""봉별 BB 백테스트 비교, 정책 저장, MTF 시뮬 차트."""
from mtf_bb import apply_policy, load_frames_from_db, run_interval_comparison, save_policy
monitor = Monitor()
policy, _ = run_interval_comparison(monitor)
save_policy(policy)
apply_policy(policy)
frames = load_frames_from_db(monitor, SYMBOL)
df_1d = frames.get(TREND_INTERVAL_1D)
if df_1d is None or df_1d.empty:
df_1d = frames[ENTRY_INTERVAL]
df_1h = frames.get(TREND_INTERVAL_1H)
if df_1h is None or df_1h.empty:
df_1h = frames[ENTRY_INTERVAL]
cfg = strategy.StrategyConfig(
name="MTF_BB",
use_mtf=True,
use_regime_switch=strategy.ACTIVE_CONFIG.use_regime_switch,
use_rsi_filter=False,
use_volume_filter=False,
use_squeeze_filter=False,
use_stop_loss=True,
)
df_sig = strategy.annotate_mtf_signals(SYMBOL, frames, df_1d, df_1h, policy, cfg)
trend = strategy.get_trend(df_1d, df_1h)
print(f"\nMTF 시뮬 ({policy.name}) | 추세: {trend}")
result = run_backtest(df_sig, df_1d, df_1h, config_name=policy.name)
print_backtest_report(result)
Simulation().render_plotly(
df_sig,
trend,
result,
interval_min=policy.buy_interval,
note=f"MTF 정책: 매수 {policy.buy_interval}분 / 확인 {policy.buy_confirm_intervals}",
)
def _load_all_frames_or_exit() -> dict[int, pd.DataFrame] | None:
"""coins.db 전 간격 로드. 부족 시 None."""
from rule_discovery import load_frames
monitor = Monitor(cooldown_file=None)
frames = load_frames(monitor)
if len(frames) < 3:
print("coins.db 데이터 부족. python downloader.py 실행 후 재시도.")
return None
return frames
def run_analyze(frames: dict[int, pd.DataFrame] | None = None) -> None:
"""전 봉 BB·일목 위치 조합 분석."""
from combination_analyzer import analyze_combinations, save_report
if frames is None:
print("=== 전 봉 BB·일목 조합 분석 ===")
frames = _load_all_frames_or_exit()
if frames is None:
return
report = analyze_combinations(frames)
save_report(report)
def run_discover(frames: dict[int, pd.DataFrame] | None = None):
"""모든 봉·BB·일목 특징으로 최적 규칙 탐색 후 JSON 저장."""
from rule_discovery import discover_rules, save_rules
if frames is None:
print("=== 규칙 탐색 (discover) ===")
frames = _load_all_frames_or_exit()
if frames is None:
return None
rules = discover_rules(frames)
save_rules(rules)
print(f"\n저장: discovered_rules.json")
return rules
def run_full_pipeline() -> None:
"""
일반 사용자용 일괄 실행: analyze → discover → HTML.
DB 로드는 한 번만 수행합니다.
"""
print("=" * 60)
print("전체 파이프라인: analyze → discover → HTML")
print("=" * 60)
frames = _load_all_frames_or_exit()
if frames is None:
return
print("\n[1/3] 조합 분석 (analyze)")
run_analyze(frames)
print("\n[2/3] 규칙 탐색 (discover)")
run_discover(frames)
print("\n[3/3] 백테스트·HTML 차트 (탐색 규칙 매수·매도)")
if rules is None:
print("규칙 탐색 실패 — HTML 생략")
return
Simulation().run_discovered_chart(frames, rules=rules)
print("\n완료.")
def print_usage() -> None:
print(
"""
DeepCoin simulation.py
python simulation.py
analyze + discover + HTML (차트 = discovered_rules 매수·매도)
(고급) analyze | discover | compare | mtf
"""
)
def main() -> None:
sim = Simulation()
if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help", "help"):
print_usage()
return
if len(sys.argv) == 1 or (len(sys.argv) > 1 and sys.argv[1] in ("all", "chart", "html")):
if len(sys.argv) > 1 and sys.argv[1] in ("chart", "html"):
print("참고: chart/html 옵션은 제거되었습니다. python simulation.py 만 사용하세요.\n")
run_full_pipeline()
return
if len(sys.argv) > 1 and sys.argv[1] == "analyze":
run_analyze()
return
if len(sys.argv) > 1 and sys.argv[1] == "discover":
run_discover()
return
if len(sys.argv) > 1 and sys.argv[1] == "mtf":
run_mtf_analysis()
return
if len(sys.argv) > 1 and sys.argv[1] == "compare":
df_1d, df_1h, df_3m = sim.load_mtf(SYMBOL)
run_comparison(df_1d, df_1h, df_3m)
return
print(f"알 수 없는 옵션: {sys.argv[1]}\n")
print_usage()
if __name__ == "__main__":
main()