Skip to content

Commit

Permalink
Merge branch 'master' of github.com:jkirkby3/fypy
Browse files Browse the repository at this point in the history
  • Loading branch information
jkirkby3 committed Sep 13, 2024
2 parents 91221e9 + 4164fab commit a447707
Show file tree
Hide file tree
Showing 30 changed files with 2,930 additions and 97 deletions.
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,23 @@ This library is under active development, although the currently posted features
- Stochastic Volatility with Jumps Model Calibration
- SABR Model calibration

## Coming Soon !
### Contract types supported (single underlying):
- European Options
- Barrier Options (Single/Double barrier, and rebates)
- Asian Options (Discrete/Continuous)
- Discrete Variance Swaps, Variance/Volatility Options
- Bermudan/American early-exercise Options
- Parisian Options (Cumulative and resetting Parisian barrier options)
- Cliquets/Equity Indexed Annuities (Additive/Multiplicative)
- Step (Soft Barrier) Options
- Lookback/Hindsight Options
- Fader/Range-Accrual Options

- Exotic Option Pricing - Asian, Barrier, American, Parisian, Cliquet, Variance Swaps, etc.
- Models: Stochastic Volatility, Regime Switching, Stochastic Local Vol
## Coming Soon !
- More Exotic Option Pricing
- Models: Stochastic Volatility, Stochastic Local Vol
- Additional pricing methods, such as Mellin Series, PDE, Monte Carlo, etc.
- Regime Switching Calibration
- Credit model calibration
- Many of the exotic pricing algorithms will be translated into python from:
https://github.com/jkirkby3/PROJ_Option_Pricing_Matlab

Expand Down Expand Up @@ -137,4 +147,4 @@ plt.legend()
plt.xlabel(r'strike, $K$')
plt.ylabel('implied vol')
plt.show()
```
```
Empty file.
112 changes: 111 additions & 1 deletion fypy/pricing/analytical/black_scholes.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,76 @@ def black76_price_strikes(F: float,
return prices


def black76_gamma(F: float,
K: Union[float, np.ndarray],
vol: Union[float, np.ndarray],
disc: float,
T: float) -> Union[float, np.ndarray]:
"""
Gamma(s) for strike(s)
:param F: float, forward price
:param K: float or array, the Strike(s)
:param vol: float or array, the Volatility(ies) ... if float, all strikes get same vol, else a vol smile
:param disc: float, the discount factor, e.g. 0.99
:param T: float, time to maturity of option
:return: float or np.ndarray, same shape as strikes
"""
vol_st = vol * np.sqrt(T)
d_1 = (np.log(F / K) + 0.5 * vol_st ** 2) / vol_st
return disc * norm.pdf(d_1) / (F * vol_st)


def black76_theta(F: float,
K: Union[float, np.ndarray],
is_call: bool,
vol: Union[float, np.ndarray],
disc: float,
T: float) -> Union[float, np.ndarray]:
"""
Theta(s) for strike(s)
:param F: float, forward price
:param K: float or array, the Strike(s)
:param is_call: bool, determines if ALL strikes are call or all are put
:param vol: float or array, the Volatility(ies) ... if float, all strikes get same vol, else a vol smile
:param disc: float, the discount factor, e.g. 0.99
:param T: float, time to maturity of option
:return: float or np.ndarray, same shape as strikes
"""
sqt = np.sqrt(T)
vol_st = vol * sqt
d_1 = (np.log(F / K) + 0.5 * vol_st ** 2) / vol_st
d_2 = d_1 - vol_st

term1 = - F * disc * norm.pdf(d_1) * vol / (2 * sqt)
r = - np.log(disc) / T

phi = 1 if is_call else -1
return term1 - phi * r * K * disc * norm.cdf(phi * d_2) + phi * r * F * disc * norm.cdf(phi * d_1)


def black76_rho(F: float,
K: Union[float, np.ndarray],
is_call: bool,
vol: Union[float, np.ndarray],
disc: float,
T: float) -> Union[float, np.ndarray]:
"""
Rhos(s) for strike(s)
:param F: float, forward price
:param K: float or array, the Strike(s)
:param is_call: bool, determines if ALL strikes are call or all are put
:param vol: float or array, the Volatility(ies) ... if float, all strikes get same vol, else a vol smile
:param disc: float, the discount factor, e.g. 0.99
:param T: float, time to maturity of option
:return: float or np.ndarray, same shape as strikes
"""
vol_st = vol * np.sqrt(T)
d_1 = (np.log(F / K) + 0.5 * vol_st ** 2) / vol_st
d_2 = d_1 - vol_st
phi = 1 if is_call else -1
return -T * disc * phi * (F * norm.cdf(phi * d_1) - K * norm.cdf(phi * d_2))


def black76_vega(F: float,
K: Union[float, np.ndarray],
vol: Union[float, np.ndarray],
Expand All @@ -77,6 +147,46 @@ def black76_vega(F: float,
d_1 = (np.log(F / K) + 0.5 * vol_st ** 2) / vol_st
return disc * F * norm.pdf(d_1) * np.sqrt(T)

def black76_vanna(F: float,
K: Union[float, np.ndarray],
vol: Union[float, np.ndarray],
disc: float,
T: float) -> Union[float, np.ndarray]:
"""
Vanna(s) for strike(s)
:param F: float, forward price
:param K: float or array, the Strike(s)
:param vol: float or array, the Volatility(ies) ... if float, all strikes get same vol, else a vol smile
:param disc: float, the discount factor, e.g. 0.99
:param T: float, time to maturity of option
:return: float or np.ndarray, same shape as strikes
"""
vol_st = vol * np.sqrt(T)
d_1 = (np.log(F / K) + 0.5 * vol_st ** 2) / vol_st
d_2 = d_1 - vol_st
return -disc * norm.pdf(d_1) * d_2 / vol


def black76_vomma(F: float,
K: Union[float, np.ndarray],
vol: Union[float, np.ndarray],
disc: float,
T: float) -> Union[float, np.ndarray]:
"""
Vomma(s) for strike(s)
:param F: float, forward price
:param K: float or array, the Strike(s)
:param vol: float or array, the Volatility(ies) ... if float, all strikes get same vol, else a vol smile
:param disc: float, the discount factor, e.g. 0.99
:param T: float, time to maturity of option
:return: float or np.ndarray, same shape as strikes
"""
sqt = np.sqrt(T)
vol_st = vol * sqt
d_1 = (np.log(F / K) + 0.5 * vol_st ** 2) / vol_st
d_2 = d_1 - vol_st
vega = disc * F * norm.pdf(d_1) * sqt
return vega * d_1 * d_2 / vol

def black76_vanna(F: float,
K: Union[float, np.ndarray],
Expand Down Expand Up @@ -141,7 +251,7 @@ def black76_delta(F: float,
phi = 1 if is_call else -1
d_1 = (np.log(F / K) + 0.5 * vol_st ** 2) / vol_st
delta = phi * norm.cdf(phi * d_1)
if is_fwd_delta:
if not is_fwd_delta:
delta *= div_disc
return delta

Expand Down
2 changes: 1 addition & 1 deletion fypy/pricing/fourier/GilPeleazEuropeanPricer.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ def price(self, T: float, K: float, is_call: bool):
if not is_call:
price -= sf - K * disc

return price
return price
2 changes: 1 addition & 1 deletion fypy/pricing/fourier/ProjAsianPricer.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,4 +461,4 @@ def _price_update(self, option_params: np.ndarray, grid_params: np.ndarray, beta

Val = Val + option_params['C'].item() * np.exp(-r * T) * mult - K[index] * np.exp(-r * T)

output[index] = max(0, Val)
output[index] = max(0, Val)
16 changes: 16 additions & 0 deletions fypy/pricing/fourier/StochVol/ProjAmericanRichardson.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import numpy as np


from fypy.model.FourierModel import FourierModel
from fypy.pricing.fourier.StochVol.ProjBermudanPricer_SV import ProjBermudanPricer_SV


class ProjAmericanRichardson:
def __init__(self, model: FourierModel, N: int = 2**11):
self._bermudan_pricer = ProjBermudanPricer_SV(model, N)
return

def price(self, T: float, W: int, S0: float, M: int, is_call: bool) -> float:
price2M = self._bermudan_pricer.price(T, W, S0, 2 * M, is_call)
priceM = self._bermudan_pricer.price(T, W, S0, M, is_call)
return 2 * price2M - priceM
Loading

0 comments on commit a447707

Please sign in to comment.