슬리피지를 고려한 시장가 시뮬레이션

슬리피지란?

슬리피지(Slippage)란 우리가 주식시장에 시장가 주문시 원하는 가격 대비 높거나 낮은 금액으로 체결되는 현상을 말합니다. 주된 원인은 거래량이 충분치 않거나, 주문량이 너무 많거나, 시장의 가격이 급변하거나, 또는 주문이 지연되어 처리될때 발생될 수 있습니다.

예를들어, 20,000원짜리 주식의 호가는 코스피/코스닥 공히 50원 단위입니다. 20,000원에 체결되길 희망하며 시장가로 매수 주문을 낸다면, 20,000원에 그대로 주문이 체결되는 경우도 많겠지만, 여러가지 원인에 의해 20,050원이나 20,100원에 체결되기도 합니다.

자동주식거래 시스템 개발시 이러한 슬리피지를 전혀 고려하지 않는다면, 백테스터(Backtester)에서 높은 수익을 가져오는 훌륭한 전략도 시장에서는 수익을 전혀 내지 못할 가능성도 있습니다. 다수의 거래시 수익의 상당부분을 슬리피지가 까먹을 수 있기 때문입니다. 따라서, 백테스터에 슬리피지 시뮬레이션을 기능을 넣는 것이 투자전략 개발의 중요한 요소가 됩니다.

슬리피지 구현시 고려사항

그렇다면, 슬리피지 발생확률과 크기를 어떻게 결정하면 좋을까요? 개별 종목별로 주문 시점의 거래량과 가격변동을 동시에 고려해 슬리피지를 계산해낼 수 있을 것입니다. 하지만, 모든 데이터를 고려한다고 해서 정확하게 계산해낼 수 있을까요? 과연 슬리피지란 정확하게 계산해낼 수 있는 것이긴 할까요? 우리는 투자전략 개발에서와 마찬가지로 언제나 과적합(Overfitting)의 유혹에 빠지지 않도록 주의해야 합니다.

합리적인 수준에서 가능한 단순한 구현을 위해, 시스템의 기본적인 가정에 대해 살펴보겠습니다. 제 경우에는, 터무니 없이 적은 거래량으로 인해 과도한 슬리피지가 발생하지 않도록 일정수준 이상의 거래량을 갖춘 종목으로 유니버스를 구성합니다. 또한 한정된 규모의 자본으로 다수의 종목을 거래하기 때문에 한 종목에 대한 주문량은 크지 않습니다. 따라서, 결과적으로 시장의 급격한 가격변동 및 주문이 지연되어 처리되는 경우에 따른 슬리피지만 고려하면 됩니다.

급격한 가격변동과 주문지연으로 슬리피지가 발생하는 것은 상황에 따라 다릅니다. 어떠한 조건이 충족되면 무조건 발생하는 것이 아니라, 발생할 수도 있고 발생하지 않을 수도 있습니다. 말그대로 확률의 문제입니다. 따라서, 확률함수를 활용해 구현하는 방법을 택했습니다. 몇가지 확률함수를 테스트해본 결과, 정규분포를 사용하는 것으로 결정했습니다.

슬리피지 모델링

정규분포는 2개의 매개변수인 평균 μ와 표준편차 σ에 의해 확률분포의 모양이 결정됩니다. 그간의 짧은 경험으로 평균과 표준편차를 적절히 조절하는 것으로 슬리피지 발생확률와 크기를 파이썬으로 모델링해 보았습니다.

매수하는 경우, 정규분포 함수에 매개변수로 평균 0.5, 표준편차 0.75를 넘겨주고, 결과값을 소수점 이하를 버림하여 정수값으로 바꾸어 히스토그램으로 그려주는 코드입니다.

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

sns.distplot([int(n) for n in np.random.normal(0.5, 0.75, size=1000)], kde=False)

위 코드 실행결과, 아래와 같은 슬리피지 확률분포를 확인할 수 있습니다. 그래프의 x축은 의도한 호가 대비 실제 체결된 호가의 차이를 나타냅니다. 0이 의미하는 것은, 슬리피지가 발생하지 않는 경우이며 약 70%의 확률로 보입니다. 약 25%의 확률로 1호가 높은 슬리피지가 발생할 수 있음을 보여주고 있습니다. 또한, 매우 낮은 확률로 2호가 높은 슬리피지가 발생할 수도 있으며, 더 낮은 확률로 1호가 낮은 슬리피지도 발생할 수 있음을 보여줍니다.

Histogram for buy

이번에는 반대로, 매도하는 경우의 정규분포 함수에 따른 슬리피지 확률분포률 확인해 보았습니다. 평균을 -0.5로 전달해 히스토그램을 그려보면, 매수의 경우와 대칭되는 형태로 그래프가 그려지는 것을 확인할 수 있습니다.

sns.distplot([int(n) for n in np.random.normal(-0.5, 0.75, size=1000)], kde=False)

Histogram for sell

시장가 시뮬레이션 구현

이제 백테스터에서 가상의 주문을 처리하는 Broker 모듈에서 슬리피지를 감안해 시장가를 결정하는 루틴을 구현할 차례입니다.

앞서 기술한 바와 마찬가지로, 정규분포 확률함수에 적절한 평균 μ와 표준편차 σ를 전달해 슬리피지를 모델링 했습니다. 표준편차에 전달되는 값에 따라 슬리피지의 분포가 크게 달라지므로, 백테스터의 환경설정을 통해 손쉽게 바꿀 수 있도록 합니다.

config = {
    'slippage_stdev': 0.75,
}

시장가를 결정하는 함수입니다. 정규함수 확률함수에 평균과 표준편차를 전달해 슬리피지의 호가차이를 의미하는 offset을 구합니다. 평균은 매수일땐 0.5, 매도일땐 -0.5를 사용하고, 표준편차는 앞서 백테스터의 환경설정 값을 사용하도록 합니다. 이렇게 구해낸 offset에 호가단위를 곱해 당초 주문가에 더해주면 시장가 시뮬레이션이 끝납니다.

def simulate_market_price(order: Order, slippage_stdev: float) -> float:
    offset = int(np.random.normal(
        0.5 if order.quantity > 0 else -0.5,
        slippage_stdev))

    unit_price = _get_unit_price(order.price)

    return order.price + unit_price * offset

참고로, 코스피의 호가단위를 산출하는 코드는 이렇게 되어 있습니다.

UNIT_PRICE_MAX = 1000
UNIT_PRICES = [(1000, 1), (5000, 5), (10000, 10),
               (50000, 50), (100000, 100), (500000, 500)]

def _get_unit_price(price: float) -> float:
    for p, u in UNIT_PRICES:
        if price < p:
            return u

    return UNIT_PRICE_MAX

결론

자동주식거래 시스템을 개발하는 목적은, 다수의 종목을 모니터링하는 가운데 절호의 찬스에 주문을 체결시켜 높은 수익을 얻기 위함입니다. 여기에는 시장에 참여하는 가운데 쉽사리 겪게되는 심리적인 위축과 자만에 따른 매매를 배제하고, 기계적인 매매를 통해 안정적인 수익을 얻기위한 목적도 있습니다.

자동주식거래 시스템의 핵심인 투자전략 개발은 수십억건의 빅데이터를 백테스터에 탑재해 가상으로 주문을 체결시키고 수익성을 검토하는 반복된 과정입니다. 그 결과, 사람이 손으로 처리하기 어려울 만큼 많은 종목이 빈번하게 거래되며, 그에 따른 거래비용은 큰 폭으로 증가해 수익을 잠식해 나갑니다. 따라서, 백테스트 단계부터 거래비용을 빠짐없이 고려해야만 실제 매매에서 수익을 낼 수 있는 투자전략을 개발할 수 있을 것입니다.

comments powered by Disqus