복제 잡이 "상류 없음"을 성공으로 적고 있었다

두 달치 데이터를 정리하자고 했다가 가격 이력이 8월 24일에 멈춰 있는 걸 봤다. 상류가 비밀번호를 거부한 게 시작이었고, 컷오버 때 환경변수 하나가 목록에서 빠지면서 시끄럽던 실패가 조용한 성공으로 바뀌었다. 그리고 안전장치는 두 달 동안 518번 통과했다

자동매매 봇은 14화부터 만들어 온 시스템이다. 규칙이 내는 전략과 Claude가 내는 전략을 나란히 돌려 비교하고, 아직 실제 돈이 아니라 증권사 모의계좌에서 산다. 두 달을 돌렸으니 성적을 정리하자고 했는데, 정리하다가 매매를 평가하는 층이 열여덟 날 전에 멈춰 있었다는 걸 봤다.

하려던 것

자동매매를 돌린 지 두 달이 됐다. 성적을 판정할 수는 없다. 판정 조건은 20영업일짜리 30기간인데 43영업일이면 30분의 2다. 그래도 뭐가 쌓였는지는 봐야 했다.

자동매매 지금까지 진행했던거 정리좀 해야할 것 같은데.. 어떻게 생각하니 데이터좀 쌓였잖아이제

어떻게 시켰나

성적표가 아니라 운영 점검으로 쓰라고 방향을 잡았다. 얼마 벌었나가 아니라 이 시스템이 판정 가능한 상태인가를 보는 문서다.

읽을 자리도 못 박았다. 대시보드나 로그 요약이 아니라 데이터베이스 원본이다. 잡 실행 기록, 시스템 이벤트, 가드레일 평가, 현금 흐름, 화면 열람 기록. 실전 전환 관문 여섯 개를 그 표들과 하나씩 대조하게 했다.

어디서 막혔나

성적은 예상대로였다. 세 장부가 전부 마이너스고 지수만 플러스다.

대상수익률
Claude 방식 가상 장부-4.56%
규칙 방식 가상 장부-7.59%
실제 모의계좌-2.72%
지수+2.58%

여기까지는 그냥 숫자다. 두 달로는 운인지 실력인지 말할 수 없다.

문제는 그다음 장에서 나왔다. 신호 채점표가 비어 있었다. 점수는 3,855행이 쌓이는데 그 점수가 맞았는지 채점한 결과는 0행이다. 왜 0행인지 따라가니 가격 이력이 필요하고, 가격 이력을 보니 8월 24일에서 멈춰 있었다. 점검하던 날이 9월 11일이다. 열여덟 날이다.

경위는 두 겹이었다.

첫 겹은 8월 25일이다. 가격을 받아 오는 상류 데이터베이스가 그날부터 비밀번호를 거부하기 시작했다. 이건 조용하지 않았다. 복제 잡이 스물한 번 실패했고 실패로 기록됐다. 두 달치 잡 실패 27건 중 21건이 이것이다.

둘째 겹은 9월 5일이다. 그날 Railway에서 맥미니로 컷오버를 했다. 비용 때문이었고, 정지부터 덤프, 복원, 37개 테이블 전수 대조, launchd 등록, DNS, 삭제, 정리까지 일곱 단계짜리 스크립트로 옮겼다. 그런데 데스크 프로세스로 내보내는 환경변수 목록에서 상류 주소 하나가 빠졌다.

그러자 복제 코드가 이렇게 동작했다.

try:
    up = krx_replicate.Upstream()
except RuntimeError as e:
    _log_agent(app, "MARKET", "POSTMARKET", "SKIPPED", f"복제 생략 — {e}")
    return "no upstream"

주소가 없으면 상류 객체 생성이 예외를 던진다. 그 예외를 잡아서 “건너뜀”으로 적고 정상 종료한다. 실패가 아니다.

그러니까 8월 25일부터 시끄럽게 실패하던 것이 9월 5일부터 조용히 성공했다. 컷오버 이후 잡 104회가 전부 성공이라고 기록에 남아 있는데, 그 안에 이게 섞여 있었다. 알람이 멈춘 걸 고쳐진 걸로 읽었다.

알람이 완전히 멈춘 것도 아니었다. 저녁 시장 화면 잡은 매일 “복제본이 묵었다”로 끝나고 있었고, 신선도 경고가 매일 아침 열 시에 왔다. 나는 그 경고를 두 주 넘게 받고 있었다. 문구가 데이터가 좀 오래됐다는 쪽이라 급한 걸로 안 읽었다. 같은 사건인데 시끄러운 채널이 꺼지고 조용한 채널만 남으니 우선순위가 통째로 내려갔다.

어떻게 풀었나

내보내기 목록에 상류 주소를 넣었다. 한 줄이다. 그리고 그 목록을 검증하는 테스트에 같은 키를 추가했다. 목록에서 빠지면 테스트가 잡는다.

상류 자체는 안 고쳤다. 그건 결정이 필요한 항목으로 남겼다. 상류가 살아 있으면 새 접속 정보만 넣으면 되고, 접은 거면 거래소에서 직접 받도록 바꿔야 한다. 백테스트용 코드가 이미 있어서 작업은 크지 않지만 복제 설계를 개정해야 한다. 문서에 두 안을 적고 추천까지 붙여서 결정 요청으로 올렸다. 고칠 수 있는 것만 그 자리에서 고치고 정할 것은 정하게 남긴 것이다.

점검이 더 잡은 게 있다. 이게 사실 더 중요했다.

안전장치가 두 달 동안 518번 평가되어 518번 다 통과했다. 한 번도 막은 적이 없다. 실전 전환 관문 중 하나가 “안전장치가 실제로 한 번은 발동해야 한다”인데, 발동 0회면 그건 잘 만들어진 게 아니라 검증된 적이 없는 것이다. 문서에 그대로 적었다. 발동 안 한 안전장치는 없는 것과 같다. 다음 순서에 모의계좌에서 일부러 한 번 발동시키는 리허설을 넣었다.

에스컬레이션 24건 중 23건이 미결정이었다. 시스템이 판단해 달라고 올린 게 24건인데 결정이 기록된 건 1건이다. 나머지는 올라왔다가 그냥 흘렀다. 결정을 안 할 거면 안 한다는 기록이라도 남겨야 한다고 적었다.

화면 열람 기록이 0행이었다. 저녁 화면을 20일 중 15일 봐야 한다는 관문이 있는데, 열람을 기록하는 테이블이 비어 있다. 게다가 그 화면의 주소가 컷오버 이후 DNS가 없어서 안 열린다. 기록이 없으면 이 관문은 영원히 측정 불가다. 그날 밤 내가 뒤늦게 물은 것도 그거였다.

상황실 어디서 볼수 있어 웹페이지가 있어?

점검을 안 했으면 이 넷 중 하나도 못 봤을 것이다. 매매는 두 달 동안 멈춘 적이 없고 8월 1일 이후로는 실패도 없다. 표면은 멀쩡했다. 평가하는 층이 통째로 멈춰 있었을 뿐이다.

배운 점