자동매매를 처음 공부할 때 가장 강한 장면은 대개 백테스트 결과에서 나온다. 우상향하는 자산 곡선, 괜찮은 승률, 그럴듯한 손익비, 그리고 “이 정도면 실전에서도 먹히지 않을까?”라는 기대감. 그런데 막상 라이브로 넘어가면 분위기가 달라진다. 같은 전략인데도 기대보다 수익이 약하고, 손절은 더 거칠고, 운영 스트레스는 훨씬 크다. 이때 많은 사람은 시장이 갑자기 나빠졌다고 생각한다. 하지만 실제로는 시장보다 먼저 봐야 할 것이 있다. 백테스트와 라이브가 애초에 같은 세계를 보고 있었는가라는 질문이다.
이 글은 “백테스트는 다 쓸모없다”는 이야기를 하려는 글이 아니다. 오히려 반대다. 좋은 백테스트는 필요하다. 다만 좋은 백테스트만으로는 충분하지 않다. 실전에서는 데이터가 들어오는 시점도 다르고, 체결되는 방식도 다르고, 주문이 살아 있는지 확인하는 방법도 다르고, 보호주문이 비었는지 감시하는 계층도 따로 필요하다. 차트 위의 전략은 같아 보여도, 시간 기준, 체결 기준, 상태 기준, 관측 기준이 달라지면 결과는 전혀 다른 전략처럼 변한다.
실제로 트레이딩 시스템을 다듬는 과정에서 가장 많이 고친 것도 새로운 알파가 아니었다. 오히려 라이브와 백테스트가 같은 규칙을 같은 현실 위에서 실행하고 있는지를 맞추는 일이 더 많았다. 이 글에서는 그 간극이 어디서 생기는지, 그리고 왜 그 간극이 생각보다 훨씬 실전적인 문제인지 차근차근 정리해보려 한다.
백테스트는 전략을 테스트하지만, 운영 전체를 테스트하지는 않는다
백테스트의 가장 큰 장점은 빠르고, 싸고, 반복 가능하다는 점이다. 같은 데이터 위에서 같은 규칙을 수백 번 돌려볼 수 있고, 손절과 청산 규칙을 바꿔가며 결과를 비교할 수도 있다. 문제는 이 과정이 너무 매끈해서, 우리가 실제 시장에서 마주칠 거친 표면을 잊게 만든다는 데 있다.
예를 들어 백테스트는 아주 쉽게 미래 정보를 몰래 데려온다. 아직 확정되지 않은 봉의 고가와 저가를 당연한 것처럼 쓰거나, 나중에 확인된 데이터를 현재 시점의 판단에 섞어버리기도 한다. 미래정보 반영, 생존 편향, 과최적화 같은 문제가 괜히 반복해서 등장하는 게 아니다. 백테스트는 원래 “미래를 모르는 상태”를 흉내 내는 도구인데, 실제로는 조금만 방심해도 미래를 슬쩍 아는 시뮬레이터로 변해버린다.
그렇다고 이런 왜곡이 백테스트 안에서만 끝나는 것도 아니다. 더 중요한 건 백테스트가 대체로 운영 계층을 생략한 채 전략 계층만 테스트한다는 점이다. 주문이 일부만 체결됐을 때 내부 상태가 어떻게 바뀌는지, 재시작 이후 기존 보호주문을 어떻게 이어받는지, 프로세스는 살아 있는데 1분 보호 루프만 멈춘 상황을 어떻게 감지할지 같은 문제는 보통 기본 백테스트가 거의 다루지 않는다. 그래서 차트 위에서는 멀쩡한 전략이 실전에서는 갑자기 서툴고 불안정한 시스템처럼 느껴지는 일이 생긴다.
같은 전략도 시간 기준이 다르면 다른 전략이 된다
백테스트와 라이브의 간극을 가장 자주 만드는 건 의외로 화려한 수식이 아니라 시간 처리다. 언제 봉이 “끝났다”고 볼 것인지, 어느 순간부터 보호주문을 움직일 것인지, 한 번에 모아서 판단할 것인지 아니면 더 짧은 주기로 계속 갱신할 것인지가 조금만 달라도 같은 전략은 금세 다른 전략처럼 행동한다.
이 차이는 짧은 주기로 손절선을 조정하는 보호 로직에서 특히 선명하게 드러난다. 어떤 환경은 1분 동안 나온 가격 움직임 전체를 보고 손절선을 옮기고, 어떤 환경은 그 순간 보이는 마지막 가격 하나만 보고 움직인다. 겉보기엔 둘 다 “1분마다 보호를 업데이트한다”는 말로 설명할 수 있지만, 실제로는 보는 정보가 다르다. 한쪽은 그 1분 동안 어디까지 올랐다가 어디까지 밀렸는지를 보고, 다른 한쪽은 마지막 숫자 한 점만 본다. 그러면 청산 타이밍도 달라지고, 손절선이 따라붙는 속도도 달라지고, 체감 리스크도 달라질 수밖에 없다. 차트 한 개로 요약하면 보이지 않는 봉 내부의 순서, 즉 무엇이 먼저 일어났는지도 결과를 바꾼다.
실전에서 자주 생기는 또 하나의 문제는 보호 로직의 시작 시점이다. 어떤 시스템은 포지션이 열리자마자 바로 보호를 시작하고, 어떤 시스템은 어느 정도 수익이 난 뒤에야 같은 보호를 켠다. 이 차이도 생각보다 크다. 포지션 초반은 방향이 아직 굳지 않았고, 예상과 다르게 바로 꺾일 수도 있는 가장 취약한 구간이기 때문이다. 백테스트는 그 구간까지 보호하고 있었는데 라이브는 아직 기다리고 있었다면, 같은 전략처럼 보여도 실제로는 다른 위험을 감수하고 있는 셈이다.
여기서 최근 점검으로 더 선명해진 문제도 있다. 같은 날짜를 비교한다고 해서 같은 맥락을 비교하는 것은 아니라는 점이다. 느리게 움직이는 지표나 상위 시간 프레임 필터를 쓰는 전략은, 오늘 신호를 만들기 위해 그 전의 긴 맥락을 함께 먹고 들어간다. 한쪽은 충분한 과거 데이터를 미리 읽어 지표가 안정된 상태에서 시작하고, 다른 한쪽은 막 켜져서 짧은 최근 데이터만 본다면, 겉으로는 같은 4월 구간을 보더라도 실제 판단은 이미 달라진다.
그래서 “같은 전략인데 왜 청산 위치가 다르지?”라는 질문이 나오면, 파라미터를 다시 보기 전에 먼저 두 환경이 같은 시계로 움직이고 있는지부터 확인하는 편이 낫다. 봉 확정 기준은 같은지, 보호 로직은 같은 순간에 켜지는지, 손절선은 같은 종류의 가격 정보를 보고 갱신되는지, 재시작 이후에도 직전까지 보던 시장 상태를 제대로 이어받는지, 느린 지표를 계산하기 위한 예열 구간은 충분했는지 같은 것들이다. 실전에서는 이런 시간 기준의 미세한 차이가 결국 수익곡선의 굵은 차이로 돌아온다.
이 관점은 백테스트 집계 방식까지 바꾼다. 어떤 전략은 긴 예열 구간이 꼭 필요하다. 하지만 예열을 위해 불러온 과거 데이터와, 실제로 성과를 집계하려는 비교 구간은 분리해서 봐야 한다. 지표를 안정적으로 계산하려고 앞부분 데이터를 더 읽는 건 필요하지만, 그 구간의 거래나 상태까지 결과 표에 섞이면 사용자는 4월을 비교한다고 생각했는데 실제로는 3월의 흔적까지 같이 보고 있는 셈이 된다. 백테스트를 공정하게 만들려면, 무엇을 계산하기 위해 읽었는지와 무엇을 성과로 집계하는지를 분리해야 한다.
체결 기준이 다르면 수익률보다 먼저 포지션이 어긋난다
백테스트가 대체로 묵인하는 두 번째 단순화는 체결이다. 차트 위에서는 보통 “이 가격에 진입했다”가 하나의 문장으로 끝난다. 그런데 실제 시장의 주문은 그렇게 깨끗하지 않다. 지정가는 부분 체결될 수 있고, 취소 직전에 일부가 이미 체결돼 있을 수도 있고, 남은 잔량만 시장가로 넘어가야 하는데 시스템이 처음 요청한 전체 수량을 다시 들이받으려 할 수도 있다.
이건 숫자의 오차가 아니라 포지션 자체를 바꾸는 문제다. 시스템은 1.0을 요청했는데 거래소는 먼저 0.4만 채워줬고, 그다음 남은 0.6만 시장가로 메워야 하는 상황을 생각해보자. 만약 내부 엔진이 “아직 1.0이 안 들어갔다”고 착각하면 과체결 위험이 바로 생긴다. 그러면 백테스트에서는 존재하지 않던 새로운 리스크가 실전에서 생긴다. 전략이 틀린 게 아니라, 요청값과 실제 체결값을 다루는 방식이 틀린 것이다.
실전 시스템을 다듬다 보면 결국 하나의 결론으로 모인다. 내부 계산보다 거래소가 더 믿을 만하다. 요청한 수량보다 실제로 체결된 수량을 신뢰해야 하고, 추가 처리가 필요해도 이미 채워진 양을 뺀 잔량만 다뤄야 한다. “내가 이렇게 주문했으니 이렇게 됐겠지”라는 추정은 생각보다 자주 틀린다. 라이브와 백테스트의 간극은 종종 슬리피지보다 먼저, 바로 이 체결 기준에서 벌어진다.
가장 무서운 건 손실이 아니라 보호주문의 공백이다
실전 자동매매에서 제일 불안한 순간은 손익이 흔들릴 때가 아니다. 시스템이 자신은 보호되고 있다고 믿지만, 실제로는 보호주문이 비어 있는 순간이 훨씬 위험하다. 이건 백테스트 화면만 오래 보면 잘 보이지 않는 리스크다. 차트 위의 손절선은 늘 존재하는 것처럼 보이지만, 실제 거래소에서는 주문 교체, 재시작, API 예외, 조건부 주문 거절 때문에 보호 레이어가 잠깐씩 비는 일이 생긴다.
예를 들어 재시작 이후 내부 메모리는 비었는데 거래소에는 이전 손절 주문이 살아 있을 수 있다. 반대로 내부는 여전히 손절이 있다고 믿는데, 실제 거래소 쪽 주문 ID 추적이 끊겨 있어 아무도 그 공백을 모를 수도 있다. 더 난감한 경우는 손절 주문을 더 유리한 가격으로 올리려 했는데 거래소가 “지금 그 가격으론 바로 발동된다”고 거절하는 상황이다. 새 주문은 실패했고, 이전 주문은 이미 취소한 뒤라면 그 짧은 몇 초가 그대로 보호주문 공백이 된다.
이런 사례를 몇 번 겪고 나면 트레이딩 시스템을 보는 시선이 달라진다. 좋은 시스템은 단순히 손절을 잘 계산하는 시스템이 아니다. 손절이 사라졌을 때 그 사실을 먼저 알아차리고, 기존 주문이 살아 있으면 이어받고, 새 주문 배치가 실패하면 직전 손절을 되살리고, 아무것도 없으면 즉시 복구 루틴을 돌리는 시스템이다. 실전 자동매매에서 “손절이 잘 걸린다”보다 더 중요한 문장은 종종 “손절이 비어 있지 않다”가 된다.
실전은 자주 조용히 망가지기 때문에, 관측 가능성이 따로 필요하다
백테스트는 거의 언제나 명확하게 끝난다. 실행이 완료되거나 실패하거나 둘 중 하나다. 그런데 라이브는 훨씬 교묘하다. 프로세스는 살아 있는데 텔레그램 폴링만 죽어 있을 수 있고, 메인 전략 루프는 살아 있는데 1분 보호 루프만 멈춰 있을 수도 있고, 서비스는 실행 중으로 보이는데 상태 신호는 오래전에 끊겨 있을 수도 있다. 이런 상태는 겉보기에 “돌아가고는 있는 것 같은데, 뭔가 이상한” 시스템을 만든다.
그래서 라이브에서는 전략보다 먼저 관측 가능성이 필요하다. 상태 신호, 감시 루틴, 지연 알림, 보호 루프 상태 체크, 실패 후 복구 알림 같은 것들이 전부 여기에 들어간다. 겉으로 보기엔 글의 주제와 멀어 보일 수 있지만, 사실은 아주 가깝다. 왜냐하면 백테스트와 라이브의 간극은 단순히 결과 숫자의 차이가 아니라, 어떤 실패가 드러나고 어떤 실패는 침묵 속에 묻히는가의 차이이기도 하기 때문이다.
실전 자동매매에서 가장 위험한 실패는 보통 가장 시끄러운 실패가 아니다. 오히려 “프로세스가 죽었는데 아무 알림도 없었다”, “1분 보호 루프가 멈췄는데 상태판은 멀쩡해 보였다”, “서비스는 돌아가는 것처럼 보이는데 실제 상태 신호는 끊겨 있었다” 같은 종류가 더 무섭다. 이건 전략의 알파 문제가 아니라 운영의 시야 문제다. 결국 라이브 시스템은 똑똑해야 하기 전에 먼저 자기 상태를 솔직하게 드러낼 수 있어야 한다.
간극은 느낌이 아니라 비교 가능한 데이터로 줄여야 한다
백테스트와 라이브의 간극을 이야기할 때 흔히 빠지는 함정이 하나 있다. 체감만 남고 비교 기준은 흐려지는 것이다. “요즘 라이브가 좀 안 좋다”, “백테스트보다 덜 예쁘다” 같은 문장은 느낌으로는 맞을 수 있지만, 개선에는 별로 도움이 안 된다. 실제로 간극을 줄이려면 라이브 기록과 백테스트 기록을 같은 날, 같은 시간대, 같은 기준 시각 위에 올려놓고 비교할 수 있어야 한다.
이 지점에서 시간대 통일도 의외로 중요하다. 라이브 기록은 KST 기준이고, 백테스트 기록은 UTC 기준이면 같은 하루를 보고 있는지부터 헷갈리기 시작한다. 그래서 실전에서는 결과를 예쁘게 보여주는 것보다 먼저 같은 날짜를 같은 시간 기준으로 본다는 합의가 필요하다. 라이브와 백테스트 기록을 분리해 적재하고, 날짜 컬럼을 UTC로 통일하고, 매일 비교 리포트로 거래 수, 승률, 평균 R, 총손익 같은 지표를 나란히 보는 이유가 여기에 있다.
여기서 한 단계 더 나가면, 비교 리포트 자체도 설계 대상이 된다. 라이브와 백테스트 기록이 한 표 안에서 뒤섞이거나, 중복 행이 섞여 있거나, 날짜 기준이 제각각이면 진단은 금세 흐려진다. 그래서 최근 점검에서는 라이브와 백테스트를 아예 분리된 기록 경로에 쌓고, 같은 UTC 날짜 열과 고유 거래 ID를 기준으로 맞춰보는 쪽이 훨씬 낫다는 점이 더 분명해졌다. 비교 체계가 흐리면 시스템의 차이보다 기록 방식의 차이가 먼저 눈에 들어온다.
또 하나 놓치기 쉬운 건 비교 지표의 단위다. 총손익만 보면 “둘 다 벌었네” 혹은 “둘 다 잃었네” 정도로 끝난다. 그런데 실전에서는 같은 손익이라도 어떤 쪽이 더 큰 위험을 감수했는지가 중요하다. 그래서 최근에는 순손익뿐 아니라 손실 단위 기준의 누적 성과와 낙폭까지 함께 보는 쪽이 더 유용했다. 백테스트와 라이브를 비교할 때도 결국 중요한 건 얼마 벌었는가만이 아니라, 얼마나 같은 방식으로 흔들렸는가다.
이 비교 체계가 생기면 비로소 질문도 달라진다. “전략이 죽었나?”보다 “오늘은 실행 간극이 컸나?”, “부분 체결이 많았나?”, “보호주문 복구 로직이 개입했나?”, “시간 기준 차이가 있었나?”를 먼저 볼 수 있다. 백테스트와 라이브의 차이는 완전히 제거할 수 없지만, 적어도 어디서 벌어졌는지 설명 가능한 차이로 바꾸는 건 가능하다.
좋은 시스템은 간극을 없애지 못해도, 그 간극이 생기는 위치를 좁혀둔다
결국 핵심은 이거다. 백테스트와 라이브의 차이를 완전히 0으로 만드는 방법은 없다. 실제 시장에는 슬리피지도 있고, 지연도 있고, 주문 거절도 있고, 예측하지 못한 상태 전이도 있다. 하지만 그 차이가 어디서 생기는지 모르는 시스템과, 차이가 생겨도 그 위치를 빠르게 좁혀가는 시스템은 전혀 다르다.
좋은 시스템은 먼저 시간 기준을 맞춘다. 봉 확정 규칙을 엄격히 지키고, 라이브 보호 루프가 백테스트와 같은 입력을 보도록 만든다. 그다음 체결 기준을 맞춘다. 요청값보다 실제 체결 수량을 신뢰하고, 부분 체결 뒤에는 잔량만 처리한다. 그다음 보호 기준을 맞춘다. 재시작 후 기존 손절 주문을 이어받고, 주문 교체 실패 시 직전 손절을 복원하고, 보호주문이 비면 즉시 복구한다. 마지막으로 관측 기준을 맞춘다. 상태 신호, 감시 루틴, 지연 알림, 비교 리포트로 시스템이 자신을 숨기지 못하게 만든다.
이 네 단계를 지나면 백테스트는 더 이상 “예쁜 그림”이 아니라, 라이브와 비교 가능한 가설 검증 도구가 된다. 그리고 라이브는 더 이상 “운에 맡기는 실전”이 아니라, 백테스트가 놓친 마찰을 계속 드러내는 현실의 검사장처럼 보이기 시작한다.
마무리
백테스트가 이기고 라이브가 지는 이유는 종종 전략이 약해서가 아니다. 같은 진입 규칙을 써도 시간이 다르게 흘렀고, 같은 포지션처럼 보여도 체결 기준이 달랐고, 같은 손절선처럼 보여도 실제 보호 레이어는 비어 있었고, 같은 서비스처럼 보여도 내부 루프는 조용히 멈춰 있었기 때문이다.
그래서 실전 자동매매에서 중요한 질문은 “이 전략이 먹히는가?” 하나로 끝나지 않는다. 그보다 앞에 이런 질문이 붙는다. “이 전략은 실제 시장의 시간, 체결, 상태, 장애를 견디는 구조 위에서 실행되고 있는가?” 백테스트는 전략 아이디어를 고르는 도구다. 하지만 라이브 운영은 그 아이디어가 현실에서도 살아남을 수 있는지를 판정하는 단계다. 좋은 전략은 테스트를 통과할 수 있지만, 좋은 시스템만이 테스트와 현실 사이의 간극을 줄여준다.