지표가 아니라 심사 기준
거래소 앱을 열면 종목마다 “24시간 거래대금"이 붙어 있다. 이건 엄밀히 말해 매매 신호를 주는 지표가 아니다. 이 종목을 내가 사고팔 수 있느냐를 묻는 심사 기준에 가깝다.
내 봇에서 이 숫자가 하는 일도 딱 그거다. 스캔을 시작하기 전에 후보 목록 자체를 잘라낸다. 조건이 아무리 완벽해도 유동성이 없는 종목은 아예 목록에 안 올린다. 게이트 열 개를 통과시키는 것보다 먼저 하는 일이다.
① 전 종목 조회 거래소 상장 목록 전부
② 유니버스 필터 ◀ 여기가 24시간 거래대금
거래대금 하한 미달 → 제외
가격 하한 미달 → 제외 (호가 단위 문제)
③ 지표 계산 살아남은 종목만
④ 게이트 판정 10개 조건
⑤ 랭킹 → 진입
②에서 자르면 ③~⑤ 의 계산량도 함께 줄어든다 — API 호출 절약이라는 부수 효과
②를 건너뛰면 ④에서 통과한 종목이 ⑤에서 체결이 안 되는 사고가 난다거래량과 거래대금은 다른 말이다
용어부터 정리하고 가자. 차트 아래 막대는 보통 거래량(수량)인데, 종목을 가로질러 비교할 때 의미가 있는 건 거래대금이다.
거래량(수량) 몇 개가 거래됐나
BTC 1개와 200원짜리 코인 1개가 같은 '1' 로 계산된다
종목 간 비교에 쓸 수 없다
거래대금(금액) 얼마어치가 거래됐나 = 수량 × 가격
종목 간 비교가 가능하다 — 유니버스 필터에 쓰는 건 이쪽
거래소 API 에서
volume 기초자산 수량 (base volume)
quote_volume 원화 환산 금액 (quote volume) ← 대개 이걸 쓴다
직접 계산할 때 주의: 수량 × 종가 는 근사치다
봉 안에서 가격이 움직였다면 실제 체결 금액과 다르다
거래소가 quote_volume 을 주면 그걸 쓰는 게 정확하다이 구분을 안 하면 유니버스 필터가 이상하게 작동한다. 수량 기준으로 하한을 걸면 가격이 낮은 코인만 통과하게 되는데, 그건 필터가 아니라 저가주 선별기다.
같은 실수를 나도 다른 형태로 했다. 신호 게이트에 절대 거래대금 기준을 썼다가 “게이트가 종목을 거르는 게 아니라 체급을 차별한다"는 지적을 받은 적이 있다. 유니버스 필터에는 절대 금액이 맞고, 신호 게이트에는 자기 평균 대비 배수가 맞다. 같은 재료라도 어느 자리에 쓰느냐에 따라 기준이 달라진다.
왜 하필 24시간인가
1시간도 아니고 7일도 아니고 왜 24시간일까. 이유는 코인 시장의 구조에 있다.
주식 시장은 개장과 폐장이 있어서 “하루치 거래량"이라는 게 자연스러운 단위다. 코인은 24시간 돌기 때문에 그런 경계가 없다. 그래서 롤링 24시간 — 지금 이 순간부터 정확히 하루 전까지 — 을 쓴다. 언제 조회하든 같은 길이의 창을 보게 된다.
1시간 너무 예민하다. 펌핑 한 번에 순위가 통째로 뒤집힌다
"방금 누가 크게 샀다" 는 알 수 있지만 "평소 거래가 되는 종목인가" 는 모른다
24시간 하루 안의 시간대 편차를 한 바퀴 흡수한다
한국 저녁의 활발함과 새벽의 한산함이 같은 창 안에 들어간다
→ 조회 시각에 따라 값이 크게 안 흔들린다
7일 너무 둔하다. 사흘 전에 죽은 종목이 아직 살아 있는 것처럼 보인다
상장 폐지 예정·거래 중단 직전 종목을 못 거른다
24시간이 표준이 된 건 관습이 아니라 시간대 편차를 한 주기 담기 때문이다시간대 편차 이야기는 내 봇에서도 실측한 적이 있다. 스캔 기록 1,116건을 시간대별로 갈라 성과를 봤더니 저녁 시간대(한국 시각 1923시)가 뚜렷하게 나빴다. 19시 -0.752%, 20시 -0.806%, 21시 -0.716%였고 승률도 1020%대였다. 거래가 제일 활발한 시간이 성과가 제일 나빴던 것이다.
거래량이 많다는 건 참여자가 많다는 뜻이지 내 전략이 잘 통한다는 뜻이 아니다. 이 구분을 놓치면 “붐빌 때 공격적으로"라는 직관적이지만 틀린 설계를 하게 된다. 실제로 처음엔 그렇게 만들었다가 데이터에 뒤집혔다.
이 숫자가 거짓말하는 세 가지 경우
24시간 거래대금은 유용하지만 만능이 아니다. 이 숫자가 실제 유동성을 과대평가하는 경우가 있다.
① 몰린 거래량
하루 총액은 큰데 대부분이 특정 몇 분에 몰려 있다
그 시간을 벗어나면 호가창이 텅 빈다 → 지금 이 순간엔 못 산다
② 넓은 호가
거래대금은 기준을 넘는데 호가 한 칸이 가격의 0.2% 를 넘는다
사는 순간 이미 마이너스로 시작한다 — 거래대금만으론 안 잡힌다
③ 세탁 거래
같은 주체가 자기 물량을 주고받아 만든 숫자
호가창의 실제 두께와 무관하다
대응: 거래대금은 1차 관문일 뿐. 호가 기반 지표를 2차로 세운다②번은 내 봇이 실제로 물린 함정이다. 앞서 말한 본전 청산 27건 실측이 그 결과인데, 저가 코인의 호가 한 칸이 가격의 0.2%를 넘어 청산선이 호가 사이 허공에 놓이는 일이 있었다. 이걸 겪고 나서 보정을 한 번 하고(본전 바닥 0.4%로 상향), 이틀 뒤엔 아예 구조적으로 막았다. 호가 한 칸이 가격의 0.15%를 넘는 종목은 진입 금지라는 게이트를 신설한 것이다.
이 순서가 중요하다고 생각한다. 사고 → 실측 → 원인 규명 → 임시 보정 → 구조적 예방. 나흘 걸렸다. 문제를 겪는 시스템에서 문제를 만나지 않는 시스템으로 옮겨가는 과정이 이런 모양이다.
거래대금 순위표를 믿으면 안 되는 이유
거래소 앱에는 거래대금 순위가 있다. 위에서부터 몇 개를 골라 매매하는 사람도 많다. 그런데 이 순위표에는 성격이 전혀 다른 종목들이 섞여 있다.
① 원래 큰 종목
시가총액이 크고 평소에도 거래가 많다. 순위가 잘 안 바뀐다
→ 안정적. 다만 변동성이 작아 눌림목 전략의 먹잇감은 적다
② 오늘 터진 종목
호재나 펌핑으로 하루 만에 거래가 폭증. 어제는 순위 밖이었다
→ 유동성은 지금만 있다. 내일이면 사라질 수 있다
③ 신규 상장
상장 직후 며칠은 거래가 몰린다. 과거 데이터가 아예 없다
→ 지표 계산 자체가 불가능하다 (워밍업 봉이 안 쌓임)
셋을 구분하려면 24시간 값 하나로는 부족하다
거래대금의 최근 7일 대비 배수를 같이 보면 ②가 분리된다
상장 경과일 필터를 두면 ③이 분리된다③번은 봇에서 특히 조용하게 사고를 낸다. 상장한 지 얼마 안 된 종목은 봉이 부족해서 지표가 이상한 값을 뱉거나 결측이 나오는데, 이걸 걸러내지 않으면 판정 로직이 예상 못 한 경로로 들어간다. 조건문이 결측을 만나면 어느 쪽으로 튈지는 코드를 짠 사람도 모르는 경우가 많다.
내가 무결성 검사에 “미정의 참조 0건"을 항목으로 넣어둔 이유도 이 계열이다. 크래시가 나면 오히려 다행이고, 조용히 잘못된 값으로 진행되는 게 제일 무섭다.
하한선은 얼마가 적당한가
내 봇의 거래대금 하한은 몇 번 움직였다. 처음엔 50억으로 잡았는데 평가 가능한 종목이 10개 남짓밖에 안 됐다. 후보가 열 개면 게이트 열 개를 통과하는 종목이 사실상 안 나온다. 그래서 10억으로 내렸고, 평가 종목이 40~50개로 늘었다.
하한 높음 후보 적음 · 유동성 좋음 · 체결 확실 · 기회 부족
50억 기준일 때 평가 대상 ~10 종목 → 게이트 통과 사실상 0
하한 낮음 후보 많음 · 유동성 나쁨 · 슬리피지 · 미체결 위험
현행은 거래대금과 가격 하한을 함께 걸어 균형을 잡는다
핵심: 하한선은 독립 변수가 아니다
게이트가 몇 개인지, 슬롯이 몇 개인지, 계좌 규모가 얼마인지에 따라 최적점이 움직인다
슬롯 4개짜리 봇은 후보가 20개만 있어도 돌아간다
슬롯 10개짜리 봇이었다면 같은 하한선에서 굶었을 것이다실제로 슬롯을 10개에서 4개로 줄인 결정과 유니버스 하한 조정은 같은 시기에 맞물려 있었다. 슬롯이 적으면 후보도 적어도 되고, 슬롯당 금액이 커지니 호가가 넓은 종목의 부담도 커진다. 파라미터 하나만 떼어놓고 “이 값이 적당한가"를 논하는 게 왜 공허한지 보여주는 사례다.
유니버스가 작다는 것의 의미
내 봇이 실제로 다루는 종목 수를 적어두면 이 글의 다른 얘기들이 더 잘 이해될 것이다. 필터를 통과하고 나면 스캔 대상은 수십 개 수준이다. 업비트 원화 마켓 전체에서 상당수가 걸러진다는 뜻이다.
유니버스가 20~50 종목일 때
① 격리 규칙이 치명적이 된다
손절한 종목을 하루 격리하면 후보의 상당 비율이 하루 사라진다
실제로 격리 기간을 하루 → 0 으로 푼 적이 있다
포트 익절이 4슬롯을 한꺼번에 털면 후보 4개가 동시에 빠지기 때문
② 게이트를 겹칠수록 통과가 0 에 수렴한다
50 종목에 10 게이트면 전부 통과하는 종목이 없는 날도 생긴다
③ 시간대 차단 같은 규칙의 비용이 커진다
하루 5시간을 막으면 기회의 20% 가 사라진다
그래서 이 봇의 규칙들은 전부 '유니버스가 작다' 는 전제 위에 서 있다
종목이 수천 개인 시장이었다면 완전히 다른 설계가 나왔을 것이다①번은 실제로 겪은 일이다. 손절한 종목을 하루 격리하는 규칙을 넣었는데, 포트폴리오 익절로 네 슬롯을 한꺼번에 수확하면 그 네 종목이 동시에 격리 목록에 들어갔다. 유니버스가 작으니 그다음 스캔에서 살 게 없었다. 그래서 격리를 아예 0으로 풀었다가, 나중에 “손절한 종목만 그날 자정까지"라는 절충안으로 다시 정리했다.
이 사례가 보여주는 건 규칙 하나가 독립적으로 존재하지 않는다는 것이다. 격리 기간은 유니버스 크기의 함수이고, 유니버스 크기는 거래대금 하한의 함수이고, 하한은 슬롯 수와 계좌 규모의 함수다. 하나를 바꾸면 나머지가 다 흔들린다.
계좌 규모라는 숨은 변수
한 가지 더. 24시간 거래대금 기준은 내 주문 크기와의 상대적 관계로 봐야 한다.
하루 10억이 거래되는 종목에 10만 원을 넣는 것과 1억을 넣는 것은 완전히 다른 일이다. 전자는 호가창에 흔적도 안 남지만 후자는 자기가 자기 가격을 밀어 올린다. 흔히 하루 거래대금의 일정 비율 이하로 주문을 제한하는 원칙을 쓰는데, 소액 계좌에서는 이게 거의 제약이 안 된다.
소액 계좌 내 주문이 시장에 영향 없음
→ 거래대금 기준의 목적은 '체결 가능성' 과 '호가 폭' 확보
→ 하한선을 비교적 낮게 잡아도 된다
중대형 계좌 내 주문이 호가를 움직임
→ 목적이 '시장 충격 회피' 로 바뀐다
→ 하한선이 훨씬 높아지고, 분할 주문 설계가 필요해진다
같은 지표, 같은 종목, 다른 판정 — 계좌 규모가 기준을 바꾼다
내 봇은 전자다. 그래서 이 글의 숫자들을 그대로 가져다 쓰면 안 된다스프레드와 함께 봐야 완성된다
거래대금이 1차 관문이라면 2차 관문은 스프레드다. 매수 최우선 호가와 매도 최우선 호가의 간격 말이다.
스프레드 좁음 스프레드 넓음
거래대금 큼 ✓ 최적 △ 이상 신호
정상 종목 거래는 많은데 호가가 비었다?
세탁 거래 의심 구간
거래대금 작음 △ 조용한 종목 ✗ 최악
지금은 한산할 뿐 사는 순간 손해로 시작
절대 건드리면 안 되는 칸
내 봇의 대응
유니버스 단계 — 거래대금 하한 + 가격 하한
게이트 단계 — 스프레드 상한 (9번째 게이트로 승격)
게이트 단계 — 호가 한 칸 / 가격 비율 0.15% 상한
세 겹을 두는 이유: 각각이 못 잡는 경우가 다르기 때문이다오른쪽 위 칸(거래대금은 큰데 스프레드가 넓다)이 흥미롭다. 논리적으로 잘 안 어울리는 조합인데, 실제로 나타나면 거래대금 숫자를 의심해볼 만하다. 하루 종일 활발히 거래된 종목의 호가창이 비어 있을 이유가 별로 없기 때문이다.
스프레드를 게이트로 올린 건 원래 계획에 없던 일이었다. 터미널 로그에 “조건은 다 통과했는데 왜 안 샀지?” 하는 종목이 자꾸 보여서 추적해보니, 게이트 밖에서 스프레드 조건에 막히고 있었다. 판정은 하는데 화면에 안 보이니 원인을 모르고 있었던 것이다. 그래서 아예 정식 게이트로 승격시켜 통과율 통계에 포함시켰다.
보이지 않는 조건은 없는 조건보다 나쁘다. 있는데 안 보이면 디버깅이 불가능해진다.
거래대금과 변동성의 관계
한 가지 자주 오해되는 게 있다. 거래대금이 큰 종목이 안전하다는 생각이다.
거래대금이 크다는 건 체결이 쉽다는 뜻이지 가격이 안정적이라는 뜻이 아니다. 오히려 하루 만에 거래가 폭증한 종목은 대체로 그날 크게 움직인 종목이다. 거래가 몰리는 이유가 가격이 움직였기 때문이니 당연하다.
그래서 거래대금 필터는 변동성 필터를 대신하지 못한다. 둘은 다른 축이고, 각각 따로 세워야 한다. 내 봇에도 변동성 조건이 별도로 있는데, 하한과 상한이 함께 있다. 너무 조용한 종목은 움직임이 없어서 못 먹고, 너무 요동치는 종목은 손절선이 의미를 잃기 때문이다.
이 상하한 구조를 처음부터 알고 만든 건 아니다. 박스장에서 진입이 막혔을 때 변동성 하한을 계속 내렸는데, 그러다 보니 이번엔 정상 범위를 한참 벗어난 종목까지 들어왔다. 한쪽을 풀면 다른 쪽이 새는 구조였다. 조건을 하나 완화할 때는 그 조건이 막고 있던 게 무엇이었는지 같이 봐야 한다는 걸 그때 배웠다.
하한선을 정할 때 실제로 해본 것
값을 정하는 절차를 구체적으로 남겨두는 게 이 글에서 제일 쓸모 있는 부분일 것 같다. 거래대금 하한을 정할 때 나는 성과가 아니라 후보 개수부터 봤다.
① 후보 개수 곡선을 먼저 그린다
하한값을 바꿔가며 통과 종목 수를 센다 (성과는 아직 안 본다)
50억 → 약 10종목 · 10억 → 40~50종목
② 최소 필요 후보 수를 정한다
슬롯 4개 · 게이트 10개 → 후보가 최소 20~30 은 돼야 통과가 나온다
이건 성과 문제가 아니라 봇이 굶느냐 마느냐의 문제
③ 그 범위 안에서만 성과를 비교한다
후보가 10개인 구간은 성과를 볼 것도 없이 탈락
④ 호가·가격 조건을 별도로 세운다
거래대금 하한을 낮춘 대가는 다른 축에서 막는다
핵심: 성과 최적화 이전에 '봇이 돌아가는 범위' 를 먼저 확정한다①번을 먼저 하는 이유가 있다. 후보가 10개인 상태에서 성과를 재면 표본이 너무 적어 아무 결론도 안 나온다. 성과 비교는 봇이 정상 작동하는 구간 안에서만 의미가 있다.
이 순서를 몰라서 시간을 버린 적이 있다. 하한을 높게 잡아놓고 “왜 성과가 안 나오지” 하며 다른 파라미터를 만졌는데, 실은 진입 자체가 거의 없어서 통계가 무의미했던 것이다. 문제를 엉뚱한 데서 찾고 있었다.
표본이 없으면 최적화도 없다. 이건 이 봇의 개발 전체를 관통하는 제약이다. 조건을 조일수록 성과는 좋아 보이지만 표본이 마르고, 표본이 마르면 그 판단이 맞는지 확인할 수 없게 된다. 시간대 차단 규칙을 해제한 것도 결국 같은 이유였다.
실전 체크리스트
이 글에서 다룬 것들을 봇에 넣을 때 확인할 항목으로 정리해둔다.
□ 거래대금 하한이 내 슬롯 크기·게이트 개수와 함께 정해졌는가
후보가 몇 개 남는지 실제로 세어봤는가 (10개 남으면 봇이 굶는다)
□ 가격 하한이 있는가
저가 코인의 호가 단위 문제를 거래대금만으로는 못 잡는다
□ 호가 기반 조건이 별도로 있는가
스프레드 상한 · 호가 한 칸/가격 비율 — 거래대금과 다른 것을 잰다
□ 상장 경과일 조건이 있는가
지표 워밍업이 안 된 종목은 판정 자체가 무의미하다
□ 필터에 걸린 종목이 로그에 남는가
왜 안 샀는지 나중에 추적 못 하면 튜닝이 감으로 돌아간다
□ 유니버스 크기를 매 스캔마다 기록하는가
어느 날 갑자기 후보가 줄었을 때 원인을 시장 탓/코드 탓으로 가를 수 있다마지막 두 항목이 실전에서 제일 자주 빠진다. 필터는 조용히 일하기 때문에 잘 작동하는지 아닌지가 안 보인다. 그러다 “이틀째 매수가 없다” 같은 상황이 오면 원인을 못 찾는다.
내 봇도 그 상황을 겪고 나서 게이트별 통과율을 매 스캔마다 출력하게 바꿨다. 돌파 3/15, 거래량 8/15 같은 식으로 찍어주니 어느 관문에서 막히는지 바로 보였다. 측정하지 않는 필터는 튜닝할 수 없다.
호가 한 칸이라는 진짜 관문
거래대금이 유동성의 대리 지표라면, 호가 단위는 그 유동성의 해상도다. 이 개념이 내 봇에서 어떻게 게이트가 됐는지 자세히 적어둘 가치가 있다.
업비트는 가격대별로 호가 단위가 정해져 있다. 가격이 낮을수록 단위도 작아지지만, 비율로 보면 저가 코인이 훨씬 불리하다.
호가 단위 / 현재가 × 100 = 한 칸이 차지하는 비율
10,000원대 종목 한 칸이 가격의 0.1% 미만 — 여유롭다
1,000원대 종목 0.1% 안팎
443원 종목 한 칸이 0.226% (실측 사례)
100원대 종목 더 나빠진다
이게 왜 문제인가
퍼센트로 설계한 청산선이 호가와 호가 사이 허공에 놓인다
+0.2% 에 바닥을 뒀는데 한 칸이 0.226% 면 그 가격은 존재하지 않는다
→ 한 칸 아래에서 체결되거나 아예 체결이 안 된다
현행 대응: 호가 한 칸 / 가격 비율이 0.15% 를 넘으면 진입 금지이 게이트가 만들어진 과정이 개인적으로 가장 만족스러운 사례 중 하나다. 순서가 이랬다.
먼저 이상 징후를 봤다. 본전 사수 장치가 이익을 지키라고 만든 건데 실제로는 손실이 나고 있었다. 그다음 실측을 했다. 본전 청산 27건을 뽑아보니 바닥을 +0.2%에 뒀는데 평균 실현이 +0.017%, 합계로는 마이너스였다. 원인 규명 단계에서 슬리피지를 의심했다가 아니라는 걸 확인했고, 호가 관통이 범인이었다. 관통 손실 0.183%p에 왕복 수수료 0.10%를 더하면 손익분기가 0.283%였다.
그래서 1차로 값을 보정했다. 바닥을 0.4%로 올리고 무장선도 0.8%로 함께 올렸다. 간격이 좁으면 무장하자마자 청산돼서 승자를 못 키우기 때문이다. 60건을 재현해보니 본전 손익이 −33,203원에서 +44,906원으로 뒤집혔다.
여기까지가 보정이고, 이틀 뒤에 구조적 예방을 넣었다. 애초에 호가가 넓은 종목을 안 사는 게이트를 만든 것이다. 문제를 겪고 대응하는 시스템에서 문제를 안 만나는 시스템으로 옮긴 셈이다.
1일차 이상 징후: 본전 사수가 적자를 내고 있다
2일차 실측 27건 — 바닥 +0.2% 설계, 평균 실현 +0.017%, 합계 −18,144원
2일차 원인 규명 — 슬리피지 아님. 호가 관통 0.183%p
2일차 보정 — 바닥 0.4% / 무장 0.8%, 60건 재현으로 확인
4일차 구조화 — 호가단위 게이트 신설 (0.15% 상한)
이 파이프라인의 속도가 개발 체계의 성적표라고 생각한다
감으로 고치면 하루면 되지만 같은 문제가 다시 온다
측정하고 고치면 나흘 걸리는 대신 다시 안 온다이 필터가 막아준 것들
거래대금 필터의 성과는 눈에 잘 안 띈다. 막아낸 사고는 일어나지 않았으니 기록에도 안 남기 때문이다. 그래도 정황으로 짐작되는 건 있다.
내 봇 이력에서 유동성 관련 사고는 대부분 필터가 약했던 시기에 몰려 있다. 저유동성 코인에서 매도 주문이 체결되지 않았는데 봇은 팔렸다고 판단해 포지션 목록에서 지워버린 일이 있었다. 거래소에는 물량이 남아 있고 봇은 모르는 상태 — 이른바 유령 포지션이다. 이걸 겪고 나서 체결 확인 폴링을 넣었고, 거래소 보유분과 봇 상태를 대조해 자동 복구하는 로직도 붙였다.
사고 자체는 집행 로직의 문제였지만, 애초에 그 종목이 후보 목록에 없었다면 일어나지 않았을 일이다. 필터는 사고를 막는 게 아니라 사고가 일어날 수 있는 표면적을 줄인다. 그래서 성과 측정이 어렵고, 그래서 자주 과소평가된다.
체결 확인 — 유동성 문제의 마지막 방어선
필터를 아무리 잘 세워도 뚫리는 경우가 있다. 그래서 마지막 방어선이 하나 더 필요하다. 주문을 냈으면 체결됐는지 확인하는 것이다.
당연한 얘기 같지만 초기 코드에서는 이걸 안 했다. 주문 API가 성공 응답을 주면 체결된 것으로 간주했다. 그런데 주문 접수와 체결은 다른 사건이다.
잘못된 흐름
① 매도 주문 전송
② 응답에 주문번호가 있음 → "팔렸다" 고 판단
③ 봇의 보유 목록에서 해당 종목 제거
④ 그런데 실제로는 체결 안 됨 (호가에 물량이 없어서)
⑤ 거래소엔 물량이 남아 있고 봇은 모른다 = 유령 포지션
고친 흐름
① 매도 주문 전송
② 주문 상태를 반복 조회 (체결 폴링)
③ 체결 확인되면 보유 목록에서 제거
④ 일정 시간 내 미체결이면 목록 유지 + 로그
추가 방어: 계좌 보유분과 봇 상태를 주기적으로 대조
거래소엔 있는데 봇 목록에 없는 종목 → 자동으로 다시 편입이 사고는 저유동성 코인에서 났다. 거래대금 하한을 겨우 넘긴 종목이었고, 매도 시점에 호가창이 비어 있었다. 필터가 약했던 게 1차 원인이고, 체결 확인을 안 한 게 2차 원인이다.
여기서 배운 게 방어를 한 겹만 두면 안 된다는 것이었다. 유니버스 필터가 뚫려도 게이트가 막고, 게이트가 뚫려도 체결 확인이 잡고, 그것도 놓치면 원장 대조가 잡는다. 각 층이 못 잡는 경우가 서로 다르기 때문에 겹쳐야 의미가 있다.
그리고 이 사고 이후 원칙이 하나 생겼다. 봇의 기억보다 거래소의 기록을 믿는다. 봇이 “팔았다"고 생각하는 것과 거래소가 “체결됐다"고 말하는 것이 다를 때, 거래소 쪽이 정답이다. 손익 계산의 근거를 봇 메모리에서 거래소 주문내역으로 옮긴 것도 같은 맥락이다.
시간대별 거래량 — 언제 사야 하나
거래대금의 세 번째 얼굴은 시장의 리듬이다. 코인 시장은 24시간 돌지만 균질하지 않다.
처음 설계할 때 나는 직관적인 가정을 했다. 거래가 활발한 시간에 기회가 많을 테니 그때 공격적으로 가자는 것. 그래서 시간대별로 평가 종목 수와 진입 문턱을 다르게 두는 구조를 만들었다. 한국 저녁 시간대는 평가 종목을 늘리고 문턱을 낮췄고, 낮 시간대는 보수적으로 잡았다.
실측이 이 설계를 뒤집었다.
19시 평균 −0.752% 승률 19.4%
20시 평균 −0.806% 승률 12.1%
21시 평균 −0.716% 승률 14.6%
22시 평균 −0.044%
23시 평균 −0.409% 승률 23.6%
거래가 제일 활발한 시간대가 성과는 제일 나빴다
19~23시 차단 채택 → 잔여 평균 −0.240% → −0.182%
부트스트랩 우세 78.8%
그런데 이 규칙은 나중에 해제된다
이유: 매수 가뭄. 하루 8건 → 1.6건으로 급감
성과 개선 +0.058%p 보다 표본 고갈의 비용이 컸다이 사례에서 두 가지를 배웠다.
하나, 거래량이 많다는 건 참여자가 많다는 뜻이지 내 전략이 잘 통한다는 뜻이 아니다. 붐비는 시간에는 나 같은 눌림목 사냥꾼도 많다. 좋은 자리가 남아 있지 않을 수 있는 것이다.
둘, 통계적으로 옳은 규칙이 운용상 틀린 규칙일 수 있다. 19~23시 차단은 데이터가 지지하는 결정이었다. 부트스트랩 우세 78.8%면 우연이라고 보기 어렵다. 그런데 실제로 돌려보니 진입이 하루 1.6건으로 줄었고, 그러면 통계를 쌓을 표본 자체가 안 만들어진다. 검증 가능한 상태를 유지하는 것도 운용의 일부라는 걸 그때 알았다.
이건 백테스트와 실전의 차이이기도 하다. 백테스트에서는 표본이 이미 존재하지만, 실전에서는 매매를 해야 표본이 생긴다. 진입을 줄이는 규칙은 성과를 개선하는 동시에 학습 속도를 떨어뜨린다. 그 교환을 계산에 넣어야 한다.
마무리
24시간 거래대금은 화려한 지표가 아니다. RSI처럼 매수 시점을 알려주지도, 볼린저 밴드처럼 그림이 예쁘지도 않다. 하는 일은 딱 하나 — 문 앞에서 못 들어올 종목을 돌려보내는 것이다.
그런데 봇을 오래 굴리다 보면 이 종류의 규칙이 수익률에 미치는 영향이 생각보다 크다는 걸 알게 된다. 좋은 종목을 찾는 것보다 나쁜 종목을 안 만나는 게 쉽고, 쉬운 쪽이 더 확실하게 계좌를 지킨다. 신호를 잘 잡는 문제와 계좌를 지키는 문제는 다른 문제이고, 후자는 대체로 이런 지루한 필터들이 담당한다.
거래소 앱에서 무심코 지나치던 그 숫자가, 사실은 내 봇에서 가장 먼저 실행되는 조건이다.
덧붙임 — 세탁 거래를 의심하는 방법
마지막으로 이 지표의 신뢰성 문제를 조금 더 다루고 끝내자. 거래량 지표는 시장이 정직하다는 전제 위에 서 있는데, 그 전제가 항상 성립하지는 않는다.
세탁 거래는 같은 주체가 자기 물량을 주고받아 거래량을 부풀리는 행위다. 목적은 대개 순위표 노출이나 상장 유지 조건 충족이다. 개인이 이걸 정확히 판별하기는 어렵지만, 의심할 만한 정황은 몇 가지 볼 수 있다.
① 거래대금 대비 호가 두께가 얇다
하루 종일 활발히 거래된 종목의 호가창이 비어 있을 이유가 없다
② 체결 크기 분포가 이상하다
비슷한 크기의 체결이 규칙적으로 반복되면 사람의 매매 같지 않다
③ 가격 변동 없이 거래량만 크다
진짜 수급이라면 가격이 움직인다. 안 움직이는데 거래만 많으면 이상하다
④ 특정 시간대에만 거래가 몰린다
자동화된 반복 패턴일 수 있다
봇 관점의 실용적 결론
판별하려 애쓰기보다 호가 기반 조건으로 우회하는 게 낫다
세탁 거래는 거래대금을 부풀릴 수 있지만 호가창을 채우진 못한다
스프레드 상한과 호가단위 게이트가 이 문제의 실질적 방어선이다마지막 줄이 실무적 답이다. 세탁 거래인지 아닌지를 판정하려 하지 말고, 세탁 거래가 만들어낼 수 없는 조건을 거는 것. 거래대금 숫자는 조작할 수 있어도 실제로 살 수 있는 호가는 조작하기 어렵다.
이 접근은 다른 문제에도 적용된다. 원인을 정확히 규명하기 어려울 때, 원인 대신 증상이 나타나는 경로를 막는 것. 유령 포지션 문제를 체결 확인으로 막은 것도, 호가 관통을 게이트로 막은 것도 같은 발상이다. 완벽한 이해가 없어도 실용적인 방어는 세울 수 있다.