import pandas as pd

from app.entity.product.ChartDataBean import *

from app.model.BlackScholesCalculator import *
from app.model.MonteCarloEuropean import *
from app.model.BinomialEuropean import *

from app.entity.ConstantValue import ConstantValue

class EuropeanOptionCalculator:
    @staticmethod
    def calculate(calculatorDataBean):
        stockPrice = float(calculatorDataBean.stockPrice)
        strike = float(calculatorDataBean.strike)
        expiryYears = float(calculatorDataBean.expiration)
        if calculatorDataBean.maturityUnit == 'InDays':
            expiryYears = expiryYears / 365.
        volatility = float(calculatorDataBean.volatility)
        dividendYield = float(calculatorDataBean.dividendYield)
        riskfreeRate = float(calculatorDataBean.riskfreeRate)
                
        bs = BlackScholesCalculator()
        
        if calculatorDataBean.model == ConstantValue.BINOMIAL_TREE:
            bt = BinomialEuropean()
            
            calculatorDataBean.callValue = bt.getValue('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            calculatorDataBean.putValue = bt.getValue('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        elif calculatorDataBean.model == ConstantValue.MONTE_CARLO:
            mc = MonteCarloEuropean()
            
            calculatorDataBean.callValue = mc.getValue('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            calculatorDataBean.putValue = mc.getValue('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        else:
            calculatorDataBean.callValue = bs.getValue('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            calculatorDataBean.putValue = bs.getValue('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            
        calculatorDataBean.callDelta = bs.getDelta('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        calculatorDataBean.callGamma = bs.getGamma(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        calculatorDataBean.callTheta = bs.getTheta('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        calculatorDataBean.callVega = bs.getVega(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        calculatorDataBean.callRho = bs.getRho('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)

        calculatorDataBean.putDelta = bs.getDelta('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        calculatorDataBean.putTheta = bs.getTheta('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        calculatorDataBean.putGamma = calculatorDataBean.callGamma
        calculatorDataBean.putVega = calculatorDataBean.callVega
        calculatorDataBean.putRho = bs.getRho('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
        
        return calculatorDataBean


    @staticmethod
    def calculateChart(calculatorDataBean):
        stockPrice = float(calculatorDataBean.stockPrice)
        strike = float(calculatorDataBean.strike)
        expiryYears = float(calculatorDataBean.expiration)
        if calculatorDataBean.maturityUnit == 'InDays':
            expiryYears = expiryYears / 365.
        volatility = float(calculatorDataBean.volatility)
        dividendYield = float(calculatorDataBean.dividendYield)
        riskfreeRate = float(calculatorDataBean.riskfreeRate)
                
        bs = BlackScholesCalculator()
        
        initialValue = 0
        if calculatorDataBean.axisName == 'strike':
            initialValue = strike
            xAxisName = 'Strike'
        elif calculatorDataBean.axisName == 'volatility':
            initialValue = volatility * 100.
            xAxisName = 'Volatility(%)'
        elif calculatorDataBean.axisName == 'expiration':
            initialValue = expiryYears * 365.
            xAxisName = 'Years to Expiry'
        else:  
            initialValue = stockPrice
            xAxisName = 'Stock Price'
        
        if calculatorDataBean.axisMin > 0:
            plMin = calculatorDataBean.axisMin
        else:
            plMin = initialValue * 0.1
        if calculatorDataBean.axisMax > 0:
            plMax = calculatorDataBean.axisMax
        else:
            plMax = initialValue * 2
        
        
        priceStep = (float(plMax) - float(plMin)) / 36.

        if calculatorDataBean.axisName == 'expiration':
            plMin = round(plMin)
            priceStep = round(priceStep)
            if plMin < 1.: plMin = 1.
            priceStep = max(priceStep, 1)
            plMax = max(plMax, plMin + 35 * priceStep)
        else:
            plMin = round(plMin, 2)
            priceStep = round(priceStep, 2)
            if plMin < 0.01: plMin = 0.01
            priceStep = max(priceStep, 0.01)
            plMax = max(plMax, plMin + 35 * priceStep)
        
        
        calculatorDataBean.axisMin = plMin
        calculatorDataBean.axisMax = plMax
                

        plCurrent = plMin
        dfcolumns = [xAxisName, 'Call Value', 'Call Delta', 'Call Gamma', 'Call Theta', 'Call Vega', 'Call Rho',
                     'Put Value', 'Put Delta', 'Put Gamma', 'Put Theta', 'Put Vega', 'Put Rho']
        results = pd.DataFrame([], columns=dfcolumns)
    
        while (plCurrent <= plMax):
            if calculatorDataBean.axisName == 'expiration':
                expiryYears = plCurrent / 365.
            elif calculatorDataBean.axisName == 'volatility':
                volatility = plCurrent / 100
            elif calculatorDataBean.axisName == 'strike':
                strike = plCurrent
            else:  
                stockPrice = plCurrent
            
            callValue = bs.getValue('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            callDelta = bs.getDelta('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            callGamma = bs.getGamma(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            callTheta = bs.getTheta('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            callVega = bs.getVega(stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            callRho = bs.getRho('call', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)

            putValue = bs.getValue('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            putDelta = bs.getDelta('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            putGamma = callGamma
            putTheta = bs.getTheta('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            putVega = callVega
            putRho = bs.getRho('put', stockPrice, strike, volatility, expiryYears, dividendYield, riskfreeRate)
            
            theResult = pd.DataFrame([[plCurrent, callValue, callDelta, callGamma, callTheta, callVega, callRho, 
                                       putValue, putDelta, putGamma, putTheta, putVega, putRho]], columns=dfcolumns)
            results = results.append(theResult)
        
            plCurrent += priceStep
        
        chartValueDict = {'chartTitles':dfcolumns, 'chartValues': results}
        return ChartDataBean(**chartValueDict)

