본문 바로가기
금융공학

Brownian bridge: 1년뒤 주가타겟을 정조준하는 일일주가의 움직임을 모델링하자 #2

by hustler78 2022. 11. 24.
728x90
반응형

이번 글은

2022.11.24 - [금융공학] - Brownian bridge: 1년뒤 주가타겟을 정조준하는 일일주가의 움직임을 모델링하자 #1

 

Brownian bridge: 1년뒤 주가타겟을 정조준하는 일일주가의 움직임을 모델링하자 #1

이번 글은 지수/주가 모델링 중 유용한 테크닉으로 알려진 브라운 브리지(Brownian bridge)에 대해 알아보려 합니다. Brown 운동은 위너 프로세스(Wiener process)로도 잘 알려져 있으며, 지수/주가 모델 중

sine-qua-none.tistory.com

에서 이어집니다.

 

지난 글에서 Brownian Bridge를 설계하는 방법에 대해 알아보았는데요, 복습을 먼저 해보겠습니다.

 

 

Brownian Bridge 복습

미래의 어떤 시점 $T$에 대해 어떤 위너프로세스 $Z$의 값 $Z(T)$가 이미 결정된 숫자라고 합시다.

이때, $[0,T]$사이에서 


$$ B(t) =  \frac{t}{T} Z(T) + W(t) -\frac{t}{T}W(T) \tag{1}$$

라 하면, $B(t)$가 0과 $Z(T)$를 잇는 Brownian bridge가 된다고 했습니다. 여기서, $W(\cdot)$는 $Z(\cdot)$와 독립인 또 다른 위너프로세스입니다.

 

 

다리의 건설

위의 이론을 사용하여 이제 다리를 놔볼까요?

 

우선 시간 간격 $[0,T]$를 $N$개의 동일 간격으로 나눠봅시다. 즉,

$$ 0=t_0 < t_1 < t_2 \cdots <t_{N-1} < t_N = T $$

입니다. 

목적은 각 시점 $t_i$에 교각(즉 위너프로세스 난수)을 세우는 것이죠.

 

 

다리의 교각을 세우는  방법에는 잘못된 방법과 올바른 방법이 있습니다.

 

잘못된 방법을 굳이 소개하는 이유는 부지불식간에 이런 실수를 하는 경우가 많아서인데요, 먼저 잘못된 방법을 알아봅시다.

 

 잘못된 방법
틀린 생각

식(1)을 보니 $W(t)$와 $W(T)$를 구하면 되겠네? 
각 시점 $t_i$에 대해
$$ B(t_i) = \frac{t_i}{t_N} Z(T) + W(t_i) -\frac{t_i}{t_N}W(t_N) $$
인 각 $W(t_i)$ 들을 추출해 내면 되겠구먼,  $W(t_i)$는 평균이 0이고 분산이 $t_i$인 정규분포 난수니까 오케이! 모든 교각을 다 찾았다!

이렇게 한번 코딩해보겠습니다(설명은 주석을 참조)

 

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

def bb_test_WrongMethod():
    maturity = 1	# 미래 특정시점을 1
    nPath = 5		# Brownian Bridge를 5개 그릴 예정
    ntime = 20		# 시간 [0,1]을 20 등분하여 bridge 건설

    np.random.seed(0)	#seed 고정
    target = np.random.normal()	# target : 수식(1)의 Z(T)
    time_grid = np.linspace(0, maturity, ntime + 1)	#시간을 20등분, linspace사용

    plt.scatter(maturity, target, s=50, c='b')	#target 점을 표시
    for _ in range(nPath):
        rn = np.sqrt(time_grid) * np.random.normal(size=ntime + 1) 
        # 각 시점 t_i에 대응되는, 평균 0 분산 sqrt(t_i)인 난수를 발생하여 배열화
        
        brownian_bridge = target * time_grid / maturity + rn - time_grid / maturity * rn[-1]
        # 식(1)이 B(t) = t/T Z(t) + W(t)- (t/T)W(T) 이므로
        # 각 B(t)를 구하여 배열화한 과정
        
        plt.plot(time_grid, brownian_bridge)

    plt.show()


if __name__ == '__main__':
    bb_test_WrongMethod()

 

식(1)에 충실하게 구한 것입니다.  결과를 보면

뭔가 너무 들쑥날쑥하고 얌전하기 않은 다리가 건설된 느낌이 드는지요? 사실 뒤에 소개할 올바른 방법과 비교하면 좀 수상함을 알 수 있습니다. 

결론적으로 이 방법은 틀렸는데요, $B(t_i)$를 구한 방법을 보시면 어디가 틀렸는지 알 수 있습니다.

 

각 $t_i$별로 분산이 $t_i$인 정규분포를 구하기 위해 

$$ B(t_i) \sim \mathcal{N}(0, \sqrt{t_i}^2)$$ 

코드로 말하자면  numpy.random.normal() *numpy.sqrt(t_i) 식으로 구한 겁니다. 따라서 

각각의 $B(t_i)$들끼리는 서로 독립이 됩니다. 구체적으로

$B(t_1), B(t_2), \cdots, B(t_{N-1}), B(t_N)$ 이 모두 서로 독립이죠.

 

하지만 위너프로세스라는 것이 이렇게 독립적으로 시점별로 아무 데나 떨어져 있으면 안 되죠. 

위너프로세스란, 정의에 입각해 보아도 다음 한 발자국의 움직임이 지금의 위치와 독립이어야 된다는 뜻입니다. 즉,

 

 

$B(t_i)$와 $B(t_{i+1})$이 독립이 아닌,

$B(t_i)$와 $B(t_{i+1})-B(t_i)$가 독립이어야 한다

 

는 뜻입니다. 그럼 이렇게 설계를 해 보겠습니다. 바로 이게 올바른 방법입니다.

 

 

 

올바른 방법

위에서 논했듯이, $B(t_i)$와 $B(t_{i+1})-B(t_i)$가 독립이게끔 세팅해 줘야 합니다. 따라서 식 (1)에서

$$W(t_{i+1}) = W(t_i) + \epsilon \sqrt{t_{i+1}-t_i} $$
여기서 $\epsilon$은 $\epsilon \sim \mathcal{N}(0,1)$ 인 난수

 

처럼 재귀적으로 뽑아 주면 됩니다. 좀 풀어써보면

$$
\begin{align}
W(t_0) &=0 \\
W(t_1) & = \epsilon_1 \sqrt{t_1} \\
W(t_2) & = W(t_1) + \epsilon_2 \sqrt{t_2-t_1} \\
        & \vdots \\
W(t_N) &= W(t_{N-1}) + \epsilon_N \sqrt{t_N-t_{N-1}}
\end{align}
$$

 

 

코드를 볼까요?

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

def bb_test_RightMethod():
    maturity = 1
    nPath = 5
    ntime = 20

    np.random.seed(0)
    target = np.random.normal()
    time_grid = np.linspace(0, maturity, ntime + 1)

    plt.scatter(maturity, target, s=50, c='b')
    for _ in range(nPath):
        rn = np.zeros(ntime + 1)	# 0으로 이루어진 배열 setting
        for i in range(ntime):	# 재귀적으로 t_i 시점의 위너프로세스 난수 발생
            rn[i + 1] = rn[i] + np.random.normal() * np.sqrt(time_grid[i + 1] - time_grid[i])
        brownian_bridge = target * time_grid / maturity + rn - time_grid / maturity * rn[-1] # rn[-1]은 마지막 값
        plt.plot(time_grid, brownian_bridge)

    plt.show()


if __name__ == '__main__':
    bb_test_RightMethod()

 

결과는

어떤가요? 일단 확실한 것은 패스의 들쑥날쑥의 저번 그림에 비해 작다는 것이죠. 약간 안정적이기도 하고요.

 

 


그럼 두 방법을 짬뽕하여 그려보겠습니다.

 

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

def bb_test_Wrond_and_RightMethod():
    maturity = 1
    nPath = 100
    ntime = 20

    np.random.seed(0)
    target = np.random.normal()
    time_grid = np.linspace(0, maturity, ntime + 1)

    plt.figure(figsize=(20, 10))

    plt.subplot(2, 1, 1)
    plt.scatter(maturity, target, s=50, c='b')
    plt.subplot(2, 1, 2)
    plt.scatter(maturity, target, s=50, c='b')

    for _ in range(nPath):
        rn = np.sqrt(time_grid) * np.random.normal(size=ntime + 1)
        brownian_bridge = target * time_grid / maturity + rn - time_grid / maturity * rn[-1]
        plt.subplot(2, 1, 1)
        plt.plot(time_grid, brownian_bridge)
    plt.title('wrong brownian bridge')

    for _ in range(nPath):
        rn = np.zeros(ntime + 1)
        for i in range(ntime):
            rn[i + 1] = rn[i] + np.random.normal() * np.sqrt(time_grid[i + 1] - time_grid[i])
        brownian_bridge = target * time_grid / maturity + rn - time_grid / maturity * rn[-1]
        plt.subplot(2, 1, 2)
        plt.plot(time_grid, brownian_bridge)
    plt.title('right brownian bridge')

    plt.show()


if __name__ == '__main__':
    bb_test_Wrond_and_RightMethod()

 

 

 

패스를 100씩 그려본 것입니다. 한눈에 봐도 아래의 방법이 정갈해 보이고 들쑥날쑥 정도도 합리적이죠.

잘못된 방법은 만기 $T$ 시점으로 갈수록 분산이 $T$로 다가가는 난수를 뽑기 때문에 더욱더 들쑥날쑥 정도가 심해지는 모습을 보입니다. 반면 옳은 방법은 어떤 제한된 영역을 높은 확률로 벗어나지 않으며 패스를 건설하죠.

 

 

 

결론

Brownian Bridge를 건설하는 목적과 과정을 요약하면 다음과 같습니다.

 

목적

위너 프로세스 $Z(\cdot)$ 의 만기시점 $T$에서의 값이 $Z(T)$로 주어져 있을 때, 현재 시점 $t=0$에서부터 $Z(T)$까지 잘게 잘게 움직이는 위너 프로세스를 만들어 보는 작업

 

과정

1. 수식을 작성합니다 수식은 식(1)과 같습니다.

 

2. 시점 그리드 $0=t_0<t_1<\cdots <t_{N-1} <t_N= T$ 의 각 시점에서의 브리지 값을 구할 때, 식(1)의 $W(\cdots)$ 값을

$$ W(t_{i+1}) = W(t_i) +\epsilon \sqrt{t_{i+1}-t_i}~,~ \epsilon \sim \mathcal{N}(0,1)$$

의 관계식을 사용하여 난수 추출한다

 

3. 식(1)에 의해 브리지 값 $B(t_i)$들을 추출한다.

 

 

 

 

다음 글에서는 실제 GBM 주가를 가지고 실험을 해보도록 하겠습니다. 물론 옳은 방법으로만 뽑아 보겠습니다.

 

 

728x90
반응형

댓글