In [2]:
import numpy as np
import pymc3 as pm
from pymc3.distributions.timeseries import GaussianRandomWalk
from scipy import optimize
%pylab inline
Asset prices have time-varying volatility (variance of day over day returns
). In some periods, returns are highly variable, while in others very stable. Stochastic volatility models model this with a latent volatility variable, modeled as a stochastic process. The following model is similar to the one described in the No-U-Turn Sampler paper, Hoffman (2011) p21.
Here, $y$ is the daily return series and $s$ is the latent log volatility process.
First we load some daily returns of the S&P 500.
In [3]:
n = 400
returns = np.genfromtxt("../data/SP500.csv")[-n:]
returns[:5]
Out[3]:
In [4]:
plt.plot(returns)
Out[4]:
Specifying the model in pymc3 mirrors its statistical specification.
In [5]:
model = pm.Model()
with model:
sigma = pm.Exponential('sigma', 1./.02, testval=.1)
nu = pm.Exponential('nu', 1./10)
s = GaussianRandomWalk('s', sigma**-2, shape=n)
r = pm.StudentT('r', nu, lam=pm.math.exp(-2*s), observed=returns)
For this model, the full maximum a posteriori (MAP) point is degenerate and has infinite density. To get good convergence with NUTS we use ADVI (autodiff variational inference) for initialization. This is done under the hood by the sample_init()
function.
In [7]:
with model:
trace = pm.sample(2000)
In [8]:
figsize(12,6)
pm.traceplot(trace, model.vars[:-1]);
In [9]:
figsize(12,6)
title(str(s))
plot(trace[s][::10].T,'b', alpha=.03);
xlabel('time')
ylabel('log volatility')
Out[9]:
Looking at the returns over time and overlaying the estimated standard deviation we can see how the model tracks the volatility over time.
In [9]:
plot(np.abs(returns))
plot(np.exp(trace[s][::10].T), 'r', alpha=.03);
sd = np.exp(trace[s].T)
xlabel('time')
ylabel('absolute returns')
Out[9]: