어제의 고점이 오늘 없었다

전체 점검을 돌리다 숫자 하나가 어긋난 것을 봤다. 서킷브레이커의 기준 고점이 어제 3,157,642원이었는데 오늘 3,104,932원이었다. 이 방어선은 고점 대비 15% 낙폭에서 서는 구조라, 고점은 내려가면 안 되는 값이다. 그런데 내려가 있었다.

고점 래칫 유실 (실측)
08-19 아침  고점 3,157,642원 발동선 2,683,996원
재시작 2회  고점이 현재 자산으로 재시드
08-19 저녁  고점 3,104,932원 발동선 2,639,192원 45,000원 완화

원인은 상태 복원 코드였다. 재시작 시 저장 파일을 읽어오는 함수가 화이트리스트 방식이라, 미리 등록된 키만 복원한다. 고점 키는 이틀 전 서킷브레이커를 만들 때 실행 중에만 만들어 넣었고, 등록 목록에는 없었다. 저장은 매번 됐지만 복원에서 조용히 버려졌고, 부팅 직후 봇은 현재 자산을 새 고점으로 삼았다. 재시작할 때마다 누적 낙폭을 잊는 셈이다.

한 줄 수정, 재시작이 곧 검증

수정은 템플릿에 키 하나를 등재하는 한 줄이다. 래칫이 큰 값 방향으로만 움직이므로 따로 옮겨줄 데이터도 없다. 검증 방법이 마음에 들었는데, 배포를 위한 재시작 자체가 버그의 재현 조건이라 결과를 바로 볼 수 있었다.

배포 재시작 = 검증
재시작 전 저장  고점 3,242,798원
재시작 후 복원  고점 3,242,798원 현재 자산 3,240,291원으로 재시드되지 않음
판정          복원 성공

하필 전날 밤 첫 포트폴리오 수확이 나와 실현 +46,010원으로 고점이 3,242,798원까지 올라 있었다. 유실됐다면 방금 번 것의 낙폭 보호가 통째로 사라질 참이었다.

방어선은 조용히 약해진다

이 결함의 고약한 점은 방향이다. 봇이 죽거나 주문이 실패하면 바로 보인다. 그런데 방어선이 느슨해지는 쪽은 아무 증상이 없다. 발동할 일이 생기기 전까지는 값이 틀려도 화면의 다른 모든 숫자가 멀쩡하다. 원장 7일 창 사건 때도 그랬다. 손익이 조용히 사라졌고, 검증 게이트는 오염된 값끼리 비교하며 통과를 찍었다. 이번에도 저장과 복원이 서로 다른 목록을 보는데 둘 다 에러 없이 돌았다.

그래서 이번 검증 기준을 “에러가 없는가"가 아니라 “재시작 직후 고점이 저장값과 같은가"로 박았다. 앞으로 재시작이 낀 배포마다 이 값을 대조에 넣기로 한다.

하워드 막스는 리스크가 가장 큰 순간은 리스크가 안 보일 때라고 했다. 방어선이 약해지는 결함은 방어선이 시험받는 날까지 보이지 않는다. 시험받기 전에 찾았으니 이번에는 운이 좋았다.


약어 풀이

* 이 글에 나온 영어 약어를 풀어 둔다.

  • BTC (Bitcoin): 비트코인. 이 봇은 BTC 4시간봉 추세로 시장 레짐을 판정한다.