지표 3개 붙인 다중자산 롱숏 전략 — 왜 수수료에 깨졌나
ATR, EMA, RSI 지표 필터를 더한 롱숏 전략을 20개 코인에서 동시에 돌려봤습니다. 승률은 높았지만 왜 수수료에 녹았는지, 다중 코인 운영의 구조적 비용을 기록했습니다.
영상에서는 지표 필터를 추가하고 20종목으로 늘린 롱숏봇을 돌려봤습니다. 이 글은 그 정리본에 영상에는 안 넣은 핵심 실행 코드를 붙인 것입니다. 결론부터 말하면, 승률은 그럴싸하게 나왔지만 결국 수수료에 완패했습니다. 낚싯대를 많이 던지면 고기가 더 잡히는 게 아니라 떡밥값만 더 나간다는 걸 배웠거든요. 실제 제 계좌의 수익률 숫자는 여기에 옮겨 적지 않겠습니다. 제가 유리한 구간만 골라서 보여주게 되니까요.
본체는 방향성이 아니라 변동성
이 전략의 핵심은 업그레이드 전과 같습니다. 롱과 숏을 동시에 잡아서 방향 리스크를 없앤 상태로 시작합니다. 예전에는 가격이 벌어지면 한쪽을 정리했지만, 이번에는 세 가지 지표 필터를 추가했습니다. ATR로 시장이 뜨거워졌을 때만 진입하고, EMA 골든/데드크로스와 RSI로 방향을 확정한 뒤, 진 쪽 포지션을 정리하는 구조입니다.
즉, 이건 '오를까 내릴까'를 맞추는 게임이 아닙니다. '지금 크게 움직일까 아닐까'에 먼저 베팅하는 변동성 전략입니다. 시장이 조용하면 아무것도 안 하다가, 변동성이 터지면 일단 양방향으로 진입해 자리를 잡고 그 뒤에 방향을 결정하는 거죠. 이론상으로는 아주 안정적이어야 합니다.
낚싯대 20개의 명과 암
이번 버전의 필살기는 20개 코인을 동시에 돌리는 것이었습니다. 낚싯대를 하나만 던지는 게 아니라, 유동성 좋은 코인 20개에 똑같은 봇을 동시에 돌려서 어디서든 변동성이 터지면 기회를 잡겠다는 계산이었죠. 하나의 초대박이 다른 19개의 자잘한 손실을 다 덮어줄 거라는 기대였습니다.
그런데 현실은 반대였습니다. 19개의 자잘한 손실 누적이 하나의 수익을 갉아먹는 그림이 더 자주 나왔습니다. 시장 대부분은 방향성 없는 휩쏘 구간이잖아요. 20개 코인이 돌아가니, 이 휩쏘 구간에서 '어, 변동성 터졌다!' 하고 헛다리 짚는 횟수도 20배가 된 겁니다. 모든 진입과 청산에는 수수료가 붙고요. 이게 패인이었습니다.
전체 로직의 핵심은 바로 이 상태머신입니다. '대기(FLAT) → 양방향 진입(BOTH) → 방향 결정 후 한쪽만(ONE) → 청산 후 대기(FLAT)' 사이클을 계속 반복합니다.
// 상태: 0=대기, 1=양방향, 2=한방향
if (state == FLAT)
{
// 진입 조건: 지금 변동성(atrNow)이 평소(atrAvg)보다 크다
bool hot = atrNow > atrAvg * cfg.AtrMult;
if (hot)
{
// 양방향 동시 진입. 여기서부터 수수료 카운트 시작.
state = BOTH;
// ... MarketOrder(LONG), MarketOrder(SHORT) ...
}
}
else if (state == BOTH)
{
// 방향 결정: EMA 크로스 + RSI 조건 맞으면
if (goldenCross && rsi >= cfg.RsiUp)
{
winnerIsLong = true;
state = ONE; // 롱 승, 숏 청산 (수수료 또 발생)
}
else if (deadCross && rsi <= cfg.RsiDown)
{
winnerIsLong = false;
state = ONE; // 숏 승, 롱 청산
}
}
else if (state == ONE)
{
// 청산 조건: 트레일링, 손절, 변동성 죽음 등
if (close_condition_met)
{
// 남은 포지션 청산 (네 번째 수수료)
state = FLAT;
}
}
// 이 간단한 상태머신이 돈을 다 까먹었다.승률이 아니라 손익비가 문제다
이 전략은 구조적으로 승률이 높게 찍히기 쉽습니다. 두 포지션 중 하나를 비교적 빨리 정리하고, 이긴 포지션이 조금이라도 수익을 내면 익절로 끝나니까요. 하지만 이건 통계의 착시입니다. 10번 이겨서 번 돈을 단 한 번의 손절로 다 날리는 그림이 나오기 때문입니다.
결국 중요한 건 승률이 아니라 손익비였습니다. 우리는 이기는 매매를 하려는 게 아니라 돈을 버는 매매를 해야 하잖아요. '작게 자주 먹고 한 번 크게 잃는' 구조는 장기적으로 계좌를 우하향시킬 뿐입니다. 수수료는 이 우하향 그래프의 기울기를 더 가파르게 만들고요. 모든 비용은 수익 이전에 확정적으로 빠져나가니까요.
한 라운드가 손실로 끝나는 가장 흔한 경우는 '변동성 죽음'으로 양쪽을 동시에 청산할 때입니다. 이때 손익을 어떻게 계산하는지 보면 비용 구조가 한눈에 보입니다.
// 양쪽 다 청산 (변동성 죽음 / 강제 종료)
void CloseBoth(double bid, double ask, Config cfg, string why)
{
// 롱 포지션은 매수 호가(bid)에 팔고
roundPnl += (bid - entryLong) * qty - cfg.Fee * qty * bid;
// 숏 포지션은 매도 호가(ask)에 사서 갚는다
roundPnl += (entryShort - ask) * qty - cfg.Fee * qty * ask;
Log("양쪽 청산 (" + why + ")");
if (!cfg.DryRun)
{
ex.MarketOrder(Symbol, "SELL", "LONG", FmtQty(qty));
ex.MarketOrder(Symbol, "BUY", "SHORT", FmtQty(qty));
}
FinishRound(); // 라운드 성적 기록
}
// 진입2 + 청산2 = 총 4번의 수수료. 이게 기본빵이다.왜 이게 실전에서 깨지는가
- 수수료가 엣지보다 큽니다. 한 사이클당 최소 4번의 테이커 수수료와 슬리피지를 감당할 만큼의 수익 모델이 아니었습니다.
- 잦은 휩쏘에 속았습니다. '뜨거워지는' 신호 대부분이 진짜 추세가 아니라 잠깐의 노이즈였고, 그때마다 진입-청산 비용만 발생했습니다.
- 다중 종목이 오히려 독이 됐습니다. 종목을 늘린 게 기회를 늘린 게 아니라 비용 발생 횟수만 20배로 늘린 셈이 됐습니다.
- 모든 지표는 후행성입니다. EMA 크로스, RSI 같은 지표는 이미 움직임이 어느 정도 나온 뒤에 신호를 줍니다. 가장 비싼 가격에 추격하게 될 수 있습니다.
- 과최적화의 저주를 피할 수 없습니다. 15분봉, ATR 100개 평균, RSI 60/40 같은 값들은 제가 테스트한 과거 데이터에만 잘 맞는 옷일 뿐, 미래를 보장하지 않습니다.
- 슬리피지가 계산되지 않았습니다. 모의체결은 항상 최적가에 되지만, 실전에서 시장가 주문은 무조건 불리한 가격에 밀려서 체결됩니다. 이 차이가 쌓이면 무시할 수 없습니다.
이 글과 첨부 코드는 전략 구조를 설명하려고 만든 교육·연구 자료입니다. 특정 종목이나 매매를 권하지 않습니다.
첨부파일
영상에 사용된 소스코드와 설명서입니다. 로그인 없이 받을 수 있습니다.
멀티코인 롱숏봇 전체 소스 (C#)