
import numpy as np

from scipy.stats import norm


class BlackScholesNPCalculator(object):
    def getD1(self, stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate):
        d1 = (np.log(stockPrice / strike) + (riskfreeRate - dividendYield + np.power(volatility, 2) / 2.) * expiryYears) / \
                (volatility * np.sqrt(expiryYears));
        
        return d1
    
    def getD2(self, d1, volatility, expiryYears):
        d2 = d1 - volatility * np.sqrt(expiryYears)
        return d2
    
    def getValue(self, optionType, stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate):
        d1 = self.getD1(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        d2 = self.getD2(d1, volatility, expiryYears)
        
        if (optionType == ConstantValue.CALL):
            premium = stockPrice * np.exp(-1. * dividendYield * expiryYears) * norm.cdf(d1) - \
                    strike * np.exp(-1. * riskfreeRate * expiryYears) * norm.cdf(d2);
        else:
            premium = strike * np.exp(-1. * riskfreeRate * expiryYears) * norm.cdf(-d2) - \
                    stockPrice * np.exp(-1. * dividendYield * expiryYears) * norm.cdf(-d1);
                    
        return premium
    
    def getDelta(self, optionType, stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate):
        d1 = self.getD1(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate);
        if (optionType == ConstantValue.CALL):
            delta = np.exp(-1. * dividendYield * expiryYears) * norm.cdf(d1)
        else:
            delta = -1. * np.exp(-1. * dividendYield * expiryYears) * norm.cdf(-d1)

        return delta
    
    def getGamma(self, stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate):
        d1 = self.getD1(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        gamma = np.exp(-1. * dividendYield * expiryYears) * norm.pdf(d1) / \
                (stockPrice * volatility * np.sqrt(expiryYears))
        return gamma
    
    def getVega(self, stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate):
        d1 = self.getD1(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate);
        vega = stockPrice * np.exp(-1. * dividendYield * expiryYears) * norm.pdf(d1) * np.sqrt(expiryYears);
        return vega/100.
    
    def getTheta(self, optionType, stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate):
        d1 = self.getD1(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate);
        d2 = self.getD2(d1, volatility, expiryYears)
        
        phase1 = stockPrice * np.exp(-1. * dividendYield * expiryYears) * volatility * norm.pdf(d1) /\
                (2. * np.sqrt(expiryYears))
        
        if (optionType == ConstantValue.CALL):
            phase2 = riskfreeRate * strike * np.exp(-1. * riskfreeRate * expiryYears) * norm.cdf(d2)
            phase3 = dividendYield * stockPrice * np.exp(-1. * dividendYield * expiryYears) * norm.cdf(d1)
            
            theta = -phase1 - phase2 + phase3
        else:
            phase2 = riskfreeRate * strike * np.exp(-1. * riskfreeRate * expiryYears) * norm.cdf(-d2)
            phase3 = dividendYield * stockPrice * np.exp(-1. * dividendYield * expiryYears) * norm.cdf(-d1)

            theta = -phase1 + phase2 - phase3

        return theta / 365.
    
    def getRho(self, optionType, stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate):
        d1 = self.getD1(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate);
        d2 = self.getD2(d1, volatility, expiryYears)
        if (optionType == ConstantValue.CALL):
            rho = strike * expiryYears * np.exp(-riskfreeRate * expiryYears) * norm.cdf(d2)
        else:
            rho = -1. * strike * expiryYears * np.exp(-riskfreeRate * expiryYears) * norm.cdf(-d2)
        
        return rho / 100.
        