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 = FinFXModelBlackScholes(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 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 = FinFXModelBlackScholes(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_FinFXBarrierOption(): valueDate = FinDate(2015, 1, 1) expiryDate = FinDate(2016, 1, 1) 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 = FinFXModelBlackScholes(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 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 = FinFXModelBlackScholes(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 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 spotDays = 0 settlementDate = valueDate.addWorkDays(spotDays) maturityDate = settlementDate.addMonths(12) notional = 1000000.0 notionalCurrency = "EUR" calendarType = FinCalendarTypes.TARGET domDiscountCurve = FinFlatCurve(valueDate, ccy2CCRate) forDiscountCurve = FinFlatCurve(valueDate, ccy1CCRate) model = FinFXModelBlackScholes(volatility) # Two examples to show that changing the notional currency and notional # keeps the value unchanged notional = 1000000.0 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, "EURUSD", 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) print("CALL %9.6f %9.6f %9.7f %10.8f" % (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) print("PUT %9.6f %9.6f %9.7f %10.8f" % (spotFXRate, valueEuropean, valueAmerican, diff))
def test_FinFXVanillaOptionHullExample(): # Example from Hull 4th edition page 284 valueDate = FinDate(2015, 1, 1) expiryDate = valueDate.addMonths(4) spotFXRate = 1.60 volatility = 0.1411 domInterestRate = 0.08 forInterestRate = 0.11 model = FinFXModelBlackScholes(volatility) domDiscountCurve = FinDiscountCurveFlat(valueDate, domInterestRate) forDiscountCurve = FinDiscountCurveFlat(valueDate, 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(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) start = time.time() valueMC = callOption.valueMC(valueDate, 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(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) start = time.time() valueMC = callOption.valueMC(valueDate, 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(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) start = time.time() valueMC = putOption.valueMC(valueDate, 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(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) delta = callOption.delta(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) vega = callOption.vega(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) theta = callOption.theta(valueDate, 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(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) delta = putOption.delta(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) vega = putOption.vega(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model) theta = putOption.theta(valueDate, 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(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, model)['v'] impliedVol = callOption.impliedVolatility(valueDate, spotFXRate, domDiscountCurve, forDiscountCurve, value) testCases.print(spotFXRate, value, volatility, impliedVol)
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 = FinFXModelBlackScholes(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)