def __init__(self,symbol,interest,leg): symbol = symbol interest = float(interest) self.qty = int(leg.q.get()) self.expiry = datetime.strptime(leg.ex.get()+' 2359', '%m/%d/%y %H%M') self.strike = float(leg.s.get()) self.option = leg.var.get() self.price = float(leg.p.get()) sym_today = symbol+str(dt.today()) self.dte = (self.expiry - datetime.today()).days if sym_today in prices_dict: uprice = prices_dict[sym_today] else: uprice = si.get_live_price(symbol) prices_dict[sym_today] = uprice if leg.var.get() == 'Call': v = mibian.BS([uprice,self.strike,interest,self.dte], callPrice=self.price).impliedVolatility if leg.var.get() == 'Put': v = mibian.BS([uprice,self.strike,interest,self.dte], putPrice=self.price).impliedVolatility self.vol = v
def testBS(self): '''Black-Scholes model tests''' test = mibian.BS([81, 80, 6, 60], volatility=30) self.assertEqual([test.callPrice, test.putPrice], [4.8422936422068901, 3.0571309465072147]) self.assertEqual([test.callDelta, test.putDelta], [0.5963986247019829, -0.4036013752980171]) self.assertEqual([test.callDelta2, test.putDelta2], [-0.54332493698317152, 0.4468605293205825]) self.assertEqual([test.callTheta, test.putTheta], [-0.038938157820841104, -0.025916540729723249]) self.assertEqual([test.callRho, test.putRho], [0.07145095061696502, -0.05876522029421359]) self.assertEqual(test.vega, 0.12717225103657845) self.assertEqual(test.gamma, 0.039304536595328565) test = mibian.BS([52, 60, 5, 30], callPrice=3) self.assertEqual(test.impliedVolatility, 95.703125) test = mibian.BS([52, 60, 5, 30], putPrice=7.86) self.assertEqual(test.impliedVolatility, 29.78515625) test = mibian.BS([81, 80, 6, 60], callPrice=4.8422936422068901, putPrice=3.0571309465072147) self.assertEqual(test.putCallParity, 0.02254482311879258)
def getOpionsChainPE(self, **kwargs): Strike = int(0) Length = int(0) for key, value in kwargs.items(): print("%s == %s" % (key, value)) if (key == "Strike"): Strike = int(value) elif (key == "Length"): Length = int(value) else: Strike = 0 Length = 0 if Strike == 0: Strike = self.BankNiftyATMPrice if Length == 0: Length = 10 self.getBankNiftySpot() self.getBankNiftyATM() idx = self.BankNiftyPEInstruments[self.BankNiftyPEInstruments['strike'] == Strike].index x = idx.item() PE_Chain_DF = self.BankNiftyPEInstruments[(x - Length):(x + Length)].copy() PE_Instrument_list = PE_Chain_DF['tradingsymbol'].tolist() for i in range(len(PE_Instrument_list)): PE_Instrument_list[i] = "NFO:" + PE_Instrument_list[i] PEPrices = self.kconn.ltp(PE_Instrument_list) print(PEPrices) PE_Chain_DF['IV'] = np.nan PE_Chain_DF['Delta'] = np.nan for ind in PE_Chain_DF.index: PE_Chain_DF["last_price"][ind] = PEPrices[ "NFO:" + PE_Chain_DF["tradingsymbol"][ind]]["last_price"] voltyP = mibian.BS([ self.BankNiftySpotPrice, PE_Chain_DF["strike"][ind], self.interestrates, (self.Expiry - date.today()).days ], putPrice=PE_Chain_DF["last_price"][ind]) newVoltyP = float("{:.2f}".format(voltyP.impliedVolatility)) PE_Chain_DF["IV"][ind] = newVoltyP p = mibian.BS([ self.BankNiftySpotPrice, PE_Chain_DF["strike"][ind], self.interestrates, (self.Expiry - date.today()).days ], volatility=newVoltyP) PE_Chain_DF["Delta"][ind] = p.putDelta return PE_Chain_DF
def y_today(self,row,int_rate=1.0): x = self.x dte = (row.Expiration - pd.Timestamp('today')).days y=[] for a in x: if row.Option == 'Call': y.append((mibian.BS([float(a),row.Strike,int_rate,dte],\ volatility=row.Vol).callPrice-row.Price)*row.Quantity*100) if row.Option == 'Put': y.append((mibian.BS([float(a),row.Strike,int_rate,dte],\ volatility=row.Vol).putPrice-row.Price)*row.Quantity*100) return np.array(y)
def _get_odds_otm(current_price, strike, days_to_expiry, put_price): cache_key = '_get_odds_otm' + str(current_price) + str(strike) + str(days_to_expiry) + str(put_price) cached_result = cache.get(cache_key) if cached_result is not None: return cached_result put_implied_volatility_calculator = mibian.BS([current_price, strike, INTEREST_RATE, days_to_expiry], putPrice=put_price) # kinda silly, we need to construct another object to extract delta for a computation based on real put price # Yahoo's volatility in interesting_put.impliedVolatility seems low, ~20% too low, so lets use the implied volatility implied_volatility = put_implied_volatility_calculator.impliedVolatility put_with_implied_volatility = mibian.BS([current_price, strike, INTEREST_RATE, days_to_expiry], volatility=implied_volatility) result = 1 + put_with_implied_volatility.putDelta cache.set(cache_key, result, YAHOO_FINANCE_CACHE_TIMEOUT) return result
def _test_mibian(): underlying = 1.4565 strike = 1.45 interest = 1 days = 30 opt_info = [underlying, strike, interest, days] c = mibian.BS(opt_info, volatility=20) print(c.callPrice, c.putPrice, c.callDelta, c.putDelta, c.callDelta2, c.putDelta2, c.callTheta, c.putTheta, c.callRho, c.putRho, c.vega, c.gamma) co = mibian.BS(opt_info, callPrice=c.callPrice) co.impliedVolatility
def implied_vol(underlying_price: float, strike_price: float, interest: float, expiry_date: date, obs_date: date = None, call_price: float = None, put_price: float = None): """ It is used to find the implied volatility of the option. Either call_price or put_price should be given. If both are given then call is evaluated by default. :param underlying_price: float (S) Spot price :param strike_price: float (K) Strike price :param interest: float (r) Risk free interest rate :param expiry_date: date (T) Time to maturity i.e. expiry date :param obs_date: date Date of observation. By default present date. :param call_price: float Call option price for the strike. :param put_price: float Put option price for the strike. :return: float Implied volatility of the option """ days_expiry = days_to_expiry(expiry_date, obs_date=obs_date) if days_expiry > 0: if (call_price is None) & (put_price is None): _logger.warning("Either call or put price need to be given") else: c = mibian.BS if call_price is not None: c = mibian.BS( [underlying_price, strike_price, interest, days_expiry], callPrice=call_price) elif put_price is not None: c = mibian.BS( [underlying_price, strike_price, interest, days_expiry], putPrice=put_price) iv = c.impliedVolatility # if iv == float("1e-05"): # iv = 0.00001 return iv else: _logger.warning("Enter a date greater than today") _logger.info("You entered: %s" % expiry_date) return
def correct_IV_call(futures_price, data_buy, left_day, k): #修正call的隱波 import mibian import pandas as pd import numpy as np import statsmodels.api as sm IV_buy = [] for i in range(len(data_buy)): try: #先用真實價格套入BS模型回推隱波 a = mibian.BS( [futures_price, float(data_buy['履約價'][i]), 0.003, left_day], callPrice=float(data_buy['結算價'][i])) IV_buy.append(a.impliedVolatility) except: pass weights_buy = np.polyfit(k, IV_buy, 6) #用6次式回歸修正 model_buy = np.poly1d(weights_buy) b = list(range(min(data_buy['履約價']), max(data_buy['履約價']) + 100, 100)) pred_buy = model_buy(b) #套回修正過的回歸式回傳新的隱波 #print(pred_buy) plt.plot(b, pred_buy) #plt.xlim(10000,16000) #plt.ylim(10,80) plt.show() return pred_buy, b
def correct_IV_put(futures_price,data_sell,left_day,k):#修正put的隱波 import mibian import pandas as pd import numpy as np import statsmodels.api as sm IV_sell = [] print('____執行correct_IV_put中回推隱波迴圈') # def get_IV(row): # return mibian.BS([futures_price, float(row['履約價']), 0.003, left_day], putPrice=float(row['結算價'])).impliedVolatility for i in range(len(data_sell)): try: #先用真實價格套入BS模型回推隱波 print('結算價{}:'.format(i),float(data_sell['結算價'][i])) a = mibian.BS([futures_price, float(data_sell['履約價'][i]), 0.003, left_day], putPrice= float(data_sell['結算價'][i])) IV_sell.append(a.impliedVolatility) except: pass # IV_sell = data_sell.apply(get_IV, axis=1) print('____執行correct_IV_put中ployfit') weights_sell = np.polyfit(k, IV_sell, 6)#用6次式回歸修正 print('____執行correct_IV_put中ploy1d') model_sell = np.poly1d(weights_sell) print('____執行correct_IV_put中List') b = list(range(min(data_sell['履約價']),max(data_sell['履約價'])+100,100)) print('____執行correct_IV_put中model_sell') pred_sell = model_sell(b)#套回修正過的回歸式回傳新的隱波 return pred_sell, b#回傳
def get_greeks(self, today, new_stock, new_vol): '''Takes the date for today and the the current strike price and returns the greeks as a dictionary''' self.stock = new_stock self.volatility = new_vol days_left = (self.exDate - today).days position = self.direction c = m.BS([self.stock, self.strike, self.interest, days_left], volatility=self.volatility) if self.optiontype == "Call": price = c.callPrice * position * 100 theta = c.callTheta * position * 100 delta = c.callDelta * position * 100 else: price = c.putPrice * position * 100 theta = c.putTheta * position * 100 delta = c.putDelta * position * 100 options_dict = {"price": price, "theta": theta, "delta": delta} return options_dict
def mibian_bs(row) -> pd.Series: formula = mibian.BS( [ row["underlying_price"], row["strike"], 0, row["days_to_expiry"], ], volatility=row["volatility"], ) if row["type"] == "CALL": delta = formula.callDelta * row["amount"] theta = formula.callTheta * row["amount"] elif row["type"] == "PUT": delta = formula.putDelta * row["amount"] theta = formula.putTheta * row["amount"] gamma = formula.gamma * row["amount"] s = pd.Series({ "delta": delta, "theta": theta, "gamma": gamma, }) return s
def predict_call_price(futures_price,data_buy,left_day,k):#修正新的call價格 pred_buy, b = correct_IV_call(futures_price,data_buy,left_day,k)#先尋找修正後的call隱波 whole_buy_price = [] for i in range(len(b)): #用修正後的隱波套入BS模型得到價格並回傳 call_price = mibian.BS([futures_price,b[i],0.3,left_day],pred_buy[i]).callPrice whole_buy_price.append(call_price) return whole_buy_price
def greek_string(deets, iv): #array deets needs [underlyingPrice, strikePrice, interestRate, daysToExpiration] c = mibian.BS(deets, iv) return ([ c.callPrice, c.putPrice, c.callDelta, c.putDelta, c.callDelta2, c.putDelta2, c.callTheta, c.putTheta, c.callRho, c.putRho, c.vega, c.gamma ])
def implied_vol(S, K, T, rfr, callprice=0.0, putprice=0.0): """ Calculates the implied volatility of the option. """ S1 = float(S) K1 = float(K) interest_rate = float(rfr * 100) days_to_expiration = float(T * 365) if callprice > 0.0: bsc = mibian.BS([S1, K1, interest_rate, days_to_expiration], callPrice=callprice) elif putprice > 0.0: bsc = mibian.BS([S1, K1, interest_rate, days_to_expiration], putPrice=putprice) implied_vol = round(bsc.impliedVolatility / 100, 6) return implied_vol
def predict_put_price(futures_price,data_sell,left_day,k):#修正新的put價格 pred_sell,b = correct_IV_put(futures_price,data_sell,left_day,k)#先尋找修正後的put隱波 whole_sell_price = [] for i in range(len(b)): #用修正後的隱波套入BS模型得到價格並回傳 put_price = mibian.BS([futures_price,b[i],0.3,left_day],pred_sell[i]).putPrice whole_sell_price.append(put_price) return whole_sell_price
def calc(): global TMX global endTime for K in prices: c = mibian.BS([TMX, K, 0.0, (endTime - time.time()) / 15.0], callPrice=C[K]) print c.impliedVolatility print
def call_put_pricing_mibian(self, S_0, K, T, r, sigma, div, option='call'): c = mibian.BS([S_0, K, r, T], volatility=sigma) if option == 'call': return c.callPrice elif option == 'put': return c.putPrice else: raise NameError('Option not defined correctly')
def y_days(self, row,int_rate=1.0,interval=0.0): days = interval x = self.x y=[] dte = (row.Expiration - pd.Timestamp('today')).days - days if dte < 0: return [] if dte == 0: dte = .1 for a in x: if row.Option == 'Call': y.append((mibian.BS([float(a),row.Strike,int_rate,dte],volatility=row.Vol) .callPrice-row.Price)*row.Quantity*100) if row.Option == 'Put': y.append((mibian.BS([float(a),row.Strike,int_rate,dte],volatility=row.Vol) .putPrice-row.Price)*row.Quantity*100) return np.array(y)
def compute_payoff(self, date=None, dte=None, iv_now=None): day_of_exp = datetime.datetime.strptime(self.expiry, '%d%b%Y').date() today = datetime.datetime.strptime(date, '%d%b%Y').date() dte = np.max([(day_of_exp - today).days, 0]) + 0.000000001 if iv_now == None: iv_now = self.iv # If Call option... if self.opt_type == 'C': # If long self.calls... if self.qty > 0: # payoff = max(self.underlying_range- strike - premium, -premium) ??? self.payoff = (self.underlying_range.apply(lambda x: mibian.BS( [x, self.strike, 6.47, dte], volatility=iv_now).callPrice) - self.entry_price) * abs(self.qty) # If short self.calls... elif self.qty < 0: # payoff = min(- self.underlying_range+ strike + premium, premium) ??? self.payoff = (self.entry_price - self.underlying_range.apply( lambda x: mibian.BS([x, self.strike, 6.47, dte], volatility=iv_now).callPrice)) * abs( self.qty) # If Put option... elif self.opt_type == 'P': # If long self.puts... if self.qty > 0: # payoff = max(strike - self.underlying_range- premium, -premium) ??? self.payoff = (self.underlying_range.apply(lambda x: mibian.BS( [x, self.strike, 6.47, dte], volatility=iv_now).putPrice) - self.entry_price) * abs(self.qty) # If short self.puts... elif self.qty < 0: # payoff = min(- strike + self.underlying_range+ premium, premium) ??? self.payoff = (self.entry_price - self.underlying_range.apply( lambda x: mibian.BS([x, self.strike, 6.47, dte], volatility=iv_now).putPrice)) * abs( self.qty) # Set underlying price range as index self.payoff.index = self.underlying_range return self.payoff
def compute_expiry_payoff(self): # If Call option... if self.opt_type == 'C': # If long self.calls... if self.qty > 0: # expiry_payoff = max(self.underlying_range- strike - premium, -premium) ??? self.expiry_payoff = (self.underlying_range.apply( lambda x: mibian.BS([x, self.strike, 6.47, 0.00000001], volatility=self.iv).callPrice) - self.entry_price) * abs(self.qty) # If short self.calls... elif self.qty < 0: # expiry_payoff = min(- self.underlying_range+ strike + premium, premium) ??? self.expiry_payoff = ( self.entry_price - self.underlying_range.apply(lambda x: mibian.BS( [x, self.strike, 6.47, 0.00000001], volatility=self.iv) .callPrice)) * abs(self.qty) # If Put option... elif self.opt_type == 'P': # If long self.puts... if self.qty > 0: # expiry_payoff = max(strike - self.underlying_range- premium, -premium) ??? self.expiry_payoff = (self.underlying_range.apply( lambda x: mibian.BS([x, self.strike, 6.47, 0.00000001], volatility=self.iv).putPrice) - self.entry_price) * abs(self.qty) # If short self.puts... elif self.qty < 0: # expiry_payoff = min(- strike + self.underlying_range+ premium, premium) ??? self.expiry_payoff = ( self.entry_price - self.underlying_range.apply(lambda x: mibian.BS( [x, self.strike, 6.47, 0.00000001], volatility=self.iv) .putPrice)) * abs(self.qty) # Set underlying price range as index self.expiry_payoff.index = self.underlying_range return self.expiry_payoff
def option_price(underlying_price: float, strike_price: float, interest: float, expiry_date: date, volatility: float, obs_date: date = None): """ It is used to evaluate option's theoretical price and it's related greeks. :param underlying_price: float (S) Spot price :param strike_price: float (K) Strike price :param interest: float (r) Risk free interest rate :param expiry_date: date (T) Time to maturity i.e. expiry date :param volatility: float (v) (sigma) Volatility of the option :param obs_date: date Date of observation. By default present date. :return: GreekValues """ days_to_exp = days_to_expiry(expiry_date, obs_date) if days_to_exp > 0: c = mibian.BS([underlying_price, strike_price, interest, days_to_exp], volatility=volatility) call = c.callPrice call_delta = c.callDelta call_dual_delta = c.callDelta2 call_theta = c.callTheta call_rho = c.callRho put = c.putPrice put_delta = c.putDelta put_dual_delta = c.putDelta2 put_theta = c.putTheta put_rho = c.putRho vega = c.vega gamma = c.gamma greek_values = GreekValues(call, call_delta, call_dual_delta, call_theta, call_rho, put, put_delta, put_dual_delta, put_theta, put_rho, vega, gamma) return greek_values elif days_to_exp == 0: greek_values = GreekValues(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) return greek_values else: _logger.warning("Enter a date greater than today") _logger.info("You entered: %s" % expiry_date) greek_values = GreekValues(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) return greek_values
def find_price(target_value, call_put, S, K, T, r, clicked): if clicked and target_value and call_put and S and K and T and T >= 0 and r: sigma = mibian.BS([S, K, r, T], volatility=target_value) if call_put == 'c': return sigma.callPrice if call_put == 'p': return sigma.putPrice else: return ''
def diagonal_spread(sT, interest_rate, front_strike, back_strike, front_future, back_future, dte_front_opt, dte_back_opt): ''' Diagonal spread (similar to a Calendar Spread) involves options of the same underlying but with different strike price & expiry If a Call/Put is Sold with near-term expiration it is called "“front-month”" If a Call/Put is Bought with long-term expiration it is called "“back-month”" ''' net_premium = back_opt_premium - front_opt_premium days_diff = dte_back_opt - dte_front_opt + 1 # Front-month IV front_opt_iv = mibian.BS( [front_future, front_strike, interest_rate, dte_front_opt], callPrice=front_opt_premium).impliedVolatility # Back-month IV back_opt_iv = mibian.BS( [back_future, back_strike, interest_rate, dte_back_opt], callPrice=back_opt_premium).impliedVolatility # Calendar Spread DataFrame diagonal_spread = pd.DataFrame() diagonal_spread['underlying_price'] = sT diagonal_spread['front_opt_premium'] = np.nan diagonal_spread['back_opt_premium'] = np.nan for i in range( 0, len(diagonal_spread) ): # Calculating option price for different possible values of Future Contracts diagonal_spread.loc[i, 'front_opt_premium'] = mibian.BS( [ diagonal_spread.iloc[i]['underlying_price'], front_strike, interest_rate, dte_front_opt ], volatility=front_opt_iv).callPrice diagonal_spread.loc[i, 'back_opt_premium'] = mibian.BS( [ diagonal_spread.iloc[i]['underlying_price'] + days_diff, back_strike, interest_rate, dte_back_opt ], volatility=back_opt_iv).callPrice diagonal_spread[ 'payoff'] = diagonal_spread.back_opt_premium - diagonal_spread.front_opt_premium - net_premium return diagonal_spread
def _test_py_vollib(): #CL,Q2019,560P,07/02/2019,0.6,1.61,0.54,1.54,1997,4465 F = 56.25 K = 56 sigma = .366591539 flag = 'p' t = 15 / 365.0 r = .025 discounted_call_price = black.black(flag, F, K, t, r, sigma) dcp = 1.54 ivpy = implied_volatility.implied_volatility(dcp, F, K, r, t, flag) ivmn = mibian.BS([F, K, 2.5, 15], callPrice=dcp).impliedVolatility discounted_call_price, ivpy, ivmn
def calculate_delta(dic, df, contract_type, curr_price): df["Remaining Days"] = df["Expiration Date"] - datetime.now() if contract_type == "Call": delta_ls = [ mibian.BS([ curr_price, row["Strike"], dic["Interest Rate"], row["Remaining Days"].days ], row["Volatility"][:-1]).callDelta for index, row in df.iterrows() ] elif contract_type == "Put": delta_ls = [ mibian.BS([ curr_price, row["Strike"], dic["Interest Rate"], row["Remaining Days"].days ], row["Volatility"][:-1]).putDelta for index, row in df.iterrows() ] else: raise ValueError df["Delta"] = pd.Series(delta_ls, index=df.index) return df
def predict_call_price(futures_price, data_buy, left_day, k): #修正新的call價格 import mibian import pandas as pd import numpy as np import statsmodels.api as sm pred_buy, b = correct_IV_call(futures_price, data_buy, left_day, k) #先尋找修正後的call隱波 whole_buy_price = [] for i in range(len(b)): #用修正後的隱波套入BS模型得到價格並回傳 call_price = mibian.BS([futures_price, b[i], 0.3, left_day], pred_buy[i]).callPrice whole_buy_price.append(call_price) return whole_buy_price
def predict_put_price(futures_price, data_sell, left_day, k): #修正新的put價格 import mibian import pandas as pd import numpy as np import statsmodels.api as sm pred_sell, b = correct_IV_put(futures_price, data_sell, left_day, k) #先尋找修正後的put隱波 whole_sell_price = [] for i in range(len(b)): #用修正後的隱波套入BS模型得到價格並回傳 put_price = mibian.BS([futures_price, b[i], 0.3, left_day], pred_sell[i]).putPrice whole_sell_price.append(put_price) return whole_sell_price
def correct_IV_call(futures_price,data_buy,left_day,k):#修正call的隱波 IV_buy = [] for i in range(len(data_buy)): try: #先用真實價格套入BS模型回推隱波 print('結算價{}:'.format(i),float(data_buy['結算價'][i])) a = mibian.BS([futures_price, float(data_buy['履約價'][i]), 0.003, left_day], callPrice= float(data_buy['結算價'][i])) IV_buy.append(a.impliedVolatility) except: pass weights_buy = np.polyfit(pd.to_numeric(k), pd.to_numeric(IV_buy), 6)#用6次式回歸修正 model_buy = np.poly1d(weights_buy) b = list(range(min(data_buy['履約價']),max(data_buy['履約價'])+100,100)) pred_buy = model_buy(b)#套回修正過的回歸式回傳新的隱波 return pred_buy, b
def american_put_black_scholes(initial_stock_price, strike_price, rf_rate, maturity, sigma, num_steps): # Parameter initialization deltaT = maturity / num_steps up_factor = np.exp(sigma * np.sqrt(deltaT)) down_factor = 1.0 / up_factor p = (np.exp(rf_rate * deltaT) - down_factor) / (up_factor - down_factor) # Binomial Price Tree stock_values = np.zeros((num_steps + 1, num_steps + 1)) stock_values[0, 0] = initial_stock_price for i in range(1, num_steps + 1): stock_values[i, 0] = stock_values[i - 1, 0] * up_factor for j in range(1, i + 1): stock_values[i, j] = stock_values[i - 1, j - 1] * down_factor # savetxt('stock_values.csv', stock_values, delimiter=',') # Option Price at Final Node option_values = np.zeros((num_steps + 1, num_steps + 1)) for i in range(num_steps + 1): option_values[num_steps, i] = max(0, strike_price - stock_values[num_steps, i]) # Backward calculation for initial options price for j in range(num_steps - 1): option_values[num_steps, j] = mibian.BS([100, 105, 3, 365], volatility=20).putPrice for i in range(num_steps - 2, -1, -1): for j in range(i + 1): option_values[i, j] = max( 0, strike_price - stock_values[i, j], np.exp(-rf_rate * deltaT) * (p * option_values[i + 1, j] + (1 - p) * option_values[i + 1, j + 1])) # savetxt('option_values.csv', option_values, delimiter=',') return option_values[0, 0]
def put_call_parity(underlying_price: float, strike_price: float, interest: float, expiry_date: date, obs_date: date = None, call_price: float = None, put_price: float = None): """ This is used to find the put call parity for the options. Both call and put option price are required. :param underlying_price: float (S) Spot price :param strike_price: float (K) Strike price :param interest: float (r) Risk free interest rate :param expiry_date: date (T) Time to maturity i.e. expiry date :param obs_date: date Date of observation. By default present date. :param call_price: float Call option price for the strike. :param put_price: float Put option price for the strike. :return: tuple(float, float) Returns put call parity and implied volatility """ days_expiry = days_to_expiry(expiry_date, obs_date=obs_date) if days_expiry > 0: if (call_price is None) & (put_price is None): _logger.warning("Both call and put price need to be given") else: c = mibian.BS( [underlying_price, strike_price, interest, days_expiry], callPrice=call_price, putPrice=put_price) return c.putCallParity, c.impliedVolatility else: _logger.warning("Enter a date greater than today") _logger.info("You entered: %s" % expiry_date) return