이름의 유래

지금 봇의 버전은 전부 VV로 시작합니다. vv141, vv210 같은 식으로요. 이 VV가 어디서 왔냐면, VWAP입니다.

VWAP은 거래량 가중 평균가입니다. 그냥 평균가는 모든 봉을 똑같이 취급하는데, VWAP은 거래가 많이 일어난 가격에 더 무게를 둡니다. 실제로 돈이 오간 자리가 어디냐를 보는 거죠.

단순 평균 ↔ 거래량 가중 평균
  단순 평균   모든 봉의 가격을 똑같이 취급
              거래가 거의 없던 봉도 한 표

VWAP 가격 × 그 가격에서 오간 거래량 누적해서 나눈다 돈이 실제로 오간 자리에 무게가 실린다 ————————————————– “앵커드” = 특정 시점부터 누적해서 계산 예: 어제 저점부터 지금까지 → 그 구간의 평균 매입가

여기에 “앵커드"가 붙으면 특정 시점부터 누적해서 계산한다는 뜻입니다. 예를 들어 어제 저점부터 지금까지의 VWAP을 보면, 그 구간에서 사람들이 평균 얼마에 샀는지가 나옵니다.

이 지표에 끌린 이유는 명확했습니다. 의미가 해석 가능하다는 것. 이동평균은 “최근 평균"이라는 것 말고는 실체가 없는데, 앵커드 VWAP은 “이 구간에 들어온 사람들의 평균 단가"라는 실제 의미가 있어요. 그 선 위에 있으면 그 구간 참여자 대부분이 이익 상태고, 아래면 손실 상태입니다. 지표가 시장 참여자의 상태를 말해준다는 게 매력적이었습니다.

이 지표를 중심에 놓고 만든 봇의 파일 이름이 vvwap이었고, 버전을 매기다 보니 앞의 두 글자만 남아 VV가 됐습니다. 그게 지금까지 이어지고 있어요.

왜 결국 빠졌나

VWAP 자체는 결국 현행 봇의 진입 판단에서 빠졌습니다. 이유를 정리해두면 이렇습니다.

앵커드 VWAP 을 실전에 넣을 때의 문제
  1. 앵커를 어디에 둘 것인가
     어제 저점? 최근 급등 시작점? 특정 시각?
     앵커가 바뀌면 선이 통째로 달라진다
     → 사람이 눈으로 찍을 땐 되는데 자동화가 어렵다

2. 종목마다 적절한 앵커가 다르다 수백 종목을 스캔하는 봇에서 각각 앵커를 정할 수 없다

3. 대체 가능했다 “많이 빠졌나” 는 다른 방식으로도 판정된다 → RSI · 최근 저점 근접도

1번이 결정적이었습니다. 앵커드 VWAP은 사람이 차트를 보면서 “여기부터"를 찍을 때 강력합니다. 그런데 봇은 그 판단을 자동으로 해야 해요. 앵커 선택 규칙을 만들려고 하는 순간, 그 규칙이 또 하나의 튜닝 대상이 됩니다. 지표를 하나 넣었는데 파라미터가 두 개 늘어난 셈이죠.

이건 앞 시대에서 배운 교훈의 반복입니다. 사람의 해석이 필요한 지표는 봇으로 옮기는 순간 무너집니다. 좋은 지표와 자동화하기 좋은 지표는 다릅니다.

그래도 이름은 남았습니다. 지금 봇에서 안 쓰는 지표의 약자가 버전 접두어로 계속 붙어 있는 게 좀 웃긴데, 저는 그대로 두고 있어요. 이름을 바꾸면 그 전에 있었던 일들과 연결이 끊깁니다. 461개가 하나의 계보라는 게 이 프로젝트에서 제일 중요한 사실이라서요.

여기서 만들어진 진짜 자산

VWAP은 빠졌지만, 이 시기에 만들어진 것 중 지금 가장 중요한 게 하나 있습니다.

v103 — 시장 상황 판단의 시작
  BTC_DROP_LIMIT 도입
    비트코인이 일정 이상 빠지면 신규 진입을 막는다
    개별 종목 조건이 아무리 좋아도, 시장이 무너지면 소용없다는 전제
  이것이 훗날 vv202 의 "하락장 신규 진입 중단" 으로 이어진다
  BB.V008 에서는 4시간봉 완성봉 기준으로 판정하도록 정리 —
  진행 중인 봉으로 판단하면 시장 상황이 몇 분 단위로 뒤집히기 때문

MACRO 실험에서 얻은 문제의식 — 시장 전체를 먼저 봐야 한다 — 이 여기서 처음 실제 코드가 됐습니다. 개별 종목 게이트를 아무리 정교하게 깎아도 시장이 통째로 흘러내리는 날엔 다 걸려요. 그럴 땐 안 사는 게 답이고, 그 판단은 종목이 아니라 시장 레벨에서 해야 합니다.

여기서 중요한 건 구현이 아니라 결정의 성격입니다. 그전까지 봇의 모든 규칙은 “어떤 종목을 어떻게 살까"였어요. BTC_DROP_LIMIT은 처음으로 종목과 무관하게 전체를 멈추는 규칙입니다. 판단의 층위가 하나 올라간 거죠.

규칙의 두 종류
  [ 종목 단위 규칙 ]
    이 종목을 살까 말까
    틀려도 그 종목 하나의 문제

[ 계좌 단위 규칙 ] 지금 아무것도 안 할까 한 번의 판단이 모든 매매를 좌우 → 그래서 더 보수적으로, 더 느린 주기로 판단해야 ————————————————– 현행 계좌 단위 규칙: 레짐 차단 · 일일/주간 손실 한도 자산 서킷브레이커 · 포트폴리오 수확

계좌 단위 규칙은 영향력이 큰 만큼 조심스럽게 다뤄야 합니다. 종목 하나를 잘못 거르면 기회 하나를 놓치는데, 시장 판정을 잘못하면 그날 하루를 통째로 날립니다. 그래서 이런 규칙은 더 느린 주기로, 더 안정적인 기준으로 판단하게 됩니다.

시장 판정을 무엇으로 할 것인가

BTC를 기준으로 삼은 것도 이때 정해졌습니다. 여러 후보가 있었는데요.

시장 기준 후보들
  BTC 단일
    + 데이터가 가장 두껍다 · 대표성이 높다
    + 계산이 단순하다 (기준 하나)
    - BTC 만 따로 움직이는 구간이 있다

시총 상위 N개 평균 + 개별 종목 특이 움직임이 상쇄된다 - 구성 종목을 언제 갱신할지 또 정해야 한다 - 계산량이 늘고 재현이 복잡해진다

보유 종목 평균 - 보유가 없으면 판정 자체가 불가능 ————————————————– 단순함을 골랐다 — 검증 가능성이 우선

정교한 쪽이 이론적으로는 낫습니다. 그런데 정교해질수록 검증이 어려워져요. 구성 종목 갱신 규칙, 가중치, 갱신 주기 — 전부 새로운 튜닝 대상입니다. 그리고 이 시대에는 그걸 검증할 데이터가 없었습니다.

검증할 수 없는 정교함보다 검증 가능한 단순함을 고른 겁니다. 이 판단 기준은 이후에도 반복해서 씁니다. 지금 랭킹 산식이 단순한 것도, 게이트가 각각 독립적으로 판정되는 것도 같은 이유예요.

한 가지 덧붙이면, 단순한 기준의 약점은 알고 쓰는 게 중요합니다. BTC만 보면 개별 종목 장세를 놓치는 구간이 분명히 있어요. 그걸 모르고 쓰면 나중에 성적이 이상할 때 원인을 못 찾습니다. 알고 쓰면 “아, 이 구간은 기준의 한계구나” 하고 넘어갈 수 있고요. 한계를 아는 단순한 도구가 한계를 모르는 정교한 도구보다 낫습니다.

완성봉만 쓰기로 한 이유

완성봉만 쓰기로 한 것도 이때 정리됐습니다. 진행 중인 4시간봉으로 판단하면 몇 분마다 “하락장이다 / 아니다"가 뒤집힙니다.

진행 중인 봉으로 판단하면
  4시간봉이 만들어지는 동안
  --------------------------------------------------
  10:05  현재 시가 대비 -1.2%  → 하락장 판정 · 진입 차단
  10:20  반등해서 +0.3%       → 정상 판정 · 진입 허용
  10:45  다시 -0.8%           → 하락장 판정 · 진입 차단
  --------------------------------------------------
  봇은 진입을 열었다 닫았다만 하고 아무것도 못 한다
  완성봉 기준: 4시간에 한 번만 판정이 바뀐다

봇이 그때마다 진입을 열었다 닫았다 하면 아무것도 못 해요. 더 나쁜 건 그 판정 순간에 따라 결과가 완전히 달라진다는 겁니다. 같은 날 같은 종목인데 봇이 몇 분 차이로 다르게 행동하면, 나중에 성적을 분석할 때 원인을 짚을 수가 없습니다.

판단의 주기는 행동의 주기보다 느려야 합니다. 이 원칙은 시장 판정뿐 아니라 지표 전반에 적용됩니다. 지금 봇이 완성된 봉만 쓰는 것도, 진입 후 일정 시간 재판단을 안 하는 것도 같은 이유예요.

그리고 이건 검증 가능성 문제이기도 합니다. 진행 중인 봉을 쓰면 같은 데이터로 시뮬레이션을 돌려도 결과가 재현되지 않아요. 몇 시 몇 분에 봤느냐에 따라 값이 다르니까요. 재현되지 않는 로직은 검증할 수 없고, 검증할 수 없는 값은 배포하면 안 됩니다. 지금 배포 규율의 기술적 전제가 이때 마련됐습니다.

지표를 버리는 일에 대하여

VWAP을 뺀 이야기를 조금 더 하고 싶습니다. 지표를 넣는 것보다 빼는 게 훨씬 어렵거든요.

지표를 못 버리게 만드는 것들
  · 넣는 데 시간을 썼다        매몰 비용
  · 논리가 그럴듯하다          설명이 되니까 맞는 것 같다
  · 가끔 잘 맞는 사례가 있다   기억에 남는 건 맞은 쪽
  · 빼면 뭔가 허전하다        조건이 줄면 불안하다
  --------------------------------------------------
  판단 기준은 하나여야 한다:
  이 지표를 빼면 결과가 나빠지는가?
  이 시대엔 그 질문에 답할 데이터가 없었다

세 번째가 특히 함정입니다. 지표가 극적으로 잘 맞은 사례는 기억에 오래 남아요. 반대로 조용히 방해만 한 수십 건은 기억에 안 남습니다. 그래서 체감상으로는 항상 “이 지표 넣길 잘했다"가 됩니다.

이걸 깨려면 숫자가 필요합니다. 지표를 뺀 버전과 넣은 버전을 같은 데이터로 돌려서 비교하는 것요. 이 시대에는 그게 안 됐고, 그래서 VWAP을 빼는 결정도 “자동화가 어렵다"는 실무적 이유로 내렸습니다. 성적 근거가 아니었어요.

나중에 매매기록 시뮬레이션이 생기고 나서 알게 된 게 있습니다. 조건을 추가했는데 통행량만 죽고 성적은 그대로인 경우가 꽤 많다는 것. 매매가 줄면 손실도 줄어서 좋아 보이는데, 건당 기대값은 안 변했어요. 좋은 필터를 얻은 게 아니라 그냥 문을 좁힌 겁니다.

그래서 지금은 게이트를 추가할 때 반드시 두 가지를 같이 봅니다. 건당 기대값이 올라갔는지, 그리고 매매 건수가 얼마나 줄었는지. 기대값이 그대로인데 건수만 줄었다면 그 게이트는 아무 일도 안 하는 겁니다.

C++로 옮기려다 접은 이야기

이 시기 흔적 중에 C++ 프로젝트가 하나 있습니다. 파이썬 봇을 C++로 포팅하려던 시도예요. websocketpp로 통신을 짜고 jwt-cpp로 인증을 붙이다가, 완성 전에 멈췄습니다.

왜 시작했고 왜 접었나
  시작한 이유
    파이썬이 느리다는 막연한 불안 — 체결 속도에서 밀리는 것 아닌가
  접은 이유
    1. 병목이 파이썬이 아니었다 — 대부분의 지연은 네트워크와 거래소 응답
    2. 개발 속도가 절반 이하로 떨어졌다 — 하루 열 번 고치던 리듬이 죽음
    3. 눌림목 전략은 애초에 밀리초 싸움이 아니다 (15분봉 기준)
  교훈: 최적화는 측정 후에. 느낌으로 하는 최적화는 시간만 쓴다

돌이켜보면 이건 전형적인 조기 최적화였습니다. 느릴 것 같다는 느낌으로 언어를 바꾸려 했는데, 실제로 어디가 느린지 측정한 적이 없었어요.

한 번의 매매에서 시간이 쓰이는 곳
  시세 수신 대기        거래소가 보내줄 때까지
  지표 계산            파이썬 · 매우 짧음
  게이트 판정          파이썬 · 매우 짧음
  주문 전송 → 응답      네트워크 왕복 + 거래소 처리
  체결 확인            거래소 응답 대기
  --------------------------------------------------
  빨간 부분이 대부분 — 언어를 바꿔도 그대로다
  초록 부분을 100배 빠르게 해도 총 시간은 거의 안 준다

나중에 재보니 지연의 대부분은 거래소 응답 대기였고, 그건 언어를 바꿔도 그대로입니다. 계산 부분을 아무리 빠르게 만들어도 전체 시간에서 차지하는 비중이 작으니 체감이 안 돼요.

그리고 더 중요한 손실이 있었습니다. C++로 옮기는 동안 전략 개선이 멈췄어요. 이 봇에서 가장 값진 자원은 실행 속도가 아니라 고치고 확인하는 사이클의 속도였는데, 그걸 스스로 절반으로 떨어뜨린 겁니다.

포팅이 실제로 바꾼 것
  얻으려던 것   실행 속도 (체감 안 됨 · 병목이 아니었음)
  잃은 것       개발 사이클 속도 (절반 이하)
                전략 개선 기간 (정지)
                디버깅 난이도 (상승)
  --------------------------------------------------
  혼자 만드는 봇에서 최적화할 대상 1순위는
  코드 실행 속도가 아니라 사람의 반복 속도다

결국 파이썬으로 돌아왔습니다. 이 판단이 늦었으면 지금 봇이 없었을 겁니다.

조기 최적화를 알아보는 법

같은 함정에 빠지지 않으려고 기준을 하나 정해뒀습니다.

최적화 전에 던지는 질문
  1. 지금 몇 밀리초가 걸리는지 아나?
     모르면 → 측정부터. 최적화는 그다음
  2. 그 시간이 전체의 몇 %인가?
     10% 미만이면 → 절반으로 줄여도 5% 개선
  3. 이 개선이 전략 성적에 영향을 주나?
     15분봉 전략에서 0.1초 → 영향 없음
  4. 이 작업 동안 멈추는 것은 무엇인가?
     전략 개선이 멈춘다면 → 기회비용이 더 크다
  --------------------------------------------------
  네 질문 다 답할 수 있으면 해도 된다

4번이 특히 중요합니다. 최적화 작업의 비용은 그 작업에 든 시간만이 아니라 그동안 못 한 일이에요. 혼자 개발하면 동시에 두 가지를 못 하니까 이 비용이 큽니다.

이 시기에 배운 걸 한 문장으로 줄이면, 성능은 문제가 됐을 때 고치는 것입니다. 문제가 될 것 같아서 미리 고치면 대개 문제가 아닌 걸 고치게 돼요.

언어를 바꾸려던 진짜 이유

솔직하게 하나 더 적겠습니다. C++ 포팅을 시작한 데는 속도 말고 다른 이유도 있었습니다.

그때 전략이 잘 안 되고 있었어요. 파라미터를 만져도 안 되고, 조건을 바꿔도 안 되고. 그 상태에서 “언어를 바꾸면 뭔가 달라질지도"라는 생각이 들었습니다. 논리적인 판단이 아니라 막힌 상태에서 다른 일로 도망친 것에 가까웠어요.

막혔을 때 하게 되는 일들
  · 언어를 바꾼다
  · 프레임워크를 바꾼다
  · 코드를 전면 재작성한다
  · 새 지표를 붙인다
  --------------------------------------------------
  공통점: 진척이 눈에 보인다 (커밋이 쌓인다)
  공통점: 원래 막혀 있던 문제는 그대로다
  --------------------------------------------------
  막혔을 때 실제로 필요한 것: 왜 막혔는지 측정하기
  → 이 시대엔 측정 수단이 없었다. 그래서 도망쳤다

이런 종류의 도망은 알아채기 어렵습니다. 열심히 일하고 있고, 커밋도 쌓이고, 기술적으로도 배우는 게 있으니까요. 그런데 정작 막힌 문제는 그대로예요.

지금은 막히면 데이터를 봅니다. 어느 게이트에서 몇 개가 떨어지는지, 손절과 익절의 비율이 어떤지, 어떤 시간대에 성적이 나쁜지. 측정할 수 있으면 도망칠 필요가 없어요. 측정 수단이 없는 상태가 조기 최적화를 부릅니다.

이 시기가 남긴 개인적인 교훈은 이겁니다. 새로운 기술로 넘어가고 싶어질 때, 그게 정말 필요해서인지 아니면 지금 문제가 안 풀려서인지 한 번 물어보기. 대개 후자더군요.

이 시대의 정체성

VVWAP기는 짧고 어수선한 시대입니다. 주력 지표는 결국 빠졌고, C++ 포팅은 미완으로 끝났어요. 그런데 남긴 게 셋 있습니다.

VVWAP기가 남긴 것
  1. 이름        VV — 461개를 하나의 계보로 묶는 접두어
  2. 레짐 판단   시장 전체를 보는 첫 코드
                → 현행 BTC 4시간봉 하락장 진입 차단
  3. 완성봉 원칙 판단 주기는 행동 주기보다 느려야
                → 재현 가능성 · 검증 가능성의 전제
  + 조기 최적화라는 실패 사례 한 건
  --------------------------------------------------
  실패한 시도가 남긴 규칙이 성공한 코드보다 오래 남는다

2번과 3번이 사실 한 쌍입니다. 시장 판단이라는 강력한 규칙을 만들면서, 그 규칙이 흔들리지 않게 하는 방법도 같이 정한 거예요. 좋은 규칙과 그 규칙을 안정적으로 적용하는 방법은 별개의 문제인데, 이 시대에 둘을 같이 배웠습니다.

다음으로

이 시대 마지막 즈음, 파일 하나로는 더 못 버티겠다는 게 확실해집니다. 모놀리스의 문제는 3시대에 이미 알고 있었는데, 실제로 손을 대게 된 건 여기서였어요. 아는 것과 하는 것 사이에 시대가 두 개 있었습니다.

그리고 개발 도구도 바뀝니다. 7월 25일 새벽, 이 저장소의 커밋 목록에 처음으로 AI 협업 흔적이 남아요. 그 얘기는 다음 편에서 하겠습니다.

이 시대에서 가져갈 것

첫째, 좋은 지표와 자동화하기 좋은 지표는 다릅니다. 앵커드 VWAP은 사람이 쓰기엔 훌륭한데 봇으로 옮기려니 앵커 선택이라는 새 문제를 만들었어요. 지표를 고를 때는 “이게 유용한가"뿐 아니라 “이걸 사람 판단 없이 계산할 수 있나"를 같이 물어야 합니다.

둘째, 판단 주기는 행동 주기보다 느려야 합니다. 완성봉 원칙이 그겁니다. 판정이 행동보다 자주 뒤집히면 봇은 아무것도 못 하고, 결과를 재현할 수도 없어요. 재현되지 않으면 검증도 안 되고, 검증 안 된 값은 배포하면 안 됩니다. 이 사슬의 첫 고리가 완성봉이었습니다.

셋째, 최적화는 측정 다음입니다. 몇 밀리초인지 모르는 상태에서 언어를 바꾸는 건 도박이에요. 그리고 그 작업 동안 멈추는 것의 가치를 같이 계산해야 합니다. 혼자 만드는 봇에서 제일 값진 자원은 실행 속도가 아니라 반복 속도입니다.

넷째, 막혔을 때 새 기술로 도망치지 마세요. 이건 저에게 하는 말입니다. 진척이 눈에 보이는 일과 문제를 푸는 일은 다릅니다. 막히면 측정할 방법부터 만드는 게 맞더군요.

이름 하나 남기고 지표는 버린 시대. 그런데 그 이름이 지금까지 붙어 있다는 게, 이 프로젝트가 무엇을 중요하게 여기는지 보여준다고 생각합니다. 연속성이요.