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 cdxcore.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, here defined as derivative in time-to-expiry (e.g. it is positive!).
- VEGA = 16#
Flag to request Vega
- __call__(k, vol, sqrtT=1.0, what=<BSFLAGS.PRICE: 1>, is_call=True, *, logK=None, on_precision_error='raise', eps=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: Theta as derivative in time-to-expiry, e.g. it is positive.BS.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.VEGA|bs.PRICE )['price','vega']
You can also access the result by tuple access. The order of the 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.VEGA|bs.PRICE )
Note that as expected the
|operand does not alter the order of the returned data.- 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.- on_precision_error: str, default ``raise``
What to do if any of the calculated values are outside their theoretical bounds by more than some tolerance. Can be one of
"clip","warn", or"raise". The result is clipped to its theoretical bounds regardless of this setting;"warn"additionally issues a warning and"raise"raises aFloatingPointErrorinstead of clipping.- epsfloat | None, default
None An optional precision tolerance for the internal checks. If not provided, the default is
bs._eps(1E-8). Note that the function will cap/floor the values of prices or greeks with the respective bounds even if the validity test passed. Hence, ifepsis set to a large number, this will lead to floored/capped outputs. Note, however, that some bounds such as the maximum value of a call of spot are not tight.
- 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:pricedeltadkgammavegatheta(positive!)logK
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 requested) directly in tuple notation:
price, gamma, vega = bs( k=0.8, vol=0.2, sqrtT=1., what=bs.PRICE|bs.VEGA|bs.GAMMA )
The returned values are in “pure” space. You can conver them as follows:
cash_delta = df * pure_deltacash_gamma = df * pure_gamma / fwdcash_vega = df * fwd * pure_vegacash_theta = df * fwd * pure_thetacash_dK = df * pure_dK
- Raises:
- input errors:
ValueError If any of the inputs is invalid, e.g. negative strikes or volatilities, or if the “what” bitmask is zero or not a valid combination of flags.
- precision errors:
FloatingPointError In case any of the calculated values are outside their theoretical bounds by more than some tolerance. The tolerance level is set by
self._epswhich can be changed for debugging; however please raise issues of this type to the authors.
- input errors:
- delta(k, vol, sqrtT=1.0, is_call=True, *, logK=None, on_precision_error='raise', eps=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.- on_precision_errorstr, default
raise Action to take on precision errors. Options are
raiseto raise an exception orwarnto issue a warning.- epsfloat | None, default
None An optional precision tolerance for greek validation checks. If not provided, the default is
bs._eps(1E-8).
- Returns:
- deltanp.ndarray
Black Scholes deltas
- dk(k, vol, sqrtT=1.0, is_call=True, *, logK=None, on_precision_error='raise', eps=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.- on_precision_errorstr, default
raise Action to take on precision errors. Options are
raiseto raise an exception orwarnto issue a warning.- epsfloat | None, default
None An optional precision tolerance for internal price validation checks. If not provided, the default is
bs._eps(1E-8).
- Returns:
- dknp.ndarray
Black Scholes dk (derivative in k)
- gamma(k, vol, sqrtT=1.0, is_call=True, *, logK=None, on_precision_error='raise', eps=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.- on_precision_errorstr, default
raise Action to take on precision errors. Options are
raiseto raise an exception orwarnto issue a warning.- epsfloat | None, default
None An optional precision tolerance for greek validation checks. If not provided, the default is
bs._eps(1E-8).
- 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
vol_maxto 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. Must be at least1E-8. 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 Used to decide whether strike or
sqrtTare zero, and as the precision tolerance passed to the internalcdxcore.bs.BS.price()andcdxcore.bs.BS.vega()calls.- min_vegafloat, default
1E-12 Minimum vega for taking an update 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, on_precision_error='raise', eps=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.- on_precision_errorstr, default
raise Action to take on precision errors. Options are
raiseto raise an exception orwarnto issue a warning.- epsfloat | None, default
None An optional precision tolerance for internal price validation checks. If not provided, the default is
bs._eps(1E-8).
- Returns:
- pricenp.ndarray
Black Scholes prices
- static pure_to_cash(pure, *, fwd, df)[source]#
Converts dictionary of
pureprice and greeks into market price and greeks.Assume
Cp(T,k) = E[(X_T-k)^+]whereXis a pure log-normal martingale. LetC(T,K) := DF F Cp(T, K/F)be the market call price.Then:
`python Delta = dC/dK = DF Delta^p Gamma = d^2C/dK^2 = DF Gamma^p / F Vega = dC/dsigma = DF F Vega^p Opt_Theta = dC/dt = DF F Theta^p (this is optionality theta, excluding curve and carry) DK = dC/dF = DF DK^p `Note that this function computes “optionality theta” as the decay due to loss of optionality, and excludes the effect on a change in discount factor or forward.
- Parameters:
- pure
Mapping A dictionary containing any of the greeks above.
- fwdfloat
Forward price.
- dffloat
Discount factor.
- pure
- Returns:
- mkt
cdxcore.pretty.PrettyValueObject A dictionary with the same inputs as
pure; ifthetawas present in pure, then this object will containopt_theta.
- mkt
- theta(k, vol, sqrtT=1.0, is_call=True, *, logK=None, on_precision_error='raise', eps=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.- on_precision_errorstr, default
raise Action to take on precision errors. Options are
raiseto raise an exception orwarnto issue a warning.- epsfloat | None, default
None An optional precision tolerance for greek validation checks. If not provided, the default is
bs._eps(1E-8).
- Returns:
- thetanp.ndarray
Black Scholes thetas
- vega(k, vol, sqrtT=1.0, is_call=True, *, logK=None, on_precision_error='raise', eps=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.- on_precision_errorstr, default
raise Action to take on precision errors. Options are
raiseto raise an exception orwarnto issue a warning.- epsfloat | None, default
None An optional precision tolerance for greek validation checks. If not provided, the default is
bs._eps(1E-8).
- 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 )