오늘의 재료 — 13세기 수학이 차트에 사는 법

피보나치 수열은 1202년 이탈리아 수학자 레오나르도 피보나치가 토끼 번식 문제로 소개한 수열이다. 1, 1, 2, 3, 5, 8, 13, 21… 앞의 두 항을 더하면 다음 항이 된다. 이 수열이 트레이딩에 들어온 것은 인접한 두 항의 비율이 황금비 φ ≈ 1.618로 수렴하기 때문이다 — 그리고 그 역수 0.618, 제곱 역수 0.382 같은 파생 비율들이 “가격이 되돌아오는 깊이"의 기준선으로 쓰인다.

fibonacci — 수열에서 비율로, 비율에서 레벨로
수열:   1   1   2   3   5   8   13   21   34   55   89 ...
인접비: 1.0  2.0  1.5  1.667  1.6  1.625  1.615  1.619  1.618 → φ 수렴
파생 비율과 되돌림 레벨
  0.236 = 한 항 건너 셋 비율의 역     "얕은 되돌림"
  0.382 = 1/φ²                        "표준 눌림"
  0.618 = 1/φ                         "황금 되돌림"
  0.786 = √0.618                      "깊은 되돌림"
  0.500 = 피보나치가 아님 — 다우 이론의 반값 되돌림 전통이 얹혀 산다
계산: 상승 스윙(low→high) 기준  레벨 = high - (high - low) × 비율

먼저 신화부터 걷어내자. “황금비는 자연의 법칙이므로 시장에도 작동한다"는 서사는 검증된 적이 없다. 해바라기 씨앗 배열과 업비트 15분봉 사이에 인과가 있을 이유는 없다. 그런데도 이 레벨들이 실전에서 무시할 수 없는 물건인 이유는 따로 있다 — 수백만 명이 같은 자리에 같은 선을 긋기 때문이다. 모두가 0.618에 매수 주문을 걸어두면 0.618은 실제로 지지선이 된다. 법칙이라서가 아니라 합의라서 작동하는 것. 이 구분이 오늘 글 전체의 뼈대다.

공식에서 코드로 — 계산은 두 줄, 문제는 스윙

되돌림 레벨의 계산 자체는 스터디 역사상 가장 쉽다. 뺄셈과 곱셈뿐이다. 그런데 코드로 옮기는 순간, 차트에서는 보이지 않던 문제가 정면에 나타난다 — 어느 스윙에 긋는가.

python — 레벨 계산은 쉽고, 스윙 선택은 어렵다
# 레벨 계산 — 이게 전부다
def fib_levels(swing_low, swing_high):
    r = swing_high - swing_low
    return {k: swing_high - r * k for k in (0.236, 0.382, 0.5, 0.618, 0.786)}
# 진짜 문제 — swing_low, swing_high 를 누가 정하는가
# 사람: 차트를 보고 '의미 있어 보이는' 스윙을 고른다 (재현 불가)
# 봇  : 스윙 정의를 고정한다 — 예: N봉 좌우에서 최고/최저인 봉 (fractal)
def swing_high_confirmed(h, n):
    return h.shift(n) == h.rolling(2*n+1, center=True).max().shift(n)
# 주의: 이 판정은 n봉 뒤에야 확정된다 — SMC 편의 확정 지연과 동일한 문제.
#       n을 바꾸면 스윙이 바뀌고, 스윙이 바뀌면 모든 레벨이 통째로 이동한다

사람이 손으로 긋는 피보나치가 백테스트 불가능한 이유가 여기 있다. 열 명의 트레이더에게 같은 차트를 주면 열 개의 다른 스윙을 긋고, 열 벌의 다른 레벨이 나온다. 그중 맞은 사람은 “피보나치가 작동했다"고 기억하고, 틀린 사람은 “스윙을 잘못 그었다"고 기억한다 — 이론은 영원히 무죄다. 봇은 이 편리한 도피처가 없다. 스윙 정의를 코드로 고정해야 하고, 고정하는 순간 피보나치는 처음으로 반증 가능한 가설이 된다. 나는 이것이 퇴보가 아니라 승격이라고 생각한다.

레벨의 실제 모습 — 그리고 합류라는 이름의 확률 놀이

retracement — 상승 스윙의 되돌림 지도
  high ────────────────────── 0.000   ▲ 스윙 고점

  ───────────── ╲ ─────────── 0.236   얕은 눌림 (강한 추세)
  ──────────────╲──────────── 0.382   표준 눌림
  ───────────────╲─────────── 0.500   반값 (비-피보나치)
  ────────────────╲╱╲──────── 0.618   황금 되돌림 ◀ 반등 시도
  ─────────────────────────── 0.786   깊은 되돌림 (추세 의심 구간)
  low  ────────────────────── 1.000   ▼ 스윙 저점 (전량 반납)
  주의: 레벨이 5개면 스윙 범위의 상당 부분이 '레벨 근처'다.
  아무 데서나 반등해도 사후에는 어떤 레벨과든 가깝다 — 대조군이 필요한 이유

실전 피보나치 사용자들은 여기에 ‘합류(confluence)‘를 얹는다 — 0.618 되돌림이 이동평균과 겹치고, 전 저항선과 겹치면 신뢰도가 높다는 논리다. 논리 자체는 건전하다. 독립적인 근거의 교차는 통계에서도 신뢰를 올린다. 문제는 실무에서 합류가 사후 선택이 되기 쉽다는 것이다. 레벨 5개, 이동평균 3개, 전고점·전저점 여러 개를 깔아두면 차트 어디를 찍어도 두세 개는 겹친다. 합류를 신호로 쓰려면 어떤 조합을 유효한 합류로 칠지 사전에 고정해야 하고 — 또 봇의 언어로 돌아왔다.

되돌림 너머 — 확장과 프로젝션

피보나치의 절반은 되돌림(retracement)이지만 나머지 절반은 **확장(extension)**이다. 되돌림이 “얼마나 빠질까"를 묻는다면 확장은 “얼마나 갈까"를 묻는다 — 즉 익절 목표 설정에 쓰인다. 1.272, 1.618, 2.618 같은 비율을 스윙 범위에 곱해 목표가를 잡는 방식이다.

extension — 되돌림이 진입이라면 확장은 익절이다
  스윙 low → high 를 100 으로 놓고
   2.618 ──────────────────────  공격적 목표 (드물게 도달)
   1.618 ──────────────────────  표준 확장 목표 ◀ 가장 널리 쓰임
   1.272 ──────────────────────  보수적 확장 목표
   1.000 ──────────────────────  전고점
   0.618 ──────────────────────  되돌림 진입 후보
  실무: 0.618 에서 사고 1.618 에서 판다 = 손익비 약 1:1.6 구조
  내 봇의 대응물 — 비율이 아니라 실측으로 정한 값들
   익절 목표    포트폴리오 예상실현 20,000원 (VV209, 빈도 극대화)
   손절         -5% (VV204, 다층 방어 완성 후 확장)
   본전 사수    무장 +0.8% / 바닥 +0.4% (VV206, 호가 관통 실측 반영)
   근거의 출처가 다르다: 수열의 비율 vs 내 계좌의 27건·60건 재현

여기서 두 접근의 차이가 선명해진다. 피보나치 확장은 “스윙 폭의 1.618배"라는 비율의 세계에 산다. 내 봇의 익절은 “예상실현 2만원"이라는 절대금액의 세계에 산다. 어느 쪽이 우월한가는 상황에 달렸다 — 계좌 규모가 커지면 절대금액 기준은 언젠가 비율 기준으로 갈아타야 한다. 다만 지금 이 계좌, 이 슬롯 크기에서는 절대금액이 더 정직했다. 이익 구간 체류시간 중앙값이 0분이라는 실측(VV210) 앞에서, 1.618배까지 기다리는 설계는 성립하기 어려웠다.

검증 설계 — 대조군 없는 피보나치 백테스트는 전부 무효

이 지표의 검증에는 다른 지표에 없던 절차가 하나 추가된다. 대조군이다. “0.618 근처에서 반등이 잦더라"만으로는 아무것도 증명되지 않는다 — 레벨이 촘촘하니 반등은 어차피 어딘가의 레벨 근처에서 일어난다. 증명하려면 무작위 레벨과 비교해야 한다.

validation design — 자기실현 가설의 검증 절차
가설: "피보나치 레벨 근처에서 반등 확률이 유의하게 높다"
  ① 스윙 정의 고정      fractal n ∈ {5, 10, 20} — n마다 별도 실험
  ② 실험군              가격이 각 레벨 ±0.2×ATR 진입 후 k봉 내 반등률 측정
  ③ 대조군              같은 스윙에 무작위 비율 5개(균등분포)로 같은 측정
  ④ 판정                실험군 반등률이 대조군 분포의 바깥(상위 5%)일 때만 유효
추가 대조: 0.618 단독 vs 0.5 vs 0.382 — '황금비' 가 정말 특별한지 분리 측정
이 표의 결과 칸이 비어 있는 이유: 아직 측정 전이다.
측정 전의 숫자를 적는 순간 이 스터디는 광고가 된다 — 이 코너의 서명.

예상을 하나 적어두자면(예상은 수치가 아니니까 적어도 된다), 나는 실험군이 대조군을 크게 이기지 못할 가능성도 상당하다고 본다. 그래도 이 실험은 해볼 가치가 있다 — 지는 쪽이어도 ‘피보나치 합류’라는 말을 계좌 근처에서 영구 추방할 근거가 생기고, 이기는 쪽이면 게이트 후보가 하나 는다. 검증의 좋은 점은 어느 쪽 결과든 자산이 된다는 것이다.

내 봇과의 접점 — 이름 없이 이미 쓰고 있던 것

SMC 편에서 했던 고백을 여기서도 하게 된다. 내 봇은 피보나치를 그은 적이 없지만, 되돌림 깊이의 측정은 이미 하고 있다. 진입 게이트의 낙폭 상한(며칠 고점 대비 얼마나 빠졌나)과 저점근접 게이트(24시간 저점 대비 얼마나 가까운가, VV201 스윕 확정값 3.0)는 사실상 ‘되돌림이 충분히 깊은가’를 피보나치 비율 대신 실측 스윕값으로 판정하는 장치다. SMC의 디스카운트 존이 스윙 범위 하단이었던 것, 그 존의 중앙(이퀼리브리엄)이 정확히 0.5 되돌림인 것까지 — 세 지표 스터디가 결국 같은 질문의 세 가지 표기법으로 수렴하고 있다: “얼마나 빠졌을 때가 사기 좋은 자리인가.”

차이는 기준선을 어디서 가져오느냐다. 피보나치는 13세기 수열에서, SMC는 스윙 범위 등분에서, 내 봇은 내 매매기록 스윕에서 가져온다. 나는 당연히 세 번째가 옳다고 믿는 쪽이다 — RSI 편에서 적었던 원칙 그대로, 공식은 수입해도 파라미터는 국산이어야 한다. 0.618이 내 매매기록에서 유의하게 나온다면 그때 0.618을 쓰면 된다. 황금비라서가 아니라, 측정돼서.

봇 구현 — 만든다면 이런 모습

가설 검증이 통과한다고 가정하고, 실제로 구현한다면 어떤 모듈이 되는지도 그려두자. 피보나치는 RSI 같은 단일 값이 아니라 SMC의 오더블록처럼 상태를 관리하는 자료구조가 된다.

implementation — 피보나치를 봇 모듈로 만들면
state.active_swing   = (low_price, low_bar, high_price, high_bar)
state.levels         = {0.382: p1, 0.5: p2, 0.618: p3, 0.786: p4}
state.invalidated    = bool   # 스윙 저점 이탈 시 True → 레벨 전체 폐기
갱신 규칙
  ① 새 스윙 고점 확정 → 활성 스윙 갱신, 레벨 재계산
  ② 가격이 스윙 저점 하향 이탈 → 이 스윙의 레벨 전부 무효 (재사용 금지)
  ③ 상위 TF 스윙과 하위 TF 스윙을 동시에 보유 (합류 판정용, 조합 사전 고정)
질의 인터페이스
  nearest_level_below(price) → (레벨값, 거리/ATR, 비율)
  is_in_zone(price, k=0.2)   → 어떤 레벨의 ±k×ATR 안에 있는가
필수 기록: 각 레벨의 '생성 봉' 과 '확정 봉' 을 분리 저장
          스윙은 n봉 뒤 확정 — 이 지연을 무시하면 미래 참조 백테스트가 된다

②번 규칙을 특히 강조하고 싶다. 사람이 손으로 그은 피보나치의 최대 문제는 폐기 규칙이 없다는 것이다. 가격이 스윙 저점을 뚫고 내려가도 그 선들은 차트에 남아 있고, 사람은 남은 선을 계속 쳐다본다. 봇에는 무효화 규칙이 코드로 있어야 한다 — 그리고 이 요구사항은 다음 편(엘리엇 파동)의 ‘무효화 라인’ 개념과 정확히 같은 것이다.

왜 하필 이 숫자들인가 — 회의론자의 반론도 들어두자

공정하게, 피보나치 회의론의 논거도 정리해 둔다. 첫째, 0.5는 피보나치 수열과 아무 관계가 없다. 인접 항 비율 어디서도 0.5가 나오지 않는데, 실전에서는 반값 되돌림이 가장 널리 인용된다 — 다우 이론의 전통이 슬쩍 얹혀 산 것이다. ‘피보나치 레벨’이라 불리는 묶음에 비-피보나치가 섞여 있고 아무도 문제 삼지 않는다는 사실 자체가, 이 도구가 수학적 엄밀성으로 쓰이는 게 아니라는 방증이다.

둘째, 레벨의 밀도 문제. 0.236·0.382·0.5·0.618·0.786 다섯 개를 그으면 스윙 범위의 상당 부분이 어떤 레벨의 근처가 된다. 여기에 확장 레벨과 다른 시간축 스윙까지 얹으면 차트는 선으로 뒤덮인다. 사후에 “역시 피보나치에서 돌았다"고 말하기는 너무 쉽다. 셋째, 되돌림 자체의 흔함. 오른 것은 조금 되돌리는 게 정상이므로, 어떤 되돌림 이론이든 기본 적중률이 이미 높다. 그래서 대조군 없이는 아무것도 말할 수 없다.

skeptic's checklist — 피보나치 주장을 만났을 때의 3문
누군가 "피보나치가 먹혔다" 고 말할 때 물어야 할 것
  ① 스윙을 언제 정했나?    사후에 골랐다면 데이터가 아니라 이야기다
  ② 대조군은?              무작위 레벨 대비 우위가 없으면 착시다
  ③ 실패 사례의 수는?      맞은 것만 세면 어떤 이론이든 100%다
이 세 질문은 피보나치 전용이 아니다 — 모든 차트 패턴 주장에 그대로 적용된다
그리고 봇 개발자에게는 셋 다 자동으로 해결된다:
  스윙 정의가 코드에 고정 → ① 해결   전수 재현 → ③ 해결
  대조군만 직접 설계하면 된다 → 위 검증 계획

그럼에도 나는 이 지표를 완전히 버리지는 않는다. 이유는 하나 — 유동성은 사람이 보는 곳에 뭉치기 때문이다. 지지선이 물리 법칙이어서가 아니라, 그 가격에 주문을 걸어둔 사람이 실제로 있어서 체결이 일어난다. SMC 편의 EQH/EQL(유동성 풀)과 정확히 같은 논리 구조다. 그렇다면 검증해야 할 진짜 가설은 “황금비가 신비롭다"가 아니라 “널리 관측되는 가격대 근처에 주문이 뭉친다"이고, 이건 측정 가능하다.

소회

피보나치는 이 코너에서 다룬 지표 중 가장 오래됐고 가장 신비화된 물건이다. 해부해 보니 남는 것은 뺄셈과 곱셈, 그리고 두 개의 진짜 문제 — 스윙 선택의 자의성과 대조군 없는 검증의 착시 — 였다. 신비는 수열에 있지 않았다. 오히려 흥미로운 것은 사회적 현상으로서의 피보나치다. 근거가 희박한 기준선도 충분히 많은 사람이 믿으면 실재가 된다는 것. 시장이 물리계가 아니라 참여자들의 상호 예측 게임이라는 사실을 이만큼 선명하게 보여주는 지표가 없다.

그래서 봇 개발자의 결론은 양가적이다. 황금비의 신화는 버린다 — 그러나 ‘많은 사람이 보는 선 근처에서 유동성이 뭉친다’는 가설은 검증 대상으로 남긴다. 신화와 가설의 차이는 딱 하나, 반증 절차의 유무다. 위의 대조군 실험이 그 절차이고, 결과가 나오면 이 코너에서 속편으로 보고하겠다.

케인즈는 주식 투자를 미인대회에 비유했다 — 내가 예쁘다고 생각하는 얼굴이 아니라, 남들이 예쁘다고 생각할 얼굴을 골라야 이기는 게임. 피보나치 레벨은 그 미인대회의 후보 명단이다. 0.618이 아름다워서가 아니라 모두가 그 번호를 적어내기 때문에 당선되는 것이다. 그러니 물어야 할 것은 황금비의 신비가 아니라, 오늘도 그 번호를 적는 손이 몇이나 남았는가다.