import pandas as pd
from sets import Set

from app.entity.strategy.StrategyCalculatorDataBean import *
from app.model.BlackScholesCalculator import *
from app.entity.ConstantValue import *


class StrategyCalculatorProcessor:
    @staticmethod
    # calculate by TTM
    def calculateOptions(strategyCalculatorDataBean):
        bs = BlackScholesCalculator()

        market = strategyCalculatorDataBean.marketInfo
        scenario = strategyCalculatorDataBean.scenario
        portfolio = strategyCalculatorDataBean.portfolio
        
        price = market.stockPrice
        dividendYield = market.dividendYield
        riskfreeRate = market.riskfreeRate
        optionMultiplier = market.optionMultiplier
        
        for posi in portfolio:
            tradeDirection = 1. if posi.buyOrSell == ConstantValue.BUY else -1.
            
            if posi.callOrPutOrStock == ConstantValue.STOCK:
                posi.value = tradeDirection * posi.quantity * price
                posi.delta = tradeDirection * posi.quantity * 1.
            else:
                posi.value = optionMultiplier * tradeDirection * posi.quantity * bs.getValue(posi.callOrPutOrStock, price, posi.strike, posi.volatility, posi.expiryDays / 365., dividendYield, riskfreeRate)
                posi.delta = optionMultiplier * tradeDirection * posi.quantity * bs.getDelta(posi.callOrPutOrStock, price, posi.strike, posi.volatility, posi.expiryDays / 365., dividendYield, riskfreeRate)
                posi.delta *= price
                posi.gamma = optionMultiplier * tradeDirection * posi.quantity * bs.getGamma(price, posi.strike, posi.volatility, posi.expiryDays / 365., dividendYield, riskfreeRate)
                posi.gamma = posi.gamma * 0.5 * price * price 
                posi.theta = optionMultiplier * tradeDirection * posi.quantity * bs.getTheta(posi.callOrPutOrStock, price, posi.strike, posi.volatility, posi.expiryDays / 365., dividendYield, riskfreeRate)
                posi.vega = optionMultiplier * tradeDirection * posi.quantity * bs.getVega(price, posi.strike, posi.volatility, posi.expiryDays / 365., dividendYield, riskfreeRate)
                posi.rho = optionMultiplier * tradeDirection * posi.quantity * bs.getRho(posi.callOrPutOrStock, price, posi.strike, posi.volatility, posi.expiryDays / 365., dividendYield, riskfreeRate)
            
            posi.value *= -1.
        
    @staticmethod
    def calculateOptionsByDaysElapsed(strategyCalculatorDataBean):
        bs = BlackScholesCalculator()

        market = strategyCalculatorDataBean.marketInfo
        scenario = strategyCalculatorDataBean.scenario
        portfolio = strategyCalculatorDataBean.scenarioResults
        
        price = scenario.stockPrice
        if price <= 0:
            price = market.stockPrice

        daysFromToday = scenario.daysFromToday
        volatility = scenario.volatility
        riskfreeRate = scenario.riskfreeRate
        dividendYield = scenario.dividendYield
        
        optionMultiplier = market.optionMultiplier
        
        for posi in portfolio:
            tradeDirection = 1. if posi.buyOrSell == ConstantValue.BUY else -1.
            
            if posi.callOrPutOrStock == ConstantValue.STOCK:
                posi.value = tradeDirection * posi.quantity * price
                posi.delta = tradeDirection * posi.quantity * 1.
            else:
                expiry = max((posi.expiryDays - daysFromToday), 0.0001) / 365.
                if volatility <= 0:
                    volatility = posi.volatile
                    
                posi.value = optionMultiplier * tradeDirection * posi.quantity * bs.getValue(posi.callOrPutOrStock, price, posi.strike, volatility, expiry, dividendYield, riskfreeRate)
                posi.delta = optionMultiplier * tradeDirection * posi.quantity * bs.getDelta(posi.callOrPutOrStock, price, posi.strike, volatility, expiry, dividendYield, riskfreeRate)
                posi.delta *= price
                posi.gamma = optionMultiplier * tradeDirection * posi.quantity * bs.getGamma(price, posi.strike, volatility, expiry, dividendYield, riskfreeRate)
                posi.gamma = posi.gamma * 0.5 * price * price 
                posi.theta = optionMultiplier * tradeDirection * posi.quantity * bs.getTheta(posi.callOrPutOrStock, price, posi.strike, volatility, expiry, dividendYield, riskfreeRate)
                posi.vega = optionMultiplier * tradeDirection * posi.quantity * bs.getVega(price, posi.strike, volatility, expiry, dividendYield, riskfreeRate)
                posi.rho = optionMultiplier * tradeDirection * posi.quantity * bs.getRho(posi.callOrPutOrStock, price, posi.strike, volatility, expiry, dividendYield, riskfreeRate)
            
            posi.value *= -1.
        
    
    @staticmethod
    def calculateChart(strategyCalculatorDataBean):
        bs = BlackScholesCalculator()

        market = strategyCalculatorDataBean.marketInfo
        scenario = strategyCalculatorDataBean.scenario
        portfolio = strategyCalculatorDataBean.portfolio
        
        optionMultiplier = market.optionMultiplier

        daysFromToday = scenario.daysFromToday
        plVolatility = scenario.volatility
        plRiskfreeRate = scenario.riskfreeRate
        
        # x-axis range
        stockPrice = scenario.stockPrice
        if stockPrice <= 0:
            stockPrice = market.stockPrice
        
        if strategyCalculatorDataBean.axisMin > 0:
            plMin = strategyCalculatorDataBean.axisMin
        else:
            plMin = stockPrice * 0.1
        if strategyCalculatorDataBean.axisMax > 0:
            plMax = strategyCalculatorDataBean.axisMax
        else:
            plMax = stockPrice * 2

        plMin = max(0.01, round(plMin, 2))
        
        priceStep = (float(plMax) - float(plMin)) / 36.
        priceStep = max(0.01, round(priceStep, 2))
        
        plMax = max(plMax, plMin + priceStep * 35.)
        
        # set Min and Max
        strategyCalculatorDataBean.axisMin = plMin
        strategyCalculatorDataBean.axisMax = plMax

        # series title
        if daysFromToday == 0:
            currentLabel = 'Today'
        else:
            strLabel = "{0:.0f}".format(daysFromToday)
            currentLabel = 'In ' + strLabel + '-days'
        
        dfcolumns = ['Stock Price', currentLabel, 'At Expiry', 'Time Value']
        results = pd.DataFrame([], columns=dfcolumns)
        
        xAxisSet = Set([])

        plCurrent = plMin
        while (plCurrent <= plMax):
            xAxisSet.add(plCurrent)
            plCurrent += priceStep

        for posi in portfolio:
            if posi.callOrPutOrStock != ConstantValue.STOCK:
                xAxisSet.add(posi.strike)
        
        xAxisList = list(xAxisSet)
        xAxisList.sort()
        #print 'xAxisList', xAxisList
        
        plCurrent = plMin
        #while (plCurrent <= plMax):
        for plCurrent in xAxisList:
            todayValue = 0.
            expiryValue = 0.
            totalPremium = 0.
        
            for posi in portfolio:
                tradeDirection = 1. if posi.buyOrSell == ConstantValue.BUY else -1.
                
                quantity = posi.quantity
                volatility = plVolatility if plVolatility > 0. else posi.volatility
                volatility = max(volatility, 0.0001)
                expiryYears = max((posi.expiryDays - daysFromToday), 0.0001) / 365.

                #totalPremium += posi.premium * tradeDirection * quantity
                totalPremium += posi.value
                
                if posi.callOrPutOrStock == ConstantValue.STOCK:
                    theTodayValue = plCurrent
                    theExpiryValue = plCurrent
                else:
                    theTodayValue = optionMultiplier * bs.getValue(posi.callOrPutOrStock,
                                plCurrent,
                                posi.strike,
                                volatility,
                                expiryYears,
                                market.dividendYield,
                                plRiskfreeRate)
                                
                    theExpiryValue = optionMultiplier * StrategyCalculatorProcessor.getProfitValue(
                                posi.callOrPutOrStock,
                                plCurrent,
                                posi.strike)
            
                todayValue += theTodayValue * tradeDirection * quantity
                expiryValue += theExpiryValue * tradeDirection * quantity
            
            theResult = pd.DataFrame([[plCurrent, todayValue + totalPremium, expiryValue + totalPremium, 0.]], columns=dfcolumns)
            #theResult = pd.DataFrame([[plCurrent, todayValue, expiryValue, 0.]], columns=dfcolumns)
            
            results = results.append(theResult)
        
            #plCurrent += priceStep
        
        results['Time Value'] = results[currentLabel] - results['At Expiry']
        
        chartValueDict = {'chartTitles':dfcolumns, 'chartValues': results}
        return ChartDataBean(**chartValueDict)

    @staticmethod
    def getProfitValue(optionType, stockPrice, strike):
        if optionType == ConstantValue.STOCK:
            return stockPrice
        elif optionType == ConstantValue.CALL:
            return max(stockPrice - strike, 0.)
        else:
            return max(strike - stockPrice, 0.)
        
 
