판별 기준은 하나다 — 끄면 돌아갈 곳이 있나
만든 자동화가 도구인지, 일하는 방식이 바뀐 것인지는 기능 개수로 갈리지 않는다.
그것을 껐을 때 예전 방식으로 돌아갈 수 있으면 도구다. 돌아갈 곳이 없으면 일하는 방식이 바뀐 것이다.
AX(AI Transformation)라는 말은 보통 “AI를 도입했다”는 뜻으로 쓰인다. 그 정의로는 아무것도 구분되지 않는다. 도입은 전부 도입이기 때문이다. 구분이 되는 지점은 따로 있고, 나는 내가 만든 것 중 딱 하나가 그 선을 넘었다고 본다.
선을 넘은 것 — 밤에 스스로 고치고 배포하는 루프
주식 자동매매 봇에 야간 자율 개선 루프를 붙였다. 평일 21시 30분에 launchd가 깨우고, 다음 순서로 진행된다.
- 컨테이너 안에서 결함 탐지 스크립트가 돈다. 결함이 없으면 Claude를 아예 호출하지 않고 종료한다.
- 수정 전 코드로 기준선 지표를 먼저 측정한다.
- 헤드리스 Claude가 코드를 직접 고친다.
- 게이트 ① — 변경된 파일에 매핑된 테스트를 격리 사본(
/tmp/improve_run)에서 돌린다. - 게이트 ② — 같은 격리 사본에서 백테스트를 돌려 기준선과 비교한다.
- 통과하면 커밋하고
docker compose up -d --build, 헬스체크까지 확인한다. 실패하면git reset --hard로 되돌리고 다시 빌드한다.
여기서 빠진 단계가 핵심이다. 내가 매일 코드를 들여다보고 “이건 고칠 만한가”를 판단하던 단계가 통째로 없다. 게이트를 통과하면 승인 없이 배포된다. 실제로 2026년 6월 22일부터 8월 11일 사이에 이 루프가 스스로 만든 커밋 4개가 배포됐다. 빈 커밋이 아니라 hybrid_quant_ai.py, risk/trailing_stop.py 같은 실제 로직 파일의 변경이다.
설계 초기에 나는 “실계좌 전략 자동 변경 영구 금지”라는 원칙을 세워뒀었다. 이 루프를 만들면서 그 원칙을 폐기했다. 폐기했다는 사실 자체가 선을 넘었다는 표시다.
되돌아갈 곳이 없으니 게이트가 유일한 방어선이 됐다
여기서 이 글의 본론이 시작된다. 사람이 중간에 없으면 게이트가 틀렸을 때 그것을 잡아줄 것이 없다. 그래서 게이트를 무엇으로 삼을지가 전부가 된다.
처음에 쓰려던 지표는 워크포워드 OOS 샤프 지수였다. 상식적인 선택이고, 백테스트 게이트라면 보통 그렇게 만든다. 이게 무력했다.
원인은 일봉 데이터의 거래 표본이다. 이 전략은 일봉으로 돌고, 4년치를 백테스트해도 종목당 체결이 2건 남짓 나온다. 표본이 2건이면 샤프는 사실상 계산되지 않고, 어떤 수정을 넣어도 게이트가 0을 뱉는다. 통과도 탈락도 아닌 무판정이다. 무판정 게이트는 없는 게이트와 같고, 사람이 없는 파이프라인에서 없는 게이트는 그냥 무조건 배포와 같다.
그래서 체결이 아니라 점수를 재기로 했다
지표를 rank-IC(예측력 순위상관)로 바꿨다. 전략이 매 캔들마다 산출하는 combined_score와, 그 시점 이후 5·10·20일 선행 수익률 사이의 순위상관을 잰다. h=1은 노이즈가 커서 뺐다.
바꾼 이유는 하나다. 점수는 체결과 무관하게 매 캔들 산출된다. 코드에도 그렇게 적어뒀다.
# 거래 횟수와 무관 — 신호와 선행수익의 순위상관만 본다.
# 거래가 0건이어도 종목·기간별 IC가 측정되므로 표본 부족 문제를 우회한다.
표본 문제가 풀린다. 4년에 2건 체결이어도 캔들 수만큼 점수가 쌓이므로, 수정본이 신호의 예측력을 깎았는지는 판정할 수 있다.
판정도 절대값이 아니라 상대 비교로 만들었다. candidate_ic >= baseline_ic - 0.005. 현 전략의 평균 IC가 0에 가까워도 — 즉 엣지가 거의 없어도 — “이번 수정이 더 나쁘게 만들었는가”는 답할 수 있기 때문이다. 절대 하한선(min_abs_ic) 옵션도 만들어뒀지만 기본값 0으로 꺼져 있다.
문제를 푼 그 성질이 그대로 사각지대가 된다
여기가 트레이드오프의 실체다. 점수를 체결과 분리했기 때문에 표본 문제가 풀렸는데, 분리했기 때문에 실행 계열의 변경에 게이트가 원리적으로 반응하지 않는다.
점수 산출 함수는 매 캔들 strat.last_combined_score를 기록한다. 매수 임계값은 그 점수를 놓고 “체결할지 말지”를 가르는 값이다. 임계값을 어떻게 바꾸든 기록되는 점수 시계열 자체는 한 글자도 변하지 않는다. 그러면 순위상관도 변하지 않는다. 게이트는 “IC 유지됨 → 통과”라고 답한다.
이건 버그가 아니라 정의상 그렇다. 설계 문서에도 같은 말이 있다.
운영 로직만 바뀐 패치는 IC가 동일 → ‘유지’로 통과.
정리하면 이 게이트는 예측 신호가 나빠졌는지는 잡고, 그 신호를 어떻게 집행하는지가 바뀐 것은 못 잡는다. 실제로 임계값을 0.30에서 0.15로 낮춘 변경이 있었는데, 그때 근거로 쓴 숫자는 IC 변화량이 아니라 통과율(0.7% → 9.9%)이었다. IC로는 잴 수 없는 축이라 다른 자를 들었던 것이다.
이 사각지대에 대한 현재 상태
축이 하나 부족하다는 것은 알고 있고, 아직 게이트에 넣지 않았다. 지금 있는 것은 두 가지인데 둘 다 배포를 막는 위치에 있지 않다.
하나는 결함 탐지 단계의 규칙이다. 최근 7일간 매수 신호가 정확히 0건일 때만 발동해서 “거래 신호 부진”이라는 문자열을 만든다. 이건 배포를 막는 게 아니라 Claude를 깨우는 트리거다. 상한도 없어서 신호가 폭증하는 쪽은 아예 보지 않는다.
다른 하나는 signal_funnel.py인데, 신호가 어느 필터에서 죽는지 단계별로 세어주는 오프라인 분석기다. CLI로만 호출되고 개선 루프는 이 파일을 import하지 않는다.
즉 지금 구조는 “신호 예측력이 나빠지는 변경”은 자동으로 막고, “집행 빈도가 달라지는 변경”은 아침에 내가 보고 알아채는 것에 기대고 있다. 사람을 뺀 파이프라인에서 특정 축만 사람에게 남겨둔 상태다. 되돌아갈 곳이 없다는 걸 감안하면 이 축을 게이트로 올리는 게 다음 순서다.
같은 시기에 만든 것들은 이 선을 안 넘었다
비교 대상이 있어야 기준이 선명해진다. 비슷한 시기에 크롤러와 알림 자동화를 여럿 만들었다. 쇼핑몰 재입고 알림, 골프장 예약 감시 같은 것들이다.
이것들은 안 울려도 상관없다. 내가 사이트에 직접 들어가면 그만이다. 알림이 죽어도 내 판단이 사라지지는 않는다.
기능이 적어서 도구인 게 아니다. 되돌아갈 경로가 그대로 남아 있어서 도구다. 크롤러가 하는 일은 “내가 원래 하던 확인을 대신하는 것”이고, 자율 개선 루프가 하는 일은 “내가 원래 하던 판단을 대체하는 것”이다. 대신하는 것과 대체하는 것은 다르다.
선이 넘어가는 지점은 두 번째 사람이 생길 때다
기능을 늘린다고 도구가 일하는 방식으로 바뀌지 않는다. 바뀌는 순간은 그 결과물을 믿고 자기 일을 시작하는 두 번째 사람이 생길 때다. 그 사람이 검토하지 않고 다음 단계로 넘어가는 순간, 그 결과물은 참고 자료가 아니라 입력이 된다.
내 경우 두 번째 사람은 다른 사람이 아니라 어제의 나였다. 밤사이 루프가 고친 코드 위에서 아침의 내가 작업을 시작한다. 그 코드를 커밋 단위로 다시 읽지 않는다. 텔레그램으로 온 요약을 읽고 “통과했구나” 하고 넘어간다.
이게 어떤 상태인지는 실제로 남은 흔적이 보여준다. 자율 배포 커밋 4개의 메시지가 이렇게 생겼다.
auto-improve: 88개 전부 통과했습니다
auto-improve: 테스트 완료 알림을 기다리겠습니다
git commit -am "auto-improve: $PATCH_SUMMARY"에 LLM이 뱉은 대화체 문장이 그대로 들어간 결과다. 커밋 메시지가 무슨 변경인지 설명하지 못하는 상태로 두 달을 돌았다는 뜻이고, 내가 그 이력을 열어보지 않았다는 뜻이기도 하다. 검토하는 사람이 있었다면 첫 커밋에서 걸렸을 것이다.
총평
내가 만든 자동화는 대부분 도구였다. 하나만 선을 넘었고, 넘은 이유는 기능이 많아서가 아니라 판단 단계를 제거했기 때문이다.
판단을 제거하면 그 자리에 게이트가 들어가고, 게이트를 무엇으로 삼는가가 그 시스템의 실질적인 한계가 된다. 표본 부족 때문에 샤프를 버리고 IC를 골랐고, 그 선택이 표본 문제를 풀면서 동시에 집행 축을 못 보게 만들었다. 게이트는 사람의 판단을 옮겨 담은 것이 아니라, 사람의 판단 중 잴 수 있는 일부만 남긴 것이다.
돌아갈 곳을 없앨 때 확인해야 할 것은 자동화가 잘 도는지가 아니라, 남겨둔 자가 무엇을 못 재는지다.