전략은 점점 안정돼 가고 있었다. 손실 구조도 더 잘 보이기 시작했고, 백테스트와 라이브의 실행 흐름도 어느 정도 맞춰졌다. 그런데 이상하게 마지막까지 마음을 불편하게 만드는 건 성과표가 아니었다. 봇이 정말 지금도 살아 있는지, 그리고 문제가 생겼을 때 내가 제때 알 수 있는지에 대한 불안이었다.
자동매매를 오래 붙잡고 있으면 어느 순간 이런 생각이 든다. 수익률보다 더 무서운 건 침묵일 수 있다고. 손실은 차라리 눈에 보인다. 하지만 시스템이 조용히 멈추거나, 알림이 오지 않거나, 1분 트레일링이 죽었는데도 아무도 모르면 그건 전혀 다른 종류의 리스크가 된다. 이번 기록은 그래서 전략 개선이 아니라, 운영의 불안을 줄이기 위한 감시 체계를 붙인 과정이다.
문제는 ‘알림이 있다’가 아니라 ‘죽었을 때도 알 수 있는가’였다
처음엔 텔레그램 알림이 있으니 괜찮다고 생각하기 쉽다. 진입/청산이 오고, 30분 상태 알림도 오고, 에러도 어느 정도는 보인다. 그런데 이 구조에는 치명적인 맹점이 있다. 프로세스가 죽으면 그 순간 텔레그램을 보낼 주체도 함께 사라진다.
즉 “평소 알림이 잘 온다”는 것과 “장애가 났을 때도 알 수 있다”는 건 완전히 다른 문제였다. 이 차이를 깨닫고 나서부터 기준이 바뀌었다. 필요한 건 메시지 개수를 늘리는 게 아니라, 시스템이 침묵할 때조차 그 침묵을 감지하는 구조였다.
🟠 기존 운영의 불안
Visible
진입/청산 알림은 보인다
정상적으로 돌아갈 때는 텔레그램만 봐도 꽤 많은 걸 알 수 있었다.
Invisible
죽었을 때는 오히려 조용하다
프로세스, 네트워크, 폴링, 1분 트레일링 중 무엇이 멈췄는지 바로 알기 어려웠다.
목표: 침묵 자체를 감지하는 운영 체계
그래서 텔레그램을 ‘메시지 채널’이 아니라 ‘운영 인터페이스’로 보기 시작했다
이 지점에서 알림을 대하는 방식이 조금 달라졌다. 텔레그램은 단순한 편의 기능이 아니었다. 라이브 운영에서 가장 먼저 상태를 확인하는 창이고, 이상 징후를 받아보는 창이고, 필요하면 조작 명령까지 보내는 인터페이스였다. 그렇다면 당연히 “가끔 오류가 나도 괜찮은 부가 기능”으로 두면 안 됐다.
그래서 먼저 손본 건 텔레그램 쪽 자체의 회복력이었다. 폴링 시작 실패가 곧바로 엔진 전체 실패로 이어지지 않게 만들고, 연결이 끊겨도 다시 살아날 수 있도록 보강했다. `/status`도 좀 더 운영적으로 읽히도록 정리했다. 이제는 단순 요약이 아니라, 1분 트레일링이 지금 정상인지 아닌지까지 같은 화면에서 볼 수 있게 됐다.
여기서 핵심은 알림의 수를 늘리는 게 아니었다.
운영자가 “지금 안심해도 되는가”를 더 빨리 판단할 수 있게 만드는 일이었다.
그다음은 heartbeat와 watchdog이었다
하지만 텔레그램만 튼튼하게 만들어서는 충분하지 않았다. 다시 원점으로 돌아가면, 프로세스가 완전히 죽는 순간엔 텔레그램도 같이 조용해질 수 있기 때문이다. 그래서 별도 감시 체계가 필요했다. 엔진은 주기적으로 heartbeat 파일을 남기고, watchdog은 그 시간을 바깥에서 확인한다. 정해진 시간 이상 heartbeat가 갱신되지 않으면 그제서야 경고를 보낸다.
이 구조가 좋았던 이유는 명확하다. 이제 알림이 안 오는 것도 하나의 상태가 된다. 예전엔 “조용하다 = 별일 없나 보다”로 넘어갈 수 있었다면, 이제는 “너무 조용한데?” 자체가 운영 이벤트가 된다. 이 차이는 생각보다 크다. 운영에서 진짜 무서운 건 나쁜 소식이 아니라, 소식이 아예 없는 상태이기 때문이다.
🟢 운영 감시 체계는 이렇게 나뉘었다
Telegram
진입/청산/에러/상태 확인을 담당하는 운영 인터페이스
Heartbeat
엔진이 “지금도 살아 있다”는 흔적을 주기적으로 남기는 내부 신호
Watchdog
heartbeat가 멈췄는지 바깥에서 감시하고, stale 상태를 별도 경고로 바꿔주는 외부 감시자
OnFailure
systemd 실패 상태 자체를 별도의 Telegram 경고로 연결하는 마지막 안전장치
1분 트레일링은 따로 감시하는 편이 맞았다
특히 신경 쓰였던 건 1분 트레일링이었다. 이건 라이브에서 가장 섬세한 루프 중 하나라서, 조용히 죽어도 눈으로 알아차리기 어렵다. 그래서 `/status`에 전용 상태를 넣고, fetch 실패나 stale 상태가 연속될 때는 아예 경고가 오도록 만들었다.
이건 작은 디테일처럼 보여도 운영에선 꽤 중요하다. 왜냐하면 트레일링은 돌아가고 있을 때는 존재감이 없지만, 멈췄을 때의 손실 체감은 굉장히 크기 때문이다. 그래서 이번엔 “문제가 생기면 알려준다”보다, 문제가 생길 것 같은 징후를 먼저 드러낸다 쪽으로 조금 더 가까워졌다.
🟢 이번 작업으로 달라진 운영 감각
알림 역할
사후 통보 →
상태 인터페이스
운영 기준
오류 감지 →
침묵 감지
감정 변화
불안 ↓
안도감 ↑
이제는 메시지가 많이 오느냐보다, 이상할 때 반드시 드러나느냐가 더 중요해졌다. 운영의 기준이 조금 더 성숙한 쪽으로 옮겨간 셈이다.
Take
좋은 운영은
좋은 알림보다
좋은 침묵 감지에서 시작된다
이제 시스템은 “무슨 일이 일어났는지”뿐 아니라 “아무 일도 안 일어나는 게 이상한 상태인지”도 말해주기 시작했다.
결국 이 시점부터는 전략이 아니라 운영의 신뢰를 다루게 됐다
이번 작업이 재밌었던 건, 여기서부터는 더 이상 전략 로직의 아름다움이 중심이 아니라는 점이다. 전략은 이미 어느 정도 모양을 갖췄다. 이제는 그 전략이 실제 서버에서, 실제 시간 위에서, 실제 장애 가능성 안에서도 안정적으로 살아 있는지를 다뤄야 한다. 이건 전혀 다른 종류의 문제다.
그래서 이번 기록이 남긴 핵심은 꽤 단순하다. 수익률을 높이는 장치만큼, 살아 있는지 확인하는 장치도 중요하다. 특히 자동매매처럼 사람이 항상 붙어 있을 수 없는 시스템에선 더 그렇다. 시스템이 아무 말도 하지 않을 때조차, 그 침묵이 정상인지 이상인지를 알려주는 구조가 있어야 한다.
마무리
자동매매를 만들다 보면, 눈에 잘 보이는 개선은 대부분 전략 안에서 일어난다. 더 좋은 진입, 더 나은 손실 구조, 더 자연스러운 청산 같은 것들이다. 하지만 실제 운영에서 마지막까지 남는 불안은 종종 훨씬 단순하다. “지금도 잘 돌고 있나?”라는 질문이다.
이번 빌드로그에서 텔레그램 알림, heartbeat, watchdog, OnFailure를 정리한 건 그 질문에 좀 더 또렷하게 답하기 위한 작업이었다. 이제 시스템은 조금 더 잘 벌 수 있는 구조이기도 하지만, 동시에 조금 더 조용히 망가지지 않는 구조가 되기 시작했다. 그리고 운영에서 이 변화는 생각보다 훨씬 크다.