01. 코인 선물 자동매매를 시작한 이유

들어가며

솔직히 말하면, 나는 돈을 벌고 싶었다. 그것도 자는 동안에.

개발자로 일하면서 늘 생각했다. 내가 코드를 짤 수 있고, 데이터를 다룰 수 있고, 머신러닝도 어느 정도 이해하는데 — 왜 이걸 트레이딩에 적용하지 않는 거지? 수많은 퀀트 펀드가 알고리즘으로 돈을 버는데, 개인이 못할 이유가 뭐란 말인가.

물론 개인 트레이더의 90%가 돈을 잃는다는 통계도 알고 있었다. 하지만 그건 감정에 휘둘리는 사람들의 이야기라고 생각했다. 기계는 공포와 탐욕을 모른다. 기계는 새벽 3시에 비트코인이 10% 빠져도 패닉셀을 하지 않는다.

그래서 시작했다. 바이낸스 코인 선물 자동매매 시스템 개발을.

왜 코인 선물인가

주식 시장이 아닌 코인 선물을 택한 이유는 명확했다.

  • 24/7 시장: 주식은 장 마감이 있지만, 코인은 365일 쉬지 않는다. 자동매매 시스템에게 이것은 축복이다.

  • 양방향 거래: 선물은 Long(매수)과 Short(매도) 모두 가능하다. 하락장에서도 수익을 낼 수 있다.

  • 높은 변동성: 하루에 5~10% 움직이는 것이 일상이다. 변동성은 곧 기회다.

  • API 접근성: 바이낸스는 깔끔한 REST/WebSocket API를 제공한다. 개인 개발자에게 진입 장벽이 낮다.

"변동성이 높다는 건 위험하다는 뜻 아닌가?"

맞다. 하지만 위험을 통제할 수 있다면, 변동성은 무기가 된다. 그리고 그 통제를 소프트웨어에 맡기는 것이 이 프로젝트의 핵심이었다.

기술 스택을 고르다

어떤 도구로 만들 것인가. 여기서 꽤 고민했다.

Python — 선택의 여지가 없었다

데이터 사이언스와 머신러닝의 사실상 표준. NumPy, Pandas, scikit-learn, TensorFlow 모두 Python 생태계다. 트레이딩 봇을 Rust로 만드는 사람도 있지만, 나에게 중요한 건 실행 속도보다 실험 속도였다.

LSTM — 시계열의 정석

가격 데이터는 시계열이다. RNN 계열 중에서도 장기 의존성을 학습할 수 있는 LSTM(Long Short-Term Memory)을 선택했다. "30분 전에 거래량이 급증했으면 지금 가격이 움직일 가능성이 높다" 같은 패턴을 잡아내는 데 적합하다고 판단했다.

LSTM의 수학적 구조

LSTM을 선택한 이유를 이해하려면, 먼저 vanilla RNN의 한계를 알아야 한다. 기본 RNN은 시퀀스가 길어질수록 기울기가 기하급수적으로 줄어드는 기울기 소실(Vanishing Gradient) 문제를 겪는다. LSTM은 이 문제를 게이트 메커니즘으로 해결한다.

LSTM 셀 하나가 매 시간 스텝 $t$에서 수행하는 연산은 다음과 같다.

Forget Gate — 과거 정보를 얼마나 잊을지 결정한다:

$$f_t = \sigma(W_f \cdot [h_{t-1}, x_t] + b_f)$$

시그모이드 $\sigma$의 출력이 0에 가까우면 "잊어라", 1에 가까우면 "기억하라"는 뜻이다. 비유하자면, 시험공부를 하면서 "이건 출제 범위에서 빠졌으니까 잊자, 이건 기출이니까 기억하자"라고 판단하는 뇌의 필터와 같다. 금융 시계열에서는 "3시간 전의 거래량 급증이 지금도 유효한 정보인가?"를 판단하는 것이다.

Input Gate — 새로운 정보를 얼마나 받아들일지 결정한다:

$$i_t = \sigma(W_i \cdot [h_{t-1}, x_t] + b_i)$$ $$\tilde{C}t = \tanh(W_C \cdot [h{t-1}, x_t] + b_C)$$

Input gate $i_t$는 새 정보의 중요도를, 후보 셀 상태 $\tilde{C}_t$는 새 정보의 내용을 결정한다. 이 둘이 결합되어 셀 상태를 갱신한다:

$$C_t = f_t \odot C_{t-1} + i_t \odot \tilde{C}_t$$

여기서 $\odot$는 원소별 곱(Hadamard product)이다. 이 수식이 LSTM의 핵심이다. 과거 기억 $C_{t-1}$을 forget gate로 선택적으로 보존하면서, 새 정보 $\tilde{C}_t$를 input gate로 선택적으로 추가한다.

비유하면, 냉장고 정리와 비슷하다. forget gate는 "유통기한 지난 건 버리자"이고, input gate는 "오늘 산 재료 중에 뭘 넣을지"를 결정한다. 결과적으로 냉장고(셀 상태 $C_t$)에는 항상 쓸모 있는 재료만 남는다. 덕분에 기울기가 $C_t$를 따라 "고속도로"처럼 흘러서, 먼 과거의 정보도 손실 없이 전달될 수 있다.

Output Gate — 셀 상태에서 무엇을 출력할지 결정한다:

$$o_t = \sigma(W_o \cdot [h_{t-1}, x_t] + b_o)$$ $$h_t = o_t \odot \tanh(C_t)$$

내 시스템에서 LSTM은 90개 봉(7.5시간)의 시퀀스를 보며, 이 게이트들이 "어느 시점의 가격 변동이 현재 예측에 중요한가"를 스스로 학습한다. 마치 숙련된 트레이더가 차트를 훑으면서 "이 캔들은 중요하고, 이건 노이즈야"라고 직감적으로 필터링하는 것을 수식으로 구현한 셈이다. 이것이 LSTM을 시계열 예측의 정석으로 만드는 이유다.

XGBoost + LightGBM — 테이블 데이터의 왕

기술적 지표(RSI, MACD, 볼린저밴드 등)를 피처로 만들면 결국 테이블 형태의 데이터가 된다. 이 영역에서는 gradient boosting 계열이 압도적이다. XGBoost와 LightGBM, 둘 다 쓰기로 했다. 서로 다른 방식으로 학습하니 앙상블하면 개별 모델보다 나을 거라는 기대가 있었다.

XGBoost의 목적 함수

XGBoost가 단순한 gradient boosting과 다른 점은 정규화된 목적 함수에 있다. $t$번째 트리를 추가할 때 최적화하는 함수는:

$$\mathcal{L}^{(t)} = \sum_{i=1}^{n} l(y_i, \hat{y}_i^{(t-1)} + f_t(x_i)) + \Omega(f_t)$$

여기서 $l$은 손실 함수(내 시스템에서는 binary logistic loss), $f_t$는 새로 추가되는 $t$번째 트리, 그리고 $\Omega$는 정규화 항이다:

$$\Omega(f) = \gamma T + \frac{1}{2}\lambda \sum_{j=1}^{T} w_j^2$$

$T$는 리프 노드 수, $w_j$는 각 리프의 가중치다. $\gamma$는 트리를 분할할 때마다 부과되는 페널티로, 불필요한 분기를 억제한다. $\lambda$는 리프 가중치의 L2 정규화로, 특정 리프가 과도한 예측값을 갖지 않게 한다.

쉽게 말해, 마치 답안지를 쓸 때 "한 문제에 너무 긴 답을 쓰면 감점"($\lambda$)이고 "문항 수를 늘릴수록 가산비용 부과"($\gamma$)인 시험 규칙과 같다. 핵심만 간결하게 쓰도록 강제하는 것이다.

이 정규화가 왜 중요한가? 금융 데이터는 노이즈가 극심하다. 정규화 없이 학습하면 모델은 "노이즈까지 외우는" 과적합에 빠진다. 실제로 내 코드에서 reg_alpha=0.3, reg_lambda=1.5, gamma=0.2로 설정한 것은, XGBoost의 수학적 구조를 활용해 과적합을 적극적으로 억제하겠다는 의도였다.

왜 이 조합인가 — 앙상블의 이론적 근거

세 모델을 섞어 쓰겠다는 결정은 직관이 아니라 이론적 근거가 있었다. 통계학에서 예측 오차는 Bias-Variance Decomposition으로 분해된다:

$$\text{MSE} = \text{Bias}^2 + \text{Variance} + \text{Irreducible Error}$$

풀어쓰면:

$$E[(y - \hat{f}(x))^2] = (E[\hat{f}(x)] - f(x))^2 + E[(\hat{f}(x) - E[\hat{f}(x)])^2] + \sigma^2_\epsilon$$

  • Bias (편향): 모델이 현실을 얼마나 단순화했는가. 너무 단순한 모델은 bias가 높다.

  • Variance (분산): 학습 데이터가 바뀌면 예측이 얼마나 출렁이는가. 너무 복잡한 모델은 variance가 높다.

  • Irreducible Error ($\sigma^2_\epsilon$): 데이터에 내재된 본질적 불확실성. 어떤 모델도 줄일 수 없다.

양궁으로 비유하면, Bias는 "조준점이 과녁 중심에서 얼마나 벗어나 있는가"이고, Variance는 "화살이 얼마나 흩어지는가"이다. 초보자는 대충 중심을 향해 쏘지만 흩어지고(low bias, high variance), 나쁜 습관이 있는 선수는 일정하게 쏘지만 한쪽으로 치우친다(high bias, low variance). 이상적인 건 중심에 빽빽하게 모이는 것이지만, 하나의 모델로는 둘 다 잡기 어렵다.

앙상블은 이 트레이드오프를 깨뜨린다. $M$개의 독립적인 모델을 평균 내면:

$$\text{Var}\left(\frac{1}{M}\sum_{i=1}^{M} f_i(x)\right) = \frac{1}{M}\sigma^2 + \frac{M-1}{M}\rho\sigma^2$$

여기서 $\rho$는 모델 간 상관계수, $\sigma^2$는 개별 모델의 분산이다. 모델 간 상관이 낮을수록($\rho \to 0$), 분산이 $1/M$으로 줄어든다.

이건 투자 포트폴리오의 분산 투자와 본질적으로 같은 원리다. 삼성전자와 SK하이닉스를 동시에 사면 반도체 업황에 같이 출렁이지만($\rho$가 높음), 삼성전자와 농심을 동시에 사면 서로 다른 이유로 움직이니까($\rho$가 낮음) 전체 변동이 줄어든다.

그래서 나는 의도적으로 다른 성격의 모델을 조합했다:

모델성격강점LSTM시퀀스 패턴 학습시간적 의존성 포착XGBoost피처 간 비선형 관계테이블 데이터 정밀도LightGBM히스토그램 기반 분할빠른 학습, 범주형 처리

서로 다른 귀납 편향(inductive bias)을 가진 모델들이므로, 오류의 상관관계가 낮다. 즉 $\rho$가 작다. 이것이 앙상블의 수학적 힘이다.

바이낸스 API — 실전 연결고리

모델이 아무리 좋아도 실제로 주문을 넣지 못하면 의미가 없다. 바이낸스 선물 API를 통해 실시간 데이터 수집부터 주문 실행까지 전체 파이프라인을 구축하기로 했다.

전체 시스템 아키텍처

최종적으로 설계한 시스템은 4단계 파이프라인이다.

0. 데이터 수집 & 피처 엔지니어링

  1. 바이낸스 5분봉 OHLCV 데이터

  2. 기술적 지표 생성 (RSI, MACD, BB, ATR 등)

  3. 로그수익률, 변동성, 모멘텀 피처

1. 데이터 전처리 & 레이블링

  1. 정규화 (RobustScaler, StandardScaler)

  2. 시퀀스 생성 (LSTM용 90봉 시퀀스)

  3. 미래 수익률 기반 레이블 (±1.5%)

2. 모델 학습

  1. LSTM (시계열 패턴)

  2. XGBoost (비선형 관계)

  3. LightGBM (고속 부스팅)

  4. 가중 앙상블 (0.35 / 0.40 / 0.25)

3. 백테스트 & 실전 매매

  1. 백테스트로 전략 검증

  2. 리스크 관리 (손절매, 포지션 사이징)

  3. 바이낸스 선물 자동 주문

처음에는 단순했다. 과거 가격 데이터를 넣고, "1시간 후에 오를까 내릴까?"를 예측하는 이진 분류 모델. 50%만 넘으면 장기적으로 수익이 날 거라고 생각했다.

순진한 생각이었다는 걸 나중에야 알게 되지만, 모든 여정은 이렇게 시작하는 것 아닐까.

개발 연구 계획

나는 단계적으로 접근하기로 했다.

  1. 1단계: 랜덤포레스트 & XGBoost로 단기 가격 예측의 가능성을 탐색한다

  2. 2단계: LSTM으로 시계열 패턴을 학습하고, 앙상블로 성능을 끌어올린다

  3. 3단계: 강화학습(DQN, PPO)으로 매매 전략 자체를 AI가 학습하게 한다

최종 목표는 거창했다. 정확도 70% 이상, 샤프비율 40 이상, 최대 낙폭 30% 이하. 지금 돌이켜보면 무모한 목표였지만, 높은 목표가 없었다면 이 기나긴 여정을 시작하지도 않았을 것이다.

시리즈를 시작하며

이 시리즈에서는 v1.0부터 v2.7까지, 약 4개월간의 개발 과정을 솔직하게 기록한다.

모델이 처음으로 55%를 넘었을 때의 흥분, 백테스트에서 32,000% 수익률이 나왔을 때의 불신, 32,687%라는 숫자가 환상일 수 있다는 두려움, 그리고 결국 "모델을 바꾸는 것이 아니라 질문을 바꿔야 한다"는 깨달음에 도달하기까지.

성공도 있었고, 실패는 더 많았다. 하지만 모든 실패에는 배움이 있었다.

다음 글에서는 v1.0의 첫 모델이 탄생하고, 55.58%라는 숫자 앞에서 내가 무엇을 느꼈는지 이야기하겠다.