├── .gitignore ├── LICENSE ├── README.md ├── bsm.py ├── example_greeks.py ├── example_plot.py ├── plot.py ├── pricing.py └── searching.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 yzoz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vanilla options calculator 2 | ## Black-Scholes model in pure Python 3 | ### Without SciPy, NumPy or other external dependencies 4 | 5 | Use **example_greeks.py** to calculate *Theo, Delta, Theta, Vega, Gamma* for single option 6 | 7 | Use **example_plot.py** to visualize your position with *Matplotlib* 8 | -------------------------------------------------------------------------------- /bsm.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class BSM(): 4 | 5 | def pdf(self, x): 6 | return math.exp(-x**2/2) / math.sqrt(2*math.pi) 7 | 8 | def cdf(self, x): 9 | return (1 + math.erf(x / math.sqrt(2))) / 2 10 | 11 | def d1(self, S, K, V, T): 12 | return (math.log(S / float(K)) + (V**2 / 2) * T) / (V * math.sqrt(T)) 13 | 14 | def d2(self, S, K, V, T): 15 | return self.d1(S, K, V, T) - (V * math.sqrt(T)) 16 | 17 | def theo(self, S, K, V, T, dT): 18 | if dT == 'C': 19 | return S * self.cdf(self.d1(S, K, V, T)) - K * self.cdf(self.d2(S, K, V, T)) 20 | else: 21 | return K * self.cdf(-self.d2(S, K, V, T)) - S * self.cdf(-self.d1(S, K, V, T)) 22 | 23 | def delta(self, S, K, V, T, dT): 24 | if dT == 'C': 25 | delta = self.cdf(self.d1(S, K, V, T)) 26 | elif dT == 'P': 27 | delta = self.cdf(self.d1(S, K, V, T)) - 1 28 | else: 29 | delta = 1 30 | return delta 31 | 32 | def vega(self, S, K, V, T): 33 | vega = (S * math.sqrt(T) * self.pdf(self.d1(S, K, V, T))) / 100 34 | return vega 35 | 36 | def theta(self, S, K, V, T): 37 | theta = -((S * V * self.pdf(self.d1(S, K, V, T))) / (2 * math.sqrt(T))) / 365 38 | return theta 39 | 40 | def gamma(self, S, K, V, T): 41 | gamma = self.pdf(self.d1(S, K, V, T))/(S * V * math.sqrt(T)) 42 | return gamma -------------------------------------------------------------------------------- /example_greeks.py: -------------------------------------------------------------------------------- 1 | from bsm import BSM 2 | 3 | calc = BSM() 4 | 5 | #Bitcoin example 6 | 7 | S = 21000 #Underlying (price now) 8 | K = 30000 #Strike 9 | V = 0.75 #Implied Volatility (1 / 100%) 10 | T = 30 / 365 #Expiration date (days from now / 365) 11 | dT = "C" #Call / Put 12 | 13 | print('Theo: ', round(calc.theo(S, K, V, T, dT), 2)) 14 | print('Delta: ', round(calc.delta(S, K, V, T, dT), 2)) 15 | print('Theta: ', round(calc.theta(S, K, V, T), 2)) 16 | print('Vega: ', round(calc.vega(S, K, V, T), 2)) 17 | print('Gamma: ', round(calc.gamma(S, K, V, T), 2)) -------------------------------------------------------------------------------- /example_plot.py: -------------------------------------------------------------------------------- 1 | from plot import Plot 2 | import matplotlib.pyplot as plt 3 | 4 | plot = Plot() 5 | 6 | god = 365 7 | step = 10 8 | deep = 7500 9 | 10 | exp = 30 / god 11 | exp2 = 15 / god 12 | exp3 = 5 / god 13 | 14 | fPrice = 21000 15 | 16 | params = [] 17 | 18 | dType = 'F' 19 | quant = 1 20 | price = 20000 21 | strike = 0 22 | vola = 0 23 | params.append({'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp}) 24 | 25 | dType = 'C' 26 | quant = 1 27 | price = 500 28 | strike = 25000 29 | vola = 0.75 30 | params.append({'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp}) 31 | 32 | dType = 'P' 33 | quant = 1 34 | price = 100 35 | strike = 15000 36 | vola = 0.75 37 | params.append({'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp}) 38 | 39 | dType = 'P' 40 | quant = -1 41 | price = 25 42 | strike = 10000 43 | vola = 1.5 44 | params.append({'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp}) 45 | 46 | bePriceS = fPrice - deep 47 | bePriceF = fPrice + deep 48 | 49 | plot.plotPL(bePriceS, bePriceF, params, exp, step) 50 | plot.plotPL(bePriceS, bePriceF, params, exp2, step) 51 | plot.plotPL(bePriceS, bePriceF, params, exp3, step) 52 | plt.ylabel("P/L") 53 | plt.xlabel("Price") 54 | plt.title("Option") 55 | plt.grid(True) 56 | plt.legend() 57 | plt.show() 58 | 59 | plot.plotDelta(bePriceS, bePriceF, params, exp, step) 60 | plot.plotDelta(bePriceS, bePriceF, params, exp2, step) 61 | plot.plotDelta(bePriceS, bePriceF, params, exp3, step) 62 | plt.xlabel("Price") 63 | plt.title("Option") 64 | plt.grid(True) 65 | plt.legend() 66 | plt.ylabel("Delta") 67 | plt.show() 68 | 69 | plot.plotTheta(bePriceS, bePriceF, params, exp, step) 70 | plot.plotTheta(bePriceS, bePriceF, params, exp2, step) 71 | plot.plotTheta(bePriceS, bePriceF, params, exp3, step) 72 | plt.xlabel("Price") 73 | plt.title("Option") 74 | plt.grid(True) 75 | plt.legend() 76 | plt.ylabel("Theta") 77 | plt.show() 78 | 79 | plot.plotVega(bePriceS, bePriceF, params, exp, step) 80 | plot.plotVega(bePriceS, bePriceF, params, exp2, step) 81 | plot.plotVega(bePriceS, bePriceF, params, exp3, step) 82 | plt.xlabel("Price") 83 | plt.title("Option") 84 | plt.grid(True) 85 | plt.legend() 86 | plt.ylabel("Vega") 87 | plt.show() 88 | 89 | plot.plotGamma(bePriceS, bePriceF, params, exp, step) 90 | plot.plotGamma(bePriceS, bePriceF, params, exp2, step) 91 | plot.plotGamma(bePriceS, bePriceF, params, exp3, step) 92 | plt.xlabel("Price") 93 | plt.title("Option") 94 | plt.grid(True) 95 | plt.legend() 96 | plt.ylabel("Gamma") 97 | plt.show() 98 | 99 | 100 | print('D:\t', round(plot.deltaFull(fPrice, params, exp), 2)) 101 | 102 | print('V:\t', round(plot.vegaFull(fPrice, params, exp), 2)) 103 | 104 | print('T:\t', round(plot.thetaFull(fPrice, params, exp), 2)) 105 | 106 | print('G:\t', round(plot.gammaFull(fPrice, params, exp), 2)) 107 | 108 | print('P/L:\t', plot.p_l(fPrice, params, exp)) -------------------------------------------------------------------------------- /plot.py: -------------------------------------------------------------------------------- 1 | from pricing import Pricing 2 | import matplotlib.pyplot as plt 3 | 4 | god = 365 5 | 6 | class Plot(Pricing): 7 | def plotPL(self, bePriceS, bePriceF, params, exp, step): 8 | bePrice = [] 9 | i = bePriceS 10 | while i <= bePriceF: 11 | bePrice.append(i) 12 | i+=step 13 | P_L = [] 14 | for s in bePrice: 15 | P_L.append(self.p_l(s, params, exp)) 16 | plt.plot(bePrice, P_L, label=int(exp*god)) 17 | 18 | def plotDelta(self, bePriceS, bePriceF, params, exp, step): 19 | bePrice = [] 20 | i = bePriceS 21 | while i <= bePriceF: 22 | bePrice.append(i) 23 | i+=step 24 | D = [] 25 | for s in bePrice: 26 | D.append(self.deltaFull(s, params, exp)) 27 | plt.plot(bePrice, D, label=int(exp*god)) 28 | 29 | def plotTheta(self, bePriceS, bePriceF, params, exp, step): 30 | bePrice = [] 31 | i = bePriceS 32 | while i <= bePriceF: 33 | bePrice.append(i) 34 | i+=step 35 | T = [] 36 | for s in bePrice: 37 | T.append(self.thetaFull(s, params, exp)) 38 | plt.plot(bePrice, T, label=int(exp*god)) 39 | 40 | def plotVega(self, bePriceS, bePriceF, params, exp, step): 41 | bePrice = [] 42 | i = bePriceS 43 | while i <= bePriceF: 44 | bePrice.append(i) 45 | i+=step 46 | V = [] 47 | for s in bePrice: 48 | V.append(self.vegaFull(s, params, exp)) 49 | plt.plot(bePrice, V, label=int(exp*god)) 50 | 51 | def plotGamma(self, bePriceS, bePriceF, params, exp, step): 52 | bePrice = [] 53 | i = bePriceS 54 | while i <= bePriceF: 55 | bePrice.append(i) 56 | i+=step 57 | V = [] 58 | for s in bePrice: 59 | V.append(self.gammaFull(s, params, exp)) 60 | plt.plot(bePrice, V, label=int(exp*god)) -------------------------------------------------------------------------------- /pricing.py: -------------------------------------------------------------------------------- 1 | from bsm import BSM 2 | 3 | class Pricing(BSM): 4 | 5 | def deltaFull(self, fPrice, params, exp): 6 | i = 0 7 | n = len(params) 8 | deltaFull = 0 9 | while i < n: 10 | param = params[i] 11 | if exp: 12 | param['exp'] = exp 13 | deltaSingle = self.delta(fPrice, param['strike'], param['vola'], param['exp'], param['dType']) * param['quant'] 14 | deltaFull = deltaFull + deltaSingle 15 | i+=1 16 | return deltaFull 17 | 18 | def vegaFull(self, fPrice, params, exp): 19 | i = 0 20 | n = len(params) 21 | vegaFull = 0 22 | while i < n: 23 | param = params[i] 24 | if exp: 25 | param['exp'] = exp 26 | if param['dType'] != 'F': 27 | vegaSingle = self.vega(fPrice, param['strike'], param['vola'], param['exp']) * param['quant'] 28 | vegaFull = vegaFull + vegaSingle 29 | i+=1 30 | return vegaFull 31 | 32 | def thetaFull(self, fPrice, params, exp): 33 | i = 0 34 | n = len(params) 35 | thetaFull = 0 36 | while i < n: 37 | param = params[i] 38 | if exp: 39 | param['exp'] = exp 40 | if param['dType'] != 'F': 41 | thetaSingle = self.theta(fPrice, param['strike'], param['vola'], param['exp']) * param['quant'] 42 | thetaFull = thetaFull + thetaSingle 43 | i+=1 44 | return thetaFull 45 | 46 | def gammaFull(self, fPrice, params, exp): 47 | i = 0 48 | n = len(params) 49 | gammaFull = 0 50 | while i < n: 51 | param = params[i] 52 | if exp: 53 | param['exp'] = exp 54 | if param['dType'] != 'F': 55 | gammaSingle = self.gamma(fPrice, param['strike'], param['vola'], param['exp']) * param['quant'] 56 | gammaFull = gammaFull + gammaSingle 57 | i+=1 58 | return gammaFull 59 | 60 | def p_l(self, fPrice, params, exp): 61 | i = 0 62 | n = len(params) 63 | plF = 0 64 | while i < n: 65 | param = params[i] 66 | if exp: 67 | param['exp'] = exp 68 | if param['dType'] == 'F': 69 | plS = (fPrice - param['price']) * param['quant'] 70 | else: 71 | theo = round(self.theo(fPrice, param['strike'], param['vola'], param['exp'], param['dType']), 3) 72 | plS = (theo - param['price']) * param['quant'] 73 | plF = plF + plS 74 | i+=1 75 | return plF -------------------------------------------------------------------------------- /searching.py: -------------------------------------------------------------------------------- 1 | from pricing import Pricing 2 | 3 | #in deep development 4 | 5 | class Search(Pricing): 6 | def searchDelta(self, fPrice, deep, acc, paramD, params): 7 | """if paramD['dType'] == 'C': 8 | if paramD['quant'] < 0: 9 | direct = 'U' 10 | else: 11 | direct = 'D' 12 | else: 13 | if paramD['quant'] < 0: 14 | direct = 'D' 15 | else: 16 | direct = 'U' 17 | if direct == 'U': 18 | start = fPrice 19 | finish = fPrice + deep 20 | else: 21 | start = fPrice - deep 22 | finish = fPrice""" 23 | matrix = [] 24 | start = fPrice - deep 25 | finish = fPrice + deep 26 | while start <= finish: 27 | deltaD = round((self.delta(start, paramD['strike'], paramD['vola'], paramD['exp'], paramD['dType'])) * paramD['quant'], 4) 28 | deltaF = round(self.deltaFull(start, params, 0), 3) 29 | #print(start, '|', deltaD, '|', deltaF) 30 | if deltaD == -deltaF: 31 | if paramD['dType'] != 'F': 32 | theo = round(self.theo(start, paramD['strike'], paramD['vola'], paramD['exp'], paramD['dType']), 3) 33 | else: 34 | theo = 0 35 | matrix.append([round(start, 2), paramD['dType'], paramD['strike'], paramD['quant'], deltaD, deltaF , theo]) 36 | #break 37 | start = start + acc 38 | i = 0 39 | n = len(matrix) 40 | prices = [] 41 | while i < n: 42 | if prices: 43 | if matrix[i-1][4] != matrix[i][4]: 44 | prices.append(matrix[i]) 45 | else: 46 | prices.append(matrix[i]) 47 | i = i + 1 48 | for j in prices: 49 | print(j[0], '|', j[1], '|', j[2], '|', j[3], '|', j[4], '|', j[5], '|', j[6]) 50 | 51 | search = Search() 52 | 53 | god = 365 54 | step = 10 55 | deep = 7500 56 | 57 | exp = 30 / god 58 | exp2 = 15 / god 59 | exp3 = 5 / god 60 | 61 | fPrice = 21000 62 | 63 | params = [] 64 | 65 | dType = 'F' 66 | quant = 1 67 | price = 20000 68 | strike = 0 69 | vola = 0 70 | params.append({'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp}) 71 | 72 | dType = 'C' 73 | quant = 1 74 | price = 500 75 | strike = 25000 76 | vola = 0.75 77 | params.append({'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp}) 78 | 79 | dType = 'P' 80 | quant = 1 81 | price = 100 82 | strike = 15000 83 | vola = 0.75 84 | params.append({'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp}) 85 | 86 | dType = 'P' 87 | quant = -1 88 | price = 25 89 | strike = 10000 90 | vola = 1.5 91 | params.append({'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp}) 92 | 93 | bePriceS = fPrice - deep 94 | bePriceF = fPrice + deep 95 | 96 | acc = 0.1 97 | 98 | dType = 'F' 99 | strike = 0 100 | price = 0 101 | quant = -1 102 | vola = 0 103 | paramD_1 = {'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp} 104 | 105 | dType = 'F' 106 | strike = 0 107 | price = 0 108 | quant = 1 109 | vola = 0 110 | paramD1 = {'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp} 111 | 112 | dType = 'F' 113 | strike = 0 114 | price = 0 115 | quant = -2 116 | vola = 0 117 | paramD_2 = {'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp} 118 | 119 | dType = 'F' 120 | strike = 0 121 | price = 0 122 | quant = 2 123 | vola = 0 124 | paramD2 = {'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp} 125 | 126 | dType = 'F' 127 | strike = 0 128 | price = 0 129 | quant = -3 130 | vola = 0 131 | paramD_3 = {'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp} 132 | 133 | dType = 'F' 134 | strike = 0 135 | price = 0 136 | quant = 3 137 | vola = 0 138 | paramD3 = {'dType': dType, 'price': price, 'quant': quant, 'strike': strike, 'vola': vola, 'exp': exp} 139 | 140 | search.searchDelta(fPrice, deep, acc, paramD1, params) 141 | search.searchDelta(fPrice, deep, acc, paramD_1, params) 142 | search.searchDelta(fPrice, deep, acc, paramD2, params) 143 | search.searchDelta(fPrice, deep, acc, paramD_2, params) 144 | search.searchDelta(fPrice, deep, acc, paramD3, params) 145 | search.searchDelta(fPrice, deep, acc, paramD_3, params) --------------------------------------------------------------------------------