파이썬 TWAP 주문 실행 전략

TWAP 주문이란?

TWAP(Time-Weighted Average Price)는 대량 주문을 일정 시간 간격으로 분할하여 체결하는 알고리즘 주문 실행 전략입니다. 기관 투자자와 퀀트 트레이더가 시장 충격(Market Impact)을 최소화하기 위해 널리 사용합니다. 한 번에 큰 물량을 시장가로 던지면 호가창이 밀리면서 불리한 가격에 체결되는데, TWAP는 이 문제를 해결합니다.

TWAP와 자주 비교되는 VWAP(거래량 가중 평균가) 전략은 거래량 분포를 기반으로 주문 크기를 조절하는 반면, TWAP는 시간만을 기준으로 균등 분할합니다. 단순하지만 효과적이라 자동매매 시스템의 핵심 모듈로 자리잡고 있습니다.

TWAP vs 시장가 주문 비교

  • 시장가 일괄 주문: 즉시 체결되지만, 대량 주문 시 슬리피지가 크고 시장 충격이 심합니다.
  • TWAP 분할 주문: 정해진 시간 동안 균등하게 나눠 체결하여 평균 단가를 개선합니다.
  • 체결 속도: 시장가는 즉시, TWAP는 설정 시간에 비례하여 완료됩니다.
  • 적합한 상황: 소량 주문은 시장가, 일일 거래량 대비 1% 이상의 대량 주문은 TWAP가 유리합니다.

TWAP 핵심 파라미터

효과적인 TWAP 전략을 설계하려면 세 가지 핵심 파라미터를 이해해야 합니다.

  • 총 실행 시간(Duration): 전체 주문을 완료할 시간 범위입니다. 너무 짧으면 시장 충격이 크고, 너무 길면 가격 변동 리스크에 노출됩니다.
  • 분할 간격(Interval): 각 슬라이스 주문 사이의 시간 간격입니다. 보통 1분~5분 간격을 사용합니다.
  • 슬라이스 수량(Slice Size): 총 수량을 분할 횟수로 나눈 개별 주문 크기입니다. 균등 분할이 기본이지만, 랜덤 노이즈를 추가하기도 합니다.

파이썬 TWAP 구현

아래는 파이썬으로 구현한 TWAP 주문 엔진의 핵심 로직입니다. ccxt 라이브러리를 활용하여 거래소 API와 연동합니다.

import ccxt
import time
import math

class TWAPEngine:
    def __init__(self, exchange, symbol, side, total_qty,
                 duration_min=60, interval_sec=60):
        self.exchange = exchange
        self.symbol = symbol
        self.side = side  # 'buy' or 'sell'
        self.total_qty = total_qty
        self.duration_min = duration_min
        self.interval_sec = interval_sec

        self.num_slices = math.ceil(
            (duration_min * 60) / interval_sec
        )
        self.slice_qty = total_qty / self.num_slices
        self.filled = 0
        self.orders = []

    def execute(self):
        """TWAP 주문 실행 루프"""
        for i in range(self.num_slices):
            remaining = self.total_qty - self.filled
            qty = min(self.slice_qty, remaining)
            if qty <= 0:
                break

            try:
                order = self.exchange.create_market_order(
                    self.symbol, self.side, qty
                )
                self.filled += order['filled']
                self.orders.append(order)
                avg = self._calc_avg_price()
                print(f"[{i+1}/{self.num_slices}] "
                      f"체결: {order['filled']:.4f} @ "
                      f"{order['average']:.2f} | "
                      f"평균단가: {avg:.2f}")
            except Exception as e:
                print(f"주문 실패: {e}")

            if i < self.num_slices - 1:
                time.sleep(self.interval_sec)

        return self._summary()

    def _calc_avg_price(self):
        total_cost = sum(
            o['filled'] * o['average']
            for o in self.orders if o['average']
        )
        total_filled = sum(
            o['filled'] for o in self.orders
        )
        return total_cost / total_filled if total_filled else 0

    def _summary(self):
        return {
            'total_filled': self.filled,
            'avg_price': self._calc_avg_price(),
            'num_orders': len(self.orders),
            'completion': self.filled / self.total_qty * 100
        }

# 사용 예시
exchange = ccxt.binance({
    'apiKey': 'YOUR_KEY',
    'secret': 'YOUR_SECRET'
})

twap = TWAPEngine(
    exchange=exchange,
    symbol='BTC/USDT',
    side='buy',
    total_qty=0.5,       # 총 0.5 BTC 매수
    duration_min=30,      # 30분에 걸쳐
    interval_sec=60       # 1분 간격
)
result = twap.execute()
print(f"완료: {result['completion']:.1f}% | "
      f"평균단가: ${result['avg_price']:,.2f}")

TWAP 고급 최적화 기법

기본 TWAP를 실전에서 더 효과적으로 만드는 고급 기법들을 살펴봅니다.

1. 랜덤화(Randomization)

균등 분할은 다른 알고리즘 트레이더에게 패턴이 노출될 수 있습니다. 슬라이스 수량과 간격에 ±20% 랜덤 노이즈를 추가하면 탐지를 어렵게 만들 수 있습니다.

import random

def randomized_qty(base_qty, noise_pct=0.2):
    factor = 1 + random.uniform(-noise_pct, noise_pct)
    return base_qty * factor

def randomized_interval(base_sec, noise_pct=0.2):
    factor = 1 + random.uniform(-noise_pct, noise_pct)
    return base_sec * factor

2. 지정가 주문 활용

시장가 대신 지정가 주문을 사용하면 슬리피지를 더 줄일 수 있습니다. 현재 최우선 호가에 지정가를 걸고, 일정 시간 내 미체결 시 취소 후 재주문하는 방식입니다.

def place_limit_with_timeout(exchange, symbol, side,
                              qty, timeout_sec=10):
    ticker = exchange.fetch_ticker(symbol)
    # 매수: 최우선 매도호가, 매도: 최우선 매수호가
    price = ticker['ask'] if side == 'buy' else ticker['bid']

    order = exchange.create_limit_order(
        symbol, side, qty, price
    )

    time.sleep(timeout_sec)
    status = exchange.fetch_order(order['id'], symbol)

    if status['status'] != 'closed':
        exchange.cancel_order(order['id'], symbol)
        # 미체결분 시장가로 처리
        remaining = qty - status['filled']
        if remaining > 0:
            market = exchange.create_market_order(
                symbol, side, remaining
            )
            return status['filled'] + market['filled']
    return status['filled']

3. 체결률 모니터링

TWAP 실행 중 체결률이 예상보다 낮으면 후반부에 물량이 몰려 시장 충격이 커질 수 있습니다. 실시간으로 체결률을 추적하고, 지연 시 슬라이스 수량을 자동 조정하는 로직이 필요합니다.

def adjust_slice_qty(total_qty, filled, remaining_slices):
    """남은 물량을 남은 슬라이스에 균등 재분배"""
    unfilled = total_qty - filled
    if remaining_slices <= 0:
        return unfilled
    return unfilled / remaining_slices

TWAP 실전 적용 시 주의사항

  • 유동성 확인: 해당 종목의 일일 거래량 대비 TWAP 총 수량이 10%를 넘지 않도록 합니다.
  • 시간대 선택: 거래량이 적은 새벽 시간대는 피하고, 활발한 거래 시간에 실행합니다.
  • 급변 대응: 급격한 가격 변동 시 TWAP를 일시 중단하는 서킷 브레이커를 구현해야 합니다.
  • API 제한: 거래소별 주문 빈도 제한(Rate Limit)을 반드시 확인하고 간격을 조절합니다.
  • 최소 주문 단위: 분할 시 거래소의 최소 주문 수량 이하로 떨어지지 않도록 주의합니다.

TWAP 성과 측정

TWAP 전략의 성과는 Implementation Shortfall로 측정합니다. 이는 의사결정 시점의 가격과 실제 평균 체결가의 차이입니다.

def calc_implementation_shortfall(decision_price,
                                   avg_fill_price,
                                   side):
    """
    Implementation Shortfall 계산
    매수: 양수면 비용 발생, 음수면 이득
    """
    if side == 'buy':
        return (avg_fill_price - decision_price) / decision_price * 100
    else:
        return (decision_price - avg_fill_price) / decision_price * 100

# 예: 의사결정가 $84,000, 평균 체결가 $84,120
shortfall = calc_implementation_shortfall(84000, 84120, 'buy')
print(f"Implementation Shortfall: {shortfall:.3f}%")
# 출력: Implementation Shortfall: 0.143%

일반적으로 TWAP의 Implementation Shortfall은 시장가 일괄 주문 대비 30~60% 개선되는 것으로 알려져 있습니다. 특히 유동성이 낮은 알트코인에서 효과가 두드러집니다.

마무리

TWAP는 단순하면서도 강력한 주문 실행 알고리즘입니다. 대량 주문의 시장 충격을 줄이고 평균 체결 단가를 개선하는 데 효과적입니다. 랜덤화, 지정가 활용, 체결률 모니터링 등의 고급 기법을 결합하면 실전에서도 충분히 경쟁력 있는 실행 엔진을 구축할 수 있습니다. 자동매매 시스템을 운영한다면 TWAP 모듈은 반드시 갖춰야 할 핵심 구성 요소입니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux