Example #1
0
def test_FinFXVanillaOptionBloombergExample():

    # 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(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 = valueDate.addWorkDays(spotDays)
    maturityDate = settlementDate.addMonths(12)
    notional = 1000000.0
    notionalCurrency = "EUR"
    calendarType = FinCalendarTypes.TARGET

    depos = []
    fras = []
    swaps = []
    depo = FinLiborDeposit(settlementDate, maturityDate, domDepoRate,
                           FinDayCountTypes.ACT_360, notional, calendarType)
    depos.append(depo)
    domDiscountCurve = FinLiborCurve(forName, settlementDate, depos, fras,
                                     swaps)

    depos = []
    fras = []
    swaps = []
    depo = FinLiborDeposit(settlementDate, maturityDate, forDepoRate,
                           FinDayCountTypes.ACT_360, notional, calendarType)
    depos.append(depo)
    forDiscountCurve = FinLiborCurve(domName, settlementDate, depos, fras,
                                     swaps)

    model = FinFXModelBlackScholes(volatility)

    callOption = FinFXVanillaOption(expiryDate, strikeFXRate, currencyPair,
                                    FinOptionTypes.EUROPEAN_CALL, notional,
                                    notionalCurrency, 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)
Example #2
0
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

    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
    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)
Example #3
0
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)
Example #4
0
def test_FinFXVanillaOptionHullExample():

    #   Example from Hull 4th edition page 284
    valuation_date = Date(1, 1, 2015)
    expiry_date = valuation_date.addMonths(4)
    spotFXRate = 1.60
    volatility = 0.1411
    domInterestRate = 0.08
    forInterestRate = 0.11
    model = FinModelBlackScholes(volatility)
    domDiscountCurve = DiscountCurveFlat(valuation_date, domInterestRate)
    forDiscountCurve = DiscountCurveFlat(valuation_date, forInterestRate)

    num_pathsList = [10000, 20000, 40000, 80000, 160000, 320000]

    testCases.header("NUMPATHS", "VALUE_BS", "VALUE_MC", "TIME")
    strikeFXRate = 1.60

    for num_paths in num_pathsList:

        callOption = FinFXVanillaOption(expiry_date, strikeFXRate, "EURUSD",
                                        FinOptionTypes.EUROPEAN_CALL, 1000000,
                                        "USD")

        value = callOption.value(valuation_date, spotFXRate, domDiscountCurve,
                                 forDiscountCurve, model)

        start = time.time()

        valueMC = callOption.valueMC(valuation_date, spotFXRate,
                                     domDiscountCurve, forDiscountCurve, model,
                                     num_paths)

        end = time.time()
        duration = end - start
        testCases.print(num_paths, value, valueMC, duration)

##########################################################################

    spotFXRates = np.arange(100, 200, 10)
    spotFXRates = spotFXRates / 100.0
    num_paths = 100000

    testCases.header("NUMPATHS", "CALL_VALUE_BS", "CALL_VALUE_MC", "TIME")

    for spotFXRate in spotFXRates:

        callOption = FinFXVanillaOption(expiry_date, strikeFXRate, "EURUSD",
                                        FinOptionTypes.EUROPEAN_CALL, 1000000,
                                        "USD")

        value = callOption.value(valuation_date, spotFXRate, domDiscountCurve,
                                 forDiscountCurve, model)
        start = time.time()
        valueMC = callOption.valueMC(valuation_date, spotFXRate,
                                     domDiscountCurve, forDiscountCurve, model,
                                     num_paths)
        end = time.time()
        duration = end - start
        testCases.print(num_paths, value, valueMC, duration)

##########################################################################

    spotFXRates = np.arange(100, 200, 10) / 100.0
    num_paths = 100000

    testCases.header("SPOT FX RATE", "PUT_VALUE_BS", "PUT_VALUE_MC", "TIME")

    for spotFXRate in spotFXRates:

        putOption = FinFXVanillaOption(expiry_date, strikeFXRate, "EURUSD",
                                       FinOptionTypes.EUROPEAN_PUT, 1000000,
                                       "USD")

        value = putOption.value(valuation_date, spotFXRate, domDiscountCurve,
                                forDiscountCurve, model)
        start = time.time()
        valueMC = putOption.valueMC(valuation_date, spotFXRate,
                                    domDiscountCurve, forDiscountCurve, model,
                                    num_paths)
        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(expiry_date, strikeFXRate, "EURUSD",
                                        FinOptionTypes.EUROPEAN_CALL, 1000000,
                                        "USD")
        value = callOption.value(valuation_date, spotFXRate, domDiscountCurve,
                                 forDiscountCurve, model)
        delta = callOption.delta(valuation_date, spotFXRate, domDiscountCurve,
                                 forDiscountCurve, model)
        vega = callOption.vega(valuation_date, spotFXRate, domDiscountCurve,
                               forDiscountCurve, model)
        theta = callOption.theta(valuation_date, spotFXRate, domDiscountCurve,
                                 forDiscountCurve, model)
        #  callOption.rho(valuation_date,stock_price, 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(expiry_date, strikeFXRate, "EURUSD",
                                       FinOptionTypes.EUROPEAN_PUT, 1000000,
                                       "USD")

        value = putOption.value(valuation_date, spotFXRate, domDiscountCurve,
                                forDiscountCurve, model)
        delta = putOption.delta(valuation_date, spotFXRate, domDiscountCurve,
                                forDiscountCurve, model)
        vega = putOption.vega(valuation_date, spotFXRate, domDiscountCurve,
                              forDiscountCurve, model)
        theta = putOption.theta(valuation_date, spotFXRate, domDiscountCurve,
                                forDiscountCurve, model)
        # putOption.rho(valuation_date,stock_price, 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(expiry_date, strikeFXRate, "EURUSD",
                                        FinOptionTypes.EUROPEAN_CALL, 1000000,
                                        "USD")

        value = callOption.value(valuation_date, spotFXRate, domDiscountCurve,
                                 forDiscountCurve, model)['v']

        impliedVol = callOption.impliedVolatility(valuation_date, spotFXRate,
                                                  domDiscountCurve,
                                                  forDiscountCurve, value)

        testCases.print(spotFXRate, value, volatility, impliedVol)
Example #5
0
        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('-', '_')]
Example #6
0
        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('-', '_')]