서버를 올리고 나서 처음으로 매수 신호가 떴다. 텔레그램에 "[매수] KRW-BTC" 알림이 왔고, "[보유중] 수익률 +X%" 메시지도 왔다. 그런데 업비트 앱을 열어보니 실제로 매수된 게 없었다.
1. 상황 파악: 안 샀는데 샀다고 했다
텔레그램으로 이런 메시지들이 왔다.
가격: XXX,XXX,XXX KRW
금액: 700,000 KRW
현재가: XXX,XXX,XXX KRW
수익률: +X.XX%
그런데 업비트 앱에서 확인하니 체결 내역이 없었다. BTC 잔고도 그대로였다.
2. 원인 분석: 두 가지 문제가 겹쳤다
문제 1: 봇이 2개 동시에 실행 중이었다
로그를 확인하니 텔레그램 getUpdates API에서 409 Conflict 에러가 수백 개 찍혀 있었다.
409 Conflict는 텔레그램이 "같은 봇 토큰으로 두 곳에서 동시에 연결하려 한다"는 뜻이다. 즉, main.py 프로세스가 2개 떠 있었던 것이다.
두 봇이 동시에 같은 신호를 감지했다.
| 봇 | 결과 |
| 봇 1번 | 업비트에 매수 주문 → 성공 (잔고 소진) |
| 봇 2번 | 잔고 부족으로 실패 |
문제 2: 주문 실패 여부를 확인하지 않았다
당시 코드는 이랬다.
buy_market(symbol, invest_krw)
# 주문 실패해도 아래가 무조건 실행됨
on_buy(symbol, sig['price'])
# 성공으로 상태 저장
send_buy(...)
# 매수 알림 발송
buy_market()이 실패해서 빈 {}를 반환해도, 바로 아래 on_buy()와 send_buy()가 무조건 실행됐다.
결과적으로:
① 실제 매수는 실패
② 봇 내부 상태는 "BTC 보유 중"으로 저장
③ 텔레그램엔 매수 성공 알림 발송
④ 이후 체크마다 "보유중, 수익률 +X%" 메시지 반복 발송
3. 해결: 즉시 조치 + 코드 개선
즉시 조치: 중복 프로세스 제거
sudo systemctl stop upbit-trader
# 남은 프로세스 강제 종료
sudo pkill -f main.py
# 재시작
sudo systemctl start upbit-trader
코드 개선: 주문 응답 확인 후 알림
매수/매도 모두 업비트 응답을 먼저 확인하고 알림을 보내도록 수정했다.
order = buy_market(symbol, invest_krw)
# 성공했을 때만
if order:
on_buy(symbol, sig['price'])
send_buy(...)
# 실패하면 실패 알림
else:
send(f'[매수 실패] {symbol}\n업비트 주문이 실패했습니다.')
4. 개선 결과
이제 주문 실패 시 텔레그램으로 아래 메시지가 온다.
업비트 주문이 실패했습니다. 로그를 확인하세요.
성공했을 때만 기존 알림이 발송된다. 실패를 성공으로 착각하는 일은 없다.
5. 배운 것
첫째, 외부 API 호출은 항상 실패할 수 있다. 응답 결과를 확인하지 않고 다음 로직을 실행하면 안 된다.
둘째, 알림이 온다고 실제 거래가 됐다는 보장이 없다. 초기엔 반드시 업비트 앱에서 직접 체결 내역을 교차 확인해야 한다.
셋째, 봇이 살아있다는 것과 봇이 제대로 작동한다는 것은 다른 말이다. 서버가 돌아가고 알림이 와도 실제 주문이 정상 체결됐는지는 별도로 확인해야 한다.
# 자동매매 트러블슈팅: 409 Conflict 에러와 주문 상태 동기화 오류 개선
# 업비트 자동매매 7편: 첫 매수 신호와 뼈아픈 실패 (원인 분석 및 코드 개선)
'업비트 트레이더' 카테고리의 다른 글
| 6편 (특별판): 단타 전략 탐색의 모든 것 — 10번의 시뮬레이션과 최종 결론 (0) | 2026.03.23 |
|---|---|
| 5편: 트레일링 스탑 최적화 — "진입 즉시 활성화"가 정말 맞을까? (1) | 2026.03.22 |
| 4편: 실전 배포 — 24시간 무중단 서버 구축과 실시간 모니터링 시스템 (1) | 2026.03.22 |
| 3편: 최종 전략 확정 — 포트폴리오 구성과 백테스트 결론 (0) | 2026.03.21 |
| 2편: 데이터 분석과 전략 수립 — 3년 백테스트 결과 (0) | 2026.03.21 |