feat(spot): 3단계 운영 파이프라인 — composite_v3 + MTF paper/live
MTF 필터 백테스트, paper/live 체결, 빗썸 Private API 연동 및 운영 스크립트·설계 문서를 추가해 2단계 전략을 실거래 단계에 연결한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
55
scripts/3_run_filtered_backtest.py
Normal file
55
scripts/3_run_filtered_backtest.py
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
"""3단계: MTF 필터 적용 composite_v3 백테스트 (최근 3년 sim)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SRC = ROOT / "src"
|
||||
if str(SRC) not in sys.path:
|
||||
sys.path.insert(0, str(SRC))
|
||||
|
||||
from deepcoin.config import load_settings
|
||||
from deepcoin.operations.backtest import run_filtered_backtest, save_backtest_report
|
||||
|
||||
|
||||
def _configure_logging(verbose: bool) -> None:
|
||||
level = logging.DEBUG if verbose else logging.INFO
|
||||
logging.basicConfig(
|
||||
level=level,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""CLI 진입점."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="3단계: composite_v3 + MTF 필터 3년 백테스트",
|
||||
)
|
||||
parser.add_argument("-v", "--verbose", action="store_true")
|
||||
args = parser.parse_args()
|
||||
_configure_logging(args.verbose)
|
||||
|
||||
settings = load_settings()
|
||||
report = run_filtered_backtest(settings)
|
||||
path = save_backtest_report(report, settings.ops_filtered_backtest_json)
|
||||
|
||||
filt = report["filtered_sim"]
|
||||
raw = report["raw_sim"]
|
||||
print("\n=== 3단계 MTF 필터 백테스트 ===")
|
||||
print(f"기법: {report['technique_id']}")
|
||||
print(f"원시 신호: {report['raw_signal_count']} → 필터 통과: {report['filtered_signal_count']}")
|
||||
print(f"원시 3년 sim: {raw.get('total_return_pct')}%")
|
||||
print(f"필터 3년 sim: {filt.get('total_return_pct')}%")
|
||||
print(f"개선: {report['improvement_return_pct']}%p")
|
||||
print(f"\nJSON: {path}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
94
scripts/3_run_operations.py
Normal file
94
scripts/3_run_operations.py
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
"""3단계: paper/live 운영 1회 tick (신호 확인·체결)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SRC = ROOT / "src"
|
||||
if str(SRC) not in sys.path:
|
||||
sys.path.insert(0, str(SRC))
|
||||
|
||||
from deepcoin.config import load_settings
|
||||
from deepcoin.operations.runner import OperationsRunner
|
||||
|
||||
|
||||
def _configure_logging(verbose: bool) -> None:
|
||||
level = logging.DEBUG if verbose else logging.INFO
|
||||
logging.basicConfig(
|
||||
level=level,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""CLI 진입점."""
|
||||
parser = argparse.ArgumentParser(description="3단계: DeepCoin 운영 tick")
|
||||
parser.add_argument(
|
||||
"--mode",
|
||||
choices=("paper", "live"),
|
||||
default=None,
|
||||
help="OPS_MODE 덮어쓰기 (기본 .env)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-sync",
|
||||
action="store_true",
|
||||
help="캔들 증분 동기화 생략",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--loop",
|
||||
type=int,
|
||||
default=0,
|
||||
metavar="SEC",
|
||||
help="N초마다 반복 실행 (0=1회)",
|
||||
)
|
||||
parser.add_argument("-v", "--verbose", action="store_true")
|
||||
args = parser.parse_args()
|
||||
_configure_logging(args.verbose)
|
||||
|
||||
if args.mode:
|
||||
import os
|
||||
os.environ["OPS_MODE"] = args.mode
|
||||
settings = load_settings()
|
||||
|
||||
if settings.ops_mode == "live":
|
||||
if not settings.bithumb_access_key or not settings.bithumb_secret_key:
|
||||
print("live 모드에는 BITHUMB_ACCESS_KEY / BITHUMB_SECRET_KEY 가 필요합니다.", file=sys.stderr)
|
||||
return 1
|
||||
print("경고: live 모드 — 실제 주문이 발생할 수 있습니다.")
|
||||
|
||||
runner = OperationsRunner(settings)
|
||||
sync = not args.no_sync
|
||||
|
||||
while True:
|
||||
report = runner.tick(sync_candles=sync)
|
||||
port = report["portfolio"]
|
||||
print("\n=== 3단계 운영 tick ===")
|
||||
print(f"모드: {report['mode']}")
|
||||
print(
|
||||
f"최신 봉 후보: {report.get('latest_bar_candidates', 0)} · "
|
||||
f"필터 통과: {report['filtered_signals']} · "
|
||||
f"처리 bar: {report.get('pending_bars', [])}"
|
||||
)
|
||||
print(f"이번 체결: {len(report['executions'])}건")
|
||||
print(
|
||||
f"포트폴리오: 현금 {port['cash_krw']:,.0f}원 · "
|
||||
f"코인 {port['coin_qty']:.8f} {settings.symbol}"
|
||||
)
|
||||
print(f"리포트: {settings.ops_report_json}")
|
||||
|
||||
if args.loop <= 0:
|
||||
break
|
||||
time.sleep(args.loop)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
15
scripts/3_run_stage3_all.sh
Executable file
15
scripts/3_run_stage3_all.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# 3단계 일괄: MTF 필터 백테스트 → paper 운영 1회 tick
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
export PYTHONPATH=src
|
||||
|
||||
echo "=== 3-1 MTF 필터 백테스트 ==="
|
||||
python scripts/3_run_filtered_backtest.py
|
||||
|
||||
echo ""
|
||||
echo "=== 3-2 paper 운영 tick ==="
|
||||
python scripts/3_run_operations.py --no-sync
|
||||
|
||||
echo ""
|
||||
echo "완료. 리포트: docs/spot/3_operations/"
|
||||
Reference in New Issue
Block a user