⑫ 그래서, 뭘 버릴 것인가
계측 없이는 판정도 없다
⑪편에서 계측 얘기를 했다. check_risk()와 check_risk_b_plan()이 로그를 하나도 안 남기고 있었다는 것, 있는 줄 알았던 live_monitor.py가 서버에 아예 존재하지 않았다는 것. 부끄러운 얘기였지만 필요한 얘기였다. 이번 편을 쓰려면 정확히 그 계측이 있어야 했기 때문이다. "이 기능이 실제로 도움이 됐는가"를 판단하려면 최소한 얼마나 자주 작동했는지는 알아야 하는데, 그걸 볼 방법이 없었다.
그래서 이번 편은 완벽한 데이터 기반 결론이 아니라, 몇 달 동안 봇을 돌리면서 몸으로 느낀 것과 코드를 다시 읽으면서 확인한 것을 섞은 판단이다. 정확한 숫자보다는 방향에 대한 이야기로 읽어주면 좋겠다. 그리고 이번 정리 과정에서 각 관문과 로직에 카운트 로그를 최소한으로 붙여뒀다. 앞으로 몇 주치 데이터가 쌓이면 이번 판단이 맞았는지 다시 검증할 생각이다.
다섯 개 매수 관문 — 절반만 남긴다
처음 봇을 설계할 때 매수 조건을 다섯 개나 걸었다. RSI 조건, 이동평균 배열 조건, 거래량 조건, 국면 필터, 그리고 쿨다운까지. 당시엔 "조건을 많이 걸수록 안전하다"는 단순한 생각이었다. 그런데 지금 코드를 다시 읽어보니 그중 두 개는 사실상 같은 순간에 같이 발생하는, 서로 겹치는 조건이었다. RSI가 과매도 구간에 들어오는 시점과 이동평균이 특정 배열을 이루는 시점이 대부분 겹쳤다. 관문이 다섯 개가 아니라 실질적으로는 세 개였던 셈이다. 나머지 두 개는 "혹시 몰라서" 걸어둔, 판단에 거의 영향을 주지 않는 장식에 가까웠다.
이번에 아주 단순한 카운트 로그를 추가했다. 각 관문에서 조건이 통과됐는지, 걸렸는지만 기록하는 수준이다.
몇 주 데이터가 쌓이면 정말 겹치는 조건인지 확인하고, 겹친다면 하나로 합칠 생각이다. 지금 다섯 개짜리 관문을 계속 유지할 이유는 없어 보인다.
3단 손절 — 못 버린다, 대신 이제는 이유를 안다
트레일링 스탑, 다이나믹 스탑, 손절 B플랜까지 3단으로 걸어둔 손절 로직은 사실 이번 시리즈 전체를 관통한 주제였다. ⑨편에서 근본 원인을 찾았고, ⑩편에서 실제로 고쳤다. peak_pct가 트리거 값을 넘은 적이 없으면 트레일링 스탑 조건 자체가 None을 반환하며 조용히 무력화되던 게 문제였는데, 그 조건을 제거한 뒤 6월 데이터로 백테스트해보니 트리거 발동 횟수가 여러 배로 늘었고 손익도 눈에 띄게 개선됐다. 이건 버릴 게 아니라 오히려 더 아껴야 할 부분이라는 결론이다.
재미있는 건, 이 3단 구조 중에서도 실제로 자주 발동하는 건 하나뿐이라는 점이다. 나머지 둘은 극단적인 상황에서만 작동하는 안전판에 가깝다. 그런데 "잘 안 쓰이니까 빼자"고 판단하면 안 된다는 걸 이번에 깨달았다.
국면 분류 — 이게 제일 아팠다
④편에서 캔들을 재구성해가며 92.5%짜리 확신으로 만들었던 국면 분류 로직. 상승장, 하락장, 횡보장을 나눠서 매수 전략을 다르게 가져가겠다는 아이디어였는데, 솔직히 지금은 이게 제일 정리가 필요한 부분이라고 생각한다.
문제는 분류 자체가 틀렸다는 게 아니라, 그 분류 결과가 매수 판단에 실제로 어떻게 반영되는지가 코드 안에서 너무 흩어져 있다는 점이다. 국면 값을 참조하는 곳이 최소 세 군데인데, 각각 다른 방식으로 가중치를 준다. 누가 봐도 한 사람이 몇 달에 걸쳐 이것저것 덧붙인 흔적이다.
다음 단계는 기능을 더 붙이는 게 아니라, 국면 값이 쓰이는 세 군데를 하나의 함수로 합쳐서 최소한 "지금 이게 왜 이렇게 판단했는지"를 한 곳에서 볼 수 있게 만드는 것부터다. 통합하기 전까지는 이 로직을 신뢰하지 않기로 했다.
웹 명령 인터페이스 — 이건 그냥 버린다
봇을 웹에서 원격으로 켜고 끄고, 파라미터를 실시간으로 바꿀 수 있게 만든 인터페이스가 있다. 만들 때는 꽤 뿌듯했다. 그런데 최근 몇 달간의 사용 흐름을 떠올려보면 실제로 쓴 기억이 거의 없다. SSH로 직접 서버에 들어가서 설정 파일을 고치는 쪽이 더 빠르고 확실했기 때문이다.
결국 이건 "있으면 멋있을 것 같아서" 만든 기능이었지, 실제 운영 흐름에서 필요해서 만든 기능이 아니었다. 게다가 이 인터페이스가 지금 별도 인증 없이 특정 포트로 열려 있다는 것도 이번에 다시 확인했다. 쓰지도 않으면서 공격 표면만 넓히고 있었던 셈이다. 다음 정리 작업에서 완전히 제거할 예정이다.
| 항목 | 판정 | 근거 |
|---|---|---|
| 매수 관문 5개 | 절반으로 축소 검토 | 조건 간 중복 발견, 카운트 로그로 검증 예정 |
| 3단 손절 | 유지·강화 | ⑨~⑩편에서 실효성 확인, 발동 빈도와 가치는 별개 |
| 국면 분류 | 통합 후 재평가 | 참조 지점 3곳 분산, 설명 불가능한 복잡도 |
| 웹 명령 인터페이스 | 제거 | 실사용 없음, 인증 없는 개방 포트 |
정리하면서 든 생각
열두 편을 쓰는 동안 이 봇에는 계속 뭔가가 더해지기만 했다. 손실이 나면 조건을 하나 더 걸었고, 불안하면 안전장치를 하나 더 붙였다. 그 결과가 지금 다섯 개 매수 관문과 3단 손절과 세 군데에 흩어진 국면 로직과, 아무도 안 쓰는 웹 인터페이스다. 되돌아보니 이건 성장이 아니라 축적이었다. 뭔가 불안할 때마다 코드를 늘리는 게 가장 쉬운 해결책이었으니까.
이번 편을 쓰면서 처음으로 "무엇을 더할까"가 아니라 "무엇을 남길 자격이 있는가"를 물었다. 그리고 그 기준은 결국 하나였다 — 계측했을 때 설명이 되는가. 설명이 안 되는 기능은, 그게 아무리 안전해 보여도, 사실은 안전한 게 아니라 그냥 이해를 포기한 부분이었다.
매매일지는 여기서 일단 마무리한다. 처음엔 6월 -53%짜리 손실의 범인을 찾는 탐정 이야기로 시작했는데, 끝나고 보니 결국은 "내가 만든 걸 내가 얼마나 이해하고 있는가"에 대한 이야기였던 것 같다. 봇은 계속 돌아간다. 다음에 다시 뭔가 쓸 일이 생긴다면, 아마 이번에 정리한 국면 로직 통합 작업일 가능성이 높다.