실전 운용을 시작하고 나서 한 가지 찜찜한 부분이 있었다. 트레일링 스탑이 진입 직후부터 작동한다는 점이었다.
1. 문제 제기: 조기 청산의 함정
외부에서 이런 의견을 받았다.
"BTC 가격이 골든크로스 직후 +1% 올랐다가 -3% 빠지는 경우,
손절가(-4%)에는 도달하지 않았지만 트레일링 스탑(-3%)이 먼저 발동해서
조기 청산될 수 있다."
생각해보니 맞는 말이었다. 트레일링 스탑은 최고점에서 일정 %만큼 빠지면 청산하는 로직이다. 진입 직후에도 활성화되면, 작은 등락에도 청산되는 경우가 생긴다.
현재 코드는 이렇게 되어 있었다.
# 기존 코드 - 진입 즉시 활성화
if (peak_price - current_price) / peak_price >= trail:
return 'TRAIL'
해결책은 활성화 임계값을 두는 것이다. 최고점 수익이 일정 % 이상 달성됐을 때만 트레일링 스탑을 켠다.
# 개선 코드 - trail_activate 조건 추가
max_pnl = (peak - entry_price) / entry_price
if max_pnl >= trail_activate and (peak - current_price) / peak >= trail:
return 'TRAIL'
2. 백테스트 설계: 3가지 시나리오
3가지 시나리오를 비교했다.
| 시나리오 | 트레일링 스탑 활성화 조건 |
| 현행 (즉시) | 진입 즉시 활성화 |
| 수정A (+2%) | 최고점 수익이 +2% 이상일 때만 활성화 |
| 수정B (+3%) | 최고점 수익이 +3% 이상일 때만 활성화 |
- 기간: 2023-01-01 ~ 2026-03-22 (약 3년)
- 전략: MA(20/120) 골든크로스 + RSI(14) < 70
- 나머지 조건: 손절 BTC -4% / SOL -5%, 익절 BTC +8% / SOL +10%, 트레일 -3%
3. 백테스트 결과: BTC vs SOL
KRW-BTC (1시간봉)
| 시나리오 | 거래 | 승률 | 수익률 | MDD | 평균손익 | 익절 | 손절 | 트레일 |
| 현행 (즉시) | 87 | 48.3% | +144% | -19.1% | +1.12% | 18 | 2 | 67 |
| 수정A (+2%) ★ | 72 | 55.6% | +154% | -19.9% | +1.42% | 19 | 20 | 33 |
| 수정B (+3%) | 70 | 60.0% | +122% | -19.9% | +1.27% | 19 | 26 | 25 |
수정A가 수익률 +10%p 우세했다. 핵심 변화는 트레일 청산이 67회 → 33회로 줄고, 손절이 2회 → 20회로 늘어난 것이다.
처음엔 이게 나쁜 것 같았다. 손절이 늘었으니까. 그런데 생각해보면 맞다. 기존엔 트레일로 청산되던 것들이 이제는 제대로 손절 또는 익절로 처리된다. "불필요한 조기 청산"이 줄어든 것이다.
KRW-SOL (30분봉)
| 시나리오 | 거래 | 승률 | 수익률 | MDD | 평균손익 | 익절 | 손절 | 트레일 |
| 현행 (즉시) ★ | 198 | 42.4% | +175% | -25.2% | +0.61% | 25 | 3 | 170 |
| 수정A (+2%) | 171 | 52.0% | +130% | -36.2% | +0.64% | 26 | 52 | 93 |
| 수정B (+3%) | 163 | 58.3% | +182% | -31.8% | +0.81% | 30 | 60 | 73 |
SOL은 결과가 반대였다. 수정A에서 수익률이 크게 떨어지고(-45%p) MDD도 악화됐다. 현행이 가장 좋다.
4. 왜 BTC와 SOL이 다른가: 변동성의 차이
이 차이가 흥미로웠다.
BTC는 1시간봉 기준으로 큰 추세를 타는 경향이 있다. 진입 직후 소폭 등락이 잦다. 이 구간에서 트레일이 즉시 활성화되면 노이즈에 청산당하는 경우가 많다. +2% 활성화 임계값이 이 노이즈를 걸러주는 역할을 한다.
SOL은 변동성이 더 크다. 추세가 시작되면 빠르게 움직이는 경향이 있다. 즉시 활성화해야 급등 후 급락 시 수익을 지킬 수 있다. 임계값을 두면 오히려 수익 실현 타이밍을 놓친다.
5. 최종 결정: 코인별 맞춤 설정
| 코인 | 트레일링 스탑 활성화 조건 |
| KRW-BTC | 최고점 수익 +2% 이상일 때만 활성화 |
| KRW-SOL | 진입 즉시 활성화 (현행 유지) |
src/risk.py의 PARAMS에 trail_activate 파라미터를 추가했다.
PARAMS = {
'KRW-BTC': {
'tp': 0.08, 'sl': 0.04, 'trail': 0.03,
'cooldown_h': 48,
'trail_activate': 0.02, # +2% 이상일 때만 트레일 활성화
},
'KRW-SOL': {
'tp': 0.10, 'sl': 0.05, 'trail': 0.03,
'cooldown_h': 60,
'trail_activate': 0.00, # 즉시 활성화
},
}
6. 미채택 의견: 눌림목 진입 조건
이 분석을 하면서 다른 의견도 받았다.
"BTC 가격이 전고점 부근에서 횡보 중. 골든크로스가 이미 진행 중이므로
MA(20) 근처까지 가격이 내려왔을 때 눌림목 진입 조건을 추가하면 유리할 수 있다."
검토해봤지만 맞지 않는 의견이었다. 우리 전략의 매수 조건은 골든크로스가 발생하는 순간에만 진입한다.
golden = prev['ma20'] <= prev['ma120'] and curr['ma20'] > curr['ma120']
골든크로스가 "진행 중"이면 이미 그 시점에 진입했거나, 놓쳤으면 다음 크로스까지 대기하는 구조다. 고점에서 계속 진입 가능한 전략이 아니다. 눌림목 조건 추가는 불필요하다.
7. 서버 반영: 배포 완료
변경 사항을 Oracle Cloud 서버에 SCP로 업로드하고 서비스를 재시작했다.
scp src/risk.py ubuntu@{서버IP}:~/upbit-trader/src/
ssh ubuntu@{서버IP} "sudo systemctl restart upbit-trader"
이제 BTC는 진입 후 +2% 이상 올랐을 때부터 트레일링 스탑이 작동한다. 조기 청산 가능성이 줄었다.
8. 다음 편 예고
전략 최적화도 했고, 서버도 잘 돌아가고 있다.
그런데 한 가지 욕심이 생겼다.
"BTC/SOL 골든크로스는 자주 발생하지 않는다.
신호가 없는 구간이 꽤 길다. 이 쉬는 시간에 다른 코인으로 단타를 칠 수 있지 않을까?"
이 아이디어가 생각보다 깊은 탐구로 이어졌다. 10회 이상의 백테스트와 숱한 실패를 거친 단타 전략 탐색 이야기를 다음 편에서 다룬다.
'업비트 트레이더' 카테고리의 다른 글
| 7편: "매수 성공"이라는데 내 잔고는 왜 0원일까? (유령 매수 사건과 해결책) (0) | 2026.03.24 |
|---|---|
| 6편 (특별판): 단타 전략 탐색의 모든 것 — 10번의 시뮬레이션과 최종 결론 (0) | 2026.03.23 |
| 4편: 실전 배포 — 24시간 무중단 서버 구축과 실시간 모니터링 시스템 (1) | 2026.03.22 |
| 3편: 최종 전략 확정 — 포트폴리오 구성과 백테스트 결론 (0) | 2026.03.21 |
| 2편: 데이터 분석과 전략 수립 — 3년 백테스트 결과 (0) | 2026.03.21 |