2
2
3
3
from functools import lru_cache
4
4
5
- import numpy as np
6
5
from numpy import ndarray , exp , abs , round , diff , flatnonzero , arange , inf
7
6
from numpy .lib .scimath import log , sqrt
8
7
from numpy .random import normal , laplace
@@ -25,9 +24,9 @@ def get_pl_profile(
25
24
x : float ,
26
25
val : float ,
27
26
n : int ,
28
- s : np . ndarray ,
27
+ s : ndarray ,
29
28
commission : float = 0.0 ,
30
- ) -> tuple [np . ndarray , float ]:
29
+ ) -> tuple [ndarray , float ]:
31
30
"""
32
31
get_pl_profile(option_type, action, x, val, n, s, commission) -> returns the profit/loss
33
32
profile and cost of an option trade at expiration.
@@ -60,8 +59,8 @@ def get_pl_profile(
60
59
61
60
62
61
def get_pl_profile_stock (
63
- s0 : float , action : Action , n : int , s : np . ndarray , commission : float = 0.0
64
- ) -> tuple [np . ndarray , float ]:
62
+ s0 : float , action : Action , n : int , s : ndarray , commission : float = 0.0
63
+ ) -> tuple [ndarray , float ]:
65
64
"""
66
65
get_pl_profile_stock(s0, action, n, s, commission) -> returns the profit/loss
67
66
profile and cost of a stock position.
@@ -94,7 +93,7 @@ def get_pl_profile_bs(
94
93
target_to_maturity_years : float ,
95
94
volatility : float ,
96
95
n : int ,
97
- s : np . ndarray ,
96
+ s : ndarray ,
98
97
y : float = 0.0 ,
99
98
commission : float = 0.0 ,
100
99
):
@@ -137,7 +136,7 @@ def get_pl_profile_bs(
137
136
138
137
139
138
@lru_cache
140
- def create_price_seq (min_price : float , max_price : float ) -> np . ndarray :
139
+ def create_price_seq (min_price : float , max_price : float ) -> ndarray :
141
140
"""
142
141
create_price_seq(min_price, max_price) -> generates a sequence of stock prices
143
142
from 'min_price' to 'max_price' with increment $0.01.
@@ -164,7 +163,7 @@ def create_price_samples(
164
163
distribution : Distribution = "black-scholes" ,
165
164
y : float = 0.0 ,
166
165
n : int = 100_000 ,
167
- ) -> np . ndarray :
166
+ ) -> ndarray :
168
167
"""
169
168
create_price_samples(s0, volatility, years_to_maturity, r, distribution, y, n) -> generates
170
169
random stock prices at maturity according to a statistical distribution.
@@ -177,28 +176,26 @@ def create_price_samples(
177
176
r: annualized risk-free interest rate (default is 0.01). Used only if
178
177
distribution is 'black-scholes'.
179
178
distribution: statistical distribution used to generate random stock prices
180
- at maturity. It can be 'black-scholes' (default), 'normal' or
181
- 'laplace'.
179
+ at maturity. It can be 'black-scholes' (default; 'normal' is
180
+ equivalent) or 'laplace'.
182
181
y: annualized dividend yield (default is zero).
183
182
n: number of randomly generated terminal prices.
184
183
"""
185
- if distribution == "normal" :
186
- return exp (normal (log (s0 ), volatility * sqrt (years_to_maturity ), n ))
187
- elif distribution == "black-scholes" :
188
- drift = (r - y - 0.5 * volatility * volatility ) * years_to_maturity
184
+ drift = (r - y - 0.5 * volatility * volatility ) * years_to_maturity
189
185
186
+ if distribution in ("black-scholes" , "normal" ):
190
187
return exp (normal ((log (s0 ) + drift ), volatility * sqrt (years_to_maturity ), n ))
191
188
elif distribution == "laplace" :
192
189
return exp (
193
- laplace (log (s0 ), (volatility * sqrt (years_to_maturity )) / sqrt (2.0 ), n )
190
+ laplace (
191
+ (log (s0 ) + drift ), (volatility * sqrt (years_to_maturity )) / sqrt (2.0 ), n
192
+ )
194
193
)
195
194
else :
196
195
raise ValueError ("Distribution not implemented yet!" )
197
196
198
197
199
- def get_profit_range (
200
- s : np .ndarray , profit : np .ndarray , target : float = 0.01
201
- ) -> list [Range ]:
198
+ def get_profit_range (s : ndarray , profit : ndarray , target : float = 0.01 ) -> list [Range ]:
202
199
"""
203
200
get_profit_range(s, profit, target) -> returns pairs of stock prices, as a list,
204
201
for which an option trade is expected to get the desired profit in between.
@@ -252,13 +249,12 @@ def get_pop(
252
249
get_pop(profit_ranges, source, kwargs) -> estimates the probability of profit
253
250
(PoP) of an option trade.
254
251
255
- * For 'source="normal "' or 'source="laplace"' : the probability of
256
- profit is calculated assuming either a (log)normal or a (log)Laplace
257
- distribution of terminal stock prices at maturity .
252
+ * For 'source="black-scholes "' (default; 'normal' is equivalent) : the probability
253
+ of profit is calculated assuming a (log)normal distribution as implemented in
254
+ the Black-Scholes model .
258
255
259
- * For 'source="black-scholes"' (default): the probability of profit
260
- is calculated assuming a (log)normal distribution with risk neutrality
261
- as implemented in the Black-Scholes model.
256
+ * For 'source="laplace"': the probability of profit is calculated assuming
257
+ a (log)Laplace distribution of terminal stock prices at maturity.
262
258
263
259
* For 'source="array"': the probability of profit is calculated
264
260
from a 1D numpy array of stock prices typically at maturity generated
@@ -282,24 +278,17 @@ def get_pop(
282
278
stock_price = inputs .stock_price
283
279
volatility = inputs .volatility
284
280
years_to_maturity = inputs .years_to_maturity
285
- drift = 0.0
286
-
287
- if inputs .source == "black-scholes" :
288
- r = (
289
- inputs .interest_rate or 0.0
290
- ) # 'or' just for typing purposes, as `interest_rate` must be non-zero
291
- y = inputs .dividend_yield
292
-
293
- drift = (r - y - 0.5 * volatility * volatility ) * years_to_maturity
294
-
281
+ r = (
282
+ inputs .interest_rate or 0.0
283
+ ) # 'or' just for typing purposes, as `interest_rate` must be non-zero
284
+ y = inputs .dividend_yield
285
+ drift = (r - y - 0.5 * volatility * volatility ) * years_to_maturity
295
286
sigma = volatility * sqrt (years_to_maturity )
296
287
297
288
if sigma == 0.0 :
298
289
sigma = 1e-10
299
290
300
- beta = 0.0
301
- if inputs .source == "laplace" :
302
- beta = sigma / sqrt (2.0 )
291
+ beta = sigma / sqrt (2.0 )
303
292
304
293
for p_range in profit_ranges :
305
294
lval = p_range [0 ]
@@ -314,8 +303,8 @@ def get_pop(
314
303
) - stats .norm .cdf ((log (lval / stock_price ) - drift ) / sigma )
315
304
else :
316
305
pop += stats .laplace .cdf (
317
- log (hval / stock_price ) / beta
318
- ) - stats .laplace .cdf (log (lval / stock_price ) / beta )
306
+ ( log (hval / stock_price ) - drift ) / beta
307
+ ) - stats .laplace .cdf (( log (lval / stock_price ) - drift ) / beta )
319
308
320
309
elif isinstance (inputs , ProbabilityOfProfitArrayInputs ):
321
310
stocks = inputs .array
@@ -338,8 +327,8 @@ def get_pop(
338
327
339
328
340
329
def _get_pl_option (
341
- option_type : OptionType , opvalue : float , action : Action , s : np . ndarray , x : float
342
- ) -> np . ndarray :
330
+ option_type : OptionType , opvalue : float , action : Action , s : ndarray , x : float
331
+ ) -> ndarray :
343
332
"""
344
333
getPLoption(option_type,opvalue,action,s,x) -> returns the profit (P) or loss
345
334
(L) per option of an option trade at expiration.
@@ -361,7 +350,7 @@ def _get_pl_option(
361
350
raise ValueError ("Action must be either 'sell' or 'buy'!" )
362
351
363
352
364
- def _get_payoff (option_type : OptionType , s : np . ndarray , x : float ) -> np . ndarray :
353
+ def _get_payoff (option_type : OptionType , s : ndarray , x : float ) -> ndarray :
365
354
"""
366
355
get_payoff(option_type, s, x) -> returns the payoff of an option trade at expiration.
367
356
@@ -380,7 +369,7 @@ def _get_payoff(option_type: OptionType, s: np.ndarray, x: float) -> np.ndarray:
380
369
raise ValueError ("Option type must be either 'call' or 'put'!" )
381
370
382
371
383
- def _get_pl_stock (s0 : float , action : Action , s : np . ndarray ) -> np . ndarray :
372
+ def _get_pl_stock (s0 : float , action : Action , s : ndarray ) -> ndarray :
384
373
"""
385
374
get_pl_stock(s0,action,s) -> returns the profit (P) or loss (L) of a stock
386
375
position.
0 commit comments