Ornstein-Uhlenbeck Model
In finance, the OU process is often used to model the evolution of a commoditiy’s price over time. In this context, the OU process is used to capture the tendency of prices to return to their long-term average, while allowing for random fluctuations around that average.
Here
\(\lambda\) is the speed of mean reversion and determines how fast the process drifts to the mean reversion level \(\mu\),
\(\mu\) denotes the mean reversion level,
\(\sigma\) is the volatiity of the process.
The OU model has several properties that make it useful for modeling real-world phenomena. For example, it is a stationary process, meaning that its statistical properties do not change over time. It is also Markovian, meaning that its future behavior depends only on its current state, not on its past history.
[2]:
import sys
sys.path.append('../../../..')
import numpy as np
import matplotlib.pyplot as plt
import rivapy
c:\Users\DrHansNguyen\Documents\MyRivacon\RiVaPy\rivapy\__init__.py:11: UserWarning: The pyvacon module is not available. You may not use all functionality without this module. Consider installing pyvacon.
warnings.warn('The pyvacon module is not available. You may not use all functionality without this module. Consider installing pyvacon.')
Simulation of Spot
[3]:
timegrid = np.arange(0.0,5.0,1.0/365.0) # simulate on daily timegrid over 5yrs horizon
rnd = np.random.normal(size=(timegrid.shape[0],10_000))
plt.figure(figsize=(16,4))
for mr_speed, volatility in [(10.0,0.3), (0.1,0.3), (10.0, 1.0), (0.1,1.0)]:
ou_model = rivapy.models.OrnsteinUhlenbeck(speed_of_mean_reversion = mr_speed, volatility=volatility)
sim = ou_model.simulate(timegrid, start_value=0.0, rnd=rnd)
plt.subplot(1,2,1)
plt.plot(timegrid,sim[:,0],'-', label='$\lambda$='+str(mr_speed)+', $\sigma$=' + str(volatility))
plt.xlabel('time [years]')
plt.legend()
plt.subplot(1,2,2)
plt.hist(sim[-1,:], bins=100, density=True, alpha=0.25,
label='$\lambda$='+str(mr_speed)+', $\sigma$=' + str(volatility))
plt.legend('simulated value at final time')
plt.legend()
<>:9: SyntaxWarning: invalid escape sequence '\l'
<>:9: SyntaxWarning: invalid escape sequence '\s'
<>:14: SyntaxWarning: invalid escape sequence '\l'
<>:14: SyntaxWarning: invalid escape sequence '\s'
<>:9: SyntaxWarning: invalid escape sequence '\l'
<>:9: SyntaxWarning: invalid escape sequence '\s'
<>:14: SyntaxWarning: invalid escape sequence '\l'
<>:14: SyntaxWarning: invalid escape sequence '\s'
C:\Users\DrHansNguyen\AppData\Local\Temp\ipykernel_8904\3691699829.py:9: SyntaxWarning: invalid escape sequence '\l'
plt.plot(timegrid,sim[:,0],'-', label='$\lambda$='+str(mr_speed)+', $\sigma$=' + str(volatility))
C:\Users\DrHansNguyen\AppData\Local\Temp\ipykernel_8904\3691699829.py:9: SyntaxWarning: invalid escape sequence '\s'
plt.plot(timegrid,sim[:,0],'-', label='$\lambda$='+str(mr_speed)+', $\sigma$=' + str(volatility))
C:\Users\DrHansNguyen\AppData\Local\Temp\ipykernel_8904\3691699829.py:14: SyntaxWarning: invalid escape sequence '\l'
label='$\lambda$='+str(mr_speed)+', $\sigma$=' + str(volatility))
C:\Users\DrHansNguyen\AppData\Local\Temp\ipykernel_8904\3691699829.py:14: SyntaxWarning: invalid escape sequence '\s'
label='$\lambda$='+str(mr_speed)+', $\sigma$=' + str(volatility))
findfont: Font family ['STIXGeneral'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXGeneral'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXGeneral'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXGeneral'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXNonUnicode'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXNonUnicode'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXNonUnicode'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXSizeOneSym'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXSizeTwoSym'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXSizeThreeSym'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXSizeFourSym'] not found. Falling back to DejaVu Sans.
findfont: Font family ['STIXSizeFiveSym'] not found. Falling back to DejaVu Sans.
findfont: Font family ['cmsy10'] not found. Falling back to DejaVu Sans.
findfont: Font family ['cmr10'] not found. Falling back to DejaVu Sans.
findfont: Font family ['cmtt10'] not found. Falling back to DejaVu Sans.
findfont: Font family ['cmmi10'] not found. Falling back to DejaVu Sans.
findfont: Font family ['cmb10'] not found. Falling back to DejaVu Sans.
findfont: Font family ['cmss10'] not found. Falling back to DejaVu Sans.
findfont: Font family ['cmex10'] not found. Falling back to DejaVu Sans.
findfont: Font family ['DejaVu Sans Mono'] not found. Falling back to DejaVu Sans.
findfont: Font family ['DejaVu Sans Display'] not found. Falling back to DejaVu Sans.
Calibration
The model may be calibrated to data. Here, the data must be given on a uniform grid. The calibration can either be done by maximum likelihood or by minimum least square.
Here, to test the calibration, we simulate paths of an Ornstein-Uhlenbeck process and calibrate to this simulated data.
[4]:
iters = 100 # number of calibrations performed on simualted data
mean_reversion_speed, volatility, mean_level = np.empty((iters,)), np.empty((iters,)), np.empty((iters,))
for i in range (iters):
ou_model = rivapy.models.OrnsteinUhlenbeck(speed_of_mean_reversion = 5.0, volatility=0.1)
sim = ou_model.simulate(timegrid, start_value=0.2,rnd=np.random.normal(size=(timegrid.shape[0],1)))
ou_model.calibrate(sim.reshape((-1)),dt=1.0/365.0, method = 'minimum_least_square')
mean_reversion_speed[i] = ou_model.speed_of_mean_reversion
volatility[i] = ou_model.volatility
mean_level[i] = ou_model.mean_reversion_level
plt.figure(figsize=(16,6))
plt.subplot(1,3,1)
plt.hist(mean_reversion_speed, bins=10)
plt.title('speed of meanreversion')
plt.xlabel('speed of meanreversion')
plt.subplot(1,3,2)
plt.hist(volatility, bins=10)
plt.title('volatility')
plt.xlabel('volatility')
plt.subplot(1,3,3)
plt.hist(mean_level, bins=10)
plt.title('mean reversion level')
plt.xlabel('mean reversion level');
[ ]: