Exemplo n.º 1
0
def test_FinFXMktVolSurface5(verboseCalibration):

    ###########################################################################
    # Here I remove the 10D Vols
    ###########################################################################
    
    if 1 == 1:

        # Example from Book extract by Iain Clark using Tables 3.3 and 3.4
        # print("EURUSD EXAMPLE CLARK")

        valueDate = FinDate(10, 4, 2020)

        forName = "EUR"
        domName = "USD"
        forCCRate = 0.03460  # EUR
        domCCRate = 0.02940  # USD

        domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate)
        forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate)

        currencyPair = forName + domName
        spotFXRate = 1.3465

        tenors = ['1M', '2M', '3M', '6M', '1Y', '2Y']
        atmVols = [21.00, 21.00, 20.750, 19.400, 18.250, 17.677]
        marketStrangle25DeltaVols = [0.65, 0.75, 0.85, 0.90, 0.95, 0.85]
        riskReversal25DeltaVols = [-0.20, -0.25, -0.30, -0.50, -0.60, -0.562]
        marketStrangle10DeltaVols = [2.433, 2.83, 3.228, 3.485, 3.806, 3.208]
        riskReversal10DeltaVols = [-1.258, -1.297, -1.332, -1.408, -1.359, -1.208]

        marketStrangle10DeltaVols = None
        riskReversal10DeltaVols = None
        
        notionalCurrency = forName

        atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL
        deltaMethod = FinFXDeltaMethod.SPOT_DELTA
        volFunctionType = FinVolFunctionTypes.CLARK
        alpha = 0.50 # FIT WINGS AT 10D if ALPHA = 1.0

        fxMarketPlus = FinFXVolSurfacePlus(valueDate,
                                       spotFXRate,
                                       currencyPair,
                                       notionalCurrency,
                                       domDiscountCurve,
                                       forDiscountCurve,
                                       tenors,
                                       atmVols,
                                       marketStrangle25DeltaVols,
                                       riskReversal25DeltaVols,
                                       marketStrangle10DeltaVols,
                                       riskReversal10DeltaVols,
                                       alpha,
                                       atmMethod,
                                       deltaMethod, 
                                       volFunctionType)

        fxMarketPlus.checkCalibration(False)
Exemplo n.º 2
0
def test_FinFXMktVolSurface2(verboseCalibration):

    #print("==============================================================")

    # Example from Book extract by Iain Clarke using Tables 3.3 and 3.4
    # print("EURJPY EXAMPLE CLARK")

    valueDate = FinDate(10, 4, 2020)

    forName = "EUR"
    domName = "JPY"
    forCCRate = 0.0294  # EUR
    domCCRate = 0.0171  # USD

    domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate)
    forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate)

    currencyPair = forName + domName
    spotFXRate = 90.72

    tenors = ['1M', '2M', '3M', '6M', '1Y', '2Y']
    atmVols = [21.50, 20.50, 19.85, 18.00, 15.95, 14.009]
    marketStrangle25DeltaVols = [0.35, 0.325, 0.300, 0.225, 0.175, 0.100]
    riskReversal25DeltaVols = [-8.350, -8.650, -8.950, -9.250, -9.550, -9.500]
    marketStrangle10DeltaVols = [3.704, 4.047, 4.396, 4.932, 5.726, 5.709]
    riskReversal10DeltaVols = [
        -15.855, -16.467, -17.114, -17.882, -18.855, -18.217
    ]
    alpha = 0.50  # Equally fit 10 and 25 Delta

    notionalCurrency = forName

    atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL_PREM_ADJ
    deltaMethod = FinFXDeltaMethod.SPOT_DELTA_PREM_ADJ
    volFunctionType = FinVolFunctionTypes.CLARK5

    fxMarketPlus = FinFXVolSurfacePlus(
        valueDate, spotFXRate, currencyPair, notionalCurrency,
        domDiscountCurve, forDiscountCurve, tenors, atmVols,
        marketStrangle25DeltaVols, riskReversal25DeltaVols,
        marketStrangle10DeltaVols, riskReversal10DeltaVols, alpha, atmMethod,
        deltaMethod, volFunctionType)

    #        fxMarketPlus.checkCalibration(True)

    if PLOT_GRAPHS:
        fxMarketPlus.plotVolCurves()

        plt.figure()

        dbns = fxMarketPlus.impliedDbns(30, 120, 1000)

        for i in range(0, len(dbns)):
            plt.plot(dbns[i]._x, dbns[i]._densitydx)
            plt.title(volFunctionType)
            print("SUM:", dbns[i].sum())
Exemplo n.º 3
0
def test_FinFXMktVolSurface2(verboseCalibration):

        #print("==============================================================")

        # Example from Book extract by Iain Clarke using Tables 3.3 and 3.4
        # print("EURJPY EXAMPLE CLARKE")

        valueDate = FinDate(10, 4, 2020)

        forName = "EUR"
        domName = "JPY"
        forCCRate = 0.0294  # EUR
        domCCRate = 0.0171  # USD

        domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate)
        forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate)

        currencyPair = forName + domName
        spotFXRate = 90.72

        tenors = ['1M', '2M', '3M', '6M', '1Y', '2Y']
        atmVols = [21.50, 20.50, 19.85, 18.00, 15.95, 14.009]
        marketStrangle25DeltaVols = [0.35, 0.325, 0.300, 0.225, 0.175, 0.100]
        riskReversal25DeltaVols = [-8.350, -8.650, -8.950, -9.250, -9.550, -9.500]
        marketStrangle10DeltaVols = [3.704, 4.047, 4.396, 4.932, 5.726, 5.709]
        riskReversal10DeltaVols = [-15.855, -16.467, -17.114, -17.882, -18.855, -18.217]

        notionalCurrency = forName

        atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL_PREM_ADJ
        deltaMethod = FinFXDeltaMethod.SPOT_DELTA_PREM_ADJ

        fxMarket = FinFXVolSurfacePlus(valueDate,
                                       spotFXRate,
                                       currencyPair,
                                       notionalCurrency,
                                       domDiscountCurve,
                                       forDiscountCurve,
                                       tenors,
                                       atmVols,
                                       marketStrangle25DeltaVols,
                                       riskReversal25DeltaVols,
                                       marketStrangle10DeltaVols,
                                       riskReversal10DeltaVols,
                                       atmMethod,
                                       deltaMethod)

        fxMarket.checkCalibration(verboseCalibration)
Exemplo n.º 4
0
    def build_vol_surface(self, value_date):
        """Builds the implied volatility surface for a particular value date and calculates the benchmark strikes etc.

        Before we do any sort of interpolation later, we need to build the implied_vol vol surface.

        Parameters
        ----------
        value_date : str
            Value date (need to have market data for this date)

        asset : str
            Asset name

        depo_tenor : str
            Depo tenor to use

            default - '1M'

        field : str
            Market data field to use

            default - 'close'
        """

        self._value_date = self._market_util.parse_date(value_date)

        value_fin_date = self._findate(self._value_date)

        date_index = self._market_df.index == value_date

        # TODO: add whole rates curve
        dom_discount_curve = FinDiscountCurveFlat(value_fin_date,
                                                  self._domCCRate[date_index])
        for_discount_curve = FinDiscountCurveFlat(value_fin_date,
                                                  self._forCCRate[date_index])

        self._dom_discount_curve = dom_discount_curve
        self._for_discount_curve = for_discount_curve

        self._spot = float(self._spot_history[date_index][0])

        # New implementation in FinancePy also uses 10d for interpolation
        self._fin_fx_vol_surface = FinFXVolSurface(
            value_fin_date,
            self._spot,
            self._asset,
            self._asset[0:3],
            dom_discount_curve,
            for_discount_curve,
            self._tenors.copy(),
            self._atm_vols[date_index][0],
            self._market_strangle25DeltaVols[date_index][0],
            self._risk_reversal25DeltaVols[date_index][0],
            self._market_strangle10DeltaVols[date_index][0],
            self._risk_reversal10DeltaVols[date_index][0],
            self._alpha,
            atmMethod=self._atm_method,
            deltaMethod=self._delta_method,
            volatilityFunctionType=self._vol_function_type,
            finSolverType=self._solver)
Exemplo n.º 5
0
def test_FinFXMktVolSurface1(verboseCalibration):

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

    if 1 == 1:

        # Example from Book extract by Iain Clark using Tables 3.3 and 3.4
        # print("EURUSD EXAMPLE CLARK")

        valueDate = FinDate(10, 4, 2020)

        forName = "EUR"
        domName = "USD"
        forCCRate = 0.03460  # EUR
        domCCRate = 0.02940  # USD

        domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate)
        forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate)

        currencyPair = forName + domName
        spotFXRate = 1.3465

        tenors = ['1M', '2M', '3M', '6M', '1Y', '2Y']
        atmVols = [21.00, 21.00, 20.750, 19.400, 18.250, 17.677]
        marketStrangle25DeltaVols = [0.65, 0.75, 0.85, 0.90, 0.95, 0.85]
        riskReversal25DeltaVols = [-0.20, -0.25, -0.30, -0.50, -0.60, -0.562]
        marketStrangle10DeltaVols = [2.433, 2.83, 3.228, 3.485, 3.806, 3.208]
        riskReversal10DeltaVols = [
            -1.258, -1.297, -1.332, -1.408, -1.359, -1.208
        ]

        notionalCurrency = forName

        atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL
        deltaMethod = FinFXDeltaMethod.SPOT_DELTA
        volFunctionType = FinVolFunctionTypes.CLARK5
        alpha = 0.5  # FIT WINGS AT 10D if ALPHA = 1.0

        fxMarketPlus = FinFXVolSurfacePlus(
            valueDate, spotFXRate, currencyPair, notionalCurrency,
            domDiscountCurve, forDiscountCurve, tenors, atmVols,
            marketStrangle25DeltaVols, riskReversal25DeltaVols,
            marketStrangle10DeltaVols, riskReversal10DeltaVols, alpha,
            atmMethod, deltaMethod, volFunctionType)

        fxMarketPlus.checkCalibration(False)

        if 1 == 0:  # PLOT_GRAPHS:

            fxMarketPlus.plotVolCurves()

            plt.figure()

            dbns = fxMarketPlus.impliedDbns(0.5, 2.0, 1000)

            for i in range(0, len(dbns)):
                plt.plot(dbns[i]._x, dbns[i]._densitydx)
                plt.title(volFunctionType)
                print("SUM:", dbns[i].sum())
Exemplo n.º 6
0
def test_FinFXMktVolSurface4(verboseCalibration):

        # USDJPY Example from Paper by Uwe Wystup using Tables 4
#        print("USDJPY EXAMPLE WYSTUP")

        valueDate = FinDate(20, 1, 2009)

        forName = "USD"
        domName = "JPY"
        forCCRate = 0.003525  # USD
        domCCRate = 0.0042875  # JPY

        domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate)
        forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate)

        currencyPair = forName + domName
        spotFXRate = 90.68

        tenors = ['1M']
        atmVols = [21.00]
        marketStrangle25DeltaVols = [0.184]
        riskReversal25DeltaVols = [-5.30]

        notionalCurrency = forName

        atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL
        deltaMethod = FinFXDeltaMethod.SPOT_DELTA_PREM_ADJ

        fxMarket = FinFXVolSurfacePlus(valueDate,
                                   spotFXRate,
                                   currencyPair,
                                   notionalCurrency,
                                   domDiscountCurve,
                                   forDiscountCurve,
                                   tenors,
                                   atmVols,
                                   marketStrangle25DeltaVols,
                                   riskReversal25DeltaVols,
                                   atmMethod,
                                   deltaMethod)

        fxMarket.checkCalibration(verboseCalibration)
Exemplo n.º 7
0
def test_FinFXMktVolSurface3(verboseCalibration):

        # EURUSD Example from Paper by Uwe Wystup using Tables 4
#        print("EURUSD EXAMPLE WYSTUP")

        valueDate = FinDate(20, 1, 2009)

        forName = "EUR"
        domName = "USD"
        forCCRate = 0.020113  # EUR
        domCCRate = 0.003525  # USD

        domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate)
        forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate)

        currencyPair = forName + domName
        spotFXRate = 1.3088

        tenors = ['1M']
        atmVols = [21.6215]
        marketStrangle25DeltaVols = [0.7375]
        riskReversal25DeltaVols = [-0.50]

        notionalCurrency = forName

        atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL
        deltaMethod = FinFXDeltaMethod.SPOT_DELTA

        fxMarket = FinFXVolSurfacePlus(valueDate,
                                   spotFXRate,
                                   currencyPair,
                                   notionalCurrency,
                                   domDiscountCurve,
                                   forDiscountCurve,
                                   tenors,
                                   atmVols,
                                   marketStrangle25DeltaVols,
                                   riskReversal25DeltaVols,
                                   atmMethod,
                                   deltaMethod)

        fxMarket.checkCalibration(verboseCalibration)
Exemplo n.º 8
0
class FXVolSurface(AbstractVolSurface):
    """Holds data for an FX vol surface and also interpolates vol surface, converts strikes to implied vols etc.

    """
    def __init__(
            self,
            market_df=None,
            asset=None,
            field='close',
            tenors=market_constants.fx_options_tenor_for_interpolation,
            vol_function_type=market_constants.fx_options_vol_function_type,
            atm_method=market_constants.fx_options_atm_method,
            delta_method=market_constants.fx_options_delta_method,
            depo_tenor=market_constants.fx_options_depo_tenor,
            solver=market_constants.fx_options_solver,
            alpha=market_constants.fx_options_alpha):
        """Initialises object, with market data and various market conventions

        Parameters
        ----------
        market_df : DataFrame
            Market data with spot, FX volatility surface, FX forwards and base depos

        asset : str
            Eg. 'EURUSD'

        field : str
            Market data field to use

            default - 'close'

        tenors : str(list)
            Tenors to be used

        vol_function_type : str
            What type of interpolation scheme to use
            default - 'CLARK5' (also 'CLARK', 'BBG' and 'SABR')

        atm_method : str
            How is the ATM quoted? Eg. delta neutral, ATMF etc.

            default - 'fwd-delta-neutral-premium-adj'

        delta_method : str
            Spot delta, forward delta etc.

            default - 'spot-delta'

        solver : str
            Which solver to use in FX vol surface calibration?

            default - 'nelmer-mead'

        alpha : float
            Between 0 and 1 (default 0.5)
        """
        self._market_df = market_df
        self._tenors = tenors
        self._asset = asset
        self._field = field
        self._depo_tenor = depo_tenor
        self._market_util = MarketUtil()

        self._dom_discount_curve = None
        self._for_discount_curve = None
        self._spot = None

        self._value_date = None
        self._fin_fx_vol_surface = None
        self._df_vol_dict = None

        for_name_base = asset[0:3]
        dom_name_terms = asset[3:6]

        field = '.' + field

        # CAREFUL: need to divide by 100 for depo rate, ie. 0.0346 = 3.46%
        self._forCCRate = market_df[for_name_base + depo_tenor +
                                    field].values / 100.0  # 0.03460  # EUR
        self._domCCRate = market_df[dom_name_terms + depo_tenor +
                                    field].values / 100.0  # 0.02940  # USD

        self._spot_history = market_df[asset + field].values
        self._atm_vols = market_df[[asset + "V" + t + field
                                    for t in tenors]].values
        self._market_strangle25DeltaVols = market_df[[
            asset + "25B" + t + field for t in tenors
        ]].values
        self._risk_reversal25DeltaVols = market_df[[
            asset + "25R" + t + field for t in tenors
        ]].values
        self._market_strangle10DeltaVols = market_df[[
            asset + "10B" + t + field for t in tenors
        ]].values
        self._risk_reversal10DeltaVols = market_df[[
            asset + "10R" + t + field for t in tenors
        ]].values

        if vol_function_type == 'CLARK':
            self._vol_function_type = FinVolFunctionTypes.CLARK
        elif vol_function_type == 'CLARK5':
            self._vol_function_type = FinVolFunctionTypes.CLARK5
        elif vol_function_type == 'BBG':
            self._vol_function_type = FinVolFunctionTypes.BBG

        # Note: currently SABR isn't fully implemented in FinancePy
        elif vol_function_type == 'SABR':
            self._vol_function_type = FinVolFunctionTypes.SABR
        elif vol_function_type == 'SABR3':
            self._vol_function_type = FinVolFunctionTypes.SABR3

        # What does ATM mean? (for most
        if atm_method == 'fwd-delta-neutral':  # ie. strike such that a straddle would be delta neutral
            self._atm_method = FinFXATMMethod.FWD_DELTA_NEUTRAL
        elif atm_method == 'fwd-delta-neutral-premium-adj':
            self._atm_method = FinFXATMMethod.FWD_DELTA_NEUTRAL_PREM_ADJ
        elif atm_method == 'spot':  # ATM is spot
            self._atm_method = FinFXATMMethod.SPOT
        elif atm_method == 'fwd':  # ATM is forward
            self._atm_method = FinFXATMMethod.FWD

        # How are the deltas quoted?
        if delta_method == 'spot-delta':
            self._delta_method = FinFXDeltaMethod.SPOT_DELTA
        elif delta_method == 'fwd-delta':
            self._delta_method = FinFXDeltaMethod.FORWARD_DELTA
        elif delta_method == 'spot-delta-prem-adj':
            self._delta_method = FinFXDeltaMethod.SPOT_DELTA_PREM_ADJ
        elif delta_method == 'fwd-delta-prem-adj':
            self._delta_method = FinFXDeltaMethod.FORWARD_DELTA_PREM_ADJ

        # Which solver to use in FX vol surface calibration
        if solver == 'nelmer-mead':
            self._solver = FinSolverTypes.NELDER_MEAD
        elif solver == 'nelmer-mead-numba':
            self._solver = FinSolverTypes.NELDER_MEAD_NUMBA
        elif solver == 'cg':
            self._solver = FinSolverTypes.CONJUGATE_GRADIENT

        self._alpha = alpha

    def build_vol_surface(self, value_date):
        """Builds the implied volatility surface for a particular value date and calculates the benchmark strikes etc.

        Before we do any sort of interpolation later, we need to build the implied_vol vol surface.

        Parameters
        ----------
        value_date : str
            Value date (need to have market data for this date)

        asset : str
            Asset name

        depo_tenor : str
            Depo tenor to use

            default - '1M'

        field : str
            Market data field to use

            default - 'close'
        """

        self._value_date = self._market_util.parse_date(value_date)

        value_fin_date = self._findate(self._value_date)

        date_index = self._market_df.index == value_date

        # TODO: add whole rates curve
        dom_discount_curve = FinDiscountCurveFlat(value_fin_date,
                                                  self._domCCRate[date_index])
        for_discount_curve = FinDiscountCurveFlat(value_fin_date,
                                                  self._forCCRate[date_index])

        self._dom_discount_curve = dom_discount_curve
        self._for_discount_curve = for_discount_curve

        self._spot = float(self._spot_history[date_index][0])

        # New implementation in FinancePy also uses 10d for interpolation
        self._fin_fx_vol_surface = FinFXVolSurface(
            value_fin_date,
            self._spot,
            self._asset,
            self._asset[0:3],
            dom_discount_curve,
            for_discount_curve,
            self._tenors.copy(),
            self._atm_vols[date_index][0],
            self._market_strangle25DeltaVols[date_index][0],
            self._risk_reversal25DeltaVols[date_index][0],
            self._market_strangle10DeltaVols[date_index][0],
            self._risk_reversal10DeltaVols[date_index][0],
            self._alpha,
            atmMethod=self._atm_method,
            deltaMethod=self._delta_method,
            volatilityFunctionType=self._vol_function_type,
            finSolverType=self._solver)

    def calculate_vol_for_strike_expiry(self, K, expiry_date=None, tenor='1M'):
        """Calculates the implied_vol volatility for a given strike and tenor (or expiry date, if specified). The
        expiry date/broken dates are intepolated linearly in variance space.

        Parameters
        ----------
        K : float
            Strike for which to find implied_vol volatility

        expiry_date : str (optional)
            Expiry date of option

        tenor : str (optional)
            Tenor of option

            default - '1M'

        Returns
        -------
        float
        """

        if expiry_date is not None:
            expiry_date = self._findate(
                self._market_util.parse_date(expiry_date))
            return self._fin_fx_vol_surface.volatilityFromStrikeDate(
                K, expiry_date)
        else:
            try:
                tenor_index = self._get_tenor_index(tenor)
                return self.get_vol_from_quoted_tenor(K, tenor_index)
            except:
                pass

        return None

    def calculate_vol_for_delta_expiry(self, delta_call, expiry_date=None):
        """Calculates the implied_vol volatility for a given delta call and expiry date. The
        expiry date/broken dates are intepolated linearly in variance space.

        Parameters
        ----------
        delta_call : float
            Delta for the strike for which to find implied volatility

        expiry_date : str (optional)
            Expiry date of option

        Returns
        -------
        float
        """
        if expiry_date is not None:
            expiry_date = self._findate(
                self._market_util.parse_date(expiry_date))
            return self._fin_fx_vol_surface.volatilityFromDeltaDate(
                delta_call, expiry_date)

        return None

    def extract_vol_surface(self, num_strike_intervals=60):
        """Creates an interpolated implied vol surface which can be plotted (in strike space), and also in delta
        space for key strikes (ATM, 25d call and put). Also for key strikes converts from delta to strike space.

        Parameters
        ----------
        num_strike_intervals : int
            Number of points to interpolate

        Returns
        -------
        dict
        """
        ## Modified from FinancePy code for plotting vol curves

        # columns = tenors
        df_vol_surface_strike_space = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)
        df_vol_surface_delta_space = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)

        # columns = tenors
        df_vol_surface_implied_pdf = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)

        # Conversion between main deltas and strikes
        df_deltas_vs_strikes = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)

        # ATM, 10d + 25d market strangle and 25d risk reversals
        df_vol_surface_quoted_points = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)

        # Note, at present we're not using 10d strikes
        quoted_strikes_names = [
            'ATM', 'STR_25D_MS', 'RR_25D_P', 'STR_10D_MS', 'RR_10D_P'
        ]
        key_strikes_names = [
            'K_10D_P', 'K_10D_P_MS', 'K_25D_P', 'K_25D_P_MS', 'ATM', 'K_25D_C',
            'K_25D_C_MS', 'K_10D_C', 'K_10D_C_MS'
        ]

        # Get max/min strikes to interpolate (from the longest dated tenor)
        low_K = self._fin_fx_vol_surface._K_25D_P[-1] * 0.95
        high_K = self._fin_fx_vol_surface._K_25D_C[-1] * 1.05

        if num_strike_intervals is not None:
            # In case using old version of FinancePy
            try:
                implied_pdf_fin_distribution = self._fin_fx_vol_surface.impliedDbns(
                    low_K, high_K, num_strike_intervals)
            except:
                pass

        for tenor_index in range(0, self._fin_fx_vol_surface._numVolCurves):

            # Get the quoted vol points
            tenor_label = self._fin_fx_vol_surface._tenors[tenor_index]

            atm_vol = self._fin_fx_vol_surface._atmVols[tenor_index] * 100
            ms_25d_vol = self._fin_fx_vol_surface._mktStrangle25DeltaVols[
                tenor_index] * 100
            rr_25d_vol = self._fin_fx_vol_surface._riskReversal25DeltaVols[
                tenor_index] * 100
            ms_10d_vol = self._fin_fx_vol_surface._mktStrangle10DeltaVols[
                tenor_index] * 100
            rr_10d_vol = self._fin_fx_vol_surface._riskReversal10DeltaVols[
                tenor_index] * 100

            df_vol_surface_quoted_points[tenor_label] = pd.Series(
                index=quoted_strikes_names,
                data=[atm_vol, ms_25d_vol, rr_25d_vol, ms_10d_vol, rr_10d_vol])

            # Do interpolation in strike space for the implied vols (if intervals have been specified)
            strikes = []
            vols = []

            if num_strike_intervals is not None:
                K = low_K
                dK = (high_K - low_K) / num_strike_intervals

                for i in range(0, num_strike_intervals):
                    sigma = self.get_vol_from_quoted_tenor(K,
                                                           tenor_index) * 100.0
                    strikes.append(K)
                    vols.append(sigma)
                    K = K + dK

                df_vol_surface_strike_space[tenor_label] = pd.Series(
                    index=strikes, data=vols)

            try:
                df_vol_surface_implied_pdf[tenor_label] = pd.Series(
                    index=implied_pdf_fin_distribution[tenor_index]._x,
                    data=implied_pdf_fin_distribution[tenor_index]._densitydx)
            except:
                pass

            # Extract strikes for the quoted points (ie. 10d, 25d and ATM)
            key_strikes = []
            key_strikes.append(self._fin_fx_vol_surface._K_10D_P[tenor_index])
            key_strikes.append(
                self._fin_fx_vol_surface._K_10D_P_MS[tenor_index])
            key_strikes.append(self._fin_fx_vol_surface._K_25D_P[tenor_index])
            key_strikes.append(
                self._fin_fx_vol_surface._K_25D_P_MS[tenor_index])
            key_strikes.append(self._fin_fx_vol_surface._K_ATM[tenor_index])
            key_strikes.append(self._fin_fx_vol_surface._K_25D_C[tenor_index])
            key_strikes.append(
                self._fin_fx_vol_surface._K_25D_C_MS[tenor_index])
            key_strikes.append(self._fin_fx_vol_surface._K_10D_C[tenor_index])
            key_strikes.append(
                self._fin_fx_vol_surface._K_10D_C_MS[tenor_index])

            df_deltas_vs_strikes[tenor_label] = pd.Series(
                index=key_strikes_names, data=key_strikes)

            # Put a conversion between quoted deltas and strikes (eg. which is ATM in strike space, 25d call/put strikes)
            key_vols = []

            for K, name in zip(key_strikes, key_strikes_names):
                sigma = self.get_vol_from_quoted_tenor(K, tenor_index) * 100.0
                key_vols.append(sigma)

            df_vol_surface_delta_space[tenor_label] = pd.Series(
                index=key_strikes_names, data=key_vols)

        df_vol_dict = {}
        df_vol_dict['vol_surface_implied_pdf'] = df_vol_surface_implied_pdf
        df_vol_dict['vol_surface_strike_space'] = df_vol_surface_strike_space
        df_vol_dict['vol_surface_delta_space'] = df_vol_surface_delta_space
        df_vol_dict[
            'vol_surface_delta_space_exc_ms'] = df_vol_surface_delta_space[
                ~df_vol_surface_delta_space.index.str.contains('_MS')]
        df_vol_dict['vol_surface_quoted_points'] = df_vol_surface_quoted_points
        df_vol_dict['deltas_vs_strikes'] = df_deltas_vs_strikes

        self._df_vol_dict = df_vol_dict

        return df_vol_dict

    def get_vol_from_quoted_tenor(self, K, tenor, gaps=None):

        if not (isinstance(tenor, int)):
            tenor_index = self._get_tenor_index(tenor)
        else:
            tenor_index = tenor

        if gaps is None:
            gaps = np.array([0.1])

        params = self._fin_fx_vol_surface._parameters[tenor_index]
        t = self._fin_fx_vol_surface._texp[tenor_index]
        f = self._fin_fx_vol_surface._F0T[tenor_index]

        return volFunction(self._vol_function_type.value, params,
                           np.array([K]), gaps, f, K, t)

    def get_atm_method(self):
        return self._atm_method

    def get_delta_method(self):
        return self._delta_method

    def get_all_market_data(self):
        return self._market_df

    def get_spot(self):
        return self._spot

    def get_atm_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['ATM']

    def get_25d_call_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_25D_C']

    def get_25d_put_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_25D_P']

    def get_10d_call_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_10D_C']

    def get_10d_put_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_10D_P']

    def get_25d_call_ms_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_25D_C_MS']

    def get_25d_put_ms_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_25D_P_MS']

    def get_10d_call_ms_strike(self, expiry_date=None, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_10D_C_MS']

    def get_10d_put_ms_strike(self, expiry_date=None, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_10D_P_MS']

    def get_atm_quoted_vol(self, tenor):
        """The quoted ATM vol from the market (ie. which has NOT been obtained from build vol surface)

        Parameters
        ----------
        tenor : str
            Tenor

        Returns
        -------
        float
        """
        return self._atm_vols[self._market_df.index == self._value_date][0][
            self._get_tenor_index(tenor)]

    def get_atm_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor]['ATM']

    def get_25d_call_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor]['K_25D_C']

    def get_25d_put_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor]['K_25D_P']

    def get_25d_call_ms_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor][
            'K_25D_C_MS']

    def get_25d_put_ms_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor][
            'K_25D_P_MS']

    def get_10d_call_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor]['K_10D_C']

    def get_10d_put_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor]['K_10D_P']

    def get_10d_call_ms_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor][
            'K_10D_C_MS']

    def get_10d_put_ms_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor][
            'K_10D_P_MS']

    def get_dom_discount_curve(self):
        return self._dom_discount_curve

    def get_for_discount_curve(self):
        return self._for_discount_curve

    def plot_vol_curves(self):
        if self._fin_fx_vol_surface is not None:
            self._fin_fx_vol_surface.plotVolCurves()

    def _findate(self, timestamp):

        return FinDate(timestamp.day,
                       timestamp.month,
                       timestamp.year,
                       hh=timestamp.hour,
                       mm=timestamp.minute,
                       ss=timestamp.second)
Exemplo n.º 9
0
    def build_vol_surface(self,
                          value_date,
                          asset=None,
                          depo_tenor='1M',
                          field=None,
                          atm_method=FinFXATMMethod.FWD_DELTA_NEUTRAL,
                          delta_method=FinFXDeltaMethod.SPOT_DELTA):
        """Builds the implied_vol volatility for a particular value date and calculates the benchmark strikes etc.

        Before we do any sort of interpolation later, we need to build the implied_vol vol surface.

        Parameters
        ----------
        value_date : str
            Value data (need to have market data for this date)

        asset : str
            Asset name

        depo_tenor : str
            Depo tenor to use

            default - '1M'

        field : str
            Market data field to use

            default - 'close'

        atm_method : FinFXATMMethod
            How is the ATM quoted? Eg. delta neutral, ATMF etc.

            default - FinFXATMMethod.FWD_DELTA_NEUTRAL

        delta_method : FinFXDeltaMethod
            Spot delta, forward delta etc.

            default - FinFXDeltaMethod.SPOT_DELTA
        """

        value_date = self._market_util.parse_date(value_date)

        self._value_date = value_date

        market_df = self._market_df

        value_fin_date = self._findate(
            self._market_util.parse_date(value_date))

        tenors = self._tenors

        # Change ON (overnight) to 1D (convention for financepy)
        # tenors_financepy = list(map(lambda b: b.replace("ON", "1D"), self._tenors.copy()))
        tenors_financepy = self._tenors.copy()

        if field is None:
            field = self._field

        field = '.' + field

        if asset is None:
            asset = self._asset

        for_name_base = asset[0:3]
        dom_name_terms = asset[3:6]

        notional_currency = for_name_base

        date_index = market_df.index == value_date

        # CAREFUL: need to divide by 100 for depo rate, ie. 0.0346 = 3.46%
        forCCRate = market_df[
            for_name_base + depo_tenor +
            field][date_index].values[0] / 100.0  # 0.03460  # EUR
        domCCRate = market_df[
            dom_name_terms + depo_tenor +
            field][date_index].values[0] / 100.0  # 0.02940  # USD

        currency_pair = for_name_base + dom_name_terms
        spot_fx_rate = float(market_df[currency_pair +
                                       field][date_index].values[0])

        # For vols we do NOT need to divide by 100 (financepy does that internally)
        atm_vols = market_df[[currency_pair + "V" + t + field
                              for t in tenors]][date_index].values[0]

        market_strangle25DeltaVols = market_df[[
            currency_pair + "25B" + t + field for t in tenors
        ]][date_index].values[0]  #[0.65, 0.75, 0.85, 0.90, 0.95, 0.85]
        risk_reversal25DeltaVols = market_df[[
            currency_pair + "25R" + t + field for t in tenors
        ]][date_index].values[0]  #[-0.20, -0.25, -0.30, -0.50, -0.60, -0.562]
        market_strangle10DeltaVols = market_df[[
            currency_pair + "10B" + t + field for t in tenors
        ]][date_index].values[0]
        risk_reversal10DeltaVols = market_df[[
            currency_pair + "10R" + t + field for t in tenors
        ]][date_index].values[0]

        dom_discount_curve = FinDiscountCurveFlat(value_fin_date, domCCRate)
        for_discount_curve = FinDiscountCurveFlat(value_fin_date, forCCRate)

        use_only_25d = True

        # Construct financepy vol surface (uses polynomial interpolation for determining vol between strikes)
        if use_only_25d:
            self._fin_fx_vol_surface = FinFXVolSurface(
                value_fin_date, spot_fx_rate, currency_pair, notional_currency,
                dom_discount_curve, for_discount_curve, tenors_financepy,
                atm_vols, market_strangle25DeltaVols, risk_reversal25DeltaVols,
                atm_method, delta_method)
        else:
            # New implementation in FinancePy also uses 10d for interpolation
            from financepy.market.volatility.FinFXVolSurfacePlus import FinFXVolSurfacePlus

            self._fin_fx_vol_surface = FinFXVolSurfacePlus(
                value_fin_date, spot_fx_rate, currency_pair, notional_currency,
                dom_discount_curve, for_discount_curve, tenors_financepy,
                atm_vols, market_strangle25DeltaVols, risk_reversal25DeltaVols,
                market_strangle10DeltaVols, risk_reversal10DeltaVols,
                atm_method, delta_method)
Exemplo n.º 10
0
class FXVolSurface(AbstractVolSurface):
    """Holds data for an FX vol surface and also interpolates vol surface, converts strikes to implied_vol vols etc.

    """
    def __init__(self,
                 market_df=None,
                 asset=None,
                 field='close',
                 tenors=data_constants.fx_vol_tenor):
        self._market_df = market_df
        self._tenors = tenors
        self._asset = asset
        self._field = field
        self._market_util = MarketUtil()

        self._value_date = None
        self._fin_fx_vol_surface = None
        self._df_vol_dict = None

        self._vol_function_type = FinVolFunctionTypes.CLARKE.value

    def build_vol_surface(self,
                          value_date,
                          asset=None,
                          depo_tenor='1M',
                          field=None,
                          atm_method=FinFXATMMethod.FWD_DELTA_NEUTRAL,
                          delta_method=FinFXDeltaMethod.SPOT_DELTA):
        """Builds the implied_vol volatility for a particular value date and calculates the benchmark strikes etc.

        Before we do any sort of interpolation later, we need to build the implied_vol vol surface.

        Parameters
        ----------
        value_date : str
            Value data (need to have market data for this date)

        asset : str
            Asset name

        depo_tenor : str
            Depo tenor to use

            default - '1M'

        field : str
            Market data field to use

            default - 'close'

        atm_method : FinFXATMMethod
            How is the ATM quoted? Eg. delta neutral, ATMF etc.

            default - FinFXATMMethod.FWD_DELTA_NEUTRAL

        delta_method : FinFXDeltaMethod
            Spot delta, forward delta etc.

            default - FinFXDeltaMethod.SPOT_DELTA
        """

        value_date = self._market_util.parse_date(value_date)

        self._value_date = value_date

        market_df = self._market_df

        value_fin_date = self._findate(
            self._market_util.parse_date(value_date))

        tenors = self._tenors

        # Change ON (overnight) to 1D (convention for financepy)
        # tenors_financepy = list(map(lambda b: b.replace("ON", "1D"), self._tenors.copy()))
        tenors_financepy = self._tenors.copy()

        if field is None:
            field = self._field

        field = '.' + field

        if asset is None:
            asset = self._asset

        for_name_base = asset[0:3]
        dom_name_terms = asset[3:6]

        notional_currency = for_name_base

        date_index = market_df.index == value_date

        # CAREFUL: need to divide by 100 for depo rate, ie. 0.0346 = 3.46%
        forCCRate = market_df[
            for_name_base + depo_tenor +
            field][date_index].values[0] / 100.0  # 0.03460  # EUR
        domCCRate = market_df[
            dom_name_terms + depo_tenor +
            field][date_index].values[0] / 100.0  # 0.02940  # USD

        currency_pair = for_name_base + dom_name_terms
        spot_fx_rate = float(market_df[currency_pair +
                                       field][date_index].values[0])

        # For vols we do NOT need to divide by 100 (financepy does that internally)
        atm_vols = market_df[[currency_pair + "V" + t + field
                              for t in tenors]][date_index].values[0]

        market_strangle25DeltaVols = market_df[[
            currency_pair + "25B" + t + field for t in tenors
        ]][date_index].values[0]  #[0.65, 0.75, 0.85, 0.90, 0.95, 0.85]
        risk_reversal25DeltaVols = market_df[[
            currency_pair + "25R" + t + field for t in tenors
        ]][date_index].values[0]  #[-0.20, -0.25, -0.30, -0.50, -0.60, -0.562]
        market_strangle10DeltaVols = market_df[[
            currency_pair + "10B" + t + field for t in tenors
        ]][date_index].values[0]
        risk_reversal10DeltaVols = market_df[[
            currency_pair + "10R" + t + field for t in tenors
        ]][date_index].values[0]

        dom_discount_curve = FinDiscountCurveFlat(value_fin_date, domCCRate)
        for_discount_curve = FinDiscountCurveFlat(value_fin_date, forCCRate)

        use_only_25d = True

        # Construct financepy vol surface (uses polynomial interpolation for determining vol between strikes)
        if use_only_25d:
            self._fin_fx_vol_surface = FinFXVolSurface(
                value_fin_date, spot_fx_rate, currency_pair, notional_currency,
                dom_discount_curve, for_discount_curve, tenors_financepy,
                atm_vols, market_strangle25DeltaVols, risk_reversal25DeltaVols,
                atm_method, delta_method)
        else:
            # New implementation in FinancePy also uses 10d for interpolation
            from financepy.market.volatility.FinFXVolSurfacePlus import FinFXVolSurfacePlus

            self._fin_fx_vol_surface = FinFXVolSurfacePlus(
                value_fin_date, spot_fx_rate, currency_pair, notional_currency,
                dom_discount_curve, for_discount_curve, tenors_financepy,
                atm_vols, market_strangle25DeltaVols, risk_reversal25DeltaVols,
                market_strangle10DeltaVols, risk_reversal10DeltaVols,
                atm_method, delta_method)

    def calculate_vol_for_strike_expiry(self, K, expiry_date=None, tenor='1M'):
        """Calculates the implied_vol volatility for a given strike

        Parameters
        ----------
        K : float
            Strike for which to find implied_vol volatility

        expiry_date : str (optional)
            Expiry date of option (TODO not implemented)

        tenor : str (optional)
            Tenor of option

            default - '1M'

        Returns
        -------

        """
        # TODO interpolate for broken dates, not just quoted tenors
        if tenor is not None:
            try:
                tenor_index = self._get_tenor_index(tenor)
                return self.vol_function(K, tenor_index)
            except:
                pass

        return None

    def extract_vol_surface(self, num_strike_intervals=60):
        """Creates an interpolated implied vol surface which can be plotted (in strike space), and also in delta
        space for key strikes (ATM, 25d call and put). Also for key strikes converts from delta to strike space.

        Parameters
        ----------
        num_strike_intervals : int
            Number of points to interpolate

        Returns
        -------
        dict
        """
        ## Modified from FinancePy code for plotting vol curves

        # columns = tenors
        df_vol_surface_strike_space = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)
        df_vol_surface_delta_space = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)

        # columns = tenors
        df_vol_surface_implied_pdf = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)

        # Conversion between main deltas and strikes
        df_deltas_vs_strikes = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)

        # ATM, 25d market strangle and 25d risk reversals
        df_vol_surface_quoted_points = pd.DataFrame(
            columns=self._fin_fx_vol_surface._tenors)

        # Note, at present we're not using 10d strikes
        quoted_strikes_names = ['ATM', 'STR_25D_MS', 'RR_25D_P']
        key_strikes_names = [
            'K_25D_P', 'K_25D_P_MS', 'ATM', 'K_25D_C', 'K_25D_C_MS'
        ]

        # Get max/min strikes to interpolate (from the longest dated tenor)
        low_K = self._fin_fx_vol_surface._K_25D_P[-1] * 0.95
        high_K = self._fin_fx_vol_surface._K_25D_C[-1] * 1.05

        # In case using old version of FinancePy
        try:
            implied_pdf_fin_distribution = self._fin_fx_vol_surface.impliedDbns(
                low_K, high_K, num_strike_intervals)
        except:
            pass

        for tenor_index in range(0, self._fin_fx_vol_surface._numVolCurves):

            # Get the quoted vol points
            tenor_label = self._fin_fx_vol_surface._tenors[tenor_index]

            atm_vol = self._fin_fx_vol_surface._atmVols[tenor_index] * 100
            ms_25d_vol = self._fin_fx_vol_surface._mktStrangle25DeltaVols[
                tenor_index] * 100
            rr_10d_vol = self._fin_fx_vol_surface._riskReversal25DeltaVols[
                tenor_index] * 100

            df_vol_surface_quoted_points[tenor_label] = pd.Series(
                index=quoted_strikes_names,
                data=[atm_vol, ms_25d_vol, rr_10d_vol])

            # Do interpolation in strike space for the implied_vol vols
            strikes = []
            vols = []

            if num_strike_intervals is not None:
                K = low_K
                dK = (high_K - low_K) / num_strike_intervals

                for i in range(0, num_strike_intervals):
                    sigma = self.vol_function(K, tenor_index) * 100.0
                    strikes.append(K)
                    vols.append(sigma)
                    K = K + dK

                df_vol_surface_strike_space[tenor_label] = pd.Series(
                    index=strikes, data=vols)

            try:
                df_vol_surface_implied_pdf[tenor_label] = pd.Series(
                    index=implied_pdf_fin_distribution[tenor_index]._x,
                    data=implied_pdf_fin_distribution[tenor_index]._densitydx)
            except:
                pass

            # Extract strikes for the quoted points (ie. 25d and ATM)
            key_strikes = []
            key_strikes.append(self._fin_fx_vol_surface._K_25D_P[tenor_index])
            key_strikes.append(
                self._fin_fx_vol_surface._K_25D_P_MS[tenor_index])
            key_strikes.append(self._fin_fx_vol_surface._K_ATM[tenor_index])
            key_strikes.append(self._fin_fx_vol_surface._K_25D_C[tenor_index])
            key_strikes.append(
                self._fin_fx_vol_surface._K_25D_C_MS[tenor_index])

            df_deltas_vs_strikes[tenor_label] = pd.Series(
                index=key_strikes_names, data=key_strikes)

            # Put a conversion between quoted deltas and strikes (eg. which is ATM in strike space, 25d call/put strikes)
            key_vols = []

            for K, name in zip(key_strikes, key_strikes_names):
                sigma = self.vol_function(K, tenor_index) * 100.0
                key_vols.append(sigma)

            df_vol_surface_delta_space[tenor_label] = pd.Series(
                index=key_strikes_names, data=key_vols)

        df_vol_dict = {}
        df_vol_dict['vol_surface_implied_pdf'] = df_vol_surface_implied_pdf
        df_vol_dict['vol_surface_strike_space'] = df_vol_surface_strike_space
        df_vol_dict['vol_surface_delta_space'] = df_vol_surface_delta_space
        df_vol_dict['vol_surface_quoted_points'] = df_vol_surface_quoted_points
        df_vol_dict['deltas_vs_strikes'] = df_deltas_vs_strikes

        self._df_vol_dict = df_vol_dict

        return df_vol_dict

    def vol_function(self, K, tenor_index):

        params = self._fin_fx_vol_surface._parameters[tenor_index]
        t = self._fin_fx_vol_surface._texp[tenor_index]
        f = self._fin_fx_vol_surface._F0T[tenor_index]

        return volFunctionFAST(self._vol_function_type, params, f, K, t)

    def get_all_market_data(self):
        return self._market_df

    def get_atm_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['ATM']

    def get_25d_call_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_25D_C']

    def get_25d_put_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_25P_C']

    def get_10d_call_strike(self, tenor=None):
        pass

    def get_10d_put_strike(self, tenor=None):
        pass

    def get_25d_call_ms_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_25D_C_MS']

    def get_25d_put_ms_strike(self, tenor=None):
        return self._df_vol_dict['deltas_vs_strikes'][tenor]['K_25D_C_MS']

    def get_10d_call_ms_strike(self, expiry_date=None, tenor=None):
        pass

    def get_10d_put_ms_strike(self, expiry_date=None, tenor=None):
        pass

    def get_atm_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor]['ATM']

    def get_25d_call_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor]['K_25D_C']

    def get_25d_put_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor]['K_25D_P']

    def get_25d_call_ms_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor][
            'K_25D_C_MS']

    def get_25d_put_ms_vol(self, tenor=None):
        return self._df_vol_dict['vol_surface_delta_space'][tenor][
            'K_25D_P_MS']

    def get_10d_call_vol(self, tenor=None):
        pass

    def get_10d_put_vol(self, tenor=None):
        pass

    def get_10d_call_ms_vol(self, tenor=None):
        pass

    def get_10d_put_ms_vol(self, tenor=None):
        pass

    def plot_vol_curves(self):
        if self._fin_fx_vol_surface is not None:
            self._fin_fx_vol_surface.plotVolCurves()
Exemplo n.º 11
0
def test_FinFXMktVolSurface3(verboseCalibration):

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

    if 1 == 1:

        # Example from Book extract by Iain Clark using Tables 4.4 and 4.5
        # where we examine the calibration to a full surface in Chapter 4

        valueDate = FinDate(10, 4, 2020)

        forName = "EUR"
        domName = "USD"
        forCCRate = 0.03460  # EUR
        domCCRate = 0.02940  # USD

        domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate)
        forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate)

        currencyPair = forName + domName
        spotFXRate = 1.3465

        tenors = ['1Y', '2Y']
        atmVols = [18.250, 17.677]
        marketStrangle25DeltaVols = [0.95, 0.85]
        riskReversal25DeltaVols = [-0.60, -0.562]
        marketStrangle10DeltaVols = [3.806, 3.208]
        riskReversal10DeltaVols = [-1.359, -1.208]

        notionalCurrency = forName

        # I HAVE NO YET MADE DELTA METHOD A VECTOR FOR EACH TERM AS I WOULD
        # NEED TO DO AS DESCRIBED IN CLARK PAGE 70
        
        atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL
        deltaMethod = FinFXDeltaMethod.FORWARD_DELTA # THIS IS DIFFERENT
        volFunctionType = FinVolFunctionTypes.CLARK5
        alpha = 0.5 # FIT WINGS AT 10D if ALPHA = 1.0

        fxMarketPlus = FinFXVolSurfacePlus(valueDate,
                                       spotFXRate,
                                       currencyPair,
                                       notionalCurrency,
                                       domDiscountCurve,
                                       forDiscountCurve,
                                       tenors,
                                       atmVols,
                                       marketStrangle25DeltaVols,
                                       riskReversal25DeltaVols,
                                       marketStrangle10DeltaVols,
                                       riskReversal10DeltaVols,
                                       alpha,
                                       atmMethod,
                                       deltaMethod, 
                                       volFunctionType)

        fxMarketPlus.checkCalibration(False)

        if 1==0: # PLOT_GRAPHS:

            fxMarketPlus.plotVolCurves()

            plt.figure()

            dbns = fxMarketPlus.impliedDbns(0.5, 2.0, 1000)

            for i in range(0, len(dbns)):
                plt.plot(dbns[i]._x, dbns[i]._densitydx)
                plt.title(volFunctionType)
                print("SUM:", dbns[i].sum())

        # Test interpolation
        
        years = [1.0, 1.5, 2.0]
        dates = valueDate.addYears(years)

        strikes = np.linspace(1.0, 2.0, 20)

        if 1==0:
            volSurface = []        
            for k in strikes:
                volSmile = []
                for dt in dates:
                    vol = fxMarketPlus.volatilityFromStrikeDate(k, dt)
                    volSmile.append(vol*100.0)
                    
                    print(k, dt, vol*100.0)
                volSurface.append(volSmile)
    
            fig = plt.figure()
            ax = fig.add_subplot(111, projection='3d')
            X, Y = np.meshgrid(years, strikes)
            zs = np.array(volSurface)
            Z = zs.reshape(X.shape)
            
            ax.plot_surface(X, Y, Z)
            
            ax.set_xlabel('Years')
            ax.set_ylabel('Strikes')
            ax.set_zlabel('Volatility')
            
            plt.show()

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

        deltas = np.linspace(0.10, 0.90, 17)

        if 1==0:
            volSurface = []        
            for delta in deltas:
                volSmile = []
                for dt in dates:
                    (vol, k) = fxMarketPlus.volatilityFromDeltaDate(delta, dt)
                    volSmile.append(vol*100.0)
                    print(delta, k, dt, vol*100.0)

                volSurface.append(volSmile)
    
            fig = plt.figure()
            ax = fig.add_subplot(111, projection='3d')
            X, Y = np.meshgrid(years, deltas)
            zs = np.array(volSurface)
            Z = zs.reshape(X.shape)
            
            ax.plot_surface(X, Y, Z)
            
            ax.set_xlabel('Years')
            ax.set_ylabel('Delta')
            ax.set_zlabel('Volatility')
            
            plt.show()
Exemplo n.º 12
0
def test_FinFXMktVolSurface4(verboseCalibration):

    ###########################################################################
    # Here I remove the 25D Vols
    ###########################################################################

    if 1 == 1:

        # Example from Book extract by Iain Clark using Tables 3.3 and 3.4
        # print("EURUSD EXAMPLE CLARK")

        valueDate = FinDate(10, 4, 2020)

        forName = "EUR"
        domName = "USD"
        forCCRate = 0.03460  # EUR
        domCCRate = 0.02940  # USD

        domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate)
        forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate)

        currencyPair = forName + domName
        spotFXRate = 1.3465

        tenors = ['1M', '2M', '3M', '6M', '1Y', '2Y']
        atmVols = [21.00, 21.00, 20.750, 19.400, 18.250, 17.677]
        marketStrangle25DeltaVols = [0.65, 0.75, 0.85, 0.90, 0.95, 0.85]
        riskReversal25DeltaVols = [-0.20, -0.25, -0.30, -0.50, -0.60, -0.562]
        marketStrangle10DeltaVols = [2.433, 2.83, 3.228, 3.485, 3.806, 3.208]
        riskReversal10DeltaVols = [
            -1.258, -1.297, -1.332, -1.408, -1.359, -1.208
        ]

        marketStrangle25DeltaVols = None
        riskReversal25DeltaVols = None

        notionalCurrency = forName

        atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL
        deltaMethod = FinFXDeltaMethod.SPOT_DELTA
        volFunctionType = FinVolFunctionTypes.CLARK
        alpha = 0.50  # FIT WINGS AT 10D if ALPHA = 1.0

        fxMarketPlus = FinFXVolSurfacePlus(
            valueDate, spotFXRate, currencyPair, notionalCurrency,
            domDiscountCurve, forDiscountCurve, tenors, atmVols,
            marketStrangle25DeltaVols, riskReversal25DeltaVols,
            marketStrangle10DeltaVols, riskReversal10DeltaVols, alpha,
            atmMethod, deltaMethod, volFunctionType)

        fxMarketPlus.checkCalibration(False)

        years = [1.0 / 12.0, 2. / 12., 0.25, 0.5, 1.0, 2.0]

        dates = valueDate.addYears(years)

        deltas = np.linspace(0.10, 0.90, 17)

        if 1 == 1:
            volSurface = []
            for delta in deltas:
                volSmile = []
                for dt in dates:
                    (vol, k) = fxMarketPlus.volatilityFromDeltaDate(delta, dt)
                    volSmile.append(vol * 100.0)
                    print(delta, k, dt, vol * 100.0)

                volSurface.append(volSmile)

            fig = plt.figure()
            ax = fig.add_subplot(111, projection='3d')
            X, Y = np.meshgrid(years, deltas)
            zs = np.array(volSurface)
            Z = zs.reshape(X.shape)

            ax.plot_surface(X, Y, Z)

            ax.set_xlabel('Years')
            ax.set_ylabel('Delta')
            ax.set_zlabel('Volatility')
            plt.title("EURUSD Volatility Surface")
            plt.show()
Exemplo n.º 13
0
def test_FinFXMktVolSurface1LONG(verboseCalibration):

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

    if 1 == 1:

        # Example from Book extract by Iain Clarke using Tables 3.3 and 3.4
        # print("EURUSD EXAMPLE CLARKE")

        valueDate = FinDate(10, 4, 2020)

        forName = "EUR"
        domName = "USD"
        forCCRate = 0.03460  # EUR
        domCCRate = 0.02940  # USD

        domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate)
        forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate)

        currencyPair = forName + domName
        spotFXRate = 1.3465

        tenors = ['1M', '2M', '3M', '6M', '1Y', '2Y']
        atmVols = [21.00, 21.00, 20.750, 19.400, 18.250, 17.677]
        marketStrangle25DeltaVols = [0.65, 0.75, 0.85, 0.90, 0.95, 0.85]
        riskReversal25DeltaVols = [-0.20, -0.25, -0.30, -0.50, -0.60, -0.562]
        marketStrangle10DeltaVols = [2.433, 2.83, 3.228, 3.485, 3.806, 3.208]
        riskReversal10DeltaVols = [-1.258, -1.297, -1.332, -1.408, -1.359, -1.208]

        if 1==1:
            tenors = ['1Y']
            atmVols = [18.250]
            marketStrangle25DeltaVols = [0.950]
            riskReversal25DeltaVols = [-0.600]
            marketStrangle10DeltaVols = [3.806]
            riskReversal10DeltaVols = [-1.359]

        notionalCurrency = forName

        atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL
        deltaMethod = FinFXDeltaMethod.SPOT_DELTA
        volFunctionType = FinVolFunctionTypes.CLARKE

        # EXPLORE AND TEST DIFFERENT CATEGORICAL PARAMETERS
        for atmMethod in FinFXATMMethod:
            for deltaMethod in FinFXDeltaMethod:
                for volFunctionType in FinVolFunctionTypes:
    
                    fxMarket = FinFXVolSurfacePlus(valueDate,
                                                   spotFXRate,
                                                   currencyPair,
                                                   notionalCurrency,
                                                   domDiscountCurve,
                                                   forDiscountCurve,
                                                   tenors,
                                                   atmVols,
                                                   marketStrangle25DeltaVols,
                                                   riskReversal25DeltaVols,
                                                   marketStrangle10DeltaVols,
                                                   riskReversal10DeltaVols,
                                                   atmMethod,
                                                   deltaMethod, 
                                                   volFunctionType)
        
                    fxMarket.checkCalibration(verboseCalibration)

        if PLOT_GRAPHS:
            fxMarket.plotVolCurves()

        dbns = fxMarket.impliedDbns(0.00001, 5.0, 10000)

        for i in range(0, len(dbns)):
            plt.plot(dbns[i]._x, dbns[i]._densitydx)
            print("SUM:", dbns[i].sum())