Ejemplo n.º 1
0
    def black_scholes_merton(**kwargs):
        """
        Black-Scholes-Merton Option price

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.

        Returns
        -------
        opt_price : Float
            Option Price.

        """
        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            option = params['option']

        b = r - q
        carry = np.exp((b - r) * T)
        d1 = ((np.log(S / K) + (b + (0.5 * sigma**2)) * T) /
              (sigma * np.sqrt(T)))
        d2 = ((np.log(S / K) + (b - (0.5 * sigma**2)) * T) /
              (sigma * np.sqrt(T)))

        # Cumulative normal distribution function
        Nd1 = si.norm.cdf(d1, 0.0, 1.0)
        minusNd1 = si.norm.cdf(-d1, 0.0, 1.0)
        Nd2 = si.norm.cdf(d2, 0.0, 1.0)
        minusNd2 = si.norm.cdf(-d2, 0.0, 1.0)

        if option == "call":
            opt_price = ((S * carry * Nd1) - (K * np.exp(-r * T) * Nd2))
        if option == 'put':
            opt_price = ((K * np.exp(-r * T) * minusNd2) -
                         (S * carry * minusNd1))

        return opt_price
Ejemplo n.º 2
0
    def black_76(**kwargs):
        """
        Black 76 Futures Option price

        Parameters
        ----------
        F : Float
            Discounted Futures Price.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.

        Returns
        -------
        opt_price : Float
            Option Price.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            F = params['F']
            K = params['K']
            T = params['T']
            r = params['r']
            sigma = params['sigma']
            option = params['option']

        carry = np.exp(-r * T)
        d1 = (np.log(F / K) + (0.5 * sigma**2) * T) / (sigma * np.sqrt(T))
        d2 = (np.log(F / K) + (-0.5 * sigma**2) * T) / (sigma * np.sqrt(T))

        # Cumulative normal distribution function
        Nd1 = si.norm.cdf(d1, 0.0, 1.0)
        minusNd1 = si.norm.cdf(-d1, 0.0, 1.0)
        Nd2 = si.norm.cdf(d2, 0.0, 1.0)
        minusNd2 = si.norm.cdf(-d2, 0.0, 1.0)

        if option == "call":
            opt_price = ((F * carry * Nd1) - (K * np.exp(-r * T) * Nd2))
        if option == 'put':
            opt_price = ((K * np.exp(-r * T) * minusNd2) -
                         (F * carry * minusNd1))

        return opt_price
Ejemplo n.º 3
0
    def __init__(self, **kwargs):

        # Import dictionary of default parameters
        self.default_dict = copy.deepcopy(models_params_dict)

        # Store initial inputs
        inputs = {}
        for key, value in kwargs.items():
            inputs[key] = value

        # Initialise system parameters
        params = Utils.init_params(inputs)

        self.params = params
Ejemplo n.º 4
0
    def black_scholes_merton_vega(**kwargs):
        """
        Black-Scholes-Merton Option Vega

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.

        Returns
        -------
        opt_vega : Float
            Option Vega.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']

        b = r - q
        carry = np.exp((b - r) * T)
        d1 = ((np.log(S / K) + (b + (0.5 * sigma**2)) * T) /
              (sigma * np.sqrt(T)))
        nd1 = (1 / np.sqrt(2 * np.pi)) * (np.exp(-d1**2 * 0.5))

        opt_vega = S * carry * nd1 * np.sqrt(T)

        return opt_vega
    def crank_nicolson(**kwargs):
        """
        Crank Nicolson

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        steps : Int
            Number of time steps. The default is 1000.
        nodes : Float
            Number of price steps. The default is 100.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        american : Bool
            Whether the option is American. The default is False.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Option Price.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            steps = params['steps']
            nodes = params['nodes']
            option = params['option']
            american = params['american']

        if option == 'call':
            z = 1
        else:
            z = -1

        b = r - q
        dt = T / steps
        dx = sigma * np.sqrt(3 * dt)
        pu = -0.25 * dt * (((sigma / dx)**2) + (b - (sigma**2) / 2) / dx)
        pm = 1 + 0.5 * dt * ((sigma / dx)**2) + 0.5 * r * dt
        pd = -0.25 * dt * (((sigma / dx)**2) - (b - (sigma**2) / 2) / dx)
        St = np.zeros(nodes + 2)
        pmd = np.zeros(nodes + 1)
        p = np.zeros(nodes + 1)
        St[0] = S * np.exp(-nodes / 2 * dx)
        C = np.zeros((int(nodes / 2) + 2, nodes + 2), dtype='float')
        C[0, 0] = max(0, z * (St[0] - K))

        for node in range(1, nodes + 1):
            St[node] = St[node - 1] * np.exp(dx)  # Asset price at maturity
            C[0, node] = max(0, z * (St[node] - K))  # At maturity

        pmd[1] = pm + pd
        p[1] = (-pu * C[0, 2] - (pm - 2) * C[0, 1] - pd * C[0, 0] - pd *
                (St[1] - St[0]))

        step = steps - 1
        while step > -1:
            for outer_node in range(2, nodes):
                p[outer_node] = (-pu * C[0, outer_node + 1] -
                                 (pm - 2) * C[0, outer_node] -
                                 pd * C[0, outer_node - 1] -
                                 p[outer_node - 1] * pd / pmd[outer_node - 1])
                pmd[outer_node] = pm - pu * pd / pmd[outer_node - 1]

            for outer_node in range(nodes - 2, 0, -1):
                C[1,
                  outer_node] = ((p[outer_node] - pu * C[1, outer_node + 1]) /
                                 pmd[outer_node])

                for inner_node in range(nodes + 1):
                    if american:
                        C[0, inner_node] = max(C[1, inner_node],
                                               z * (St[inner_node] - K))
                    else:
                        C[0, inner_node] = C[1, inner_node]
            step -= 1

        result = C[0, int(nodes / 2)]

        return result
    def explicit_finite_difference_lns(**kwargs):
        """
        Explicit Finite Differences - rewrite BS-PDE in terms of ln(S)

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        steps : Int
            Number of time steps. The default is 1000.
        nodes : Float
            Number of price steps. The default is 100.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        american : Bool
            Whether the option is American. The default is False.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Option Price.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            steps_itt = params['steps_itt']
            nodes = params['nodes']
            option = params['option']
            american = params['american']

        if option == 'call':
            z = 1
        else:
            z = -1

        b = r - q
        dt = T / steps_itt
        dx = sigma * np.sqrt(3 * dt)
        pu = 0.5 * dt * (((sigma / dx)**2) + (b - (sigma**2) / 2) / dx)
        pm = 1 - dt * ((sigma / dx)**2) - r * dt
        pd = 0.5 * dt * (((sigma / dx)**2) - (b - (sigma**2) / 2) / dx)
        St = np.zeros(nodes + 2)
        St[0] = S * np.exp(-nodes / 2 * dx)
        C = np.zeros((int(nodes / 2) + 1, nodes + 2), dtype='float')
        C[steps_itt, 0] = max(0, z * (St[0] - K))

        for i in range(1, nodes + 1):
            St[i] = St[i - 1] * np.exp(dx)  # Asset price at maturity
            C[steps_itt, i] = max(0, z * (St[i] - K))  # At maturity

        for j in range(steps_itt - 1, -1, -1):
            for i in range(1, nodes):
                C[j, i] = pu * C[j + 1, i + 1] + pm * C[j + 1, i] + (
                    pd * C[j + 1, i - 1])
                if american:
                    C[j, i] = max(C[j, i], z * (St[i] - K))

                # Upper boundary
                C[j, nodes] = C[j, nodes - 1] + (St[nodes] - St[nodes - 1])

                # Lower boundary
                C[j, 0] = C[j, 1]

        result = C[0, int(nodes / 2)]

        return result
    def explicit_finite_difference(**kwargs):
        """
        Explicit Finite Difference

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        nodes : Int
            Number of price steps. The default is 100.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        american : Bool
            Whether the option is American. The default is False.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Option Price.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            nodes = params['nodes']
            option = params['option']
            american = params['american']

        if option == 'call':
            z = 1
        else:
            z = -1

        b = r - q
        dS = S / nodes
        nodes = int(K / dS) * 2
        St = np.zeros((nodes + 2), dtype='float')

        SGridtPt = int(S / dS)
        dt = (dS**2) / ((sigma**2) * 4 * (K**2))
        N = int(T / dt) + 1

        C = np.zeros((N + 1, nodes + 2), dtype='float')
        dt = T / N
        Df = 1 / (1 + r * dt)

        for i in range(nodes + 1):
            St[i] = i * dS  # Asset price at maturity
            C[N, i] = max(0, z * (St[i] - K))  # At maturity

        for j in range(N - 1, -1, -1):
            for i in range(1, nodes):
                pu = 0.5 * ((sigma**2) * (i**2) + b * i) * dt
                pm = 1 - (sigma**2) * (i**2) * dt
                pd = 0.5 * ((sigma**2) * (i**2) - b * i) * dt
                C[j, i] = Df * (pu * C[j + 1, i + 1] + pm * C[j + 1, i] +
                                pd * C[j + 1, i - 1])
                if american:
                    C[j, i] = max(z * (St[i] - K), C[j, i])

                if z == 1:  # Call option
                    C[j, 0] = 0
                    C[j, nodes] = (St[i] - K)
                else:
                    C[j, 0] = K
                    C[j, nodes] = 0

        result = C[0, SGridtPt]

        return result
    def implicit_finite_difference(**kwargs):
        """
        Implicit Finite Difference
        # Slow to converge - steps has small effect, need nodes 3000+

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        steps : Int
            Number of time steps. The default is 1000.
        nodes : Float
            Number of price steps. The default is 100.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        american : Bool
            Whether the option is American. The default is False.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Option Price.


        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            steps = params['steps']
            nodes = params['nodes']
            option = params['option']
            american = params['american']

        if option == 'call':
            z = 1
        else:
            z = -1

        # Make sure current asset price falls at grid point
        dS = 2 * S / nodes
        SGridtPt = int(S / dS)
        nodes = int(K / dS) * 2
        dt = T / steps
        b = r - q

        CT = np.zeros(nodes + 1)
        p = np.zeros((nodes + 1, nodes + 1), dtype='float')

        for j in range(nodes + 1):
            CT[j] = max(0, z * (j * dS - K))  # At maturity
            for i in range(nodes + 1):
                p[j, i] = 0

        p[0, 0] = 1
        for i in range(1, nodes):
            p[i, i - 1] = 0.5 * i * (b - (sigma**2) * i) * dt
            p[i, i] = 1 + (r + (sigma**2) * (i**2)) * dt
            p[i, i + 1] = 0.5 * i * (-b - (sigma**2) * i) * dt

        p[nodes, nodes] = 1

        C = np.matmul(np.linalg.inv(p), CT.T)

        for j in range(steps - 1, 0, -1):
            C = np.matmul(np.linalg.inv(p), C)

            if american:
                for i in range(1, nodes + 1):
                    C[i] = max(float(C[i]), z * ((i - 1) * dS - K))

        result = C[SGridtPt + 1]

        return result
Ejemplo n.º 9
0
    def european_monte_carlo_with_greeks(**kwargs):
        """
        Standard Monte Carlo with Greeks

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        simulations : Int
            Number of Monte Carlo runs. The default is 10000.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        output_flag : Str
            Whether to return 'price', 'delta', 'gamma', 'theta',
            'vega' or 'all'. The default is 'price'.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Various
            Depending on output flag:
                'price' : Float; Option Price
                'delta' : Float; Option Delta
                'gamma' : Float; Option Gamma
                'theta' : Float; Option Theta
                'vega' : Float; Option Vega
                'all' : Dict; Option Price, Option Delta, Option
                               Gamma, Option Theta, Option Vega

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            simulations = params['simulations']
            option = params['option']
            output_flag = params['output_flag']

        if option == 'call':
            z = 1
        else:
            z = -1

        b = r - q
        Drift = (b - (sigma**2) / 2) * T
        sigmarT = sigma * np.sqrt(T)
        val = 0
        deltasum = 0
        gammasum = 0
        output = np.zeros((5))

        counter = 1
        while counter < simulations + 1:
            St = S * np.exp(Drift + sigmarT *
                            norm.ppf(random.random(), loc=0, scale=1))
            val = val + max(z * (St - K), 0)
            if z == 1 and St > K:
                deltasum = deltasum + St
            if z == -1 and St < K:
                deltasum = deltasum + St
            if abs(St - K) < 2:
                gammasum = gammasum + 1
            counter += 1

        # Option Value
        output[0] = np.exp(-r * T) * val / simulations

        # Delta
        output[1] = np.exp(-r * T) * deltasum / (simulations * S)

        # Gamma
        output[2] = (np.exp(-r * T) * ((K / S)**2) * gammasum /
                     (4 * simulations))

        # Theta
        output[3] = ((r * output[0] - b * S * output[1] -
                      (0.5 * (sigma**2) * (S**2) * output[2])) / 365)

        # Vega
        output[4] = output[2] * sigma * (S**2) * T / 100

        output_dict = {
            'price': output[0],
            'delta': output[1],
            'gamma': output[2],
            'theta': output[3],
            'vega': output[4],
            'all': {
                'Price': output[0],
                'Delta': output[1],
                'Gamma': output[2],
                'Theta': output[3],
                'Vega': output[4]
            }
        }

        result = output_dict.get(output_flag,
                                 'Please enter a valid output flag')

        #        if output_flag == 'price':
        #            result = output[0]
        #        if output_flag == 'delta':
        #            result = output[1]
        #        if output_flag == 'gamma':
        #            result = output[2]
        #        if output_flag == 'theta':
        #            result = output[3]
        #        if output_flag == 'vega':
        #            result = output[4]
        #        if output_flag == 'all':
        #            result = {'Price':output[0],
        #                      'Delta':output[1],
        #                      'Gamma':output[2],
        #                      'Theta':output[3],
        #                      'Vega':output[4]}

        return result
Ejemplo n.º 10
0
    def implied_vol_naive(**kwargs):
        """
        Finds implied volatility using simple naive iteration,
        increasing precision each time the difference changes sign.

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        cm : Float
            # Option price used to solve for vol. The default is 5.
        epsilon : Float
            Degree of precision. The default is 0.0001
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Implied Volatility.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            cm = params['cm']
            epsilon = params['epsilon']
            option = params['option']

        # Seed vol
        vi = 0.2

        # Calculate starting option price using this vol
        ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                    K=K,
                                                    T=T,
                                                    r=r,
                                                    q=q,
                                                    sigma=vi,
                                                    option=option,
                                                    refresh=True)

        # Initial price difference
        price_diff = cm - ci

        if price_diff > 0:
            flag = 1

        else:
            flag = -1

        # Starting vol shift size
        shift = 0.01

        price_diff_start = price_diff

        while abs(price_diff) > epsilon:

            # If the price difference changes sign after the vol shift,
            # reduce the decimal by one and reverse the sign
            if np.sign(price_diff) != np.sign(price_diff_start):
                shift = shift * -0.1

            # Calculate new vol
            vi += (shift * flag)

            # Set initial price difference
            price_diff_start = price_diff

            # Calculate the option price with new vol
            ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                        K=K,
                                                        T=T,
                                                        r=r,
                                                        q=q,
                                                        sigma=vi,
                                                        option=option,
                                                        refresh=True)

            # Price difference after shifting vol
            price_diff = cm - ci

            # If values are diverging reverse the shift sign
            if abs(price_diff) > abs(price_diff_start):
                shift = -shift

        result = vi

        return result
Ejemplo n.º 11
0
    def implied_vol_bisection(**kwargs):
        """
        Finds implied volatility using bisection method.

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        cm : Float
            # Option price used to solve for vol. The default is 5.
        epsilon : Float
            Degree of precision. The default is 0.0001
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Implied Volatility.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            cm = params['cm']
            epsilon = params['epsilon']
            option = params['option']

        vLow = 0.005
        vHigh = 4
        cLow = AnalyticalMethods.black_scholes_merton(S=S,
                                                      K=K,
                                                      T=T,
                                                      r=r,
                                                      q=q,
                                                      sigma=vLow,
                                                      option=option,
                                                      refresh=True)

        cHigh = AnalyticalMethods.black_scholes_merton(S=S,
                                                       K=K,
                                                       T=T,
                                                       r=r,
                                                       q=q,
                                                       sigma=vHigh,
                                                       option=option,
                                                       refresh=True)

        counter = 0

        vi = vLow + (cm - cLow) * (vHigh - vLow) / (cHigh - cLow)

        while abs(cm - AnalyticalMethods.black_scholes_merton(
                S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option, refresh=True)
                  ) > epsilon:

            counter = counter + 1
            if counter == 100:
                result = 'NA'

            if AnalyticalMethods.black_scholes_merton(
                    S=S, K=K, T=T, r=r, q=q, sigma=vi, option=option,
                    refresh=True) < cm:
                vLow = vi

            else:
                vHigh = vi

            cLow = AnalyticalMethods.black_scholes_merton(S=S,
                                                          K=K,
                                                          T=T,
                                                          r=r,
                                                          q=q,
                                                          sigma=vLow,
                                                          option=option,
                                                          refresh=True)

            cHigh = AnalyticalMethods.black_scholes_merton(S=S,
                                                           K=K,
                                                           T=T,
                                                           r=r,
                                                           q=q,
                                                           sigma=vHigh,
                                                           option=option,
                                                           refresh=True)

            vi = vLow + (cm - cLow) * (vHigh - vLow) / (cHigh - cLow)

        result = vi

        return result
Ejemplo n.º 12
0
    def leisen_reimer_binomial(**kwargs):
        """
        Leisen Reimer Binomial

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        steps : Int
            Number of time steps. The default is 1000.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        output_flag : Str
            Whether to return 'price', 'delta', 'gamma' or 'all'. The
            default is 'price'.
        american : Bool
            Whether the option is American. The default is False.

        Returns
        -------
        result : Various
            Depending on output flag:
                'price' : Float; Option Price
                'delta' : Float; Option Delta
                'gamma' : Float; Option Gamma
                'all' : Tuple; Option Price, Option Delta, Option Gamma

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            steps = params['steps']
            option = params['option']
            output_flag = params['output_flag']
            american = params['american']

        if option == 'call':
            z = 1
        else:
            z = -1

        b = r - q
        d1 = ((np.log(S / K) + (b + (0.5 * sigma**2)) * T) /
              (sigma * np.sqrt(T)))
        d2 = ((np.log(S / K) + (b - (0.5 * sigma**2)) * T) /
              (sigma * np.sqrt(T)))
        hd1 = (0.5 + np.sign(d1) *
               (0.25 - 0.25 * np.exp(-(d1 / (steps + 1 / 3 + 0.1 /
                                             (steps + 1)))**2 *
                                     (steps + 1 / 6)))**(0.5))
        hd2 = (0.5 + np.sign(d2) *
               (0.25 - 0.25 * np.exp(-(d2 / (steps + 1 / 3 + 0.1 /
                                             (steps + 1)))**2 *
                                     (steps + 1 / 6)))**(0.5))

        dt = T / steps
        p = hd2
        u = np.exp(b * dt) * hd1 / hd2
        d = (np.exp(b * dt) - p * u) / (1 - p)
        df = np.exp(-r * dt)

        optionvalue = np.zeros((steps + 1))
        returnvalue = np.zeros((4))

        for i in range(steps + 1):
            optionvalue[i] = max(0, z * (S * (u**i) * (d**(steps - i)) - K))

        for j in range(steps - 1, -1, -1):
            for i in range(j + 1):
                if american:
                    optionvalue[i] = ((p * optionvalue[i + 1]) +
                                      ((1 - p) * optionvalue[i])) * df
                else:
                    optionvalue[i] = max((z * (S * (u**i) * (d**(j - i)) - K)),
                                         ((p * optionvalue[i + 1]) +
                                          ((1 - p) * optionvalue[i])) * df)

            if j == 2:
                returnvalue[2] = (((optionvalue[2] - optionvalue[1]) /
                                   (S * (u**2) - S * u * d) -
                                   (optionvalue[1] - optionvalue[0]) /
                                   (S * u * d - S * (d**2))) /
                                  (0.5 * (S * (u**2) - S * (d**2))))

                returnvalue[3] = optionvalue[1]

            if j == 1:
                returnvalue[1] = ((optionvalue[1] - optionvalue[0]) /
                                  (S * u - S * d))

        returnvalue[0] = optionvalue[0]

        if output_flag == 'price':
            result = returnvalue[0]
        if output_flag == 'delta':
            result = returnvalue[1]
        if output_flag == 'gamma':
            result = returnvalue[2]
        if output_flag == 'all':
            result = {
                'Price': returnvalue[0],
                'Delta': returnvalue[1],
                'Gamma': returnvalue[2]
            }

        return result
Ejemplo n.º 13
0
    def european_binomial(**kwargs):
        """
        European Binomial Option price.
        Combinatorial function limit c1000

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        steps : Int
            Number of time steps. The default is 1000.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.

        Returns
        -------
        Float
            European Binomial Option Price.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            steps = params['steps']
            option = params['option']

        b = r - q
        dt = T / steps
        u = np.exp(sigma * np.sqrt(dt))
        d = 1 / u
        p = (np.exp(b * dt) - d) / (u - d)
        a = int(np.log(K / (S * (d**steps))) / np.log(u / d)) + 1

        val = 0

        if option == 'call':
            for j in range(a, steps + 1):
                val = (val +
                       (comb(steps, j) * (p**j) * ((1 - p)**(steps - j)) *
                        ((S * (u**j) * (d**(steps - j))) - K)))
        if option == 'put':
            for j in range(0, a):
                val = (val +
                       (comb(steps, j) * (p**j) * ((1 - p)**(steps - j)) *
                        (K - ((S * (u**j)) * (d**(steps - j))))))

        return np.exp(-r * T) * val
Ejemplo n.º 14
0
    def hull_white_88(**kwargs):
        """
        Hull White 1988 - Correlated Stochastic Volatility.

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sig0 : Float
            Initial Volatility. The default is 0.09 (9%).
        sigLR : Float
            Long run mean reversion level of volatility. The default
            is 0.0625 (6.25%).
        halflife : Float
            Half-life of volatility deviation. The default is 0.1.
        vvol : Float
            Vol of vol. The default is 0.5.
        rho : Float
            Correlation between asset price and volatility. The
            default is 0.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Option price.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sig0 = params['sig0']
            sigLR = params['sigLR']
            halflife = params['halflife']
            vvol = params['vvol']
            rho = params['rho']
            option = params['option']

        b = r - q
        # Find constant, beta, from Half-life
        beta = -np.log(2) / halflife

        # Find constant, a, from long run volatility
        a = -beta * (sigLR**2)
        delta = beta * T
        ed = np.exp(delta)
        v = sig0**2

        # Average expected variance
        if abs(beta) < 0.0001:
            vbar = v + 0.5 * a * T
        else:
            vbar = (v + (a / beta)) * ((ed - 1) / delta) - (a / beta)

        d1 = (np.log(S / K) + (b + (vbar / 2)) * T) / np.sqrt(vbar * T)
        d2 = d1 - np.sqrt(vbar * T)

        # standardised normal density function
        nd1 = (1 / np.sqrt(2 * np.pi)) * (np.exp(-d1**2 * 0.5))

        # Cumulative normal distribution function
        Nd1 = si.norm.cdf(d1, 0.0, 1.0)
        Nd2 = si.norm.cdf(d2, 0.0, 1.0)

        # Partial derivatives
        cSV = (-S * np.exp((b - r) * T) * nd1 * (d2 / (2 * vbar)))
        cVV = ((S * np.exp(
            (b - r) * T) * nd1 * np.sqrt(T) / (4 * vbar**1.5)) * (d1 * d2 - 1))

        cSVV = ((S * np.exp((b - r) * T) / (4 * vbar**2)) * nd1 *
                ((-d1 * (d2**2)) + d1 + (2 * d2)))

        cVVV = (((S * np.exp(
            (b - r) * T) * nd1 * np.sqrt(T)) / (8 * vbar**2.5)) *
                ((d1 * d2 - 1) * (d1 * d2 - 3) - ((d1**2) + (d2**2))))

        if abs(beta) < 0.0001:
            f1 = rho * ((a * T / 3) + v) * (T / 2) * cSV
            phi1 = (rho**2) * ((a * T / 4) + v) * ((T**3) / 6)
            phi2 = (2 + (1 / (rho**2))) * phi1
            phi3 = (rho**2) * (((a * T / 3) + v)**2) * ((T**4) / 8)
            phi4 = 2 * phi3

        else:  # Beta different from zero
            phi1 = (((rho**2) / (beta**4)) *
                    (((a + (beta * v)) *
                      ((ed * (((delta**2) / 2) - delta + 1)) - 1)) +
                     (a * ((ed * (2 - delta)) - (2 + delta)))))

            phi2 = ((2 * phi1) +
                    ((1 / (2 * (beta**4))) *
                     (((a + (beta * v)) * ((ed**2) - (2 * delta * ed) - 1)) -
                      ((a / 2) * ((ed**2) - (4 * ed) + (2 * delta) + 3)))))

            phi3 = (((rho**2) / (2 * (beta**6))) *
                    ((((a + (beta * v)) * (ed - delta * ed - 1)) -
                      (a * (1 + delta - ed)))**2))

            phi4 = 2 * phi3

            f1 = ((rho / ((beta**3) * T)) * (((a + (beta * v)) *
                                              (1 - ed + (delta * ed))) +
                                             (a * (1 + delta - ed))) * cSV)

        f0 = S * np.exp((b - r) * T) * Nd1 - (K * np.exp(-r * T) * Nd2)

        f2 = (((phi1 / T) * cSV) + ((phi2 / (T**2)) * cVV) +
              ((phi3 / (T**2)) * cSVV) + ((phi4 / (T**3)) * cVVV))

        callvalue = f0 + f1 * vvol + f2 * vvol**2

        if option == 'call':
            result = callvalue
        else:
            result = (callvalue - (S * np.exp(
                (b - r) * T)) + (K * np.exp(-r * T)))

        return result
Ejemplo n.º 15
0
    def trinomial_tree(**kwargs):
        """
        Trinomial Tree

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        steps : Int
            Number of time steps. The default is 1000.
        option : Str
            Type of option, 'put' or 'call'. The default is 'call'.
        output_flag : Str
            Whether to return 'price', 'delta', 'gamma', 'theta' or
            'all'. The default is 'price'.
        american : Bool
            Whether the option is American. The default is False.

        Returns
        -------
        result : Various
            Depending on output flag:
                'price' : Float; Option Price
                'delta' : Float; Option Delta
                'gamma' : Float; Option Gamma
                'theta' : Float; Option Theta
                'all' : Tuple; Option Price, Option Delta, Option Gamma,
                        Option Theta

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            steps = params['steps']
            option = params['option']
            output_flag = params['output_flag']
            american = params['american']

        if option == 'call':
            z = 1
        else:
            z = -1

        b = r - q
        dt = T / steps
        u = np.exp(sigma * np.sqrt(2 * dt))
        d = np.exp(-sigma * np.sqrt(2 * dt))
        pu = ((np.exp(b * dt / 2) - np.exp(-sigma * np.sqrt(dt / 2))) /
              (np.exp(sigma * np.sqrt(dt / 2)) -
               np.exp(-sigma * np.sqrt(dt / 2))))**2
        pd = ((np.exp(sigma * np.sqrt(dt / 2)) - np.exp(b * dt / 2)) /
              (np.exp(sigma * np.sqrt(dt / 2)) -
               np.exp(-sigma * np.sqrt(dt / 2))))**2
        pm = 1 - pu - pd
        df = np.exp(-r * dt)
        optionvalue = np.zeros((steps * 2 + 2))
        returnvalue = np.zeros((4))

        for i in range(2 * steps + 1):
            optionvalue[i] = max(
                0,
                z * (S * (u**max(i - steps, 0)) * (d**(max(
                    (steps - i), 0))) - K))

        for j in range(steps - 1, -1, -1):
            for i in range(j * 2 + 1):

                optionvalue[i] = (pu * optionvalue[i + 2] +
                                  pm * optionvalue[i + 1] +
                                  pd * optionvalue[i]) * df

                if american:
                    optionvalue[i] = max(
                        z * (S * (u**max(i - j, 0)) * (d**(max(
                            (j - i), 0))) - K), optionvalue[i])

            if j == 1:
                returnvalue[1] = ((optionvalue[2] - optionvalue[0]) /
                                  (S * u - S * d))

                returnvalue[2] = (((optionvalue[2] - optionvalue[1]) /
                                   (S * u - S) -
                                   (optionvalue[1] - optionvalue[0]) /
                                   (S - S * d)) / (0.5 * ((S * u) - (S * d))))

                returnvalue[3] = optionvalue[0]

        returnvalue[3] = (returnvalue[3] - optionvalue[0]) / dt / 365

        returnvalue[0] = optionvalue[0]

        if output_flag == 'price':
            result = returnvalue[0]
        if output_flag == 'delta':
            result = returnvalue[1]
        if output_flag == 'gamma':
            result = returnvalue[2]
        if output_flag == 'theta':
            result = returnvalue[3]
        if output_flag == 'all':
            result = {
                'Price': returnvalue[0],
                'Delta': returnvalue[1],
                'Gamma': returnvalue[2],
                'Theta': returnvalue[3]
            }

        return result
Ejemplo n.º 16
0
    def hull_white_87(**kwargs):
        """
        Hull White 1987 - Uncorrelated Stochastic Volatility.

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        vvol : Float
            Vol of vol. The default is 0.5.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Option price.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            vvol = params['vvol']
            option = params['option']

        k = vvol**2 * T
        ek = np.exp(k)
        b = r - q
        d1 = ((np.log(S / K) + (b + (sigma**2) / 2) * T) /
              (sigma * np.sqrt(T)))
        d2 = d1 - sigma * np.sqrt(T)
        Nd1 = si.norm.cdf(d1, 0.0, 1.0)

        cgbs = AnalyticalMethods.black_scholes_merton(S=S,
                                                      K=K,
                                                      T=T,
                                                      r=r,
                                                      q=q,
                                                      sigma=sigma,
                                                      option='call',
                                                      refresh=True)

        # Partial Derivatives
        cVV = (S * np.exp(
            (b - r) * T) * np.sqrt(T) * Nd1 * (d1 * d2 - 1) / (4 * (sigma**3)))

        cVVV = (S * np.exp((b - r) * T) * np.sqrt(T) * Nd1 *
                ((d1 * d2 - 1) * (d1 * d2 - 3) - ((d1**2) +
                                                  (d2**2))) / (8 * (sigma**5)))

        callvalue = (cgbs + 1 / 2 * cVV * (2 * sigma**4 *
                                           (ek - k - 1) / k**2 - sigma**4) +
                     (1 / 6 * cVVV * sigma**6 *
                      (ek**3 - (9 + 18 * k) * ek + 8 + 24 * k + 18 * k**2 +
                       (6 * k**3)) / (3 * k**3)))

        if option == 'call':
            result = callvalue

        if option == 'put':  # use put-call parity
            result = callvalue - S * np.exp((b - r) * T) + K * np.exp(-r * T)

        return result
Ejemplo n.º 17
0
    def implied_trinomial_tree(cls, **kwargs):
        """
        Implied Trinomial Tree

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        steps_itt : Int
            Number of time steps. The default is 10.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        output_flag : Str
            UPM: A matrix of implied up transition probabilities
            UPni: The implied up transition probability at a single
                  node
            DPM: A matrix of implied down transition probabilities
            DPni: The implied down transition probability at a single
                  node
            LVM: A matrix of implied local volatilities
            LVni: The local volatility at a single node
            ADM: A matrix of Arrow-Debreu prices at a single node
            ADni: The Arrow-Debreu price at a single node (at
                  time step - 'step' and state - 'state')
            price: The value of the European option
        step : Int
            Time step used for Arrow Debreu price at single node. The
            default is 5.
        state : Int
            State position used for Arrow Debreu price at single node.
            The default is 5.
        skew : Float
            Rate at which volatility increases (decreases) for every
            one point decrease
            (increase) in the strike price. The default is 0.0004.

        Returns
        -------
        result : Various
            Depending on output flag:
                UPM: A matrix of implied up transition probabilities
                UPni: The implied up transition probability at a single
                      node
                DPM: A matrix of implied down transition probabilities
                DPni: The implied down transition probability at a
                      single node
                LVM: A matrix of implied local volatilities
                LVni: The local volatility at a single node
                ADM: A matrix of Arrow-Debreu prices at a single node
                ADni: The Arrow-Debreu price at a single node (at
                      time step - 'step' and state - 'state')
                price: The European option price.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            steps_itt = params['steps_itt']
            option = params['option']
            output_flag = params['output_flag']
            step = params['step']
            state = params['state']
            skew = params['skew']

        if option == 'call':
            z = 1
        else:
            z = -1

        optionvaluenode = np.zeros((steps_itt * 2 + 1))
        # Arrow Debreu prices
        ad = np.zeros((steps_itt + 1, steps_itt * 2 + 1), dtype='float')
        pu = np.zeros((steps_itt, steps_itt * 2 - 1), dtype='float')
        pd = np.zeros((steps_itt, steps_itt * 2 - 1), dtype='float')
        localvol = np.zeros((steps_itt, steps_itt * 2 - 1), dtype='float')

        dt = T / steps_itt
        u = np.exp(sigma * np.sqrt(2 * dt))
        d = 1 / u
        df = np.exp(-r * dt)
        ad[0, 0] = 1

        for n in range(steps_itt):
            for i in range(n * 2 + 1):
                val = 0
                Si1 = (S * (u**(max(i - n, 0))) * (d**(max(n * 2 - n - i, 0))))
                Si = Si1 * d
                Si2 = Si1 * u
                b = r - q
                Fi = Si1 * np.exp(b * dt)
                sigmai = sigma + (S - Si1) * skew

                if i < (n * 2) / 2 + 1:
                    for j in range(i):
                        Fj = (S * (u**(max(j - n, 0))) *
                              (d**(max(n * 2 - n - j, 0))) * np.exp(b * dt))

                        val = val + ad[n, j] * (Si1 - Fj)

                    optionvalue = cls.trinomial_tree(S=S,
                                                     K=Si1,
                                                     T=(n + 1) * dt,
                                                     r=r,
                                                     q=q,
                                                     sigma=sigmai,
                                                     steps=(n + 1),
                                                     option='put',
                                                     output_flag='price',
                                                     american=False,
                                                     refresh=True)

                    qi = ((np.exp(r * dt) * optionvalue - val) / (ad[n, i] *
                                                                  (Si1 - Si)))

                    pi = (Fi + qi * (Si1 - Si) - Si1) / (Si2 - Si1)

                else:
                    optionvalue = cls.trinomial_tree(S=S,
                                                     K=Si1,
                                                     T=(n + 1) * dt,
                                                     r=r,
                                                     q=q,
                                                     sigma=sigmai,
                                                     steps=(n + 1),
                                                     option='call',
                                                     output_flag='price',
                                                     american=False,
                                                     refresh=True)

                    val = 0
                    for j in range(i + 1, n * 2 + 1):
                        Fj = (S * (u**(max(j - n, 0))) *
                              (d**(max(n * 2 - n - j, 0))) * np.exp(b * dt))

                        val = val + ad[n, j] * (Fj - Si1)

                    pi = ((np.exp(r * dt) * optionvalue - val) / (ad[n, i] *
                                                                  (Si2 - Si1)))

                    qi = (Fi - pi * (Si2 - Si1) - Si1) / (Si - Si1)

                # Replacing negative probabilities
                if pi < 0 or pi > 1 or qi < 0 or qi > 1:
                    if Si2 > Fi > Si1:
                        pi = (1 / 2 * ((Fi - Si1) / (Si2 - Si1) + (Fi - Si) /
                                       (Si2 - Si)))

                        qi = 1 / 2 * ((Si2 - Fi) / (Si2 - Si))

                    elif Si1 > Fi > Si:
                        pi = 1 / 2 * ((Fi - Si) / (Si2 - Si))

                        qi = (1 / 2 * ((Si2 - Fi) / (Si2 - Si1) + (Si1 - Fi) /
                                       (Si1 - Si)))

                pd[n, i] = qi
                pu[n, i] = pi

                # Calculating local volatilities
                Fo = (pi * Si2 + qi * Si + (1 - pi - qi) * Si1)
                localvol[n, i] = np.sqrt(
                    (pi * (Si2 - Fo)**2 + (1 - pi - qi) * (Si1 - Fo)**2 + qi *
                     (Si - Fo)**2) / (Fo**2 * dt))

                # Calculating Arrow-Debreu prices
                if n == 0:
                    ad[n + 1, i] = qi * ad[n, i] * df
                    ad[n + 1, i + 1] = (1 - pi - qi) * ad[n, i] * df
                    ad[n + 1, i + 2] = pi * ad[n, i] * df

                elif n > 0 and i == 0:
                    ad[n + 1, i] = qi * ad[n, i] * df

                elif n > 0 and i == n * 2:
                    ad[n + 1, i] = (pu[n, i - 2] * ad[n, i - 2] * df +
                                    (1 - pu[n, i - 1] - pd[n, i - 1]) *
                                    (ad[n, i - 1]) * df + qi * (ad[n, i] * df))
                    ad[n + 1, i + 1] = (pu[n, i - 1] * (ad[n, i - 1]) * df +
                                        (1 - pi - qi) * (ad[n, i] * df))
                    ad[n + 1, i + 2] = pi * ad[n, i] * df

                elif n > 0 and i == 1:
                    ad[n + 1, i] = ((1 - pu[n, i - 1] -
                                     (pd[n, i - 1])) * ad[n, i - 1] * df +
                                    (qi * ad[n, i] * df))

                else:
                    ad[n + 1, i] = (pu[n, i - 2] * (ad[n, i - 2]) * df +
                                    (1 - pu[n, i - 1] - pd[n, i - 1]) *
                                    (ad[n, i - 1]) * df + qi * (ad[n, i]) * df)

        # Calculation of option price using the implied trinomial tree
        for i in range(2 * steps_itt + 1):
            optionvaluenode[i] = max(
                0,
                z * (S * (u**max(i - steps_itt, 0)) * (d**(max(
                    (steps_itt - i), 0))) - K))

        for n in range(steps_itt - 1, -1, -1):
            for i in range(n * 2 + 1):
                optionvaluenode[i] = ((pu[n, i] * optionvaluenode[i + 2] +
                                       (1 - pu[n, i] - pd[n, i]) *
                                       (optionvaluenode[i + 1]) + pd[n, i] *
                                       (optionvaluenode[i])) * df)

        price = optionvaluenode[0]

        output_dict = {
            'UPM': pu,
            'UPni': pu[step, state],
            'DPM': pd,
            'DPni': pd[step, state],
            'LVM': localvol,
            'LVni': localvol[step, state],
            'ADM': ad,
            'ADni': ad[step, state],
            'price': price
        }

        return output_dict.get(output_flag,
                               "Please select a valid output flag")
Ejemplo n.º 18
0
    def implied_vol_newton_raphson(**kwargs):
        """
        Finds implied volatility using Newton-Raphson method - needs
        knowledge of partial derivative of option pricing formula
        with respect to volatility (vega)

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        cm : Float
            # Option price used to solve for vol. The default is 5.
        epsilon : Float
            Degree of precision. The default is 0.0001
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Implied Volatility.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            cm = params['cm']
            epsilon = params['epsilon']
            option = params['option']

        # Manaster and Koehler seed value
        vi = np.sqrt(abs(np.log(S / K) + r * T) * (2 / T))

        ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                    K=K,
                                                    T=T,
                                                    r=r,
                                                    q=q,
                                                    sigma=vi,
                                                    option=option,
                                                    refresh=True)

        vegai = AnalyticalMethods.black_scholes_merton_vega(S=S,
                                                            K=K,
                                                            T=T,
                                                            r=r,
                                                            q=q,
                                                            sigma=vi,
                                                            refresh=True)

        mindiff = abs(cm - ci)

        while abs(cm - ci) >= epsilon and abs(cm - ci) <= mindiff:
            vi = vi - (ci - cm) / vegai

            ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                        K=K,
                                                        T=T,
                                                        r=r,
                                                        q=q,
                                                        sigma=vi,
                                                        option=option,
                                                        refresh=True)

            vegai = AnalyticalMethods.black_scholes_merton_vega(S=S,
                                                                K=K,
                                                                T=T,
                                                                r=r,
                                                                q=q,
                                                                sigma=vi,
                                                                refresh=True)

            mindiff = abs(cm - ci)

        if abs(cm - ci) < epsilon:
            result = vi
        else:
            result = 'NA'

        return result
Ejemplo n.º 19
0
    def cox_ross_rubinstein_binomial(**kwargs):
        """
        Cox-Ross-Rubinstein Binomial model

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        steps : Int
            Number of time steps. The default is 1000.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        output_flag : Str
            Whether to return 'price', 'delta', 'gamma', 'theta' or
            'all'. The default is 'price'.
        american : Bool
            Whether the option is American. The default is False.

        Returns
        -------
        result : Various
            Depending on output flag:
                'price' : Float; Option Price
                'delta' : Float; Option Delta
                'gamma' : Float; Option Gamma
                'theta' : Float; Option Theta
                'all' : Tuple; Option Price, Option Delta, Option
                        Gamma, Option Theta

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            steps = params['steps']
            option = params['option']
            output_flag = params['output_flag']
            american = params['american']

        if option == 'call':
            z = 1
        else:
            z = -1

        b = r - q
        dt = T / steps
        u = np.exp(sigma * np.sqrt(dt))
        d = 1 / u
        p = (np.exp(b * dt) - d) / (u - d)
        df = np.exp(-r * dt)
        optionvalue = np.zeros((steps + 2))
        returnvalue = np.zeros((4))

        for i in range(steps + 1):
            optionvalue[i] = max(0, z * (S * (u**i) * (d**(steps - i)) - K))

        for j in range(steps - 1, -1, -1):
            for i in range(j + 1):
                if american:
                    optionvalue[i] = ((p * optionvalue[i + 1]) +
                                      ((1 - p) * optionvalue[i])) * df
                else:
                    optionvalue[i] = max((z * (S * (u**i) * (d**(j - i)) - K)),
                                         ((p * optionvalue[i + 1]) +
                                          ((1 - p) * optionvalue[i])) * df)

            if j == 2:
                returnvalue[2] = (((optionvalue[2] - optionvalue[1]) /
                                   (S * (u**2) - S) -
                                   (optionvalue[1] - optionvalue[0]) /
                                   (S - S * (d**2))) / (0.5 * (S * (u**2) - S *
                                                               (d**2))))

                returnvalue[3] = optionvalue[1]

            if j == 1:
                returnvalue[1] = ((optionvalue[1] - optionvalue[0]) /
                                  (S * u - S * d))

        returnvalue[3] = (returnvalue[3] - optionvalue[0]) / (2 * dt) / 365
        returnvalue[0] = optionvalue[0]

        if output_flag == 'price':
            result = returnvalue[0]
        if output_flag == 'delta':
            result = returnvalue[1]
        if output_flag == 'gamma':
            result = returnvalue[2]
        if output_flag == 'theta':
            result = returnvalue[3]
        if output_flag == 'all':
            result = {
                'Price': returnvalue[0],
                'Delta': returnvalue[1],
                'Gamma': returnvalue[2],
                'Theta': returnvalue[3]
            }

        return result
Ejemplo n.º 20
0
    def implied_vol_naive_verbose(**kwargs):
        """
        Finds implied volatility using simple naive iteration,
        increasing precision each time the difference changes sign.

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        cm : Float
            # Option price used to solve for vol. The default is 5.
        epsilon : Float
            Degree of precision. The default is 0.0001
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Implied Volatility.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            cm = params['cm']
            epsilon = params['epsilon']
            option = params['option']

        vi = 0.2
        ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                    K=K,
                                                    T=T,
                                                    r=r,
                                                    q=q,
                                                    sigma=vi,
                                                    option=option,
                                                    refresh=True)

        price_diff = cm - ci
        if price_diff > 0:
            flag = 1
        else:
            flag = -1
        while abs(price_diff) > epsilon:
            while price_diff * flag > 0:
                ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                            K=K,
                                                            T=T,
                                                            r=r,
                                                            q=q,
                                                            sigma=vi,
                                                            option=option,
                                                            refresh=True)

                price_diff = cm - ci
                vi += (0.01 * flag)

            while price_diff * flag < 0:
                ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                            K=K,
                                                            T=T,
                                                            r=r,
                                                            q=q,
                                                            sigma=vi,
                                                            option=option,
                                                            refresh=True)

                price_diff = cm - ci
                vi -= (0.001 * flag)

            while price_diff * flag > 0:
                ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                            K=K,
                                                            T=T,
                                                            r=r,
                                                            q=q,
                                                            sigma=vi,
                                                            option=option,
                                                            refresh=True)

                price_diff = cm - ci
                vi += (0.0001 * flag)

            while price_diff * flag < 0:
                ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                            K=K,
                                                            T=T,
                                                            r=r,
                                                            q=q,
                                                            sigma=vi,
                                                            option=option,
                                                            refresh=True)

                price_diff = cm - ci
                vi -= (0.00001 * flag)

            while price_diff * flag > 0:
                ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                            K=K,
                                                            T=T,
                                                            r=r,
                                                            q=q,
                                                            sigma=vi,
                                                            option=option,
                                                            refresh=True)

                price_diff = cm - ci
                vi += (0.000001 * flag)

            while price_diff * flag < 0:
                ci = AnalyticalMethods.black_scholes_merton(S=S,
                                                            K=K,
                                                            T=T,
                                                            r=r,
                                                            q=q,
                                                            sigma=vi,
                                                            option=option,
                                                            refresh=True)

                price_diff = cm - ci
                vi -= (0.0000001 * flag)

        result = vi

        return result
Ejemplo n.º 21
0
    def european_monte_carlo(**kwargs):
        """
        Standard Monte Carlo

        Parameters
        ----------
        S : Float
            Stock Price. The default is 100.
        K : Float
            Strike Price. The default is 100.
        T : Float
            Time to Maturity.  The default is 0.25 (3 Months).
        r : Float
            Interest Rate. The default is 0.005 (50bps)
        q : Float
            Dividend Yield.  The default is 0.
        sigma : Float
            Implied Volatility.  The default is 0.2 (20%).
        simulations : Int
            Number of Monte Carlo runs. The default is 10000.
        option : Str
            Type of option. 'put' or 'call'. The default is 'call'.
        default : Bool
            Whether the function is being called directly (in which
            case values that are not supplied are set to default
            values) or called from another function where they have
            already been updated.

        Returns
        -------
        result : Float
            Option Price.

        """

        # Update pricing input parameters to default if not supplied
        if 'refresh' in kwargs and kwargs['refresh']:
            params = Utils.init_params(kwargs)
            S = params['S']
            K = params['K']
            T = params['T']
            r = params['r']
            q = params['q']
            sigma = params['sigma']
            simulations = params['simulations']
            option = params['option']

        if option == 'call':
            z = 1
        else:
            z = -1

        b = r - q
        Drift = (b - (sigma**2) / 2) * T
        sigmarT = sigma * np.sqrt(T)
        val = 0

        counter = 1
        while counter < simulations + 1:
            St = S * np.exp(Drift + sigmarT *
                            norm.ppf(random.random(), loc=0, scale=1))
            val = val + max(z * (St - K), 0)
            counter += 1

        result = np.exp(-r * T) * val / simulations

        return result