# Monte Carlo valuation of European option
#
import numpy as np
import math

from app.entity.ConstantValue import ConstantValue
from numpy import cumsum

class BinomialEuropean:

    def getValue(self, optionType, stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate):
        S0 = stockPrice
        K = strike
        T = expiryYears
        r = riskfreeRate
        sigma = volatility
        
        M = 500         # number of time steps
        
        dt = float(T) / float(M)    # length of time interval
        df = np.exp(-r * dt)           # discount factor per time
        
        # binomial parameters
        u = np.exp(sigma * np.sqrt(dt))   # up-movement
        d = 1 / u                   # down-movement
        q = (np.exp(r * dt) - d) / (u - d) # martingale probability
        
        # Index levels with Numpy
        mu = np.arange(M + 1)
        mu = np.resize(mu, (M + 1, M + 1))
        md = np.transpose(mu)
        mu = u ** (mu - md)
        md = d ** md
        ST = S0 * mu * md
        
        if (optionType == ConstantValue.CALL):
            pv = np.maximum(ST - K, 0)
        else:
            pv = np.maximum(K - ST, 0)
            
        z = 0
        for t in range(M - 1, -1, -1):  # backword interation
            pv[0:M - z, t] = (q * pv[0:M - z, t + 1] + \
                (1 - q) * pv[1:M - z + 1, t + 1]) * df
            z += 1
                    
        return pv[0, 0]
