Skip to content

Added trinomial tree #9

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

Open
wants to merge 1 commit into
base: master
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
12 changes: 11 additions & 1 deletion development_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@
# print(eqOption_BSM_test_pricer.risk_parameters_num())
# print(eqOption_BSM_test_pricer.risk_parameters())

# #
# """EqOption using Trinomial"""
# eqOption_BSM_test = qbdp.EqOption(option_type='Call', strike=100, expiry_date='20180630', expiry_type='American')
# eqOption_BSM_test_pricer = eqOption_BSM_test.engine(model="Trinomial", spot0=110, pricing_date='20180531', volatility=.25,
# rf_rate=.05, div_list=div_list, yield_div=0.01, seed=12)
#
# print(eqOption_BSM_test_pricer.valuation())
# print(eqOption_BSM_test_pricer.risk_parameters_num())
# print(eqOption_BSM_test_pricer.risk_parameters())

# """FutOption using Binomial"""
# eqOption_BSM_test = qbdp.FutOption(option_type='Call', strike=100, expiry_date='20180630', expiry_type='American')
# eqOption_BSM_test_pricer = eqOption_BSM_test.engine(model="Binomial", fwd0=110, pricing_date='20180531', volatility=.25,
Expand Down Expand Up @@ -225,4 +235,4 @@
print(delta)
delta.show()

"""
"""
21 changes: 11 additions & 10 deletions quantsbin/derivativepricing/namesnmapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class PricingModel(Enum):
MC_GBM = "MC_GBM"
MC_GBM_LSM = "MC_GBM_LSM"
BINOMIAL = "Binomial"

TRINOMIAL = "Trinomial"

class UnderlyingParameters(Enum):
SPOT = "spot0"
Expand Down Expand Up @@ -72,17 +72,17 @@ class DivType(Enum):

OBJECT_MODEL = {
UdlType.STOCK.value: {ExpiryType.EUROPEAN.value: [PricingModel.BLACKSCHOLESMERTON.value, PricingModel.MC_GBM.value
, PricingModel.BINOMIAL.value],
ExpiryType.AMERICAN.value: [PricingModel.MC_GBM.value, PricingModel.BINOMIAL.value]}
, PricingModel.BINOMIAL.value, PricingModel.TRINOMIAL.value],
ExpiryType.AMERICAN.value: [PricingModel.MC_GBM.value, PricingModel.BINOMIAL.value, PricingModel.TRINOMIAL.value]}
, UdlType.FUTURES.value: {ExpiryType.EUROPEAN.value: [PricingModel.BLACK76.value, PricingModel.MC_GBM.value
, PricingModel.BINOMIAL.value],
ExpiryType.AMERICAN.value: [PricingModel.MC_GBM.value, PricingModel.BINOMIAL.value]}
, PricingModel.BINOMIAL.value, PricingModel.TRINOMIAL.value],
ExpiryType.AMERICAN.value: [PricingModel.MC_GBM.value, PricingModel.BINOMIAL.value, PricingModel.TRINOMIAL.value]}
, UdlType.FX.value: {ExpiryType.EUROPEAN.value: [PricingModel.GK.value, PricingModel.MC_GBM.value
, PricingModel.BINOMIAL.value],
ExpiryType.AMERICAN.value: [PricingModel.MC_GBM.value, PricingModel.BINOMIAL.value]}
, PricingModel.BINOMIAL.value, PricingModel.TRINOMIAL.value],
ExpiryType.AMERICAN.value: [PricingModel.MC_GBM.value, PricingModel.BINOMIAL.value, PricingModel.TRINOMIAL.value]}
, UdlType.COMMODITY.value: {ExpiryType.EUROPEAN.value: [PricingModel.GK.value, PricingModel.MC_GBM.value
, PricingModel.BINOMIAL.value],
ExpiryType.AMERICAN.value: [PricingModel.MC_GBM.value, PricingModel.BINOMIAL.value]}
, PricingModel.BINOMIAL.value, PricingModel.TRINOMIAL.value],
ExpiryType.AMERICAN.value: [PricingModel.MC_GBM.value, PricingModel.BINOMIAL.value, PricingModel.TRINOMIAL.value]}
}

DEFAULT_MODEL = {
Expand Down Expand Up @@ -115,5 +115,6 @@ class DivType(Enum):
PricingModel.BLACK76.value: pm.B76,
PricingModel.GK.value: pm.GK,
PricingModel.MC_GBM.value: pm.MonteCarloGBM,
PricingModel.BINOMIAL.value: pm.BinomialModel
PricingModel.BINOMIAL.value: pm.BinomialModel,
PricingModel.TRINOMIAL.value: pm.TrinomialModel
}
106 changes: 106 additions & 0 deletions quantsbin/derivativepricing/pricingmodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

"""

# ExtraLine1
# ExtraLine2
# ExtraLine3
from abc import ABCMeta, abstractmethod
from datetime import datetime as dt
from math import log, sqrt
Expand Down Expand Up @@ -476,4 +479,107 @@ def valuation(self):
def risk_parameters(self):
pass

class TrinomialModel(Model):
"""
This is the generalised Trinomial model used for valuation calculation for both European and American type
Args required:
instrument = Instrument parameters mapped from instrument module
spot0 = (Float) e.g. 110.0
rf_rate = (Float < 1) e.g. 0.2
cnv_yield = (Float < 1) e.g. 0.3
cost_yield = (Float < 1) e.g. 0.2
volatility = (Float < 1) e.g. 0.25
pricing_date = (Date in string format "YYYYMMDD") e.g. 10 Dec 2018 as "20181210"
no_of_steps = (Integer). Number of steps (nodes) for the premium calculation e.g. 100
div_list = (List). list of tuples with Ex-Dates and Dividend amounts. e.g. [('20180625',0.2),('20180727',0.6)]

"""

def __init__(self, instrument, spot0=None, rf_rate=0, cnv_yield=0, cost_yield=0,
volatility=None, pricing_date=None, no_of_steps=None, div_list=None, **kwargs):
self.instrument = instrument
self.spot0 = spot0 or 0.0001
self.rf_rate = rf_rate or 0
self.cnv_yield = cnv_yield or 0
self.cost_yield = cost_yield or 0
self.volatility = volatility or 0.10
self._pricing_date = dt.strptime(pricing_date, '%Y%m%d')
self.no_of_steps = no_of_steps or 100
self.div_list = div_list
self.cache_node = {}

@property
def drift(self):
return self.rf_rate + self.cost_yield - self._cnv_yield

@property
def div_processed(self):
return dividend_processor(self.div_list, self._pricing_date, self.instrument.expiry_date)

@property
def spot_update(self):
return self.spot0 - pv_div(self.div_processed, 0, self.rf_rate)

@property
def t_delta(self):
return self.maturity/self.no_of_steps

@property
def up_mult(self):
return e**(self.volatility*((2 * self.t_delta) ** 0.5))

@property
def up_prob(self):
return (1/6 + (self.drift - 0.5 * self.volatility**2 ) * (self.t_delta/(12*self.volatility**2))**0.5 )

@property
def dn_prob(self):
return (1/6 - (self.drift - 0.5 * self.volatility**2 ) * (self.t_delta/(12*self.volatility**2))**0.5 )

@property
def md_prob(self):
return 2/3

@property
def step_discount_fact(self):
return e**(-1*self.rf_rate * self.t_delta)

def node_value_store(self, pv, intrinsic_value):
if self.instrument.expiry_type == ExpiryType.AMERICAN.value:
return max(pv, intrinsic_value)
else:
return pv

def intrinsic_value(self, _spot):
return max(self.option_flag * (_spot - self.instrument.strike), 0.0)

def calc_spot(self, step_no, no_up):
return self.spot_update * (self.up_mult**(1*no_up - 0*step_no)) + \
pv_div(self.div_processed, self.t_delta * step_no, self.rf_rate)

def node_value(self, step_no, no_up):
cache_node_key = (step_no, no_up)
if cache_node_key in self.cache_node:
return self.cache_node[cache_node_key]
else:
_spot = self.calc_spot(step_no, no_up)
_intrinsic_value = self.intrinsic_value(_spot)

if step_no >= self.no_of_steps:
return _intrinsic_value
else:
_pv = ((self.up_prob*self.node_value(step_no+1, no_up+1)) +
(self.md_prob*self.node_value(step_no+1, no_up)) +
(self.dn_prob*self.node_value(step_no+1, no_up-1))
)*self.step_discount_fact
_node_value = self.node_value_store(_pv,_intrinsic_value)
self.cache_node[cache_node_key] = _node_value
return self.cache_node[cache_node_key]

def valuation(self):
return self.node_value(0, 0)

def risk_parameters(self):
pass