# -*- coding: utf-8 -*-
"""
Created on Thu Feb 21 08:05:41 2019

@author: cpace
"""
import numpy as np
import pandas as pd

class bolinger:
    def __init__(self, df, period=20):
        # calculate Simple Moving Average with 20 days window
        sma = df.Close.rolling(window=period).mean()
        
        # calculate the standar deviation
        rstd = df.Close.rolling(window=period).std()
        
        upper_band = sma + 2 * rstd
        self.upper_band = df.join(upper_band.rename('Upper'))
        lower_band = sma - 2 * rstd
        self.lower_band = df.join(lower_band.rename('Lower'))
        range = upper_band - lower_band
        self.range = df.join(range.rename('Range'))
        df = pd.DataFrame(self.lower_band, columns=['Lower'])
        self.bolinger = df.join(self.upper_band).join(self.range)

class rsi:
    def __init__(self, df, period=14):
        delta = df.Close.diff().dropna()
        u = delta * 0
        d = u.copy()
        u[delta > 0] = delta[delta > 0]
        d[delta < 0] = -delta[delta < 0]
        u[u.index[period-1]] = np.mean( u[:period] ) #first value is sum of avg gains
        u = u.drop(u.index[:(period-1)])
        d[d.index[period-1]] = np.mean( d[:period] ) #first value is sum of avg losses
        d = d.drop(d.index[:(period-1)])
        rs = u.ewm(com=period-1, adjust=False).mean() / \
        d.ewm(com=period-1, adjust=False).mean()
        rsi = 100 - 100 / (1 + rs)
        self.rsi = df.join(rsi.rename('RSI')).fillna(0)

class sma:
    def __init__(self, df, period=20):
        self.sma = df.join(df.Close.rolling(window=period).mean().rename('SMA'))

class ema:
    def __init__(self, series, period=20):
        ema = (series.ewm(span=period, min_periods=period).mean()).fillna(0)
        self.ema = ema.rename('EMA_' + str(period))
        
class macd:
    def __init__(self, df):
        ema26 = ema(df.Close,period=26).ema
        ema12 = ema(df.Close,period=12).ema
        macdline = ema12-ema26
        signalline = ema(macdline.dropna(),period=9).ema
        difference = macdline - signalline
        self.macdline = df.join((macdline.rename('MACDLine')).fillna(0))
        self.signalline = df.join((signalline.rename('MACDSignal')).fillna(0))
        self.difference = df.join((difference.rename('MACDDifference')).fillna(0))
        self.all = df.join((macdline.rename('MACDLine')).fillna(0)).join((signalline.rename('MACDSignal')).fillna(0)).join((difference.rename('MACDDifference')).fillna(0))
        
class adx:
    def __init__(self, df, n):
        self.TR = [float(0)]
        plusDM = [float(0)]
        minusDM = [float(0)]
        for i in range(1,len(df)):
            self.TR.append(max((df.loc[i, 'High']-df.loc[i, 'Low']), abs(df.loc[i, 'High']-df.loc[i-1, 'Close']), abs(df.loc[i, 'Low']-df.loc[i-1, 'Close'])) )
            if(df.loc[i, 'High']-df.loc[i-1, 'High']>df.loc[i-1, 'Low']-df.loc[i, 'Low']):
                plusDM.append(float(max(df.loc[i, 'High']-df.loc[i-1, 'High'],0)))
                minusDM.append(float(0))
            else:
                plusDM.append(float(0))
                minusDM.append(float(max(df.loc[i-1, 'Low']-df.loc[i, 'Low'],0)))
                
        
        TR_14 = np.asarray(self.smooth(self.TR,n))
        plusDM_14 = np.asarray(self.smooth(plusDM,n))
        minusDM_14 = np.asarray(self.smooth(minusDM,n))
        self.plusDI = 100*np.true_divide(plusDM_14, TR_14, where=(TR_14 != 0))
        self.minusDI =100*np.true_divide(minusDM_14, TR_14, where=(TR_14 != 0))
        DI_14_Diff = np.asarray(abs(self.plusDI - self.minusDI))
        DI_14_Sum = np.asarray(abs(self.plusDI + self.minusDI))
        self.DX = 100*np.true_divide(DI_14_Diff, DI_14_Sum, where=(DI_14_Sum != 0))
        self.ADX = (self.adxsmooth(n))
        self.adx = self.adx_all = df.join((pd.Series(self.ADX[v] for v in range(len(self.ADX))).rename('ADX')).fillna(0))
        self.adx_all = df.join((pd.Series(self.ADX[v] for v in range(len(self.ADX))).rename('ADX')).fillna(0)).join((pd.Series(self.plusDI[v] for v in range(len(self.plusDI))).rename('plusDI')).fillna(0)).join((pd.Series(self.minusDI[v] for v in range(len(self.minusDI))).rename('minusDI')).fillna(0))
    
    def smooth(self, series, n):
        sum = 0
        smooth=[]
        for i in range(1,n+1):
            smooth.append(0)
            sum = series[i] + sum
        smooth.append(sum)
        for i in range(n+1,len(self.TR)):
            smooth.append((smooth[i-1] - smooth[i-1]/n + series[i]))
        return smooth
    
    def adxsmooth(self, n):
        sum = 0
        smooth=[]
        for i in range(1,2*n):
            smooth.append(0)
            sum = self.DX[i] + sum
        smooth.append(sum/n)
        for i in range(2*n,len(self.DX)):
            smooth.append((smooth[i-1]*13 + self.DX[i])/14)
        return smooth
    
class stochastics:
    def __init__(self, df, k=14, d=3):
        H = df.High.rolling(k).max()
        L = df.Low.rolling(k).min()
        
        K = (df.Close - L)/(H-L) * 100
        D = K.rolling(d).mean()
        diff = K - D
        
        self.stochastics = df.join((K.rename('StochSlow')).fillna(0)).join((D.rename('StochFast')).fillna(0)).join((diff.rename('StochDiff')).fillna(0))
        