cdxcore.bs#
Basic Black & Scholes pricing routines.
Overview#
This module offers with the bs instance of cdxcore.bs.BS a basic Black & Scholes pricing framework for the drift-less case:
from cdxbasics.bs import bs
call = bs.price(1.,vol=0.2,sqrtT=0.1)
Aside from the respective pricing functions and greeks, bs object also offers
with cdxcore.bs.BS.implied()
a “mass” implied volatility
solver. It can solve implied volatilities for a large number of options in one big Euler/bisection search.
The module contains the cdxcore.bs.BS, whose use case is to define the member cdxcore.bs.bs
which provides the pricing functionality.
Import#
from cdxcore.bs import bs
Documentation#
Module Attributes
Main instance of the |
Classes
|
Base class for the computation of Black & Scholes analytics in drift-less ("pure") price domain. |
|
- class cdxcore.bs.BS[source]#
Bases:
objectBase class for the computation of Black & Scholes analytics in drift-less (“pure”) price domain.
The class is synthactic sugar; simply use the one instance
bsvia:from cdxcore.bs import bs
Then you can do:
call = bs.price( 1., 0.2, sqrtT=0.5 ) vega = bs.vega( 1., 0.2, sqrtT=0.5 )
The most useful function is
bs.implieddocumented undercdxcore.bs.BS.implied`().- DELTA = 2#
Flag to request Delta: N1 for a call
- DK = 4#
Flag to request dK: -N2 for a call
- GAMMA = 8#
Flag to request Gamma
- LOGK = 64#
Flag to request logK, set to 1 where k=0 or vol*sqrtT=0
- PRICE = 1#
Flag to request Price, for a call: N1 - k N2
- THETA = 32#
Flag to request Theta
- VEGA = 16#
Flag to request Vega
- __call__(k, vol, sqrtT=1.0, what=<BSFLAGS.PRICE: 1>, is_call=True, *, logK=None)[source]#
Compute Black Scholes call option prices, and greeks in drift-less price domain efficiently.
This function aims to support almost any broadcast combination for its inputs.
The function returns values for the intrinsic call/put functions when strikes or sqrt-variances approach zero.
Example of using broadcastable shapes:
from cdxcore.bs import bs, np sqrtT = np.array( [0.2, 0.4] ).reshape((1,2)) nms = np.linspace( -1,+1,11 ).reshape((11,1)) k = np.exp( nms * sqrtT ) v = np.random.normal( size=(11,1) )**2 sqrtT = np.array( [0.2, 0.4] ).reshape((1,2)) price, vega = bs( k=k, vol=v, sqrtT=sqrtT, what=bs.PRICE|bs.VEGA )['price','vega'] assert price.shape == (11,2)
- Parameters:
- knp.ndarray | float
Strikes.
- volnp.ndarray | float
Volatilities or a single volatility.
- sqrtTnp.ndarray | float, default
1 Square-root of time.
- whatcombination of
cdxcore.bs.BSFLAGSflags, defaultBS.PRICE A bitmask indicating what to compute. Can be any combination of:
BS.PRICE: Call priceBS.DELTA: DeltaBS.DK: Derivative in strikeBS.VEGA: VegaBS.GAMMA: GammaBS.THETA: ThetaBS.LOGK: Log-strike, with 1 whereeverk==0. This is mainly useful to avoid recomputinglog(k)multiple times.
Note that if only one item is requested the function returns a
np.ndarrayorfloat. Otherwise it will return acdxcore.pretty.PrettyValueObjectwith the requested outputs as attributes. The following then works as expected:from cdxcore.bs import bs price, vega = bs( k=0.8, vol=0.2, sqrtT=1., what=bs.PRICE|bs.VEGA )['price','vega']
The order of items is always “price”, “delta”, “dk”, “gamma”, “vega”, “theta”, “logK” whichever was requested; hence you can also do:
price, vega = bs( k=0.8, vol=0.2, sqrtT=1., what=bs.PRICE|bs.VEGA )
- logKnp.ndarray | float | None, default
None An optional pre-computed log-strike. If provided, this is used instead of computing
log(k)internally. The function will mask this array wherek==0, hencelogKcan be leftNaNin those locations.
- Returns:
- resultnp.ndarray | float | PrettyValueObject[str, np.ndarray|float]
If only one
whatwas requested, this function returns afloatornp.ndarrayfor whatever calculation was requested.If several
whatwere requested, this function returns aPrettyObjectwith the requested outputs as attributes, named and in order:pricedeltadkvegagammathetalogK
That means you can access the result as follows:
from cdxcore.bs import bs price, vega = bs( k=0.8, vol=0.2, sqrtT=1., what=bs.PRICE|bs.VEGA )['price','vega']
or in order of “price”, “delta”, “dk”, “gamma”, “vega”, “theta”, “logK” (whenever reauested) directly in tuple notation:
price, gamma, vega = bs( k=0.8, vol=0.2, sqrtT=1., what=bs.PRICE|bs.VEGA|bs.GAMMA )
- delta(k, vol, sqrtT=1.0, is_call=True, *, logK=None)[source]#
Compute Black Scholes option deltas in drift-less price domain.
- Parameters:
- knp.ndarray
Strikes.
- volnp.ndarray | float
Volatilities or a single volatility.
- sqrtTnp.ndarray | float, default
1 Square-root of time.
- is_callnp.ndarray | bool, default
True Whether to compute call (
True) or put (False) prices.- logK: np.ndarray | float | None, default ``None``
An optional pre-computed log-strike. If provided, this is used instead of computing
log(k)internally.
- Returns:
- deltanp.ndarray
Black Scholes deltas
- dk(k, vol, sqrtT=1.0, is_call=True, *, logK=None)[source]#
Compute Black Scholes dk (derivative in k) in drift-less price domain.
- Parameters:
- knp.ndarray
Strikes.
- volnp.ndarray | float
Volatilities or a single volatility.
- sqrtTnp.ndarray | float, default
1 Square-root of time.
- is_callnp.ndarray | bool, default
True Whether to compute call (
True) or put (False) prices.- logK: np.ndarray | float | None, default ``None``
An optional pre-computed log-strike. If provided, this is used instead of computing
log(k)internally.
- Returns:
- dknp.ndarray
Black Scholes dk (derivative in k)
- gamma(k, vol, sqrtT=1.0, is_call=True, *, logK=None)[source]#
Compute Black Scholes option gamma in drift-less price domain.
- Parameters:
- knp.ndarray
Strikes.
- volnp.ndarray | float
Volatilities or a single volatility.
- sqrtTnp.ndarray | float, default
1 Square-root of time.
- is_callnp.ndarray | bool, default
True Whether to compute call (
True) or put (False) prices.- logK: np.ndarray | float | None, default ``None``
An optional pre-computed log-strike. If provided, this is used instead of computing
log(k)internally.
- Returns:
- gammanp.ndarray
Black Scholes gammas
- implied(k, prices, is_call=True, sqrtT=1.0, *, price_tol=1e-06, vol_min=0.01, vol_max=5.0, default_vol=0.0, mask=None, max_iters=100, eps=1e-10, min_vega=1e-12, ret_only_vols=True, on_exceed_bounds='warn', verbose=<cdxcore.verbose.Context object>)[source]#
Solve for implied volatilities using a robust bisection and Newton-Raphson hybrid method. This function is designed to be run for a large number of potentially unrelated options, for example for a time series of surfaces of options.
This routine:
Assigns
max_volto every option whose price is at or above the option price implied byvol_max, andvol_minto every option whose price is at or below the option price implied byvol_min.Initializes the search at
default_volif it is an array, or otherwise using the closed-form approximation (20). Ifdefault_volis a float, then it is only used where the approximation fails to yield a value.Run a hybrid bisection and Newton-Raphson method to find the implied volatilities for the remaining options until the maximum number of itersations,
max_iters, is reached or the method has converged in the sense that:|price(vol) - market_price| < price_tol
By default the function just returns the implied volatilities, or their last best guesses. If
ret_only_volsis set toFalse, then it returns acdxcore.pretty.PrettyValueObjectcontaining the fitted prices, a boolean area indicating issues, and an error statistics object in the following fields:volscontains the fitted volatilities.fitscontains the the prices at the fitted volatilities.pricescontains the input prices at those values.failedcontains a boolean array indicating which fits did not converge.max_iters: the inputmax_iters.iters: iterations used.max_err: maximum price error.l1_err: average price error.l2_err: quadratic average price error.
- Parameters:
- knp.ndarray
Strikes.
- pricesnp.ndarray
Prices in the same shape as
k.- is_callnp.ndarray | bool, default
True Boolean or boolean array of shape compatible with
k.- sqrtTnp.ndarray | float, default
1 Square-root of time as float or array compatible with
k.- price_tolnp.ndarray | float, default
1E-6 Price tolerance (typically a fraction of spreads) as float or array or array compatible with
k. A standard value is0.1*spread.- vol_minfloat, default
0.01 Minimum volatility.
- vol_maxfloat, default
5 Maximum volatility.
- default_volnp.ndarray | float, default
0. Default and initial volatility guess as float or array compatible with
k. See description above. Also note thatdefault_volwill be clipped to the range[vol_min, vol_max].- masknp.ndarray | None, default
None A numpy array boolean mask to indicate which options to process. The implied vol of options excluded by
maskwill be set todefault_vol.- max_itersint, default
100 Maximum iterations. Usually the routine uses very few iterations.
- epsfloat, default
1E-10 Only used to decide whether stirke or sqrtVar are zero.
- min_vegafloat, default
1E-12 Minimum vega for taking an updates step.
- ret_only_volsbool, default
True Return only vols.
- on_exceed_bounds
error|warn|quiet| None, defaultwarn What to do if the input data violate basic option price bounds such as intrinsic from below or unit/strike from above, respectively.
Noneis equivalent toquiet.- verbose
cdxcore.verbose.Context, defaultcdxcore.verbose.Context.quiet For printing progress information.
- Returns:
- resultsnp.ndarray | PrettyValueObject
See above.
- price(k, vol, sqrtT=1.0, is_call=True, *, logK=None)[source]#
Compute Black Scholes option prices in drift-less price domain.
- Parameters:
- knp.ndarray
Strikes.
- volnp.ndarray | float
Volatilities or a single volatility.
- sqrtTnp.ndarray | float, default
1 Square-root of time.
- is_callnp.ndarray | bool, default
True Whether to compute call (
True) or put (False) prices.- logK: np.ndarray | float | None, default ``None``
An optional pre-computed log-strike. If provided, this is used instead of computing
log(k)internally.
- Returns:
- pricenp.ndarray
Black Scholes prices
- theta(k, vol, sqrtT=1.0, is_call=True, *, logK=None)[source]#
Compute Black Scholes option theta in drift-less price domain.
- Parameters:
- knp.ndarray
Strikes.
- volnp.ndarray | float
Volatilities or a single volatility.
- sqrtTnp.ndarray | float, default
1 Square-root of time.
- is_callnp.ndarray | bool, default
True Whether to compute call (
True) or put (False) prices.- logK: np.ndarray | float | None, default ``None``
An optional pre-computed log-strike. If provided, this is used instead of computing
log(k)internally.
- Returns:
- thetanp.ndarray
Black Scholes thetas
- vega(k, vol, sqrtT=1.0, is_call=True, *, logK=None)[source]#
Compute Black Scholes option vega in drift-less price domain.
- Parameters:
- knp.ndarray
Strikes.
- volnp.ndarray | float
Volatilities or a single volatility.
- sqrtTnp.ndarray | float, default
1 Square-root of time.
- is_callnp.ndarray | bool, default
True Whether to compute call (
True) or put (False) prices.- logK: np.ndarray | float | None, default ``None``
An optional pre-computed log-strike. If provided, this is used instead of computing
log(k)internally.
- Returns:
- veganp.ndarray
Black Scholes vegas
- class cdxcore.bs.BSFLAGS(*values)[source]#
Bases:
IntFlag- DELTA = 2#
Flag to request Delta: N1 for a call
- DK = 4#
Flag to request dK: -N2 for a call
- GAMMA = 8#
Flag to request Gamma
- LOGK = 64#
Flag to request logK, set to 1 where k=0 or vol*sqrtT=0
- PRICE = 1#
Flag to request Price, for a call: N1 - k N2
- THETA = 32#
Flag to request Theta
- VEGA = 16#
Flag to request Vega
- cdxcore.bs.bs = <cdxcore.bs.BS object>#
Main instance of the
cdxcore.bs.BSclass.This is synthatic sugar for being able to write:
from cdxcore.bs import bs price, vega = bs( k, vol, sqrtT, is_call, what=BS.PRICE|BS.VEGA )
or:
gamma = bs.gamma( k, vol, sqrtT, is_call )