├── 6g01e0o8qp041.png ├── GUI ├── Option3d.cp37-win32.pyd ├── Option3d.cp36-win_amd64.pyd ├── __pycache__ │ ├── MAIN.cpython-37.pyc │ └── Test.cpython-37.pyc └── MAIN.py ├── Cython ├── Option3d.cp37-win32.pyd ├── __pycache__ │ ├── Base.cpython-37.pyc │ └── Python_text.cpython-37.pyc ├── requirements.txt ├── compile.py ├── Example.py └── Option3d.pyx ├── README.md └── cy_wrapper.pyx /6g01e0o8qp041.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nathan-T1/Option3d_Public/HEAD/6g01e0o8qp041.png -------------------------------------------------------------------------------- /GUI/Option3d.cp37-win32.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nathan-T1/Option3d_Public/HEAD/GUI/Option3d.cp37-win32.pyd -------------------------------------------------------------------------------- /Cython/Option3d.cp37-win32.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nathan-T1/Option3d_Public/HEAD/Cython/Option3d.cp37-win32.pyd -------------------------------------------------------------------------------- /GUI/Option3d.cp36-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nathan-T1/Option3d_Public/HEAD/GUI/Option3d.cp36-win_amd64.pyd -------------------------------------------------------------------------------- /GUI/__pycache__/MAIN.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nathan-T1/Option3d_Public/HEAD/GUI/__pycache__/MAIN.cpython-37.pyc -------------------------------------------------------------------------------- /GUI/__pycache__/Test.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nathan-T1/Option3d_Public/HEAD/GUI/__pycache__/Test.cpython-37.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Option3d_Public 2 | ![alt text](https://github.com/Nathan-T1/Option3d_Public/blob/master/6g01e0o8qp041.png?raw=true) 3 | -------------------------------------------------------------------------------- /Cython/__pycache__/Base.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nathan-T1/Option3d_Public/HEAD/Cython/__pycache__/Base.cpython-37.pyc -------------------------------------------------------------------------------- /Cython/__pycache__/Python_text.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nathan-T1/Option3d_Public/HEAD/Cython/__pycache__/Python_text.cpython-37.pyc -------------------------------------------------------------------------------- /Cython/requirements.txt: -------------------------------------------------------------------------------- 1 | as==0.1 2 | numpy==1.17.3 3 | pandas==0.25.2 4 | pd==0.0.1 5 | python-dateutil==2.8.0 6 | pytz==2019.3 7 | six==1.12.0 8 | -------------------------------------------------------------------------------- /Cython/compile.py: -------------------------------------------------------------------------------- 1 | 2 | from distutils.core import setup, Extension 3 | from Cython.Build import cythonize 4 | import numpy 5 | 6 | 7 | setup( 8 | ext_modules=cythonize("Option3d.pyx"), 9 | include_dirs=[numpy.get_include()] 10 | ) 11 | -------------------------------------------------------------------------------- /Cython/Example.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from Option3d import * 3 | import time 4 | import matplotlib.pyplot as plt 5 | from mpl_toolkits.mplot3d import Axes3D 6 | from matplotlib import colors 7 | from matplotlib import cm 8 | 9 | 10 | ''' 11 | Each row in array represents a seperate leg 12 | Each row must have 9 elements each 13 | 14 | Element 1: Long or short (1 for long, -1 for short) 15 | Element 2: Call or put (0 for call, 1 for put) 16 | Element 3: Spot price (Enter the same value for all) 17 | Element 4: Stirke 18 | Element 5: DTE (Enter integer) 19 | Element 6: IV (Enter as decimal, 20% ~ .20) 20 | Element 7: Spot Width (.20 means calculate for spot value +-20% from the current underlying price) 21 | Element 8: Spot Step (.005 means within the range, calculate at .5% intervals) 22 | Element 9: Time Step (1 would mean calculate once a day, .25 means calculate every 1/4 of a day ~ 4 times a day) 23 | 24 | Below would represent a Iron Condor 25 | ''' 26 | 27 | array = np.array( 28 | [[-1, 0, 100, 101, 40, .20, .10, .001, .1], 29 | [1, 0, 100, 102, 40, .20, .10, .001, .1], 30 | [-1, 1, 100, 99, 40, .20, .10, .001, .1], 31 | [1, 1, 100, 98, 40, .20, .10, .001, .1]], 32 | np.float64 33 | ) 34 | 35 | ''' 36 | Function is called main_array 37 | Takes two parameters 38 | 39 | 1: A two demensional array. If you only want to use one leg make sure you 40 | use a 2d array ~ [[a,b,c]] 41 | 42 | 2: Z. Represents what you want to calculate and display on the z axis of the surface plot. 43 | Must be a string like: Delta, Gamma, Theta, Vega, P/L, Exercise Probability 44 | ''' 45 | 46 | start = time.time() 47 | df = main_array(array, "P/L") 48 | 49 | X,Y = np.meshgrid(df.columns.astype(float), df.index) 50 | Z = df.values 51 | 52 | fig = plt.figure() 53 | ax = fig.add_subplot(1,3,1, projection='3d') 54 | surf = ax.plot_surface(X, Y, Z,cmap=cm.coolwarm,label = "Graph", antialiased=True) 55 | ax.set_zlabel("P/L") 56 | ax.set_ylabel('Spot') 57 | ax.set_xlabel('DTE') 58 | 59 | df_delta = main_array(array, "Delta") 60 | 61 | X,Y = np.meshgrid(df_delta.columns.astype(float), df_delta.index) 62 | Z = df_delta.values 63 | 64 | ax1 = fig.add_subplot(1,3,2, projection='3d') 65 | surf = ax1.plot_surface(X, Y, Z,cmap=cm.coolwarm,label = "Graph", antialiased=True) 66 | ax1.set_zlabel("Delta") 67 | ax1.set_ylabel('Spot') 68 | ax1.set_xlabel('DTE') 69 | plt.title("Iron Condor: 40 DTE, 100 Spot. 101/102 Call , 99/98 Put") 70 | 71 | df_gamma = main_array(array, "Theta") 72 | 73 | X,Y = np.meshgrid(df_gamma.columns.astype(float), df_gamma.index) 74 | Z = df_gamma.values 75 | 76 | ax2 = fig.add_subplot(1,3,3, projection='3d') 77 | surf = ax2.plot_surface(X, Y, Z,cmap=cm.coolwarm,label = "Graph", antialiased=True) 78 | ax2.set_zlabel("Theta") 79 | ax2.set_ylabel('Spot') 80 | ax2.set_xlabel('DTE') 81 | 82 | plt.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) 83 | end = time.time() 84 | 85 | print(end-start) 86 | plt.show() 87 | 88 | 89 | -------------------------------------------------------------------------------- /cy_wrapper.pyx: -------------------------------------------------------------------------------- 1 | cimport cython 2 | cimport numpy as np 3 | import numpy as np 4 | from libc.math cimport log, exp, round, abs 5 | 6 | cdef extern from "math.h" nogil: 7 | double exp(double) 8 | double sqrt(double) 9 | double pow(double, double) 10 | double log(double) 11 | double erf(double) 12 | 13 | 14 | @cython.cdivision(True) 15 | cdef double std_norm_cdf(double x): 16 | return .5*(1+erf(x/sqrt(2.0))) 17 | 18 | 19 | @cython.boundscheck(False) # Deactivate bounds checking 20 | @cython.wraparound(False) # Deactivate negative indexing. 21 | @cython.cdivision(True) 22 | cdef double cprice_func(float s_var, float k0, float t_var, float sig, double cp): 23 | 24 | cdef double d1_var, d2_var, n_d1, n_d2, val 25 | cdef double r = .02 26 | 27 | d1_var = (log(s_var/k0) + (r + (sig**2)/2)*t_var) / (sig * (t_var**.5)) 28 | d2_var = d1_var - sig * t_var**.5 29 | 30 | val = (cp * s_var * std_norm_cdf(cp*d1_var) - cp*k0*exp(-r*t_var) * std_norm_cdf(cp*d2_var)) 31 | 32 | return val 33 | 34 | 35 | @cython.boundscheck(False) # Deactivate bounds checking 36 | @cython.wraparound(False) # Deactivate negative indexing. 37 | @cython.cdivision(True) 38 | cdef double gamma_func(float s_var, float k0, float t_var, float sig, float r): 39 | 40 | cdef double pi = 3.1415926535897 41 | 42 | cdef double d1_var, n_pdf, c_gamma 43 | 44 | d1_var = (log(s_var/k0) + (r + (sig**2)/2)*t_var) / (sig * (t_var**.5)) 45 | n_pdf = exp(-d1_var**2/2)/(2*pi)**.5 46 | 47 | gamma = n_pdf/(s_var*(sig * t_var**.5)) 48 | 49 | return gamma 50 | 51 | @cython.boundscheck(False) # Deactivate bounds checking 52 | @cython.wraparound(False) # Deactivate negative indexing. 53 | @cython.cdivision(True) 54 | cdef double vega_func(float s_var, float k0, float t_var, float sig): 55 | 56 | cdef double d1_var, vega 57 | cdef double pi = 3.1415926535897 58 | cdef double r = .02 59 | 60 | d1_var = (log(s_var/k0) + (r + .5 * sig**2)*t_var) / (sig * (t_var**.5)) 61 | n_pdf = exp(-d1_var**2/2)/(2*pi)**.5 62 | vega = s_var * n_pdf * (t_var**.5) / 100 63 | 64 | return vega 65 | 66 | DTYPE = np.float64 67 | ctypedef np.float64_t DTYPE_t 68 | 69 | @cython.boundscheck(False) # Deactivate bounds checking 70 | @cython.wraparound(False) # Deactivate negative indexing. 71 | @cython.cdivision(True) 72 | cpdef cy_wrapper_iv(np.ndarray[dtype = DTYPE_t, ndim=2] main, int cp): 73 | 74 | cdef double [:,:] main_view = main 75 | cdef int I = main_view.shape[0] 76 | cdef double [:] return_view = np.zeros((I), np.float64) 77 | 78 | 79 | cdef double e = .01 80 | cdef double x0 81 | cdef double max_iter 82 | cdef int i 83 | cdef double delta, s_var, k0, t_var, market 84 | cdef int max_range 85 | 86 | 87 | for i in range(0,I): 88 | 89 | x0 = .1 90 | max_iter = 0 91 | 92 | if k0 - s_var > market: 93 | 94 | max_range = 100 95 | 96 | else: 97 | 98 | max_range = 1000 99 | 100 | s_var = main_view[i,0] 101 | k0 = main_view[i,1] 102 | t_var = main_view[i,2] 103 | market = main_view[i,3] 104 | 105 | delta = cprice_func(s_var, k0, t_var, x0, cp) - market 106 | 107 | while abs(delta) > e and max_iter <= max_range: 108 | 109 | vega = vega_func(s_var,k0,t_var,x0) 110 | 111 | if delta > 0: 112 | if delta > vega: 113 | x0 -= .01 114 | else: 115 | x0 -= .0001 116 | 117 | delta = cprice_func(s_var, k0, t_var, x0, cp) - market 118 | 119 | else: 120 | if -1 *delta > vega: 121 | x0 += .01 122 | else: 123 | x0 += .0001 124 | 125 | delta = cprice_func(s_var, k0, t_var, x0, cp) - market 126 | 127 | max_iter += 1 128 | 129 | return_view[i] = x0 130 | 131 | return return_view 132 | 133 | @cython.boundscheck(False) # Deactivate bounds checking 134 | @cython.wraparound(False) # Deactivate negative indexing. 135 | @cython.cdivision(True) 136 | def cy_wrapper_gamma(np.ndarray[dtype = DTYPE_t, ndim=2] main): 137 | 138 | cdef double [:,:] main_view = main 139 | cdef int I = main_view.shape[0] 140 | cdef double [:] return_view = np.zeros((I), np.float64) 141 | 142 | for i in range(0,I): 143 | 144 | s_var = main_view[i,0] 145 | k0 = main_view[i,1] 146 | t_var = main_view[i,2] 147 | iv = main_view[i,4] 148 | 149 | g = gamma_func(s_var, k0, t_var, iv, .02) 150 | return_view[i] = g 151 | 152 | return return_view 153 | -------------------------------------------------------------------------------- /Cython/Option3d.pyx: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | cimport numpy as np 3 | cimport cython 4 | from libc.math cimport log, exp, round 5 | from math import * 6 | from libc.stdlib cimport malloc, free 7 | import pandas as pd 8 | 9 | @cython.boundscheck(False) # Deactivate bounds checking 10 | @cython.wraparound(False) # Deactivate negative indexing. 11 | @cython.cdivision(True) 12 | cdef double cprice_func(float s_var, float k0, float t_var, float sig, float r): 13 | 14 | cdef double d1_var, d2_var, n_d1, n_d2, c_val 15 | 16 | d1_var = (log(s_var/k0) + (r + (sig**2)/2)*t_var) / (sig * (t_var**.5)) 17 | d2_var = d1_var - sig * t_var**.5 18 | 19 | n_d1 = (1.0 + erf(d1_var / (2.0)**.5)) / 2.0 20 | n_d2 = (1.0 + erf(d2_var / (2.0)**.5)) / 2.0 21 | c_val = (s_var * n_d1 - k0 * exp(-r*t_var) * n_d2) 22 | 23 | return c_val 24 | 25 | @cython.boundscheck(False) # Deactivate bounds checking 26 | @cython.wraparound(False) # Deactivate negative indexing. 27 | @cython.cdivision(True) 28 | cdef double pprice_func(float s_var, float k0, float t_var, float sig, float r): 29 | 30 | cdef double d1_var, d2_var, n_d1, n_d2, p_val 31 | 32 | d1_var = (log(s_var/k0) + (r + (sig**2)/2)*t_var) / (sig * (t_var**.5)) 33 | d2_var = d1_var - sig * t_var**.5 34 | 35 | n_d1 = (1.0 + erf(-d1_var / (2.0)**.5)) / 2.0 36 | n_d2 = (1.0 + erf(-d2_var / (2.0)**.5)) / 2.0 37 | p_val = ((k0 * exp(-r*t_var)) * n_d2) - (s_var * n_d1) 38 | 39 | return p_val 40 | 41 | @cython.boundscheck(False) # Deactivate bounds checking 42 | @cython.wraparound(False) # Deactivate negative indexing. 43 | @cython.cdivision(True) 44 | cdef double cdelta_func(float s_var, float k0, float t_var, float sig, float r): 45 | 46 | cdef double d1_var, c_delta 47 | 48 | d1_var = (log(s_var/k0) + (r + .5 * sig**2)*t_var) / (sig * (t_var**.5)) 49 | c_delta = ((1.0 + erf(d1_var / (2.0)**.5)) / 2.0) 50 | 51 | return c_delta 52 | 53 | @cython.boundscheck(False) # Deactivate bounds checking 54 | @cython.wraparound(False) # Deactivate negative indexing. 55 | @cython.cdivision(True) 56 | cdef double pdelta_func(float s_var, float k0, float t_var, float sig, float r): 57 | 58 | cdef double d1_var, p_delta 59 | 60 | d1_var = (log(s_var/k0) + (r + .5 * sig**2)*t_var) / (sig * (t_var**.5)) 61 | p_delta = -1*((1.0 + erf(-1*d1_var / (2.0)**.5)) / 2.0) 62 | 63 | return p_delta 64 | 65 | @cython.boundscheck(False) # Deactivate bounds checking 66 | @cython.wraparound(False) # Deactivate negative indexing. 67 | @cython.cdivision(True) 68 | cdef double cvega_func(float s_var, float k0, float t_var, float sig, float r): 69 | 70 | cdef double d1_var, c_vega 71 | 72 | d1_var = (log(s_var/k0) + (r + .5 * sig**2)*t_var) / (sig * (t_var**.5)) 73 | n_pdf = exp(-d1_var**2/2)/(2*pi)**.5 74 | c_vega = s_var * n_pdf * (t_var**.5) / 100 75 | 76 | return c_vega 77 | 78 | @cython.boundscheck(False) # Deactivate bounds checking 79 | @cython.wraparound(False) # Deactivate negative indexing. 80 | @cython.cdivision(True) 81 | cdef double ctheta_func(float s_var, float k0, float t_var, float sig, float r): 82 | 83 | cdef double d1_var, d2_var, b, n_d2, n_pdf, c_theta 84 | 85 | d1_var = (log(s_var/k0) + (r + (sig**2)/2)*t_var) / (sig * (t_var**.5)) 86 | d2_var = d1_var - sig * t_var**.5 87 | b = exp(-r*t_var) 88 | 89 | n_pdf = exp(-d1_var**2/2)/(2*pi)**.5 90 | n_d2 = (1.0 + erf(d2_var / (2.0)**.5)) / 2.0 91 | 92 | c_theta = ((-1 * s_var * n_pdf * sig) / (2*t_var**.5)) - r*(k0*b*n_d2) 93 | 94 | return c_theta/365 95 | 96 | @cython.boundscheck(False) # Deactivate bounds checking 97 | @cython.wraparound(False) # Deactivate negative indexing. 98 | @cython.cdivision(True) 99 | cdef double ptheta_func(float s_var, float k0, float t_var, float sig, float r): 100 | 101 | cdef double d1_var, d2_var, b, n_d2, n_pdf, p_theta 102 | 103 | d1_var = (log(s_var/k0) + (r + (sig**2)/2)*t_var) / (sig * (t_var**.5)) 104 | d2_var = d1_var - sig * t_var**.5 105 | b = exp(-r*t_var) 106 | 107 | n_pdf = exp(-d1_var**2/2)/(2*pi)**.5 108 | n_d2 = (1.0 + erf(-1*d2_var / (2.0)**.5)) / 2.0 109 | 110 | p_theta = ((-1 * s_var * n_pdf * sig) / (2*t_var**.5)) + r*(k0*b*n_d2) 111 | 112 | return p_theta/365 113 | 114 | @cython.boundscheck(False) # Deactivate bounds checking 115 | @cython.wraparound(False) # Deactivate negative indexing. 116 | @cython.cdivision(True) 117 | cpdef double cgamma_func(float s_var, float k0, float t_var, float sig, float r): 118 | 119 | cdef double d1_var, n_pdf, c_gamma 120 | 121 | d1_var = (log(s_var/k0) + (r + (sig**2)/2)*t_var) / (sig * (t_var**.5)) 122 | n_pdf = exp(-d1_var**2/2)/(2*pi)**.5 123 | 124 | c_gamma = n_pdf/(s_var*(sig * t_var**.5)) 125 | 126 | return c_gamma 127 | 128 | cdef double cAR_func(float s_var, float k0, float t_var, float sig, float r): 129 | 130 | cdef double d1_var, d2_var, ar 131 | 132 | d1_var = (log(s_var/k0) + (r + (sig**2)/2)*t_var) / (sig * (t_var**.5)) 133 | d2_var = d1_var - sig * t_var**.5 134 | 135 | ar = (1.0 + erf(d2_var / (2.0)**.5)) / 2.0 136 | 137 | return ar 138 | 139 | 140 | 141 | @cython.boundscheck(False) # Deactivate bounds checking 142 | @cython.wraparound(False) # Deactivate negative indexing. 143 | @cython.cdivision(True) 144 | cpdef cbsm(float scalar, float s0, float k0, float t, float sig, str param, s_view, t_view, c_type): 145 | 146 | cdef double [:,:] main_t = np.empty([s_view.shape[0], t_view.shape[0]]) 147 | 148 | cdef float r = .02 149 | cdef double s_var, t_var 150 | cdef double c, c0 151 | 152 | cdef int i, j, I, J 153 | I = s_view.shape[0] 154 | J = t_view.shape[0] 155 | 156 | 157 | if param == 'Delta': 158 | if c_type == 0: 159 | for i in range(I): 160 | for j in range(J): 161 | 162 | s_var = s_view[i] 163 | t_var = (t_view[j]/365) 164 | 165 | c = cdelta_func(s_var, k0, t_var, sig, r) * scalar 166 | 167 | main_t[i,j] = c 168 | 169 | return main_t, t_view, s_view 170 | 171 | else: 172 | for i in range(I): 173 | for j in range(J): 174 | 175 | s_var = s_view[i] 176 | t_var = (t_view[j]/365) 177 | 178 | c = pdelta_func(s_var, k0, t_var, sig, r) * scalar 179 | 180 | main_t[i,j] = c 181 | 182 | return main_t, t_view, s_view 183 | 184 | 185 | if param == 'Gamma': 186 | for i in range(I): 187 | for j in range(J): 188 | 189 | s_var = s_view[i] 190 | t_var = (t_view[j]/365) 191 | 192 | c = cgamma_func(s_var, k0, t_var, sig, r) * scalar 193 | 194 | main_t[i,j] = c 195 | 196 | return main_t, t_view, s_view 197 | 198 | if param == 'Theta': 199 | if c_type == 0: 200 | for i in range(I): 201 | for j in range(J): 202 | 203 | s_var = s_view[i] 204 | t_var = (t_view[j]/365) 205 | 206 | 207 | c = ctheta_func(s_var, k0, t_var, sig, r) * scalar 208 | 209 | main_t[i,j] = c 210 | 211 | return main_t, t_view, s_view 212 | 213 | else: 214 | for i in range(I): 215 | for j in range(J): 216 | 217 | s_var = s_view[i] 218 | t_var = (t_view[j]/365) 219 | 220 | 221 | c = ptheta_func(s_var, k0, t_var, sig, r) * scalar 222 | 223 | main_t[i,j] = c 224 | 225 | return main_t, t_view, s_view 226 | 227 | 228 | if param == 'Vega': 229 | for i in range(I): 230 | for j in range(J): 231 | 232 | s_var = s_view[i] 233 | t_var = (t_view[j]/365) 234 | 235 | 236 | c = cvega_func(s_var, k0, t_var, sig, r) * scalar 237 | 238 | main_t[i,j] = c 239 | 240 | return main_t, t_view, s_view 241 | 242 | if param == 'Exercise Probability': 243 | for i in range(I): 244 | for j in range(J): 245 | 246 | s_var = s_view[i] 247 | t_var = (t_view[j]/365) 248 | 249 | 250 | c = cAR_func(s_var, k0, t_var, sig, r) 251 | 252 | main_t[i,j] = c 253 | 254 | return main_t, t_view, s_view 255 | 256 | else: 257 | 258 | if c_type == 0: 259 | 260 | c0 = cprice_func(s0, k0, t/365, sig, r) 261 | 262 | for i in range(I): 263 | for j in range(J): 264 | 265 | s_var = s_view[i] 266 | t_var = (t_view[j]/365) 267 | 268 | 269 | c = (cprice_func(s_var, k0, t_var, sig, r) - c0) * scalar 270 | 271 | main_t[i,j] = c 272 | 273 | return main_t, t_view, s_view 274 | 275 | else: 276 | 277 | c0 = pprice_func(s0, k0, t/365, sig, r) 278 | 279 | for i in range(I): 280 | for j in range(J): 281 | 282 | s_var = s_view[i] 283 | t_var = (t_view[j]/365) 284 | 285 | 286 | c = (pprice_func(s_var, k0, t_var, sig, r) - c0) * scalar 287 | 288 | main_t[i,j] = c 289 | 290 | return main_t, t_view, s_view 291 | 292 | 293 | DTYPE = np.float64 294 | ctypedef np.float64_t DTYPE_t 295 | 296 | cpdef main_array(np.ndarray[dtype = DTYPE_t, ndim=2] main, str Z): 297 | 298 | cdef double [:,:] main_view = main 299 | cdef int I = main_view.shape[0] 300 | cdef int i, j, k 301 | 302 | cdef double s_w = main_view[0,6] 303 | cdef double s_s = main_view[0,7] 304 | cdef double t_s = main_view[0,8] 305 | cdef double s0 = main_view[0,2] 306 | cdef double t0 = main_view[0,4] 307 | cdef float s_max = s0+s_w*s0 308 | cdef float s_min = s0-s_w*s0 309 | 310 | cdef double [:] t_view = np.arange(1,t0, t_s) 311 | cdef double [:] s_view = np.arange(s_min,s_max, s_s*s0) 312 | cdef double [:,:] return_matrix = np.zeros((s_view.shape[0], t_view.shape[0]), np.float64) 313 | cdef double [:,:,:] matrix = np.zeros((s_view.shape[0],t_view.shape[0],I), np.float64) 314 | 315 | cdef double scalar, c_type, s_var, k0, t_var, sig 316 | cdef int x = s_view.shape[0] 317 | cdef int y = t_view.shape[0] 318 | for k in range(I): 319 | 320 | scalar = main_view[k,0] 321 | c_type = main_view[k,1] 322 | s_var = main_view[k,2] 323 | k0 = main_view[k,3] 324 | t_var = main_view[k,4] 325 | sig = main_view[k,5] 326 | 327 | array, array_x, array_y = cbsm(scalar, s_var, k0, t_var, sig, Z, s_view, t_view, c_type) 328 | 329 | for i in range(x): 330 | for j in range(y): 331 | matrix[i,j,k] = array[i,j] 332 | 333 | for k in range(I): 334 | for i in range(x): 335 | for j in range(y): 336 | return_matrix[i,j] += matrix[i,j,k] 337 | 338 | return_array = np.asanyarray(return_matrix) 339 | return_index = np.asanyarray(s_view) 340 | return_columns = np.asanyarray(t_view) 341 | 342 | df = pd.DataFrame(return_array) 343 | df.columns = return_columns 344 | df = df.set_index(return_index) 345 | 346 | 347 | return df 348 | -------------------------------------------------------------------------------- /GUI/MAIN.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtWidgets 2 | from PyQt5.QtCore import Qt 3 | from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QSizePolicy, QSlider, QSpacerItem, \ 4 | QVBoxLayout, QWidget, QGridLayout, QMainWindow, QLineEdit, QPushButton, QComboBox 5 | 6 | 7 | import os 8 | import numpy as np 9 | from matplotlib.figure import Figure # For Matplotlib Figure Object 10 | import matplotlib.pyplot as plt 11 | from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas 12 | from mpl_toolkits.mplot3d import Axes3D 13 | from Option3d import * 14 | import sys 15 | from matplotlib import cm 16 | import pandas as pd 17 | 18 | 19 | class ThreeDSurface_GraphWindow(FigureCanvas): #Class for 3D window 20 | def __init__(self): 21 | self.fig =plt.figure(figsize=(7,7)) 22 | FigureCanvas.__init__(self, self.fig) #creating FigureCanvas 23 | self.axes = self.fig.gca(projection='3d')#generates 3D Axes object 24 | self.setWindowTitle("Main") # sets Window title 25 | 26 | def DrawGraph(self, x, y, z, z_label): 27 | self.axes.clear() 28 | self.axes.set_zlabel(z_label) 29 | self.axes.set_ylabel('Spot') 30 | self.axes.set_xlabel('DTE') 31 | #self.fig.suptitle(title) 32 | self.axes.plot_surface(x, y, z, cmap = cm.coolwarm) #plots the 3D surface plot 33 | self.draw() 34 | 35 | 36 | 37 | #### PyQt5 GUI #### 38 | class MainWindow_single(QWidget): 39 | def __init__(self, parent = None): 40 | 41 | super(MainWindow_single,self).__init__() 42 | 43 | ## GRID LAYOUT 44 | self.layout = QGridLayout() 45 | self.input_frame = QtWidgets.QFrame() 46 | self.input_layout = QGridLayout() 47 | 48 | 49 | self.ThreeDWin = ThreeDSurface_GraphWindow() 50 | self.layout.addWidget(self.ThreeDWin, 2, 0, 1, 4) 51 | 52 | self.layout_button = QPushButton('Toggle Inputs', self) 53 | self.layout.addWidget(self.layout_button, 3, 0) 54 | self.layout_button.clicked.connect(self.toggle_inputs) 55 | 56 | self.export_df_button = QPushButton('Export CSV', self) 57 | self.layout.addWidget(self.export_df_button, 3, 1) 58 | self.export_df_button.clicked.connect(self.export_df) 59 | 60 | self.export_df_button = QPushButton('Export PNG', self) 61 | self.layout.addWidget(self.export_df_button, 3, 2) 62 | self.export_df_button.clicked.connect(self.export_jpg) 63 | 64 | self.strike_input_label = QLabel(self) 65 | self.strike_input_label.setText("Strike: ") 66 | self.strike_input_label.setStyleSheet('color: black') 67 | self.input_layout.addWidget(self.strike_input_label, 0, 0) 68 | self.strike_input = QLineEdit(self) 69 | self.input_layout.addWidget(self.strike_input, 0, 1) 70 | 71 | self.spot_input_label = QLabel(self) 72 | self.spot_input_label.setText("Spot: ") 73 | self.spot_input_label.setStyleSheet('color: black') 74 | self.input_layout.addWidget(self.spot_input_label, 0, 2) 75 | self.spot_input = QLineEdit(self) 76 | self.input_layout.addWidget(self.spot_input, 0, 3) 77 | 78 | self.exp_input_label = QLabel(self) 79 | self.exp_input_label.setText("DTE: ") 80 | self.exp_input_label.setStyleSheet('color: black') 81 | self.input_layout.addWidget(self.exp_input_label, 0, 4) 82 | self.exp_input = QLineEdit(self) 83 | self.input_layout.addWidget(self.exp_input, 0, 5) 84 | 85 | self.iv_input_label = QLabel(self) 86 | self.iv_input_label.setText("IV: ") 87 | self.iv_input_label.setStyleSheet('color: black') 88 | self.input_layout.addWidget(self.iv_input_label, 0, 6) 89 | self.iv_input = QLineEdit(self) 90 | self.input_layout.addWidget(self.iv_input, 0, 7) 91 | 92 | self.r_input_label = QLabel(self) 93 | self.r_input_label.setText("R: ") 94 | self.r_input_label.setStyleSheet('color: black') 95 | self.input_layout.addWidget(self.r_input_label, 0, 8) 96 | self.r_input = QLineEdit(self) 97 | self.input_layout.addWidget(self.r_input, 0, 9) 98 | 99 | self.sw_input_label = QLabel(self) 100 | self.sw_input_label.setText("Spot Width: ") 101 | self.sw_input_label.setStyleSheet('color: black') 102 | self.input_layout.addWidget(self.sw_input_label, 1, 0) 103 | self.sw_input = QLineEdit(self) 104 | self.input_layout.addWidget(self.sw_input, 1, 1) 105 | 106 | 107 | self.ss_input_label = QLabel(self) 108 | self.ss_input_label.setText("Spot Step: ") 109 | self.ss_input_label.setStyleSheet('color: black') 110 | self.input_layout.addWidget(self.ss_input_label, 1, 2) 111 | self.ss_input = QLineEdit(self) 112 | self.input_layout.addWidget(self.ss_input, 1, 3) 113 | 114 | self.ts_input_label = QLabel(self) 115 | self.ts_input_label.setText("Time Step: ") 116 | self.ts_input_label.setStyleSheet('color: black') 117 | self.input_layout.addWidget(self.ts_input_label, 1, 4) 118 | self.ts_input = QLineEdit(self) 119 | self.input_layout.addWidget(self.ts_input, 1, 5) 120 | 121 | self.longshort_comboBox = QComboBox(self) 122 | self.longshort_comboBox.setObjectName(("comboBox")) 123 | self.longshort_comboBox.addItem("Long") 124 | self.longshort_comboBox.addItem("Short") 125 | self.input_layout.addWidget(self.longshort_comboBox, 1, 6) 126 | 127 | self.putcall_comboBox = QComboBox(self) 128 | self.putcall_comboBox.setObjectName(("comboBox")) 129 | self.putcall_comboBox.addItem("Put") 130 | self.putcall_comboBox.addItem("Call") 131 | self.input_layout.addWidget(self.putcall_comboBox, 1, 7) 132 | 133 | self.target_label = QLabel(self) 134 | self.target_label.setText("Target: ") 135 | self.target_label.setStyleSheet('color: black') 136 | self.input_layout.addWidget(self.target_label, 1, 8) 137 | self.comboBox = QComboBox(self) 138 | self.comboBox.setObjectName(("comboBox")) 139 | self.comboBox.addItem("P/L") 140 | self.comboBox.addItem("Delta") 141 | self.comboBox.addItem("Gamma") 142 | self.comboBox.addItem("Theta") 143 | self.comboBox.addItem("Vega") 144 | self.input_layout.addWidget(self.comboBox, 1, 9) 145 | 146 | self.button = QPushButton('Submit', self) 147 | self.input_layout.addWidget(self.button, 2, 1) 148 | self.button.clicked.connect(self.init_plot) 149 | 150 | self.input_frame.setLayout(self.input_layout) 151 | 152 | self.layout.addWidget(self.input_frame, 0, 0, 1, 4) 153 | 154 | self.setLayout(self.layout) 155 | 156 | def toggle_inputs(self, MainWindow_single): 157 | 158 | self.input_frame.setHidden(not self.input_frame.isHidden()) 159 | 160 | def export_jpg(self, MainWindow_single): 161 | 162 | 163 | self.ThreeDWin.fig.savefig('Option3d_fig.png', dpi = self.ThreeDWin.fig.dpi) 164 | 165 | 166 | def export_df(self, MainWindow_single_single): 167 | 168 | try: 169 | self.df.to_csv("Option3d_data.csv") 170 | except: 171 | print("No data to return") 172 | 173 | 174 | def init_plot(self, MainWindow_single): 175 | 176 | try: 177 | 178 | self.strike = float(self.strike_input.text()) 179 | self.spot = float(self.spot_input.text()) 180 | self.iv = float(self.iv_input.text()) 181 | self.r = float(self.r_input.text()) 182 | self.ss = float(self.ss_input.text()) 183 | self.sw = float(self.sw_input.text()) 184 | self.ts = float(self.ts_input.text()) 185 | self.exp = float(self.exp_input.text()) 186 | 187 | self.target = str(self.comboBox.currentText()) 188 | self.longshort = str(self.longshort_comboBox.currentText()) 189 | self.putcall = str(self.putcall_comboBox.currentText()) 190 | 191 | except: 192 | print("Failed to gather inputs") 193 | 194 | if self.longshort == 'Long': 195 | self.scaler = 1 196 | else: 197 | self.scaler = -1 198 | if self.putcall == 'Put': 199 | self.type = 1 200 | else: 201 | self.type = 0 202 | 203 | self.array = np.array( 204 | [[self.scaler, self.type, self.spot, self.strike, self.exp, self.iv, self.sw, self.ss, self.ts]], 205 | np.float64 206 | ) 207 | 208 | self.df = main_array(self.array, self.target) 209 | self.x = self.df.columns.values 210 | self.y = self.df.index.values 211 | Z = self.df.values 212 | 213 | X,Y=np.meshgrid(self.x,self.y) 214 | 215 | #title = self.target + " for " + str(self.strike) + " " + "Call with " + str(self.iv*100) + "% IV" + " and " + str(self.exp) + " DTE" 216 | self.ThreeDWin.DrawGraph(X, Y, Z, self.target) 217 | 218 | #### PyQt5 GUI #### 219 | class MainWindow_double(QWidget): 220 | def __init__(self, parent = None): 221 | 222 | super(MainWindow_double,self).__init__() 223 | 224 | ## GRID LAYOUT 225 | self.layout = QGridLayout() 226 | self.input_frame = QtWidgets.QFrame() 227 | self.input_layout = QGridLayout() 228 | 229 | 230 | self.ThreeDWin = ThreeDSurface_GraphWindow() 231 | self.layout.addWidget(self.ThreeDWin, 2, 0, 1, 4) 232 | 233 | self.layout_button = QPushButton('Toggle Inputs', self) 234 | self.layout.addWidget(self.layout_button, 3, 0) 235 | self.layout_button.clicked.connect(self.toggle_inputs) 236 | 237 | self.export_df_button = QPushButton('Export CSV', self) 238 | self.layout.addWidget(self.export_df_button, 3, 1) 239 | self.export_df_button.clicked.connect(self.export_df) 240 | 241 | self.export_df_button = QPushButton('Export PNG', self) 242 | self.layout.addWidget(self.export_df_button, 3, 2) 243 | self.export_df_button.clicked.connect(self.export_jpg) 244 | 245 | my_range = [0,2] 246 | 247 | counter = 1 248 | for i in my_range: 249 | 250 | self.contract_label = QLabel(self) 251 | self.contract_label.setText("Contract {}:".format(counter)) 252 | self.input_layout.addWidget(self.contract_label, i, 0) 253 | counter += 1 254 | 255 | self.strike_input_label = QLabel(self) 256 | self.strike_input_label.setText("Strike: ") 257 | self.strike_input_label.setStyleSheet('color: black') 258 | self.input_layout.addWidget(self.strike_input_label, i, 1) 259 | self.strike_input = QLineEdit(self) 260 | objname = "strike_{}".format(str(i)) 261 | self.strike_input.setObjectName(objname) 262 | self.input_layout.addWidget(self.strike_input, i, 2) 263 | 264 | if i == 0: 265 | self.spot_input_label = QLabel(self) 266 | self.spot_input_label.setText("Spot: ") 267 | self.spot_input_label.setStyleSheet('color: black') 268 | self.input_layout.addWidget(self.spot_input_label, i, 3) 269 | self.spot_input = QLineEdit(self) 270 | objname = "spot_{}".format(str(i)) 271 | self.spot_input.setObjectName(objname) 272 | self.input_layout.addWidget(self.spot_input, i, 4) 273 | if i == 0: 274 | self.exp_input_label = QLabel(self) 275 | self.exp_input_label.setText("DTE: ") 276 | self.exp_input_label.setStyleSheet('color: black') 277 | self.input_layout.addWidget(self.exp_input_label, i, 5) 278 | self.exp_input = QLineEdit(self) 279 | objname = "exp_{}".format(str(i)) 280 | self.exp_input.setObjectName(objname) 281 | self.input_layout.addWidget(self.exp_input, i, 6) 282 | 283 | self.iv_input_label = QLabel(self) 284 | self.iv_input_label.setText("IV: ") 285 | self.iv_input_label.setStyleSheet('color: black') 286 | self.input_layout.addWidget(self.iv_input_label, i, 7) 287 | self.iv_input = QLineEdit(self) 288 | objname = "iv_{}".format(str(i)) 289 | self.iv_input.setObjectName(objname) 290 | self.input_layout.addWidget(self.iv_input, i, 8) 291 | 292 | if i == 0: 293 | 294 | self.r_input_label = QLabel(self) 295 | self.r_input_label.setText("R: ") 296 | self.r_input_label.setStyleSheet('color: black') 297 | self.input_layout.addWidget(self.r_input_label, i, 9) 298 | self.r_input = QLineEdit(self) 299 | objname = "r_{}".format(str(i)) 300 | self.r_input.setObjectName(objname) 301 | self.input_layout.addWidget(self.r_input, i, 10) 302 | 303 | self.longshort_comboBox = QComboBox(self) 304 | self.longshort_comboBox.setObjectName(("comboBox")) 305 | self.longshort_comboBox.addItem("Long") 306 | self.longshort_comboBox.addItem("Short") 307 | objname = "longshort_{}".format(str(i)) 308 | self.longshort_comboBox.setObjectName(objname) 309 | self.input_layout.addWidget(self.longshort_comboBox, i + 1, 1) 310 | 311 | self.putcall_comboBox = QComboBox(self) 312 | self.putcall_comboBox.setObjectName(("comboBox")) 313 | self.putcall_comboBox.addItem("Put") 314 | self.putcall_comboBox.addItem("Call") 315 | objname = "putcall_{}".format(str(i)) 316 | self.putcall_comboBox.setObjectName(objname) 317 | self.input_layout.addWidget(self.putcall_comboBox, i + 1, 2) 318 | 319 | self.contract_label = QLabel(self) 320 | self.contract_label.setText("Parameters: ") 321 | self.input_layout.addWidget(self.contract_label, 6, 0) 322 | 323 | self.sw_input_label = QLabel(self) 324 | self.sw_input_label.setText("Spot Width: ") 325 | self.sw_input_label.setStyleSheet('color: black') 326 | self.input_layout.addWidget(self.sw_input_label, 6, 1) 327 | self.sw_input = QLineEdit(self) 328 | self.input_layout.addWidget(self.sw_input, 6, 2) 329 | 330 | 331 | self.ss_input_label = QLabel(self) 332 | self.ss_input_label.setText("Spot Step: ") 333 | self.ss_input_label.setStyleSheet('color: black') 334 | self.input_layout.addWidget(self.ss_input_label, 6, 3) 335 | self.ss_input = QLineEdit(self) 336 | self.input_layout.addWidget(self.ss_input, 6, 4) 337 | 338 | self.ts_input_label = QLabel(self) 339 | self.ts_input_label.setText("Time Step: ") 340 | self.ts_input_label.setStyleSheet('color: black') 341 | self.input_layout.addWidget(self.ts_input_label, 6, 5) 342 | self.ts_input = QLineEdit(self) 343 | self.input_layout.addWidget(self.ts_input, 6, 6) 344 | 345 | self.target_label = QLabel(self) 346 | self.target_label.setText("Target: ") 347 | self.target_label.setStyleSheet('color: black') 348 | self.input_layout.addWidget(self.target_label, 6, 7) 349 | self.comboBox = QComboBox(self) 350 | self.comboBox.setObjectName(("comboBox")) 351 | self.comboBox.addItem("P/L") 352 | self.comboBox.addItem("Delta") 353 | self.comboBox.addItem("Gamma") 354 | self.comboBox.addItem("Theta") 355 | self.comboBox.addItem("Vega") 356 | self.input_layout.addWidget(self.comboBox, 6, 8) 357 | 358 | self.button = QPushButton('Submit', self) 359 | self.input_layout.addWidget(self.button, 6, 10) 360 | self.button.clicked.connect(self.init_plot) 361 | 362 | self.input_frame.setLayout(self.input_layout) 363 | self.layout.addWidget(self.input_frame, 0, 0, 1, 4) 364 | self.setLayout(self.layout) 365 | 366 | def toggle_inputs(self, MainWindow_double): 367 | 368 | self.input_frame.setHidden(not self.input_frame.isHidden()) 369 | 370 | def export_jpg(self, MainWindow_double): 371 | 372 | 373 | self.ThreeDWin.fig.savefig('Option3d_fig.png', dpi = self.ThreeDWin.fig.dpi) 374 | 375 | 376 | def export_df(self, MainWindow_double): 377 | 378 | try: 379 | self.df.to_csv("Option3d_data.csv") 380 | except: 381 | print("No data to return") 382 | 383 | def init_plot(self, MainWindow_double): 384 | 385 | my_range = ["0","2"] 386 | 387 | self.ss = float(self.ss_input.text()) 388 | self.sw = float(self.sw_input.text()) 389 | self.ts = float(self.ts_input.text()) 390 | self.exp = float(self.exp_input.text()) 391 | self.r = float(self.r_input.text()) 392 | self.spot = float(self.spot_input.text()) 393 | self.target = str(self.comboBox.currentText()) 394 | 395 | self.main_array = np.zeros(shape=(2,9)) 396 | counter = 0 397 | for i in my_range: 398 | 399 | self.strike = float(self.findChild(QtWidgets.QLineEdit, "strike_{}".format(i)).text()) 400 | self.iv = float(self.findChild(QtWidgets.QLineEdit, "iv_{}".format(i)).text()) 401 | self.putcall = str(self.findChild(QtWidgets.QComboBox, "putcall_{}".format(i)).currentText()) 402 | self.longshort = str(self.findChild(QtWidgets.QComboBox, "longshort_{}".format(i)).currentText()) 403 | 404 | if self.longshort == 'Long': 405 | self.scaler = 1 406 | else: 407 | self.scaler = -1 408 | if self.putcall == 'Put': 409 | self.type = 1 410 | else: 411 | self.type = 0 412 | 413 | self.array = np.array( 414 | [[self.scaler, self.type, self.spot, self.strike, self.exp, self.iv, 415 | self.sw, self.ss, self.ts]], 416 | np.float64 417 | ) 418 | self.main_array[counter] = self.array 419 | counter += 1 420 | 421 | self.df = main_array(self.main_array, self.target) 422 | self.x = self.df.columns.values 423 | self.y = self.df.index.values 424 | Z = self.df.values 425 | 426 | X,Y=np.meshgrid(self.x,self.y) 427 | 428 | #title = self.target + " for " + str(self.strike) + " " + "Call with " + str(self.iv*100) + "% IV" + " and " + str(self.exp) + " DTE" 429 | self.ThreeDWin.DrawGraph(X, Y, Z, self.target) 430 | 431 | 432 | #### PyQt5 GUI #### 433 | class MainWindow_tri(QWidget): 434 | def __init__(self, parent = None): 435 | 436 | super(MainWindow_tri,self).__init__() 437 | 438 | ## GRID LAYOUT 439 | self.layout = QGridLayout() 440 | self.input_frame = QtWidgets.QFrame() 441 | self.input_layout = QGridLayout() 442 | 443 | 444 | self.ThreeDWin = ThreeDSurface_GraphWindow() 445 | self.layout.addWidget(self.ThreeDWin, 2, 0, 1, 4) 446 | 447 | self.layout_button = QPushButton('Toggle Inputs', self) 448 | self.layout.addWidget(self.layout_button, 3, 0) 449 | self.layout_button.clicked.connect(self.toggle_inputs) 450 | 451 | self.export_df_button = QPushButton('Export CSV', self) 452 | self.layout.addWidget(self.export_df_button, 3, 1) 453 | self.export_df_button.clicked.connect(self.export_df) 454 | 455 | self.export_df_button = QPushButton('Export PNG', self) 456 | self.layout.addWidget(self.export_df_button, 3, 2) 457 | self.export_df_button.clicked.connect(self.export_jpg) 458 | 459 | my_range = [0,2,4] 460 | counter = 1 461 | for i in my_range: 462 | 463 | self.contract_label = QLabel(self) 464 | self.contract_label.setText("Contract {}:".format(counter)) 465 | self.input_layout.addWidget(self.contract_label, i, 0) 466 | counter += 1 467 | 468 | self.strike_input_label = QLabel(self) 469 | self.strike_input_label.setText("Strike: ") 470 | self.strike_input_label.setStyleSheet('color: black') 471 | self.input_layout.addWidget(self.strike_input_label, i, 1) 472 | self.strike_input = QLineEdit(self) 473 | objname = "strike_{}".format(str(i)) 474 | self.strike_input.setObjectName(objname) 475 | self.input_layout.addWidget(self.strike_input, i, 2) 476 | 477 | if i == 0: 478 | self.spot_input_label = QLabel(self) 479 | self.spot_input_label.setText("Spot: ") 480 | self.spot_input_label.setStyleSheet('color: black') 481 | self.input_layout.addWidget(self.spot_input_label, i, 3) 482 | self.spot_input = QLineEdit(self) 483 | objname = "spot_{}".format(str(i)) 484 | self.spot_input.setObjectName(objname) 485 | self.input_layout.addWidget(self.spot_input, i, 4) 486 | if i == 0: 487 | self.exp_input_label = QLabel(self) 488 | self.exp_input_label.setText("DTE: ") 489 | self.exp_input_label.setStyleSheet('color: black') 490 | self.input_layout.addWidget(self.exp_input_label, i, 5) 491 | self.exp_input = QLineEdit(self) 492 | objname = "exp_{}".format(str(i)) 493 | self.exp_input.setObjectName(objname) 494 | self.input_layout.addWidget(self.exp_input, i, 6) 495 | 496 | self.iv_input_label = QLabel(self) 497 | self.iv_input_label.setText("IV: ") 498 | self.iv_input_label.setStyleSheet('color: black') 499 | self.input_layout.addWidget(self.iv_input_label, i, 7) 500 | self.iv_input = QLineEdit(self) 501 | objname = "iv_{}".format(str(i)) 502 | self.iv_input.setObjectName(objname) 503 | self.input_layout.addWidget(self.iv_input, i, 8) 504 | 505 | if i == 0: 506 | 507 | self.r_input_label = QLabel(self) 508 | self.r_input_label.setText("R: ") 509 | self.r_input_label.setStyleSheet('color: black') 510 | self.input_layout.addWidget(self.r_input_label, i, 9) 511 | self.r_input = QLineEdit(self) 512 | objname = "r_{}".format(str(i)) 513 | self.r_input.setObjectName(objname) 514 | self.input_layout.addWidget(self.r_input, i, 10) 515 | 516 | self.longshort_comboBox = QComboBox(self) 517 | self.longshort_comboBox.setObjectName(("comboBox")) 518 | self.longshort_comboBox.addItem("Long") 519 | self.longshort_comboBox.addItem("Short") 520 | objname = "longshort_{}".format(str(i)) 521 | self.longshort_comboBox.setObjectName(objname) 522 | self.input_layout.addWidget(self.longshort_comboBox, i + 1, 1) 523 | 524 | self.putcall_comboBox = QComboBox(self) 525 | self.putcall_comboBox.setObjectName(("comboBox")) 526 | self.putcall_comboBox.addItem("Put") 527 | self.putcall_comboBox.addItem("Call") 528 | objname = "putcall_{}".format(str(i)) 529 | self.putcall_comboBox.setObjectName(objname) 530 | self.input_layout.addWidget(self.putcall_comboBox, i + 1, 2) 531 | 532 | self.contract_label = QLabel(self) 533 | self.contract_label.setText("Parameters: ") 534 | self.input_layout.addWidget(self.contract_label, 6, 0) 535 | 536 | self.sw_input_label = QLabel(self) 537 | self.sw_input_label.setText("Spot Width: ") 538 | self.sw_input_label.setStyleSheet('color: black') 539 | self.input_layout.addWidget(self.sw_input_label, 6, 1) 540 | self.sw_input = QLineEdit(self) 541 | self.input_layout.addWidget(self.sw_input, 6, 2) 542 | 543 | 544 | self.ss_input_label = QLabel(self) 545 | self.ss_input_label.setText("Spot Step: ") 546 | self.ss_input_label.setStyleSheet('color: black') 547 | self.input_layout.addWidget(self.ss_input_label, 6, 3) 548 | self.ss_input = QLineEdit(self) 549 | self.input_layout.addWidget(self.ss_input, 6, 4) 550 | 551 | self.ts_input_label = QLabel(self) 552 | self.ts_input_label.setText("Time Step: ") 553 | self.ts_input_label.setStyleSheet('color: black') 554 | self.input_layout.addWidget(self.ts_input_label, 6, 5) 555 | self.ts_input = QLineEdit(self) 556 | self.input_layout.addWidget(self.ts_input, 6, 6) 557 | 558 | self.target_label = QLabel(self) 559 | self.target_label.setText("Target: ") 560 | self.target_label.setStyleSheet('color: black') 561 | self.input_layout.addWidget(self.target_label, 6, 7) 562 | self.comboBox = QComboBox(self) 563 | self.comboBox.setObjectName(("comboBox")) 564 | self.comboBox.addItem("P/L") 565 | self.comboBox.addItem("Delta") 566 | self.comboBox.addItem("Gamma") 567 | self.comboBox.addItem("Theta") 568 | self.comboBox.addItem("Vega") 569 | self.input_layout.addWidget(self.comboBox, 6, 8) 570 | 571 | self.button = QPushButton('Submit', self) 572 | self.input_layout.addWidget(self.button, 6, 10) 573 | self.button.clicked.connect(self.init_plot) 574 | 575 | self.input_frame.setLayout(self.input_layout) 576 | self.layout.addWidget(self.input_frame, 0, 0, 1, 4) 577 | self.setLayout(self.layout) 578 | 579 | def toggle_inputs(self, MainWindow_tri): 580 | 581 | self.input_frame.setHidden(not self.input_frame.isHidden()) 582 | 583 | def export_jpg(self, MainWindow_tri): 584 | 585 | 586 | self.ThreeDWin.fig.savefig('Option3d_fig.png', dpi = self.ThreeDWin.fig.dpi) 587 | 588 | 589 | def export_df(self, MainWindow_tri): 590 | 591 | try: 592 | self.df.to_csv("Option3d_data.csv") 593 | except: 594 | print("No data to return") 595 | 596 | def init_plot(self, MainWindow_tri): 597 | 598 | my_range = ["0","2", "4"] 599 | 600 | self.ss = float(self.ss_input.text()) 601 | self.sw = float(self.sw_input.text()) 602 | self.ts = float(self.ts_input.text()) 603 | self.exp = float(self.exp_input.text()) 604 | self.r = float(self.r_input.text()) 605 | self.spot = float(self.spot_input.text()) 606 | self.target = str(self.comboBox.currentText()) 607 | 608 | self.main_array = np.zeros(shape=(3,9)) 609 | counter = 0 610 | for i in my_range: 611 | 612 | self.strike = float(self.findChild(QtWidgets.QLineEdit, "strike_{}".format(i)).text()) 613 | self.iv = float(self.findChild(QtWidgets.QLineEdit, "iv_{}".format(i)).text()) 614 | self.putcall = str(self.findChild(QtWidgets.QComboBox, "putcall_{}".format(i)).currentText()) 615 | self.longshort = str(self.findChild(QtWidgets.QComboBox, "longshort_{}".format(i)).currentText()) 616 | 617 | if self.longshort == 'Long': 618 | self.scaler = 1 619 | else: 620 | self.scaler = -1 621 | if self.putcall == 'Put': 622 | self.type = 1 623 | else: 624 | self.type = 0 625 | 626 | self.array = np.array( 627 | [[self.scaler, self.type, self.spot, self.strike, self.exp, self.iv, 628 | self.sw, self.ss, self.ts]], 629 | np.float64 630 | ) 631 | self.main_array[counter] = self.array 632 | counter += 1 633 | 634 | self.df = main_array(self.main_array, self.target) 635 | self.x = self.df.columns.values 636 | self.y = self.df.index.values 637 | Z = self.df.values 638 | 639 | X,Y=np.meshgrid(self.x,self.y) 640 | 641 | #title = self.target + " for " + str(self.strike) + " " + "Call with " + str(self.iv*100) + "% IV" + " and " + str(self.exp) + " DTE" 642 | self.ThreeDWin.DrawGraph(X, Y, Z, self.target) 643 | 644 | #### PyQt5 GUI #### 645 | class MainWindow_quad(QWidget): 646 | def __init__(self, parent = None): 647 | 648 | super(MainWindow_quad,self).__init__() 649 | 650 | ## GRID LAYOUT 651 | self.layout = QGridLayout() 652 | self.input_frame = QtWidgets.QFrame() 653 | self.input_layout = QGridLayout() 654 | 655 | 656 | self.ThreeDWin = ThreeDSurface_GraphWindow() 657 | self.layout.addWidget(self.ThreeDWin, 2, 0, 1, 4) 658 | 659 | self.layout_button = QPushButton('Toggle Inputs', self) 660 | self.layout.addWidget(self.layout_button, 3, 0) 661 | self.layout_button.clicked.connect(self.toggle_inputs) 662 | 663 | self.export_df_button = QPushButton('Export CSV', self) 664 | self.layout.addWidget(self.export_df_button, 3, 1) 665 | self.export_df_button.clicked.connect(self.export_df) 666 | 667 | self.export_df_button = QPushButton('Export PNG', self) 668 | self.layout.addWidget(self.export_df_button, 3, 2) 669 | self.export_df_button.clicked.connect(self.export_jpg) 670 | 671 | my_range = [0,2,4,6] 672 | counter = 1 673 | for i in my_range: 674 | 675 | self.contract_label = QLabel(self) 676 | self.contract_label.setText("Contract {}:".format(counter)) 677 | self.input_layout.addWidget(self.contract_label, i, 0) 678 | counter += 1 679 | 680 | self.strike_input_label = QLabel(self) 681 | self.strike_input_label.setText("Strike: ") 682 | self.strike_input_label.setStyleSheet('color: black') 683 | self.input_layout.addWidget(self.strike_input_label, i, 1) 684 | self.strike_input = QLineEdit(self) 685 | objname = "strike_{}".format(str(i)) 686 | self.strike_input.setObjectName(objname) 687 | self.input_layout.addWidget(self.strike_input, i, 2) 688 | 689 | if i == 0: 690 | self.spot_input_label = QLabel(self) 691 | self.spot_input_label.setText("Spot: ") 692 | self.spot_input_label.setStyleSheet('color: black') 693 | self.input_layout.addWidget(self.spot_input_label, i, 3) 694 | self.spot_input = QLineEdit(self) 695 | objname = "spot_{}".format(str(i)) 696 | self.spot_input.setObjectName(objname) 697 | self.input_layout.addWidget(self.spot_input, i, 4) 698 | if i == 0: 699 | self.exp_input_label = QLabel(self) 700 | self.exp_input_label.setText("DTE: ") 701 | self.exp_input_label.setStyleSheet('color: black') 702 | self.input_layout.addWidget(self.exp_input_label, i, 5) 703 | self.exp_input = QLineEdit(self) 704 | objname = "exp_{}".format(str(i)) 705 | self.exp_input.setObjectName(objname) 706 | self.input_layout.addWidget(self.exp_input, i, 6) 707 | 708 | self.iv_input_label = QLabel(self) 709 | self.iv_input_label.setText("IV: ") 710 | self.iv_input_label.setStyleSheet('color: black') 711 | self.input_layout.addWidget(self.iv_input_label, i, 7) 712 | self.iv_input = QLineEdit(self) 713 | objname = "iv_{}".format(str(i)) 714 | self.iv_input.setObjectName(objname) 715 | self.input_layout.addWidget(self.iv_input, i, 8) 716 | 717 | if i == 0: 718 | 719 | self.r_input_label = QLabel(self) 720 | self.r_input_label.setText("R: ") 721 | self.r_input_label.setStyleSheet('color: black') 722 | self.input_layout.addWidget(self.r_input_label, i, 9) 723 | self.r_input = QLineEdit(self) 724 | objname = "r_{}".format(str(i)) 725 | self.r_input.setObjectName(objname) 726 | self.input_layout.addWidget(self.r_input, i, 10) 727 | 728 | self.longshort_comboBox = QComboBox(self) 729 | self.longshort_comboBox.setObjectName(("comboBox")) 730 | self.longshort_comboBox.addItem("Long") 731 | self.longshort_comboBox.addItem("Short") 732 | objname = "longshort_{}".format(str(i)) 733 | self.longshort_comboBox.setObjectName(objname) 734 | self.input_layout.addWidget(self.longshort_comboBox, i + 1, 1) 735 | 736 | self.putcall_comboBox = QComboBox(self) 737 | self.putcall_comboBox.setObjectName(("comboBox")) 738 | self.putcall_comboBox.addItem("Put") 739 | self.putcall_comboBox.addItem("Call") 740 | objname = "putcall_{}".format(str(i)) 741 | self.putcall_comboBox.setObjectName(objname) 742 | self.input_layout.addWidget(self.putcall_comboBox, i + 1, 2) 743 | 744 | self.contract_label = QLabel(self) 745 | self.contract_label.setText("Parameters: ") 746 | self.input_layout.addWidget(self.contract_label, 8, 0) 747 | 748 | self.sw_input_label = QLabel(self) 749 | self.sw_input_label.setText("Spot Width: ") 750 | self.sw_input_label.setStyleSheet('color: black') 751 | self.input_layout.addWidget(self.sw_input_label, 8, 1) 752 | self.sw_input = QLineEdit(self) 753 | self.input_layout.addWidget(self.sw_input, 8, 2) 754 | 755 | 756 | self.ss_input_label = QLabel(self) 757 | self.ss_input_label.setText("Spot Step: ") 758 | self.ss_input_label.setStyleSheet('color: black') 759 | self.input_layout.addWidget(self.ss_input_label, 8, 3) 760 | self.ss_input = QLineEdit(self) 761 | self.input_layout.addWidget(self.ss_input, 8, 4) 762 | 763 | self.ts_input_label = QLabel(self) 764 | self.ts_input_label.setText("Time Step: ") 765 | self.ts_input_label.setStyleSheet('color: black') 766 | self.input_layout.addWidget(self.ts_input_label, 8, 5) 767 | self.ts_input = QLineEdit(self) 768 | self.input_layout.addWidget(self.ts_input, 8, 6) 769 | 770 | self.target_label = QLabel(self) 771 | self.target_label.setText("Target: ") 772 | self.target_label.setStyleSheet('color: black') 773 | self.input_layout.addWidget(self.target_label, 8, 7) 774 | self.comboBox = QComboBox(self) 775 | self.comboBox.setObjectName(("comboBox")) 776 | self.comboBox.addItem("P/L") 777 | self.comboBox.addItem("Delta") 778 | self.comboBox.addItem("Gamma") 779 | self.comboBox.addItem("Theta") 780 | self.comboBox.addItem("Vega") 781 | self.input_layout.addWidget(self.comboBox, 8, 8) 782 | 783 | self.button = QPushButton('Submit', self) 784 | self.input_layout.addWidget(self.button, 8, 10) 785 | self.button.clicked.connect(self.init_plot) 786 | 787 | self.input_frame.setLayout(self.input_layout) 788 | self.layout.addWidget(self.input_frame, 0, 0, 1, 4) 789 | self.setLayout(self.layout) 790 | 791 | def toggle_inputs(self, MainWindow_quad): 792 | 793 | self.input_frame.setHidden(not self.input_frame.isHidden()) 794 | 795 | def export_jpg(self, MainWindow_quad): 796 | 797 | 798 | self.ThreeDWin.fig.savefig('Option3d_fig.png', dpi = self.ThreeDWin.fig.dpi) 799 | 800 | 801 | def export_df(self, MainWindow_quad): 802 | 803 | try: 804 | self.df.to_csv("Option3d_data.csv") 805 | except: 806 | print("No data to return") 807 | 808 | def init_plot(self, MainWindow_quad): 809 | 810 | my_range = ["0","2", "4", "6"] 811 | 812 | self.ss = float(self.ss_input.text()) 813 | self.sw = float(self.sw_input.text()) 814 | self.ts = float(self.ts_input.text()) 815 | self.exp = float(self.exp_input.text()) 816 | self.r = float(self.r_input.text()) 817 | self.spot = float(self.spot_input.text()) 818 | self.target = str(self.comboBox.currentText()) 819 | 820 | self.main_array = np.zeros(shape=(4,9)) 821 | counter = 0 822 | for i in my_range: 823 | 824 | self.strike = float(self.findChild(QtWidgets.QLineEdit, "strike_{}".format(i)).text()) 825 | self.iv = float(self.findChild(QtWidgets.QLineEdit, "iv_{}".format(i)).text()) 826 | self.putcall = str(self.findChild(QtWidgets.QComboBox, "putcall_{}".format(i)).currentText()) 827 | self.longshort = str(self.findChild(QtWidgets.QComboBox, "longshort_{}".format(i)).currentText()) 828 | 829 | if self.longshort == 'Long': 830 | self.scaler = 1 831 | else: 832 | self.scaler = -1 833 | if self.putcall == 'Put': 834 | self.type = 1 835 | else: 836 | self.type = 0 837 | 838 | self.array = np.array( 839 | [[self.scaler, self.type, self.spot, self.strike, self.exp, self.iv, 840 | self.sw, self.ss, self.ts]], 841 | np.float64 842 | ) 843 | self.main_array[counter] = self.array 844 | counter += 1 845 | 846 | self.df = main_array(self.main_array, self.target) 847 | self.x = self.df.columns.values 848 | self.y = self.df.index.values 849 | Z = self.df.values 850 | 851 | X,Y=np.meshgrid(self.x,self.y) 852 | 853 | #title = self.target + " for " + str(self.strike) + " " + "Call with " + str(self.iv*100) + "% IV" + " and " + str(self.exp) + " DTE" 854 | self.ThreeDWin.DrawGraph(X, Y, Z, self.target) 855 | 856 | 857 | 858 | 859 | 860 | class Window(QWidget): 861 | 862 | def __init__(self, parent = None): 863 | 864 | super(Window,self).__init__() 865 | 866 | self.parent_layout = QGridLayout() 867 | self.setLayout(self.parent_layout) 868 | 869 | self.tabwidget = QtWidgets.QTabWidget() 870 | self.Tab1 = MainWindow_single() 871 | self.Tab2 = MainWindow_double() 872 | self.Tab3 = MainWindow_tri() 873 | self.Tab4 = MainWindow_quad() 874 | 875 | self.tabwidget.addTab(self.Tab1, '1 Leg') 876 | self.tabwidget.addTab(self.Tab2, '2 Leg') 877 | self.tabwidget.addTab(self.Tab3, '3 Leg') 878 | self.tabwidget.addTab(self.Tab4, '4 Leg') 879 | 880 | self.parent_layout.addWidget(self.tabwidget, 0,0) 881 | 882 | 883 | if __name__ == "__main__": 884 | App = QApplication(sys.argv) 885 | window = Window() 886 | window.show() 887 | sys.exit(App.exec()) 888 | --------------------------------------------------------------------------------