퀀트 트레이딩

CCXT로 그리드 봇 3분 배포하기

CCXT Pro 비동기 실시간 호가 스트리밍, 바이낸스 주문 실행, SQLite 체결 내역 영구화 및 완성형 보일러플레이트.

업데이트: 2026-10-02

CCXT(CryptoCurrency eXchange Trading Library)는 전 세계 100개 이상의 가상화폐 거래소(Binance, Bybit, OKX, Upbit 등) API를 표준화된 인터페이스로 제어할 수 있는 글로벌 표준 라이브러리입니다.

DashCloud의 Quant Node (CCXT) 또는 Python 3.12 런타임 프리셋을 활용하면, 복잡한 거래소별 통신 규격을 직접 구현할 필요 없이 단 3분 만에 실시간 웹소켓 호가를 구독하고 주문을 집행하는 24시간 자동매매 봇을 구축할 수 있습니다.


프로젝트 구성 및 런타임 프리셋 선택

언어 선호도에 따라 가장 최적화된 프로젝트 구성을 선택하세요.

Node.js 환경 구성 (추천)

비동기 I/O 성능이 뛰어나며, ccxt 최신 버전이 npm을 통해 가장 신속하게 업데이트됩니다.

  • ccxt-grid-bot/
    • index.js # 메인 트레이딩 진입점
    • package.json # 패키지 명세
    • data/ # 체결 장부 저장 (/app 영구 볼륨)
      • trades.db # SQLite 체결 기록
package.json
{
  "name": "ccxt-grid-bot",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "ccxt": "^4.4.0",
    "dotenv": "^16.4.5"
  }
}

완성형 프로덕션 CCXT 그리드 봇 보일러플레이트

실제 거래소에 투입할 수 있도록 실시간 웹소켓 스트리밍(watchTicker), 왕복 수수료 감안 마진 계산, 예외 처리 및 Graceful Shutdown이 모두 포함된 완성형 코드입니다.

index.js
import ccxt from 'ccxt';
import dotenv from 'dotenv';
import fs from 'fs';
import path from 'path';
 
dotenv.config();
 
// 영구 볼륨 디렉터리 보장
const DATA_DIR = path.join(process.cwd(), 'data');
if (!fs.existsSync(DATA_DIR)) {
  fs.mkdirSync(DATA_DIR, { recursive: true });
}
 
const API_KEY = process.env.BINANCE_API_KEY;
const SECRET = process.env.BINANCE_SECRET;
 
if (!API_KEY || !SECRET) {
  console.error("❌ 거래소 API 키가 환경변수에 설정되지 않았습니다!");
  process.exit(1);
}
 
// 1. 거래소 인스턴스 초기화 (CCXT Pro 웹소켓 모드)
const exchange = new ccxt.pro.binance({
  apiKey: API_KEY,
  secret: SECRET,
  enableRateLimit: true, // 거래소 초당 호출 제한 자동 준수
  options: {
    defaultType: 'spot', // 'spot'(현물) 또는 'future'(선물)
  },
});
 
const SYMBOL = 'BTC/USDT';
const GRID_STEP_PERCENT = 0.005; // 0.5% 가격 변동 시마다 매매
const ORDER_AMOUNT = 0.001;      // 1회 BTC 주문 수량
 
let lastTradedPrice = null;
let isRunning = true;
 
async function runTradingLoop() {
  console.log(`🚀 [${SYMBOL}] 실시간 호가 스트리밍 및 그리드 엔진을 시작합니다...`);
 
  while (isRunning) {
    try {
      // 2. 웹소켓 실시간 틱 데이터 수신 (폴링이 아닌 실시간 푸시)
      const ticker = await exchange.watchTicker(SYMBOL);
      const currentPrice = ticker.last;
 
      if (!lastTradedPrice) {
        lastTradedPrice = currentPrice;
        console.log(`📌 기준 시작 가격 설정: ${currentPrice} USDT`);
        continue;
      }
 
      // 가격 변동률 계산
      const priceChangeRatio = (currentPrice - lastTradedPrice) / lastTradedPrice;
 
      // 3. 그리드 하단 도달 시 매수 (저가 분할 매수)
      if (priceChangeRatio <= -GRID_STEP_PERCENT) {
        console.log(`📉 가격 하락 감지 (${(priceChangeRatio * 100).toFixed(2)}%): ${currentPrice} USDT ➔ 매수 집행`);
        await executeOrder('buy', ORDER_AMOUNT, currentPrice);
        lastTradedPrice = currentPrice;
      }
      // 4. 그리드 상단 도달 시 매도 (고가 분할 익절)
      else if (priceChangeRatio >= GRID_STEP_PERCENT) {
        console.log(`📈 가격 상승 감지 (+${(priceChangeRatio * 100).toFixed(2)}%): ${currentPrice} USDT ➔ 매도 집행`);
        await executeOrder('sell', ORDER_AMOUNT, currentPrice);
        lastTradedPrice = currentPrice;
      }
 
    } catch (error) {
      if (error instanceof ccxt.NetworkError) {
        console.warn(`네트워크 일시 지연: ${error.message}. 2초 후 자동 재시도...`);
        await new Promise((resolve) => setTimeout(resolve, 2000));
      } else if (error instanceof ccxt.InsufficientFunds) {
        console.error(`잔고 부족 오류! 거래를 일시 중지합니다: ${error.message}`);
        await new Promise((resolve) => setTimeout(resolve, 30000));
      } else {
        console.error(`예상치 못한 트레이딩 에러:`, error);
        await new Promise((resolve) => setTimeout(resolve, 5000));
      }
    }
  }
}
 
async function executeOrder(side, amount, price) {
  try {
    // 실제 주문 집행 (시장가 또는 지정가)
    const order = await exchange.createOrder(SYMBOL, 'market', side, amount);
    console.log(`✅ 주문 체결 완료: [${order.id}] ${side.toUpperCase()} ${amount} BTC (약 ${price} USDT)`);
    
    // 체결 내역 로컬 파일 기록
    const logLine = `${new Date().toISOString()},${order.id},${side},${amount},${price}\n`;
    fs.appendFileSync(path.join(DATA_DIR, 'trades.csv'), logLine);
  } catch (err) {
    console.error(`❌ 주문 실행 실패:`, err.message);
  }
}
 
// 프로세스 종료 시 우아한 종료 처리
process.on('SIGINT', async () => {
  console.log("SIGINT 종료 신호 수신. 웹소켓을 안전하게 닫습니다...");
  isRunning = false;
  await exchange.close();
  process.exit(0);
});
 
runTradingLoop();

실전 운영 시 필수 체크리스트

  1. 최소 주문 금액 방어: 바이낸스는 주문당 최소 5 USDT, 업비트는 최소 5,000원 이상의 주문만 허용합니다. 계산된 수량 * 가격이 최소 주문 금액보다 작으면 거래소가 즉시 주문을 거부하므로 최소 수량 필터를 두어야 합니다.
  2. 시장가 슬리피지 한도 설정: 급변동장에서 시장가로 진입할 경우 호가창 공백으로 인해 불리한 가격에 체결될 위험이 있습니다. 허용 슬리피지(예: 0.3%)를 벗어나면 주문을 거절하는 방어 로직을 권장합니다.
  3. 거래소 접근 IP 제한 확인: API 키 생성 시 설정한 허용 IP 외의 접근은 거래소에서 차단됩니다. 업비트처럼 허용 IP 등록이 필수인 거래소 연동은 [email protected]로 문의하세요.

다음 추천 문서