자동매매에서 중요한 것은 로직보다 실행 경로다
요약
분할익절 설정은 켜져 있었고, rule_engine 내부 로직도 구현되어 있었다. 그런데 장중 5분 매도 루프에서 check_sell_signal 호출 시 실제 보유수량(total_qty)과 partial_sold 상태가 전달되지 않았다. total_qty가 0이면 PARTIAL_SELL 분기는 구조적으로 진입할 수 없다. 문제는 조건식이 아니라 실행 경로의 배선 오류였다.
왜 rule_engine을 먼저 의심했나
자동매매 전략은 rule_engine에 집중되어 있다. 매도 조건, 우선순위, 수량 계산 — 대부분의 로직이 거기에 있다. 그래서 뭔가 작동하지 않으면 자연스럽게 rule_engine 내부를 먼저 뒤지게 된다.
그런데 이번 경우엔 rule_engine에는 아무 이상이 없었다. 문제는 그것을 호출하는 쪽에 있었다. scheduler가 필요한 값을 넘기지 않으면 rule_engine은 올바른 판단을 할 수 없다.
이 경험에서 생긴 규칙: 어떤 기능이 실행되지 않을 때, 로직보다 먼저 호출 경로와 전달 인자를 확인한다.
상태 흐름으로 봐야 한다
매도 로직은 단순한 조건문이 아니라 상태 흐름이다. 하나씩 연결이 되어야 한다.
실계좌 잔고 → total_qty 조회
order_log.json → partial_sold 조회
check_sell_signal(total_qty, partial_sold) 호출
PARTIAL_SELL 반환 → 수량 계산 → 매도 실행
partial_sold=True 저장
다음 루프 → partial_sold=True 재조회 → 재발동 방지
잔여수량 → 트레일링스탑 관리 계속
이 흐름 중 하나라도 끊기면 전략 의도와 실제 실행은 달라진다. 분할익절이 실행되지 않은 이유는 첫 번째 단계 — total_qty 조회 결과가 호출 인자로 연결되지 않은 것 — 때문이었다.
로그 보안도 실행 경로 문제다
같은 날 로그 점검 중 텔레그램 API 토큰이 로그에 노출되는 문제를 발견했다. httpx가 HTTP 요청 URL을 INFO 레벨로 기록하는데, 그 URL에 봇 토큰이 포함되어 있었다. 기능은 정상 작동하고 있었지만, 그 결과가 예상치 않은 경로로 남고 있었다.
이것도 실행 경로의 문제다. 무엇을 처리하느냐뿐 아니라 처리 과정이 어디에 어떻게 남느냐도 시스템 설계의 일부다.
운영 점검 기준
자동매매 기능을 추가하거나 수정한 뒤에는 아래 순서로 확인한다.
- 설정값이 활성화되어 있는가
- rule_engine 로직이 구현되어 있는가
- 실제 실행 루프(scheduler)에서 호출되는가
- 필요한 상태값이 인자로 전달되는가
- 실행 결과가 order_log에 저장되는가
- 다음 루프에서 저장 상태가 재조회되는가
- 로그에 민감정보가 남지 않는가
로직이 있어도 경로가 끊기면 없는 것과 같다. 자동매매 운영에서 디버깅의 핵심은 조건식이 아니라 실행 경로와 상태 연결이다.