남의 청산으로 돈 버는 전략 — 실시간 청산데이터 관찰하기
바이낸스 선물 강제청산 신호와 체결량 폭증을 결합해 청산 캐스케이드 역추세 매매봇을 만들었습니다. 백테스트가 불가능한 이 전략의 진짜 비용이 어디서 터지는지, 파라미터의 함정을 기록입니다.
유튜브 영상 '남들이 청산당해야 돈을 벌 수 있는 전략을 전격 공개합니다' 편에서 돌렸던 청산 캐스케이드 봇입니다. 이 글은 그 정리본에 영상에는 안 넣은 최소 실행 코드를 붙인 것입니다. 수익률은 영상에서 확인하시고 여기에 옮겨 적지는 않겠습니다. 제가 유리한 구간만 고르게 되니까요. 결론부터 말하면 이 전략은 '알파'를 찾는 과정이 아니라 '숨겨진 비용'의 구조를 파악하는 것에 가깝습니다. 특히 백테스트가 원천적으로 불가능해서요.
청산은 신호, 체결이 본체다
이 전략의 아이디어는 간단합니다. 누군가의 고배율 롱 포지션이 터지면(강제청산), 거래소는 그 물량을 시장가로 던져버립니다. 이 매도 폭탄은 가격을 추가로 하락시키고, 그 바로 아래 있던 다른 롱 포지션들을 연쇄적으로 터뜨립니다. 바로 청산 캐스케이드죠. 이 과정에서 가격이 '필요 이상으로' 과하게 빠지는 순간, 즉 오버슈팅이 발생했을 때 바닥에서 롱을 잡아 되돌림을 먹는 전략입니다.
그런데 함정은 바이낸스가 주는 청산 데이터에 있습니다. 청산 스트림(!forceOrder@arr)은 1초에 전 종목을 통틀어 '가장 큰 청산 1건'만 던져줍니다. 즉, 수많은 작은 청산들이 동시에 터져도 우리는 그 전체 규모를 알 수 없어요. 그래서 청산 데이터는 그저 '지금 뭔가 심상치 않은 일이 벌어졌다'는 신호로만 쓰고, 실제 판단은 모든 체결 내역이 실시간으로 쏟아지는 체결(aggTrade) 스트림으로 합니다. 최근 N초간 몇 %가 빠졌는지, 그리고 거래량이 평소의 몇 배로 터졌는지를 직접 계산하는 거죠.
아래 코드가 바로 그 판단 로직의 핵심입니다. 짧은 기간(shortBuf)과 긴 기간(longBuf)의 체결 데이터를 각각 큐(Queue)에 담아두고, 짧은 창의 거래량이 긴 창의 거래량을 기준으로 계산한 '기대 거래량'보다 얼마나 폭발했는지, 그리고 짧은 창의 고점 대비 현재 가격이 얼마나 떨어졌는지를 계산해 진입 여부를 결정합니다.
void TryEnter(double price, long tMs, Config cfg, BinanceFutures ex)
{
// 1. 짧은 창(예: 60초)의 고점 대비 얼마나 빠졌나?
double hi = 0;
foreach (double[] e in shortBuf) if (e[1] > hi) hi = e[1];
if (hi <= 0) return;
double drop = (hi - price) / hi * 100.0;
// 2. 거래량이 평소 대비 얼마나 폭발했나?
// 기준선(예: 30분) 거래량으로 이 짧은 창에 나와야 할 '기대 거래량'을 구한다. 그냥 산수다.
double expectShort = longNotional * ((double)cfg.WindowSec / cfg.BaselineSec);
// 실제 거래량이 기대치의 몇 배인지. 이게 거래량 폭발 지표.
double spike = expectShort > 0 ? shortNotional / expectShort : 0;
// 3. (옵션) 최근 N초간 쌓인 청산 규모가 최소치를 넘었나?
bool liqOk = !cfg.RequireLiq || liqNotional >= cfg.LiqMinUsdt;
if (drop >= cfg.DropPct && spike >= cfg.VolMult && liqOk)
{
// 슬리피지 함정. 시장가 주문은 여기서 계산한 가격보다 무조건 불리하게 체결된다.
double fillP = price * (1 + cfg.SlippagePct / 100.0);
qty = FloorStep(cfg.OrderUsdt / fillP);
if (qty < MinQty) qty = MinQty;
entryPrice = fillP; entryMs = tMs; inPos = true;
if (!cfg.DryRun) ex.MarketBuy(Symbol, FmtQty(qty));
// 조건이 맞으면 기도와 함께 시장가 주문을 던진다.
}
}백테스트가 불가능한 구조적 한계
이 전략의 가장 큰 특징은 과거 데이터로 백테스트를 할 수 없다는 점입니다. 바이낸스를 포함한 대부분의 거래소는 과거 강제청산 데이터를 API로 제공하지 않거든요. 그래서 전략을 검증하려면, 일단 실시간으로 청산 데이터를 수집하는 '수집기'를 몇 주, 몇 달 동안 돌려서 자신만의 데이터셋을 구축하는 것부터 시작해야 합니다. 제가 만든 봇에도 collect 모드가 따로 있는 이유입니다.
이건 단순히 불편한 점이 아니라 전략의 성격을 완전히 바꿔버립니다. 과거 데이터에 파라미터를 최적화하는 게 불가능하니까요. 대신 '이런 조건이면 통계적으로 되돌림이 나올 것이다'라는 가설을 세우고, 소액 실매매나 모의매매(dry run)로 그 가설을 라이브 시장에서 검증해야 합니다. 우리가 하는 건 백테스트가 아니라 미래를 예측하는 실험에 가까워집니다.
비용은 확정값이고 엣지는 추정값이다
청산 캐스케이드를 잡을 때 우리의 '엣지', 즉 가격이 되돌아올 것이라는 기대는 어디까지나 확률입니다. 하지만 우리가 지불하는 '비용'은 100% 확정된 값입니다. 바로 수수료와 슬리피지죠. 진입할 때 시장가(Taker)로 한번, 청산할 때 시장가로 또 한 번. 왕복 수수료는 무조건 나갑니다. 여기에 청산의 아수라장 속에서 주문을 던지면 호가창의 빈 공간을 뚫고 미끄러지며 체결되는 슬리피지 비용이 추가됩니다.
따라서 손익 계산은 항상 이 비용을 먼저 제하고 시작해야 합니다. 아래 코드는 포지션을 관리하고 수익을 계산하는 부분입니다. 총 손익(TotalPnl)을 계산할 때마다 수수료(FeeTaker)를 꼬박꼬박 빼주는 걸 볼 수 있습니다. 슬리피지는 예상 체결가에 미리 반영했고요. 이 두 가지 비용을 합치면, 차트 상으로는 0.3%를 먹은 것 같아도 실제 계좌에는 마이너스가 찍히는 일이 흔합니다.
void ManagePosition(double price, long tMs, Config cfg, BinanceFutures ex)
{
bool exit = false;
if ((tMs - entryMs) >= (long)cfg.HoldMinutes * 60000) exit = true;
if (cfg.TpPct > 0 && (price - entryPrice) / entryPrice * 100.0 >= cfg.TpPct) exit = true;
if (exit)
{
// 매도할 때도 슬리피지를 반영해 보수적으로 계산한다.
double closeP = price * (1 - cfg.SlippagePct / 100.0);
double gross = (closeP - entryPrice) * qty;
// 이익(gross)에서 수수료를 한 번 더 뺀다. 이게 최종 PnL이다.
TotalPnl += gross - cfg.FeeTaker * closeP * qty;
if (!cfg.DryRun) ex.MarketCloseLong(Symbol, FmtQty(qty));
inPos = false;
}
}
// 모든 청산 주문에는 reduceOnly=true 옵션을 건다.
public string MarketCloseLong(string symbol, string qtyStr)
{
string query = "symbol=" + symbol + "&side=SELL&type=MARKET&quantity=" + qtyStr + "&reduceOnly=true";
return SignedReq(HttpMethod.Post, "/fapi/v1/order", query);
// 실수로 포지션을 늘리는 걸 막는 최소한의 안전장치.
}왜 이게 실전에서 깨지는가
- 슬리피지를 이길 수 없습니다. 청산 캐스케이드는 변동성이 최고조인 순간이라 호가창이 비고 스프레드가 벌어집니다. 설정값 slippage_pct는 방어선일 뿐, 실제 체결가는 훨씬 나쁠 수 있습니다.
- 과최적화의 늪에 빠지기 쉽습니다. 백테스트가 안 되니 짧은 실매매 기간의 데이터로 파라미터를 맞추게 됩니다. 이건 시장에 맞춘 게 아니라 내 계좌의 운에 맞춘 겁니다.
- 진짜 지하실은 돌아오지 않습니다. 이 전략은 '일시적 과매도' 후 반등을 노립니다. 하지만 시장 전체를 무너뜨리는 진짜 악재로 인한 폭락은 반등 없이 그대로 흘러내립니다. 둘을 구분할 방법이 없습니다.
- 거래 비용이 생각보다 훨씬 큽니다. 진입과 청산 모두 시장가(Taker) 수수료를 냅니다. 여기에 슬리피지까지 더하면, 목표 익절 폭이 0.5%여도 실제로는 남는 게 거의 없을 수 있습니다.
- 기회비용이 너무 높습니다. 대규모 청산은 매일 오는 이벤트가 아닙니다. 봇은 대부분의 시간을 아무것도 안 하고 기다립니다. 그동안 자본은 다른 기회를 놓치게 됩니다.
- 데이터의 편향에 속게 됩니다. 청산 스트림은 '가장 큰 1건'만 보여줍니다. 수많은 작은 청산들이 연쇄적으로 터지는 상황은 데이터에 잡히지 않아, 시장의 실제 압력을 과소평가할 수 있습니다.
이 글과 첨부 코드는 전략 구조를 설명하려고 만든 교육·연구 자료입니다. 특정 종목이나 매매를 권하지 않습니다.
첨부파일
영상에 사용된 소스코드와 설명서입니다. 로그인 없이 받을 수 있습니다.
청산 캐스케이드 C# 봇 전체 소스