2026년 9월 5일 · 리스크

백테스트에서는 청산되지 않았다: 마진, 마크 가격, 그리고 건너뛴 레버리지 한도선

백테스트에서는 청산되지 않았다: 마진, 마크 가격, 그리고 건너뛴 레버리지 한도선

지난달 생성형 전략에서 자산 곡선이 하나 나왔는데, 낙폭이 63%였습니다. 깊고 끔찍하게 빠졌다가 이후 6주 동안 깔끔하게 회복하는 모습이었죠. 연구자라면 한참 들여다보다가 포지션 크기를 줄이면 견딜 만하겠다고 판단할 법한 곡선입니다. 유지증거금 구간을 아는 마진 엔진으로 다시 돌려봤습니다. 계좌는 그 낙폭 한가운데인 41일째에 청산됐고, 41일째 이후의 모든 결과는 허구였습니다.

제가 아는 백테스트 버그 중 비용이 가장 큰 유형입니다. 수익률을 몇 베이시스포인트 왜곡하는 데 그치지 않기 때문입니다. 시뮬레이션에서 흡수 상태 하나를 통째로 지워버립니다. 계좌가 0이 될 수 있는 전략과 그럴 수 없는 전략은 서로 다른 전략입니다. 마진을 확인하지 않는 백테스트로는 어느 쪽인지 알 수 없습니다.

잘못 구현되는 방식이 계속 눈에 띄는 것만 3가지입니다.

첫 번째 잘못된 방식: 수익률에 곱하는 숫자로서의 레버리지

가장 흔한 형태입니다. 시그널에서 수익률 시계열을 계산하고, 10x로 운용하기로 한 뒤 수익률에 10을 곱합니다. 조금 더 정교한 경우도 있습니다. 포지션 크기를 equity * leverage / price로 정하기도 하죠. 하지만 계좌 상태는 여전히 오르내리는 하나의 숫자일 뿐입니다. 마진 잔액도, 명목가치 한도도, 파산 조건도 없습니다.

문제는 미묘해서 리뷰를 통과합니다. Sharpe는 규모에 영향을 받지 않으므로 레버리지를 3x에서 30x로 바꿔도 대표 수치는 그대로입니다. 연구자는 레버리지가 변동성을 수익으로 바꾸는 공짜 조절 장치라고 결론 내립니다. 최대 낙폭은 선형으로 커지지만, 산술상의 우연으로 100% 아래에 머뭅니다. 수익률에 배수를 곱한 시계열은 점근적으로 0에 가까워질 뿐 0을 넘지 않기 때문입니다. -9%인 날에 12를 곱하면 제대로 계산한 경우 -108%가 됩니다. 단순한 방식에서는 복리로 계산했는지 합산했는지에 따라 “-108%인데 자산 곡선은 여전히 양수”가 되기도 합니다. 둘 다 봤습니다.

빠진 점은 레버리지를 수익률에 적용하는 게 아니라는 겁니다. 레버리지는 담보에 적용되고, 담보는 거래소가 매초 확인하는 유한한 자산입니다. 담보가 유지증거금 요건 아래로 떨어지는 순간 포지션은 더 이상 내 것이 아닙니다.

판별 기준은 이렇습니다. 어떤 레버리지에서도 시뮬레이션이 전액 손실을 낼 수 없다면, 레버리지를 모델링한 게 아닙니다.

두 번째 잘못된 방식: 레버리지만으로 계산한 청산 가격

조금 더 정교한 방식에서는 청산 검사를 추가합니다. 보통 이런 식이죠. 20x에서는 반대 방향으로 5% 움직이면 마진이 사라지므로, 롱 포지션은 진입가 × 0.95에서 청산합니다. 깔끔하고 직관적이지만, 서로 다른 4가지 면에서 동시에 틀렸습니다.

무기한 선물 거래소는 레버리지 설정값으로 청산 시점을 정하지 않습니다. 포지션 명목가치가 커질수록 단계적으로 올라가는 유지증거금률을 티어 표에 따라 적용합니다. 거래소가 개정할 때마다 달라지는 BTCUSDT USDⓈ-M의 대략적인 형태는 다음과 같습니다.

포지션 명목가치 (USDT)유지증거금률최대 레버리지
0 – 50,0000.40%125x
50,000 – 600,0000.50%100x
600,000 – 3,000,0001.00%50x
3,000,000 – 12,000,0002.50%20x
12,000,000 – 70,000,0005.00%10x

따라서 격리 마진 롱 포지션이 견딜 수 있는 반대 방향 움직임은 대략 1/L − MMR이지, 1/L가 아닙니다. 두 번째 티어에서 20x라면 5% − 0.5% = 4.5%입니다. 0.5%는 반올림 오차처럼 들릴 수 있습니다. BTC가 84,000일 때는 가격 420달러에 해당하고, 급격한 움직임이 발생한 한 시간 동안의 420달러는 포지션을 청산시킨 꼬리와 청산시키지 않은 꼬리의 차이입니다. 모든 꼬리가 동전 던지기인데, 그 결과를 전부 자기 쪽으로 기울여 놓은 셈입니다.

그다음은 수수료입니다. taker 진입 수수료는 체결되는 즉시 마진에서 빠져나갑니다. 명목가치 500,000에 대한 0.045%는 225 USDT입니다. 마진 잔액이 25,000이라면 포지션이 움직이기도 전에 청산 가격이 바뀝니다. 펀딩도 같은 식으로 계속 반영됩니다. 레버리지 명목가치에 적용되므로 연구자들이 예상하는 것보다 훨씬 큽니다.

4.55%진입 수수료 포함, 20x에서 청산까지 허용되는 실제 반대 방향 움직임
0.60%20x에서 마진에 부과되는 일일 펀딩 부담, 0.01%/8h
18%움직임 없는 한 달 동안 소진되는 마진

30일 동안 아무 일도 하지 않은 포지션, 즉 불리한 가격 움직임도 거래도 없었던 포지션은 담보의 거의 5분의 1을 소진하고 청산 가격을 상당히 끌어당깁니다. 세 번째와 네 번째 오류는 이렇습니다. 포지션을 늘리면서 티어가 바뀝니다. 0.5% 티어에서 시작해 추가 매수로 1.0% 티어에 들어간 포지션은 진입 시 계산한 것보다 불리한 청산 가격을 갖게 됩니다. 또 교차 마진은 여러 포지션의 담보를 공유하므로 BTC 롱의 생존 여부는 ETH 숏의 움직임에도 달려 있습니다. 교차 마진을 서로 독립적인 격리 포지션으로 시뮬레이션한다면, 자기 계좌의 상관관계 구조를 거꾸로 이해한 것입니다.

세 번째 잘못된 방식: 잘못된 가격을 기준으로 잘못된 시점에 잘못된 체결가로 청산 처리하기

청산 발동 수준은 맞게 계산했다고 해봅시다. 그럼 이제 무엇을 기준으로 가격이 그 수준을 넘는지, 언제 확인하는지, 어떤 가격으로 체결되는지가 남습니다.

청산은 마크 가격에서 발동합니다. 여러 현물 거래소의 지수에 평활화된 베이시스 요소를 더한 값으로, 한 거래소에서만 발생한 꼬리에 좌우되지 않도록 설계되어 있습니다. 반면 손절은 설정에 따라 마지막 체결 가격을 기준으로 발동할 가능성이 높습니다. 가장 중요한 순간에 이 두 가격은 서로 달라집니다. 급락이 이어질 때 무기한 선물의 마지막 가격이 마크 가격보다 1–2% 낮은 상태로 수십 초간 벌어질 수 있습니다. 손절과 청산을 모두 같은 OHLCV 저가로 처리하는 백테스트는 존재하지 않는 거래소를 모델링하는 셈입니다.

여기서는 두 오류가 모두 실제로 나타나며 서로 상쇄되지 않습니다. 마지막 체결 가격을 기준으로 하면 거래소가 무시한 꼬리에서 청산됩니다. 마크 가격을 기준으로 하면 마크 가격 자체가 움직인 경우를 놓칩니다. 현물 거래소의 지수 괴리 때문에 내가 거래한 무기한 선물에서는 한 번도 형성되지 않은 가격에 청산될 수도 있습니다.

이제 체결 문제입니다. 단순한 엔진은 청산 가격에 포지션을 종료하고 손실을 손절처럼 기록합니다. 실제로는 청산 엔진이 포지션을 파산 가격에서 인수합니다. 마진이 정확히 0이 되는 수준으로, 발동 가격보다 불리합니다. 여기에 티어에 따라 달라지는 청산 처리 수수료가 추가로 부과되며, 낮은 티어에서는 명목가치의 약 1%입니다. 20x에서 명목가치의 1%는 남은 마진의 20%입니다. 엔진이 파산 가격보다 불리한 가격에 체결하면 보험 기금이 차액을 보전합니다. 일부 거래소에서는 기금이 고갈될 경우 손실이 이익을 내는 포지션 쪽으로 공동 분담됩니다. 실현 손실은 청산 가격까지의 낙폭이 아닙니다. 그보다 크고, 호가창이 얇아 손실이 커지기 쉬운 날에 초과 손실도 가장 커집니다.

장중 봉 처리와 관련된 시점 문제도 여기서 더 심각하게 나타납니다. 1-minute 봉 하나에 익절 가격과 청산 가격이 모두 포함되어 있다면, 마진을 확인하기 전에 청산 여부를 확인하는 백테스트는 태연하게 이익을 기록합니다. 거래소는 다른 어떤 이벤트보다 먼저, 대략 1초에 한 번씩 마크 가격이 업데이트될 때마다 마진을 확인합니다.

대신 엔진이 추적해야 하는 것들

복잡한 기능은 필요하지 않습니다. 상태를 추적하면 됩니다. 계좌는 수익률 시계열이 아니라 잔액을 가진 객체입니다. 매 봉마다 거래소와 같은 순서로 상태를 업데이트해야 합니다.

  1. 지갑 잔액과 미실현 PnL을 별도로 추적합니다. 마진 비율은 유지증거금을 마진 잔액으로 나눈 값이며, 마진 잔액에는 미실현 PnL이 포함됩니다. 두 항목을 혼동하면 손실 중인 포지션의 담보가 실제보다 충분해 보입니다.
  2. 모든 심볼의 티어 표를 날짜별로 관리합니다. 티어는 개정됩니다. 2023년 백테스트를 2026년 티어 기준으로 돌리면 미묘한 룩어헤드 편향이 생깁니다. 대체로 유리한 방향인데, 거래소들이 시간이 지나면서 주요 종목의 요건을 완화해 온 경우가 많기 때문입니다.
  3. 캔들 데이터만이 아니라 마크 가격 시계열을 사용합니다. 마크 가격 이력을 구할 수 없다면 결과에 그 사실을 밝히고 보수적인 대리값을 사용합니다. 마지막 체결 가격으로 조용히 대체해서는 안 됩니다.
  4. 봉 안에서 이벤트를 처리할 때 마진을 가장 먼저 확인합니다. 손절, 익절, 시그널, 리밸런싱보다 먼저입니다.
  5. 파산 가격 체결과 청산 처리 수수료를 반영합니다. 수수료율은 MMR과 같은 티어 행에서 가져옵니다.
  6. 되돌릴 수 없는 종료 상태를 둡니다. 자산이 0이 되면 실행을 끝냅니다. 재기준화하거나, “트레이더가 자금을 추가한다고 가정”하거나, 새 명목가치로 시계열을 이어가지 않습니다.

마지막 항목에서 논쟁이 가장 많이 벌어집니다. 실제 운용 데스크라면 담보를 더 넣을 테니 시뮬레이션을 끝내는 건 비현실적으로 가혹하다고 누군가는 꼭 말합니다. 그럴 수도 있습니다. 하지만 “망한 뒤에도 계속 자금을 넣어주면 이 전략은 작동한다”는 주장은 바로 그렇게 명시하고 의도적으로 테스트해야 합니다. 추가 자금을 명시적 입력값으로 넣어야지 기본값에 슬쩍 끼워 넣어서는 안 됩니다. 정직하게 적어 놓고 나면, 대부분은 원래 그런 뜻이 아니었다는 걸 알게 됩니다.

이 모든 작업의 실질적인 이점은 이렇습니다. 같은 전략을 실시간 거래소와 나란히 모의 실행할 때 거래소가 보고하는 마진 비율과 시뮬레이터가 계산하는 값이 하루 종일 1~2 베이시스포인트 이내로 따라가야 합니다. 이 일치는 매분, 모든 오픈 포지션에서 무료로 확인할 수 있습니다. 전체 스택에서 가장 저렴한 패리티 테스트인데도 거의 아무도 실행하지 않습니다. 두 수치가 벌어지면 거래소가 맞습니다. 낙폭이 먼저 문제를 드러내기 전에 찾아낼 가치가 있는 버그가 있다는 뜻입니다.

무기한 선물레버리지청산마진 엔진백테스팅
← 전체 글