2026년 9월 21일 · 엔지니어링

백테스트 도중 전략을 재시작하면 보유 상태를 기억할까?

백테스트 도중 전략을 재시작하면 보유 상태를 기억할까?

유용한 백테스트 테스트는 더 나은 파라미터를 찾는 것과는 무관합니다. 프로세스를 중간에 멈추고 복원한 다음, 같은 리플레이를 끝까지 실행해 보세요. 입력값이 같고 실행 시뮬레이터를 통제했다면, 의사결정과 주문, 자산 곡선은 중단 없이 실행한 결과와 일치해야 합니다.

결과가 다르다면 상태 관리에 문제가 있는 것입니다. 저장하지 않았거나 재구성할 수 없는 무언가에 전략이 의존하고 있다는 뜻입니다. 리서치 실행을 이어서 진행하거나, 워커를 교체하거나, 모의 거래 서비스에 새 코드를 배포할 때 이런 의존성이 문제가 됩니다.

저는 이 테스트를 좋아합니다. 기대하는 결과가 유난히 명확하기 때문이죠. 시장이 바뀌었는지를 두고 논쟁할 여지가 없습니다. 두 실행 모두 같은 시장 데이터를 받습니다.

잘못된 재시작 방식 세 가지를 살펴보겠습니다. 숫자는 설명을 위한 예시이며, 각 문제는 그 밖의 부분이 결정론적인 시스템에서도 발생할 수 있습니다.

1. 바 몇 개를 불러와 지표가 준비됐다고 간주하기

전략이 100기간 지수 이동 평균을 사용한다고 가정해 보겠습니다. 계산 방식은 다음과 같습니다.

alpha = 2 / 101
ema_next = alpha * close + (1 - alpha) * ema_previous

중단 없이 실행되는 프로세스는 누적된 EMA를 계속 이어갑니다. 재시작된 프로세스는 바 100개를 가져와 첫 종가로 EMA를 초기화한 뒤, 100기간 지표에는 관측값 100개면 충분하다고 가정합니다.

이 가정은 지표의 평활화 파라미터를 유한한 기억 구간과 혼동합니다. EMA는 초기 상태의 영향을 점점 줄어드는 방식으로 계속 반영합니다. 두 버전의 EMA가 10 가격 단위 차이로 시작하면, 이후 가격이 같더라도 그 차이는 다음과 같이 줄어듭니다.

초기화 후 경과한 업데이트 수남은 차이초기 오차 대비 비율
1001.35313.53%
2500.06740.674%
5000.0004540.00454%

계산식은 10 * (99 / 101)^k입니다. 첫 관측값을 초기값으로 사용한다면 바 100개를 가져와도 업데이트는 99회뿐입니다.

문제는 대개 의사결정 경계에서 드러납니다. 한쪽 실행에서는 가격이 EMA보다 높고, 다른 쪽에서는 낮게 나타납니다. 작은 수치 차이가 거래 하나를 통째로 추가할 수 있습니다. 그러고 나면 쿨다운과 가용 현금, 이후 의사결정까지 달라질 수 있습니다.

재귀 지표의 상태와 초기화 여부, 마지막으로 처리한 이벤트를 저장하세요. 또는 알려진 시작 상태부터 다시 재생하세요. 워밍업 구간을 늘리면 허용 가능한 근삿값을 얻을 수도 있지만, 명시적인 오차 허용 범위를 바탕으로 길이를 정하고 그 오차가 의사결정을 바꿀 수 있는지 확인해야 합니다. “기간의 5배”는 관례일 뿐, 증명은 아닙니다.

그리고 과거 데이터가 필요한 것은 지표만이 아닙니다. 롤링 백분위수에는 윈도우가 필요합니다. 온라인 모델에는 옵티마이저 상태가 필요할 수 있습니다. 손실 후 바 3개를 기다리는 규칙이라면 손실 여부와 카운터를 기억해야 합니다.

2. 포지션은 저장하고 진행 중인 주문은 잊기

목표 포지션은 10단위입니다. 10단위 매수 주문 중 4단위가 체결되어 6단위가 남았습니다. 체크포인트에는 포지션을 4로 저장하고 재시작한 뒤, 부족한 6단위를 다시 매수 주문으로 제출합니다.

기존 잔량과 대체 주문이 모두 체결되면 보유량은 16이 됩니다.

백테스트에서는 체결 엔진을 재시작할 때 대기 중인 주문이 조용히 사라지는 경우가 많아 이 버그가 드러나지 않습니다. 모의 거래에서는 시뮬레이터나 외부 서비스가 주문을 보유하고 있을 수 있습니다. 그러면 어떤 구성 요소가 살아남았는지에 따라 같은 복구 코드가 서로 다른 익스포저를 만들 수 있습니다.

재시작 시점실제 상태포지션만 복구할 때 파악하는 상태
목표 포지션1010
체결된 포지션44
미체결 매수 수량60
추가로 필요한 수량06

복구 직후 설명되지 않는 주문이 갑자기 쏟아지는 것이 문제의 징후입니다. 익스포저가 두 배가 되기도 합니다. 보호 주문이 아직 유효한데 포지션을 청산해 버려서, 그 주문이 나중에 새 포지션을 열 수도 있습니다.

체크포인트에는 포지션과 함께 주문 식별 정보 및 생명주기 상태도 들어가야 합니다. 복구 과정에서 새 동작을 만들기 전에 해당 기록을 체결 시스템과 대조해야 합니다. 결과를 알 수 없는 주문은 조사해야 합니다. “확인 응답이 저장되지 않았다”를 “제출된 적이 없다”로 취급하면 중복 주문이 생깁니다.

안정적인 클라이언트 주문 ID가 있으면 주문의 처리 결과를 조회하기 편합니다. 다만 수신 시스템이 필요한 고유성 또는 멱등성 규칙을 실제로 적용할 때만 중복을 막을 수 있습니다. 처리한 체결 ID도 저장해야 리플레이된 체결이 포지션을 두 번 늘리지 않습니다.

저는 지루한 주문 상태 화면을 은근히 좋아합니다. 재시작하는 날이면, 그 작은 행들이 갑자기 건물 안에서 가장 흥미로운 인터페이스가 됩니다.

3. 포지션을 복구하고 새 P&L 원장을 시작하기

수수료가 없는 무레버리지 현물 예시를 생각해 보겠습니다. 현금 $10,000으로 시작해 $100에 10단위를 매수하고, 평가 가격이 $110일 때 체크포인트를 저장합니다.

올바른 상태는 현금 $9,000에 $1,100 가치의 포지션을 더한 것으로, 자산은 $10,100입니다. 복구 과정에서 10단위는 복원하면서 현금은 원래 금액인 $10,000으로 초기화하면 자산이 $11,100으로 표시됩니다. 프로세스를 재시작해 $1,000을 만들어 낸 셈입니다.

다른 문제는 겉으로 덜 극적입니다. 복구 후 자산은 그대로지만 진입 가격이 $110으로 초기화될 수 있습니다. 총자산은 맞더라도 실현 손익과 미실현 손익의 구분이 달라집니다. 손절이나 청산 조건이 진입 가격을 참조한다면, 이 회계상의 편법이 이제 거래 동작까지 바꿉니다.

또는 시스템이 이전 자산 최고점을 잊을 수 있습니다. 자산이 $10,600까지 올랐다가 $10,100으로 떨어졌다고 해 보겠습니다. 낙폭은 약 4.72%입니다. 복구 시 최고점을 초기화하면 전략은 갑자기 낙폭이 0이라고 판단합니다. 낙폭 기반 위험 관리 기능이 승인 없이 초기화된 것입니다.

따라서 자산 곡선의 불연속, 갑자기 개선된 것처럼 보이는 낙폭, 배포 후 작동을 멈추는 위험 규칙 등이 문제의 징후가 될 수 있습니다. 원장과 전략의 회계 의존 상태를 보존하세요. 여기에는 현금 이동, 포지션, 해당되는 원가 기준, 누적 비용, 위험 관리 기록이 포함됩니다. 복구한 자산을 같은 평가 시점의 원장과 대조하세요.

체크포인트는 일관된 경계를 가져야 합니다. 체결 후 현금을 저장하고 체결 전 포지션 수량을 저장하면 실제로 존재한 적 없는 상태가 됩니다. 서로 관련된 상태를 함께 커밋하거나, 재구성에 사용할 내구성 있는 이벤트 순서를 기록하세요. 복구 과정에서 체결을 건너뛰거나 두 번 적용하지 않도록 상태와 함께 이벤트 커서도 저장해야 합니다.

리서치 하네스에 남겨 둘 테스트로는 중단 없이 실행하는 기준 리플레이 하나를 돌린 뒤, 일부러 까다로운 시점에서 두 번째 실행을 재시작하겠습니다. 지표 초기화 도중, 부분 체결 직후, 위험 한도가 활성화된 동안이 그런 시점입니다. 이벤트 순서를 동일하게 유지하고 시뮬레이터의 난수 상태도 보존하세요. 복구 후 첫 의사결정과 주문 및 체결 기록, 자산 경로를 비교하세요. 마지막 잔액만 일치하면 서로 상쇄되는 오류를 놓칠 수 있습니다.

제출 후 확인 응답을 받기 전에 충돌하는 경우를 테스트하려면, 하네스가 체결 서비스의 상태도 전략 프로세스와 별도로 보존해야 합니다. 그렇지 않으면 테스트하려는 바로 그 불확실성을 없애게 됩니다.

전략 명세에는 무엇을 기억하는지도 포함됩니다. 리플레이 도중 프로세스를 종료해도 전략이 어떻게 작업을 재개하는지 정확히 보여 줄 수 있도록 그 기억을 명시적으로 관리하세요.

전략 상태백테스팅체크포인트 복구모의 거래
← 전체 글