Files
Volt/notebooks/Example-Usage.ipynb
T
2022-06-12 11:16:58 -04:00

696 KiB

In [1]:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import torch
import gpytorch
import matplotlib.style as style
from matplotlib.lines import Line2D

sns.set_style('white')
palette = ["#1b4079", "#C6DDF0", "#50723C", "#B9E28C", "#8C2155", "#AF7595", "#E6480F", "#FA9500"]
sns.set(palette = palette, font_scale=2.0, style="white", rc={"lines.linewidth": 3.0})

# import sys
# sys.path.append("../")
from voltron.likelihoods import VolatilityGaussianLikelihood
from voltron.models import SingleTaskVariationalGP
from voltron.kernels import BMKernel, VolatilityKernel
from voltron.models import BMGP, VoltronGP
from voltron.train_utils import TrainVolModel, TrainDataModel
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Input In [1], in <cell line: 2>()
      1 import numpy as np
----> 2 import matplotlib.pyplot as plt
      3 import seaborn as sns
      4 import torch

ModuleNotFoundError: No module named 'matplotlib.pyplot'
In [3]:
np.random.seed(2019)
torch.random.manual_seed(2019)
Out [3]:
<torch._C.Generator at 0x7f77d62d35d0>
In [4]:
F0 = 10 ## init price
V0 = 0.2 ## init price
mu = 0.05 ## rate of return

alpha = 1.25
beta = 0.9
rho = -0.2

T = 1 ## Time of Simulation
steps = 400 ## steps per time
dt = T/(steps) ## delta t

dW = np.random.normal(0, np.sqrt(dt), steps*T)
dZ = rho * dW + np.sqrt(1 - rho **2) * np.random.normal(0, np.sqrt(dt), steps*T)


train_time = time = torch.linspace(0, T, steps-1) + dt
test_time = torch.linspace(T + dt, 1.5*T, int(.5*steps)-1) + dt

In [5]:
F = np.zeros(steps*T)
V = np.zeros(steps*T)

F[0] = F0
V[0] = V0

for t in range(1, steps*T):
    F[t] = F[t-1] + V[t-1] * (F[t-1])**beta * dW[t]
    V[t] = V[t-1] + alpha * V[t-1]*dZ[t]
In [6]:
fig, ax = plt.subplots(dpi=100)
ax.plot(F, label='Price')
ax2 = ax.twinx()
ax2.plot(V, color = palette[1], label='Vol')

ax.set_ylabel("Price")
ax2.set_ylabel("Vol")

fig.legend(bbox_to_anchor=(0.9, 0.9))
sns.despine()
plt.show()
In [7]:
fig, ax = plt.subplots(1, 3, figsize=(15, 4))
ax[0].plot(train_time, F[1:])


sns.despine()
plt.show()
In [8]:
# scaled_returns =np.log(F[1:]/F[:-1])
scaled_returns = (F[1:] - F[:-1]) / (F[:-1]**beta) / dt**0.5
In [9]:
fig, ax = plt.subplots(figsize = (8, 5), dpi=100, facecolor = "w")
ax.plot(scaled_returns, label='Log Returns')
ax2 = ax.twinx()
# ax2.plot(V, color=palette[1], label='Volatility')

ax2.plot(V, color = palette[1], label='Volatility')

ax.set_ylabel("Log Returns")
ax2.set_ylabel("Vol")
ax.set_xlabel("Time")
fig.legend(bbox_to_anchor=(0.9, 0.9))
sns.despine()
plt.show()
plt.savefig("./log-ret-vol.pdf", bbox_inches = "tight")
<Figure size 432x288 with 0 Axes>

Now apply GCPV

In [10]:
full_x = torch.FloatTensor(np.linspace(0, T, steps-1)) + 1
full_y = torch.FloatTensor(scaled_returns)

train_x = full_x
train_y = full_y
In [11]:
likelihood = VolatilityGaussianLikelihood(param="exp")
# likelihood.raw_a.data -= 4.
covar_module = BMKernel()
model = SingleTaskVariationalGP(
    init_points=train_x.view(-1,1), likelihood=likelihood, use_piv_chol_init=False,
    mean_module = gpytorch.means.ConstantMean(), covar_module=covar_module, 
    learn_inducing_locations=False, use_whitened_var_strat=False
)
model.initialize_variational_parameters(likelihood, train_x, y=train_y)
# model.mean_module.constant.data *= 0.
/home/greg_b/miniconda3/envs/rpp/lib/python3.8/site-packages/torch/functional.py:445: UserWarning: torch.meshgrid: in an upcoming release, it will be required to pass the indexing argument. (Triggered internally at  /opt/conda/conda-bld/pytorch_1634272068694/work/aten/src/ATen/native/TensorShape.cpp:2157.)
  return _VF.meshgrid(tensors, **kwargs)  # type: ignore[attr-defined]
In [12]:
# this is for running the notebook in our testing framework
import os
smoke_test = ('CI' in os.environ)
training_iterations = 2 if smoke_test else 500


# Find optimal model hyperparameters
model.train()
likelihood.train()

# Use the adam optimizer
optimizer = torch.optim.Adam([
    {"params": model.parameters()}, 
    # {"params": likelihood.parameters(), "lr": 0.1}
], lr=0.01)

# "Loss" for GPs - the marginal log likelihood
# num_data refers to the number of training datapoints
mll = gpytorch.mlls.VariationalELBO(likelihood, model, train_y.numel(), combine_terms = True)
In [13]:
output = model(train_x)
mll(output, train_y)
Out [13]:
tensor(-12.3532, grad_fn=<SubBackward0>)
In [14]:
print_every = 50
for i in range(training_iterations):
    # Zero backpropped gradients from previous iteration
    optimizer.zero_grad()
    # Get predictive output
    with gpytorch.settings.num_gauss_hermite_locs(75):
        output = model(train_x)
        # Calc loss and backprop gradients
        loss = -mll(output, train_y)
        loss.backward()
        if i % print_every == 0:
            print('Iter %d/%d - Loss: %.3f' % (i + 1, training_iterations, loss.item()))
        optimizer.step()
Iter 1/500 - Loss: 12.353
Iter 51/500 - Loss: -0.448
Iter 101/500 - Loss: -0.556
Iter 151/500 - Loss: -0.575
Iter 201/500 - Loss: -0.577
Iter 251/500 - Loss: -0.577
Iter 301/500 - Loss: -0.577
Iter 351/500 - Loss: -0.577
Iter 401/500 - Loss: -0.577
Iter 451/500 - Loss: -0.577
In [15]:
model.eval();
likelihood.eval();
predictive = model(full_x)
pred_scale = likelihood(predictive, return_gaussian=False).scale.mean(0).detach()
In [16]:
samples = likelihood(predictive, return_gaussian=False).scale.detach()
In [17]:
time = torch.linspace(0, T, steps-1)
In [18]:
fig, ax = plt.subplots(figsize = (10, 5), dpi=100, facecolor = "w")
plt.plot(time, V[1:], label = "Actual", color = palette[0], alpha=0.75)
plt.plot(time, pred_scale, label = "Posterior Mean", color = palette[-2])
# plt.plot(time, samples.t(), color=palette[3], alpha=0.2, label = "Posterior Samples")
plt.plot(time, samples[0], color = palette[-1], alpha = 0.4, label = "Posterior Samples")
plt.plot(time, samples[1:3].t(), color = palette[-1], alpha = 0.4)
# plt.plot(time, V[1:], label = "Actual", color = palette[0])
plt.xlabel("Time")
plt.ylabel("sigma(t)")
plt.legend(ncol = 3, loc = "upper center", bbox_to_anchor = (0.45, -0.2))

# fig.legend()
sns.despine()
plt.show()
# plt.savefig("sabr_vol.pdf", bbox_inches = "tight")

Plotting

Tutorial Plots

In [19]:
fig, ax = plt.subplots(1, 3, figsize=(20, 4))
plt.subplots_adjust(wspace=0.3)
ax[0].plot(train_time, F[1:], c=palette[0])
ax[0].set_xlabel("Time")
ax[0].set_ylabel("Price")
ax[0].set_title("Price Movements")

ax[1].plot(train_time, train_y, c=palette[0])
ax[1].set_xlabel("Time")
ax[1].set_ylabel("Returns")
ax[1].set_title("Returns")

# ax[2].plot(train_time, V[1:], c=palette[-2])
ax[2].plot(train_time, V[1:], c=palette[-2])
ax2 = ax[2].twinx()
ax2.plot(train_time, train_y, c=palette[0], alpha=0.25)
ax2.set_ylabel("Returns")
ax[2].set_xlabel("Time")
ax[2].set_ylabel("Volatiltiy")
ax[2].set_title("Volatility")


sns.despine()
plt.savefig("./tutorial-vol.pdf", bbox_inches="tight")
plt.show()

Example Plot

In [20]:
sample = likelihood(predictive, return_gaussian=False).scale.detach()[0, :]
In [21]:
vmod, vlh = TrainVolModel(train_time, sample)
dmod, dlh = TrainDataModel(train_time, torch.FloatTensor(F)[1:], vmod, vlh, sample)
In [22]:
# vmod.eval();
# nvol = 10
# vol_samples = vmod(test_time).sample(torch.Size((nvol,))).T.detach().exp()
# train_vol_samples = vmod(train_time).sample(torch.Size((10,))).T.detach().exp()
In [23]:
dmod.eval();
dlh.eval();
dmod.vol_model.eval();

nvol = 8
npx = 1
px_paths = torch.zeros(npx*nvol, test_time.shape[0])
vol_paths = torch.zeros(nvol, test_time.shape[0])

for vidx in range(nvol):
#     print(vidx)
    vol_pred = dmod.vol_model(test_time).sample().exp()
    vol_paths[vidx, :] = vol_pred.detach()
    
    px_pred = dmod.GeneratePrediction(test_time, vol_pred, npx).exp()
    px_paths[vidx*npx:(vidx*npx + npx), :] = px_pred.detach().T
In [24]:
def FormatAx(ax):
    ax.set_xlim(0, test_time.max())
#     ax.axvline(train_time.max(), ls="--", c='k', lw=0.5)
In [32]:
fig, ax = plt.subplots(3, 1, figsize = (8, 10), dpi=100, facecolor = "w")
plt.subplots_adjust(hspace=0.3)

## vol & log return plot ##
ax[0].plot(train_time, F[1:], label='Data', alpha=0.8)
ax2 = ax[0].twinx()
ax2.plot(train_time, V[1:], color = palette[1], label='Volatility')
ax[0].set_ylabel("Y")
ax2.set_ylabel("Vol")
# ax[0].legend(bbox_to_anchor=(0.9, 0.9))

custom_lines = [Line2D([0], [0], color=palette[0], lw=2., alpha=0.8,label="Data"),
                Line2D([0], [0], color=palette[1], lw=2., label="Volatility")]

ax[0].legend(handles=custom_lines,loc="upper right", bbox_to_anchor=(1.07, 1.), 
             frameon=False, fontsize=18)



## Vol and Forecast Plot ##
ax[1].plot(train_time, V[1:], color = palette[1], alpha=0.75, label="True Vol.")
ax[1].plot(train_time, sample, color = palette[-2], label = "Learned Vol.")
# ax[1].plot(test_time, vol_samples[:, 0], color=palette[-1], label="Pred Vol.")
ax[1].plot(test_time, vol_paths[0, :].T, color=palette[-1], alpha=0.5, label="Predicted Vol.")
ax[1].plot(test_time, vol_paths[1:, :].T, color=palette[-1], alpha=0.5)
ax[1].set_ylabel("Vol")
ax[1].legend(loc="upper right", bbox_to_anchor=(0.8, 1.1), frameon=False, fontsize=18)
# ax[1].legend(ncol = 3, loc = "upper center", bbox_to_anchor = (0.45, -0.2))

## Price and Forecast Plot ##
ax[2].plot(train_time, F[1:], color=palette[0], alpha=0.8, label="Data")
ax[2].plot(test_time, px_paths[1:10, :].T, color=palette[2], alpha=0.7)
ax[2].plot(test_time, px_paths[0, :].T, color=palette[2], alpha=0.7, label="Predictions")
ax[2].set_ylabel("Y")
ax[2].set_xlabel("X")
ax[2].legend(loc="upper right", bbox_to_anchor=(0.5, 1.1), frameon=False, fontsize=18)
## General Adjustments ##
FormatAx(ax[0])
FormatAx(ax[1])
FormatAx(ax[2])


# fig.legend()
sns.despine()
plt.savefig("./ret-vol-px.pdf", bbox_inches = "tight")
plt.show()
In [ ]: