Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use of aleatory processes to price qablet contracts #11

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions aleatory/processes/base_eu.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,22 @@ def _sample_em_process(self, n, log=False):

return path

def start(self, initial):
"""Set an initial state for all paths. initial can be a scalar or a numpy array."""
self.val = initial
self.cur_time = 0.0

def advance(self, t):
"""Advance the process to time t, for all paths."""
dt = t - self.cur_time
if dt < 1e-10:
return
# Generate the Brownian increments, for all paths at time t
dws = self.rng.normal(scale=np.sqrt(dt), size=self.val.shape)

# update val for all paths
self.val += self.f(self.val, t) * dt + self.g(self.val, t) * dws
self.cur_time = t

def sample(self, n):
return self._sample_em_process(n)
5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ numpy>=1.23.5
scipy>=1.10.0
matplotlib>=3.6.2
statsmodels>=0.13.5
parameterized>=0.8.1
parameterized>=0.8.1
qablet_contracts>=0.2.2
qablet-basic>=0.3.2
pyarrow>=13.0.0
33 changes: 33 additions & 0 deletions tests/test_advance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from aleatory.processes import CEVProcess
import unittest
import numpy as np


class TestAdvance(unittest.TestCase):
def test_advance_method(self):
cev = CEVProcess()

cev.start(np.zeros(5))
for t in np.arange(0.1, 1.01, 0.1):
cev.advance(t)
print(cev.val)


if __name__ == "__main__":
unittest.main()

"""
Output:

[0.07483507 0.06892834 0.0103775 0.08222023 -0.00520991]
[0.1242685 0.09343143 0.0547229 0.04053699 0.07427636]
[0.19546715 0.1776751 0.09312892 0.07602916 0.09573145]
[0.2762749 0.25656871 0.17613864 0.12536773 0.09022976]
[0.29868798 0.33389956 0.28908409 0.15776951 0.09993488]
[0.40701358 0.45013018 0.30473939 0.23695351 0.16412336]
[0.47892648 0.54385911 0.3692668 0.29175553 0.20496346]
[0.57069992 0.66287047 0.40147713 0.33604199 0.20004954]
[0.58912079 0.73014259 0.45835246 0.37663655 0.22481997]
[0.63629761 0.7450066 0.50876839 0.42058831 0.32175386]

"""
132 changes: 132 additions & 0 deletions tests/test_qablet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"""
Create a Qablet model using the CEV Process, and use it to price vanilla options and accumulators.
"""

from aleatory.processes import CEVProcess
import unittest
import numpy as np
from datetime import datetime
import pandas as pd

from qablet.base.mc import MCModel, MCStateBase
from qablet_contracts.eq.vanilla import Option
from qablet_contracts.eq.cliquet import Accumulator
from qablet_contracts.timetable import py_to_ts
from qablet.base.utils import Forwards


class CEVModelState(MCStateBase):
"""A Qablet Model based on alreatory.processes.CEVProcess."""

def __init__(self, timetable, dataset):
super().__init__(timetable, dataset)
# Initialize the model parameters
self.N = dataset["MC"]["PATHS"]
self.asset = dataset["CEV"]["ASSET"]
self.asset_fwd = Forwards(dataset["ASSETS"][self.asset])
self.spot = self.asset_fwd.forward(0)
# Create the CEV process
self.cev = (
CEVProcess()
) # TODO: Set the drift and volatility based on parameters
self.cev.start(np.zeros(self.N))

def advance(self, new_time):
self.cev.advance(new_time)

def get_value(self, unit):
if unit == self.asset:
return self.spot * np.exp(self.cev.val)


class CEVModel(MCModel):
def state_class(self):
return CEVModelState


#
# Use above model to test the pricing of vanilla options and accumulators
#
class TestContracts(unittest.TestCase):
def setUp(self):
"""Setup the dataset needed for the pricing model."""
pricing_dt = datetime(2023, 12, 31)
times = np.array([0.0, 5.0])
rates = np.array([0.04, 0.04])
discount_data = ("ZERO_RATES", np.column_stack((times, rates)))

self.spot = 5000.0
div_rate = 0.01
fwds = self.spot * np.exp((rates - div_rate) * times)
fwd_data = ("FORWARDS", np.column_stack((times, fwds)))

self.dataset = {
"BASE": "USD",
"PRICING_TS": py_to_ts(pricing_dt).value,
"ASSETS": {"USD": discount_data, "SPX": fwd_data},
"MC": {
"PATHS": 10_000,
"TIMESTEP": 1 / 250,
"SEED": 1,
},
"CEV": {
"ASSET": "SPX",
},
}

self.model = CEVModel()

def test_option(self):
"""Test the pricing of vanilla options."""

print("Testing vanilla options")
for x in [0.5, 1.0, 1.5]:
strike = self.spot * x
timetable = Option(
"USD",
"SPX",
strike=strike,
maturity=datetime(2024, 12, 31),
is_call=True,
).timetable()
price, _ = self.model.price(timetable, self.dataset)

print(f"strike: {strike:6.0f} price: {price:11.6f}")

def test_accumulator(self):
"""Test the pricing of an accumulator."""
print("Testing Accumulator")

for local_cap in [0.01, 0.02, 0.03]:
fix_dates = pd.bdate_range(
datetime(2023, 12, 31), datetime(2024, 12, 31), freq="1BQE"
)
timetable = Accumulator(
"USD",
"SPX",
fix_dates,
0.0,
local_floor=-local_cap,
local_cap=local_cap,
).timetable()
price, _ = self.model.price(timetable, self.dataset)

print(f"cap/floor: {local_cap:6.3f} price: {price:11.6f}")


if __name__ == "__main__":
unittest.main()

"""
Output of test_option:

Testing Accumulator
cap/floor: 0.010 price: 2.790405
cap/floor: 0.020 price: 5.575411
cap/floor: 0.030 price: 8.314456
.Testing vanilla options
strike: 2500 price: 5538.443450
strike: 5000 price: 3138.557753
strike: 7500 price: 802.604143

"""