본문 바로가기
실전머신러닝

워렌 버핏도 쓴다는 켈리의 공식, 과연 유용한 것인가?

by 다빈치스탁 2024. 6. 7.
728x90
반응형

https://youtu.be/HXajHxFfYtQ

 

안녕하세요, 다빈치스탁의 시청자 여러분. 오늘은 투자의 거장 워렌 버핏도 높이 평가한다는 켈리의 공식에 대해 살펴보겠습니다.

 

켈리 공식은 반복적으로 베팅을 할 때 파산하지 않고 최대의 수익을 얻기 위해 어느 정도 비율로 베팅해야 하는지 계산하는 공식으로, 연구소에서 근무했던 Kelly라는 연구원이 개발했는데요. 그러나 공식의 발표 초기만 하더라도 현실성에 대해 많은 의문점이 제기 되었습니다.

 

이에 켈리는 가지 실험을 통해 단순화된 가정 하의 대부분의 도박과 몇몇 투자 시나리오에서, 장기적으로 다른 어떤 전략보다 우월한 성과를 낸다는 현실적 유용성을 검증하였습니다.

 

하지만 이건 모든 게임이나 투자에 적용되지는 않습니다. 그런지 살펴볼까요? 우선 켈리의 공식은 다음과 같이 계산됩니다. 여기서 f는 배당 비율, b는 단위 금액 당 배당 금액, p는 승리 확률, 마지막으로 q는 패배 확률인데요.

 

만약 이기면 만큼 받는 동전 던지기 게임을 한다면, 승률은 50% 되고 배당률은 1이되기 때문에 공식에 대입하면 0으로 계산되기 때문이죠. 따라서 아주 작은 금액이라도 투자하는 것이 유리하기 위해서는 승률과 배당률 하나는 높아야 합니다.

 

또한 배당률과 승률이 낮을 경우 음수로 계산되기 때문에 일반적인 도박에서는 허용되지 않는 역베팅 방법이 됩니다. 그러나 이 공식을 주식투자에 대입한다면 이야기가 달라지는데요. 우선 주식투자는 동전 던지기와 같은 순수한 무작위 게임과는 거리가 있죠.

 

아울러 건 돈을 다 날리는 것이 아닌 등락률에 따라 1보다 크거나 작은 배당률을 받습니다. 그리고 음수의 베팅률이 계산되더라도 매도로서 대응이 가능하죠. 어때요, 그림이 그려지시나요? 아직 잘 모르시겠다구요?

 

그럼 지금부터 몇 가지 실험을 통해 이 공식의 유용성에 대해 검증해보겠습니다. 실험에 사용할 종목은 KODEX200 ETF로 지정하고, 매일 1주씩 분할 매수하는 방법과 켈리의 공식에 입각한 방식을 비교해서 분석해보겠습니다.

 

첫 째, 거래시작일부터 오늘까지 매일 1주씩 분할 매수를 수행했다면 어떤 결과가 나올까요? 시뮬레이션 결과 약 11%의 평가손익으로 계산됩니다. 그리고 필요한 투자 금액은 평균단가인 32425원 곱하기 500거래일을 적용하여 약 1550만원 정도되죠.

 

이어서 이와 동일한 금액인 1550만원에 대해 켈리의 공식을 사용하여 시뮬레이션 해볼건데요. 그전에 켈리의 공식을 적용하기 위해서는 몇 가지 가정이 필요합니다. 바로 배당률과 승률에 대한 가정입니다.

 

우선 승률은 테스트 기간인 과거 2년 동안 전일 대비 상승했으면 승리, 하락했으면 패배라고 가정하여 측정한 결과인 51%를 사용하였고, 매 거래일의 등락률을 배당률로 산정하여 시뮬레이션 해보았습니다.

 

측정 결과 최종 평균 단가는 31322원으로 낮아져 약 15%의 평가 수익으로 계산됩니다. 물론 매일 1주씩 분할 매수한 결과보다는 우수한 것으로 산출되었지만, 유의미한 결과라 보기에는 차이가 너무 미미하다고 볼 수 있죠.

 

그래서 이번에는 동 종목에 대해 실험 구간을 과거 2년이 아닌 4년으로 변경하여 시뮬레이션 해본 결과 분할 매수는 4.52%, 켈리의 공식은 30.49%로 압도적 우위를 보였습니다. 우연의 결과일 수도 있기에 이번에는 나스닥 지수를 추종하는 QQQ ETF를 대상으로 실험을 진행해보겠습니다.

 

실험결과 분할 매수는 35.56%, 켈리의 공식은 93.56%로 이번에도 압도적인 승리였습니다. 이외에도 엔비디아의 경우 288% 1134%, 테슬라의 경우에는 -19.85% 168.93%로 극명한 차이를 보이는 등 통계적 유의미성을 높이는 결과를 보였습니다.

 

물론 이 실험의 결과는 앞으로 나올 승률이 아니라 과거 등락률의 분포를 통해 승률을 추정했다는 점에서 반박의 여지는 있습니다. 하지만 켈리의 공식은 음의 베팅을 허용합니다. 즉 과거의 높은 승률에서 점차 낮은 승률로 변한다 하더라도 보유비중을 줄여나가는 결과를 제시하겠죠.

 

오히려 문제가 되는 것은 많은 시행을 해야 한다는 점인데요. 제 아무리 날고 기는 엔비디아라 하더라도 과거 4년 동안 상승한 일수는 전체 구간에서 55% 밖에 되지 않습니다. 따라서 투자 심리가 뒷받침 되지 않는다면 결국에는 아무런 의미가 없어지게 되죠.

 

하지만 켈리의 이론이 워렌 버핏과 같은 투자의 대가 뿐만 아니라 많은 경제학자들에게 인정되어, 투자론의 한 분야로 자리잡게 되었다는 점을 감안한다면 꾸준함과 인내심을 가지고 시도해볼 만한 전략이라고 판단됩니다.

 

이상 켈리의 공식을 이용한 투자전략에 대한 강의를 마치겠습니다. 오늘의 영상이 여러분들에게 도움이 되었기를 바라며, 다음 시간에는 더 유익한 정보로 찾아 뵙겠습니다. 시청해주셔서 감사합니다.

 

import numpy as np
import pandas as pd
import yfinance as yf

# 켈리의 공식 함수
def kelly_criterion(b, p):
    # 승률 p, 배당률 b
    q = 1 - p
    f = (b * p - q) / b
    return f

# 종목 데이터 다운로드
code = 'NVDA'
data = yf.download(code, start="2020-05-24", end="2024-05-31")
data = data.dropna()
data['Adj Close'] = data['Adj Close'] 
data['Returns'] = data['Adj Close'].pct_change()
data.dropna(inplace=True)

# pct_change가 양수인 경우의 수를 세기
positive_changes = (data['Returns'] > 0).sum()

# 전체 데이터 포인트의 수
total_changes = len(data['Returns'])

# 양수인 pct_change의 확률 계산
probability_positive = positive_changes / total_changes
print(f"pct_change가 양수인 확률: {probability_positive:.2f}")

# 총 누적 투자금액
investment = 0
stock_quantity = 0
average_price = 0

# 매 거래일마다 1주씩 주식 구매
for i in range(len(data) - 1):

    # 보유 주식 수 업데이트
    stock_quantity += 1

    # 평단가 업데이트
    average_price = ((average_price * (stock_quantity - 1)) + data.iloc[i]['Adj Close']) / stock_quantity  
    
    # 구매한 금액을 누적
    investment += data.iloc[i]['Adj Close']

 
# 최종 투자금액 및 평단가 대비 수익률 출력
print("-----------------------------------------------------normal----------------------------------------------------")
final_return = (data.iloc[-1]['Adj Close'] - average_price) / average_price *100
print(f"최종 평가손익: {final_return:.2f} %")
print(f"최종 필요자본: {investment:.2f}원")
print(f"최종 평균단가: {average_price}원")

# 투자금액, 주식 수, 평단가 초기화
stock_quantity = 0
average_price = 0
cash = investment

# 매 거래일마다 켈리의 공식에 따른 투자 실행 및 평단가 계산
for i in range(len(data) - 1):
    # 오늘의 등락률을 기반으로 배당률 계산
    dividend = 1 + data.iloc[i]['Returns']
    
    # 켈리의 공식에 따른 배팅 비율 계산
    betting_ratio = kelly_criterion(dividend, probability_positive)
    
    # 배팅 비율에 따른 투자금액 계산
    bet_amount = cash * betting_ratio
    
    # 배팅 비율이 양수면 매수, 음수면 매도
    if bet_amount > 0:
        # 매수할 주식 수 계산
        buy_quantity = bet_amount / data.iloc[i]['Adj Close']

        # 보유 주식 수 업데이트
        stock_quantity += buy_quantity

        # 평단가 업데이트
        average_price = ((average_price * (stock_quantity - buy_quantity)) + data.iloc[i]['Adj Close'] * buy_quantity) / stock_quantity    

        # 매수한 금액만큼 보유현금에서 차감
        cash -= buy_quantity * data.iloc[i]['Adj Close']
            
        # 구매한 금액을 누적
        investment += buy_quantity * data.iloc[i]['Adj Close']


# 최종 투자금액 및 평단가 대비 수익률 출력
print("-----------------------------------------------------kelly-----------------------------------------------------")
final_return = 0
if average_price > 0:
    final_return = (data.iloc[-1]['Adj Close'] - average_price) / average_price
print(f"최종 수익률: {final_return*100:.2f} %")
print(f"최종 평균단가: {average_price}원")

# 초기 투자금액 설정
initial_investment = 10000000 

# 투자금액, 주식 수, 평단가 초기화
stock_quantity = 0
average_price = 0
cash = initial_investment

# 매 거래일마다 켈리의 공식에 따른 투자 실행 및 평단가 계산
for i in range(len(data) - 1):
    # 오늘의 등락률을 기반으로 배당률 계산
    dividend = 1 + data.iloc[i]['Returns']
    
    # 켈리의 공식에 따른 배팅 비율 계산
    betting_ratio = kelly_criterion(dividend, probability_positive)

    # 배팅 비율에 따른 투자금액 계산
    bet_amount = cash * betting_ratio
    
    # 배팅 비율이 양수면 매수, 음수면 매도
    if bet_amount > 0:
        # 현금 잔고가 있을 경우에만 매수
        if cash > 0:
            # 매수할 주식 수 계산
            buy_quantity_ = bet_amount / data.iloc[i]['Adj Close']

            buy_quantity = int(buy_quantity_)
            
            # 새로 매수한 주식의 구매 비용
            new_purchase_cost = buy_quantity * data.iloc[i]['Adj Close']

            # 총 구매 비용 업데이트
            total_purchase_cost = average_price * stock_quantity + new_purchase_cost

            # 보유 주식 수 업데이트
            stock_quantity += buy_quantity

            # 평단가 계산
            average_price = total_purchase_cost / stock_quantity      
            
            # 매수한 금액만큼 보유현금에서 차감
            cash -= buy_quantity * data.iloc[i]['Adj Close']
    else:
        if stock_quantity > 0:
            # 매도할 주식 수 계산 (절대값 사용)
            sell_quantity = min(stock_quantity, abs(bet_amount / data.iloc[i]['Adj Close']))
            
            # 주식 수 업데이트
            stock_quantity -= sell_quantity

            # 매도한 금액을 보유현금에 반영
            cash += sell_quantity * data.iloc[i]['Adj Close']

# 최종 투자금액 및 평단가 대비 수익률 출력
print("-----------------------------------------------------kelly&limit-----------------------------------------------")
print(f"최종 보유주식: {stock_quantity*data.iloc[-1]['Adj Close']}")
print(f"최종 보유현금: {cash:.2f}원")
print(f"최종 잔고: {cash + stock_quantity*data.iloc[-1]['Adj Close']:.2f}원")
final_return = (cash + stock_quantity*data.iloc[-1]['Adj Close']) / initial_investment - 1
print(f"최종 수익률: {final_return*100:.2f} %")
728x90
반응형

댓글