finlib / finmc

MIT License
0 stars 0 forks source link

Add a LVMC model that accepts SVI Vol. #1

Open somdipdatta opened 2 months ago

somdipdatta commented 2 months ago

A prototype is currently implemented in https://github.com/qatwalk/eq/blob/main/src/model/localvol.py

It is generally useful, therefore it should be incorporated in finmc itself.

somdipdatta commented 2 months ago

Furthermore, localvol using scipy RegularGridInterpolator is very slow. (See https://github.com/scipy/scipy/issues/18010) Find alternative. If the grid is uniform, then the index search can be avoided.

somdipdatta commented 2 months ago

An LVMC model that uses this interpolator?

from scipy.interpolate import RegularGridInterpolator
import numpy as np
import time

def create_interp():
    times = [0.01, 0.2, 1.0]
    strikes = [-5.0, -0.5, -0.1, 0.0, 0.1, 0.5, 5.0]
    vols = np.array(
        [
            [2.713, 0.884, 0.442, 0.222, 0.032, 0.032, 0.032],
            [2.187, 0.719, 0.372, 0.209, 0.032, 0.032, 0.032],
            [1.237, 0.435, 0.264, 0.200, 0.101, 0.032, 0.032],
        ]
    )
    volinterp = RegularGridInterpolator(
        (times, strikes), vols, fill_value=None, bounds_error=False
    )
    return volinterp

def create_interp_two_step():
    times = [0.01, 0.2, 1.0]
    strikes = [-5.0, -0.5, -0.1, 0.0, 0.1, 0.5, 5.0]
    vols = np.array(
        [
            [2.713, 0.884, 0.442, 0.222, 0.032, 0.032, 0.032],
            [2.187, 0.719, 0.372, 0.209, 0.032, 0.032, 0.032],
            [1.237, 0.435, 0.264, 0.200, 0.101, 0.032, 0.032],
        ]
    )
    t_len = len(times)

    def interp(args):
        t, x_vec = args
        i_left = np.searchsorted(times, t, side="left")
        i_right = np.searchsorted(times, t, side="right")

        if i_left == i_right:
            if i_right == 0:
                y = vols[0]
            elif i_right == t_len:
                y = vols[-1]
            else:
                i_left -= 1
                t_right = times[i_right]
                t_left = times[i_left]
                den = t_right - t_left
                y = (
                    vols[i_left]
                    + (vols[i_right] - vols[i_left]) * (t - t_left) / den
                )
        else:
            y = vols[i_left]
        return np.interp(x_vec, strikes, y)

    return interp

if __name__ == "__main__":
    # interp = create_interp()
    interp = create_interp_two_step()

    t = 0.1
    x_vec = np.linspace(-0.1, 0.1, 100_000)

    start = time.time()
    for i in range(100):
        res = interp((t, x_vec))
    end = time.time()
    print(f"{end - start:5.4f} {res}")