import pandas as pd
import numpy as np
import pandas.io.data as web
import datetime
import math
import sys

from app.processor.stockprofiling.entity.StockProfilingDataBean import * 
from app.processor.stockprofiling.entity.StockReturnProfiles import * 
from app.processor.stockprofiling.entity.HistoricalVolatilities import * 
from app.processor.stockprofiling.entity.StandardDeviationBuckets import * 


# for debug
from flask import json
from app.resource.CustomerJSONEncoder import CustomerJSONEncoder

class StockProfilingProcessor:

    @staticmethod
    def calculate(ticker):
        #ticker = 'SPY'
        
        endDate = datetime.date.today().strftime("%m/%d/%Y")
        if datetime.date.today().month == 2 and datetime.date.today().day == 29:
            startDate = datetime.date.today().replace(month = 2, day = 28, year=datetime.date.today().year - 2).strftime("%m/%d/%Y")
        else: 
            startDate = datetime.date.today().replace(year=datetime.date.today().year - 2).strftime("%m/%d/%Y")
                
        try:
            stock = web.DataReader(ticker, 'yahoo', start = startDate, end =endDate)
        except Exception as e:
            print 'Error occur while reading stock data from Yahoo: ' + str(e)
            raise

        stock['Return'] = np.log(stock['Adj Close'] / stock['Adj Close'].shift(1))
        Volume30 = stock['Volume'][:-31:-1].mean()
        stock['MA50']  = pd.rolling_mean(stock['Adj Close'], 50)
        stock['MA200']  = pd.rolling_mean(stock['Adj Close'], 200)
        Up = stock['Return'][stock['Return'] > 0].count()
        Down = stock['Return'][stock['Return'] < 0].count()
        UpDown = Up/Down + 0.0
        
        MIN5 = stock['Adj Close'][:-6:-1].min()
        MIN30 = stock['Adj Close'][:-31:-1].min()
        MIN90 = stock['Adj Close'][:-91:-1].min()
        MIN252 = stock['Adj Close'][:-253:-1].min()
        MIN1YR = stock['Adj Close'][:-253:-1].min()
        MIN3YR = stock['Adj Close'].min()
        
        MAX5 = stock['Adj Close'][:-6:-1].max()
        MAX30 = stock['Adj Close'][:-31:-1].max()
        MAX90 = stock['Adj Close'][:-91:-1].max()
        MAX1YR = stock['Adj Close'][:-252:-1].max()
        MAX3YR = stock['Adj Close'].max()
        
        Volume5 = stock['Volume'][:-6:-1].mean()
        Volume90 = stock['Volume'][:-91:-1].mean()
        Volume252 = stock['Volume'][:-253:-1].mean()
        VolumePeriod = stock['Volume'].mean()
        
        Ret5 = math.log(stock['Adj Close'][-1] / stock['Adj Close'][-6])
        Ret30 = math.log(stock['Adj Close'][-1] / stock['Adj Close'][-31])
        Ret90 = math.log(stock['Adj Close'][-1] / stock['Adj Close'][-91])
        Ret252 = math.log(stock['Adj Close'][-1] / stock['Adj Close'][-253])
        RetPeriod = math.log(stock['Adj Close'][-1] / stock['Adj Close'][0])
        
        stock['Gap Return'] = np.log(stock['Open'] / stock['Close'].shift(1))
        Gap5 = stock['Gap Return'][:-6:-1].mean()
        Gap30 = stock['Gap Return'][:-31:-1].mean()
        Gap90 = stock['Gap Return'][:-91:-1].mean()
        Gap252 = stock['Gap Return'][:-253:-1].mean()
        GapPeriod = stock['Gap Return'].mean()
        
        Ret5MIN = stock['Return'][:-6:-1].min()
        Ret30MIN = stock['Return'][:-31:-1].min()
        Ret90MIN = stock['Return'][:-91:-1].min()
        Ret252MIN = stock['Return'][:-252:-1].min()
        RetPeriodMIN = stock['Return'].min()
        
        Ret5MAX = stock['Return'][:-6:-1].max()
        Ret30MAX = stock['Return'][:-31:-1].max()
        Ret90MAX = stock['Return'][:-91:-1].max()
        Ret252MAX = stock['Return'][:-253:-1].max()
        RetPeriodMAX = stock['Return'].max()
        
        AutoCorr5 = stock['Adj Close'][:-6:-1].autocorr()
        AutoCorr30 = stock['Adj Close'][:-31:-1].autocorr()
        AutoCorr90 = stock['Adj Close'][:-91:-1].autocorr()
        AutoCorr252 = stock['Adj Close'][:-253:-1].autocorr()
        AutoCorrPeriod = stock['Adj Close'].autocorr()

        stock['HV10'] = pd.rolling_std(stock['Return'],10) * np.sqrt(252)
        stock['HV20'] = pd.rolling_std(stock['Return'],20) * np.sqrt(252)
        stock['HV30'] = pd.rolling_std(stock['Return'],30) * np.sqrt(252)
        stock['HV60'] = pd.rolling_std(stock['Return'],60) * np.sqrt(252)
        stock['HV90'] = pd.rolling_std(stock['Return'],90) * np.sqrt(252)

        stock['xSTD10'] = stock['Return'] / (stock['HV10'].shift(1) / np.sqrt(252))
        stock['xSTD20'] = stock['Return'] / (stock['HV20'].shift(1) / np.sqrt(252))
        stock['xSTD30'] = stock['Return'] / (stock['HV30'].shift(1) / np.sqrt(252))
        stock['xSTD60'] = stock['Return'] / (stock['HV60'].shift(1) / np.sqrt(252))
        stock['xSTD90'] = stock['Return'] / (stock['HV90'].shift(1) / np.sqrt(252))
        
        stockProfiling = StockProfilingDataBean()
        stockProfiling.lastPrice = stock['Adj Close'][-1]
        stockProfiling.change = stock['Return'][-1]
        stockProfiling.dayAverageVolume30 = Volume30
        stockProfiling.dayAverageVolume50 = stock['MA50'][-1]
        stockProfiling.dayMovingAverage200 = stock['MA200'][-1]
        stockProfiling.upDownRatio = UpDown
        
        stockProfiling.stockReturnProfiles = [
                               StockProfilingProcessor.buildStockReturnProfiles("1 Week: ", MIN5, MAX5, Volume5, Ret5, Gap5, Ret5MIN, Ret5MAX, AutoCorr5),
                               StockProfilingProcessor.buildStockReturnProfiles("30 Days: ", MIN30, MAX30, Volume30, Ret30, Gap30, Ret30MIN, Ret30MAX, AutoCorr30),
                               StockProfilingProcessor.buildStockReturnProfiles("90 Days: ", MIN90, MAX90, Volume90, Ret90, Gap90, Ret90MIN, Ret90MAX, AutoCorr90),
                               #StockProfilingProcessor.buildStockReturnProfiles("YTD: ",)
                               StockProfilingProcessor.buildStockReturnProfiles("1 Year: ", MIN1YR, MAX1YR, Volume252, Ret252, Gap252, Ret252MIN, Ret252MAX, AutoCorr252),
                               StockProfilingProcessor.buildStockReturnProfiles("3 Year: ", MIN3YR, MAX3YR, VolumePeriod,RetPeriod, GapPeriod, RetPeriodMIN, RetPeriodMAX, AutoCorrPeriod),
                               ]
                               
        stockProfiling.historicalVolatilities = [
                               StockProfilingProcessor.buildHistoricalVolatilities("Previous Close: ", stock['HV10'][-1], stock['HV20'][-1], stock['HV30'][-1], stock['HV60'][-1], stock['HV90'][-1]),
                               StockProfilingProcessor.buildHistoricalVolatilities("1 Week Ago: ", stock['HV10'][-6], stock['HV20'][-6], stock['HV30'][-6], stock['HV60'][-6], stock['HV90'][-6]),
                               StockProfilingProcessor.buildHistoricalVolatilities("1 Year High: ", stock['HV10'][:-253:-1].max(), stock['HV20'][:-253:-1].max(), stock['HV30'][:-253:-1].max(), stock['HV60'][:-253:-1].max(), stock['HV90'][:-253:-1].max()),
                               StockProfilingProcessor.buildHistoricalVolatilities("1 Year Low: ", stock['HV10'][:-253:-1].min(), stock['HV20'][:-253:-1].min(), stock['HV30'][:-253:-1].min(), stock['HV60'][:-253:-1].min(), stock['HV90'][:-253:-1].min()),
                               ]

        stockProfiling.standardDeviationBuckets = [
                               StockProfilingProcessor.buildStandardDeviationBuckets("HV10:", stock['xSTD10'][:-253:-1][stock['xSTD10'].abs() > 1].count(), stock['xSTD10'][:-253:-1][stock['xSTD10'].abs() > 2].count(), stock['xSTD10'][:-253:-1][stock['xSTD10'].abs() > 3].count(), stock['xSTD10'][:-253:-1][stock['xSTD10'].abs() > 4].count()),
                               StockProfilingProcessor.buildStandardDeviationBuckets("HV20:", stock['xSTD20'][:-253:-1][stock['xSTD20'].abs() > 1].count(), stock['xSTD20'][:-253:-1][stock['xSTD20'].abs() > 2].count(), stock['xSTD20'][:-253:-1][stock['xSTD20'].abs() > 3].count(), stock['xSTD20'][:-253:-1][stock['xSTD20'].abs() > 4].count()),
                               StockProfilingProcessor.buildStandardDeviationBuckets("HV30:", stock['xSTD30'][:-253:-1][stock['xSTD30'].abs() > 1].count(), stock['xSTD30'][:-253:-1][stock['xSTD30'].abs() > 2].count(), stock['xSTD30'][:-253:-1][stock['xSTD30'].abs() > 3].count(), stock['xSTD30'][:-253:-1][stock['xSTD30'].abs() > 4].count()),
                               StockProfilingProcessor.buildStandardDeviationBuckets("HV60:", stock['xSTD60'][:-253:-1][stock['xSTD60'].abs() > 1].count(), stock['xSTD60'][:-253:-1][stock['xSTD60'].abs() > 2].count(), stock['xSTD60'][:-253:-1][stock['xSTD60'].abs() > 3].count(), stock['xSTD60'][:-253:-1][stock['xSTD60'].abs() > 4].count()),
                               StockProfilingProcessor.buildStandardDeviationBuckets("HV90:", stock['xSTD90'][:-253:-1][stock['xSTD90'].abs() > 1].count(), stock['xSTD90'][:-253:-1][stock['xSTD90'].abs() > 2].count(), stock['xSTD90'][:-253:-1][stock['xSTD90'].abs() > 3].count(), stock['xSTD90'][:-253:-1][stock['xSTD90'].abs() > 4].count()),
                               ]
                               
        
        volChartData = stock.loc[:, ['HV10', 'HV20', 'HV30', 'HV60', 'HV90']]
        volChartData.loc[:,'Market Date'] = volChartData.index
        volChartData = volChartData[91:]
        chartColumns = ['Market Date', 'HV10', 'HV20', 'HV30', 'HV60', 'HV90']
        chartValueDict = {'chartTitles':chartColumns, 'chartValues': volChartData, 'format':'date'}
        stockProfiling.volChart = ChartDataBean(**chartValueDict)

        stockPriceChartData = stock.loc[:, ['Adj Close']]
        stockPriceChartData.loc[:,'Market Date'] = stockPriceChartData.index
        stockPriceChartData = stockPriceChartData[91:]
        chartColumns = ['Market Date', 'Adj Close']
        chartValueDict = {'chartTitles':chartColumns, 'chartValues': stockPriceChartData, 'format':'date'}
        stockProfiling.stockPriceChart = ChartDataBean(**chartValueDict)

        xSTDChartData = stock.loc[:, ['xSTD10', 'xSTD20', 'xSTD30', 'xSTD60', 'xSTD90']]
        xSTDChartData.loc[:,'Market Date'] = xSTDChartData.index
        xSTDChartData = xSTDChartData[91:]
        chartColumns = ['Market Date', 'xSTD10', 'xSTD20', 'xSTD30', 'xSTD60', 'xSTD90']
        chartValueDict = {'chartTitles':chartColumns, 'chartValues': xSTDChartData, 'format':'date'}
        stockProfiling.xSTDChart = ChartDataBean(**chartValueDict)
        
        count, division = np.histogram(stock['Return'], bins = np.arange(round(stock['Return'].min(),2),round(stock['Return'].max(),2), round((stock['Return'].max() - stock['Return'].min())/16,3)))
        histogramDict = {'hist':[x for x in count], 'bin_edges':[x for x in division]}
        stockProfiling.histogramChart = histogramDict;
        
        return stockProfiling

    @staticmethod
    def buildStockReturnProfiles(period='', priceMin=0., priceMax=0., volume=0., periodReturn=0., 
                gapReturnMean=0., returnMin=0., returnMax=0., autoCorrelation=0.):
            dict = {
                    "period": period, 
                    "priceMin": priceMin, 
                    "priceMax": priceMax, 
                    "volume": volume,
                    "periodReturn": periodReturn,
                    "gapReturnMean": gapReturnMean, 
                    "returnMin": returnMin, 
                    "returnMax": returnMax, 
                    "autoCorrelation": autoCorrelation,
                    }
            return StockReturnProfiles(**dict)
        
    @staticmethod
    def buildHistoricalVolatilities(period='', HV10=0., HV20=0., HV30=0., HV60=0., HV90=0.):
            dict = {
                    "period": period, 
                    "HV10": HV10, 
                    "HV20": HV20, 
                    "HV30": HV30,
                    "HV60": HV60,
                    "HV90": HV90, 
                    }
            return HistoricalVolatilities(**dict)

    @staticmethod
    def buildStandardDeviationBuckets(period='', returnOver1STD=0., returnOver2STD=0., returnOver3STD=0., returnOver4STD=0.):
            dict = {
                    "period": period, 
                    "returnOver1STD": returnOver1STD, 
                    "returnOver2STD": returnOver2STD, 
                    "returnOver3STD": returnOver3STD,
                    "returnOver4STD": returnOver4STD,
                    }
            return StandardDeviationBuckets(**dict)
