# -*- coding: utf-8 -*-
from datetime import date, datetime
from typing import List as _List, Union as _Union
from rivapy.tools.interfaces import FactoryObject
from rivapy.tools.datetools import _date_to_datetime as datetime_to_date, _datetime_to_date_list as datetime_to_date_list
class PricingRequestBase(FactoryObject):
_keys = ['theo_val', 'paths_udl', 'cf_expected', 'cf_paths']
def __init__(self, **kwargs):
#validate the given keys
for key in kwargs.keys():
if key not in PricingRequestBase._keys:
raise ValueError("Unknown key '{}'".format(key))
#set the attributes
setattr(self, key, kwargs[key])
def _to_dict(self) -> dict:
return self.__dict__
[docs]
class GreenPPAPricingRequest(PricingRequestBase):
def __init__(self, theo_val: bool, cf_paths: bool=False, cf_expected: bool=False):
"""PricingRequest for Green PPA pricing.
Args:
price (bool): If True, the :term:`Theoretical Value` is calculated.
cf_expected (bool, optional): If True, the paths of the simulated :term:`Cash flow` is returned. Defaults to False.
"""
super().__init__(theo_val=theo_val, cf_expected=cf_expected, cf_paths=cf_paths)
class PricingRequest:
# def __init__(self, calc_delta_gamma: bool = False, calc_cross_gamma: bool = True, calc_clean_price: bool = False,
# calc_rho: bool = False, rho_scale: float = 0.0001, calc_vega: bool = False, vega_scale: float = 0.01,
# calc_cross_volga: bool = False, calc_vanna: bool = False, calc_theta: bool = False,
# theta_scale: float = 1.0, calc_spline: bool = False, calc_grid_sizes: bool = False,
# calc_implied_volatility: bool = False, management_delta_limit: float = 0.01,
# calc_pricing_data: bool = False, calc_expected_cashflows: bool = False,
# calc_simulation_data: bool = False, calc_additional_information: bool = False,
# calc_z_spread: bool = False, calc_yield_to_maturity: bool = False, calc_convexity: bool = False,
# max_expected_cashflow_date: _Union[date, datetime] = None,
# cashflow_times: _List[_Union[date, datetime]] = None, calc_macaulay_duration: bool = False):
# """
# Defines the set of information, e.g. sensitivities, cashflow information, etc., to be calculated together with
# the instrument's price in the context of pricing. For some sensitivities the shift parameter for calculating
# the difference quotient (rather than the sensitivity in closed formula) is also stored in the pricing request
# object.
#
# Args: TODO: Read carefully and double-check if these guessed descriptions are valid!
# calc_delta_gamma (bool, optional): Flag determining if the change of the instrument's price associated with
# small changes of the underlying's price shall be calculated to first (and
# second) order within the pricing routine. Defaults to False.
# TODO: also second order? delta_scale=?
# calc_cross_gamma (bool, optional): Flag determining if the change of the instrument's price associated with
# small changes of two underlyings' prices (both at fist order) shall be
# calculated within the pricing routine. Defaults to True.
# TODO: why defaults to True?
# calc_clean_price (bool, optional): Flag determining if the instrument's clean price shall be calculated
# additional to its dirty price within the pricing routine.
# Defaults to False.
# calc_rho (bool, optional): Flag determining if the change of the instrument's price associated with small
# changes of the zero rate curve shall be calculated to first order within the
# pricing routine. Defaults to False.
# rho_scale (float, optional): Size of zero rate shift in the calculation of the change of the instrument's
# price associated with zero rate curve shifts as difference quotient.
# Defaults to 0.0001 (= 1 basis point).
# calc_vega (bool, optional): Flag determining if the change of the instrument's price associated with small
# changes of the implied volatility surface shall be calculated to first order
# within the pricing routine. Defaults to False.
# vega_scale (float, optional): Size of implied volatility shift in the calculation of the change of the
# instrument's price associated with implied volatility surface shifts as
# difference quotient. Defaults to 0.01.
# calc_cross_volga (bool, optional): Flag determining if the change of the instrument's price associated with
# small changes of two implied volatility surfaces (both at first order)
# shall be calculated within the pricing routine. Defaults to True.
# TODO: why defaults to True?
# calc_vanna (bool, optional): Flag determining if the change of the instrument's price associated with small
# changes of the underlying's price and the implied volatility surface (both at
# first order) shall be calculated within the pricing routine. Defaults to False.
# TODO: why defaults to False in contrast to cross Gamma/Volga?
# calc_theta (bool, optional): Flag determining if the change of the instrument's price associated with small
# changes of the instrument's maturity shall be calculated to first order within
# the pricing routine. Defaults to False.
# theta_scale (float, optional): Size of maturity shift in the calculation of the change of the instrument's
# price associated with maturity shifts as difference quotient. Defaults to 1.0
# calc_spline (bool, optional):
# calc_grid_sizes (bool, optional):
# calc_implied_volatility (bool, optional):
# management_delta_limit (float, optional):
# calc_pricing_data (bool, optional):
# calc_expected_cashflows (bool, optional):
# calc_simulation_data (bool, optional):
# calc_additional_information (bool, optional):
# calc_z_spread (bool, optional): Flag determining if the instrument's z-spread shall be calculated within the
# pricing routine. Defaults to False.
# calc_yield_to_maturity (bool, optional): Flag determining if the instrument's yield-to-maturity shall be
# calculated within the pricing routine. Defaults to False.
# calc_convexity (bool, optional): Flag determining if the change of the instrument's price associated with
# small changes of the zero rate curve shall be calculated to second order
# within the pricing routine. Defaults to False.
# max_expected_cashflow_date (_Union[date, datetime], optional):
# cashflow_times (_List[_Union[date, datetime]], optional):
# calc_macaulay_duration (bool, optional): Flag determining if the instrument's Macaulay duration shall be
# calculated within the pricing routine. Defaults to False.
# """
def __init__(self, calc_delta_gamma: bool = None, calc_cross_gamma: bool = None, calc_clean_price: bool = None,
calc_rho: bool = None, rho_scale: float = None, calc_vega: bool = None, vega_scale: float = None,
calc_cross_volga: bool = None, calc_vanna: bool = None, calc_theta: bool = None,
theta_scale: float = None, calc_spline: bool = None, calc_grid_sizes: bool = None,
calc_implied_volatility: bool = None, management_delta_limit: float = None,
calc_pricing_data: bool = None, calc_expected_cashflows: bool = None,
calc_simulation_data: bool = None, calc_additional_information: bool = None,
calc_z_spread: bool = None, calc_yield_to_maturity: bool = None, calc_convexity: bool = None,
max_expected_cashflow_date: _Union[date, datetime] = None,
cashflow_times: _List[_Union[date, datetime]] = None, calc_macaulay_duration: bool = None):
"""
Defines the set of information, e.g. sensitivities, cashflow information, etc., to be calculated together with
the instrument's price in the context of pricing. For some sensitivities the shift parameter for calculating
the difference quotient (rather than the sensitivity in closed formula) is also stored in the pricing request
object.
Args: TODO: Read carefully and double-check if these guessed descriptions are valid!
calc_delta_gamma (bool, optional): Flag determining if the change of the instrument's price associated with
small changes of the underlying's price shall be calculated to first (and
second) order within the pricing routine. Defaults to None.
TODO: also second order? delta_scale=?
calc_cross_gamma (bool, optional): Flag determining if the change of the instrument's price associated with
small changes of two underlyings' prices (both at fist order) shall be
calculated within the pricing routine. Defaults to None.
calc_clean_price (bool, optional): Flag determining if the instrument's clean price shall be calculated
additional to its dirty price within the pricing routine.
Defaults to None.
calc_rho (bool, optional): Flag determining if the change of the instrument's price associated with small
changes of the zero rate curve shall be calculated to first order within the
pricing routine. Defaults to None.
rho_scale (float, optional): Size of zero rate shift in the calculation of the change of the instrument's
price associated with zero rate curve shifts as difference quotient.
Defaults to None.
calc_vega (bool, optional): Flag determining if the change of the instrument's price associated with small
changes of the implied volatility surface shall be calculated to first order
within the pricing routine. Defaults to None.
vega_scale (float, optional): Size of implied volatility shift in the calculation of the change of the
instrument's price associated with implied volatility surface shifts as
difference quotient. Defaults to None.
calc_cross_volga (bool, optional): Flag determining if the change of the instrument's price associated with
small changes of two implied volatility surfaces (both at first order)
shall be calculated within the pricing routine. Defaults to None.
calc_vanna (bool, optional): Flag determining if the change of the instrument's price associated with small
changes of the underlying's price and the implied volatility surface (both at
first order) shall be calculated within the pricing routine. Defaults to None.
calc_theta (bool, optional): Flag determining if the change of the instrument's price associated with small
changes of the instrument's maturity shall be calculated to first order within
the pricing routine. Defaults to None.
theta_scale (float, optional): Size of maturity shift in the calculation of the change of the instrument's
price associated with maturity shifts as difference quotient.
Defaults to None
calc_spline (bool, optional):
calc_grid_sizes (bool, optional):
calc_implied_volatility (bool, optional):
management_delta_limit (float, optional):
calc_pricing_data (bool, optional):
calc_expected_cashflows (bool, optional):
calc_simulation_data (bool, optional):
calc_additional_information (bool, optional):
calc_z_spread (bool, optional): Flag determining if the instrument's z-spread shall be calculated within the
pricing routine. Defaults to None.
calc_yield_to_maturity (bool, optional): Flag determining if the instrument's yield-to-maturity shall be
calculated within the pricing routine. Defaults to None.
calc_convexity (bool, optional): Flag determining if the change of the instrument's price associated with
small changes of the zero rate curve shall be calculated to second order
within the pricing routine. Defaults to None.
max_expected_cashflow_date (_Union[date, datetime], optional):
cashflow_times (_List[_Union[date, datetime]], optional):
calc_macaulay_duration (bool, optional): Flag determining if the instrument's Macaulay duration shall be
calculated within the pricing routine. Defaults to None.
"""
self._calc_delta_gamma = calc_delta_gamma
self._calc_cross_gamma = calc_cross_gamma
self._calc_clean_price = calc_clean_price
self._calc_rho = calc_rho
self._rho_scale = rho_scale
self._calc_vega = calc_vega
self._vega_scale = vega_scale
self._calc_cross_volga = calc_cross_volga
self._calc_vanna = calc_vanna
self._calc_theta = calc_theta
self._theta_scale = theta_scale
self._calc_spline = calc_spline
self._calc_grid_sizes = calc_grid_sizes
self._calc_implied_volatility = calc_implied_volatility
self._management_delta_limit = management_delta_limit
self._calc_pricing_data = calc_pricing_data
self._calc_expected_cashflows = calc_expected_cashflows
self._calc_simulation_data = calc_simulation_data
self._calc_additional_information = calc_additional_information
self._calc_z_spread = calc_z_spread
self._calc_yield_to_maturity = calc_yield_to_maturity
self._calc_convexity = calc_convexity
self._max_expected_cashflow_date = max_expected_cashflow_date
self._cashflow_times = cashflow_times
self._calc_macaulay_duration = calc_macaulay_duration
@property
def _calc_delta_gamma(self):
return self.__calc_delta_gamma
@_calc_delta_gamma.setter
def _calc_delta_gamma(self, calc_delta_gamma: bool):
if calc_delta_gamma is not None:
if isinstance(calc_delta_gamma, bool):
self.__calc_delta_gamma = calc_delta_gamma
else:
raise TypeError("'" + str(calc_delta_gamma) + "' must be of type bool!")
@property
def _calc_cross_gamma(self):
return self.__calc_cross_gamma
@_calc_cross_gamma.setter
def _calc_cross_gamma(self, calc_cross_gamma: bool):
if calc_cross_gamma is not None:
if isinstance(calc_cross_gamma, bool):
self.__calc_cross_gamma = calc_cross_gamma
if self._calc_cross_gamma:
self.calc_delta_gamma = True
else:
raise TypeError("'" + str(calc_cross_gamma) + "' must be of type bool!")
@property
def _calc_clean_price(self):
return self.__calc_clean_price
@_calc_clean_price.setter
def _calc_clean_price(self, calc_clean_price: bool):
if calc_clean_price is not None:
if isinstance(calc_clean_price, bool):
self.__calc_clean_price = calc_clean_price
else:
raise TypeError("'" + str(calc_clean_price) + "' must be of type bool!")
@property
def _calc_rho(self):
return self.__calc_rho
@_calc_rho.setter
def _calc_rho(self, calc_rho: bool):
if calc_rho is not None:
if isinstance(calc_rho, bool):
self.__calc_rho = calc_rho
if self._calc_rho & ~hasattr(self, 'rho_scale'):
self.rho_scale = 0.0001
else:
raise TypeError("'" + str(calc_rho) + "' must be of type bool!")
@property
def _rho_scale(self):
return self.__rho_scale
@_rho_scale.setter
def _rho_scale(self, rho_scale: float):
if rho_scale is not None:
if isinstance(rho_scale, float):
self.__rho_scale = rho_scale
self.calc_rho = True
else:
raise TypeError("'" + str(rho_scale) + "' must be of type float!")
@property
def _calc_vega(self):
return self.__calc_vega
@_calc_vega.setter
def _calc_vega(self, calc_vega: bool):
if calc_vega is not None:
if isinstance(calc_vega, bool):
self.__calc_vega = calc_vega
if self._calc_vega & ~hasattr(self, 'vega_scale'):
self.vega_scale = 0.01
else:
raise TypeError("'" + str(calc_vega) + "' must be of type bool!")
@property
def _vega_scale(self):
return self.__vega_scale
@_vega_scale.setter
def _vega_scale(self, vega_scale: float):
if vega_scale is not None:
if isinstance(vega_scale, float):
self.__vega_scale = vega_scale
self.calc_vega = True
else:
raise TypeError("'" + str(vega_scale) + "' must be of type float!")
@property
def _calc_cross_volga(self):
return self.__calc_cross_volga
@_calc_cross_volga.setter
def _calc_cross_volga(self, calc_cross_volga: bool):
if calc_cross_volga is not None:
if isinstance(calc_cross_volga, bool):
self.__calc_cross_volga = calc_cross_volga
if self._calc_cross_volga:
self.calc_vega = True
if not hasattr(self, 'vega_scale'):
self.vega_scale = 0.01
else:
raise TypeError("'" + str(calc_cross_volga) + "' must be of type bool!")
@property
def _calc_vanna(self):
return self.__calc_vanna
@_calc_vanna.setter
def _calc_vanna(self, calc_vanna: bool):
if calc_vanna is not None:
if isinstance(calc_vanna, bool):
self.__calc_vanna = calc_vanna
if self._calc_vanna:
self.calc_vega = True
if not hasattr(self, 'vega_scale'):
self.vega_scale = 0.01
else:
raise TypeError("'" + str(calc_vanna) + "' must be of type bool!")
@property
def _calc_theta(self):
return self.__calc_theta
@_calc_theta.setter
def _calc_theta(self, calc_theta: bool):
if calc_theta is not None:
if isinstance(calc_theta, bool):
self.__calc_theta = calc_theta
if self._calc_theta & ~hasattr(self, 'theta_scale'):
self.theta_scale = 1.0
else:
raise TypeError("'" + str(calc_theta) + "' must be of type bool!")
@property
def _theta_scale(self):
return self.__theta_scale
@_theta_scale.setter
def _theta_scale(self, theta_scale: float):
if theta_scale is not None:
if isinstance(theta_scale, float):
self.__theta_scale = theta_scale
self.calc_theta = True
else:
raise TypeError("'" + str(theta_scale) + "' must be of type float!")
@property
def _calc_spline(self):
return self.__calc_spline
@_calc_spline.setter
def _calc_spline(self, calc_spline: bool):
if calc_spline is not None:
if isinstance(calc_spline, bool):
self.__calc_spline = calc_spline
else:
raise TypeError("'" + str(calc_spline) + "' must be of type bool!")
@property
def _calc_grid_sizes(self):
return self.__calc_grid_sizes
@_calc_grid_sizes.setter
def _calc_grid_sizes(self, calc_grid_sizes: bool):
if calc_grid_sizes is not None:
if isinstance(calc_grid_sizes, bool):
self.__calc_grid_sizes = calc_grid_sizes
else:
raise TypeError("'" + str(calc_grid_sizes) + "' must be of type bool!")
@property
def _calc_implied_volatility(self):
return self.__calc_implied_volatility
@_calc_implied_volatility.setter
def _calc_implied_volatility(self, calc_implied_volatility: bool):
if calc_implied_volatility is not None:
if isinstance(calc_implied_volatility, bool):
self.__calc_implied_volatility = calc_implied_volatility
else:
raise TypeError("'" + str(calc_implied_volatility) + "' must be of type bool!")
@property
def _management_delta_limit(self):
return self.__management_delta_limit
@_management_delta_limit.setter
def _management_delta_limit(self, management_delta_limit: float):
self.__management_delta_limit = management_delta_limit
@property
def _calc_pricing_data(self):
return self.__calc_pricing_data
@_calc_pricing_data.setter
def _calc_pricing_data(self, calc_pricing_data: bool):
if calc_pricing_data is not None:
if isinstance(calc_pricing_data, float):
self.__calc_pricing_data = calc_pricing_data
else:
raise TypeError("'" + str(calc_pricing_data) + "' must be of type float!")
@property
def _calc_expected_cashflows(self):
return self.__calc_expected_cashflows
@_calc_expected_cashflows.setter
def _calc_expected_cashflows(self, calc_expected_cashflows: bool):
if calc_expected_cashflows is not None:
if isinstance(calc_expected_cashflows, bool):
self.__calc_expected_cashflows = calc_expected_cashflows
else:
raise TypeError("'" + str(calc_expected_cashflows) + "' must be of type bool!")
@property
def _calc_simulation_data(self):
return self.__calc_simulation_data
@_calc_simulation_data.setter
def _calc_simulation_data(self, calc_simulation_data: bool):
if calc_simulation_data is not None:
if isinstance(calc_simulation_data, bool):
self.__calc_simulation_data = calc_simulation_data
else:
raise TypeError("'" + str(calc_simulation_data) + "' must be of type bool!")
@property
def _calc_additional_information(self):
return self.__calc_additional_information
@_calc_additional_information.setter
def _calc_additional_information(self, calc_additional_information: bool):
if calc_additional_information is not None:
if isinstance(calc_additional_information, bool):
self.__calc_additional_information = calc_additional_information
else:
raise TypeError("'" + str(calc_additional_information) + "' must be of type bool!")
@property
def _calc_z_spread(self):
return self.__calc_z_spread
@_calc_z_spread.setter
def _calc_z_spread(self, calc_z_spread: bool):
if calc_z_spread is not None:
if isinstance(calc_z_spread, bool):
self.__calc_z_spread = calc_z_spread
else:
raise TypeError("'" + str(calc_z_spread) + "' must be of type bool!")
@property
def _calc_yield_to_maturity(self):
return self.__calc_yield_to_maturity
@_calc_yield_to_maturity.setter
def _calc_yield_to_maturity(self, calc_yield_to_maturity: bool):
if calc_yield_to_maturity is not None:
if isinstance(calc_yield_to_maturity, bool):
self.__calc_yield_to_maturity = calc_yield_to_maturity
else:
raise TypeError("'" + str(calc_yield_to_maturity) + "' must be of type bool!")
@property
def _calc_convexity(self):
return self.__calc_convexity
@_calc_convexity.setter
def _calc_convexity(self, calc_convexity: bool):
if calc_convexity is not None:
if isinstance(calc_convexity, bool):
self.__calc_convexity = calc_convexity
if self._calc_convexity:
self.calc_rho = True
if not hasattr(self, 'rho_scale'):
self.rho_scale = 0.0001
else:
raise TypeError("'" + str(calc_convexity) + "' must be of type bool!")
@property
def _max_expected_cashflow_date(self):
return self.__max_expected_cashflow_date
@_max_expected_cashflow_date.setter
def _max_expected_cashflow_date(self, max_expected_cashflow_date: _Union[date, datetime]):
if max_expected_cashflow_date is not None:
if isinstance(max_expected_cashflow_date, datetime) | isinstance(max_expected_cashflow_date, date):
self.__max_expected_cashflow_date = datetime_to_date(max_expected_cashflow_date)
else:
raise TypeError("'" + str(max_expected_cashflow_date) + "' must be of type datetime or date!")
@property
def _cashflow_times(self):
return self.__cashflow_times
@_cashflow_times.setter
def _cashflow_times(self, cashflow_times: _List[_Union[date, datetime]]):
if cashflow_times is not None:
if isinstance(cashflow_times, list) & (
isinstance(cashflow_times[0], datetime) | isinstance(cashflow_times[0], date)
):
self.__cashflow_times = datetime_to_date_list(cashflow_times)
else:
raise TypeError("'" + str(cashflow_times) + "' must be of type list of datetime or date!")
@property
def _calc_macaulay_duration(self):
return self.__calc_macaulay_duration
@_calc_macaulay_duration.setter
def _calc_macaulay_duration(self, calc_macaulay_duration: bool):
if calc_macaulay_duration is not None:
if isinstance(calc_macaulay_duration, bool):
self.__calc_macaulay_duration = calc_macaulay_duration
else:
raise TypeError("'" + str(calc_macaulay_duration) + "' must be of type bool!")
class BondPricingRequest(PricingRequest):
def __init__(self, calc_clean_price: bool = False, calc_rho: bool = False, rho_scale: float = None,
calc_theta: bool = False, theta_scale: float = None, calc_yield_to_maturity: bool = False,
calc_convexity: bool = False, calc_macaulay_duration: bool = False): # TODO: clarify why no z-spread
"""
Configuration of set of information to be calculated together with the bond's dirty price. In restricts the
general PricingRequest to the sub-set relevant for the pricing of bonds.
"""
PricingRequest.__init__(self, calc_clean_price=calc_clean_price, calc_rho=calc_rho, rho_scale=rho_scale,
calc_theta=calc_theta, theta_scale=theta_scale,
calc_yield_to_maturity=calc_yield_to_maturity, calc_convexity=calc_convexity,
calc_macaulay_duration=calc_macaulay_duration)
if __name__ == '__main__':
bond_pricing_request = BondPricingRequest()