AlgoLab Blog · 실전 코드 · 2026 작동 보증

빗썸 자동매매 봇 30분 안에 만들기 — Python 볼린저 밴드 풀코드

빗썸 2026-05-30 · 약 12분 읽기 / 실습 30분 · 알고랩 AlgoLab
한 줄 요약 이 글의 코드를 그대로 복붙하면 빗썸에서 30분 안에 작동하는 자동매매 봇을 만들 수 있습니다. 횡보장에 강한 볼린저 밴드 + RSI 평균회귀 전략 / 백테스트 / 페이퍼 트레이딩 / 실거래 전환을 한 파일에 담았습니다. 단, 이 전략 자체가 수익을 보장하지는 않습니다 — 코드 구조를 익히는 학습용입니다.

"빗썸 자동매매" 검색 결과에는 유료 자동매매 광고가 많고, 막상 코드를 끝까지 따라가면 봇이 실제로 도는 글은 의외로 적습니다. 이 글은 빗썸 공식 API 2.0 문서와 GitHub 공개 사례를 기준으로 정리한 가장 기본적인 봇 골격을 30분 안에 따라할 수 있게 압축했습니다.

전제는 빗썸 Open API 키가 발급되어 있다는 것입니다. 마이페이지 → API 관리에서 발급하며, 자산조회·주문 권한만 ON, 출금은 반드시 OFF, IP 화이트리스트 등록을 권장합니다. 빗썸·업비트 코인 자동매매의 일반적인 인프라 패턴은 → 업비트 자동매매 REST·WebSocket 실전 아키텍처에서 함께 다뤘습니다.

30분 안에 만들 것

  1. 1단계 — 환경 세팅 + HMAC-SHA512 인증 이해 (5분)
  2. 2단계 — 시세(캔들) 받기 (5분)
  3. 3단계 — 볼린저 밴드 + RSI 전략 로직 (5분)
  4. 4단계 — 페이퍼 트레이딩 모드 (5분)
  5. 5단계 — 백테스트로 전략 검증 (5분)
  6. 6단계 — 실거래 전환 + 안전장치 (5분)
  7. 통합 풀코드 (한 번에 복붙)
  8. 자주 실수하는 5가지

1환경 세팅 + HMAC-SHA512 인증5분

Python 3.10+ 가 설치되어 있다고 가정합니다.

# 가상환경 생성 + 활성화
python -m venv venv
source venv/bin/activate   # Windows: venv\Scripts\activate

# 필수 패키지 설치
pip install python-bithumb pandas numpy python-dotenv

프로젝트 폴더에 .env 파일을 만들고 발급받은 키를 저장합니다:

# .env (반드시 .gitignore 에 추가)
BITHUMB_CONNECT_KEY=발급받은_Connect_Key
BITHUMB_SECRET_KEY=발급받은_Secret_Key

빗썸 인증 원리 — HMAC-SHA512

빗썸 API는 모든 Private 요청에 HMAC-SHA512 시그니처를 헤더로 넣어야 합니다. endpoint + URL-encoded params + nonce를 Secret Key로 서명하고, Base64로 인코딩한 값이 시그니처가 됩니다. python-bithumb 라이브러리는 이 과정을 내부에서 처리해주므로 입문 단계에서는 신경 쓸 필요가 없습니다. 직접 호출 시에는 다음과 같습니다(참고용):

import hmac, hashlib, base64, time
from urllib.parse import urlencode

def make_signature(endpoint, params, secret_key):
    nonce = str(int(time.time() * 1000))
    query_string = urlencode(params)
    msg = endpoint + chr(0) + query_string + chr(0) + nonce
    h = hmac.new(secret_key.encode(), msg.encode(), hashlib.sha512)
    signature = base64.b64encode(h.hexdigest().encode())
    return signature, nonce

⚠️ .env 를 깃 리포에 커밋하지 마세요. 빗썸 키가 깃허브에 노출되면 자동 스캐너가 즉시 가져갑니다. 출금 권한을 OFF로 발급했더라도 거래 권한만으로 자산을 망가뜨릴 수 있습니다. .gitignore.env를 꼭 추가하세요.

2시세(캔들) 받기5분

볼린저 밴드 전략은 1시간봉을 사용합니다(일봉은 신호가 너무 드물고, 분봉은 노이즈가 큼). python-bithumb으로 BTC 1시간봉 데이터를 가져옵니다:

import python_bithumb as pb
import pandas as pd

def get_ohlcv(ticker="KRW-BTC", interval="hour1", count=200):
    """빗썸 캔들 데이터 → pandas DataFrame"""
    df = pb.get_ohlcv(ticker, interval=interval, count=count)
    # df 컬럼: open, high, low, close, volume
    return df

df = get_ohlcv()
print(df.tail())
print("현재가:", pb.get_current_price("KRW-BTC"))

실행 후 최근 캔들과 현재가가 출력되면 성공입니다. 빗썸은 한국 거래소라 별도 IP 우회 없이 바로 조회됩니다.

💡 빗썸 티커 표기: python-bithumb에서는 KRW-BTC(원화마켓), KRW-ETH 형식을 사용합니다. 빗썸 REST API 자체는 BTC_KRW 형식이지만 라이브러리가 변환해줍니다. 입문자는 거래량이 가장 큰 원화마켓(KRW-)으로 시작하세요.

3볼린저 밴드 + RSI 전략 로직5분

볼린저 밴드(Bollinger Bands)는 가격이 이동평균에서 표준편차(σ) 만큼 벗어나는 범위를 시각화한 지표입니다. 평균회귀(Mean Reversion) 전략의 클래식 도구로, 횡보장·박스권에서 잘 작동합니다. 원리는 단순합니다:

import numpy as np

def add_indicators(df, window=20, std=2):
    """볼린저 밴드, RSI 컬럼 추가"""
    # 볼린저 밴드
    df["ma"] = df["close"].rolling(window).mean()
    df["std"] = df["close"].rolling(window).std()
    df["upper"] = df["ma"] + std * df["std"]
    df["lower"] = df["ma"] - std * df["std"]

    # RSI 14
    delta = df["close"].diff()
    gain = delta.clip(lower=0).rolling(14).mean()
    loss = -delta.clip(upper=0).rolling(14).mean()
    rs = gain / loss
    df["rsi"] = 100 - (100 / (1 + rs))
    return df

def get_signal(df, held):
    """매매 신호: 'BUY' / 'SELL' / None"""
    if len(df) < 22:
        return None
    last = df.iloc[-1]
    # 매수: 하단 밴드 아래 + RSI 과매도
    if not held and last["close"] < last["lower"] and last["rsi"] < 30:
        return "BUY"
    # 매도: 중심선 복귀
    if held and last["close"] >= last["ma"]:
        return "SELL"
    return None

💡 왜 볼린저 밴드 + RSI 조합인가: 볼린저 밴드만으로는 강추세 하락 시 "계속 하단을 갱신"하면서 매수가 누적되는 문제가 있습니다. RSI 30 필터로 "진짜 과매도"인 순간만 잡아 가짜 신호를 줄입니다. 완벽하지는 않지만 횡보장에서는 효과적입니다.

4페이퍼 트레이딩 모드5분

실거래 전에 가상으로 매매를 돌립니다. 빗썸은 공식 테스트넷이 없으므로 이 페이퍼 트레이딩이 필수입니다.

class PaperTrader:
    """실거래 대신 가상 잔고로 시뮬레이션"""
    def __init__(self, cash=1_000_000, fee=0.0025):  # 빗썸 기본 수수료 0.25%
        self.cash = cash
        self.coin = 0.0
        self.avg_price = 0.0
        self.fee = fee

    def buy(self, price):
        if self.cash < 5000:   # 빗썸 최소 주문 5,000원
            return
        qty = (self.cash * (1 - self.fee)) / price
        self.coin = qty
        self.avg_price = price
        print(f"[PAPER BUY] price={price:,.0f} qty={qty:.8f}")
        self.cash = 0

    def sell(self, price):
        if self.coin == 0:
            return
        proceeds = self.coin * price * (1 - self.fee)
        pnl = proceeds - (self.avg_price * self.coin)
        self.cash = proceeds
        print(f"[PAPER SELL] price={price:,.0f} pnl={pnl:+,.0f}원")
        self.coin = 0
        self.avg_price = 0

    def value(self, price):
        return self.cash + self.coin * price

💡 빗썸 수수료: 빗썸 일반 수수료는 0.25%로 업비트(0.05%)보다 다소 높습니다. 쿠폰 구매 시 0.04~0.10%까지 낮출 수 있어 자동매매 운영자는 쿠폰 사용을 권장합니다. 위 코드에서는 안전하게 0.25%로 시뮬레이션합니다.

5백테스트로 전략 검증5분

실거래·페이퍼 시작 전에 과거 데이터로 전략이 어떻게 행동했는지 확인합니다:

def backtest(ticker="KRW-BTC", interval="hour1", count=500, initial=1_000_000):
    df = pb.get_ohlcv(ticker, interval=interval, count=count)
    df = add_indicators(df).dropna().reset_index(drop=True)
    pt = PaperTrader(cash=initial)
    held = False

    for i in range(len(df)):
        window = df.iloc[:i+1]
        sig = get_signal(window, held)
        price = window.iloc[-1]["close"]
        if sig == "BUY":
            pt.buy(price)
            held = True
        elif sig == "SELL":
            pt.sell(price)
            held = False

    final = pt.value(df.iloc[-1]["close"])
    bnh = initial * df.iloc[-1]["close"] / df.iloc[0]["close"]

    print(f"\n[Backtest] {ticker} {len(df)}봉 (1시간봉)")
    print(f"전략 수익: {final:,.0f}원 ({(final/initial-1)*100:+.1f}%)")
    print(f"단순보유: {bnh:,.0f}원 ({(bnh/initial-1)*100:+.1f}%)")
    return pt

if __name__ == "__main__":
    backtest()

출력의 전략 수익 vs 단순보유(Buy & Hold)를 비교하세요. 전략이 단순보유보다 못하다면 그 파라미터 조합은 의미가 없습니다.

💡 백테스트 함정: 위 백테스트는 슬리피지, 호가 스프레드, 거래 정체를 무시한 단순 시뮬레이션입니다. 볼린저 밴드 전략은 특히 하단 밴드 매수 시 매도호가가 멀리 있어 슬리피지가 큽니다. 자세히는 → 백테스트와 실전의 괴리 줄이기 참고.

6실거래 전환 + 안전장치5분

충분히 검증됐다면 실거래로 전환합니다. paper_trade 플래그 하나로 전환되게 만드는 것이 사고를 줄이는 핵심입니다.

import os, time
import python_bithumb as pb
from dotenv import load_dotenv

load_dotenv()
bithumb = pb.Bithumb(os.getenv("BITHUMB_CONNECT_KEY"), os.getenv("BITHUMB_SECRET_KEY"))

def run_bot(ticker="KRW-BTC", interval="hour1", invest_krw=10000, paper_trade=True):
    """볼린저 밴드 봇 메인 루프"""
    pt = PaperTrader(cash=invest_krw) if paper_trade else None
    held = False
    last_check_hour = -1

    while True:
        try:
            now = time.localtime()
            # 매 시간이 바뀔 때만 신호 체크 (1시간봉 기준)
            if now.tm_hour == last_check_hour:
                time.sleep(60)
                continue
            last_check_hour = now.tm_hour

            df = pb.get_ohlcv(ticker, interval=interval, count=100)
            df = add_indicators(df).dropna().reset_index(drop=True)
            sig = get_signal(df, held)
            current = pb.get_current_price(ticker)

            if sig == "BUY" and not held:
                if paper_trade:
                    pt.buy(current)
                else:
                    krw = bithumb.get_balance("KRW")
                    if krw > 5000:
                        # 시장가 매수: 금액 기준
                        bithumb.buy_market_order(ticker, krw * 0.997)
                        print(f"[LIVE BUY] {ticker} {krw:,.0f}원")
                held = True

            elif sig == "SELL" and held:
                if paper_trade:
                    pt.sell(current)
                else:
                    coin = bithumb.get_balance(ticker)
                    if coin > 0:
                        # 시장가 매도: 수량 기준
                        bithumb.sell_market_order(ticker, coin)
                        print(f"[LIVE SELL] {ticker} {coin:.8f}")
                held = False

            print(f"[{time.strftime('%H:%M')}] price={current:,.0f} sig={sig} held={held}")
            time.sleep(60)
        except Exception as e:
            print(f"[ERROR] {e}")
            time.sleep(60)

if __name__ == "__main__":
    run_bot(paper_trade=True)   # ← 처음엔 무조건 True

✅ 실거래 전환 체크리스트

통합 풀코드 (한 번에 복붙)

위 단계를 모두 합친 단일 파일입니다. bithumb_bot.py로 저장하고 python bithumb_bot.py 실행:

import os, time
import python_bithumb as pb
from dotenv import load_dotenv

load_dotenv()
bithumb = pb.Bithumb(os.getenv("BITHUMB_CONNECT_KEY"), os.getenv("BITHUMB_SECRET_KEY"))

def add_indicators(df, window=20, std=2):
    df["ma"] = df["close"].rolling(window).mean()
    df["std"] = df["close"].rolling(window).std()
    df["upper"] = df["ma"] + std * df["std"]
    df["lower"] = df["ma"] - std * df["std"]
    delta = df["close"].diff()
    gain = delta.clip(lower=0).rolling(14).mean()
    loss = -delta.clip(upper=0).rolling(14).mean()
    df["rsi"] = 100 - (100 / (1 + gain/loss))
    return df

def get_signal(df, held):
    if len(df) < 22: return None
    l = df.iloc[-1]
    if not held and l["close"] < l["lower"] and l["rsi"] < 30: return "BUY"
    if held and l["close"] >= l["ma"]: return "SELL"
    return None

class PaperTrader:
    def __init__(self, cash=1_000_000, fee=0.0025):
        self.cash, self.coin, self.avg, self.fee = cash, 0.0, 0.0, fee
    def buy(self, price):
        if self.cash < 5000: return
        self.coin = (self.cash * (1 - self.fee)) / price
        self.avg, self.cash = price, 0
        print(f"[PAPER BUY] {price:,.0f} qty={self.coin:.8f}")
    def sell(self, price):
        if self.coin == 0: return
        proceeds = self.coin * price * (1 - self.fee)
        pnl = proceeds - (self.avg * self.coin)
        print(f"[PAPER SELL] {price:,.0f} pnl={pnl:+,.0f}원")
        self.cash, self.coin, self.avg = proceeds, 0, 0

def run_bot(ticker="KRW-BTC", interval="hour1", invest_krw=10000, paper_trade=True):
    pt = PaperTrader(cash=invest_krw) if paper_trade else None
    held = False
    last_h = -1
    while True:
        try:
            now = time.localtime()
            if now.tm_hour == last_h:
                time.sleep(60); continue
            last_h = now.tm_hour
            df = add_indicators(pb.get_ohlcv(ticker, interval=interval, count=100)).dropna().reset_index(drop=True)
            sig = get_signal(df, held)
            current = pb.get_current_price(ticker)
            if sig == "BUY" and not held:
                if paper_trade: pt.buy(current)
                else:
                    krw = bithumb.get_balance("KRW")
                    if krw > 5000: bithumb.buy_market_order(ticker, krw * 0.997)
                held = True
            elif sig == "SELL" and held:
                if paper_trade: pt.sell(current)
                else:
                    coin = bithumb.get_balance(ticker)
                    if coin > 0: bithumb.sell_market_order(ticker, coin)
                held = False
            print(f"[{time.strftime('%H:%M')}] {current:,.0f} sig={sig} held={held}")
            time.sleep(60)
        except Exception as e:
            print(f"[ERR] {e}"); time.sleep(60)

if __name__ == "__main__":
    run_bot(paper_trade=True)   # 실거래는 False, 검증 끝나면 변경

자주 실수하는 5가지

1. 시장가 매수/매도 파라미터 혼동

빗썸 시장가 주문은 매수는 KRW 금액, 매도는 코인 수량으로 지정합니다. 업비트와 동일한 비대칭 구조입니다. buy_market_order(ticker, 10000)은 1만원어치 매수, sell_market_order(ticker, 0.001)은 0.001 코인 매도. 이걸 바꿔 넣으면 주문이 거부되거나 의도와 다르게 체결됩니다.

2. 잔고 전액 매수 → 수수료 부족으로 주문 실패

get_balance("KRW") 전액을 그대로 매수하면 수수료(0.25%)를 낼 돈이 없어 거부됩니다. 위 코드처럼 0.997을 곱해 수수료 여유분을 남기세요(쿠폰 미사용 기준). 쿠폰으로 수수료가 낮아지면 계수도 조정.

3. paper_trade=False 로 두고 실수로 실행

코드 마지막 줄 기본값은 항상 True로. 실거래 전환은 명시적으로 False로 바꿀 때만.

4. nonce 충돌 — "Nonce already used" 에러

빗썸 인증은 nonce(타임스탬프)가 매번 증가해야 합니다. 같은 millisecond에 두 번 호출하거나, 봇을 두 인스턴스 동시에 돌리면 충돌이 발생합니다. python-bithumb은 내부적으로 이를 관리하지만, 직접 구현 시에는 nonce를 마이크로초 단위로 만들거나 락(lock)으로 직렬화해야 합니다.

5. Rate Limit 무시 → 차단

빗썸 Public API는 분당 약 90회, Private API는 더 엄격합니다. 빠른 루프로 현재가를 계속 조회하면 일정 시간 차단됩니다. 위 코드는 time.sleep(60)으로 여유를 뒀습니다. 다종목 운영 시에는 호출 간격 관리가 필수입니다.

다음 단계 — 진짜 운용 가능한 봇으로

위 골격은 학습용 출발점입니다. 실제 운용하려면 다음을 추가해야 합니다:

이 모든 것을 직접 구현하는 게 부담스럽다면, 알고랩이 1~3주 안에 위 골격 + 안전장치 + 모니터링 + 무중단 운영까지 통합된 빗썸 봇을 만들어드립니다.

자주 묻는 질문

Q. python-bithumb과 직접 API 호출 중 무엇을 쓰나요?

입문은 python-bithumb 권장. HMAC-SHA512 서명·nonce 관리·페이로드 인코딩을 모두 처리. 정밀 제어가 필요하면 requests + hmac/hashlib로 직접.

Q. 볼린저 밴드 + RSI 전략으로 돈을 벌 수 있나요?

횡보장·박스권 클래식 전략입니다. 강추세 하락장에서는 손실 누적. 손절·추세 필터·자산 배분이 추가로 필요합니다.

Q. 빗썸 API 키는 어떻게 발급하나요?

빗썸 마이페이지 → API 관리. Connect Key + Secret Key 한 쌍. 자산조회·주문 권한만 ON, 출금 OFF, IP 화이트리스트 등록 권장.

Q. 실거래 전 안전한 테스트 방법은?

빗썸은 공식 테스트넷이 없습니다. (1) 페이퍼 트레이딩 1~2주 (2) 과거 데이터 백테스트 (3) 5,000원 수준 실거래 1주일 순서로.

Q. 빗썸과 업비트 봇은 코드가 같나요?

구조는 비슷하지만 인증(HMAC-SHA512 vs JWT), 티커 표기, 응답 구조가 다릅니다. 업비트 버전은 → 업비트 자동매매 봇 30분 참고.

Q. 봇을 24시간 돌리려면?

VPS(Vultr, AWS Lightsail, Oracle Free Tier)에서 systemd / PM2로 실행. 1시간봉 기준이라 1분 주기 체크로 충분합니다.

빗썸 봇 맞춤 제작

위 골격에 손절 규칙 / 다종목 분산 / WebSocket / 텔레그램 알림 / VPS 무중단 운영까지 포함한 통합 봇을 알고랩이 만들어드립니다.
24시간 빠른 답변 가능합니다.

무료 상담 시작하기