오늘의 재료 — 계산이 필요 없는 유일한 지표

거래량은 지표 중에서 유일하게 계산이 없다. RSI는 가공이고 이동평균도 가공이지만, 거래량은 거래소가 주는 원자료 그 자체다. 그런데도 이 코너에서 한 편을 통째로 쓰는 이유는, 이 원자료가 가장 오해받는 지표이기 때문이다. 그리고 고백하자면 — 내 봇 461개 버전의 역사에서 가장 여러 번 넣었다 뺐다 한 게이트가 바로 거래량이다. 오늘은 그 실측의 역사를 데이터와 함께 전부 꺼내놓는다.

먼저 개념 정리. 차트 아래 막대는 보통 ‘거래량(수량)‘인데, 코인 시장에서 의미 있는 것은 대부분 **거래대금(수량×가격)**이다. 개당 4억 원짜리 BTC의 1개와 개당 200원짜리 코인의 1개는 같은 ‘1’이 아니기 때문이다. 그리고 거래량 해석에는 최소 세 가지 다른 얼굴이 있다.

volume — 같은 막대의 세 가지 얼굴
① 절대 거래대금   "이 종목은 하루에 얼마나 거래되나"
     용도: 유니버스 필터 (살 수 있는 종목인가) — 종목 간 비교
② 상대 배수       "지금 이 봉의 거래량은 자기 평균의 몇 배인가"
     용도: 신호 게이트 (뭔가 일어나고 있는가) — 자기 자신과 비교
③ 시간대 프로파일  "이 시장은 언제 붐비나"
     용도: 운용 정책 (언제 진입을 허용하나) — 시장 리듬과 비교
흔한 실수: ①의 잣대로 ②의 질문에 답하는 것.
"거래대금 50억 이상만" 은 대형주 필터이지, 신호 감지가 아니다.

이 세 얼굴을 섞으면 게이트가 이상해진다. 내 봇이 정확히 그 실수를 했고, 그 대가를 실측으로 치렀다. 아래에서 연대기로 보인다.

공식에서 코드로 — 다섯 줄, 함정은 셋

코드는 짧다. 함정은 짧지 않다.

python — 상대 배수와 거래대금, 그리고 함정들
# 거래대금 (코인 시장의 기본 단위)
value = df["volume"] * df["close"]          # 또는 거래소가 주는 quote_volume
# 상대 배수: 이 봉의 거래량 / 최근 N봉 평균
vol_mult = df["volume"] / df["volume"].rolling(20).mean()
# 함정 1: 진행 중인 봉 — 15분봉이 3분 지난 시점의 거래량은 당연히 평균 미달
#         완성봉만 쓰거나, 경과 시간으로 정규화해야 한다
# 함정 2: 자기 포함 — rolling 평균에 현재 봉을 넣으면 급증 봉이 분모를 키워
#         자기 자신의 배수를 깎아 먹는다 → shift(1) 로 직전까지의 평균과 비교
# 함정 3: 세탁 거래 — 거래소·종목에 따라 거래량 자체가 오염될 수 있다.
#         거래량 지표는 시장의 정직함을 전제로 한다는 것을 잊지 말 것

함정 1은 RSI 편부터 반복되는 완성봉 원칙의 거래량 버전인데, 거래량에서는 정도가 훨씬 심하다. 가격은 봉 진행 중에도 ‘현재가’라는 완결된 값이지만, 거래량은 봉이 닫힐 때까지 누적 중인 미완성 값이기 때문이다. 진행봉의 거래량으로 배수를 계산하면 모든 봉이 하루 종일 ‘거래량 부족’으로 보인다.

내 봇의 거래량 연대기 — 두 번 죽고 한 번 부활한 게이트

이제 실측의 역사다. 아래 수치는 전부 커밋 기록에 남아 있는 실제 값이다.

history — 거래량 게이트 연대기 (커밋 기록 실측값)
── 유니버스 필터 (절대 거래대금) ──
VV131-KO  MIN_TRADE_VALUE_24H  50억 → 10억   평가 종목 ~10 → ~40-50개
VV191     유니버스 하한 400원 / 7.5억         "살 수 없는 종목을 스캔에서 제거"
── 신호 게이트 (상대 배수) : 완화의 내리막 ──
VV131→138  VOL_SMA_5_MULT  1.5 → 1.2 → 1.0 → 0.6   (신호 가뭄 해소 목적)
VV140      0.6 → 0.5  "통과율 31% → ~60% 목표"
── 1차 사망 ──
VV167     구조적 편향 지적 — 게이트가 종목을 거르는 게 아니라 체급을 차별
VV182     정밀분석에서 판별력 부족 판정 → 게이트 제거
── 부활 ──
VV197     10번째 게이트로 재설계 부활: 거량 >= 0.5x (자기 평균 대비 배수)
VV200     0.5x → 0.6x 한 칸 조임
VV201     8.9일 전수 스윕: "거량은 현행(0.6)이 정점" — 생존 확정

이 연대기에서 배울 것은 값이 아니라 죽고 살아난 이유다. 초기의 거래량 게이트는 ‘거래량 급증 = 좋은 신호’라는 돌파 전략의 문법으로 설계됐고, 신호가 마를 때마다 임계를 깎다가(1.5→0.5) 결국 판별력이 없다는 판정으로 제거됐다. 부활한 게이트는 문법이 다르다 — 눌림목 전략에서 거래량의 역할은 ‘급증 감지’가 아니라 죽은 종목 배제다. 자기 평균의 0.6배도 안 되는 봉에서의 반등은 반등이 아니라 호가 공백일 확률이 높으니까. 같은 데이터, 같은 이름의 게이트라도 전략이 바뀌면 존재 이유가 바뀐다.

가장 값진 실측 — 단변량의 함정

거래량 게이트 역사에서 내가 최고로 치는 데이터는 VV184의 철회 기록이다. 스캔로그 1,116건을 전수 시뮬레이션하던 중, 거래량 게이트 재도입(≥1.0x)을 검토했고 — 단변량으로는 유일하게 성적이 좋았다.

VV184 — 거래량 게이트 재도입 검토와 철회 (커밋 기록)
단변량 분석: 거래량 1.0~2.0x 구간
  휩쏘 9.0% / 평균 +0.175% — 전 구간에서 유일한 플러스
    → 여기까지만 보면 채택이 당연해 보인다
조합 분석: 다른 게이트 통과분에 적용하면
  후보 46건 → 6건 (-87%) 로 급감, 성과도 무너짐
    → 철회
교훈: 단변량으로 좋은 필터가 조합에서도 좋다는 보장은 없다.
      다른 게이트들이 이미 같은 정보를 다른 각도로 거르고 있었다 —
      거래량 조건은 '중복 검문소' 였고, 통행량만 죽였다.

지표 스터디 전체를 통틀어 이 한 장이 제일 중요한 도판일지도 모른다. 백테스트 글에서 흔히 보는 “이 필터를 넣으니 승률이 올랐다"는 대부분 단변량 분석이다. 그러나 봇은 필터를 하나만 돌리지 않는다 — 게이트 10개가 이미 서 있는 시스템에 11번째를 넣을 때의 질문은 “이 필터가 좋은가"가 아니라 “이 필터가 나머지 열 개가 못 거르는 것을 거르는가“다. 정보의 독립성이 필터의 가치다. VV201 스윕이 조합 검증을 필수 절차로 못박은 것도, VV195의 RSI 50이 거래량 게이트 도입 후 45로 이동한 것도 전부 같은 원리의 재확인이다.

세 번째 얼굴 — 시간대 프로파일

거래량의 세 번째 얼굴은 종목이 아니라 시장의 리듬을 본다. 코인 시장은 24시간 돌지만 균질하지 않다. 한국 시장은 저녁에 붐비고 낮에 한산하며, 해외 세션이 열리고 닫힐 때 성격이 바뀐다. 이걸 처음 코드로 옮긴 것이 VV131-KO의 시간대 동적 설정이었다 — 시간대별로 평가 종목 수와 허용 스프레드, 진입 점수 문턱을 다르게 두는 구조.

time profile — 시간대별 운용 파라미터 (VV131-KO 커밋 기록)
  UTC 00-06 (KST 08-14, 한산)   보수: 평가 15 · 점수 75 · 스프레드 0.1%
  UTC 07-11 (KST 16-20, 저녁)   공격: 평가 40 · 점수 70 · 스프레드 0.2%
  기본                          균형: 평가 30 · 점수 72 · 스프레드 0.15%
이후 실측이 이 설계를 뒤집는다 — VV184, 스캔 1,116건 시간대별 성과
  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%
  그러나 VV191 에서 해제 — 매수 가뭄 (하루 8건 → 1.6건). 기회 확보를 택함

이 대목이 재미있는 이유는 거래가 활발한 시간이 수익이 나는 시간은 아니었다는 것이다. 설계 단계의 직관은 “붐빌 때 기회가 많으니 공격적으로"였는데, 실측은 정확히 그 시간대(KST 19~23시)의 성과가 최악이라고 답했다. 거래량이 많다는 것은 참여자가 많다는 뜻이지 내 전략이 잘 통한다는 뜻이 아니다. 붐비는 시간에는 나 같은 눌림목 사냥꾼도 많아서, 좋은 자리가 남아 있지 않을 수도 있는 것이다. 그리고 이 차단마저 결국 해제됐다 — 성과 개선(+0.058%p)보다 표본 고갈(하루 8건→1.6건)의 비용이 컸기 때문이다. 통계적으로 옳은 규칙이 운용상 틀린 규칙일 수 있다는, 값비싼 교훈이다.

파생 지표들 — OBV와 VWAP, 그리고 이름의 기원

거래량 원자료 위에 세워진 파생 지표도 잠깐 짚자. **OBV(On Balance Volume)**는 상승 봉의 거래량은 더하고 하락 봉의 거래량은 빼서 누적하는 지표다 — 매집/분산의 방향성을 보려는 시도. **VWAP(거래량 가중 평균가)**는 거래량으로 가중한 평균 체결가로, 기관의 체결 품질 기준선으로 쓰인다.

derived — 거래량 위에 세운 두 지표
# OBV — 방향성 누적
obv = (np.sign(close.diff()) * volume).fillna(0).cumsum()
  해석: 가격은 횡보하는데 OBV가 오르면 '조용한 매집' 이라는 서사
  주의: 봉 하나의 방향에 거래량 전체를 귀속시키는 거친 가정 위에 서 있다
# VWAP — 거래량 가중 평균가 (앵커 시점부터 누적)
vwap = (price * volume).cumsum() / volume.cumsum()   # price = (H+L+C)/3
  이 봇 이름의 기원이 여기 있다 — 앵커드 VWAP 을 쓰던 시기의 코드가
  vvwap → VV 로 줄어 지금의 VV 모듈 시대까지 이름으로 남았다
  현행 전략은 VWAP 을 진입 판정에 쓰지 않는다. 이름만 유산으로 남은 셈

VWAP은 개인적으로 각별하다. 내 봇의 이름 ‘VV’가 바로 이 지표에서 나왔기 때문이다(앵커드 VWAP을 쓰던 시기의 파일명 vvwap이 줄어 굳었다). 지금의 현행 전략은 VWAP을 진입 판정에 쓰지 않는다 — 지표는 떠났는데 이름은 남았다. 461개 버전을 지나온 프로젝트에는 이런 화석이 몇 개씩 있다.

저유동성의 진짜 청구서 — 거래량 게이트의 숨은 존재 이유

거래량이 적은 종목의 비용은 ‘신호가 부정확하다’ 수준이 아니다. 실계좌에서는 훨씬 물리적인 형태로 청구된다. 본전 청산 27건을 실측했을 때(VV206), 보호선을 +0.2%에 뒀는데 평균 실현이 +0.017%, 합계 -18,144원이었다 — 원인은 슬리피지가 아니라 호가 관통이었다. 저가·저유동성 코인은 호가 한 칸이 0.2%를 넘어(실측 예: 443원짜리 코인에서 한 칸 0.226%), 내가 그어놓은 청산선이 호가와 호가 사이 허공에 떠 있었던 것이다. 관통 손실 0.183%p에 왕복 수수료 0.10%를 더하면 손익분기만 0.283% — 이 계산이 본전 바닥을 0.4%로 올렸고(VV206), 이틀 뒤 아예 호가 한 칸/가격 비율이 0.15%를 넘는 종목은 진입 금지라는 게이트(VV208, TICK_RATIO_MAX)로 구조화됐다.

liquidity cost — 거래량 부족이 계좌에 청구되는 경로
거래량 부족 종목의 3단 청구서
 ① 스프레드     사고파는 순간마다 매수-매도 호가 간격만큼 상납
 ② 호가 관통    청산선이 호가 사이 허공에 → 의도한 가격에 못 팔림
                실측: 바닥 +0.2% 설계 → 평균 실현 +0.017% (VV206, 27건)
 ③ 체결 실패    수량이 없어 지정가 미체결 → 유령 포지션 위험
                실측: 저유동성 코인 매도 미체결이 원장 불일치로 (VANA 사건)
방어 계층 (현행):
  유니버스: 거래대금 하한 + 가격 하한   게이트: 거량 0.6x + TICK_RATIO 0.15%
  집행: 체결 폴링 확인                  회계: 원장 대조 (게이트21)

그래서 내 결론은 이렇다 — 거래량 게이트의 진짜 존재 이유는 알파(초과수익)가 아니라 비용 방어다. 좋은 종목을 찾아주는 게 아니라, 계좌를 갉아먹는 종목을 문 앞에서 돌려보내는 것. 수익 기여도로만 평가하면 거래량 게이트는 늘 애매하게 나온다(그래서 두 번 죽었다). 비용 방어까지 계산에 넣어야 이 게이트의 손익계산서가 완성된다.

검증 계획 — 거래량을 다시 심사대에 올린다면

현행 거량 게이트(0.6x)는 VV201 스윕에서 ‘현행이 정점’으로 확인받았지만, 그것은 8.9일치 기록 위에서의 판정이었다. 표본이 더 쌓인 지금 다시 심사한다면 설계는 이렇게 짠다.

validation design — 거래량 게이트 재심사 설계
  축                    후보값                     비고
  거량 배수 하한        0.4 / 0.5 / 0.6 / 0.8      현행 0.6
  평균 기간             10 / 20 / 60봉             자기 평균의 정의
  거래대금 하한         5억 / 7.5억 / 15억         현행 7.5억 (유니버스)
  TICK_RATIO 상한       0.10% / 0.15% / 0.20%      현행 0.15%
  목적함수: 평균수익 · 승률 · 손절권 진입률 — 파레토 우세만 채택
  필수 절차: 단변량 → 조합 검증 → 기간 반분 안정성 (VV184의 교훈)
  추가 관측: 게이트 통과분의 '체결 품질' 도 같이 측정한다
            (지정가 체결률 · 실체결가와 의도가의 괴리 · 본전 청산 실현율)
  결과 칸이 비어 있는 이유: 아직 측정 전이다.

마지막 항목이 이 편의 결론과 이어진다. 거래량 게이트를 수익률로만 심사하면 또 애매하게 나올 것이다 — 이미 두 번 그렇게 죽었다. 체결 품질(지정가가 의도한 가격에 실제로 체결되는가, 본전 청산이 설계대로 실현되는가)을 같이 측정해야 이 게이트의 진짜 기여가 장부에 잡힌다. 측정 대상을 잘못 고르면 좋은 부품도 해고당한다.

소회

계산이 없는 지표를 다루는 데 한 편이 다 갔다. 그럴 만했다고 생각한다. 거래량은 유일하게 시장의 ‘의견’이 아니라 ‘행동’을 기록한 데이터다. 가격은 단 한 주의 체결로도 움직일 수 있지만, 거래량은 누군가 실제로 지불한 총량이다. 그래서 조작이 아닌 한 거래량은 거짓말을 못 하고, 그래서 세탁 거래가 있는 시장에서는 거래량부터 의심해야 한다 — 동전의 양면이다.

내 봇에서 거래량 게이트가 걸어온 길(완화 → 사망 → 재설계 부활 → 스윕 생존)은 이 코너가 말하려는 것의 축소판이기도 하다. 지표는 전략의 문법 안에서만 의미를 갖고, 필터의 가치는 단독 성적이 아니라 조합 기여로 측정되며, 어떤 게이트는 수익이 아니라 비용 쪽 장부에서 밥값을 한다. 세 문장 모두 실측 데이터가 가르쳐줬다.

조셉 그랜빌은 거래량이 가격에 선행한다고 가르쳤다 — OBV라는 지표까지 만들면서. 그 명제가 보편 법칙인지는 지금도 논쟁거리지만, 적어도 이것 하나는 분명하다. 가격은 의견이고 거래량은 지불이다. 의견은 공짜지만 지불은 공짜가 아니므로, 나는 지불 쪽 데이터를 더 믿는 편이다.