Fri 08-21 · 오늘 3건 · 통산 1,499호
falette
스크랩 쪽지 로그인
업비트 자동매매 봇 만들기
 

결함의 정체는 지난 편에서 다 드러났다. 남은 건 메스를 대는 일이었다. 이번 편은 그 봉합 기록이다 — 무엇을 잘라냈고, 그 결과가 숫자로 어떻게 나왔는지.

잘라낸 부분

수술은 예상보다 간단했다. check_risk()check_risk_b_plan() 두 함수 맨 아래, 트레일링 신호를 return None으로 죽이던 조건문 하나를 통째로 들어냈다.

- if peak_price > 0:
-     peak_drop = (current_price - peak_price) / peak_price * 100
-     if peak_drop <= -trailing_stop:
-         if dynamic.get("use"):
-             peak_pct = (peak_price - buy_price) / buy_price * 100
-             t1 = float(dynamic.get("trigger1", 1.0))
-             if peak_pct < t1:
-                 return None
-         return f"트레일링({regime}) {peak_drop:.2f}%"
-     return None
+ if peak_price > 0:
+     peak_drop = (current_price - peak_price) / peak_price * 100
+     if peak_drop <= -trailing_stop:
+         return f"트레일링({regime}) {peak_drop:.2f}%"
+     return None

이제 트레일링은 오직 하나만 본다. 고점 대비 얼마나 떨어졌는가. 그 위에 얹혀 있던 "수익권 근처에 한 번이라도 가봤는가"라는 별개의 조건은 사라졌다. 수정 전후로 두 함수가 정확히 1회씩 매치되는 걸 확인하고 나서 반영했고, 원본은 coin_bot.py_20260821로 남겨뒀다. 검증용 check_risk_b_plan() 쪽도 B_PLAN_TRIGGER1 상수만 다를 뿐 완전히 같은 구조였던 만큼, 두 곳을 동시에 고쳐서 실거래 로직과 백테스트 로직이 다시 같은 기준으로 손절을 판단하도록 맞췄다.

검증 설계

백테스트는 6월 한 달(6/1~6/30) 전체, 감시 종목 6개, 15분봉 기준으로 돌렸다. 같은 데이터에 수정 전 로직과 수정 후 로직을 각각 그대로 시뮬레이션해서, 조건 하나 말고는 아무것도 안 건드린 상태로 비교했다. 매매 신호를 만드는 지표 설정, 매수 조건, 자금 배분 로직은 전부 그대로 두고 딱 이 손절 판단 한 줄만 바꿨으니, 여기서 나오는 차이는 온전히 그 한 줄에서 비롯된 것이라고 볼 수 있다.

구분수정 전수정 후차이
총 거래 수241258+17
승률60.6%46.9%-13.7%p
총 손익률-9.21%-1.74%+7.47%p
트레일링 발동8건121건+113건
최대낙폭(MDD)32.73%32.40%-0.33%p
최종잔고(100만 시작)889,032965,646+76,614

가장 먼저 눈에 띄는 건 트레일링 발동 횟수다. 8건에서 121건 — 15배가 늘었다. 그동안 조용히 죽어있던 신호가 이만큼이나 있었다는 뜻이다. 그런데 승률은 오히려 떨어졌다. 60.6%에서 46.9%로.

언뜻 나빠 보이지만 정상적인 트레이드오프다. 트레일링이 더 자주 발동한다는 건, 그만큼 더 얕은 손실에서 더 자주 끊는다는 뜻이다. 크게 물려서 나중에 겨우 본전 근처로 빠져나오는 거래(이건 승리로 잡힌다) 대신, 작게 자주 손절하는 거래가 늘어난다. 승률은 낮아져도 손실의 크기 자체가 줄어드니 총손익은 개선된다 — 이번 결과가 정확히 그 패턴이다.

총 손익률은 -9.21%에서 -1.74%로, 7.47%p 개선됐다. 여전히 마이너스지만 6월이라는 한 달 데이터만 놓고 보면 손실 폭이 5분의 1 수준으로 줄었다. 최대낙폭도 소폭 개선됐다.

다만 이 수치를 곧이곧대로 최종 성적표로 받아들이면 안 된다. 예전에 확인한 적 있는 "시뮬레이션 합산치와 실제 DB 기록 합산치가 어긋나는" 괴리가 이 백테스트 방식에도 구조적으로 남아있을 수 있다. 여기 나온 숫자는 정확한 실거래 재현이 아니라, 같은 조건에서 수정 전후를 상대 비교한 시뮬레이션 결과로 읽어야 한다.

지금 상태

패치는 라이브 코드에 반영됐고, 이 글을 쓰는 시점에 봇도 새 코드로 재시작하는 중이다. 백테스트 숫자가 아무리 좋아도, 결국 확인해야 할 건 실거래에서 진짜로 어떻게 움직이느냐다. 시뮬레이션은 슬리피지나 체결 지연, 그리고 그때그때 바뀌는 호가창의 미묘한 흐름까지는 담아내지 못한다. 여기서 나온 숫자는 "이 방향이 맞는지"를 가늠하는 나침반이지, 실거래 결과를 미리 보여주는 예언은 아니다.

돌이켜보면 이번 부검 3부작에서 가장 아팠던 건 코드 자체보다, 그 코드가 "일부러 그렇게 짜인" 로직이었다는 점이다. 버그라고 하면 실수 하나 고치고 끝날 문제인데, 이건 설계 의도와 실제 시장 상황이 어긋난 경우였다. 문턱을 걸어둔 이유는 분명 있었을 텐데, 그 문턱이 하필 가장 취약한 장에서 방어선을 걷어내는 쪽으로 작동했다. 코드 리뷰만으로는 절대 못 잡는 종류의 결함이다 — 실제 데이터로 국면별 손익을 쪼개보지 않았다면 여전히 몰랐을 거다.

8편에서 용의자를 추리고, 9편에서 범인을 확정하고, 10편에서 메스를 대고 봉합했다 — Part 3(부검)이 여기서 끝난다. 다음은 Part 4, 이 수정이 실제 시장에서 며칠, 몇 주를 버티는지 지켜보고 시리즈를 마무리하는 순서다.

본 게시글은 자동매매 봇의 개발·운영 기록이며, 투자 조언이 아닙니다. 백테스트 결과는 과거 데이터에 대한 시뮬레이션이며 향후 수익을 보장하지 않습니다. 암호화폐 자동매매는 원금 손실 위험이 있으며, 모든 투자 판단과 책임은 투자자 본인에게 있습니다.

지난 편: 신호는 죽지 않았다, 죽인 건 조건문이었다 — ⑨ 6월 -53% ② (제목)
다음 편: 실거래로 이 수정이 며칠을 버티는지 — Part 4로

댓글 0
첫 번째 댓글을 남겨보세요
댓글 작성
LOGIN