Python으로 AI-Native 트레이딩 엔진을 구축했습니다. 5개월 후, 무엇이 변했을까요?
요약
Python을 사용하여 Bybit 선물 거래를 위한 AI-Native 트레이딩 엔진 v2를 구축한 과정을 다룹니다. 머신러닝 스코어링, 백테스팅 엔진, SQLite 기반 상태 관리 및 AI 에이전트를 활용한 시스템 고도화 내용을 포함합니다.
핵심 포인트
- ML 스코어링 도입을 통한 전략 정교화
- SQLite를 활용한 데이터 무결성 및 레이스 컨디션 해결
- AI 에이전트를 활용한 운영 버그 탐지 및 시스템 개선
- 백테스팅 엔진과 부분 익절 로직 등 실전 기능 강화
Python으로 AI-Native 트레이딩 엔진을 구축했습니다. 5개월 후, 무엇이 변했을까요?
9개의 전략 → 12개. ML 스코어링 (ML scoring), 백테스팅 (backtesting), 부분 익절 (partial take-profit), 3단계 감사(3-echelon audit)를 견뎌낸 Telegram bot. 오픈 소스, MIT 라이선스, 실제 자금으로 트레이딩 중.
내가 이것을 만든 이유 (그리고 계속 만드는 이유)
트레이딩 봇은 두 가지 유형이 있습니다. 월 50달러짜리 블랙박스 SaaS이거나, 새벽 3시에 다운되는 GitHub 스크립트입니다. 나는 둘 다 원하지 않았습니다.
5개월 전, 나는 Bybit 선물 거래를 위한 AI-native 트레이딩 엔진인 bybit-ws v1을 출시했습니다. 핵심 루프는 간단했습니다. 매일 Bollinger Bands를 스캔하고, 8가지 지표를 통해 모든 신호의 점수를 매긴 뒤, 점수가 5.5/10을 넘으면 진입하는 방식이었습니다. 작동했습니다. 돈을 벌었습니다. 그리고 나는 계속해서 만들어 나갔습니다.
이것은 v2에 대한 이야기입니다: 실제 거래 데이터에 대한 머신러닝 (machine learning), 과거 klines를 대상으로 실행되는 백테스팅 (backtesting) 엔진, 부분 익절 (partial take-profit) 로직, 그리고 세 명의 AI 에이전트가 병렬로 찾아낸 14개의 운영 버그를 견뎌낸 Telegram bot에 대한 이야기입니다.
아키텍처: 5개월간의 반복 작업 결과물
v1은 깔끔했습니다. v2는 실전에서 검증되었습니다.
┌──────────────────────────┐
│ Hermes Agent │
│ Voice/chat orchestrator │
...
★ = v2에서 새로 추가됨
두 번이 아닌 세 번의 사이클입니다. 새로운 x10 사이클 (8분마다 실행)은 3배 레버리지 포지션에는 영향을 주지 않으면서 고레버리지 포지션의 트레일링 스톱 (trailing stops)을 처리합니다. 헤비 사이클 (Heavy cycle, 7분에서 2분으로 단축)에는 이제 부분 익절 (partial take-profit) 및 펀딩 로테이션 (funding rotation) 확인 기능이 포함됩니다.
상태(State)는 SQLite, 그 자체입니다. v1은 인메모리 딕셔너리 (in-memory dicts)와 JSON 스냅샷이 섞여 있었습니다. v2는 단일 진실 공급원 (single source of truth)으로서 WAL 모드를 사용하는 SQLite를 사용합니다. 8개의 테이블, WHERE 절을 사용한 원자적 UPDATE (atomic UPDATEs), 레이스 컨디션 (race conditions) 제로. JSON 스냅샷은 백업용으로만 사용됩니다.
12개의 전략 (9개에서 증가)
| 전략 | 레버리지 (Lev) | 타임프레임 (Timeframe) | 새로운 점 |
|---|---|---|---|
| Bollinger Grid LONG | 3x | Daily | — |
| ... | |||
| 황금률은 여전히 적용됩니다: 모든 진입은 5.5/10 임계값을 가진 **8개 이상의 지표를 통한 스코어링 (scoring across 8+ metrics)**을 통과해야 합니다. 하지만 이제 그 위에 새로운 레이어가 추가되었습니다. |
ML 스코어링 (ML Scoring): 휴리스틱 (Heuristics)만으로는 부족할 때
5개월 후, 시스템은 진입 가격, 청산 가격, 손익 (PnL), 익절 (take-profit) 또는 손절 (stop-loss) 여부와 같은 결과를 포함한 **282개의 실제 신호 (real signals)**를 기록했습니다. 이것은 하나의 데이터셋입니다.
저는 이를 바탕으로 **RandomForest 분류기 (RandomForest classifier)**를 학습시켰습니다:
features = ['score', 'price_vs_lower', 'price_vs_upper',
'volume_24h', 'funding_rate', 'rsi_14',
'bb_squeeze', 'consecutive_down_days']
...
결과:
- F1 스코어 (F1 score): 0.69 (262개 신호의 테스트 세트 기준)
- 주요 특성 (Top features): score (가중치 0.31), price_vs_lower (0.22), RSI (0.15)
- 결합 스코어링 (Combined scoring): ML 70% + 휴리스틱 (heuristic) 30%. 두 방식이 충돌할 경우, ML이 우선합니다.
모델은 매 무거운 사이클 (heavy cycle)마다 실행되어 오픈된 포지션의 점수를 재산정하며, 새로운 진입을 거부(veto)할 수 있습니다. 이것은 블랙박스 (black box)가 아닙니다. 특성 중요도 (feature importance)가 기록되므로, 모델이 왜 그런 결정을 내렸는지 확인할 수 있습니다.
F1=0.69가 "돈을 찍어내는 AI"일까요? 아닙니다. 그것은 휴리스틱이 놓칠 수 있는 나쁜 진입을 잡아내는 필터입니다. 백테스팅 (backtesting) 결과, ML 레이어는 하위 25%의 신호를 거부하는 것만으로도 거래당 평균 PnL을 18% 개선했습니다.
백테스팅 (Backtesting): 실제 K라인 (Klines)을 이용한 워크 포워드 (Walk-Forward)
백테스팅하지 않은 전략은 신뢰할 수 없습니다. 저는 Bybit의 REST API에서 과거 K라인 (klines)을 가져와 매일 재현하는 워크 포워드 엔진을 구축했습니다:
# 매일: 스캔(scan) → 스코어링(score) → 진입 시뮬레이션(simulate entry) → PnL 추적(track PnL)
for day in trading_days:
klines = fetch_klines(symbol, start=day.start_ms)
...
일봉 (Daily) 신호를 사용하여 BTC, ETH, SUI, ADA를 대상으로 테스트했습니다:
| 심볼 (Symbol) | 승률 (Win Rate) | 평균 PnL (Avg PnL) | 최고 거래 (Best Trade) | 최악의 거래 (Worst Trade) |
|---|---|---|---|---|
| SUIUSDT | 42.9% | +6.56% | +27.3% | −12.1% |
| ... |
모든 전략이 승리하는 것은 아닙니다. 백테스팅을 통해 ETH는 경계선에 있다는 사실이 드러났습니다. 승률은 무작위(random)를 겨우 상회하는 수준이며, 평균 PnL은 수수료에 근접합니다. 이는 매우 가치 있는 정보입니다. 계좌가 파산한 뒤에 아는 것보다 백테스팅을 통해 미리 아는 것이 훨씬 낫습니다.
리스크 관리 (Risk Management): 계좌를 지켜주는 지루한 작업들
부분 익절 (Partial Take-Profit)
대부분의 봇은 포지션을 한 번에 종료합니다. 부분 익절 (Partial TP)은 점진적으로 물량을 줄여나갑니다:
# 동적 분할: 첫 번째 TP에서 20% → 최종 TP에서 최대 50%
tp_levels = calculate_partial_tp(
entry_price, mark_price, unrealized_pnl_pct
...
Numpy를 사용하지 않았습니다. 순수 statistics 모듈만 사용합니다. 추가 의존성 없이 모든 Python 3.11+ 환경에서 작동합니다.
x10을 위한 트레일링 스탑 (Trailing Stop)
고레버리지 포지션은 매우 빠르게 움직입니다. 새로운 trailing_sl_x10 모듈은 매 헤비 사이클 (heavy cycle)마다 실행되지만, 레버리지가 10배(≥10) 이상인 포지션에 대해서만 작동합니다. 다음과 같은 경우 스탑로스 (Stop-loss)를 조입니다:
- W-BB가 25% 미만으로 떨어질 때 (LONG 포지션의 경우 가격이 하단 밴드에 접근할 때)
- PnL이 15%를 초과할 때 (수익 확정)
- 업데이트 임계값: 현재 시장가 (mark price)로부터 0.5% (SL 잦은 변경 방지)
자동 펀딩 로테이션 (Auto Funding Rotation)
음수 펀딩비 (Negative funding rates)는 마진을 갉아먹습니다. 로테이션 모듈은 매 헤비 사이클마다 다음을 확인합니다:
- 오픈된 포지션 중 펀딩비가 -0.01% 미만인 것이 있다면: 플래그(flag) 설정
- 더 나은 대안이 존재한다면 (양수 펀딩비 + BB 시그널): 현재 포지션 종료 후 새 포지션 오픈
- 순서가 중요합니다: 먼저 오픈하고, 그 다음 종료합니다 — 로테이션 중에 시장에서 이탈하는 일이 절대 없어야 합니다.
운영 환경에서 살아남은 텔레그램 봇
@Gridbolbot (이전 명칭 @GridSignalBot)은 시장을 스캔하고, 알림을 보내며, 사용자가 인라인으로 거래를 실행할 수 있게 해주는 2,153라인 규모의 텔레그램 봇입니다. 운영 5개월 차에, 이 봇은 침묵했습니다.
수정 후: 45/45 스모크 테스트 (smoke tests) 통과, 봇이 즉각적으로 응답합니다. 전체 이야기는 별도의 기사에서 확인하실 수 있습니다.
테스트: 45개의 테스트, 외부 서비스 제로
모든 수정 사항, 모든 기능, 모든 릴리스마다 스모크 테스트 (smoke test) 스위트가 실행됩니다:
@pytest.mark.asyncio
async def test_scan_button_does_not_block_event_loop():
"""스캔 핸들러가 이벤트 루프 (event loop)에 제어권을 다시 양보하는지 확인합니다."""
...
도구: pytest, pytest-asyncio, unittest.mock, SQLite :memory: 데이터베이스. 외부 서비스 없음 — 2초 미만의 순수 결정론적 (deterministic) 테스트.
45개의 테스트. 0개의 실패. CI 통과.
중요한 수치들
| 지표 (Metric) | v1 (2026년 1월) | v2 (2026년 6월) |
|---|---|---|
| 전략 (Strategies) | 9 | 12 |
| ... | ||
| 메모리 감소는 오타가 아닙니다. Python 딕셔너리 (dict)에서 SQLite로 전환하면서 RAM 사용량을 88% 절감했습니다. |
로드맵: 4단계
- 🔜 ATR 기반 리스크 사이징 (risk sizing) — 고정된 크기가 아닌 변동성 (volatility)에 따른 포지션 규모 결정
- 🔜 멀티 타임프레임 컨플루언스 (Multi-timeframe confluence) — 진입을 위해 일/주/월 단위의 일치 필요
- 🔜 Grafana 대시보드 — 실시간 PnL (손익), 드로다운 (drawdown), 포지션 히트맵 (heatmap)
- 🔜 텔레그램 미니 앱 (Telegram Mini App) — 텔레그램 내에서 바로 확인하는 대시보드
- 🔮 OKX/Binance 지원 — 동일한 전략, 더 높은 유동성
사용해 보기
git clone https://github.com/poliakarmai/bybit-ws
cd bybit-ws
cp config.example.yaml config.yaml
...
또는 systemd 서비스로 배포하기:
sudo cp bybit-ws.service /etc/systemd/system/
sudo systemctl enable --now bybit-ws
GitHub: poliakarmai/bybit-ws
Bot: @Gridbolbot
License: MIT
저자는 트레이더이자 AI 엔지니어입니다. 트레이딩 인프라, 멀티 에이전트 시스템 (multi-agent systems), 그리고 실제로 계좌를 지켜주는 지루한 리스크 관리 (risk management)에 대해 글을 씁.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기