def testFinModelBlackScholes(): valueDate = FinDate(8, 5, 2015) expiryDate = FinDate(15, 1, 2016) strikePrice = 130.0 stockPrice = 127.62 volatility = 0.20 interestRate = 0.001 dividendYield = 0.0163 optionType = FinOptionTypes.AMERICAN_CALL euOptionType = FinOptionTypes.EUROPEAN_CALL amOption = FinEquityAmericanOption(expiryDate, strikePrice, optionType) ameuOption = FinEquityAmericanOption(expiryDate, strikePrice, euOptionType) euOption = FinEquityVanillaOption(expiryDate, strikePrice, euOptionType) discountCurve = FinDiscountCurveFlat(valueDate, interestRate, FinFrequencyTypes.CONTINUOUS, FinDayCountTypes.ACT_365F) numStepsPerYear = 400 modelTree = FinModelBlackScholes(volatility, FinModelBlackScholesTypes.CRR_TREE, {'numStepsPerYear': numStepsPerYear}) v = amOption.value(valueDate, stockPrice, discountCurve, dividendYield, modelTree) # print(v) modelApprox = FinModelBlackScholes(volatility, FinModelBlackScholesTypes.BARONE_ADESI, None) v = amOption.value(valueDate, stockPrice, discountCurve, dividendYield, modelApprox) # print(v) v = ameuOption.value(valueDate, stockPrice, discountCurve, dividendYield, modelTree) # print(v) v = euOption.value(valueDate, stockPrice, discountCurve, dividendYield, modelTree) # print(v) amTreeValue = [] amBAWValue = [] euTreeValue = [] euAnalValue = [] volatility = 0.20
def test_FinEquityCliquetOptionHaug(): ''' Following example in Haug Page 130 ''' startDate = FinDate(1, 1, 2015) finalExpiryDate = FinDate(1, 1, 2017) freqType = FinFrequencyTypes.QUARTERLY optionType = FinOptionTypes.EUROPEAN_CALL cliquetOption = FinEquityCliquetOption(startDate, finalExpiryDate, optionType, freqType) valueDate = FinDate(1, 8, 2016) stockPrice = 50.0 volatility = 0.35 interestRate = 0.10 dividendYield = 0.05 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) v = cliquetOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) testCases.header("LABEL", "VALUE") testCases.print("FINANCEPY", v)
def test_FinEquityChooserOptionHaug(): ''' Following example in Haug Page 130 ''' valueDate = FinDate(1, 1, 2015) chooseDate = FinDate(2, 4, 2015) callExpiryDate = FinDate(1, 7, 2015) putExpiryDate = FinDate(2, 8, 2015) callStrike = 55.0 putStrike = 48.0 stockPrice = 50.0 volatility = 0.35 interestRate = 0.10 dividendYield = 0.05 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) dividendCurve = FinDiscountCurveFlat(valueDate, dividendYield) chooserOption = FinEquityChooserOption(chooseDate, callExpiryDate, putExpiryDate, callStrike, putStrike) v = chooserOption.value(valueDate, stockPrice, discountCurve, dividendCurve, model) v_mc = chooserOption.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, model, 20000) v_haug = 6.0508 testCases.header("", "", "", "", "", "") testCases.print("FINANCEPY", v, "HAUG", v_haug, "MC", v_mc)
def test_FinEquityChooserOptionMatlab(): '''https://fr.mathworks.com/help/fininst/chooserbybls.html ''' valueDate = FinDate(1, 6, 2007) chooseDate = FinDate(31, 8, 2007) callExpiryDate = FinDate(2, 12, 2007) putExpiryDate = FinDate(2, 12, 2007) callStrike = 60.0 putStrike = 60.0 stockPrice = 50.0 volatility = 0.20 interestRate = 0.10 dividendYield = 0.05 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) dividendCurve = FinDiscountCurveFlat(valueDate, dividendYield) chooserOption = FinEquityChooserOption(chooseDate, callExpiryDate, putExpiryDate, callStrike, putStrike) v = chooserOption.value(valueDate, stockPrice, discountCurve, dividendCurve, model) v_mc = chooserOption.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, model, 20000) v_matlab = 8.9308 testCases.header("", "", "", "", "", "") testCases.print("FINANCEPY", v, "MATLAB", v_matlab, "MC", v_mc)
def test_FinEquityChooserOptionDerivicom(): '''http://derivicom.com/support/finoptionsxl/index.html?complex_chooser.htm ''' valueDate = FinDate(1, 1, 2007) chooseDate = FinDate(1, 2, 2007) callExpiryDate = FinDate(1, 4, 2007) putExpiryDate = FinDate(1, 5, 2007) callStrike = 40.0 putStrike = 35.0 stockPrice = 38.0 volatility = 0.20 interestRate = 0.08 dividendYield = 0.0625 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) dividendCurve = FinDiscountCurveFlat(valueDate, dividendYield) chooserOption = FinEquityChooserOption(chooseDate, callExpiryDate, putExpiryDate, callStrike, putStrike) v = chooserOption.value(valueDate, stockPrice, discountCurve, dividendCurve, model) v_mc = chooserOption.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, model, 20000) v_derivicom = 1.0989 testCases.header("", "", "", "", "", "") testCases.print("FINANCEPY", v, "DERIVICOM", v_derivicom, "MC", v_mc)
def test_FinEquityCliquetOption(): startDate = FinDate(1, 1, 2014) finalExpiryDate = FinDate(1, 1, 2017) freqType = FinFrequencyTypes.QUARTERLY optionType = FinOptionTypes.EUROPEAN_CALL cliquetOption = FinEquityCliquetOption(startDate, finalExpiryDate, optionType, freqType) valueDate = FinDate(1, 1, 2015) stockPrice = 100.0 volatility = 0.20 interestRate = 0.05 dividendYield = 0.02 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) dividendCurve = FinDiscountCurveFlat(valueDate, dividendYield) v = cliquetOption.value(valueDate, stockPrice, discountCurve, dividendCurve, model) testCases.header("LABEL", "VALUE") testCases.print("FINANCEPY", v)
def test_FinFXVanillaOptionBloombergExample(): # Example Bloomberg Pricing at # https://stackoverflow.com/questions/48778712/fx-vanilla-call-price-in-quantlib-doesnt-match-bloomberg valuationDate = FinDate(13, 2, 2018) expiryDate = FinDate(15, 2, 2019) # In BS the FX rate is the price in domestic of one unit of foreign # In case of EURUSD = 1.3 the domestic currency is USD and foreign is EUR # DOM = USD , FOR = EUR forName = "EUR" domName = "USD" forDepoRate = 0.05 # EUR domDepoRate = 0.02 # USD currencyPair = forName + domName # Always FORDOM spotFXRate = 1.30 strikeFXRate = 1.3650 volatility = 0.20 spotDays = 0 settlementDate = valuationDate.addWeekDays(spotDays) maturityDate = settlementDate.addMonths(12) notional = 1000000.0 notionalCurrency = "EUR" calendarType = FinCalendarTypes.TARGET depos = [] fras = [] swaps = [] depo = FinIborDeposit(settlementDate, maturityDate, domDepoRate, FinDayCountTypes.ACT_360, notional, calendarType) depos.append(depo) domDiscountCurve = FinIborSingleCurve(valuationDate, depos, fras, swaps) depos = [] fras = [] swaps = [] depo = FinIborDeposit(settlementDate, maturityDate, forDepoRate, FinDayCountTypes.ACT_360, notional, calendarType) depos.append(depo) forDiscountCurve = FinIborSingleCurve(valuationDate, depos, fras, swaps) model = FinModelBlackScholes(volatility) callOption = FinFXVanillaOption(expiryDate, strikeFXRate, currencyPair, FinOptionTypes.EUROPEAN_CALL, notional, notionalCurrency, 2) value = callOption.value(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) delta = callOption.delta(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) testCases.header("value", "delta") testCases.print(value, delta)
def test_FinFXVanillaOptionWystupExample1(): # Example from Book extract by Uwe Wystup with results in Table 1.2 # https://mathfinance.com/wp-content/uploads/2017/06/FXOptionsStructuredProducts2e-Extract.pdf # Not exactly T=1.0 but close so don't exact exact agreement # (in fact I do not get exact agreement even if I do set T=1.0) valueDate = FinDate(13, 2, 2018) expiryDate = FinDate(13, 2, 2019) # In BS the FX rate is the price in domestic of one unit of foreign # In case of EURUSD = 1.3 the domestic currency is USD and foreign is EUR # DOM = USD , FOR = EUR ccy1 = "EUR" ccy2 = "USD" ccy1CCRate = 0.030 # EUR ccy2CCRate = 0.025 # USD currencyPair = ccy1 + ccy2 # Always ccy1ccy2 spotFXRate = 1.20 strikeFXRate = 1.250 volatility = 0.10 notional = 1000000.0 domDiscountCurve = FinDiscountCurveFlat(valueDate, ccy2CCRate) forDiscountCurve = FinDiscountCurveFlat(valueDate, ccy1CCRate) model = FinModelBlackScholes(volatility) # Two examples to show that changing the notional currency and notional # keeps the value unchanged notional = 1000000.0 callOption = FinFXVanillaOption(expiryDate, strikeFXRate, currencyPair, FinOptionTypes.EUROPEAN_CALL, notional, "EUR", 2) value = callOption.value(1.0, spotFXRate, domDiscountCurve, forDiscountCurve, model) notional = 1250000.0 callOption = FinFXVanillaOption(expiryDate, strikeFXRate, currencyPair, FinOptionTypes.EUROPEAN_CALL, notional, "USD", 2) value = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) delta = callOption.delta(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) testCases.header("value", "delta") testCases.print(value, delta)
def test_FinFXVanillaOptionWystupExample2(): # Example Bloomberg Pricing at # https://stackoverflow.com/questions/48778712/fx-vanilla-call-price-in-quantlib-doesnt-match-bloomberg valueDate = FinDate(13, 2, 2018) expiryDate = FinDate(13, 2, 2019) # In BS the FX rate is the price in domestic of one unit of foreign # In case of EURUSD = 1.3 the domestic currency is USD and foreign is EUR # DOM = USD , FOR = EUR ccy1 = "EUR" ccy2 = "USD" ccy1CCRate = 0.0396 # EUR ccy2CCRate = 0.0357 # USD currencyPair = ccy1 + ccy2 # Always ccy1ccy2 spotFXRate = 0.9090 strikeFXRate = 0.9090 volatility = 0.12 notional = 1000000.0 domDiscountCurve = FinDiscountCurveFlat(valueDate, ccy2CCRate) forDiscountCurve = FinDiscountCurveFlat(valueDate, ccy1CCRate) model = FinModelBlackScholes(volatility) # Two examples to show that changing the notional currency and notional # keeps the value unchanged notional = 1000000.0 callOption = FinFXVanillaOption(expiryDate, strikeFXRate, currencyPair, FinOptionTypes.EUROPEAN_PUT, notional, "EUR", 2) value = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) delta = callOption.delta(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) testCases.header("value", "delta") testCases.print(value, delta)
def testTimeEvolution(): startAveragingDate = FinDate(2015, 1, 1) expiryDate = FinDate(2016, 1, 1) stockPrice = 100.0 volatility = 0.20 interestRate = 0.30 dividendYield = 0.10 numObservations = 100 # weekly as we have a year accruedAverage = None K = 100 seed = 1976 model = FinModelBlackScholes(volatility) asianOption = FinEquityAsianOption(startAveragingDate, expiryDate, K, FinOptionTypes.EUROPEAN_CALL, numObservations) testCases.header("Date", "Geometric", "Turnbull_Wakeman", "Curran", "FastMC", "FastMC_CV") valuesTurnbull = [] valuesCurran = [] valuesGeometric = [] valuesMC_fast = [] valuesMC_CV = [] valueDates = [] valueDates.append(FinDate(2014, 4, 1)) valueDates.append(FinDate(2014, 6, 1)) valueDates.append(FinDate(2014, 8, 1)) valueDates.append(FinDate(2015, 2, 1)) valueDates.append(FinDate(2015, 4, 1)) valueDates.append(FinDate(2015, 6, 1)) valueDates.append(FinDate(2015, 8, 1)) numPaths = 10000 for valueDate in valueDates: accruedAverage = stockPrice * 0.9 discountCurve = FinDiscountCurveFlat(valueDate, interestRate) valueMC_fast = asianOption._valueMC_fast(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, accruedAverage) valueMC_CV = asianOption.valueMC(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, accruedAverage) valueGeometric = asianOption.value( valueDate, stockPrice, discountCurve, dividendYield, model, FinAsianOptionValuationMethods.GEOMETRIC, accruedAverage) valueTurnbullWakeman = asianOption.value( valueDate, stockPrice, discountCurve, dividendYield, model, FinAsianOptionValuationMethods.TURNBULL_WAKEMAN, accruedAverage) valueCurran = asianOption.value(valueDate, stockPrice, discountCurve, dividendYield, model, FinAsianOptionValuationMethods.CURRAN, accruedAverage) valuesGeometric.append(valueGeometric) valuesTurnbull.append(valueTurnbullWakeman) valuesCurran.append(valueCurran) valuesMC_fast.append(valueMC_fast) valuesMC_CV.append(valueMC_CV) testCases.print(str(valueDate), valueGeometric, valueTurnbullWakeman, valueCurran, valueMC_fast, valueMC_CV)
def test_FinNumbaNumbaParallel(useSobol): valueDate = FinDate(1, 1, 2015) expiryDate = FinDate(1, 7, 2015) stockPrice = 100 volatility = 0.30 interestRate = 0.05 dividendYield = 0.01 seed = 2021 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) useSobolInt = int(useSobol) testCases.header("NUMPATHS", "VALUE_BS", "VALUE_MC", "TIME") callOption = FinEquityVanillaOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_CALL) value = callOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) numPoints = 20 v_exact = [value] * numPoints numPathsList = np.arange(1, numPoints + 1, 1) * 1000000 NUMBA_ONLY_v = [] NUMBA_ONLY_t = [] print("NUMBA ONLY") for numPaths in numPathsList: start = time.time() valueMC = callOption.valueMC_NUMBA_ONLY(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, useSobolInt) end = time.time() duration = end - start print("%10d %9.5f %9.5f %9.6f" % (numPaths, value, valueMC, duration)) NUMBA_ONLY_v.append(valueMC) NUMBA_ONLY_t.append(duration) NUMBA_PARALLEL_v = [] NUMBA_PARALLEL_t = [] print("NUMBA PARALLEL") for numPaths in numPathsList: start = time.time() valueMC = callOption.valueMC_NUMBA_PARALLEL(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, useSobolInt) end = time.time() duration = end - start print("%10d %9.5f %9.5f %9.6f" % (numPaths, value, valueMC, duration)) NUMBA_PARALLEL_v.append(valueMC) NUMBA_PARALLEL_t.append(duration) ########################################################################### import matplotlib.pyplot as plt if useSobol: title = "SOBOL: NUMBA VS NUMBA + PARALLEL" else: title = "PSEUDORANDOM: NUMBA VS NUMBA + PARALLEL" plt.figure(figsize=(8, 6)) plt.plot(numPathsList, NUMBA_ONLY_t, 'o-', label="NUMBA ONLY") plt.plot(numPathsList, NUMBA_PARALLEL_t, 'o-', label="NUMBA PARALLEL") plt.xlabel("Number of Paths") plt.ylabel("Wall Time (s)") plt.legend() plt.title(title) plt.figure(figsize=(8, 6)) plt.plot(numPathsList, v_exact, label="EXACT") plt.plot(numPathsList, NUMBA_ONLY_v, 'o-', label="NUMBA ONLY") plt.plot(numPathsList, NUMBA_PARALLEL_v, 'o-', label="NUMBA PARALLEL") plt.xlabel("Number of Paths") plt.ylabel("Option Value") plt.legend() plt.title(title)
def test_FinNumbaNumpySpeed(useSobol): valueDate = FinDate(1, 1, 2015) expiryDate = FinDate(1, 7, 2015) stockPrice = 100 volatility = 0.30 interestRate = 0.05 dividendYield = 0.01 seed = 1999 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) useSobolInt = int(useSobol) testCases.header("NUMPATHS", "VALUE_BS", "VALUE_MC", "TIME") callOption = FinEquityVanillaOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_CALL) value = callOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) numPoints = 20 v_exact = [value] * numPoints ########################################################################### # DO UP TO 100K AS IT IS SLOW ########################################################################### numPathsList = np.arange(1, numPoints + 1, 1) * 100000 NONUMBA_NONUMPY_v = [] NONUMBA_NONUMPY_t = [] print("PURE PYTHON") for numPaths in numPathsList: start = time.time() valueMC = callOption.valueMC_NONUMBA_NONUMPY(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, useSobolInt) end = time.time() duration = end - start print("%10d %9.5f %9.5f %9.6f" % (numPaths, value, valueMC, duration)) NONUMBA_NONUMPY_v.append(valueMC) NONUMBA_NONUMPY_t.append(duration + 1e-10) NUMPY_ONLY_v = [] NUMPY_ONLY_t = [] print("NUMPY ONLY") for numPaths in numPathsList: start = time.time() valueMC = callOption.valueMC_NUMPY_ONLY(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, useSobolInt) end = time.time() duration = end - start print("%10d %9.5f %9.5f %9.6f" % (numPaths, value, valueMC, duration)) NUMPY_ONLY_v.append(valueMC) NUMPY_ONLY_t.append(duration + 1e-10) # speedUp = np.array(NONUMBA_NONUMPY_t)/np.array(NUMPY_ONLY_t) # print(NUMPY_ONLY_t) # print(NONUMBA_NONUMPY_t) # print(speedUp) if useSobol: title = "SOBOL: PURE PYTHON VS NUMPY" else: title = "PSEUDORANDOM: PURE PYTHON VS NUMPY" plt.figure(figsize=(8, 6)) plt.plot(numPathsList, NONUMBA_NONUMPY_t, 'o-', label="PURE PYTHON") plt.plot(numPathsList, NUMPY_ONLY_t, 'o-', label="NUMPY ONLY") plt.xlabel("Number of Paths") plt.ylabel("Wall Time (s)") plt.legend() plt.title(title) plt.figure(figsize=(8, 6)) plt.plot(numPathsList, v_exact, label="EXACT") plt.plot(numPathsList, NONUMBA_NONUMPY_v, 'o-', label="PURE PYTHON") plt.plot(numPathsList, NUMPY_ONLY_v, 'o-', label="NUMPY ONLY") plt.xlabel("Number of Paths") plt.ylabel("Option Value") plt.legend() plt.title(title) ########################################################################### # DO UP TO 10 MILLION NOW THAT WE HAVE NUMPY ########################################################################### numPathsList = np.arange(1, numPoints + 1, 1) * 1000000 NUMPY_ONLY_v = [] NUMPY_ONLY_t = [] print("NUMPY ONLY") for numPaths in numPathsList: start = time.time() valueMC = callOption.valueMC_NUMPY_ONLY(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, useSobolInt) end = time.time() duration = end - start print("%10d %9.5f %9.5f %9.6f" % (numPaths, value, valueMC, duration)) NUMPY_ONLY_v.append(valueMC) NUMPY_ONLY_t.append(duration) NUMBA_NUMPY_v = [] NUMBA_NUMPY_t = [] print("NUMBA+NUMPY") for numPaths in numPathsList: start = time.time() valueMC = callOption.valueMC_NUMPY_NUMBA(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, useSobolInt) end = time.time() duration = end - start print("%10d %9.5f %9.5f %9.6f" % (numPaths, value, valueMC, duration)) NUMBA_NUMPY_v.append(valueMC) NUMBA_NUMPY_t.append(duration) NUMBA_ONLY_v = [] NUMBA_ONLY_t = [] print("NUMBA ONLY") for numPaths in numPathsList: start = time.time() valueMC = callOption.valueMC_NUMBA_ONLY(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, useSobolInt) end = time.time() duration = end - start print("%10d %9.5f %9.5f %9.6f" % (numPaths, value, valueMC, duration)) NUMBA_ONLY_v.append(valueMC) NUMBA_ONLY_t.append(duration) NUMBA_PARALLEL_v = [] NUMBA_PARALLEL_t = [] print("NUMBA PARALLEL") for numPaths in numPathsList: start = time.time() valueMC = callOption.valueMC_NUMBA_PARALLEL(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, useSobolInt) end = time.time() duration = end - start print("%10d %9.5f %9.5f %9.6f" % (numPaths, value, valueMC, duration)) NUMBA_PARALLEL_v.append(valueMC) NUMBA_PARALLEL_t.append(duration) # speedUp = np.array(NUMBA_ONLY_t)/np.array(NUMBA_PARALLEL_t) # print("PARALLEL:", speedUp) ########################################################################### # COMPUTED USING NUMSTEPS FROM 1M to 10M ########################################################################### CPP_t = np.array([ 0.075, 0.155, 0.223, 0.313, 0.359, 0.421, 0.495, 0.556, 0.64, 0.702, 0.765, 0.841, 0.923, 0.982, 1.05, 1.125, 1.195, 1.261, 1.333, 1.408 ]) CPP_v = np.array([ 9.30872, 9.29576, 9.29422, 9.29832, 9.29863, 9.30153, 9.2994, 9.3025, 9.29653, 9.29875, 9.29897, 9.29996, 9.29931, 9.29796, 9.29784, 9.2992, 9.3001, 9.30093, 9.29876, 9.29921 ]) if useSobol: title = "SOBOL: COMPARING OPTIMISATIONS" else: title = "PSEUDORANDOM: COMPARING OPTIMISATIONS" plt.figure(figsize=(8, 6)) plt.plot(numPathsList, NUMPY_ONLY_t, 'o-', label="NUMPY ONLY") plt.plot(numPathsList, NUMBA_NUMPY_t, 'o-', label="NUMBA + NUMPY") plt.xlabel("Number of Paths") plt.ylabel("Wall Time (s)") plt.legend() plt.title(title) ########################################################################### if useSobol: title = "SOBOL: COMPARING OPTIMISATIONS" else: title = "PSEUDORANDOM: COMPARING OPTIMISATIONS" plt.figure(figsize=(8, 6)) plt.plot(numPathsList, NUMPY_ONLY_t, 'o-', label="NUMPY ONLY") plt.plot(numPathsList, NUMBA_NUMPY_t, 'o-', label="NUMBA + NUMPY") plt.plot(numPathsList, NUMBA_ONLY_t, 'o-', label="NUMBA ONLY") plt.plot(numPathsList, NUMBA_PARALLEL_t, 'o-', label="NUMBA PARALLEL") if useSobol == False: plt.plot(numPathsList, CPP_t, 'o-', label="C++") plt.xlabel("Number of Paths") plt.ylabel("Wall Time (s)") plt.legend() plt.title(title) ########################################################################### plt.figure(figsize=(8, 6)) plt.plot(numPathsList, v_exact, label="EXACT") plt.plot(numPathsList, NUMBA_ONLY_v, 'o-', label="NUMBA ONLY") plt.plot(numPathsList, CPP_v, 'o-', label="C++") plt.xlabel("Number of Paths") plt.ylabel("Option Value") plt.legend() plt.title(title)
def _price_option(contract_type_, contract_type_fin_): for i in range(len(expiry_date)): built_vol_surface = False # If we have a "key strike" need to fit the vol surface if isinstance(strike[i], str): if not (built_vol_surface): fx_vol_surface.build_vol_surface(horizon_date[i]) fx_vol_surface.extract_vol_surface( num_strike_intervals=None) built_vol_surface = True # Delta neutral strike/or whatever strike is quoted as ATM # usually this is ATM delta neutral strike, but can sometimes be ATMF for some Latam # Take the vol directly quoted, rather than getting it from building vol surface if strike[i] == 'atm': strike[i] = fx_vol_surface.get_atm_strike(tenor) vol[i] = fx_vol_surface.get_atm_quoted_vol( tenor) / 100.0 # vol[i] = fx_vol_surface.get_atm_vol(tenor) / 100.0 # interpolated elif strike[i] == 'atms': strike[i] = fx_vol_surface.get_spot( ) # Interpolate vol later elif strike[i] == 'atmf': # Quoted tenor, no need to interpolate strike[i] = float(fx_vol_surface.get_all_market_data()[cross + ".close"][horizon_date[i]]) \ + (float(fx_vol_surface.get_all_market_data()[cross + tenor + ".close"][horizon_date[i]]) \ / self._fx_forwards_pricer.get_forwards_divisor(cross[3:6])) # Interpolate vol later elif strike[i] == '25d-otm': if 'call' in contract_type_: strike[i] = fx_vol_surface.get_25d_call_strike( tenor) vol[i] = fx_vol_surface.get_25d_call_vol( tenor) / 100.0 elif 'put' in contract_type_: strike[i] = fx_vol_surface.get_25d_put_strike( tenor) vol[i] = fx_vol_surface.get_25d_put_vol( tenor) / 100.0 elif strike[i] == '10d-otm': if 'call' in contract_type_: strike[i] = fx_vol_surface.get_10d_call_strike( tenor) vol[i] = fx_vol_surface.get_10d_call_vol( tenor) / 100.0 elif 'put' in contract_type_: strike[i] = fx_vol_surface.get_10d_put_strike( tenor) vol[i] = fx_vol_surface.get_10d_put_vol( tenor) / 100.0 if not (built_vol_surface): try: fx_vol_surface.build_vol_surface(horizon_date[i]) except: logger.warn("Failed to build vol surface for " + str(horizon_date) + ", won't be able to interpolate vol") # fx_vol_surface.extract_vol_surface(num_strike_intervals=None) # If an implied vol hasn't been provided, interpolate that one, fit the vol surface (if hasn't already been # done) if np.isnan(vol[i]): if tenor is None: vol[i] = fx_vol_surface.calculate_vol_for_strike_expiry( strike[i], expiry_date=expiry_date[i], tenor=None) else: vol[i] = fx_vol_surface.calculate_vol_for_strike_expiry( strike[i], expiry_date=None, tenor=tenor) model = FinModelBlackScholes(float(vol[i])) logger.info("Pricing " + contract_type_ + " option, horizon date = " + str(horizon_date[i]) + ", expiry date = " + str(expiry_date[i])) option = FinFXVanillaOption(self._findate(expiry_date[i]), strike[i], cross, contract_type_fin_, notional, cross[0:3]) spot[i] = fx_vol_surface.get_spot() """ FinancePy will return the value in the following dictionary for values {'v': vdf, "cash_dom": cash_dom, "cash_for": cash_for, "pips_dom": pips_dom, "pips_for": pips_for, "pct_dom": pct_dom, "pct_for": pct_for, "not_dom": notional_dom, "not_for": notional_for, "ccy_dom": self._domName, "ccy_for": self._forName} """ option_values[i] = option_values[i] + option.value( self._findate(horizon_date[i]), spot[i], fx_vol_surface.get_dom_discount_curve(), fx_vol_surface.get_for_discount_curve(), model)[premium_output.replace('-', '_')] intrinsic_values[i] = intrinsic_values[i] + option.value( self._findate(expiry_date[i]), spot[i], fx_vol_surface.get_dom_discount_curve(), fx_vol_surface.get_for_discount_curve(), model)[premium_output.replace('-', '_')] """FinancePy returns this dictionary for deltas {"pips_spot_delta": pips_spot_delta, "pips_fwd_delta": pips_fwd_delta, "pct_spot_delta_prem_adj": pct_spot_delta_prem_adj, "pct_fwd_delta_prem_adj": pct_fwd_delta_prem_adj} """ delta[i] = delta[i] + option.delta( self._findate(horizon_date[i]), spot[i], fx_vol_surface.get_dom_discount_curve(), fx_vol_surface.get_for_discount_curve(), model)[delta_output.replace('-', '_')]
def _price_option(contract_type_, contract_type_fin_): for i in range(len(expiry_date)): built_vol_surface = False # If we have a "key strike" need to fit the vol surface if isinstance(strike[i], str): if not (built_vol_surface): fx_vol_surface.build_vol_surface(horizon_date[i]) fx_vol_surface.extract_vol_surface( num_strike_intervals=None) built_vol_surface = True if strike[i] == 'atm': strike[i] = fx_vol_surface.get_atm_strike(tenor) vol[i] = fx_vol_surface.get_atm_vol(tenor) / 100.0 elif strike[i] == 'atms': strike[i] = fx_vol_surface.get_spot() elif strike[i] == 'atmf': delivery_date = self._calendar.get_delivery_date_from_horizon_date( horizon_date[i], cal=cross) strike[i] = self._fx_forwards_price.price_instrument( cross, delivery_date, market_df=fx_vol_surface.get_all_market_data()) elif strike[i] == '25d-otm': if 'call' in contract_type_: strike[i] = fx_vol_surface.get_25d_call_strike( tenor) vol[i] = fx_vol_surface.get_25d_call_strike( tenor) / 100.0 elif 'put' in contract_type_: strike[i] = fx_vol_surface.get_25d_put_strike( tenor) vol[i] = fx_vol_surface.get_25d_put_strike( tenor) / 100.0 elif strike[i] == '10d-otm': if 'call' in contract_type_: strike[i] = fx_vol_surface.get_10d_call_strike( tenor) vol[i] = fx_vol_surface.get_10d_call_strike( tenor) / 100.0 elif 'put' in contract_type_: strike[i] = fx_vol_surface.get_10d_put_strike( tenor) vol[i] = fx_vol_surface.get_10d_put_strike( tenor) / 100.0 if not (built_vol_surface): fx_vol_surface.build_vol_surface(horizon_date[i]) # fx_vol_surface.extract_vol_surface(num_strike_intervals=None) # If an implied vol hasn't been provided, interpolate that one, fit the vol surface (if hasn't already been # done) if np.isnan(vol[i]): if tenor is None: vol[i] = fx_vol_surface.calculate_vol_for_strike_expiry( strike[i], expiry_date=expiry_date[i], tenor=None) else: vol[i] = fx_vol_surface.calculate_vol_for_strike_expiry( strike[i], expiry_date=None, tenor=tenor) model = FinModelBlackScholes(float(vol[i])) logger.info("Pricing " + contract_type_ + " option, horizon date = " + str(horizon_date[i]) + ", expiry date = " + str(expiry_date[i])) option = FinFXVanillaOption(self._findate(expiry_date[i]), strike[i], cross, contract_type_fin_, notional, cross[0:3]) spot[i] = fx_vol_surface.get_spot() """ FinancePy will return the value in the following dictionary for values {'v': vdf, "cash_dom": cash_dom, "cash_for": cash_for, "pips_dom": pips_dom, "pips_for": pips_for, "pct_dom": pct_dom, "pct_for": pct_for, "not_dom": notional_dom, "not_for": notional_for, "ccy_dom": self._domName, "ccy_for": self._forName} """ option_values[i] = option_values[i] + option.value( self._findate(horizon_date[i]), spot[i], fx_vol_surface.get_dom_discount_curve(), fx_vol_surface.get_for_discount_curve(), model)[premium_output.replace('-', '_')] intrinsic_values[i] = intrinsic_values[i] + option.value( self._findate(expiry_date[i]), spot[i], fx_vol_surface.get_dom_discount_curve(), fx_vol_surface.get_for_discount_curve(), model)[premium_output.replace('-', '_')] """FinancePy returns this dictionary for deltas {"pips_spot_delta": pips_spot_delta, "pips_fwd_delta": pips_fwd_delta, "pct_spot_delta_prem_adj": pct_spot_delta_prem_adj, "pct_fwd_delta_prem_adj": pct_fwd_delta_prem_adj} """ delta[i] = delta[i] + option.delta( self._findate(horizon_date[i]), spot[i], fx_vol_surface.get_dom_discount_curve(), fx_vol_surface.get_for_discount_curve(), model)[delta_output.replace('-', '_')]
def test_FinEquityDigitalOption(): underlyingType = FinDigitalOptionTypes.CASH_OR_NOTHING valueDate = FinDate(2015, 1, 1) expiryDate = FinDate(2016, 1, 1) stockPrice = 100.0 volatility = 0.30 interestRate = 0.05 dividendYield = 0.01 discountCurve = FinDiscountCurveFlat(valueDate, interestRate) dividendCurve = FinDiscountCurveFlat(valueDate, dividendYield) model = FinModelBlackScholes(volatility) import time callOptionValues = [] callOptionValuesMC = [] numPathsList = [ 10000, 20000, 40000, 80000, 160000, 320000, 640000, 1280000, 2560000 ] testCases.header("NumLoops", "ValueBS", "ValueMC", "TIME") for numPaths in numPathsList: callOption = FinEquityDigitalOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_CALL, underlyingType) value = callOption.value(valueDate, stockPrice, discountCurve, dividendCurve, model) start = time.time() valueMC = callOption.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, model, numPaths) end = time.time() duration = end - start testCases.print(numPaths, value, valueMC, duration) callOptionValues.append(value) callOptionValuesMC.append(valueMC) # plt.figure(figsize=(10,8)) # plt.plot(numPathsList, callOptionValues, color = 'b', label="Call Option") # plt.plot(numPathsList, callOptionValuesMC, color = 'r', label = "Call Option MC") # plt.xlabel("Num Loops") # plt.legend(loc='best') ########################################################################## stockPrices = range(50, 150, 50) callOptionValues = [] callOptionDeltas = [] callOptionVegas = [] callOptionThetas = [] for stockPrice in stockPrices: callOption = FinEquityDigitalOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_CALL, underlyingType) value = callOption.value(valueDate, stockPrice, discountCurve, dividendCurve, model) delta = callOption.delta(valueDate, stockPrice, discountCurve, dividendCurve, model) vega = callOption.vega(valueDate, stockPrice, discountCurve, dividendCurve, model) theta = callOption.theta(valueDate, stockPrice, discountCurve, dividendCurve, model) callOptionValues.append(value) callOptionDeltas.append(delta) callOptionVegas.append(vega) callOptionThetas.append(theta) putOptionValues = [] putOptionDeltas = [] putOptionVegas = [] putOptionThetas = [] for stockPrice in stockPrices: putOption = FinEquityDigitalOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_PUT, underlyingType) value = putOption.value(valueDate, stockPrice, discountCurve, dividendCurve, model) delta = putOption.delta(valueDate, stockPrice, discountCurve, dividendCurve, model) vega = putOption.vega(valueDate, stockPrice, discountCurve, dividendCurve, model) theta = putOption.theta(valueDate, stockPrice, discountCurve, dividendCurve, model) putOptionValues.append(value) putOptionDeltas.append(delta) putOptionVegas.append(vega) putOptionThetas.append(theta)
def test_FinFXAmericanOption(): # There is no FXAmericanOption class. It is embedded in the FXVanillaOption # class. This test just compares it to the European valueDate = FinDate(13, 2, 2018) expiryDate = FinDate(13, 2, 2019) # In BS the FX rate is the price in domestic of one unit of foreign # In case of EURUSD = 1.3 the domestic currency is USD and foreign is EUR # DOM = USD , FOR = EUR ccy1 = "EUR" ccy2 = "USD" ccy1CCRate = 0.030 # EUR ccy2CCRate = 0.025 # USD currencyPair = ccy1 + ccy2 # Always ccy1ccy2 spotFXRate = 1.20 strikeFXRate = 1.250 volatility = 0.10 domDiscountCurve = FinDiscountCurveFlat(valueDate, ccy2CCRate) forDiscountCurve = FinDiscountCurveFlat(valueDate, ccy1CCRate) model = FinModelBlackScholes(volatility) # Two examples to show that changing the notional currency and notional # keeps the value unchanged testCases.header("SPOT FX RATE", "VALUE_BS", "VOL_IN", "IMPLD_VOL") spotFXRates = np.arange(50, 200, 10)/100.0 for spotFXRate in spotFXRates: callOption = FinFXVanillaOption(expiryDate, strikeFXRate, currencyPair, FinOptionTypes.EUROPEAN_CALL, 1000000, "USD") valueEuropean = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.AMERICAN_CALL, 1000000, "USD") valueAmerican = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] diff = (valueAmerican - valueEuropean) testCases.print(spotFXRate, valueEuropean, valueAmerican, diff) for spotFXRate in spotFXRates: callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.EUROPEAN_PUT, 1000000, "USD") valueEuropean = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.AMERICAN_PUT, 1000000, "USD") valueAmerican = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] diff = (valueAmerican - valueEuropean) testCases.print(spotFXRate, valueEuropean, valueAmerican, diff)
def test_FinEquityVanillaOption(): valueDate = FinDate(2015, 1, 1) expiryDate = FinDate(2015, 7, 1) stockPrice = 100 volatility = 0.30 interestRate = 0.05 dividendYield = 0.01 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) numPathsList = [10000, 20000, 40000, 80000, 160000, 320000] testCases.header("NUMPATHS", "VALUE_BS", "VALUE_MC", "TIME") for numPaths in numPathsList: callOption = FinEquityVanillaOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_CALL) value = callOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) start = time.time() valueMC = callOption.valueMC(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths) end = time.time() duration = end - start testCases.print(numPaths, value, valueMC, duration) ############################################################################### stockPrices = range(80, 120, 10) numPaths = 100000 testCases.header("NUMPATHS", "CALL_VALUE_BS", "CALL_VALUE_MC", "CALL_VALUE_MC_SOBOL", "TIME") useSobol = True for stockPrice in stockPrices: callOption = FinEquityVanillaOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_CALL) value = callOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) start = time.time() useSobol = False valueMC1 = callOption.valueMC(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, useSobol) useSobol = True valueMC2 = callOption.valueMC(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, useSobol) end = time.time() duration = end - start testCases.print(numPaths, value, valueMC1, valueMC2, duration) ############################################################################### stockPrices = range(80, 120, 10) numPaths = 100000 testCases.header("NUMPATHS", "PUT_VALUE_BS", "PUT_VALUE_MC", "PUT_VALUE_MC_SOBOL", "TIME") for stockPrice in stockPrices: putOption = FinEquityVanillaOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_PUT) value = putOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) start = time.time() useSobol = False valueMC1 = putOption.valueMC(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, useSobol) useSobol = True valueMC2 = putOption.valueMC(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, useSobol) end = time.time() duration = end - start testCases.print(numPaths, value, valueMC1, valueMC2, duration) ############################################################################### stockPrices = range(80, 120, 10) testCases.header("STOCK PRICE", "CALL_VALUE_BS", "CALL_DELTA_BS", "CALL_VEGA_BS", "CALL_THETA_BS", "CALL_RHO_BS") for stockPrice in stockPrices: callOption = FinEquityVanillaOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_CALL) value = callOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) delta = callOption.delta(valueDate, stockPrice, discountCurve, dividendYield, model) vega = callOption.vega(valueDate, stockPrice, discountCurve, dividendYield, model) theta = callOption.theta(valueDate, stockPrice, discountCurve, dividendYield, model) rho = callOption.rho(valueDate, stockPrice, discountCurve, dividendYield, model) testCases.print(stockPrice, value, delta, vega, theta, rho) ########################################################################### testCases.header("STOCK PRICE", "PUT_VALUE_BS", "PUT_DELTA_BS", "PUT_VEGA_BS", "PUT_THETA_BS", "PUT_RHO_BS") for stockPrice in stockPrices: putOption = FinEquityVanillaOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_PUT) value = putOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) delta = putOption.delta(valueDate, stockPrice, discountCurve, dividendYield, model) vega = putOption.vega(valueDate, stockPrice, discountCurve, dividendYield, model) theta = putOption.theta(valueDate, stockPrice, discountCurve, dividendYield, model) rho = putOption.rho(valueDate, stockPrice, discountCurve, dividendYield, model) testCases.print(stockPrice, value, delta, vega, theta, rho) ############################################################################### testCases.header("STOCK PRICE", "VALUE_BS", "VOL_IN", "IMPLD_VOL") stockPrices = range(60, 150, 10) for stockPrice in stockPrices: callOption = FinEquityVanillaOption(expiryDate, 100.0, FinOptionTypes.EUROPEAN_CALL) value = callOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) impliedVol = callOption.impliedVolatility(valueDate, stockPrice, discountCurve, dividendYield, value) testCases.print(stockPrice, value, volatility, impliedVol)
def test_FinEquityCompoundOption(): valueDate = FinDate(2015, 1, 1) expiryDate1 = FinDate(2017, 1, 1) expiryDate2 = FinDate(2018, 1, 1) k1 = 5.0 k2 = 95.0 stockPrice = 85.0 volatility = 0.15 interestRate = 0.035 dividendYield = 0.01 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) optionType1 = FinOptionTypes.EUROPEAN_CALL optionType2 = FinOptionTypes.EUROPEAN_PUT numStepsList = [ 200, 300, 400, 500, 600, 700, 800, 900, 1000, 2000, 3000, 4000, 5000 ] cmpdOption = FinEquityCompoundOption(expiryDate1, optionType1, k1, expiryDate2, optionType2, k2) stockPrice = 85.0 testCases.header("TYPE1", "TYPE2", "K1", "K2", "S", "Exact", "TreeSteps", "TreeValue") for numSteps in numStepsList: value = cmpdOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) values = cmpdOption._valueTree(valueDate, stockPrice, discountCurve, dividendYield, model, numSteps) testCases.print(optionType1, optionType2, k1, k2, stockPrice, value, numSteps, values[0]) testCases.header("TYPE1", "TYPE2", "K1", "K2", "S", "Exact", "TreeSteps", "TreeValue", "Diff", "DELTA", "GAMMA", "THETA") for optionType1 in [ FinOptionTypes.EUROPEAN_CALL, FinOptionTypes.EUROPEAN_PUT ]: for optionType2 in [ FinOptionTypes.EUROPEAN_CALL, FinOptionTypes.EUROPEAN_PUT ]: cmpdOption = FinEquityCompoundOption(expiryDate1, optionType1, k1, expiryDate2, optionType2, k2) stockPrices = range(70, 100, 5) for stockPrice in stockPrices: value = cmpdOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) delta = cmpdOption.delta(valueDate, stockPrice, discountCurve, dividendYield, model) vega = cmpdOption.vega(valueDate, stockPrice, discountCurve, dividendYield, model) theta = cmpdOption.theta(valueDate, stockPrice, discountCurve, dividendYield, model) values = cmpdOption._valueTree(valueDate, stockPrice, discountCurve, dividendYield, model) diff = value - values[0] testCases.print(optionType1, optionType2, k1, k2, stockPrice, value, numSteps, values[0], diff, delta, vega, theta)
def test_FinBinomialTree(): stockPrice = 50.0 riskFreeRate = 0.06 dividendYield = 0.04 volatility = 0.40 valueDate = FinDate(2016, 1, 1) expiryDate = FinDate(2017, 1, 1) model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, riskFreeRate) numStepsList = [100, 500, 1000, 2000, 5000] strikePrice = 50.0 testCases.banner("================== EUROPEAN PUT =======================") putOption = FinEquityVanillaOption( expiryDate, strikePrice, FinOptionTypes.EUROPEAN_PUT) value = putOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) delta = putOption.delta(valueDate, stockPrice, discountCurve, dividendYield, model) gamma = putOption.gamma(valueDate, stockPrice, discountCurve, dividendYield, model) theta = putOption.theta(valueDate, stockPrice, discountCurve, dividendYield, model) testCases.header("BS Value", "BS Delta", "BS Gamma", "BS Theta") testCases.print(value, delta, gamma, theta) payoff = FinEquityTreePayoffTypes.VANILLA_OPTION exercise = FinEquityTreeExerciseTypes.EUROPEAN params = np.array([-1, strikePrice]) testCases.header("NumSteps", "Results", "TIME") for numSteps in numStepsList: start = time.time() tree = FinEquityBinomialTree() results = tree.value( stockPrice, discountCurve, dividendYield, volatility, numSteps, valueDate, payoff, expiryDate, payoff, exercise, params) end = time.time() duration = end - start testCases.print(numSteps, results, duration) testCases.banner("================== AMERICAN PUT =======================") payoff = FinEquityTreePayoffTypes.VANILLA_OPTION exercise = FinEquityTreeExerciseTypes.AMERICAN params = np.array([-1, strikePrice]) testCases.header("NumSteps", "Results", "TIME") for numSteps in numStepsList: start = time.time() tree = FinEquityBinomialTree() results = tree.value( stockPrice, discountCurve, dividendYield, volatility, numSteps, valueDate, payoff, expiryDate, payoff, exercise, params) end = time.time() duration = end - start testCases.print(numSteps, results, duration) testCases.banner( "================== EUROPEAN CALL =======================") callOption = FinEquityVanillaOption( expiryDate, strikePrice, FinOptionTypes.EUROPEAN_CALL) value = callOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) delta = callOption.delta(valueDate, stockPrice, discountCurve, dividendYield, model) gamma = callOption.gamma(valueDate, stockPrice, discountCurve, dividendYield, model) theta = callOption.theta(valueDate, stockPrice, discountCurve, dividendYield, model) testCases.header("BS Value", "BS Delta", "BS Gamma", "BS Theta") testCases.print(value, delta, gamma, theta) payoff = FinEquityTreePayoffTypes.VANILLA_OPTION exercise = FinEquityTreeExerciseTypes.EUROPEAN params = np.array([1.0, strikePrice]) testCases.header("NumSteps", "Results", "TIME") for numSteps in numStepsList: start = time.time() tree = FinEquityBinomialTree() results = tree.value( stockPrice, discountCurve, dividendYield, volatility, numSteps, valueDate, payoff, expiryDate, payoff, exercise, params) end = time.time() duration = end - start testCases.print(numSteps, results, duration) testCases.banner( "================== AMERICAN CALL =======================") payoff = FinEquityTreePayoffTypes.VANILLA_OPTION exercise = FinEquityTreeExerciseTypes.AMERICAN params = np.array([1.0, strikePrice]) testCases.header("NumSteps", "Results", "TIME") for numSteps in numStepsList: start = time.time() tree = FinEquityBinomialTree() results = tree.value( stockPrice, discountCurve, dividendYield, volatility, numSteps, valueDate, payoff, expiryDate, payoff, exercise, params) end = time.time() duration = end - start testCases.print(numSteps, results, duration)
def test_FinFXVanillaOptionHullExample(): # Example from Hull 4th edition page 284 valuationDate = FinDate(2015, 1, 1) expiryDate = valuationDate.addMonths(4) spotFXRate = 1.60 volatility = 0.1411 domInterestRate = 0.08 forInterestRate = 0.11 model = FinModelBlackScholes(volatility) domDiscountCurve = FinDiscountCurveFlat(valuationDate, domInterestRate) forDiscountCurve = FinDiscountCurveFlat(valuationDate, forInterestRate) numPathsList = [10000, 20000, 40000, 80000, 160000, 320000] testCases.header("NUMPATHS", "VALUE_BS", "VALUE_MC", "TIME") strikeFXRate = 1.60 for numPaths in numPathsList: callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.EUROPEAN_CALL, 1000000, "USD") value = callOption.value(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) start = time.time() valueMC = callOption.valueMC(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model, numPaths) end = time.time() duration = end - start testCases.print(numPaths, value, valueMC, duration) ########################################################################## spotFXRates = np.arange(100, 200, 10) spotFXRates = spotFXRates / 100.0 numPaths = 100000 testCases.header("NUMPATHS", "CALL_VALUE_BS", "CALL_VALUE_MC", "TIME") for spotFXRate in spotFXRates: callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.EUROPEAN_CALL, 1000000, "USD") value = callOption.value(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) start = time.time() valueMC = callOption.valueMC(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model, numPaths) end = time.time() duration = end - start testCases.print(numPaths, value, valueMC, duration) ########################################################################## spotFXRates = np.arange(100, 200, 10) / 100.0 numPaths = 100000 testCases.header("SPOT FX RATE", "PUT_VALUE_BS", "PUT_VALUE_MC", "TIME") for spotFXRate in spotFXRates: putOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.EUROPEAN_PUT, 1000000, "USD") value = putOption.value(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) start = time.time() valueMC = putOption.valueMC(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model, numPaths) end = time.time() duration = end - start testCases.print(spotFXRate, value, valueMC, duration) ########################################################################## spotFXRates = np.arange(100, 200, 10) / 100.0 testCases.header("SPOT FX RATE", "CALL_VALUE_BS", "DELTA_BS", "VEGA_BS", "THETA_BS", "RHO_BS") for spotFXRate in spotFXRates: callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.EUROPEAN_CALL, 1000000, "USD") value = callOption.value(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) delta = callOption.delta(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) vega = callOption.vega(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) theta = callOption.theta(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) # callOption.rho(valueDate,stockPrice, interestRate, # dividendYield, modelType, modelParams) rho = 999 testCases.print(spotFXRate, value, delta, vega, theta, rho) testCases.header("SPOT FX RATE", "PUT_VALUE_BS", "DELTA_BS", "VEGA_BS", "THETA_BS", "RHO_BS") for spotFXRate in spotFXRates: putOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.EUROPEAN_PUT, 1000000, "USD") value = putOption.value(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) delta = putOption.delta(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) vega = putOption.vega(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) theta = putOption.theta(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) # putOption.rho(valueDate,stockPrice, interestRate, dividendYield, # modelType, modelParams) rho = 999 testCases.print(spotFXRate, value, delta, vega, theta, rho) ########################################################################## testCases.header("SPOT FX RATE", "VALUE_BS", "VOL_IN", "IMPLD_VOL") spotFXRates = np.arange(100, 200, 10) / 100.0 for spotFXRate in spotFXRates: callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.EUROPEAN_CALL, 1000000, "USD") value = callOption.value(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] impliedVol = callOption.impliedVolatility(valuationDate, spotFXRate, domDiscountCurve, forDiscountCurve, value) testCases.print(spotFXRate, value, volatility, impliedVol)
def testImpliedVolatility_NEW(): valueDate = FinDate(1, 1, 2015) stockPrice = 100.0 interestRate = 0.05 dividendYield = 0.03 discountCurve = FinDiscountCurveFlat(valueDate, interestRate) dividendCurve = FinDiscountCurveFlat(valueDate, dividendYield) strikes = np.linspace(50, 150, 11) timesToExpiry = [0.003, 0.01, 0.1, 0.5, 1.0, 2.0, 5.0] sigmas = np.arange(1, 100, 5) / 100.0 optionTypes = [FinOptionTypes.EUROPEAN_CALL, FinOptionTypes.EUROPEAN_PUT] testCases.header("OPT_TYPE", "TEXP", "STOCK_PRICE", "STRIKE", "INTRINSIC", "VALUE", "INPUT_VOL", "IMPLIED_VOL") tol = 1e-5 numTests = 0 numFails = 0 for vol in sigmas: model = FinModelBlackScholes(vol) for timeToExpiry in timesToExpiry: expiryDate = valueDate.addYears(timeToExpiry) for strike in strikes: for optionType in optionTypes: option = FinEquityVanillaOption(expiryDate, strike, optionType) value = option.value(valueDate, stockPrice, discountCurve, dividendCurve, model) intrinsic = option.intrinsic(valueDate, stockPrice, discountCurve, dividendCurve) # I remove the cases where the time value is zero # This is arbitrary but 1e-10 seems good enough to me impliedVol = -999 if value - intrinsic > 1e-10: impliedVol = option.impliedVolatility(valueDate, stockPrice, discountCurve, dividendCurve, value) numTests += 1 errVol = np.abs(impliedVol - vol) if errVol > tol: testCases.print(optionType, timeToExpiry, stockPrice, strike, intrinsic, value, vol, impliedVol) # These fails include ones due to the zero time value numFails += 1 testCases.print(optionType, timeToExpiry, stockPrice, strike, stockPrice, value, vol, impliedVol) assert numFails == 694, "Num Fails has changed."
def test_FinEquityOneTouchOption(): # Examples Haug Page 180 Table 4-22 # Agreement not exact at t is not exactly 0.50 valueDate = FinDate(1, 1, 2016) expiryDate = FinDate(2, 7, 2016) interestRate = 0.10 volatility = 0.20 barrierLevel = 100.0 # H model = FinModelBlackScholes(volatility) dividendYield = 0.03 numPaths = 10000 numStepsPerYear = 252 discountCurve = FinDiscountCurveFlat(valueDate, interestRate) dividendCurve = FinDiscountCurveFlat(valueDate, dividendYield) stockPrice = 105.0 paymentSize = 15.0 testCases.header("================================= CASH ONLY") downTypes = [ FinTouchOptionPayoffTypes.DOWN_AND_IN_CASH_AT_HIT, FinTouchOptionPayoffTypes.DOWN_AND_IN_CASH_AT_EXPIRY, FinTouchOptionPayoffTypes.DOWN_AND_OUT_CASH_OR_NOTHING ] testCases.header("TYPE", "VALUE", "VALUE_MC") for downType in downTypes: option = FinEquityOneTouchOption(expiryDate, downType, barrierLevel, paymentSize) v = option.value(valueDate, stockPrice, discountCurve, dividendCurve, model) v_mc = option.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, model, numStepsPerYear, numPaths) testCases.print("%60s " % downType, "%9.5f" % v, "%9.5f" % v_mc) stockPrice = 95.0 paymentSize = 15.0 upTypes = [ FinTouchOptionPayoffTypes.UP_AND_IN_CASH_AT_HIT, FinTouchOptionPayoffTypes.UP_AND_IN_CASH_AT_EXPIRY, FinTouchOptionPayoffTypes.UP_AND_OUT_CASH_OR_NOTHING ] testCases.header("TYPE", "VALUE", "VALUE_MC") for upType in upTypes: option = FinEquityOneTouchOption(expiryDate, upType, barrierLevel, paymentSize) v = option.value(valueDate, stockPrice, discountCurve, dividendCurve, model) v_mc = option.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, model, numStepsPerYear, numPaths) testCases.print("%60s " % upType, "%9.5f" % v, "%9.5f" % v_mc) ########################################################################### stockPrice = 105.0 testCases.banner("================= ASSET ONLY") downTypes = [ FinTouchOptionPayoffTypes.DOWN_AND_IN_ASSET_AT_HIT, FinTouchOptionPayoffTypes.DOWN_AND_IN_ASSET_AT_EXPIRY, FinTouchOptionPayoffTypes.DOWN_AND_OUT_ASSET_OR_NOTHING ] testCases.header("TYPE", "VALUE", "VALUE_MC") for downType in downTypes: option = FinEquityOneTouchOption(expiryDate, downType, barrierLevel) v = option.value(valueDate, stockPrice, discountCurve, dividendCurve, model) v_mc = option.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, model, numStepsPerYear, numPaths) testCases.print("%60s " % downType, "%9.5f" % v, "%9.5f" % v_mc) stockPrice = 95.0 upTypes = [ FinTouchOptionPayoffTypes.UP_AND_IN_ASSET_AT_HIT, FinTouchOptionPayoffTypes.UP_AND_IN_ASSET_AT_EXPIRY, FinTouchOptionPayoffTypes.UP_AND_OUT_ASSET_OR_NOTHING ] for upType in upTypes: option = FinEquityOneTouchOption(expiryDate, upType, barrierLevel) v = option.value(valueDate, stockPrice, discountCurve, dividendCurve, model) v_mc = option.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, model, numStepsPerYear, numPaths) testCases.print("%60s " % upType, "%9.5f" % v, "%9.5f" % v_mc)
def test_FinFXBarrierOption(): valueDate = FinDate(1, 1, 2015) expiryDate = FinDate(1, 1, 2016) spotFXRate = 100.0 currencyPair = "USDJPY" volatility = 0.20 domInterestRate = 0.05 forInterestRate = 0.02 optionType = FinFXBarrierTypes.DOWN_AND_OUT_CALL notional = 100.0 notionalCurrency = "USD" drift = domInterestRate - forInterestRate scheme = FinGBMNumericalScheme.ANTITHETIC processType = FinProcessTypes.GBM domDiscountCurve = FinDiscountCurveFlat(valueDate, domInterestRate) forDiscountCurve = FinDiscountCurveFlat(valueDate, forInterestRate) model = FinModelBlackScholes(volatility) ########################################################################### import time start = time.time() numObservationsPerYear = 100 for optionType in FinFXBarrierTypes: testCases.header("Type", "K", "B", "S", "Value", "ValueMC", "TIME", "Diff") for spotFXRate in range(60, 140, 10): B = 110.0 K = 100.0 option = FinFXBarrierOption(expiryDate, K, currencyPair, optionType, B, numObservationsPerYear, notional, notionalCurrency) value = option.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) start = time.time() modelParams = (spotFXRate, drift, volatility, scheme) valueMC = option.valueMC(valueDate, spotFXRate, domInterestRate, processType, modelParams) end = time.time() timeElapsed = round(end - start, 3) diff = valueMC - value testCases.print(optionType, K, B, spotFXRate, value, valueMC, timeElapsed, diff) for spotFXRate in range(60, 140, 10): B = 100.0 K = 110.0 option = FinFXBarrierOption(expiryDate, K, currencyPair, optionType, B, numObservationsPerYear, notional, notionalCurrency) value = option.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) start = time.time() modelParams = (spotFXRate, drift, volatility, scheme) valueMC = option.valueMC(valueDate, spotFXRate, domInterestRate, processType, modelParams) end = time.time() timeElapsed = round(end - start, 3) diff = valueMC - value testCases.print(optionType, K, B, spotFXRate, value, valueMC, timeElapsed, diff) end = time.time() ########################################################################## spotFXRates = range(50, 150, 50) B = 105.0 testCases.header("Type", "K", "B", "S:", "Value", "Delta", "Vega", "Theta") for optionType in FinFXBarrierTypes: for spotFXRate in spotFXRates: barrierOption = FinFXBarrierOption(expiryDate, 100.0, currencyPair, optionType, B, numObservationsPerYear, notional, notionalCurrency) value = barrierOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) delta = barrierOption.delta(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) vega = barrierOption.vega(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) theta = barrierOption.theta(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) testCases.print(optionType, K, B, spotFXRate, value, delta, vega, theta)
def testMCTimings(): valueDate = FinDate(2014, 1, 1) startAveragingDate = FinDate(2014, 6, 1) expiryDate = FinDate(2015, 1, 1) stockPrice = 100.0 volatility = 0.20 interestRate = 0.30 dividendYield = 0.10 numObservations = 120 # daily as we have a half year accruedAverage = None K = 100 seed = 1976 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) asianOption = FinEquityAsianOption(startAveragingDate, expiryDate, K, FinOptionTypes.EUROPEAN_CALL, numObservations) testCases.header("NUMPATHS", "VALUE", "TIME", "VALUE_MC", "TIME", "VALUE_MC_CV", "TIME") valuesMC = [] valuesMC_fast = [] valuesMC_fast_CV = [] tvaluesMC = [] tvaluesMC_fast = [] tvaluesMC_fast_CV = [] numPathsList = [5000] for numPaths in numPathsList: accruedAverage = stockPrice * 1.1 start = time.time() valueMC = asianOption.valueMC(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, accruedAverage) end = time.time() t_MC = end - start start = time.time() valueMC_fast = asianOption._valueMC_fast(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, accruedAverage) end = time.time() t_MC_fast = end - start start = time.time() valueMC_fast_CV = asianOption.valueMC(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, accruedAverage) end = time.time() t_MC_fast_CV = end - start valuesMC.append(valueMC) valuesMC_fast.append(valueMC_fast) valuesMC_fast_CV.append(valueMC_fast_CV) tvaluesMC.append(t_MC) tvaluesMC_fast.append(t_MC_fast) tvaluesMC_fast_CV.append(t_MC_fast_CV) testCases.print(numPaths, valueMC, t_MC, valueMC_fast, t_MC_fast, valueMC_fast_CV, t_MC_fast_CV)
def testConvergence(): valueDate = FinDate(2014, 1, 1) startAveragingDate = FinDate(2014, 6, 1) expiryDate = FinDate(2015, 1, 1) stockPrice = 100.0 volatility = 0.20 interestRate = 0.30 dividendYield = 0.10 numObservations = 120 # daily as we have a half year accruedAverage = None K = 100 seed = 1976 model = FinModelBlackScholes(volatility) discountCurve = FinDiscountCurveFlat(valueDate, interestRate) asianOption = FinEquityAsianOption(startAveragingDate, expiryDate, K, FinOptionTypes.EUROPEAN_CALL, numObservations) testCases.header("K", "Geometric", "Turnbull_Wakeman", "Curran", "FastMC", "FastMC_CV") valuesTurnbull = [] valuesCurran = [] valuesGeometric = [] valuesMC_fast = [] valuesMC_CV = [] numPathsList = [5000] for numPaths in numPathsList: accruedAverage = stockPrice * 1.1 valueMC_fast = asianOption._valueMC_fast(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, accruedAverage) valueMC_CV = asianOption.valueMC(valueDate, stockPrice, discountCurve, dividendYield, model, numPaths, seed, accruedAverage) valueGeometric = asianOption.value( valueDate, stockPrice, discountCurve, dividendYield, model, FinAsianOptionValuationMethods.GEOMETRIC, accruedAverage) valueTurnbullWakeman = asianOption.value( valueDate, stockPrice, discountCurve, dividendYield, model, FinAsianOptionValuationMethods.TURNBULL_WAKEMAN, accruedAverage) valueCurran = asianOption.value(valueDate, stockPrice, discountCurve, dividendYield, model, FinAsianOptionValuationMethods.CURRAN, accruedAverage) valuesGeometric.append(valueGeometric) valuesTurnbull.append(valueTurnbullWakeman) valuesCurran.append(valueCurran) valuesMC_fast.append(valueMC_fast) valuesMC_CV.append(valueMC_CV) testCases.print(numPaths, valueGeometric, valueTurnbullWakeman, valueCurran, valueMC_fast, valueMC_CV)
def test_FinEquityBarrierOption(): valueDate = FinDate(2015, 1, 1) expiryDate = FinDate(2016, 1, 1) stockPrice = 100.0 volatility = 0.20 interestRate = 0.05 dividendYield = 0.02 optionType = FinEquityBarrierTypes.DOWN_AND_OUT_CALL drift = interestRate - dividendYield scheme = FinGBMNumericalScheme.NORMAL processType = FinProcessTypes.GBM discountCurve = FinDiscountCurveFlat(valueDate, interestRate) dividendCurve = FinDiscountCurveFlat(valueDate, dividendYield) model = FinModelBlackScholes(volatility) ####################################################################### import time start = time.time() numObservationsPerYear = 100 testCases.header("Type", "K", "B", "S:", "Value:", "ValueMC", "Diff", "TIME") for optionType in FinEquityBarrierTypes: for stockPrice in range(80, 120, 10): B = 110.0 K = 100.0 option = FinEquityBarrierOption(expiryDate, K, optionType, B, numObservationsPerYear) value = option.value(valueDate, stockPrice, discountCurve, dividendCurve, model) start = time.time() modelParams = (stockPrice, drift, volatility, scheme) valueMC = option.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, processType, modelParams) end = time.time() timeElapsed = round(end - start, 3) diff = valueMC - value testCases.print(optionType, K, B, stockPrice, value, valueMC, diff, timeElapsed) for stockPrice in range(80, 120, 10): B = 100.0 K = 110.0 option = FinEquityBarrierOption(expiryDate, K, optionType, B, numObservationsPerYear) value = option.value(valueDate, stockPrice, discountCurve, dividendCurve, model) start = time.time() modelParams = (stockPrice, drift, volatility, scheme) valueMC = option.valueMC(valueDate, stockPrice, discountCurve, dividendCurve, processType, modelParams) end = time.time() timeElapsed = round(end - start, 3) diff = valueMC - value testCases.print(optionType, K, B, stockPrice, value, valueMC, diff, timeElapsed) end = time.time() ########################################################################## stockPrices = range(50, 150, 50) B = 105.0 testCases.header("Type", "K", "B", "S:", "Value", "Delta", "Vega", "Theta") for optionType in FinEquityBarrierTypes: for stockPrice in stockPrices: barrierOption = FinEquityBarrierOption(expiryDate, 100.0, optionType, B, numObservationsPerYear) value = barrierOption.value(valueDate, stockPrice, discountCurve, dividendCurve, model) delta = barrierOption.delta(valueDate, stockPrice, discountCurve, dividendCurve, model) vega = barrierOption.vega(valueDate, stockPrice, discountCurve, dividendCurve, model) theta = barrierOption.theta(valueDate, stockPrice, discountCurve, dividendCurve, model) testCases.print(optionType, K, B, stockPrice, value, delta, vega, theta)
def testFinEquityAmericanOption(): valueDate = FinDate(2016, 1, 1) expiryDate = FinDate(2017, 1, 1) stockPrice = 50.0 interestRate = 0.06 dividendYield = 0.04 volatility = 0.40 strikePrice = 50.0 discountCurve = FinDiscountCurveFlat(valueDate, interestRate) testCases.banner("================== EUROPEAN PUT =======================") putOption = FinEquityAmericanOption(expiryDate, strikePrice, FinOptionTypes.EUROPEAN_PUT) model = FinModelBlackScholes(volatility, FinModelBlackScholesTypes.CRR_TREE, {'numStepsPerYear': 100}) value = putOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) delta = putOption.delta(valueDate, stockPrice, discountCurve, dividendYield, model) gamma = putOption.gamma(valueDate, stockPrice, discountCurve, dividendYield, model) theta = putOption.theta(valueDate, stockPrice, discountCurve, dividendYield, model) testCases.header("OPTION_TYPE", "VALUE", "DELTA", "GAMMA", "THETA") testCases.print("EUROPEAN_PUT_BS", value, delta, gamma, theta) option = FinEquityAmericanOption(expiryDate, strikePrice, FinOptionTypes.EUROPEAN_PUT) testCases.header("OPTION_TYPE", "NUMSTEPS", "VALUE DELTA GAMMA THETA", "TIME") numStepsList = [100, 200, 500, 1000, 2000] for numSteps in numStepsList: model = FinModelBlackScholes(volatility, FinModelBlackScholesTypes.CRR_TREE, {'numStepsPerYear': numSteps}) start = time.time() results = option.value(valueDate, stockPrice, discountCurve, dividendYield, model) end = time.time() duration = end - start testCases.print("EUROPEAN_PUT_TREE", numSteps, results, duration) testCases.banner("================== AMERICAN PUT =======================") option = FinEquityAmericanOption(expiryDate, strikePrice, FinOptionTypes.AMERICAN_PUT) testCases.header("OPTION_TYPE", "NUMSTEPS", "VALUE DELTA GAMMA THETA", "TIME") for numSteps in numStepsList: model = FinModelBlackScholes(volatility, FinModelBlackScholesTypes.CRR_TREE, {'numStepsPerYear': numSteps}) start = time.time() results = option.value(valueDate, stockPrice, discountCurve, dividendYield, model) end = time.time() duration = end - start testCases.print("AMERICAN_PUT", numSteps, results, duration) testCases.banner( "================== EUROPEAN CALL =======================") callOption = FinEquityAmericanOption(expiryDate, strikePrice, FinOptionTypes.EUROPEAN_CALL) value = callOption.value(valueDate, stockPrice, discountCurve, dividendYield, model) delta = callOption.delta(valueDate, stockPrice, discountCurve, dividendYield, model) gamma = callOption.gamma(valueDate, stockPrice, discountCurve, dividendYield, model) theta = callOption.theta(valueDate, stockPrice, discountCurve, dividendYield, model) testCases.header("OPTION_TYPE", "VALUE", "DELTA", "GAMMA", "THETA") testCases.print("EUROPEAN_CALL_BS", value, delta, gamma, theta) option = FinEquityAmericanOption(expiryDate, strikePrice, FinOptionTypes.EUROPEAN_CALL) testCases.header("OPTION_TYPE", "NUMSTEPS", "VALUE DELTA GAMMA THETA", "TIME") for numSteps in numStepsList: model = FinModelBlackScholes(volatility, FinModelBlackScholesTypes.CRR_TREE, {'numStepsPerYear': numSteps}) start = time.time() results = option.value(valueDate, stockPrice, discountCurve, dividendYield, model) end = time.time() duration = end - start testCases.print("EUROPEAN_CALL_TREE", numSteps, results, duration) testCases.banner( "================== AMERICAN CALL =======================") testCases.header("OPTION_TYPE", "NUMSTEPS", "VALUE DELTA GAMMA THETA", "TIME") option = FinEquityAmericanOption(expiryDate, strikePrice, FinOptionTypes.AMERICAN_CALL) for numSteps in numStepsList: model = FinModelBlackScholes(volatility, FinModelBlackScholesTypes.CRR_TREE, {'numStepsPerYear': numSteps}) start = time.time() results = option.value(valueDate, stockPrice, discountCurve, dividendYield, model) end = time.time() duration = end - start testCases.print("AMERICAN_CALL", numSteps, results, duration)
def test_FinFXOptionSABR(): # UNFINISHED # There is no FXAmericanOption class. It is embedded in the FXVanillaOption # class. This test just compares it to the European valueDate = FinDate(13, 2, 2018) expiryDate = FinDate(13, 2, 2019) # In BS the FX rate is the price in domestic of one unit of foreign # In case of EURUSD = 1.3 the domestic currency is USD and foreign is EUR # DOM = USD , FOR = EUR ccy1CCRate = 0.030 # EUR ccy2CCRate = 0.025 # USD spotFXRate = 1.20 strikeFXRate = 1.250 volatility = 0.10 notional = 1000000.0 domDiscountCurve = FinDiscountCurveFlat(valueDate, ccy2CCRate) forDiscountCurve = FinDiscountCurveFlat(valueDate, ccy1CCRate) model = FinModelBlackScholes(volatility) # Two examples to show that changing the notional currency and notional # keeps the value unchanged notional = 1000000.0 spotFXRates = np.arange(50, 200, 10) / 100.0 testCases.header("OPTION", "FX_RATE", "VALUE_BS", "VOL_IN", "DIFF") for spotFXRate in spotFXRates: callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.EUROPEAN_CALL, notional, "USD") valueEuropean = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.AMERICAN_CALL, 1000000, "USD") valueAmerican = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] diff = (valueAmerican - valueEuropean) testCases.print("CALL:", "%9.6f" % spotFXRate, "%9.7f" % valueEuropean, "%9.7f" % valueAmerican, "%9.7f" % diff) testCases.header("OPTION", "FX_RATE", "VALUE_BS", "VOL_IN", "DIFF") for spotFXRate in spotFXRates: callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.EUROPEAN_PUT, 1000000, "USD") valueEuropean = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] callOption = FinFXVanillaOption(expiryDate, strikeFXRate, "EURUSD", FinOptionTypes.AMERICAN_PUT, 1000000, "USD") valueAmerican = callOption.value(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] diff = (valueAmerican - valueEuropean) testCases.print("PUT:", "%9.6f" % spotFXRate, "%9.7f" % valueEuropean, "%9.7f" % valueAmerican, "%9.7f" % diff)
def testImpliedVolatility(): valueDate = FinDate(1, 1, 2015) stockPrice = 100 interestRate = 0.05 dividendYield = 0.01 discountCurve = FinDiscountCurveFlat(valueDate, interestRate) strikes = [10, 20, 50, 100, 150, 200] timesToExpiry = [0.003, 0.01, 0.1, 0.5, 1.0, 2.0, 5.0] expiryDates = valueDate.addYears(timesToExpiry) sigmas = [0.01, 0.10, 0.50, 1.0] optionTypes = [FinOptionTypes.EUROPEAN_CALL, FinOptionTypes.EUROPEAN_PUT] testCases.header("OPT_TYPE", "EXP_DATE", "STRIKE", "STOCK_PRICE", "VALUE", "INPUT_VOL", "IMPLIED_VOL") tol = 1e-6 numTests = 0 numFails = 0 for vol in sigmas: model = FinModelBlackScholes(vol) for expiryDate in expiryDates: for strike in strikes: for optionType in optionTypes: option = FinEquityVanillaOption(expiryDate, 100.0, optionType) value = option.value(valueDate, stockPrice, discountCurve, dividendYield, model) impliedVol = option.impliedVolatility( valueDate, stockPrice, discountCurve, dividendYield, value) numTests += 1 if np.abs(impliedVol - vol) > tol: numFails += 1 # print(optionType, expiryDate, strike, # stockPrice, value, vol, impliedVol) testCases.print(optionType, expiryDate, strike, stockPrice, value, vol, impliedVol) print("Num Tests", numTests, "numFails", numFails) ############################################################################### K = [10, 20, 50, 100, 150, 200] T = [0.003, 0.01, 0.1, 0.5, 1.0, 2.0, 5.0] sigma = [0.01, 0.10, 0.50, 1.0] optionTypes = [FinOptionTypes.EUROPEAN_CALL, FinOptionTypes.EUROPEAN_PUT] stockPrice = 100 HOURS_PER_YEAR = 365.25 * 24 convergenceFailure = 0 assertionFailure = 0 noResult = 0 successful = 0 numberTests = 0 totalElapsedTime = 0. for t in T: expDate = valueDate.addHours(t * HOURS_PER_YEAR) for k in K: for vol in sigma: bs_model = FinModelBlackScholes(vol) for type_ in optionTypes: option = FinEquityVanillaOption(expDate, k, type_) value = option.value(valueDate, stockPrice, discountCurve, dividendYield, bs_model) if value < 1e-10: continue try: start_time = time.time() impliedVol = option.impliedVolatility_v2( valueDate, stockPrice, discountCurve, dividendYield, value) assert abs(impliedVol - vol) < 0.10 except FinError: noResult += 1 except AssertionError: assertionFailure += 1 print("-----------------") print( f"Did not converge to expected value: {round(impliedVol, 2)} vs {vol}" ) print("INPUTS\n", "Type:", type_.name, "ttm:", t, "strike:", k, "Expected IV:", vol, "BS Price:", value) except RuntimeError: import traceback traceback.print_exc() convergenceFailure += 1 print("INPUTS\n", "Type:", type_.name, "ttm:", t, "strike:", k, "Expected IV:", vol) except Exception: import traceback traceback.print_exc() noResult += 1 else: successful += 1 totalElapsedTime += time.time() - start_time finally: numberTests += 1 print("\nSuccessful:", successful) print("Convergence failure:", convergenceFailure) print("Inaccurate result:", assertionFailure) print("No result (price too low)", noResult) print("TOTAL:", numberTests) print("Mean time:", 1e6 * totalElapsedTime / successful, "us")