Coverage for src/scrilla/analysis/models/geometric/probability.py: 78%

42 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-07-18 18:14 +0000

1# This file is part of scrilla: https://github.com/chinchalinchin/scrilla. 

2 

3# scrilla is free software: you can redistribute it and/or modify 

4# it under the terms of the GNU General Public License version 3 

5# as published by the Free Software Foundation. 

6 

7# scrilla is distributed in the hope that it will be useful, 

8# but WITHOUT ANY WARRANTY; without even the implied warranty of 

9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

10# GNU General Public License for more details. 

11 

12# You should have received a copy of the GNU General Public License 

13# along with scrilla. If not, see <https://www.gnu.org/licenses/> 

14# or <https://github.com/chinchalinchin/scrilla/blob/develop/main/LICENSE>. 

15""" 

16This module contains probability functions unique to the Geometric Brownian Motion stochastic model. All functions assume an asset price process that follows GBM. In other words, the return distribution is lognormal. This is equivalent to the Black-Scholes model from finance.  

17 

18The calculations have no stance on whether the actual probability distribution or the risk-neutral probability distribution should be used. This is implicitly specified by the user when they provided a rate of return to functions in this module. If the user wishes to use a risk-neutral probability, simply provide the assumed risk-free rate. If the user wishes to use an actual probability, provide the estimate of the rate of the return for the asset. 

19""" 

20from scipy.stats import norm 

21from math import sqrt, exp, log 

22from scrilla import settings 

23from scrilla.util.outputter import Logger 

24 

25logger = Logger('scrilla.analysis.models.geometric.statistics', 

26 settings.LOG_LEVEL) 

27 

28 

29def d1(S0: float, ST: float, vol: float, ret: float, expiry: float, div: float = 0) -> float: 

30 """ 

31 Returns the value of *d1* defined by the Black Scholes model (Geometric Brownian Motion) 

32 

33 Parameters 

34 ---------- 

35 1. **S0** : ``float` 

36 Initial value of the asset. 

37 2. **ST** : ``float`` 

38 Final value of the asset. 

39 3. **vol**: ``float`` 

40 Annualized volatility of the asset price process. 

41 4. **ret**: ``float`` 

42 Annualized return of the asset price process. 

43 5. **expiry**: ``float`` 

44 Time horizon in years, i.e. the time delta between `ST` and `S0` measured in years.  

45 6. **div**: ``float`` 

46 *Optional*. Annualized dividend yield. Defaults to 0. 

47 

48 Returns 

49 ------- 

50 ``float`` : Black-Scholes *d1* 

51 """ 

52 numerator = log(S0/ST) + (ret - div + 0.5 * (vol ** 2))*expiry 

53 denominator = vol * sqrt(expiry) 

54 return (numerator/denominator) 

55 

56 

57def d2(S0: float, ST: float, vol: float, ret: float, expiry: float, div: float = 0) -> float: 

58 """ 

59 Returns the value of *d2* defined by the Black Scholes model (Geometric Brownian Motion) 

60 

61 Parameters 

62 ---------- 

63 1. **S0** : ``float`` 

64 Initial value of the asset. 

65 2. **ST** : ``float`` 

66 Final value of the asset. 

67 3. **vol**: ``float`` 

68 Annualized volatility of the asset price process. 

69 4. **ret**: ``float`` 

70 Annualized return of the asset price process. 

71 5. **expiry**: ``float`` 

72 Time horizon in years, i.e. the time delta between `ST` and `S0` measured in years.  

73 6. **div**: ``float`` 

74 *Optional*. Annualized dividend yield. Defaults to 0. 

75 

76 Returns 

77 ------- 

78 ``float`` : Black-Scholes *d2* 

79 """ 

80 thisD1 = d1(S0=S0, ST=ST, vol=vol, ret=ret, expiry=expiry, div=div) 

81 adjust = vol * sqrt(expiry) 

82 return (thisD1 - adjust) 

83 

84 

85def prob_d1(S0: float, ST: float, vol: float, ret: float, expiry: float, div: float = 0, neg: bool = False) -> float: 

86 """ 

87 Returns the probability of *d1* defined by the Black Scholes model (Geometric Brownian Motion) 

88 

89 Parameters 

90 ---------- 

91 1. **S0** : ``float`` 

92 Initial value of the asset. 

93 2. **ST** : ``float` 

94 Final value of the asset. 

95 3. **vol**: ``float`` 

96 Annualized volatility of the asset price process. 

97 4. **ret**: ``float`` 

98 Annualized return of the asset price process. 

99 5. **expiry**: ``float`` 

100 Time horizon in years, i.e. the time delta between `ST` and `S0` measured in years.  

101 6. **div**: ``float`` 

102 *Optional*. Annualized dividend yield. Defaults to 0.  

103 7. **neg**: ``bool`` 

104 *Optional*. Calculates the probability of *-d1*. Defaults to `False`. 

105 

106 Returns 

107 ------- 

108 ``float`` : cumulative probability of *d1* 

109 """ 

110 if vol == 0: 110 ↛ 111line 110 didn't jump to line 111, because the condition on line 110 was never true

111 pass 

112 thisD1 = d1(S0=S0, ST=ST, vol=vol, ret=ret, expiry=expiry, div=div) 

113 if neg: 

114 thisD1 = -thisD1 

115 return norm.cdf(thisD1) 

116 

117 

118def prob_d2(S0: float, ST: float, vol: float, ret: float, expiry: float, div: float = 0, neg: bool = False): 

119 """ 

120 Returns the probability of *d2* defined by the Black Scholes model (Geometric Brownian Motion) 

121 

122 Parameters 

123 ---------- 

124 1. **S0** : ``float`` 

125 Initial value of the asset. 

126 2. **ST** : ``float` 

127 Final value of the asset. 

128 3. **vol**: ``float`` 

129 Annualized volatility of the asset price process. 

130 4. **ret**: ``float`` 

131 Annualized return of the asset price process. 

132 5. **expiry**: ``float`` 

133 Time horizon in years, i.e. the time delta between `ST` and `S0`.  

134 6. **div**: ``float`` 

135 *Optional*. Annualized dividend yield. Defaults to 0.  

136 7. **neg**: ``bool`` 

137 *Optional*. Calculates the probability of *-d2*. Defaults to `False`. 

138 

139 Returns 

140 ------- 

141 ``float`` : cumulative probability of *d2* 

142 """ 

143 # if no uncertainty 

144 if vol == 0: 144 ↛ 146line 144 didn't jump to line 146, because the condition on line 144 was never true

145 # if the future value of initial price is greater than the strike 

146 if S0*exp((ret-div)*expiry) > ST: 

147 # probability(S>St)=1 

148 return 1 

149 # probability(S>St)=0 

150 return 0 

151 

152 thisD2 = d2(S0=S0, ST=ST, vol=vol, ret=ret, expiry=expiry, div=div) 

153 if neg: 153 ↛ 155line 153 didn't jump to line 155, because the condition on line 153 was never false

154 thisD2 = -thisD2 

155 return norm.cdf(thisD2) 

156 

157 

158def percentile(S0: float, vol: float, ret: float, expiry: float, prob: float, div: float = 0) -> float: 

159 """ 

160 Returns the final value of the asset price over the time horizon `expiry` that corresponds the `percentile` of the price's distribution. The asset price process is assumed to follow Geometric Brownian Motion, i.e. the return distribution is lognormal.  

161 

162 Parameters 

163 ---------- 

164 1. **S0** : ``float`` 

165 Initial value of the asset. 

166 2. **vol**: ``float`` 

167 Annualized volatility of the asset price process. 

168 3. **ret**: ``float`` 

169 Annualized return of the asset price process. 

170 4. **expiry**: ``float`` 

171 Time horizon in years, i.e. the time delta between `ST` and `S0` measured in years.  

172 5. **prob**: ``float`` 

173 Percentile of the distribution to be returned, i.e. if S is the price process and \\(S_t\\) is the percentile, 

174 

175 $$ Pr(S<S_t)=prob $$ 

176 

177 6. **div**: ``float`` 

178 *Optional*. Annualized dividend yield. Defaults to 0.  

179 

180 Returns 

181 ------- 

182 ``float`` : percentile of lognormal distribution 

183 

184 .. notes:: 

185 * Percentiles are preserved under log transformations, so the asset percentile is equal to the exponentiated return percentile. See Wiki article for more information: https://en.wikipedia.org/wiki/Log-normal_distribution#Mode,_median,_quantiles 

186 """ 

187 inv_norm = norm.ppf(prob) 

188 exponent = (ret - div - 0.5*(vol**2))*expiry + vol*sqrt(expiry)*inv_norm 

189 return (S0*exp(exponent)) 

190 

191 

192def conditional_expected_value(S0: float, vol: float, ret: float, expiry: float, conditional_value: float, greater: bool = False, div: float = 0) -> float: 

193 """ 

194 Calculates the conditional expected value of an equity, assuming the price process follows Geometric Brownian Motion, i.e. the price distribution is lognormal. 

195 

196 Note: Returns either 

197 >>> if greater: 

198 >>> E( St | St > condtional_value) 

199 >>> if not greater: 

200 >>> E( St | St < conditional_value) 

201 

202 Parameters 

203 ---------- 

204 1. **S0** : ``float`` 

205 Initial equity price. 

206 2. **vol** : ``float`` 

207 Annualized volatility. 

208 3. ***ret**: ``float`` 

209 Annualized return. 

210 4. **expiry** : ``float`` 

211 Time in years until the condition is evaluated. 

212 5. **conditional_value**: ``float`` 

213 The value of the equity conditioned on at time ``expiry``. 

214 6. **greater** : ``boolean`` 

215 *Optional*. Defaults to `False`. Determines the direction of the inequality for the condition attached to the expectation. See function description for more information. 

216 7. **div**: ``float`` 

217 *Optional*. Defaults to `0`. Annualized dividend yield. 

218 """ 

219 forward_value = S0*exp((ret - div)*expiry) 

220 if greater: 220 ↛ 221line 220 didn't jump to line 221, because the condition on line 220 was never true

221 this_prob_d1 = prob_d1(S0=S0, ST=conditional_value, 

222 vol=vol, ret=ret, expiry=expiry, div=div) 

223 this_prob_d2 = prob_d2(S0=S0, ST=conditional_value, 

224 vol=vol, ret=ret, expiry=expiry, div=div) 

225 else: 

226 this_prob_d1 = prob_d1(S0=S0, ST=conditional_value, 

227 vol=vol, ret=ret, expiry=expiry, div=div, neg=True) 

228 this_prob_d2 = prob_d2(S0=S0, ST=conditional_value, 

229 vol=vol, ret=ret, expiry=expiry, div=div, neg=True) 

230 

231 return (forward_value*this_prob_d1/this_prob_d2)