i05nagai/mafipy

View on GitHub
mafipy/function/payoff.py

Summary

Maintainability
B
5 hrs
Test Coverage
#!/bin/python
# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import functools

from . import util


def payoff_call(underlying, strike, gearing=1.0):
    """payoff_call
    Payoff of call option.

    :param float underlying:
    :param float strike:
    :param float gearing: Coefficient of this option. Default value is 1.
    :return: payoff call option.
    :rtype: float
    """
    return gearing * max(underlying - strike, 0)


def payoff_call_fprime(underlying, strike, gearing=1.0):
    """payoff_call_fprime
    derivative of payoff of call option with respect to underlying.

    :param underlying:
    :param strike:
    :param gearing:
    :return: value of derivative with respect to underlying.
    :rtype: float
    """
    if underlying > strike:
        return gearing
    else:
        return 0.0


def payoff_put(underlying, strike, gearing=1.0):
    """payoff_put
    Payoff of put option.

    :param float underlying:
    :param float strike:
    :param float gearing: Coefficient of this option. Default value is 1.
    """
    return gearing * max(strike - underlying, 0)


def payoff_put_fprime(underlying, strike, gearing=1.0):
    """payoff_put_fprime
    derivative of payoff of call option with respect to underlying.

    :param underlying:
    :param strike:
    :param gearing:
    :return: value of derivative with respect to underlying.
    :rtype: float
    """
    if underlying < strike:
        return -gearing
    else:
        return 0.0


def payoff_bull_spread(underlying, lower_strike, upper_strike, gearing=1.0):
    """payoff_bull_spread
    Buy call option with lower_strike :math:`K_{\mathrm{lower}}`
    and sell put option with upper_strike :math:`K_{\mathrm{upper}}`.
    As the name denotes, lower_strike is lower than upper_strike.
    Payoff formula is as follows:

    .. math::

        g(\max(S - K_{\mathrm{lower}}, 0) - \min(S - K_{\mathrm{upper}}, 0))
        = g\min(K_{\mathrm{upper}}, \max(S - K_{\mathrm{lower}}))

    where :math:`S` is underlying, :math:`g` is gearing.

    :param float underlying:
    :param float lower_strike:
    :param float upper_strike:
    :param float gearing: coefficient of this option. Default value is 1.
    :return: payoff of bull spread option.
        If lower_strike >= upper_strike, then return 0.
    :rtype: float
    """
    if lower_strike >= upper_strike:
        return 0.0
    return gearing * min(upper_strike, max(underlying - lower_strike, 0))


def payoff_bull_spread_fprime(
        underlying,
        lower_strike,
        upper_strike,
        gearing=1.0):
    """payoff_bull_spread_fprime
    calculate value of derivative of payoff of bull spread option
    with respect to underlying.

    :param underlying:
    :param lower_strike:
    :param upper_strike:
    :param float gearing: coefficient of this option. Default value is 1.
    :return: derivative of bull spread option.
        If lower_strike >= upper_strike, then return 0.
    :rtype: float
    """
    if lower_strike >= upper_strike:
        return 0.0
    if lower_strike < underlying < upper_strike:
        return gearing
    else:
        return 0.0


def payoff_straddle(underlying, strike, gearing=1.0):
    """payoff_straddle
    Buy call option and put option at same time with same strike.

    :param float underlying:
    :param float strike:
    :param float gearing: Coefficient of this option. Default value is 1.
    """
    return (payoff_call(underlying, strike, gearing)
            + payoff_put(underlying, strike, gearing))


def payoff_strangle(underlying, lower_strike, upper_strike, gearing=1.0):
    """payoff_strangle
    Buy call option and put option at same time with different strike.

    :param float underlying:
    :param float lower_strike:
    :param float upper_strike:
    :param float gearing: Coefficient of this option. Default value is 1.
    :return: payoff of strangle option.
        If lower_strike >= upper_strike, then return 0.
    :rtype: float
    """
    if lower_strike >= upper_strike:
        return 0.0
    return (payoff_put(underlying, lower_strike, gearing)
            + payoff_call(underlying, upper_strike, gearing))


def payoff_butterfly_spread(underlying, spot_price, spread, gearing=1.0):
    """payoff_butterfly_spread
    Butterfly option consists of following options:

    * Buy 1 call with a (spot_price - spread)
    * Sell 2 calls with a spread
    * Buy 1 call with (spot_price + spread)

    :param float underlying:
    :param float spot_price: spot price of underlying.
    :param float spread: non-negative value is required.
        If value is 0, this is same as straddle.
    :param float gearing: coefficient of this option. Default value is 1.
    :return: payoff of butterfly spread option.
        If spot_price is negative, return 0.
    :rtype: float
    """
    if spread < 0.0:
        return 0.0
    return (payoff_call(underlying, spot_price - spread, gearing)
            - 2.0 * payoff_call(underlying, spot_price, gearing)
            + payoff_call(underlying, spot_price + spread, gearing))


def payoff_risk_reversal(underlying, lower_strike, upper_strike, gearing=1.0):
    """payoff_risk_reversal

    * Sell 1 out of the money put option.
    * Buy 1 out of the money call option.

    :param float underlying:
    :param float lower_strike:
    :param float upper_strike:
    :param float gearing: Coefficient of this option. Default value is 1.
    :return: payoff of risk reversal option.
        If lower_strike > upper_strike, then return 0.
        If lower_strike = upper_strike, this is called synthetic forward.
    :rtype: float
    """
    return (-payoff_put(underlying, lower_strike, gearing)
            + payoff_call(underlying, upper_strike, gearing))


class PayoffHelper(object):
    """PayoffFactory
    generate payoff function as a single variable.
    derivative is with respect to the variable.
    """

    def make_func(self):
        raise NotImplementedError

    def make_fprime(self):
        raise NotImplementedError

    def make_fhess(self):
        raise NotImplementedError


class CallStrikePayoffHelper(PayoffHelper):
    """CallStrikePayoffHelper
    generate call option payoff as a signle variable funciton of strike.
    """

    def __init__(self, **params):
        """__init__

        :param **params: underlying and gearing is required as key.
        """
        keys = ["underlying", "gearing"]
        self.params = util.check_keys(keys, params, locals())

    def make_func(self):
        return lambda strike: payoff_call(
            self.params["underlying"], strike, self.params["gearing"])

    def make_fprime(self):
        return lambda strike: payoff_call_fprime(
            self.params["underlying"], strike, self.params["gearing"])

    def make_fhess(self):
        return ValueError("dirac delta function")


class CallUnderlyingPayoffHelper(PayoffHelper):
    """CallUnderlyingPayoffHelper
    generate call option payoff as a single variable funciton of underlying.
    """

    def __init__(self, **params):
        """__init__

        :param **params: strike and gearing is required as key.
        """
        keys = ["strike", "gearing"]
        self.params = util.check_keys(keys, params, locals())

    def make_func(self):
        """make_func
        """
        return functools.partial(payoff_call, **self.params)

    def make_fprime(self):
        """make_fprime
        """
        return functools.partial(payoff_call_fprime, **self.params)

    def make_fhess(self):
        """make_fhess
        """
        raise ValueError("dirac delta function")


class BullSpreadUnderlyingPayoffHelper(PayoffHelper):
    """BullSpreadUnderlyingPayoffHelper
    generate bull-spread option payoff as a funciton of underlying.

    :param float lower_strike: required.
    :param float upper_strike: required.
    :param float gearing: required.
    """

    def __init__(self, **params):
        """__init__
        """
        keys = ["lower_strike", "upper_strike", "gearing"]
        self.params = util.check_keys(keys, params, locals())
        if self.params["lower_strike"] > self.params["upper_strike"]:
            raise ValueError("lower strike must be greater than upper strike")

    def make_func(self):
        """make_func
        """
        return functools.partial(payoff_bull_spread, **self.params)

    def make_fprime(self):
        """make_fprime
        """
        return functools.partial(payoff_bull_spread_fprime, **self.params)

    def make_fhess(self):
        """make_fhess
        """
        raise ValueError("dirac delta function")