Overall Statistics
Total Trades
735
Average Win
2.26%
Average Loss
-0.96%
Compounding Annual Return
31.548%
Drawdown
24.300%
Expectancy
1.553
Net Profit
5008.488%
Sharpe Ratio
1.643
Probabilistic Sharpe Ratio
98.855%
Loss Rate
24%
Win Rate
76%
Profit-Loss Ratio
2.35
Alpha
0.199
Beta
0.252
Annual Standard Deviation
0.133
Annual Variance
0.018
Information Ratio
0.772
Tracking Error
0.178
Treynor Ratio
0.871
Total Fees
$1822.76
Estimated Strategy Capacity
$6000000.00
Lowest Capacity Asset
FDN TJPMW3BHNMUD
#region imports
from AlgorithmImports import *
#endregion
from pykalman import KalmanFilter
import statistics
        
class KalmanFilterIndicator(PythonIndicator):  
    def __init__(self,name, period, selector=Field.Close,
                 transition_matrices = [1], observation_matrices = [1], 
                 initial_state_mean = 0, initial_state_covariance = 1, 
                 observation_covariance=1, transition_covariance=.01):
                     
        self.Name     = name
        self.period   = period
        self.Value    = 0
        self.barCalc  = selector
        
        self.transition_matrices      = transition_matrices
        self.observation_matrices     = observation_matrices
        self.initial_state_mean       = initial_state_mean 
        self.initial_state_covariance = initial_state_covariance
        self.observation_covariance   = observation_covariance
        self.transition_covariance    = transition_covariance
        
        self.rollingWindow = RollingWindow[float](self.period)
    
    
    # ---------------------------------    
    def Update(self, inputBar):
        
        effectiveBarValue = self.barCalc(inputBar) # round(float(statistics.median([Field.Open(inputBar), Field.High(inputBar), Field.Low(inputBar), Field.Close(inputBar)])), 4)# self.barCalc(inputBar) 
        self.rollingWindow.Add(effectiveBarValue)         
        
        if(not self.rollingWindow.IsReady):
            return False
        else:

            basisValue = np.flipud(np.array([self.rollingWindow[i] for i in range(self.period)]))
            
            self.kf = KalmanFilter( transition_matrices = self.transition_matrices,
                                    observation_matrices     = self.observation_matrices,
                                    initial_state_mean       = self.initial_state_mean,
                                    initial_state_covariance = self.initial_state_covariance,
                                    observation_covariance   = self.observation_covariance,
                                    transition_covariance    = self.transition_covariance)
                                    
            #self.kf = self.kf.em(basisValue, n_iter=5)
            
            kf,_ = self.kf.filter(basisValue) # self.kf.smooth(basisValue)
            currKalman = kf[-1]

            self.Value = float(currKalman)
            return True
        
################################################################################
#
# LaguerreFilterIndicator
# ==============================
# Laguerre Filter as defined by John F. Ehlers in `Cybernetic Analysis for 
# Stock and Futures`, 2004, published by Wiley. `ISBN: 978-0-471-46307-8
# https://www.mt5users.com/wp-content/uploads/2020/01/timewarp.pdf
#
# Copied from @vladimir's implementation
# https://www.quantconnect.com/forum/discussion/11788/another-digital-filter-laguerre-filter/p1/comment-34897
# 
################################################################################
        
class LaguerreFilterIndicator(PythonIndicator):  
    def __init__(self, name, gamma ):
        self.Name = name
        self.gamma = gamma
        self.prices = np.array([])
        self.Value = 0
        self.L0 = 0.0; self.L1 = 0.0; self.L2 = 0.0; self.L3 = 0.0
        
    
    def Update(self, input):
        mp = (input.High + input.Low)/2
        self.prices = np.append(self.prices, mp)[-4:]
        if len(self.prices) <= 1:
            self.L0 = mp; self.L1 = mp; self.L2 = mp; self.L3 = mp;
        
        if len(self.prices) != 4 : return
    
        L01 = self.L0; L11 = self.L1; L21 = self.L2; L31 = self.L3;
        g = self.gamma  
        
        self.L0 = (1 - g)*mp + g*L01
        self.L1 = L01 - g*self.L0 + g*L11
        self.L2 = L11 - g*self.L1 + g*L21
        self.L3 = L21 - g*self.L2 + g*L31
        
        if len(self.prices) != 4 :
            self.Value = mp
            return False
        
        self.Value = (self.L0 + (2*self.L1) + 2*(self.L2) + self.L3) / 6
        return True        
        
#region imports
from AlgorithmImports import *
#endregion

###################################################
#
#  Smart Rolling window
#  ========================
#  Convenience object to build on RollingWindow functionality
#
#  Methods:
#  -------------------------
#  mySmartWindow.IsRising()
#  mySmartWindow.IsFalling()
#  mySmartWindow.crossedAboveValue(value)
#  mySmartWindow.crossedBelowValue(value)
#  mySmartWindow.crossedAbove(otherWindow)
#  mySmartWindow.crossedBelow(otherWindow)
#  mySmartWindow.IsFlat(decimalPrecision)
#  mySmartWindow.hasAtLeastThisMany(value)
#
###################################################

class SmartRollingWindow():
    
    def __init__(self, windowType, windowLength):
        self.window    = None
        self.winLength = windowLength

        if (windowType is "int"):self.window = RollingWindow[int](windowLength)
        elif (windowType is "bool"):self.window = RollingWindow[bool](windowLength)
        elif (windowType is "float"):self.window = RollingWindow[float](windowLength)
        elif (windowType is "TradeBar"):self.window = RollingWindow[TradeBar](windowLength)

    def crossedAboveValue(self, value): return (self.window[1] <= value < self.window[0])
    def crossedBelowValue(self, value): return (self.window[1] >= value > self.window[0])

    def crossedAbove(self, series): return (any(self.window[i+1] <= series[i+1] and self.window[i] > series[i] for i in range(0, self.winLength-1)))
    def crossedBelow(self, series): return (any(self.window[i+1] >= series[i+1] and self.window[i] < series[i] for i in range(0, self.winLength-1)))

    def isAbove(self, series): return (self.window[0] > series[0])
    def isBelow(self, series): return (self.window[0] < series[0])
    
    def isFlat(self):    return (self.window[1] == self.window[0])
    def isFalling(self): return (self.window[1] > self.window[0])
    def isRising(self):  return (self.window[1] < self.window[0])

    def Add(self,value): 
        self.window.Add(value)

    def IsReady(self):
        return (self.window is not None) and \
               (self.window.Count >= self.winLength) ## TODO: just use rw.IsReady?
    
    def __getitem__(self, index):
        return self.window[index]
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *

import tweepy, statistics, json
from datetime import datetime, timedelta, date
import numpy as np
from scipy import stats
from AlgorithmImports import *
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.tree import ExtraTreeRegressor, ExtraTreeClassifier
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
from sklearn import preprocessing
from sklearn.preprocessing import MinMaxScaler

from keras.models import Sequential
from keras.layers import Dense, LSTM

from helpers import myTrailingStopRiskManagementModel

from SmartRollingWindow import *

class DualMomentumWithOutDaysAlphaModel(AlphaModel):

    def __init__(self, algorithm, VOLA = 126, BASE_RET = 83, resolution = Resolution.Daily, *args, **kwargs):
        super().__init__()
        self.VOLA = VOLA
        self.BASE_RET = BASE_RET
        self.resolution = Resolution.Daily # resolution
        self.MKT = algorithm.AddEquity('SPY', resolution).Symbol
        self.SLV = algorithm.AddEquity('SLV', resolution).Symbol
        self.GLD = algorithm.AddEquity('GLD', resolution).Symbol
        self.XLI = algorithm.AddEquity('XLI', resolution).Symbol
        self.XLU = algorithm.AddEquity('XLU', resolution).Symbol
        self.DBB = algorithm.AddEquity('DBB', resolution).Symbol
        self.UUP = algorithm.AddEquity('UUP', resolution).Symbol
        
        self.MKT_MOMP = algorithm.MOMP(self.MKT, 2, Resolution.Daily)

        self.count = self.BASE_RET
        self.outday = 5

        pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP]
        for symbol in pairs:
            self.consolidator = TradeBarConsolidator(timedelta(days=1))
            self.consolidator.DataConsolidated += self.consolidation_handler
            algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)

        self.history = np.log(algorithm.History(pairs, self.VOLA + 1, self.resolution))
        #self.history = self.history['close'].unstack(level=0).dropna()

        self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), 1)
        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = f"{self.__class__.__name__}({resolutionString})"

        # Force alpha to only produce insights Daily at 11.10am
        self.set_flag = False
        algorithm.Schedule.On(algorithm.DateRules.EveryDay(),
                              algorithm.TimeRules.AfterMarketOpen('SPY', 100),
                              self.SetFlag)
        
        self.algorithm = algorithm
        self._mag = 0.00
        self._mag_actual = 0.00
        self.in_out = 0
        algorithm.Schedule.On(algorithm.DateRules.Every(DayOfWeek.Saturday),
                              algorithm.TimeRules.At(2, 00),
                              self.ml_model)
                              
    def SetFlag(self):
        self.set_flag = True

    def consolidation_handler(self, sender, consolidated):
        self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
        self.history = self.history.iloc[-(self.VOLA + 1):]
        
    def Update(self, algorithm, _data):
        if algorithm.IsWarmingUp or not self.set_flag:
            return []

        self.set_flag = False
        insights = []
        
        # Volatility
        vola = self.history[self.MKT].pct_change().std() * np.sqrt(252)
        wait_days = int(vola * self.BASE_RET)
        period = int((1.0 - vola) * self.BASE_RET)
        r = self.history.pct_change(period).iloc[-1]

        exit_market = r[self.SLV] < r[self.GLD] and r[self.XLI] < r[self.XLU] and r[self.DBB] < r[self.UUP]
        
        # # ML Model
        # pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP]
        # data = self.history
        # data['vola'] = data[self.MKT].pct_change().rolling(246).std() * np.sqrt(252)
        # data['wait_days'] = data['vola'].map(lambda x: int(int(x)*self.BASE_RET), na_action='ignore')
        # data['period'] = data['vola'].map(lambda x: int((1.0 - int(x)) * self.BASE_RET), na_action='ignore')
        # market_signals = list()
        # for i in range(0, len(data['period'])):
        #     if 'na' not in str(data['period'][i]):
        #         count = int(i)
        #         period = int(data['period'][i])
        #         r = data[count:count+246].pct_change(period).iloc[-1]
        #         exit_market = (r[self.SLV] < r[self.GLD] and r[self.XLI] < r[self.XLU] and r[self.DBB] < r[self.UUP])
        #         market_signals.append(exit_market)
        #     else:
        #         market_signals.append('na')
                
        # data['market_signals'] = market_signals
        # del market_signals
        
        # data['Market_Change'] = data[self.MKT].pct_change()
        # # # Load LabelEncoder to process string variables
        # le = preprocessing.LabelEncoder()
        
        # ml_data = data.dropna()
        # pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP, 'vola', 'wait_days', 'period', 'Market_Change']
        # X = ml_data.drop(['market_signals'], axis=1)
        # y = np.ravel(ml_data[['market_signals']].astype(str).apply(le.fit_transform))
        # algorithm.Debug(str(X.shape) + ";" + str(y.shape))
        # X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

        # Prepare Data
        # pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP]
        # df = algorithm.History(pairs, 252, Resolution.Daily).close
        # returns = df.unstack(level=1).transpose().dropna()
        # returns["Volatility"] = np.nan
        # returns["Wait_Days"] = np.nan
        # returns["Period"] = np.nan
        # returns["Exit_Market"] = np.nan
        
        # vola_list = [np.nan] * 126
        # wait_list = [np.nan] * 126
        # period_list = [np.nan] * 126
        # exit_market_list = [np.nan] * 126
        # for i in range(len(returns)-126):
        #     temp_df = returns.iloc[i:i+126]
        #     vola = temp_df[self.MKT].pct_change().std() * np.sqrt(252)
        #     wait_days = int(vola * self.BASE_RET)
        #     period = int((1.0 - vola) * self.BASE_RET)
        #     r = temp_df.pct_change(period).iloc[-1]
        #     exit_market = r[self.SLV] < r[self.GLD] and r[self.XLI] < r[self.XLU] and r[self.DBB] < r[self.UUP]
            
        #     vola_list.append(vola)
        #     wait_list.append(wait_days)
        #     period_list.append(period)
        #     exit_market_list.append(exit_market)
            
        # returns["Volatility"] = vola_list
        # returns["Wait_Days"] = wait_list
        # returns["Period"] = period_list
        # returns["Exit_Market"] = exit_market
        
        # X = returns.dropna()
        # y = returns[self.MKT].iloc[126:]
        # X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 1990)

        # # Initialize instance of Random Forest Regressor
        # regressor = RandomForestRegressor(n_estimators=500, criterion = "mse", min_samples_split=5, random_state = 1990)

        # # Fit regressor
        # regressor.fit(X_train, y_train)
        
        # # Get long-only predictions
        # weights = regressor.feature_importances_
        # symbols = X.columns[np.where(weights)]
        # selected = zip(symbols, weights)
        
        # y_pred = regressor.predict(X_test)

        # algorithm.Plot("MKT Price", "Actual", float(y_test[-1]))
        # algorithm.Plot("MKT Price", "Predicted", float(y_pred[-1]))
        
        # algorithm.Plot("Model Accuracy", "100%", float(regressor.score(X_test, y_test)*100))
        
        # algorithm.Plot("Error", 'Mean Absolute Error', metrics.mean_absolute_error(y_test, y_pred))
        # algorithm.Plot("Error", 'Mean Squared Error', metrics.mean_squared_error(y_test, y_pred, squared=True))
        # algorithm.Plot("Error", 'Root Mean Squared Error', np.sqrt(metrics.mean_squared_error(y_test, y_pred, squared=False)))
        
        direction = InsightDirection.Down
        

        if (exit_market):
            algorithm.Plot("In vs Out", "Market", -1)
            direction = InsightDirection.Down
            self.outday = self.count
        elif (self.count >= wait_days + self.outday):
                algorithm.Plot("In vs Out", "Market", 1)
                direction = InsightDirection.Up
        else:
            direciton = InsightDirection.Flat
            algorithm.Plot("In vs Out", "Market", 0)
        self.count += 1
        
        # algorithm.Plot("Wait Days", "Actual", self.count)
        # algorithm.Plot("Wait Days", "Expected", float(wait_days + self.outday))
        # algorithm.Plot("Market Volatility", str(self.MKT), float(vola))
        # if direction == InsightDirection.Down:
        #     val = -1.0
        # elif direction == InsightDirection.Up:
        #     val = 1.0
        # else:
        #     val = 0.0
        # algorithm.Plot("IN vs Out", "Before", float(val))
        if self.MKT_MOMP.Current.Value > 0:
            val = 1
        elif self.MKT_MOMP.Current.Value < 0:
            val = -1
        else:
            val = 0
        algorithm.Plot("In vs Out", "MKT_MOMP", float(val))
        if self.in_out:
            algorithm.Plot("In vs Out", "MKT_RandomForest", float(-1))
        elif not self.in_out:
            algorithm.Plot("In vs Out", "MKT_RandomForest", float(1))
        
        algorithm.Plot("Magnitude", "MKT_MOMP", float(self.MKT_MOMP.Current.Value))
        algorithm.Plot("Magnitude", "MKT_RandomForest", float(self._mag))
        algorithm.Plot("Magnitude", "MKT_Actual", float(self._mag_actual))
        
        insights.append(Insight.Price(self.MKT, self.predictionInterval, direction, self._mag, None))
        
        # if self.in_out:
        #     bull = False
        #     bear = True
        # else:
        #     bull = True
        #     bear = False
        
        # payload = json.dumps({"Predicted SPY % Change": self._mag,
        #                         "Bull Market": bull,
        #                         "Bear Market": bear
        # })
        # algorithm.Notify.Web("https://discordapp.com/api/webhooks/955625600750587924/WrhQbKDBWnUTwYoB74M_2ujJVuEnu7nRjLYxT5g-gYhwaRTrkTzVUsNxGVuRGeMmmE2L", payload)
        
        return insights
        
    def ml_model(self):
        # # ML Model
        # pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP]
        # data = self.history
        # data['vola'] = data[self.MKT].pct_change().rolling(246).std() * np.sqrt(252)
        # data['wait_days'] = data['vola'].map(lambda x: int(int(x)*self.BASE_RET), na_action='ignore')
        # data['period'] = data['vola'].map(lambda x: int((1.0 - int(x)) * self.BASE_RET), na_action='ignore')
        # market_signals = list()
        # for i in range(0, len(data['period'])):
        #     if 'na' not in str(data['period'][i]):
        #         count = int(i)
        #         period = int(data['period'][i])
        #         r = data[count:count+246].pct_change(period).iloc[-1]
        #         exit_market = (r[self.SLV] < r[self.GLD] and r[self.XLI] < r[self.XLU] and r[self.DBB] < r[self.UUP])
        #         market_signals.append(exit_market)
        #     else:
        #         market_signals.append('na')
                
        # data['market_signals'] = market_signals
        # del market_signals
        
        # data['Market_Change'] = data[self.MKT].pct_change()
        # # # Load LabelEncoder to process string variables
        # le = preprocessing.LabelEncoder()
        
        # ml_data = data.dropna()
        # pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP, 'vola', 'wait_days', 'period', 'Market_Change']
        # X = ml_data.drop(['market_signals'], axis=1)
        # y = np.ravel(ml_data[['market_signals']].astype(str).apply(le.fit_transform))
        # algorithm.Debug(str(X.shape) + ";" + str(y.shape))
        # X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

        # Prepare Data
        pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP]
        df = self.algorithm.History(pairs, 252, Resolution.Daily).close
        # Prepare Data
        returns = df.unstack(level=1).transpose().dropna()
        returns["Volatility"] = np.nan
        returns["Wait_Days"] = np.nan
        returns["Period"] = np.nan
        returns["Exit_Market"] = np.nan
        
        vola_list = [np.nan] * 126
        wait_list = [np.nan] * 126
        period_list = [np.nan] * 126
        exit_market_list = [np.nan] * 126
        for i in range(len(returns)-126):
            temp_df = returns.iloc[i:i+126]
            vola = temp_df[self.MKT].pct_change().std() * np.sqrt(252)
            wait_days = int(vola * self.BASE_RET)
            period = int((1.0 - vola) * self.BASE_RET)
            r = temp_df.pct_change(period).iloc[-1]
            exit_market = r[self.SLV] < r[self.GLD] and r[self.XLI] < r[self.XLU] and r[self.DBB] < r[self.UUP]
            
            vola_list.append(vola)
            wait_list.append(wait_days)
            period_list.append(period)
            exit_market_list.append(exit_market)
            
        returns["Volatility"] =  vola_list
        returns["Wait_Days"] = wait_list
        returns["Period"] = period_list
        returns["Exit_Market"] = exit_market_list
        
        returns = returns.dropna().iloc[-126:]
        
        # y data
        le = preprocessing.LabelEncoder()
        y_clf = le.fit_transform(returns["Exit_Market"])
        y = returns[self.MKT]
        
        X = returns
        self.algorithm.Debug(str(X.shape)+","+str(y.shape)+","+str(y_clf.shape))
        
        # Train Split Data
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 1990)
        X_train, X_test, y_train_clf, y_test_clf = train_test_split(X, y_clf, test_size = 0.2, random_state = 1990)

        # Initialize instance of Random Forest Regressor and Classifier
        self.regressor = RandomForestRegressor(n_estimators=500, criterion="mse", min_samples_split=5, random_state=1990)
        self.classifier = RandomForestClassifier(n_estimators=500, criterion="gini", min_samples_split=5, random_state=1990)

        # Fit regressor and classifier
        self.regressor.fit(X_train, y_train)
        self.classifier.fit(X_train, y_train_clf)
        
        # Get long-only predictions
        weights = self.regressor.feature_importances_
        symbols = X.columns[np.where(weights)]
        selected = zip(symbols, weights)
        
        y_pred = self.regressor.predict(X_test)
        y_pred_clf = self.classifier.predict(X_test)
        
        self._mag_actual = 100*float(y_test[-1] - y_test[-2]) / y_test[-2]
        self._mag = 100*float(y_pred[-1] - y_pred[-2]) / y_pred[-2]
        self.in_out = le.inverse_transform(y_pred_clf)[-1]

        self.algorithm.Plot("MKT Price", "Actual", float(y_test[-1]))
        self.algorithm.Plot("MKT Price", "Predicted", float(y_pred[-1]))
        
        self.algorithm.Plot("Model Accuracy", "Regression", float(self.regressor.score(X_test, y_test)*100))
        self.algorithm.Plot("Model Accuracy", "Classification", float(self.classifier.score(X_test, y_test_clf)*100))
        
        self.algorithm.Plot("Error", 'Mean Absolute Error', metrics.mean_absolute_error(y_test, y_pred))
        self.algorithm.Plot("Error", 'Mean Squared Error', metrics.mean_squared_error(y_test, y_pred, squared=True))
        self.algorithm.Plot("Error", 'Root Mean Squared Error', np.sqrt(metrics.mean_squared_error(y_test, y_pred, squared=False)))
#region imports
from AlgorithmImports import *
#endregion
import pandas as pd
import numpy as np
from scipy.optimize import minimize


class myTrailingStopRiskManagementModel:
    '''
    Credit goes to: Alex Catarino and many of his friends at QuantConnect
    
    https://github.com/QuantConnect/Lean/blob/master/Algorithm.Framework/Risk/TrailingStopRiskManagementModel.py
    
    Description:
        Limits the maximum possible loss measured from the highest unrealized profit
    '''
    
    def __init__(self, maximumDrawdownPercent = 0.08):
        '''initializes the class
        Args: maximumDrawdownPercent: The maximum percentage drawdown allowed for algorithm portfolio compared with the highest unrealized profit, defaults to 5% drawdown
        '''
        self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)
        self.trailingHighs = dict()

    def setDD(self, maximumDrawdownPercent = 0.08):
        '''allows to change the drawdown
        Args: maximumDrawdownPercent: The maximum percentage drawdown allowed for algorithm portfolio compared with the highest unrealized profit, defaults to 5% drawdown
        '''
        self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)

    def setWTtoZeroIfDDtooHigh(self, algorithm, targets=None):
        '''If drawdown is too high, set wt[symbol] to zero
           algo.wt[symbol] = weights which will be set to 0 in case drawdown exceeds the maximum    
        '''
        for kvp in algorithm.Securities:
            symbol = kvp.Key
            security = kvp.Value
            
            # Remove from trailingHighs dict if not invested
            if not security.Invested:
                self.trailingHighs.pop(symbol, None)
                continue
            
            # Add newly invested securities to trailingHighs dict
            if symbol not in self.trailingHighs:
                self.trailingHighs[symbol] = security.Holdings.AveragePrice
                continue
            
            # Check for new highs and update trailingHighs dict
            if self.trailingHighs[symbol] < security.High:
                self.trailingHighs[symbol] = security.High
                continue
            
            # Calc the drawdown
            securityHigh = self.trailingHighs[symbol]
            drawdown = (security.Low / securityHigh) - 1
            
            # If drawdown is too high, set symbol weight to zero
            if drawdown < self.maximumDrawdownPercent:
                algorithm.wt[symbol] = 0
        
        return
    
    
    
class myPortfolioOptimizer:
    '''
    Credit goes to: Emilio Freire / InnoQuantivity
    
    https://innoquantivity.com/blogs/inno-blog/portfolio-optimization-quantconnect-research-algorithm
    https://www.quantconnect.com/forum/discussion/8128/portfolio-optimization-research-amp-algorithm-for-better-workflows/p1/comment-22952
    
    Description:
        Implementation of a custom optimizer that calculates the weights for each asset to optimize a given objective function
    Details:
        Optimization can be:
            - Equal Weighting
            - Maximize Portfolio Return
            - Minimize Portfolio Standard Deviation
            - Mean-Variance (minimize Standard Deviation given a target return)
            - Maximize Portfolio Sharpe Ratio
            - Maximize Portfolio Sortino Ratio
            - Risk Parity Portfolio
        Constraints:
            - Weights must be between some given boundaries
            - Weights must sum to 1
    '''
    def __init__(self, 
                 minWeight = 0,
                 maxWeight = 1):
        '''
        Description:
            Initialize the CustomPortfolioOptimizer
        Args:
            minWeight(float): The lower bound on portfolio weights
            maxWeight(float): The upper bound on portfolio weights
        '''
        self.minWeight = minWeight
        self.maxWeight = maxWeight
        
        
    def CalcWeights(self, algorithm, symbols, objectiveFunction='riskParity', lookback=63, targetReturn=None):
        '''
        Description:
            Calculate weights from daily returns, return a pandas Series
        '''
        history = np.log10(algorithm.History(symbols, lookback, Resolution.Daily)['close'].unstack(level = 0))
        returnsDf = history.pct_change().dropna()
        returnsDf.columns = [algorithm.AddEquity(i).Symbol.Value for i in list(returnsDf.columns)]
        weights = self.Optimize(objectiveFunction, returnsDf, targetReturn)
        return pd.Series(weights, index=returnsDf.columns, name='weights')
        
        
    def Optimize(self, objFunction, dailyReturnsDf, targetReturn = None):
        '''
        Description:
            Perform portfolio optimization given a series of returns
        Args:
            objFunction: The objective function to optimize (equalWeighting, maxReturn, minVariance, meanVariance, maxSharpe, maxSortino, riskParity)
            dailyReturnsDf: DataFrame of historical daily arithmetic returns
        Returns:
            Array of double with the portfolio weights (size: K x 1)
        '''
        # initial weights: equally weighted
        size = dailyReturnsDf.columns.size # K x 1
        self.initWeights = np.array(size * [1. / size])
        
        # get sample covariance matrix
        covariance = dailyReturnsDf.cov()
        # get the sample covariance matrix of only negative returns for sortino ratio
        negativeReturnsDf = dailyReturnsDf[dailyReturnsDf < 0]
        covarianceNegativeReturns = negativeReturnsDf.cov()
        
        if objFunction == 'equalWeighting':
            return self.initWeights
        
        bounds = tuple((self.minWeight, self.maxWeight) for x in range(size))
        constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1.0}]
        
        if objFunction == 'meanVariance':
            # if no target return is provided, use the resulting from equal weighting
            if targetReturn is None:
                targetReturn = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, self.initWeights)
            constraints.append( {'type': 'eq', 'fun': lambda weights:
                                self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights) - targetReturn} )
        
        opt = minimize(lambda weights: self.ObjectiveFunction(objFunction, dailyReturnsDf,
                                                                covariance, covarianceNegativeReturns,
                                                                weights),
                                                            x0 = self.initWeights,
                                                            bounds = bounds,
                                                            constraints = constraints,
                                                            method = 'SLSQP')
        return opt['x']
        
        
    def ObjectiveFunction(self, objFunction, dailyReturnsDf, covariance, covarianceNegativeReturns, weights):
        
        '''
        Description:
            Compute the objective function
        Args:
            objFunction: The objective function to optimize (equalWeighting, maxReturn, minVariance, meanVariance,
                                                                maxSharpe, maxSortino, riskParity)
            dailyReturnsDf: DataFrame of historical daily returns
            covariance: Sample covariance
            covarianceNegativeReturns: Sample covariance matrix of only negative returns
            weights: Portfolio weights
        '''
        if objFunction == 'maxReturn':
            f = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights)
            return -f # convert to negative to be minimized
        elif objFunction == 'minVariance':
            f = self.CalculateAnnualizedPortfolioStd(covariance, weights)
            return f
        elif objFunction == 'meanVariance':
            f = self.CalculateAnnualizedPortfolioStd(covariance, weights)
            return f
        elif objFunction == 'maxSharpe':
            f = self.CalculateAnnualizedPortfolioSharpeRatio(dailyReturnsDf, covariance, weights)
            return -f # convert to negative to be minimized
        elif objFunction == 'maxSortino':
            f = self.CalculateAnnualizedPortfolioSortinoRatio(dailyReturnsDf, covarianceNegativeReturns, weights)
            return -f # convert to negative to be minimized
        elif objFunction == 'riskParity':
            f = self.CalculateRiskParityFunction(covariance, weights)
            return f
        else:
            raise ValueError(f'PortfolioOptimizer.ObjectiveFunction: objFunction input has to be one of equalWeighting,'
             + ' maxReturn, minVariance, meanVariance, maxSharpe, maxSortino or riskParity')
        
        
    def CalculateAnnualizedPortfolioReturn(self, dailyReturnsDf, weights):
        
        annualizedPortfolioReturns = np.sum( ((1 + dailyReturnsDf.mean())**252 - 1) * weights )
        
        return annualizedPortfolioReturns
        
            
    def CalculateAnnualizedPortfolioStd(self, covariance, weights):
        
        annualizedPortfolioStd = np.sqrt( np.dot(weights.T, np.dot(covariance * 252, weights)) )
        
        if annualizedPortfolioStd == 0:
            raise ValueError(f'PortfolioOptimizer.CalculateAnnualizedPortfolioStd: annualizedPortfolioStd cannot be zero. Weights: {weights}')
            
        return annualizedPortfolioStd
        
        
    def CalculateAnnualizedPortfolioNegativeStd(self, covarianceNegativeReturns, weights):
    
        annualizedPortfolioNegativeStd = np.sqrt( np.dot(weights.T, np.dot(covarianceNegativeReturns * 252, weights)) )        
        
        if annualizedPortfolioNegativeStd == 0:
            raise ValueError(f'PortfolioOptimizer.CalculateAnnualizedPortfolioNegativeStd: annualizedPortfolioNegativeStd cannot be zero. Weights: {weights}')
        
        return annualizedPortfolioNegativeStd
        
        
    def CalculateAnnualizedPortfolioSharpeRatio(self, dailyReturnsDf, covariance, weights):
        
        annualizedPortfolioReturn = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights)
        annualizedPortfolioStd = self.CalculateAnnualizedPortfolioStd(covariance, weights)
        annualizedPortfolioSharpeRatio = annualizedPortfolioReturn / annualizedPortfolioStd
            
        return annualizedPortfolioSharpeRatio
        
        
    def CalculateAnnualizedPortfolioSortinoRatio(self, dailyReturnsDf, covarianceNegativeReturns, weights):
        
        annualizedPortfolioReturn = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights)
        annualizedPortfolioNegativeStd = self.CalculateAnnualizedPortfolioNegativeStd(covarianceNegativeReturns, weights)
        annualizedPortfolioSortinoRatio = annualizedPortfolioReturn / annualizedPortfolioNegativeStd
            
        return annualizedPortfolioSortinoRatio
        
        
    def CalculateRiskParityFunction(self, covariance, weights):
        
        ''' Spinu formulation for risk parity portfolio '''
        
        assetsRiskBudget = self.initWeights
        portfolioVolatility = self.CalculateAnnualizedPortfolioStd(covariance, weights)
        
        x = weights / portfolioVolatility
        riskParity = (np.dot(x.T, np.dot(covariance, x)) / 2) - np.dot(assetsRiskBudget.T, np.log(x))
            
        return riskParity
#region imports
from AlgorithmImports import *
#endregion
import math 

class SupportResistance:
    
    # Get the S&R's for the last 2 weeks.
    def __init__(self, algorithm, ticker):
        self.Ticker = ticker
        self.Algorithm = algorithm
        self.Daily = RollingWindow[TradeBar](7);
        self.Min    = algorithm.MIN(self.Ticker, 390, Resolution.Minute)     # Range of today; breaking out of new highs/lows
        self.Max    = algorithm.MAX(self.Ticker, 390, Resolution.Minute)
        algorithm.Consolidate(self.Ticker, Resolution.Daily, self.SaveDailyBars)
        
    def NextSupport(self):
        
        support = []
        
        price = self.Algorithm.Securities[self.Ticker].Price
        
        # Rounded Price to $1
        support.append( round(price) )
        
        # Rounded Price to $10
        support.append( int(math.floor(price / 10.0)) * 10 )
        
        # Yesterday's OHLC Price
        support.append( self.Daily[0].Close )
        support.append( self.Daily[0].Low )
        
        # Append 7 day Low:
        support.append( min([bar.Low for bar in self.Daily]) )
        
        support = sorted(support, reverse=True)
        return support[0]
        
        
    def NextResistance(self):
        
        resistance = []
        price = self.Algorithm.Securities[self.Ticker].Price
        
        # Round Price Up to $1
        resistance.append( math.ceil(price) )
        
        # Rounded Price Up to $10
        resistance.append( int(math.ceil(price / 10.0)) * 10 )
            
        # Yesterday's Close, High
        resistance.append( self.Daily[0].Close )
        resistance.append( self.Daily[0].High )
        
        # Append 7 Day High
        resistance.append( max([bar.High for bar in self.Daily]) )
        
        # Take the lowest value on the list
        resistance = sorted(resistance)
        return resistance[0]
    
    # Build a 14 day historical rolling window of underlying prices.
    def SaveDailyBars(self, bar):
        self.Daily.Add(bar)
        
    # Reset any "daily" indicators
    def Reset(self):
        self.Min.Reset()
        self.Max.Reset()
#region imports
from AlgorithmImports import *
#endregion
from itertools import groupby
import tweepy
from datetime import datetime, timedelta, date
import time
import pandas as pd
import numpy as np
import re, math
import scipy
from math import ceil
from collections import deque
from itertools import chain
from pytz import timezone
import statistics

from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from QuantConnect.Data.Market import TradeBar
from QuantConnect.Algorithm.Framework.Alphas import EmaCrossAlphaModel, RsiAlphaModel
from QuantConnect.Algorithm.Framework.Execution import StandardDeviationExecutionModel, VolumeWeightedAveragePriceExecutionModel
from QuantConnect.Algorithm.Framework.Risk import MaximumDrawdownPercentPortfolio, MaximumUnrealizedProfitPercentPerSecurity, MaximumDrawdownPercentPerSecurity, TrailingStopRiskManagementModel

from dual_momentum_with_out_days_alpha import DualMomentumWithOutDaysAlphaModel
from portfolio_management import PortfolioManagementModel
from trade_execution import ScheduledExecutionModel
from manage_risk import CustomRiskModel
from symbol_data_functions import SymbolData

from market_profile import Market_Profile

VOLA = 126; BASE_RET = 83; RET = 252; EXCL = 21; LEV = 1.00;

class HorizontalQuantumCoil(QCAlgorithm):

    def Initialize(self):
        self.Portfolio.MarginCallModel = MarginCallModel.Null
        self.EnableAutomaticIndicatorWarmUp = True 
        self.SetStartDate(2008, 1, 1)
        #self.SetEndDate(2013, 8, 1)
        self.SetCash(10000)
        self.added_cash = 115
        self.upkeep = 28
        self.simulate_live = False
        self.SetWarmUp(timedelta(252))
        self.Settings.FreePortfolioValuePercentage = 0.05
        # self.SetBrokerageModel(BrokerageName.AlphaStreams)
        self.SetAlpha(DualMomentumWithOutDaysAlphaModel(self, VOLA, BASE_RET, Resolution.Daily))

        stonks = ['FDN', 'QQQ', 'IWM', 'SPY', 'VTI', 'DIA', 'IWF', 'TLT', 'TLH', 'IEI', 'IEF'] # , 'SPDN' # 'IYW', 'IVV', 
        #lev_stonks = ['TQQQ', 'URTY', 'SPXL', 'TMF']# , 'AGQ', 'UGL']
        symbols = []
        # stonks = stonks + lev_stonks
        for stonk in stonks:
            val = Symbol.Create(stonk, SecurityType.Equity, Market.USA)
            symbols.append(val)
        self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))
        self.UniverseSettings.Resolution = Resolution.Daily
        self.SetPortfolioConstruction(PortfolioManagementModel(self, RET, EXCL, LEV, Resolution.Daily, portfolioBias = PortfolioBias.Long))
        self.SetExecution(ScheduledExecutionModel(self))
        self.SetRiskManagement(CustomRiskModel(self, maximumDrawdownPercent = 0.025, maximumUnrealizedProfitPercent = 0.175, resolution = Resolution.Daily))
        
        # self.market_profile = Market_Profile()
        
        # self.optionsData = dict()
        # self.ASSETS = list()
        # for symbol in stonks:
        #     self.optionsData[symbol] = dict()
        #     sec = self.AddEquity(symbol)
        #     self.ASSETS.append(symbol)
        #     sec.SetDataNormalizationMode(DataNormalizationMode.Raw)
        #     option = self.AddOption(symbol)
        #     option.SetFilter(self.OptionFilterUniverse)
        #     _Trade = TradeManagement(self, symbol, option.Symbol)
        #     _SupportResistance = SupportResistance(self, symbol)
        #     _mean = self.EMA(symbol, 5, Resolution.Hour)
            
        #     self.optionsData[symbol]["Trade"] = _Trade
        #     self.optionsData[symbol]["RS"] =  _SupportResistance
        #     self.optionsData[symbol]["mean"] =  _mean
            
        #     # self.consolidator = TradeBarConsolidator(timedelta(minutes=24*60))
        #     # self.consolidator.DataConsolidated += self.consolidation_handler
        #     # self.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
        
        # self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(0, 0), self.market_profile.GetMarketProfile(self, 21, self.ASSETS))
        # self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", -1), self.MarketClose)
        
        self.createPlots("SPY")
        for time in range(55, 295, 30):
            self.Schedule.On(self.DateRules.EveryDay("SPY"), \
                             self.TimeRules.AfterMarketOpen("SPY", time), \
                             self.UpdateTickets)
    
        if self.simulate_live:
            self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), \
                             self.TimeRules.BeforeMarketClose("SPY", 0), \
                             self.AddCash)
    
            self.Schedule.On(self.DateRules.MonthStart("SPY"), \
                             self.TimeRules.BeforeMarketClose("SPY", 0), \
                             self.UpKeep)

    # def consolidation_handler(self, sender, consolidated):
    #     self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
    #     self.history = self.history.iloc[-(21):]

    def createPlots(self, benchmark):
        self.__benchmark = benchmark

        self.__plot_every_n_days = 5
        self.__plot_every_n_days_i = 0
        plot = Chart('Performance')
        plot.AddSeries(Series(self.__benchmark, SeriesType.Line, 0, '%'))
        plot.AddSeries(Series("Algorithm", SeriesType.Line, 0, '%'))
        self.AddChart(plot)

        self.ResetPlot()

    def ResetPlot(self):
        self.year = self.Time.year
        self.__cost_portfolio = None
        self.__cost_benchmark = None

    def CalculateBenchmarkPerformance(self):
        price = self.Securities[self.__benchmark].Price
        if self.__cost_benchmark == None:
            self.__cost_benchmark = price
        return 100.0 * ((price / self.__cost_benchmark) - 1.0)
        
    def CalculatePortfolioPerformance(self):
        if self.__cost_portfolio == None:
            self.__cost_portfolio = self.Portfolio.TotalPortfolioValue
        return 100.0 * ((self.Portfolio.TotalPortfolioValue / self.__cost_portfolio) - 1.0)

    # def CloseTo(self, x, y, delta):
    #     return abs(x-y) < delta

    # def OnData(self, data):
        
    #     for sec in self.optionsData.keys():
    #         _Trade = self.optionsData.get(sec).get("Trade")
    #         _SupportResistance = self.optionsData.get(sec).get("RS")
    #         _mean = self.optionsData.get(sec).get("mean")
            
    #         _Trade.ManageOpenPositions()
    #         price = sec.Price
    #         support = _SupportResistance.NextSupport()
    #         resistance = _SupportResistance.NextResistance()
    #         mean = _mean.Current.Value
            
    #         if (self.CloseTo(price, support, 0.1) and (mean > support)) and self.Portfolio[sec].Invested:
    #             t = _Trade.Create(OrderDirection.Buy)
    #             self.Log(f"{self.Time} LONG: Price {price} Support {support}")
                
    #         if (self.CloseTo(price, resistance, 0.1) and (mean < resistance)) and self.Portfolio[sec].Invested:
    #             t = _Trade.Create(OrderDirection.Sell)
    #             self.Log(f"{self.Time} SHORT: Price {price} Resistance {resistance}")

    def OnEndOfDay(self):
        if self.IsWarmingUp or not self.Securities[self.__benchmark].HasData:
            return

        openOrders = self.Transactions.GetOpenOrders()
        openLimitOrders = [order for order in openOrders if (order.Type == OrderType.Limit) or (order.Type == OrderType.StopMarket)]
        if len(openLimitOrders)> 0:
            for x in openLimitOrders:
                self.Transactions.CancelOrder(x.Id)

        if self.Time.year != self.year:
            self.ResetPlot()
            self.__plot_every_n_days_i == -1

        self.__plot_every_n_days_i += 1
        if self.__plot_every_n_days_i % self.__plot_every_n_days != 0:
            return
        
        self.Plot('Performance', self.__benchmark, self.CalculateBenchmarkPerformance())
        self.Plot('Performance', "Algorithm", self.CalculatePortfolioPerformance())
        # self.Plot(f"Cash", "Remaining", self.Portfolio.Cash)
        # total_unrealized_pct = 0
        # for kvp in self.Portfolio:
        #     security_holding = kvp.Value
        #     sec = security_holding.Symbol.Value
        #     if self.Portfolio[sec].Invested:
        #         self.Plot(f"UnrealizedProfitPercent", str(sec), self.Portfolio[sec].UnrealizedProfitPercent)
        #         total_unrealized_pct += self.Portfolio[sec].UnrealizedProfitPercent
        #         # security_holding = round(float(self.Portfolio[sec].AveragePrice*self.Portfolio[sec].Quantity), 3)
        #         # if security_holding >= int(self.Portfolio.Cash*0.4):
        #         #     self.Plot(f"UnrealizedProfitPercent", str(sec)+"_at 0.5 bp", self.Portfolio[sec].UnrealizedProfitPercent)
        #         # elif security_holding >= int(self.Portfolio.Cash*0.1):
        #         #     self.Plot(f"UnrealizedProfitPercent", str(sec)+"_at 0.1 bp", self.Portfolio[sec].UnrealizedProfitPercent)
        #         # self.Plot(f"Cash", str(sec), round(self.Portfolio[sec].AveragePrice*self.Portfolio[sec].Quantity, 4))
        # self.Plot(f"UnrealizedProfitPercent", "Total", total_unrealized_pct)
                
    def MarketOpen(self):
        return self.Time.hour != 0 and self.Time.minute == 1

    # def MarketClose(self):
    #     for sec in self.optionsData.keys():
    #         _SupportResistance = self.optionsData.get(sec).get("RS")
    #         _SupportResistance.Reset()
        
    def UpdateTickets(self):
        openOrders = self.Transactions.GetOpenOrders()
        openLimitOrders = [order for order in openOrders if (order.Type == OrderType.Limit) or (order.Type == OrderType.StopMarket)]
        if len(openLimitOrders)> 0:
            for x in openLimitOrders:
                self.Transactions.CancelOrder(x.Id)    
        
        invested = [x.Key for x in self.Portfolio if x.Value.Invested]
        for symbol in invested:
            security_holding = self.Portfolio[symbol]
            quantity = security_holding.Quantity
            price = security_holding.AveragePrice
            unrealized_profit_pct = self.Portfolio[symbol].UnrealizedProfitPercent
            security_holding = round(float(self.Portfolio[symbol].AveragePrice*self.Portfolio[symbol].Quantity), 3)
            if security_holding >= int(self.Portfolio.Cash*0.4):
                if self.Securities[symbol].Price > round(self.Portfolio[symbol].AveragePrice*1.1, 3):
                    stopPrice  = self.Securities[symbol].Price * 0.9725
                    limitPrice = self.Securities[symbol].Price * 1.0375
                    self.StopMarketOrder(symbol, -quantity, stopPrice)
                    self.LimitOrder(symbol, -quantity, limitPrice)
            elif security_holding <= int(self.Portfolio.Cash*0.25):
                if self.Securities[symbol].Price > round(self.Portfolio[symbol].AveragePrice*1.1, 3):
                    stopPrice  = self.Securities[symbol].Price * 0.97
                    limitPrice = self.Securities[symbol].Price * 1.05
                    self.StopMarketOrder(symbol, -quantity, stopPrice)
                    self.LimitOrder(symbol, -quantity, limitPrice)
                    
    def AddCash(self):
        self.Portfolio.SetCash(self.Portfolio.Cash + self.added_cash)

    def UpKeep(self):
        self.Portfolio.SetCash(self.Portfolio.Cash - self.upkeep)        
        
# #### Scratch
#     def OptionFilterUniverse(self, universe):
#         # Select puts 2-3 strikes OOM, expiring at least 2 days out; but no more than 7
#         return universe.IncludeWeeklys().Strikes(-3, 3).Expiration(2, 7)
from AlgorithmImports import *

from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from QuantConnect.Data.Market import TradeBar
from QuantConnect.Indicators.CandlestickPatterns import *

import pandas as pd
import numpy as np
from scipy import stats
import statistics
from operator import itemgetter
from functools import reduce
from symbol_data_functions import SymbolData


class CustomRiskModel(RiskManagementModel):
    def __init__(self, algorithm, maximumDrawdownPercent = 0.025, maximumUnrealizedProfitPercent = 0.175, resolution = Resolution.Daily, *args, **kwargs):
        super().__init__()
        '''Initializes a new instance class with various risk management systems
        Args:
            maximumDrawdownPercent: The maximum percentage drawdown allowed for algorithm portfolio compared with the highest unrealized profit, defaults to 5% drawdown
            maximumUnrealizedProfitPercent: The maximum percentage unrealized profit allowed for any single security holding, defaults to 5% drawdown per security'''
        self.resolution = resolution
        self.maximumUnrealizedProfitPercent = abs(maximumUnrealizedProfitPercent)
        self.TotalmaximumUnrealizedProfitPercent = abs(maximumUnrealizedProfitPercent) * 1.5
        self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)
        self.TotalmaximumDrawdownPercent = -abs(maximumDrawdownPercent) * 1.5
        self.Liquidate_all = False
        self.total_unrealized_profit_pct = 0.0
        self.total_drawdown_pct = 0.0
        self.trailing = dict()

    def ManageRisk(self, algorithm, targets):
        '''Manages the algorithm's risk at each time step
        Args:
            algorithm: The algorithm instance
            targets: The current portfolio targets to be assessed for risk'''
        targets = []
        
        for kvp in algorithm.Securities:
            symbol = kvp.Key
            security = kvp.Value
            
            # # Remove if not invested
            # if not security.Invested:
            #     self.trailing.pop(symbol, None)
            #     continue
            
            unrealized_profit_pct = security.Holdings.UnrealizedProfitPercent
            
            # # Add newly invested securities
            # value = self.trailing.get(symbol)
            # if value == None:
            #     newValue = unrealized_profit_pct if unrealized_profit_pct > 0 else 0
            #     self.trailing[symbol] = newValue
            #     continue
            
            # # Check for new high and update
            # if value < unrealized_profit_pct:
            #     self.trailing[symbol] = unrealized_profit_pct
            #     continue
            
            # self.total_unrealized_profit_pct += unrealized_profit_pct
            # # Liquidate if Drawdown from MaximumUnrealizedProfitPercent is reached:
            # pnl = self.GetDrawdownPercent(unrealized_profit_pct, symbol)
            # if len(targets) != 0:
            #     unreal_pct = self.maximumUnrealizedProfitPercent
            #     if unreal_pct > 0.175:
            #         if pnl < float(self.maximumDrawdownPercent - 2.0):
            #             targets.append(PortfolioTarget(security.Symbol, 0))
            #     elif unreal_pct > 0.15:
            #         if pnl < float(self.maximumDrawdownPercent - 2.0):
            #             targets.append(PortfolioTarget(security.Symbol, 0))
            #     elif unreal_pct > 0.1:
            #         if pnl < float(self.maximumDrawdownPercent - 2.0):
            #             targets.append(PortfolioTarget(security.Symbol, 0))
            #     elif unreal_pct <= -0.04:
            #         if pnl < float(self.maximumDrawdownPercent - 0.0):
            #             targets.append(PortfolioTarget(security.Symbol, 0))
            
            # self.total_drawdown_pct += pnl
            
            # If maximum unrealized profit percent reached, liquidate
            if unrealized_profit_pct > self.maximumUnrealizedProfitPercent:
                targets.append(PortfolioTarget(security.Symbol, 0))
        
        
        # # If Total Porfolio Drawdown Percent is Met
        # if len(targets) != 0:
        #     total_unreal_pct = self.TotalmaximumUnrealizedProfitPercent
        #     if total_unreal_pct > 0.225:
        #         if self.total_drawdown_pct < float(self.TotalmaximumDrawdownPercent - 4.0):
        #             self.Liquidate_all = True
        #     elif total_unreal_pct > 0.2:
        #         if self.total_drawdown_pct < float(self.TotalmaximumDrawdownPercent - 4.0):
        #             self.Liquidate_all = True
        #     elif total_unreal_pct > 0.18:
        #         if self.total_drawdown_pct < float(self.TotalmaximumDrawdownPercent - 4.0):
        #             self.Liquidate_all = True
        #     elif total_unreal_pct <= -0.8:
        #         if self.total_drawdown_pct < float(self.TotalmaximumDrawdownPercent - 0.0):
        #             self.Liquidate_all = True
                    
            
        # If Total Porfolio Unrealized Profit Percent is Met
        # if self.total_unrealized_profit_pct > self.TotalmaximumUnrealizedProfitPercent:
        #     self.Liquidate_all = True
            
        
        # if self.Liquidate_all is True:
        #     targets = [ PortfolioTarget(target.Symbol, 0) for target in targets ]
            
        # if algorithm.Time.hour == 16 and algorithm.Time.minute == 0.00:
        #     self.total_unrealized_profit_pct = 0.0
            #self.total_drawdown_pct = 0.0
            
            
        return targets
        
    # def GetDrawdownPercent(self, unrealized_profit_pct, symbol):
    #     max_holding_pct = self.trailing.get(symbol)
    #     cur_holding_pct = unrealized_profit_pct
    #     return float(cur_holding_pct) - float(max_holding_pct)
#region imports
from AlgorithmImports import *
#endregion
import scipy as sp
from sklearn.linear_model import LinearRegression
import numpy as np

class Market_Profile:
    
    def __init__(self):
        # Define lists, dictionaries needed for storing values
        self.slope_dict = {}
        self.ranked_ra = {}
        self.volume_profiles = {}
        self.val = {}
        self.vah = {}
        self.pct_change = {}
        self.top_stocks = {}

    def OnData(self, data):
        
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Arguments:
                data: Slice object keyed by symbol containing the stock data
        '''
        if self.Time.hour == 10 and self.Time.minute == 30:
            
            for key, value in self.ranked_ra.items():
                if data.ContainsKey(key) and data[key] is not None:
                    self.pct_change[key] = data[key].Price # some calculation with price at 10:30 AM; resulting value is stored in a dictionary as value with according key pair (I removed the calculation on purpose)
            
            
            self.pct_change = dict(sorted(self.pct_change.items(), key=lambda x: x[1], reverse=True))
            ranked_pct_change = {item:index for index, item in enumerate(self.pct_change,1)}

            #Combine ranking of both ranked_ra & val_pct_change; Data is pulled from dictionaries stored in Initialize and they according was generated with DataGenerator-Function
            for key, value in self.ranked_ra.items():
                if key not in ranked_pct_change:
                    continue
                value_2 = ranked_pct_change[key]
                self.top_stocks[key] = value + value_2
    
            #Rank stocks according to total rank
            self.top_stocks = dict(sorted(self.top_stocks.items(), key=lambda x: x[1], reverse=False))
    
    def CalcMarketProfile(self, algorithm, timeframe, symbols):
        
        # Clear all the data from the past before starting the calculation for this day again
        self.slope_dict.clear()
        self.ranked_ra.clear()
        self.volume_profiles.clear()
        self.val.clear()
        self.vah.clear()
        
        history = algorithm.History(symbols, timeframe, Resolution.Daily)
        if history.empty or 'close' not in history.columns:
            return
        
        for symbol in symbols:
            price_data = history.loc[str(symbol)]["close"].iloc[-len(history):]
            y = np.log(price_data)
            x = np.arange(len(y))
            slope, intercept, r_value, p_value, std_err = sp.stats.linregress(x,y)
            self.slope_dict[str(symbol)] = {"Slope":slope, "R²":r_value**2}
            
        self.slope_dict = dict(sorted(self.slope_dict.items(), key=lambda x: x[1]["R²"], reverse=True))
        self.ranked_ra = {}
        for index, (key, item) in enumerate(self.slope_dict.items(),1):
            #if index >= len(self.slope_dict.keys())*0.1:
            #    break
            self.ranked_ra[key] = index

        slices_vp = algorithm.History(list(self.ranked_ra.keys()), int(timeframe*8), Resolution.Hour) # timeframe*60
        
        #Code to create volume profiles for all requested stocks
        for i in self.ranked_ra.keys():
            #create stock keys in volume profile
            self.volume_profiles[str(i)] = {}
            
            low = round(min(slices_vp.loc[str(i)]["close"]), 2)
            high = round(max(slices_vp.loc[str(i)]["close"]), 2)
            
            price_range = high - low
            total_volume = sum(slices_vp.loc[str(i)]["volume"])

            #create volume profile for every stock in keys
            for row in slices_vp.loc[str(i)].itertuples():
                if row.volume > 0:
                    key = round(row.close, 2)
                    volume_per_level = row.volume
                    if key not in self.volume_profiles[str(i)].keys():
                        self.volume_profiles[str(i)][str(key)] = volume_per_level
                    else:
                        self.volume_profiles[str(i)][str(key)] += volume_per_level
                
            # Set target volume - 70% of daily volume
            target_vol = sum(self.volume_profiles[str(i)].values()) * 0.7
        
            # Get the price level that had the largest volume
            max_vol_price = max(self.volume_profiles[str(i)], key=self.volume_profiles[str(i)].get)
        
            # Setup a window to capture the POC, centered at the most liquid level
            curr_max_price = float(max_vol_price)
            curr_min_price = float(max_vol_price)
            curr_vol = self.volume_profiles[str(i)][max_vol_price]
        
            price_levels = sorted([float(price_level) for price_level, vol in self.volume_profiles[str(i)].items() if vol > 0])
        
        
            # Grow window bounds until we have 70% of the day's volume captured
            while curr_vol < target_vol:
                
                # Price one level up
                price_up = None
                price_up_vol = 0
                up_prices = [price for price in price_levels if price > curr_max_price]
                if len(up_prices) > 0:
                    price_up = up_prices[0]
                    price_up_vol = self.volume_profiles[str(i)][str(price_up)]
                
                # Price one level down
                price_down = None
                price_down_vol = 0
                down_prices = [price for price in price_levels if price < curr_min_price]
                if len(down_prices) > 0:
                    price_down = down_prices[-1]
                    price_down_vol = self.volume_profiles[str(i)][str(price_down)]
                
                #Grow windows in the direction of more volume
                if price_up is not None and (price_up_vol > price_down_vol):
                    curr_max_price = round(price_up, 2)
                    curr_vol += price_up_vol
                else:
                    curr_min_price = round(price_down, 2)
                    curr_vol += price_down_vol
                
            
            # Save VAL, value area low & VAH, value area high for each stock    
            self.val[str(i)] = curr_min_price
            self.vah[str(i)] = curr_max_price

        for key, value in self.ranked_ra.items():
            self.pct_change[key] = algorithm.Securities[key].Price # some calculation with price at 10:30 AM; resulting value is stored in a dictionary as value with according key pair (I removed the calculation on purpose)
        
        self.pct_change = dict(sorted(self.pct_change.items(), key=lambda x: x[1], reverse=True))
        ranked_pct_change = {item:index for index, item in enumerate(self.pct_change,1)}

        #Combine ranking of both ranked_ra & val_pct_change; Data is pulled from dictionaries stored in Initialize and they according was generated with DataGenerator-Function
        for key, value in self.ranked_ra.items():
            if key not in ranked_pct_change:
                continue
            value_2 = ranked_pct_change[key]
            self.top_stocks[key] = value + value_2

        #Rank stocks according to total rank
        self.top_stocks = dict(sorted(self.top_stocks.items(), key=lambda x: x[1], reverse=False)[:2])
            
        return self.volume_profiles, self.val, self.vah, self.ranked_ra, self.top_stocks
        
    def GetMarketProfile(self, algorithm, symbol):
        return self.volume_profiles, self.val.get(str(symbol)), self.vah.get(str(symbol)), self.ranked_ra.get(str(symbol)), self.top_stocks
#region imports
from AlgorithmImports import *
#endregion
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from QuantConnect.Data.Market import TradeBar
from QuantConnect.Indicators.CandlestickPatterns import *

import pandas as pd
import numpy as np
from scipy import stats
from scipy.signal import argrelextrema
import statistics
import datetime as dt
from operator import itemgetter
from functools import reduce

from helpers import myPortfolioOptimizer
from symbol_data_functions import SymbolData
from FilterIndicators import *
from SmartRollingWindow import *
from pykalman import KalmanFilter

from market_profile import Market_Profile
from trade import *
from levels import *
import scipy as sp
from sklearn.linear_model import LinearRegression

class PortfolioManagementModel(PortfolioConstructionModel):
    def __init__(self,
                 algorithm,
                 RET=252,
                 EXCL=21,
                 LEV=1.00,
                 resolution = Resolution.Daily,
                 portfolioBias = PortfolioBias.Long,
                 *args, **kwargs):
        super().__init__()
        algorithm.PortfolioBias = portfolioBias
        self.resolution = resolution
        self.RET = RET
        self.EXCL = EXCL
        self.LEV = LEV
        self.VOLA = 126
        self.STK1 = algorithm.AddEquity('SPY', self.resolution).Symbol # SPXL/SPY
        self.STK2 = algorithm.AddEquity('QQQ', self.resolution).Symbol # TQQQ/QQQ
        self.STK3 = algorithm.AddEquity('DIA', self.resolution).Symbol # DIA
        self.STK4 = algorithm.AddEquity('IWM', self.resolution).Symbol # URTY/IWM
        self.STK5 = algorithm.AddEquity('VTI', self.resolution).Symbol # AGQ/VTI
        self.STK6 = algorithm.AddEquity('FDN', self.resolution).Symbol # AGQ/VTI
        self.STK7 = algorithm.AddEquity('IWF', self.resolution).Symbol # AGQ/VTI
        self.BND1 = algorithm.AddEquity('TLH', self.resolution).Symbol # TMF/TLH
        self.BND2 = algorithm.AddEquity('TLT', self.resolution).Symbol # UGL/TLT
        self.BND3 = algorithm.AddEquity('IEI', self.resolution).Symbol # TMF/TLH
        self.BND4 = algorithm.AddEquity('IEF', self.resolution).Symbol # UGL/TLT
        #self.ALT1 = algorithm.AddEquity('XLE', self.resolution).Symbol # UGL/TLT
        #self.ALT2 = algorithm.AddEquity('GLD', self.resolution).Symbol # UGL/TLT
        # self.LEV1 =  algorithm.AddEquity('SPXL', self.resolution).Symbol
        # self.LEV2 =  algorithm.AddEquity('TQQQ', self.resolution).Symbol
        # self.LEV3 =  algorithm.AddEquity('URTY', self.resolution).Symbol
        # self.LEV4 =  algorithm.AddEquity('TMF', self.resolution).Symbol
        self.STOCKS = [self.STK1, self.STK2, self.STK3, self.STK4, self.STK5, self.STK6, self.STK7]
        #self.STOCKS_LEV = [self.LEV1, self.LEV2, self.LEV3]
        self.BONDS = [self.BND1, self.BND2, self.BND3, self.BND4]
        #self.BONDS_LEV = [self.LEV4]
        self.ASSETS = self.STOCKS + self.BONDS #+ self.STOCKS_LEV + self.BONDS_LEV
        #self.ASSETS_OPTIONS = dict()
        self.data = dict()
        # self.datafourHour = dict()
        # self.datathirtyMin = dict()
        self.market_profile = Market_Profile()
        self.QQQ_price = RollingWindow[float](5)
        self.QQQ_volume = RollingWindow[float](5)
        
        for symbol in self.ASSETS:
            self.consolidator = TradeBarConsolidator(timedelta(days=1))
            self.consolidator.DataConsolidated += self.consolidation_handler
            algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
            # self.fourHour_consolidator = TradeBarConsolidator(timedelta(minutes=60*4))
            # self.fourHour_consolidator.DataConsolidated += self.fourHour_consolidation_handler
            # algorithm.SubscriptionManager.AddConsolidator(symbol, self.fourHour_consolidator)
            # self.thirtyMinute_consolidator = TradeBarConsolidator(timedelta(minutes=30))
            # self.thirtyMinute_consolidator.DataConsolidated += self.thirtyMinute_consolidation_handler
            # algorithm.SubscriptionManager.AddConsolidator(symbol, self.thirtyMinute_consolidator)
            
        self.history = np.log(algorithm.History(self.ASSETS, self.VOLA + 1, Resolution.Daily))
        # self.historyHour = np.log(algorithm.History(self.ASSETS, (90*6), Resolution.Hour))
        # self.historyMinute = np.log(algorithm.History(self.ASSETS, (15*24*2), Resolution.Minute))
        self.pfo = myPortfolioOptimizer(minWeight=0, maxWeight=1)

        for symbol in self.ASSETS:
            algorithm.Securities[symbol].SetLeverage(1)
            
        # for symbol in self.ASSETS:
        #     symbol.SetDataNormalizationMode(DataNormalizationMode.Raw)
        #     self.ASSETS_OPTIONS[symbol] = dict()
        #     _option = self.AddOption(symbol)
        #     _option.SetFilter(self.OptionFilterUniverse)
        #     _trade = TradeManagement(self, symbol, _option.Symbol)
        #     _RS = SupportResistance(self, symbol)
        #     self.ASSETS_OPTIONS[symbol]["Trade"] = _trade
        #     self.ASSETS_OPTIONS[symbol]["option"] = _option
        #     self.ASSETS_OPTIONS[symbol]["RS"] = _RS
 
    def consolidation_handler(self, sender, consolidated):
        self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
        self.history = self.history.iloc[-(self.VOLA + 1):]

    # def fourHour_consolidation_handler(self, sender, consolidated):
    #     self.historyHour.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
    #     self.historyHour = self.historyHour.iloc[-(90*6):]

    # def thirtyMinute_consolidation_handler(self, sender, consolidated):
    #     self.historyMinute.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
    #     self.historyMinute = self.historyMinute.iloc[-(15*24*2):]
        
    def OnSecuritiesChanged(self, algorithm, changes):
        
        addedSymbols = []
        for security in changes.AddedSecurities:
            addedSymbols.append(security.Symbol)
            if security.Symbol not in self.data:
                self.data[security.Symbol] = SymbolData(algorithm, security.Symbol, Resolution.Daily)
                # self.datafourHour[security.Symbol] = SymbolData(algorithm, security.Symbol, Resolution.Hour)
                # self.datathirtyMin[security.Symbol] = SymbolData(algorithm, security.Symbol, Resolution.Minute)
   
        if len(addedSymbols) > 0:
            history = algorithm.History(addedSymbols, self.VOLA + 1, Resolution.Daily).loc[addedSymbols]
            for symbol in addedSymbols:
                try:
                    history = np.log(history)
                    self.data[symbol].Warmup(history.loc[symbol])
                except:
                    algorithm.Debug(str(symbol))
                    continue
            # # Four Hour
            # history = algorithm.History(addedSymbols, (90*6), Resolution.Hour).loc[addedSymbols]
            # for symbol in addedSymbols:
            #     try:
            #         history = np.log(history)
            #         self.datafourHour[symbol].Warmup(history.loc[symbol])
            #     except:
            #         algorithm.Debug(str(symbol))
            #         continue
            # # Thirty Minute
            # history = algorithm.History(addedSymbols, (15*24*2), Resolution.Minute).loc[addedSymbols]
            # for symbol in addedSymbols:
            #     try:
            #         history = np.log(history)
            #         self.datathirtyMin[symbol].Warmup(history.loc[symbol])
            #     except:
            #         algorithm.Debug(str(symbol))
            #         continue

    def calc_cndl_score(self, asset, res = "Daily"):
        if res == "Daily":
            cndl_coef = sum([x.Current.Value for x in self.data[asset].candles])
        return cndl_coef

    def custom_filter(self, algorithm, symbol, filter_type = 'both'):
        data_list = [self.data]# [self.datathirtyMin, self.datafourHour, self.data]
        count = 0
        for _dict in data_list:
            slope_cond = (_dict[symbol].roc_slope > 0.00) and (_dict[symbol].vol_slope > 0.00)
            slope_down = (_dict[symbol].roc_slope > 0.00) and (_dict[symbol].vol_slope > 0.00)
            signals = (_dict[symbol].breakout or (_dict[symbol].vpnIndicator and (_dict[symbol].is_uptrend and _dict[symbol].trix_uptrend or (_dict[symbol].roc_vol_signal_up and _dict[symbol].entry_signal and _dict[symbol].kal_entry_signal and _dict[symbol].rsi_entry_signal and _dict[symbol].macd_entry_signal and _dict[symbol].williams_entry_signal))) or (_dict[symbol].macd_uptrend and _dict[symbol].rsi_uptrend))
            quick_signals = (_dict[symbol].vpnIndicator and (_dict[symbol].breakout or _dict[symbol].entry_signal or _dict[symbol].kal_entry_signal or _dict[symbol].rsi_entry_signal or _dict[symbol].macd_entry_signal or _dict[symbol].williams_entry_signal or _dict[symbol].quick_up))
            down_signals = (_dict[symbol].breakdown or ((not _dict[symbol].vpnIndicator) and (_dict[symbol].is_downtrend and _dict[symbol].trix_downtrend or (_dict[symbol].roc_vol_signal_down and _dict[symbol].exit_signal and _dict[symbol].kal_exit_signal and _dict[symbol].rsi_exit_signal and _dict[symbol].macd_exit_signal and _dict[symbol].williams_exit_signal))) or (_dict[symbol].macd_downtrend and _dict[symbol].rsi_downtrend))
            hh_cond = _dict[symbol].hh_all and _dict[symbol].hh_sum
            ll_cond = _dict[symbol].ll_all and _dict[symbol].ll_sum
            market_uptrend = (_dict[symbol].mp_entry_signal or _dict[symbol].mp_uptrend) and _dict[symbol].roc_vol_signal_up
            market_downtrend = (_dict[symbol].mp_exit_signal or _dict[symbol].mp_downtrend) and _dict[symbol].roc_vol_signal_down
            if filter_type == 'both':
                if (slope_cond) and (signals):
                    count += 1
            if filter_type == 'either':
                if (slope_cond) or (signals):
                    count += 1
            if filter_type == 'quick':
                if quick_signals and market_uptrend: 
                    count += 1
            if filter_type == 'pullback':
                if (slope_cond) and (down_signals) and market_downtrend:
                    count += 1
            if filter_type == 'lev':
                if (slope_cond) and (_dict[symbol].median_roc_momentum >= 0.005) and (signals):
                    count += 1
        
        if count == len(data_list):
            return True
        else:
            return False

    def calc_vola(self, algorithm, sec):
        prices = np.log(algorithm.History(sec, TimeSpan.FromDays(126), self.resolution).close)
        volatility = prices.pct_change().std() * np.sqrt(252)
        return volatility

    def CreateTargets(self, algorithm, insights):
        # if algorithm.Time.hour == 00 and algorithm.Time.minute == 00:
        #     for sec in self.ASSET_OPTIONS.keys():
        #         rs = self.ASSET_OPTIONS.get(sec)
        #         rc.Reset()
        
        if algorithm.IsWarmingUp:
            return []
        #if algorithm.Time.month == 1: return []
        targets = []
        
        # We expect at most only one active insight since we only
        # generate insights for one equity.
        assert len(insights) <= 1
        if len(insights) == 1:
            insight = insights[0]
            
            if insight.Direction != InsightDirection.Flat:
                williams_fast = ((self.data[self.STK1].williamsPR.Current.Value >= -95.00) and (self.data[self.STK2].williamsPR.Current.Value >= -95.00))
                williams_slow = ((self.data[self.STK1].williamsPR_slow.Current.Value >= -95.00) and (self.data[self.STK2].williamsPR_slow.Current.Value >= -95.00))
                williams_median = ((self.data[self.STK1].williams_median >= -80.00) and (self.data[self.STK2].williams_median >= -80.00))
                williams = williams_fast and williams_slow and williams_median
                market_uptrend = all(self.custom_filter(algorithm, symbol, filter_type = 'both') for symbol in [self.STK1, self.STK2, self.STK3, self.STK4]) and all(self.data[symbol].trix_uptrend for symbol in [self.STK1, self.STK2, self.STK3, self.STK4]) and all(self.data[symbol].mp_uptrend for symbol in [self.STK1, self.STK2, self.STK3, self.STK4])
                bond_uptrend = all(self.custom_filter(algorithm, symbol, filter_type = 'both') for symbol in self.BONDS) and all(self.data[symbol].trix_uptrend for symbol in self.BONDS) and all(self.data[symbol].mp_uptrend for symbol in self.BONDS)
                if insight.Direction == InsightDirection.Down and williams and (not market_uptrend):
                    self.bull = False
                elif not bond_uptrend:
                    self.bull = True

                #self.market_profile.CalcMarketProfile(algorithm, 21, self.ASSETS)            
                #top = [*e]
                #algorithm.Debug("top"+"_"+ str(len(top))+ "_"+str(top))
                # for sec in [self.STK2]:
                #     #algorithm.Debug("mp_"+str(a))
                #     #algorithm.Debug("val_"+str(b))
                #     #algorithm.Debug("vah_"+str(c))
                #     #algorithm.Debug("%change_"+str(d))
                #     #algorithm.Debug("top"+ str(len(e)) +"_"+str(e))
                #     # a, b, c, d, e = self.market_profile.GetMarketProfile(algorithm, sec)
                #     # a = a.get(str(sec))
                #     # poc = float(max(a, key=a.get))
                #     # algorithm.Plot("Market Profile", str(sec)+"_POC", poc)
                #     # algorithm.Plot("Market Profile", str(sec)+"_VAL", b)
                #     # algorithm.Plot("Market Profile", str(sec)+"_VAH", c)
                #     # #algorithm.Plot("Market Profile", str(sec)+"_%change", d)
                #     # median_price = statistics.median([algorithm.Securities[sec].Open, algorithm.Securities[sec].High, algorithm.Securities[sec].Low, algorithm.Securities[sec].Close])
                #     # algorithm.Plot("Market Profile", str(sec)+"_MedianPrice", median_price)
                #     # _50 = self.data[sec].vwap_fst.Current.Value
                #     # _100 = self.data[sec].vwap_med.Current.Value
                #     # #_150 = self.data[sec].vwap_mlng.Current.Value
                #     # _200 = self.data[sec].vwap_lng.Current.Value
                #     # algorithm.Plot("Market Profile", str(sec)+"_50", _50)
                #     # algorithm.Plot("Market Profile", str(sec)+"_100", _100)
                #     # #algorithm.Plot("Market Profile", str(sec)+"_150", _150)
                #     # algorithm.Plot("Market Profile", str(sec)+"_200", _200)
                #     # val = float(stats.sem([poc, b, c, median_price], axis=0))
                #     # algorithm.Plot("Standard Error Mean", str(sec), val)
                #     # val_vwap = float(stats.sem([poc, b, c, median_price, _50, _100, _200], axis=0))
                #     # algorithm.Plot("Standard Error Mean", str(sec)+"_with_vwaps", val_vwap)
                        
                #     algorithm.Plot("UnrealizedProfitPercent", str(sec), algorithm.Portfolio[sec].UnrealizedProfitPercent)
                    
                #     holdings_pct = round(float(algorithm.Portfolio[sec].HoldingsValue)/algorithm.Portfolio.TotalPortfolioValue, 4)
                    
                #     roc = round(statistics.median([self.data[sec].roc.Current.Value, self.data[sec].roc_fast.Current.Value, self.data[sec].roc_med.Current.Value, self.data[sec].roc_long.Current.Value]), 4)
                #     vol = round(statistics.median([self.data[sec].vol_roc.Current.Value, self.data[sec].vol_roc_fast.Current.Value, self.data[sec].vol_roc_med.Current.Value, self.data[sec].vol_roc_long.Current.Value]), 4)
                #     self.QQQ_price.Add(roc)
                #     self.QQQ_volume.Add(vol)

                #     def calc_divergence(obj):
                #         x = np.array(list(obj))
                #         local_maxima = argrelextrema(x, np.greater)[0]
                #         local_minima = argrelextrema(x, np.less)[0]
                #         if x[-1] > x[-2]:
                #             x = np.append(x, len(x) - 1)
                #         elif x[-1] > x[-2]:
                #             x = np.append(x, len(x) - 1)
                #         hh_all = all(x[local_maxima][i] < x[local_maxima][i+1] for i in range(len(local_maxima)-1))
                #         ll_all = all(x[local_minima][i] > x[local_minima][i+1] for i in range(len(local_minima)-1))
                #         return hh_all, ll_all
                    
                #     if self.QQQ_price.IsReady and self.QQQ_volume.IsReady:
                #         algorithm.Plot("ROC", str(sec)+"price", float(sum(list(self.QQQ_price)))/5)
                #         hh, ll = calc_divergence(list(self.QQQ_price))
                #         algorithm.Plot("Divergence", str(sec)+"_price_hh", float(int(hh)))
                #         algorithm.Plot("Divergence", str(sec)+"_price_ll", float(int(ll)))
                #         algorithm.Plot("ROC", str(sec)+"volume", float(sum(list(self.QQQ_volume)))/5)
                #         hh, ll = calc_divergence(list(self.QQQ_volume))
                #         algorithm.Plot("Divergence", str(sec)+"_volume_hh", float(int(hh)))
                #         algorithm.Plot("Divergence", str(sec)+"_volume_ll", float(int(ll)))
                    
                #     holdings_pct = round(float(algorithm.Portfolio[sec].HoldingsValue)/algorithm.Portfolio.TotalPortfolioValue, 4)
                #     # if holdings_pct > 0.4:
                #     #     algorithm.Plot("UnrealizedProfitPercent", str(sec)+"_at 0.5 bp", algorithm.Portfolio[sec].UnrealizedProfitPercent)
                #     # elif holdings_pct > 0.2:
                #     #     algorithm.Plot("UnrealizedProfitPercent", str(sec)+"_at 0.25 bp", algorithm.Portfolio[sec].UnrealizedProfitPercent)
                #     # elif holdings_pct >= 0.1*0.9:
                #     #     algorithm.Plot("UnrealizedProfitPercent", str(sec)+"_at 0.1 bp", algorithm.Portfolio[sec].UnrealizedProfitPercent)
                #     # else:
                #     #     algorithm.Plot("UnrealizedProfitPercent", str(sec)+"_at 0.0 bp", algorithm.Portfolio[sec].UnrealizedProfitPercent)
                #     algorithm.Plot(f"Holdings Value %", str(sec), holdings_pct)
                    
                #     if insight.Direction == InsightDirection.Flat:
                #         _dir = 0
                #     elif insight.Direction == InsightDirection.Up:
                #         _dir = 1
                #     else:
                #         _dir = -1
                #     algorithm.Plot("Bull vs Bear", "Market_Before", float(_dir))
                #     if not self.bull:
                #         _dir = -1
                #     else:
                #         _dir = 1
                #     algorithm.Plot("Bull vs Bear", "Market_After", float(_dir))
                    
                    # _out = (median_price <= _200) and (median_price <= poc) and (holdings_pct >= 0.4)
                    
                    # algorithm.Plot(f"Trade Behavior", str(sec), float(int(_out)))
                selected = list()
                if self.bull:
                    stocks = [(symbol, self.data[symbol].median_roc, self.data[symbol].volatility) for symbol in self.STOCKS if self.custom_filter(algorithm, symbol, filter_type = 'both')]
                    stocks.sort(key=itemgetter(1, 2), reverse=True)
                    for sec, roc, vola in stocks:
                        if (len(selected) < 2):
                            selected.append(sec)
                    if len(selected) < 2:
                        stocks = [(symbol, self.data[symbol].median_roc, self.data[symbol].volatility) for symbol in self.STOCKS if self.custom_filter(algorithm, symbol, filter_type = 'either')]
                        stocks.sort(key=itemgetter(1, 2), reverse=True)
                        for sec, roc, vola in stocks:
                            if (len(selected) < 2) and (sec not in selected):
                                selected.append(sec)
                elif not self.bull:
                    bonds = [(symbol, self.data[symbol].median_roc, self.data[symbol].volatility) for symbol in self.BONDS if self.custom_filter(algorithm, symbol, filter_type = 'both')]
                    bonds.sort(key=itemgetter(1, 2), reverse=True)
                    for sec, roc, vola in bonds:
                        if (len(selected) < 2):
                            selected.append(sec)
                    if len(selected) < 2:
                        bonds = [(symbol, self.data[symbol].median_roc, self.data[symbol].volatility) for symbol in self.BONDS if self.custom_filter(algorithm, symbol, filter_type = 'either')]
                        bonds.sort(key=itemgetter(1, 2), reverse=True)
                        for sec, roc, vola in bonds:
                            if (len(selected) < 2) and (sec not in selected):
                                selected.append(sec)
    
            if insight.Direction == InsightDirection.Flat: 
                return []
                
            # self.asset_weights = self.pfo.CalcWeights(algorithm, selected, 'riskParity', lookback=21)
            # self.asset_weights_max = self.pfo.CalcWeights(algorithm, selected, 'maxReturn', lookback=21)
            # self.asset_weights_max_two = self.pfo.CalcWeights(algorithm, selected, 'maxReturn', lookback=42)
                    
            #self.market_profile.CalcMarketProfile(algorithm, 21, self.ASSETS)  
            for asset in self.ASSETS:
                cond_both = self.custom_filter(algorithm, asset, filter_type = 'both')
                cond_either = self.custom_filter(algorithm, asset, filter_type = 'either')
                cond_pullback = self.custom_filter(algorithm, asset, filter_type = 'pullback')
                
                median_price = statistics.median([algorithm.Securities[asset].Open, algorithm.Securities[asset].High, algorithm.Securities[asset].Low, algorithm.Securities[asset].Close])
                

                profit_pct_cond = algorithm.Portfolio[asset].UnrealizedProfitPercent <= -0.05
                    
                holdings_pct = round(float(algorithm.Portfolio[asset].HoldingsValue)/algorithm.Portfolio.TotalPortfolioValue, 4)
                
                rocsignal = all(x > 0.0 for x in [self.data[asset].rocSignal_quick.Current.Value, self.data[asset].rocSignal_fast.Current.Value, self.data[asset].rocSignal_med.Current.Value, self.data[asset].rocSignal_long.Current.Value])
                volsignal = all(x > 0.0 for x in [self.data[asset].volSignal_quick.Current.Value, self.data[asset].volSignal_fast.Current.Value, self.data[asset].volSignal_med.Current.Value, self.data[asset].volSignal_long.Current.Value])
                roc_vol_signal = rocsignal and volsignal
                
                cond1 = ((all((self.calc_cndl_score(asset, res = tf) > 0) for tf in ["Daily"])) and cond_both) 
                cond2 = ((all((self.calc_cndl_score(asset, res = tf) >= -2) for tf in ["Daily"])) or (cond_both and self.data[asset].cndl_uptrend))
                cond3 = ((all((self.calc_cndl_score(asset, res = tf) >= -2) for tf in ["Daily"])) or (cond_either and self.data[asset].cndl_uptrend))
                
                if (asset in selected) and (not cond_pullback):
                    # weight = self.asset_weights[self.asset_weights.index == str(asset.Value)][0]
                    # weight_max = self.asset_weights_max_two[self.asset_weights_max_two.index == str(asset.Value)][0]
            
                    if ((algorithm.Portfolio[asset].Invested and (cond2 or cond3)) or cond1):
                        if cond_pullback and ((profit_pct_cond) and (holdings_pct >= 0.1)):
                            targets.append(PortfolioTarget.Percent(algorithm, asset, 0.0))
                        else:
                            targets.append(PortfolioTarget.Percent(algorithm, asset, 0.5))
                    elif not algorithm.Portfolio[asset].Invested and (cond2 or cond3):
                        if cond_pullback:
                            targets.append(PortfolioTarget.Percent(algorithm, asset, 0.0))
                        elif roc_vol_signal:
                            targets.append(PortfolioTarget.Percent(algorithm, asset, 0.25))
                        else:
                            targets.append(PortfolioTarget.Percent(algorithm, asset, 0.1))
                else:
                    targets.append(PortfolioTarget.Percent(algorithm, asset, 0.0))
                    
        return targets

    # def ExecuteOptionsTrade(self, symbol):
    #     _trade = self.ASSETS_OPTIONS.get(symbol).get("Trade")
    #     _rs = self.ASSETS_OPTIONS.get(symbol).get("Trade")

    # def OptionFilterUniverse(self, universe):
    #     # Select puts 2-3 strikes OOM, expiring at least 2 days out; but no more than 7
    #     return universe.IncludeWeeklys().Strikes(-3, 3).Expiration(2, 7)
from AlgorithmImports import *

from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from QuantConnect.Data.Market import TradeBar
from QuantConnect.Indicators.CandlestickPatterns import *

import inspect
import pandas as pd
import numpy as np
from scipy import stats
from scipy.signal import argrelextrema
import statistics
from operator import itemgetter
from functools import reduce

from FilterIndicators import *
from SmartRollingWindow import *
from pykalman import KalmanFilter

from levels import *
from market_profile import Market_Profile

class SymbolData(object):
    def __init__(self, algorithm, symbol, resolution = Resolution.Daily, *args, **kwargs):
        super().__init__()
        self.Symbol = symbol
        self.res = Resolution.Daily

        if self.res == Resolution.Daily:
            consolidator = TradeBarConsolidator(timedelta(days=1)) # algorithm.ResolveConsolidator(symbol, self.res)
            tf_coef = 1
            res = Resolution.Daily
            multi = 1
        if self.res == Resolution.Hour:
            consolidator = TradeBarConsolidator(timedelta(minutes=60*4)) # algorithm.ResolveConsolidator(symbol, self.res)
            tf_coef = 2
            res = Resolution.Hour
            multi = 1.25
        if self.res == Resolution.Minute:
            consolidator = TradeBarConsolidator(timedelta(minutes=30)) # minutes=240
            tf_coef = 6
            res = Resolution.Minute
            multi = 1.5
        
        self.lookback = 20
        self.ceiling = 30
        self.floor = 10
        self.long = False
        self.short = False
        self.breakout = False
        self.breakdown = False
        self.quick_up = False
        self.quick_down = False
        self.hh_all = False
        self.ll_all = False
        self.hh_sum = False
        self.ll_sum = False
        self.EXCL = 21
        self.scale = 0.00
        self.volatility = 0.00
        self.PriceIsFavorable = False
        self.vwap = IntradayVwap()
        self.SpreadIsFavorable = False
        self.tol = 0.98
        self.acceptingSpreadPercent = 0.001
        self.vol_slope = 0.00
        self.vol_fast_slope = 0.00
        self.roc_slope = 0.00
        self.roc_fast_slope = 0.00
        self.median_roc = 0.00
        self.median_roc_hh = False
        self.median_roc_ll = False
        self.median_vol = 0.00
        # Support Resistance
        self.long = False
        self.short = False
        self.Daily = RollingWindow[TradeBar](21)
        ### Market Profile
        self.market_profile = Market_Profile()
        self.mp_uptrend = False
        self.mp_downtrend = False
        self.mp_entry_signal = False
        self.mp_exit_signal = False
        self.poc_Win =  RollingWindow[float](5)
        self.pocWin = SmartRollingWindow("float", 5)
        self.val_Win =  RollingWindow[float](5)
        self.valWin = SmartRollingWindow("float", 5)
        self.vah_Win =  RollingWindow[float](5)
        self.vahWin = SmartRollingWindow("float", 5)
        self.price_Win =  RollingWindow[float](5)
        self.priceWin = SmartRollingWindow("float", 5)
        self.medpri_Win =  RollingWindow[float](5)
        self.medpriWin = SmartRollingWindow("float", 5)
        self.vwap_fst = VolumeWeightedAveragePriceIndicator(50)
        self.vwap_med = VolumeWeightedAveragePriceIndicator(100)
        self.vwap_mlng = VolumeWeightedAveragePriceIndicator(150)
        self.vwap_lng = VolumeWeightedAveragePriceIndicator(200)
        self.vwap_fst_Win =  RollingWindow[float](5)
        self.vwap_fstWin = SmartRollingWindow("float", 5)
        self.vwap_med_Win =  RollingWindow[float](5)
        self.vwap_medWin = SmartRollingWindow("float", 5)
        self.vwap_lng_Win =  RollingWindow[float](5)
        self.vwap_lngWin = SmartRollingWindow("float", 5)
        self.dont_buy = False
        
        self.is_uptrend = False
        self.is_downtrend = False
        self.entry_signal = False
        self.exit_signal = False
        self.stochasticFast = Stochastic(14, 3, 3)
        self.fast = ExponentialMovingAverage(8)
        self.fast_Win = RollingWindow[float](int(41*multi))
        self.fastVwapWin = SmartRollingWindow("float", 5)
        self.stochasticSlow = Stochastic(21, 3, 3)
        self.slow = ExponentialMovingAverage(14)
        self.slow_Win = RollingWindow[float](int(41*multi))
        self.slowVwapWin = SmartRollingWindow("float", 5)
        self.roc = RateOfChange(int(5*1.0))
        self.roc_fast = RateOfChange(int(3*1.0))
        self.roc_med = RateOfChange(int(8*1.0))
        self.roc_long = RateOfChange(int(14*1.0))
        self.vol_roc = RateOfChange(int(5*1.0))
        self.vol_roc_fast = RateOfChange(int(3*1.0))
        self.vol_roc_med = RateOfChange(int(8*1.0))
        self.vol_roc_long = RateOfChange(int(14*1.0))
        self.roc_Win = RollingWindow[float](int(5*multi))
        self.roclen_Win = RollingWindow[float](int(5*multi))
        self.rocSum_Win = RollingWindow[float](int(5*multi))
        self.vol_Win = RollingWindow[float](int(5*multi))
        self.prices_Win = RollingWindow[float](int(31*multi))
        self.low_Win = RollingWindow[float](int(41*multi))
        self.high_Win = RollingWindow[float](int(41*multi))
        self.roc_prices_Win = RollingWindow[float](int(41*multi))
        self.roc_prices_lev_Win = RollingWindow[float](int(10*multi))
        self.roc_volume_Win = RollingWindow[float](int(41*multi))
        self.stochasticMACD = Stochastic(34, 3, 3)
        self.macd = MovingAverageConvergenceDivergence(12, 26, 9, MovingAverageType.Exponential)
        self.macd_stochfast_Win = RollingWindow[float](int(5*multi))
        self.macdStochFastWin = SmartRollingWindow("float", 5)
        self.macd_stochk_Win = RollingWindow[float](int(5*multi))
        self.macdStochKWin = SmartRollingWindow("float", 5)
        self.macd_stochd_Win = RollingWindow[float](int(5*multi))
        self.macdStochDWin = SmartRollingWindow("float", 5)        
        self.macd_Win = RollingWindow[float](int(5*multi))
        self.macdHist_Win = RollingWindow[float](int(5*multi))
        self.macdFast_Win = RollingWindow[float](int(5*multi))
        self.macdSlow_Win = RollingWindow[float](int(5*multi))
        self.macdSignal_Win = RollingWindow[float](int(5*multi))
        self.macdDelta_Win = RollingWindow[float](int(5*multi))
        self.macd_uptrend = False
        self.macd_downtrend = False
        self.macd_entry_signal = False
        self.macd_exit_signal = False
        self.stochasticRSI = Stochastic(21, 3, 3)
        self.rsi = RelativeStrengthIndex(14, MovingAverageType.Wilders)
        self.rsi_Win = RollingWindow[float](int(5*multi))
        self.rsiWin = SmartRollingWindow("float", 5)
        self.rsiFastStoch_Win = RollingWindow[float](int(5*multi))
        self.rsiStochFastWin = SmartRollingWindow("float", 5)
        self.rsiStochK_Win = RollingWindow[float](int(5*multi))
        self.rsiStochKWin = SmartRollingWindow("float", 5)
        self.rsiStochD_Win = RollingWindow[float](int(5*multi))
        self.rsiStochDWin = SmartRollingWindow("float", 5)
        self.rsi_uptrend = False
        self.rsi_downtrend = False
        self.rsi_entry_signal = False
        self.rsi_exit_signal = False
        self.williamsPR = WilliamsPercentR(14)
        self.williamsPR_slow = WilliamsPercentR(21)
        self.williamsWin = RollingWindow[float](int(5*multi))
        self.williamsPR_Win = RollingWindow[float](int(5*multi))
        self.williamsPRWin = SmartRollingWindow("float", 5)
        self.williamsPR_slow_Win = RollingWindow[float](int(5*multi))
        self.williamsPR_slowWin = SmartRollingWindow("float", 5)
        self.williams_median_roc = 0.00
        self.williams_median = 0.00
        self.williams_uptrend = False
        self.williams_downtrend = False
        self.vpnIndicator = False
        self.vpnScale = 0.00
        self.vpn_period = 10
        self.atr = AverageTrueRange(self.vpn_period, MovingAverageType.Exponential)
        self.vpn_vol_Win = RollingWindow[float](self.vpn_period)
        self.vpn_hlc_Win = RollingWindow[float](self.vpn_period)
        self.vpn_lst = RollingWindow[float](int(6*multi))
        self.stochasticTrix = Stochastic(21, 3, 3)
        self.trix = Trix(9)
        self.trix_slow = Trix(18)
        self.trixFastStoch_Win = RollingWindow[float](int(5*multi))
        self.trixStochFastWin = SmartRollingWindow("float", 5)
        self.trixStochK_Win = RollingWindow[float](int(5*multi))
        self.trixStochKWin = SmartRollingWindow("float", 5)
        self.trixStochD_Win = RollingWindow[float](int(5*multi))
        self.trixStochDWin = SmartRollingWindow("float", 5)
        self.trix_uptrend = False
        self.trix_downtrend = False
        self.trix_entry_signal = False
        self.trix_exit_signal = False
        self.rocSignal_quick = IndicatorExtensions.Over(self.roc_fast, self.roc_med)
        self.volSignal_quick = IndicatorExtensions.Over(self.vol_roc_fast, self.vol_roc_med)
        self.rocSignal_fast = IndicatorExtensions.Over(self.roc_fast, self.roc)
        self.volSignal_fast = IndicatorExtensions.Over(self.vol_roc_fast, self.vol_roc)
        self.rocSignal_med = IndicatorExtensions.Over(self.roc, self.roc_med)
        self.volSignal_med = IndicatorExtensions.Over(self.vol_roc, self.vol_roc_med)
        self.rocSignal_long = IndicatorExtensions.Over(self.roc_med, self.roc_long)
        self.volSignal_long = IndicatorExtensions.Over(self.vol_roc_med, self.vol_roc_long)
        self.rocvolSignal_Win = RollingWindow[float](int(5*multi))
        self.rocvolSignal_median = 0.00
        self.kalFilter = KalmanFilterIndicator(name='Kalman', period=5, selector=Field.Close)
        self.kalFilterLow = KalmanFilterIndicator(name='Kalman', period=5, selector=Field.Low)
        self.kalFilterHigh = KalmanFilterIndicator(name='Kalman', period=5, selector=Field.High)
        self.kalFilterOpen = KalmanFilterIndicator(name='Kalman', period=5, selector=Field.Open)
        self.kalWin = SmartRollingWindow("float", 5)
        self.kalWindowLow = SmartRollingWindow("float", 5)
        self.kalWindowHigh = SmartRollingWindow("float", 5)
        self.priceWin = SmartRollingWindow("float", 5)
        self.priceWindowLow = SmartRollingWindow("float", 5)
        self.priceWindowHigh = SmartRollingWindow("float", 5)
        self.kal_exit_signal = False
        self.kal_entry_signal = False
        # Candles
        self.cndl_abandonedbaby = AbandonedBaby()
        self.cndl_advanceblock = AdvanceBlock()
        self.cndl_belthold = BeltHold()
        self.cndl_breakway = Breakaway()
        self.cndl_closingmarubozu = ClosingMarubozu()
        self.cndl_concealedbabyswallow = ConcealedBabySwallow()
        self.cndl_counterattack = Counterattack()
        self.cndl_darkcloudcover = DarkCloudCover()
        self.cndl_doji = Doji()
        self.cndl_dojistar = DojiStar()
        self.cndl_dragonflydoji = DragonflyDoji()
        self.cndl_engulfing = Engulfing()
        self.cndl_eveningdojistar = EveningDojiStar()
        self.cndl_eveningstar = EveningStar()
        self.cndl_gapsidebysidewhite = GapSideBySideWhite()
        self.cndl_gravestonedoji = GravestoneDoji()
        self.cndl_hammer = Hammer()
        self.cndl_hangingman = HangingMan()
        self.cndl_harami = Harami()
        self.cndl_haramicross = HaramiCross()
        self.cndl_highwavecandle = HighWaveCandle()
        self.cndl_hikkake = Hikkake()
        self.cndl_hikkakemodified = HikkakeModified()
        self.cndl_homingpigeon = HomingPigeon()
        self.cndl_identicalthreecrows = IdenticalThreeCrows()
        self.cndl_inneck = InNeck()
        self.cndl_invertedhammer = InvertedHammer()
        self.cndl_kicking = Kicking()
        self.cndl_kickingbylength = KickingByLength()
        self.cndl_ladderbottom = LadderBottom()
        self.cndl_longleggeddoji = LongLeggedDoji()
        self.cndl_longlinecandle = LongLineCandle()
        self.cndl_marubozu = Marubozu()
        self.cndl_mathold = MatHold()
        self.cndl_matchinglow = MatchingLow()
        self.cndl_morningdojistar = MorningDojiStar()
        self.cndl_morningstar = MorningStar()
        self.cndl_onneck = OnNeck()
        self.cndl_pierce = Piercing()
        self.cndl_rickshawman = RickshawMan()
        self.cndl_risefallthreemethods = RiseFallThreeMethods()
        self.cndl_separatinglines = SeparatingLines()
        self.cndl_shootingstar = ShootingStar()
        self.cndl_shortlinecandle = ShortLineCandle()
        self.cndl_spinningtop = SpinningTop()
        self.cndl_stalledpattern = StalledPattern()
        self.cndl_sticksandwich = StickSandwich()
        self.cndl_takuri = Takuri()
        self.cndl_tasukigap = TasukiGap()
        self.cndl_threeblackcrows = ThreeBlackCrows()
        self.cndl_threeinside = ThreeInside()
        self.cndl_threelinest = ThreeLineStrike()
        self.cndl_threeoutside = ThreeOutside()
        self.cndl_threestarsinsouth = ThreeStarsInSouth()
        self.cndl_threewhitesoldiers = ThreeWhiteSoldiers()
        self.cndl_thrusting = Thrusting()
        self.cndl_tristar = Tristar()
        self.cndl_twocrows = TwoCrows()
        self.cndl_uniquethreeriver = UniqueThreeRiver()
        self.cndl_updowngapthreemethods = UpDownGapThreeMethods()
        self.cndl_upsidegaptwocrows = UpsideGapTwoCrows()
        self.candleWin = SmartRollingWindow("float", 5)
        self.candleavgWin = SmartRollingWindow("float", 5)
        self.candleContainer = RollingWindow[float](2)
        self.cndl_uptrend = False
        self.cndl_downtrend = False
        self.candlescore = 0.00
        
        self.indicators = [self.roc, self.roc_fast, self.roc_med, self.roc_long, self.vwap_fst, self.vwap_med, self.vwap_lng, self.vwap_mlng,
                            self.vol_roc, self.vol_roc_fast, self.vol_roc_med, self.vol_roc_long, 
                            self.vwap, self.fast, self.stochasticFast, self.slow, self.stochasticSlow, 
                            self.macd, self.stochasticMACD, self.rsi, self.stochasticRSI, self.williamsPR, self.williamsPR_slow, self.atr, 
                            self.stochasticTrix, self.trix, self.trix_slow, self.kalFilter, self.kalFilterOpen, self.kalFilterLow, self.kalFilterHigh]
        self.candles = [self.cndl_abandonedbaby, self.cndl_advanceblock, self.cndl_belthold, self.cndl_breakway, self.cndl_closingmarubozu, 
                            self.cndl_concealedbabyswallow, self.cndl_counterattack, self.cndl_darkcloudcover, self.cndl_doji, self.cndl_dojistar, 
                            self.cndl_dragonflydoji, self.cndl_engulfing, self.cndl_eveningdojistar, self.cndl_eveningstar, self.cndl_gapsidebysidewhite,
                            self.cndl_gravestonedoji, self.cndl_hammer, self.cndl_hangingman, self.cndl_harami, self.cndl_haramicross, 
                            self.cndl_highwavecandle, self.cndl_hikkake, self.cndl_hikkakemodified, self.cndl_homingpigeon, self.cndl_identicalthreecrows,
                            self.cndl_inneck, self.cndl_invertedhammer, self.cndl_kicking, self.cndl_kickingbylength, self.cndl_ladderbottom, 
                            self.cndl_longleggeddoji, self.cndl_longlinecandle, self.cndl_marubozu, self.cndl_mathold, self.cndl_matchinglow, 
                            self.cndl_morningdojistar, self.cndl_morningstar, self.cndl_onneck, self.cndl_pierce, self.cndl_rickshawman, 
                            self.cndl_risefallthreemethods, self.cndl_separatinglines, self.cndl_shootingstar, self.cndl_shortlinecandle, 
                            self.cndl_spinningtop, self.cndl_stalledpattern, self.cndl_sticksandwich, self.cndl_takuri, self.cndl_tasukigap, 
                            self.cndl_threeblackcrows, self.cndl_threeinside, self.cndl_threelinest, self.cndl_threeoutside, self.cndl_threestarsinsouth, 
                            self.cndl_threewhitesoldiers, self.cndl_thrusting, self.cndl_tristar, self.cndl_twocrows, self.cndl_uniquethreeriver, 
                            self.cndl_updowngapthreemethods, self.cndl_upsidegaptwocrows]
        self.indicators = self.indicators + self.candles
        for indicator in self.indicators:
            consolidator = algorithm.ResolveConsolidator(symbol, res)
            algorithm.RegisterIndicator(symbol, indicator, consolidator)
        
        # Warm up
        history = algorithm.History(symbol, int(126*tf_coef), res)
        
        if history.empty or 'close' not in history.columns:
            return
        
        for tuple in history.loc[symbol].itertuples():
            tradeBar = TradeBar(tuple.open, tuple.high, tuple.low, tuple.close, tuple.volume)
            median_price = round(float(statistics.median([tuple.open, tuple.high, tuple.low, tuple.close])), 4)
            tradeBar = TradeBar(self.kalFilterOpen.Value, self.kalFilterHigh.Value, self.kalFilterLow.Value, self.kalFilter.Value, tuple.volume)
            
            self.vwap_fst.Update(tradeBar)
            self.vwap_med.Update(tradeBar)
            self.vwap_mlng.Update(tradeBar)
            self.vwap_lng.Update(tradeBar)
            self.roc.Update(tuple.Index, median_price)
            self.roc_fast.Update(tuple.Index, median_price)
            self.roc_med.Update(tuple.Index, median_price)
            self.roc_long.Update(tuple.Index, median_price)
            self.rocSignal_quick.Update(tuple.Index, median_price)
            self.rocSignal_fast.Update(tuple.Index, median_price)
            self.rocSignal_med.Update(tuple.Index, median_price)
            self.rocSignal_long.Update(tuple.Index, median_price)
            self.vol_roc.Update(tuple.Index, tuple.volume)
            self.vol_roc_fast.Update(tuple.Index, tuple.volume)
            self.vol_roc_med.Update(tuple.Index, tuple.volume)
            self.vol_roc_long.Update(tuple.Index, tuple.volume)
            self.volSignal_quick.Update(tuple.Index, tuple.volume)
            self.volSignal_fast.Update(tuple.Index, tuple.volume)
            self.volSignal_med.Update(tuple.Index, tuple.volume)
            self.volSignal_long.Update(tuple.Index, tuple.volume)
            self.atr.Update(tradeBar)
            self.macd.Update(tuple.Index, median_price)
            self.fast.Update(tuple.Index, median_price)
            self.slow.Update(tuple.Index, median_price)
            self.williamsPR.Update(tradeBar)
            self.williamsPR_slow.Update(tradeBar)
            self.rsi.Update(tuple.Index, median_price)
            self.trix.Update(tuple.Index, median_price)
            self.trix_slow.Update(tuple.Index, median_price)
            # candles
            self.cndl_abandonedbaby.Update(tradeBar)
            self.cndl_advanceblock.Update(tradeBar)
            self.cndl_belthold.Update(tradeBar)
            self.cndl_breakway.Update(tradeBar)
            self.cndl_closingmarubozu.Update(tradeBar)
            self.cndl_concealedbabyswallow.Update(tradeBar)
            self.cndl_counterattack.Update(tradeBar)
            self.cndl_darkcloudcover.Update(tradeBar)
            self.cndl_doji.Update(tradeBar)
            self.cndl_dojistar.Update(tradeBar)
            self.cndl_dragonflydoji.Update(tradeBar)
            self.cndl_engulfing.Update(tradeBar)
            self.cndl_eveningdojistar.Update(tradeBar)
            self.cndl_eveningstar.Update(tradeBar)
            self.cndl_gapsidebysidewhite.Update(tradeBar)
            self.cndl_gravestonedoji.Update(tradeBar)
            self.cndl_hammer.Update(tradeBar)
            self.cndl_hangingman.Update(tradeBar)
            self.cndl_harami.Update(tradeBar)
            self.cndl_haramicross.Update(tradeBar)
            self.cndl_highwavecandle.Update(tradeBar)
            self.cndl_hikkake.Update(tradeBar)
            self.cndl_hikkakemodified.Update(tradeBar)
            self.cndl_homingpigeon.Update(tradeBar)
            self.cndl_identicalthreecrows.Update(tradeBar)
            self.cndl_inneck.Update(tradeBar)
            self.cndl_invertedhammer.Update(tradeBar)
            self.cndl_kicking.Update(tradeBar)
            self.cndl_kickingbylength.Update(tradeBar)
            self.cndl_ladderbottom.Update(tradeBar)
            self.cndl_longleggeddoji.Update(tradeBar)
            self.cndl_longlinecandle.Update(tradeBar)
            self.cndl_marubozu.Update(tradeBar)
            self.cndl_mathold.Update(tradeBar)
            self.cndl_matchinglow.Update(tradeBar)
            self.cndl_morningdojistar.Update(tradeBar)
            self.cndl_morningstar.Update(tradeBar)
            self.cndl_onneck.Update(tradeBar)
            self.cndl_pierce.Update(tradeBar)
            self.cndl_rickshawman.Update(tradeBar)
            self.cndl_risefallthreemethods.Update(tradeBar)
            self.cndl_separatinglines.Update(tradeBar)
            self.cndl_shootingstar.Update(tradeBar)
            self.cndl_shortlinecandle.Update(tradeBar)
            self.cndl_spinningtop.Update(tradeBar)
            self.cndl_stalledpattern.Update(tradeBar)
            self.cndl_sticksandwich.Update(tradeBar)
            self.cndl_takuri.Update(tradeBar)
            self.cndl_tasukigap.Update(tradeBar)
            self.cndl_threeblackcrows.Update(tradeBar)
            self.cndl_threeinside.Update(tradeBar)
            self.cndl_threelinest.Update(tradeBar)
            self.cndl_threeoutside.Update(tradeBar)
            self.cndl_threestarsinsouth.Update(tradeBar)
            self.cndl_threewhitesoldiers.Update(tradeBar)
            self.cndl_thrusting.Update(tradeBar)
            self.cndl_tristar.Update(tradeBar)
            self.cndl_twocrows.Update(tradeBar)
            self.cndl_uniquethreeriver.Update(tradeBar)
            self.cndl_updowngapthreemethods.Update(tradeBar)
            self.cndl_upsidegaptwocrows.Update(tradeBar)


            # Support Resistance
            def CloseTo(x, y, delta):
                return abs(x-y) < delta

            def Next_RS(self, algorithm, symbol):
                s, r = [], []
                price = algorithm.Securities[symbol].Price
                # Rounded Price to $1
                s.append(round(price))
                r.append(math.ceil(price))

                # Rounded Price to $10
                s.append(int(math.floor(price / 10.0)) * 10)
                # Rounded Price Up to $10
                r.append(int(math.ceil(price / 10.0)) * 10)

                # Yesterday's OHLC Price
                s.append( self.Daily[0].Close)
                s.append( self.Daily[0].Low )
                r.append( self.Daily[0].Close)
                r.append( self.Daily[0].High)

                # Append 7 day Low:
                s.append( min([bar.Low for bar in self.Daily]) )
                r.append( max([bar.High for bar in self.Daily]) )

                s = sorted(s, reverse=True)
                r = sorted(r)
                return s[0], r[0]

            self.Daily.Add(tradeBar)
            if self.Daily.IsReady:
                support, resistance = Next_RS(self, algorithm, symbol)

                price = algorithm.Securities[symbol].Price
                mean = self.slow.Current.Value

                if CloseTo(price, support, 0.10) and mean > support*self.tol:
                    self.long = True
                if CloseTo(price, resistance, 0.10) and mean < resistance*self.tol:
                    self.short = True

            def roc_calc(obj):
                obj_lst = list(obj)
                output_lst = list()
                for i in range(-1, -len(obj_lst)+1, -1):
                    if obj_lst[i-1] != 0:
                        val = round((obj_lst[i] - obj_lst[i-1])/obj_lst[i-1], 4)
                    else:
                        val = 0
                    output_lst.append(val)
                return output_lst

            self.roc_Win.Add(round(float(statistics.median([self.roc.Current.Value, self.roc_fast.Current.Value, self.roc_med.Current.Value, self.roc_long.Current.Value])), 4))
            if self.roc_Win.IsReady:
                roc_lst = list(self.roc_Win)
                roc_sum, roc_len = sum(roc_lst), len(roc_lst)
                self.roc_slope = round(float(roc_sum)/roc_len, 4)
                vol_sum, vol_len = sum(list(self.roc_Win)[-3:-1]), len(list(self.roc_Win)[-3:-1])
                self.roc_fast_slope = round(float(vol_sum)/vol_len, 4)

                self.quick_up = all(x > 0.0 for x in [self.roc_fast_slope, self.volSignal_quick.Current.Value])
                self.quick_down = all(x < 0.0 for x in [self.roc_fast_slope, self.volSignal_quick.Current.Value])

            def calc_divergence(obj):
                x = np.array(list(obj))
                local_maxima = argrelextrema(x, np.greater)[0]
                local_minima = argrelextrema(x, np.less)[0]
                if x[-1] > x[-2]:
                    x = np.append(x, len(x) - 1)
                elif x[-1] > x[-2]:
                    x = np.append(x, len(x) - 1)
                hh_all = all(x[local_maxima][i] < x[local_maxima][i+1] for i in range(len(local_maxima)-1))
                ll_all = all(x[local_minima][i] > x[local_minima][i+1] for i in range(len(local_minima)-1))
                return hh_all, ll_all

            self.rocSum_Win.Add(round(float(sum([self.roc.Current.Value, self.roc_fast.Current.Value, self.roc_med.Current.Value, self.roc_long.Current.Value])), 4))
            self.roclen_Win.Add(round(float(statistics.median([self.roc.Current.Value, self.roc_fast.Current.Value, self.roc_med.Current.Value, self.roc_long.Current.Value])), 4))
            if self.rocSum_Win.IsReady:
                self.hh_all, self.ll_all = calc_divergence(self.roclen_Win)
                self.hh_sum, self.ll_sum = calc_divergence(self.rocSum_Win)

            self.vol_Win.Add(round(float(statistics.median([self.vol_roc.Current.Value, self.vol_roc_fast.Current.Value, self.vol_roc_med.Current.Value, self.vol_roc_long.Current.Value])), 4))
            if self.vol_Win.IsReady:
                vol_lst = list(self.vol_Win)
                vol_sum, vol_len = sum(vol_lst), len(vol_lst)
                self.vol_slope = round(float(vol_sum)/vol_len, 4)
                vol_sum, vol_len = sum(list(self.vol_Win)[-3:-1]), len(list(self.vol_Win)[-3:-1])
                self.vol_fast_slope = round(float(vol_sum)/vol_len, 4)

                self.vol_hh, self.vol_ll = calc_divergence(self.vol_Win)

            self.roc_prices_Win.Add(round(statistics.median([self.roc.Current.Value, self.roc_fast.Current.Value, self.roc_med.Current.Value, self.roc_long.Current.Value]), 4))
            self.roc_prices_lev_Win.Add(round(statistics.median([self.roc.Current.Value, self.roc_fast.Current.Value, self.roc_med.Current.Value]), 4))
            self.roc_volume_Win.Add(round(statistics.median([self.vol_roc.Current.Value, self.vol_roc_fast.Current.Value, self.vol_roc_med.Current.Value, self.vol_roc_long.Current.Value]), 4))
            self.prices_Win.Add(tuple.close)
            if self.prices_Win.IsReady:
                prices = list(self.roc_prices_Win)
                volumes = list(self.roc_volume_Win)
                frames = [i for i in range(-2, -21, -2)]
                frames_ = [i for i in range(-1, -21, -1)]
                prices_lev = list(self.roc_prices_lev_Win)
                _frames = [i for i in range(-1, -3, -1)]

                v1 = round(statistics.median([round(float(prices[i] - prices[i-5]/ prices[i-5]), 4) if prices[i-5] != 0 else 0 for i in frames]), 4)
                v11 = round(statistics.median([round(float(prices[i] - prices[i-5]/ prices[i-5]), 4) if prices[i-5] != 0 else 0 for i in frames_]), 4)
                v1_mom = round(statistics.median([round(float(prices_lev[i] - prices_lev[i-1]/ abs(abs(i)+(i-1))), 4) if abs(abs(i)+(i-1)) != 0 else 0 for i in _frames]), 4)

                self.median_roc = v1 if (v1 > v11) else -1
                self.median_roc_momentum = v1_mom
                self.median_vol = round(statistics.median([round(float(volumes[i] - volumes[i-5]/ volumes[i-5]), 4) if volumes[i-5] != 0 else 0 for i in frames]), 4)

                self.median_roc_hh, self.median_roc_ll = calc_divergence(roc_calc(prices))
                self.median_vol_hh, self.median_vol_ll = calc_divergence(roc_calc(volumes))

                C = list(self.prices_Win)
                avg = sum(list(self.prices_Win))/len(list(self.prices_Win))
                self.volatility = float(np.sqrt(252)*reduce(lambda a,b:a+abs(avg-b),C,0)/len(C))/C[-1]
                self.hh, self.ll = calc_divergence(self.prices_Win)

            rocSignal_lst = [self.rocSignal_quick.Current.Value, self.rocSignal_fast.Current.Value, self.rocSignal_med.Current.Value, self.rocSignal_long.Current.Value]
            volSignal_lst = [self.volSignal_quick.Current.Value, self.volSignal_fast.Current.Value, self.volSignal_med.Current.Value, self.volSignal_long.Current.Value]
            rocsignal_up = all(x > 0.0 for x in rocSignal_lst)
            rocsignal_down = all(x < 0.0 for x in rocSignal_lst)
            volsignal = all(x > 0.0 for x in volSignal_lst)
            self.roc_vol_signal_up = rocsignal_up and volsignal
            self.roc_vol_signal_down = rocsignal_down and volsignal

            # Stochastic RSI
            if self.rsi.IsReady:
                rsi = self.rsi.Current.Value
                trade_bar = TradeBar(tuple.Index, rsi, rsi, rsi, rsi, 0)
                self.stochasticRSI.Update(trade_bar)
                if self.stochasticRSI.IsReady:
                    self.rsi_Win.Add(rsi)
                    self.rsiWin.Add(rsi)
                    self.rsiFastStoch_Win.Add(self.stochasticRSI.FastStoch.Current.Value)
                    self.rsiStochFastWin.Add(self.stochasticRSI.FastStoch.Current.Value)
                    self.rsiStochK_Win.Add(self.stochasticRSI.StochK.Current.Value)
                    self.rsiStochKWin.Add(self.stochasticRSI.StochK.Current.Value)
                    self.rsiStochD_Win.Add(self.stochasticRSI.StochD.Current.Value)
                    self.rsiStochDWin.Add(self.stochasticRSI.StochD.Current.Value)
                    if self.rsi_Win.IsReady:
                        rsi_lst = list(self.rsi_Win)
                        rsifast_lst = list(self.rsiFastStoch_Win)
                        rsistochk_lst = list(self.rsiStochK_Win)
                        rsistochd_lst = list(self.rsiStochD_Win)
                        cur_rsi, pre_rsi, lst_rsi = rsi_lst[-1], rsi_lst[-2], rsi_lst[-3]
                        cur_fast, pre_fast, lst_fast = rsifast_lst[-1], rsifast_lst[-2], rsifast_lst[-3]
                        cur_stochk, pre_stochk, lst_stochk = rsistochk_lst[-1], rsistochk_lst[-2], rsistochk_lst[-3]
                        cur_stochd, pre_stochd, lst_stochd = rsistochd_lst[-1], rsistochd_lst[-2], rsistochd_lst[-3]
                        cond1 = (cur_fast >= cur_stochk*self.tol) and (cur_fast >= cur_stochd*self.tol) and (cur_stochk >= cur_stochd*self.tol)
                        cond2 = (cur_rsi >= pre_rsi*self.tol) and (cur_fast >= pre_fast*self.tol) and (cur_stochk >= pre_stochk*self.tol) and (cur_stochd >= pre_stochd*self.tol)
                        cond3 = (pre_rsi >= lst_rsi*self.tol) and (pre_fast >= lst_fast*self.tol) and (pre_stochk >= lst_stochk*self.tol) and (pre_stochd >= lst_stochd*self.tol)
                        cond4 = (cur_fast <= cur_stochk*self.tol) and (cur_fast <= cur_stochd*self.tol) and (cur_stochk <= cur_stochd*self.tol)
                        cond5 = (cur_rsi <= pre_rsi*self.tol) and (cur_fast <= pre_fast*self.tol) and (cur_stochk <= pre_stochk*self.tol) and (cur_stochd <= pre_stochd*self.tol)
                        cond6 = (pre_rsi <= lst_rsi*self.tol) and (pre_fast <= lst_fast*self.tol) and (pre_stochk <= lst_stochk*self.tol) and (pre_stochd <= lst_stochd*self.tol)

                        hh_rsi, ll_rsi = calc_divergence(rsi_lst)
                        hh_rsifast, ll_rsifast = calc_divergence(rsifast_lst)
                        hh_rsik, ll_rsik = calc_divergence(rsistochk_lst)
                        hh_rsid, ll_rsid = calc_divergence(rsistochd_lst)
                        cond7 = hh_rsi, hh_rsifast, hh_rsik, hh_rsid
                        cond8 = ll_rsi, ll_rsifast, ll_rsik, ll_rsid
                        if (cond1 and cond7) and (cond2 and cond3):
                            self.rsi_uptrend = True
                        if (cond4 and cond8) and (cond5 and cond6):
                            self.rsi_downtrend = True

                        exit = self.rsiStochFastWin.crossedBelow(self.rsiWin) and self.rsiStochFastWin.crossedBelow(self.rsiStochKWin) and self.rsiStochKWin.crossedBelow(self.rsiStochDWin)
                        entry = self.rsiStochFastWin.crossedAbove(self.rsiWin) and self.rsiStochFastWin.crossedAbove(self.rsiStochKWin) and self.rsiStochKWin.crossedAbove(self.rsiStochDWin)
                        if algorithm.Portfolio[symbol].Invested:
                            if exit and self.short:
                                self.rsi_exit_signal = True
                            else:
                                self.rsi_exit_signal = False
                        if not algorithm.Portfolio[symbol].Invested:
                            if entry and self.long:
                                self.rsi_entry_signal = True
                            else:
                                self.rsi_entry_signal = False

            # MACD Trend
            if self.macd.IsReady:
                macd = self.macd.Current.Value
                trade_bar = TradeBar(tuple.Index, macd, macd, macd, macd, 0)
                self.stochasticMACD.Update(trade_bar)
                if self.stochasticMACD.IsReady:
                    macd = self.macd.Current.Value
                    macd_fast = self.macd.Fast.Current.Value
                    macd_slow = self.macd.Slow.Current.Value
                    macd_hist = self.macd.Histogram.Current.Value
                    signal = self.macd.Signal.Current.Value
                    delta = (macd - signal)/macd_fast
                    macd_stochfast = self.stochasticMACD.FastStoch.Current.Value
                    macd_stochk = self.stochasticMACD.StochK.Current.Value
                    macd_stochd = self.stochasticMACD.StochD.Current.Value
                    self.macd_Win.Add(macd)
                    self.macdFast_Win.Add(macd_fast)
                    self.macdSlow_Win.Add(macd_slow)
                    self.macdHist_Win.Add(macd_hist)
                    self.macdSignal_Win.Add(signal)
                    self.macdDelta_Win.Add(delta)
                    self.macd_stochfast_Win.Add(macd_stochfast)
                    self.macdStochFastWin.Add(macd_stochfast)
                    self.macd_stochk_Win.Add(macd_stochk)
                    self.macdStochKWin.Add(macd_stochk)
                    self.macd_stochd_Win.Add(macd_stochd)
                    self.macdStochDWin.Add(macd_stochd)
                    if self.macd_Win.IsReady:
                        macd_lst = list(self.macd_Win)
                        macdhist_lst = list(self.macdHist_Win)
                        macdfast_lst = list(self.macdFast_Win)
                        macdslow_lst = list(self.macdSlow_Win)
                        macdsignal_lst = list(self.macdSignal_Win)
                        macddelta_lst = list(self.macdDelta_Win)
                        macdstochfast_lst = list(self.macd_stochfast_Win)
                        macdstochk_lst = list(self.macd_stochk_Win)
                        macdstochd_lst = list(self.macd_stochd_Win)
                        cur_macd, pre_macd, lst_macd = macd_lst[-1], macd_lst[-2], macd_lst[-3]
                        cur_macd_fast, pre_macd_fast, lst_macd_fast = macdfast_lst[-1], macdfast_lst[-2], macdfast_lst[-3]
                        cur_macd_slow, pre_macd_slow, lst_macd_slow = macdslow_lst[-1], macdslow_lst[-2], macdslow_lst[-3]
                        cur_macd_hist, pre_macd_hist, lst_macd_hist = macdhist_lst[-1], macdhist_lst[-2], macdhist_lst[-3]
                        cur_signal, pre_signal, lst_signal = macdsignal_lst[-1], macdsignal_lst[-2], macdsignal_lst[-3]
                        cur_delta, pre_delta, lst_delta = macddelta_lst[-1], macddelta_lst[-2], macddelta_lst[-3]
                        cur_macdstochfast, pre_macdstochfast, lst_macdstochfast = macdstochfast_lst[-1], macdstochfast_lst[-2], macdstochfast_lst[-3]
                        cur_macdstochk, pre_macdstochk, lst_macdstochk = macdstochk_lst[-1], macdstochk_lst[-2], macdstochk_lst[-3]
                        cur_macdstochd, pre_macdstochd, lst_macdstochd = macdstochd_lst[-1], macdstochd_lst[-2], macdstochd_lst[-3]

                        cond1 = ((cur_macd_hist-cur_delta)>=0.0025) and (cur_macd >= cur_signal*self.tol) and (cur_macdstochfast >= cur_macdstochk*self.tol) and (cur_macdstochk >= cur_macdstochd*self.tol)
                        cond2 = (cur_macd >= pre_macd*self.tol) and (cur_macd_fast >= pre_macd_fast*self.tol) and (cur_macd_hist >= pre_macd_hist*self.tol) and (cur_signal >= pre_signal*self.tol)
                        cond3 = (pre_macd >= lst_macd*self.tol) and (pre_macd_fast >= lst_macd_fast*self.tol) and (pre_macd_hist >= lst_macd_hist*self.tol) and (pre_signal >= lst_signal*self.tol)
                        cond4 = ((cur_macd_hist-cur_delta) <= -0.0025) and (cur_macd <= cur_signal*self.tol) and (cur_macdstochfast <= cur_macdstochk*self.tol) and (cur_macdstochk <= cur_macdstochd*self.tol)
                        cond5 = (cur_macd <= pre_macd*self.tol) and (cur_macd_fast <= pre_macd_fast*self.tol) and (cur_macd_hist <= pre_macd_hist*self.tol) and (cur_signal <= pre_signal*self.tol)
                        cond6 = (pre_macd <= lst_macd*self.tol) and (pre_macd_fast <= lst_macd_fast*self.tol) and (pre_macd_hist <= lst_macd_hist*self.tol) and (pre_signal <= lst_signal*self.tol)

                        hh_macd, ll_macd = calc_divergence(macd_lst)
                        hh_macdhist, ll_macdhist = calc_divergence(macdhist_lst)
                        hh_macdfast, ll_macdfast = calc_divergence(macdfast_lst)
                        hh_macdslow, ll_macdslow = calc_divergence(macdslow_lst)
                        hh_macdsignal, ll_macdsignal = calc_divergence(macdsignal_lst)
                        hh_macdstochfast, ll_macdstochfast = calc_divergence(macdstochfast_lst)
                        hh_macdstochk, ll_macdstochk = calc_divergence(macdstochk_lst)
                        hh_macdstochd, ll_macdstochd = calc_divergence(macdstochd_lst)
                        cond7 = hh_macd, hh_macdhist, hh_macdfast, hh_macdslow, hh_macdsignal, hh_macdstochfast, hh_macdstochk, hh_macdstochd
                        cond8 = ll_macd, ll_macdhist, ll_macdfast, ll_macdslow, ll_macdsignal, ll_macdstochfast, ll_macdstochk, ll_macdstochd
                        if (cond1 and cond7) and (cond2 and cond3):
                            self.macd_uptrend = True
                        if (cond4 and cond8) and (cond5 and cond6):
                            self.macd_downtrend = True

                        exit = self.macdStochFastWin.crossedBelow(self.macdStochKWin) and self.macdStochKWin.crossedBelow(self.macdStochDWin)
                        entry = self.macdStochFastWin.crossedAbove(self.macdStochKWin) and self.macdStochKWin.crossedAbove(self.macdStochDWin)
                        if algorithm.Portfolio[symbol].Invested:
                            if exit and self.short:
                                self.macd_exit_signal = True
                            else:
                                self.macd_exit_signal = False
                        if not algorithm.Portfolio[symbol].Invested:
                            if entry and self.long:
                                self.macd_entry_signal = True
                            else:
                                self.macd_entry_signal = False

            self.williamsWin.Add(round(statistics.median([self.williamsPR.Current.Value, self.williamsPR_slow.Current.Value]), 4))
            if self.williamsWin.IsReady:
                williams = list(self.williamsWin)
                w_length = len(williams)
                frames = [i for i in range(-1, (w_length*-1)+1, -1)]
                self.williams_median_roc = round(statistics.median([round(float(williams[i] - williams[i-1]/ williams[i-1]), 4) if williams[i-1] != 0 else 0 for i in frames]), 4)
                self.williams_median = round(statistics.median(williams), 4)

            if self.williamsPR.IsReady and self.williamsPR_slow.IsReady:
                self.williamsPR_Win.Add(self.williamsPR.Current.Value)
                self.williamsPRWin.Add(self.williamsPR.Current.Value)
                self.williamsPR_slow_Win.Add(self.williamsPR_slow.Current.Value)
                self.williamsPR_slowWin.Add(self.williamsPR_slow.Current.Value)

                if self.williamsPR_Win.IsReady:
                    wills_lst = list(self.williamsPR_Win)
                    willss_lst = list(self.williamsPR_slow_Win)
                    cur_wills, pre_wills, lst_wills = wills_lst[-1], wills_lst[-2], wills_lst[-3]
                    cur_willss, pre_willss, lst_willss = willss_lst[-1], willss_lst[-2], willss_lst[-3]
                    cond1 = (cur_wills >= cur_willss*self.tol)
                    cond2 = (cur_wills >= pre_wills*self.tol) and (cur_willss >= pre_willss*self.tol)
                    cond3 = (pre_wills >= lst_wills*self.tol) and (pre_willss >= lst_willss*self.tol)
                    cond4 = (cur_wills <= cur_willss*self.tol)
                    cond5 = (cur_wills <= pre_wills*self.tol) and (cur_willss <= pre_willss*self.tol)
                    cond6 = (pre_wills <= lst_wills*self.tol) and (pre_willss <= lst_willss*self.tol)

                    hh_wills, ll_wills = calc_divergence(wills_lst)
                    hh_willss, ll_willss = calc_divergence(willss_lst)
                    cond7 = hh_wills, hh_willss
                    cond8 = ll_wills, ll_willss
                    if (cond1 and cond7) and (cond2 and cond3):
                        self.williams_uptrend = True
                    if (cond4 and cond8) and (cond5 and cond6):
                        self.williams_downtrend = True

                    exit = self.williamsPRWin.crossedBelow(self.williamsPR_slowWin)
                    entry = self.williamsPRWin.crossedAbove(self.williamsPR_slowWin)
                    if algorithm.Portfolio[symbol].Invested:
                        if exit and self.short:
                            self.williams_exit_signal = True
                        else:
                            self.williams_exit_signal = False
                    if not algorithm.Portfolio[symbol].Invested:
                        if entry and self.long:
                            self.williams_entry_signal = True
                        else:
                            self.williams_entry_signal = False

            self.high_Win.Add(tuple.high)
            self.low_Win.Add(tuple.low)
            if self.high_Win.IsReady:
                close = list(self.prices_Win)
                todayvol = np.std(close[1:31])
                yesterdayvol = np.std(close[0:30])
                deltavol = (todayvol - yesterdayvol) / todayvol
                self.lookback = round(self.lookback * (1 + deltavol))

                # Account for upper/lower limit of lockback length
                if self.lookback > self.ceiling:
                    self.lookback = self.ceiling
                elif self.lookback < self.floor:
                    self.lookback = self.floor

                high = list(self.high_Win)
                low = list(self.low_Win)

                # Buy in case of breakout
                entry = (algorithm.Securities[symbol].Price >= max(high[:-1]))
                if not algorithm.Portfolio[symbol].Invested:
                    if entry and self.long:
                        self.breakout = True
                exit = (algorithm.Securities[symbol].Price <= min(low[:-1]))
                if algorithm.Portfolio[symbol].Invested:
                    if exit and self.short:
                        self.breakdown = True

            fast = self.fast.Current.Value
            self.fastVwapWin.Add(fast)
            slow = self.slow.Current.Value
            self.slowVwapWin.Add(slow)
            fastBar = TradeBar(tuple.Index, fast, fast, fast, fast, 0)
            self.stochasticFast.Update(fastBar)
            slowBar = TradeBar(tuple.Index, slow, slow, slow, slow, 0)
            self.stochasticSlow.Update(slowBar)
            if self.stochasticFast.IsReady and self.stochasticSlow.IsReady:
                fast_stoch = self.stochasticFast.FastStoch.Current.Value
                fast_stochk = self.stochasticFast.StochK.Current.Value
                fast_stochd = self.stochasticFast.StochD.Current.Value
                slow_stoch = self.stochasticSlow.FastStoch.Current.Value
                slow_stochk = self.stochasticSlow.StochK.Current.Value
                slow_stochd = self.stochasticSlow.StochD.Current.Value
                fast_cond = ((fast >= fast_stochk*self.tol) and (fast_stoch >= fast_stochk*self.tol) and (fast_stochk >= fast_stochd*self.tol))
                slow_cond = ((slow >= slow_stochk*self.tol) and (slow_stoch >= slow_stochk*self.tol) and (slow_stochk >= slow_stochd*self.tol))
                self.is_uptrend = ((fast) >= slow*self.tol) and (tuple.close >= slow*self.tol) and fast_cond and slow_cond
                fast_cond = ((fast <= fast_stochk*self.tol) and (fast_stoch <= fast_stochk*self.tol) and (fast_stochk <= fast_stochd*self.tol))
                slow_cond = ((slow <= slow_stochk*self.tol) and (slow_stoch <= slow_stochk*self.tol) and (slow_stochk <= slow_stochd*self.tol))
                self.is_downtrend = ((fast) <= slow*self.tol) and (tuple.close <= slow*self.tol) and fast_cond and slow_cond

                exit = self.fastVwapWin.crossedBelow(self.slowVwapWin)
                entry = self.fastVwapWin.crossedAbove(self.slowVwapWin)
                if algorithm.Portfolio[symbol].Invested:
                    if exit and self.short:
                        self.exit_signal = True
                    else:
                        self.exit_signal = False
                if not algorithm.Portfolio[symbol].Invested:
                    if entry and self.long:
                        self.entry_signal = True
                    else:
                        self.entry_signal = False

            if self.is_uptrend:
                # triangle formula
                # base * height * 0.5
                self.scale = round(float(fast - slow) / ((fast+slow)/2.0), 4) if (fast+slow) != 0.0 else 0
            else:
                self.scale = 0.00

            if self.trix.IsReady:
                trix = self.trix.Current.Value
                trixBar = TradeBar(tuple.Index, trix, trix, trix, trix, 0)
                self.stochasticTrix.Update(trixBar)
                if self.stochasticTrix.IsReady:
                    self.trixFastStoch_Win.Add(self.stochasticTrix.FastStoch.Current.Value)
                    self.trixStochFastWin.Add(self.stochasticTrix.FastStoch.Current.Value)
                    self.trixStochK_Win.Add(self.stochasticTrix.StochK.Current.Value)
                    self.trixStochKWin.Add(self.stochasticTrix.StochK.Current.Value)
                    self.trixStochD_Win.Add(self.stochasticTrix.StochD.Current.Value)
                    self.trixStochDWin.Add(self.stochasticTrix.StochD.Current.Value)
                    if self.trixFastStoch_Win.IsReady:
                        trix_lst = list(self.trixFastStoch_Win)
                        trixfast_lst = list(self.trixFastStoch_Win)
                        trixstochk_lst = list(self.trixStochK_Win)
                        trixstochd_lst = list(self.trixStochD_Win)
                        cur_trix, pre_trix, lst_trix = trix_lst[-1], trix_lst[-2], trix_lst[-3]
                        cur_fast, pre_fast, lst_fast = trixfast_lst[-1], trixfast_lst[-2], trixfast_lst[-3]
                        cur_stochk, pre_stochk, lst_stochk = trixstochk_lst[-1], trixstochk_lst[-2], trixstochk_lst[-3]
                        cur_stochd, pre_stochd, lst_stochd = trixstochd_lst[-1], trixstochd_lst[-2], trixstochd_lst[-3]
                        cond1 = (cur_trix >= cur_stochk*self.tol) and (cur_fast >= cur_stochk*self.tol) and (cur_stochk >= cur_stochd*self.tol)
                        cond2 = (cur_trix >= pre_trix*self.tol) and (cur_fast >= pre_fast*self.tol) and (cur_stochk >= pre_stochk*self.tol) and (cur_stochd >= pre_stochd*self.tol)
                        cond3 = (pre_trix >= lst_trix*self.tol) and (pre_fast >= lst_fast*self.tol) and (pre_stochk >= lst_stochk*self.tol) and (pre_stochd >= lst_stochd*self.tol)
                        cond4 = (cur_trix <= cur_stochk*self.tol) and (cur_fast <= cur_stochk*self.tol) and (cur_stochk <= cur_stochd*self.tol)
                        cond5 = (cur_trix <= pre_trix*self.tol) and (cur_fast <= pre_fast*self.tol) and (cur_stochk <= pre_stochk*self.tol) and (cur_stochd <= pre_stochd*self.tol)
                        cond6 = (pre_trix <= lst_trix*self.tol) and (pre_fast <= lst_fast*self.tol) and (pre_stochk <= lst_stochk*self.tol) and (pre_stochd <= lst_stochd*self.tol)
                        
                        hh_trix, ll_trix = calc_divergence(trix_lst)
                        hh_trixfast, ll_trixfast = calc_divergence(trixfast_lst)
                        hh_trixk, ll_trixk = calc_divergence(trixstochk_lst)
                        hh_trixd, ll_trixd = calc_divergence(trixstochd_lst)
                        cond7 = hh_trix, hh_trixfast, hh_trixk, hh_trixd
                        cond8 = ll_trix, ll_trixfast, ll_trixk, ll_rsid
                        if (cond1 and cond7) and (cond2 and cond3):
                            self.trix_uptrend = True
                        if (cond4 and cond8) and (cond5 and cond6):
                            self.trix_downtrend = True
                            
                        exit = self.trixStochFastWin.crossedBelow(self.trixStochKWin) and self.trixStochKWin.crossedBelow(self.trixStochDWin)
                        entry = self.trixStochFastWin.crossedAbove(self.trixStochKWin) and self.trixStochKWin.crossedAbove(self.trixStochDWin)
                        if algorithm.Portfolio[symbol].Invested:
                            if exit and self.short:
                                self.trix_exit_signal = True
                            else:
                                self.trix_exit_signal = False
                        if not algorithm.Portfolio[symbol].Invested:
                            if entry and self.long:
                                self.trix_entry_signal = True
                            else:
                                self.trix_entry_signal = False
        
            self.kalWin.Add(self.kalFilter.Value)
            self.kalWindowLow.Add(self.kalFilterLow.Value)
            self.kalWindowHigh.Add(self.kalFilterHigh.Value)
            self.priceWin.Add(median_price)
            self.priceWindowLow.Add(tuple.low)
            self.priceWindowHigh.Add(tuple.high)
        
            if self.kalFilterLow.IsReady:
                exit = self.priceWindowLow.crossedBelow(self.kalWindowLow) and self.priceWindowHigh.crossedBelow(self.kalWindowHigh) and self.priceWin.crossedBelow(self.kalWin)
                entry = self.priceWindowLow.crossedAbove(self.kalWindowLow) and self.priceWindowHigh.crossedAbove(self.kalWindowHigh) and self.priceWin.crossedAbove(self.kalWin)
                if algorithm.Portfolio[symbol].Invested:
                    if exit and self.short:
                        self.kal_exit_signal = True
                    else:
                        self.kal_exit_signal = False
                if not algorithm.Portfolio[symbol].Invested:
                    if entry and self.long:
                        self.kal_entry_signal = True
                    else:
                        self.kal_entry_signal = False

        # VPN Indicator
        iATR = 0.1
        ema_smooth = 3
        vp = 0.0
        vn = 0.0
        vtot = 0.0
        dist = self.atr.Current.Value * iATR
        self.vpn_vol_Win.Add(tuple.volume)
        self.vpn_hlc_Win.Add(round(statistics.median([tuple.high, tuple.low, tuple.close]), 4))
        if self.vpn_vol_Win.IsReady and self.vpn_hlc_Win.IsReady:
            vpn_vol_Win = list(self.vpn_vol_Win)
            vpn_hlc_Win = list(self.vpn_hlc_Win)
            for i in range(-1, -self.vpn_period, -1):
                if (vpn_hlc_window[i] >= vpn_hlc_window[i-1] + dist):
                    vp += vpn_vol_window[i]
                elif (vpn_hlc_window[i] <= vpn_hlc_window[i-1] - dist):
                    vn += vpn_vol_window[i]
                vtot += vpn_vol_window[i]
                
            vpn_val = (((vp - vn) / (vtot/self.vpn_period)) / self.vpn_period) * 100
                
            self.vpn_lst.Add(vpn_val)

        if self.vpn_lst.IsReady:
            vpn_ema = pd.DataFrame(list(self.vpn_lst)).ewm(span=ema_smooth, adjust=False).mean().iloc[-1][0]
            vpn_scale = self.vpn_lst[-1]
            vpnIndicator = ((vpn_scale) >= (vpn_ema*self.tol)) and ((vpn_scale) >= (self.vpn_lst[-2]*self.tol))
            hh_vpn, ll_vpn = calc_divergence(self.vpn_lst)
            self.vpnIndicator = vpnIndicator and hh_vpn and (not ll_vpn)
        if self.vpnIndicator:
            cur_vpn, cur_vpn_ema = vpn_scale, vpn_ema
            low_vpn, low_vpn_ema = min(self.vpn_lst), min(pd.DataFrame(list(self.vpn_lst)).ewm(span=ema_smooth, adjust=False).mean().iloc[-1])
            vpnScale = round(float(cur_vpn - cur_vpn_ema) / ((low_vpn + low_vpn_ema) / 2.0), 4)
            self.vpnScale = vpnScale
        else:
            self.vpnScale = 0.00
            

        # Candles
        cndl_coef =  float(sum([x.Current.Value for x in self.candles]))
        
        self.candleContainer.Add(cndl_coef)
        self.candleWin.Add(cndl_coef)
        if self.candleContainer.IsReady and self.candleWin.IsReady:
            cndl_avg = statistics.median(list(self.candleContainer))
            self.candleavgWin.Add(cndl_avg)
        
            if self.candleWin.IsReady and self.candleavgWin.IsReady:
                exit = self.candleWin.crossedBelow(self.candleavgWin)
                entry = self.candleWin.crossedAbove(self.candleavgWin)
                if algorithm.Portfolio[symbol].Invested:
                    if exit and self.short:
                        self.cndl_downtrend = True
                if not algorithm.Portfolio[symbol].Invested:
                    if entry and self.long:
                        self.cndl_uptrend = True

        # Price is Favorable
        if (algorithm.Securities[symbol].BidPrice < self.VWAP):
            self.PriceIsFavorable = True
        elif (algorithm.Securities[symbol].AskPrice > self.VWAP):
            self.PriceIsFavorable = True
        else:
            self.PriceIsFavorable = False
        
        # Market Profile
        if self.vwap_lng.IsReady:
            self.market_profile.CalcMarketProfile(algorithm, 21, [symbol])
            a, val, vah, d = self.market_profile.GetMarketProfile(algorithm, symbol)
            poc = float(max(a, key=a.get))
            
            self.poc_Win.Add(poc)
            self.pocWin.Add(poc)
            self.val_Win.Add(val)
            self.valWin.Add(val)
            self.vah_Win.Add(vah)
            self.vahWin.Add(vah)
            self.price_Win.Add(algorithm.Securities[symbol].Price)
            self.priceWin.Add(algorithm.Securities[symbol].Price)
            self.medpri_Win.Add(median_price)
            self.medpriWin.Add(median_price)
            mp_fst = self.vwap_fst.Current.Value
            mp_med = self.vwap_med.Current.Value    
            mp_lng = self.vwap_lng.Current.Value
            self.vwap_fst_Win.Add(mp_fst)
            self.vwap_fstWin.Add(mp_fst)
            self.vwap_med_Win.Add(mp_med) 
            self.vwap_medWin.Add(mp_med)    
            self.vwap_lng_Win.Add(mp_lng)
            self.vwap_lngWin.Add(mp_lng)
            
            if self.poc_Win.IsReady:
                poc_lst = list(self.poc_Win)
                val_lst = list(self.val_Win)
                vah_lst = list(self.vah_Win)
                pri_lst = list(self.price_Win)
                medp_lst = list(self.medpri_Win)
                fst_lst = list(self.vwap_fst_Win)
                med_lst = list(self.vwap_med_Win)
                lng_lst = list(self.vwap_lng_Win)
                cur_poc, pre_poc, lst_poc = poc_lst[-1], poc_lst[-2], poc_lst[-3]
                cur_val, pre_val, lst_val = val_lst[-1], val_lst[-2], val_lst[-3]
                cur_vah, pre_vah, lst_vah = vah_lst[-1], vah_lst[-2], vah_lst[-3]
                cur_pri, pre_pri, lst_pri = pri_lst[-1], pri_lst[-2], pri_lst[-3]
                cur_medp, pre_medp, lst_medp = medp_lst[-1], medp_lst[-2], medp_lst[-3]
                cur_fst, pre_fst, lst_fst = fst_lst[-1], fst_lst[-2], fst_lst[-3]    
                cur_med, pre_med, lst_med = med_lst[-1], med_lst[-2], med_lst[-3]
                cur_lng, pre_lng, lst_lng = lng_lst[-1], lng_lst[-2], lng_lst[-3]
    
                cond1 = (cur_pri >= cur_poc*self.tol) and (cur_pri >= cur_medp*self.tol) and (cur_fst >= cur_med*self.tol) and (cur_med >= cur_lng*self.tol)    
                cond2 = (cur_pri >= pre_pri*self.tol) and (cur_medp >= pre_medp*self.tol) and (cur_poc >= pre_poc*self.tol) and (cur_val >= pre_val*self.tol) and (cur_vah >= pre_vah*self.tol) and (cur_fst >= pre_fst*self.tol) and (cur_med >= pre_med*self.tol) and (cur_lng >= pre_lng*self.tol)
                cond3 = (pre_pri >= lst_pri*self.tol) and (pre_medp >= lst_medp*self.tol) and (pre_poc >= lst_poc*self.tol) and (pre_val >= lst_val*self.tol) and (pre_vah >= lst_vah*self.tol) and (pre_fst >= lst_fst*self.tol) and (pre_med >= lst_med*self.tol) and (pre_lng >= lst_lng*self.tol)    
                cond4 = (cur_pri <= cur_poc*self.tol) and (cur_pri <= cur_medp*self.tol) and (cur_fst <= cur_med*self.tol) and (cur_med <= cur_lng*self.tol) 
                cond5 = (cur_pri <= pre_pri*self.tol) and (cur_medp <= pre_medp*self.tol) and (cur_poc <= pre_poc*self.tol) and (cur_val <= pre_val*self.tol) and (cur_vah <= pre_vah*self.tol) and (cur_fst <= pre_fst*self.tol) and (cur_med <= pre_med*self.tol) and (cur_lng <= pre_lng*self.tol)    
                cond6 = (pre_pri <= lst_pri*self.tol) and (pre_medp <= lst_medp*self.tol) and (pre_poc <= lst_poc*self.tol) and (pre_val <= lst_val*self.tol) and (pre_vah <= lst_vah*self.tol) and (pre_fst <= lst_fst*self.tol) and (pre_med <= lst_med*self.tol) and (pre_lng <= lst_lng*self.tol)     
                
                hh_poc, ll_poc = calc_divergence(poc_lst)
                hh_val, ll_val = calc_divergence(val_lst)
                hh_vah, ll_vah = calc_divergence(vah_lst)
                hh_pri, ll_pri = calc_divergence(pri_lst)
                hh_mpr, ll_mpr = calc_divergence(medp_lst)
                hh_fst, ll_fst = calc_divergence(fst_lng) 
                hh_med, ll_med = calc_divergence(med_lng)    
                hh_long, ll_long = calc_divergence(lng_lst)    
                cond7 = hh_poc, hh_val, hh_vah, hh_pri, hh_mpr, hh_fst, hh_med, hh_long
                cond8 = ll_poc, ll_val, ll_vah, ll_pri, hh_mpr, ll_fst, ll_med, ll_long    
                if (cond1 and cond7) and (cond2 and cond3):
                    self.mp_uptrend = True
                if (cond4 and cond8) and (cond5 and cond6):
                    self.mp_downtrend = True
                    
                exit = self.valWin.crossedBelow(self.vwap_medWin) and self.valWin.crossedBelow(self.vwap_mlngWin) and self.valWin.crossedBelow(self.medpriWin)
                entry = self.valWin.crossedAbove(self.vwap_medWin) and self.valWin.crossedAbove(self.vwap_mlngWin) and self.valWin.crossedAbove(self.medpriWin)
                if algorithm.Portfolio[symbol].Invested:
                    if exit and self.short:
                        self.mp_exit_signal = True
                    else:
                        self.mp_exit_signal = False
                if not algorithm.Portfolio[symbol].Invested:
                    if entry and self.long:
                        self.mp_entry_signal = True
                    else:
                        self.mp_entry_signal = False
                
                # if algorithm.Portfolio[symbol].Invested:
                #     if self.pocWin.crossedBelow(self.vwap_lngWin): #and self.medpriWin.crossedBelow(self.vwap_medWin) and self.valWin.crossedBelow(self.vwap_mlngWin) and self.valWin.crossedBelow(self.vwap_lngWin):
                #         self.dont_buy = True
                #     else:
                #         self.dont_buy = False
                # if not algorithm.Portfolio[symbol].Invested:
                #     if all(self.medpriWin[0] < x[0] for x in [self.vwap_lngWin]):
                #         self.dont_buy = True
                #     else:
                #         self.dont_buy = False               
                behavior = poc < mp_lng
                if behavior:
                    self.dont_buy = True
                else:
                    self.dont_buy = False
            
    @property
    def VWAP(self):
       return self.vwap.Value
       
class IntradayVwap:
    '''Defines the canonical intraday VWAP indicator'''
    def __init__(self):
        self.Value = 0.0
        self.lastDate = datetime.min
        self.sumOfVolume = 0.0
        self.sumOfPriceTimesVolume = 0.0

    @property
    def IsReady(self):
        return self.sumOfVolume > 0.0

    def Update(self, input):
        '''Computes the new VWAP'''
        success, volume, averagePrice = self.GetVolumeAndAveragePrice(input)
        if not success:
            return self.IsReady

        # reset vwap on daily boundaries
        if self.lastDate != input.EndTime.date():
            self.sumOfVolume = 0.0
            self.sumOfPriceTimesVolume = 0.0
            self.lastDate = input.EndTime.date()

        # running totals for Σ PiVi / Σ Vi
        self.sumOfVolume += volume
        self.sumOfPriceTimesVolume += averagePrice * volume

        if self.sumOfVolume == 0.0:
           # if we have no trade volume then use the current price as VWAP
           self.Value = input.Value
           return self.IsReady

        self.Value = self.sumOfPriceTimesVolume / self.sumOfVolume
        return self.IsReady

    def GetVolumeAndAveragePrice(self, input):
        '''Determines the volume and price to be used for the current input in the VWAP computation'''

        if type(input) is Tick:
            if input.TickType == TickType.Trade:
                return True, float(input.Quantity), float(input.LastPrice)

        if type(input) is TradeBar:
            if not input.IsFillForward:
                averagePrice = round(float(statistics.mean([input.Open, input.High, input.Low, input.Close])), 4)
                return True, float(input.Volume), averagePrice

        return False, 0.0, 0.0
#region imports
from AlgorithmImports import *
#endregion
import random
import string
import math

class TradeManagement:
    
    def __init__(self, algorithm, ticker, optionSymbol):
        self.Trades     = {}
        self.Algorithm  = algorithm
        self.Ticker = ticker
        self.OptionSymbol = optionSymbol
        
        # Volatility indicators for position sizing
        self.atr    = self.Algorithm.ATR(self.Ticker, 3, MovingAverageType.Simple, Resolution.Hour)
        self.stddev = self.Algorithm.STD(self.Ticker, 6, Resolution.Hour)
    
    # Manage Open Positions
    def ManageOpenPositions(self):
        
        for t in self.OpenTrades():
            
            # Scan for Profit-Loss
            if t.UnrealizedProfit() > t.Target:
                self.Close(t, "Profit")
                
            elif t.UnrealizedProfit() < -t.Target:
                self.Close(t, "Loss")
                
            # Stop Assignment
            if t.ExpiringSoon():
                self.Close(t, "Expiring")
    
    
    # Base target contract count on the number of contracts to hit the profit assuming 2SD move.
    def ContractSizing(self, targetProfit): 
        expectedDollarMovement = 2 * self.stddev.Current.Value * 100
        # At least 1 contract
        contracts = min(5, math.ceil(targetProfit / expectedDollarMovement))
        return int(contracts)
        
    # Base target profit per position on the volatility of the last few days.
    def TargetProfitEstimate(self):
        return round(self.atr.Current.Value * 100)
        
    def OpenTrades(self):
        return [t for t in self.Trades.values() if t.IsOpen()]
    
    # Place a trade in the direction signalled
    def Create(self, direction):
        
        symbol = self.SelectOptionContract(direction)
        if symbol is None:
            return
        
        # If we already hold; skip
        alreadyOpen = [c for c in self.OpenTrades() if c.Symbol == symbol]
        if len(alreadyOpen) > 0:
            return
        
        # If today's the expiry don't trade
        # if (symbol.ID.Date < self.Algorithm.Time):
        #     return
        
        targetProfit = self.TargetProfitEstimate()
        size = self.ContractSizing(targetProfit)
        price = self.Algorithm.Securities[symbol].Price
        asset = self.Algorithm.Securities[self.Ticker].Price
        
        tag = f"Asset: {asset} | Opened | Target of ${targetProfit} at ${price}"
        ticket  = self.Algorithm.MarketOrder(symbol, size, False, tag)
        trade   = Trade(self.Algorithm, self.Algorithm.Securities[symbol], ticket, targetProfit)
        
        self.Trades[trade.Id] = trade
        return trade
        
    def Close(self, trade, reason):
        trade.Close(reason)
        if trade.Id in self.Trades:
            del self.Trades[trade.Id]
        
    # Select OOM Option Contract with Given Bias
    def SelectOptionContract(self, direction):
        
        if direction == OrderDirection.Buy:
            right = OptionRight.Call
        else:
            right = OptionRight.Put
        
        chain = self.Algorithm.CurrentSlice.OptionChains.GetValue(self.OptionSymbol)
        if chain is None: 
            self.Algorithm.Log(f"{self.Algorithm.Time} - No option chains with {self.OptionSymbol}. Missing data for Mar-2nd.")
            return None
        
        # Select contracts expirying tomorrow at least.
        chain = [x for x in chain if (x.Right == right and x.Expiry > self.Algorithm.Time)]
        reverseSort = right == OptionRight.Call         # Reverse sort of a call
        contracts = sorted(sorted(chain, key = lambda x: abs(x.Strike), reverse=reverseSort), key = lambda x: x.Expiry)
        
        # No contracts found
        if len(contracts) == 0:
            return None;
            
        return contracts[0].Symbol


class Trade:
    
    def __init__(self, algorithm, contract, ticket, target): 
        self.Id         = ''.join(random.choice(string.ascii_lowercase) for i in range(10))
        self.Ticket     = ticket 
        self.Contract   = contract
        self.Symbol     = contract.Symbol
        self.Algorithm  = algorithm
        self.Open       = True
        self.Target     = target
        
    def ExpiringSoon(self): 
        expiry = self.Symbol.ID.Date + timedelta(hours=16)
        if ( (expiry - self.Algorithm.Time) < timedelta(minutes=10)):
            self.Algorithm.Log(f"{self.Symbol} Close to Expiry: {expiry} - {self.Algorithm.Time} < 10min" )
            return True
        else:
            return False
        
    def UnrealizedProfitPercent(self):
        return (self.Contract.Holdings.Price - self.Ticket.AverageFillPrice) / self.Contract.Holdings.Price
    
    def UnrealizedProfit(self):
        return 100*(self.Contract.Holdings.Price - self.Ticket.AverageFillPrice) * self.Ticket.Quantity
        
    def IsOpen(self):
        return self.Open
        
    def Close(self, reason):
        self.Open = False
        tag = f"Close | {reason} | {self.UnrealizedProfit()} Net"
        self.Algorithm.Liquidate(self.Symbol, tag)
#region imports
from AlgorithmImports import *
#endregion
from System import *
from QuantConnect import *
from QuantConnect.Indicators import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Indicators.CandlestickPatterns import *

import numpy as np
from scipy import stats
import tweepy
import statistics
from pykalman import KalmanFilter
from FilterIndicators import *
from SmartRollingWindow import *
from symbol_data_functions import SymbolData

# import datetime
from datetime import timedelta, datetime

class ScheduledExecutionModel(ExecutionModel):
    '''Execution model that submits orders while the current market price is more favorable that the current volume weighted average price.'''

    def __init__(self, algorithm, *args, **kwargs):
        super().__init__()
        '''Initializes a new instance of the VolumeWeightedAveragePriceExecutionModel class'''
        self.targetsCollection = PortfolioTargetCollection()
        self.deviations = 2
        self.symbolData = {}
        self.data = {}
        # self.datafourHour = dict()
        # self.datathirtyMin = dict()

        # Gets or sets the maximum order quantity as a percentage of the current bar's volume.
        # This defaults to 0.01m = 1%. For example, if the current bar's volume is 100,
        # then the maximum order size would equal 1 share.
        self.MaximumOrderQuantityPercentVolume = 0.1

        # Gets or sets the maximum spread compare to current price in percentage.
        self.acceptingSpreadPercent = 0.001
        
    def Execute(self, algorithm, targets):
        '''Executes market orders if the standard deviation of price is more
       than the configured number of deviations in the favorable direction.
       Args:
           algorithm: The algorithm instance
           targets: The portfolio targets'''

        # update the complete set of portfolio targets with the new targets
        self.targetsCollection.AddRange(targets)

        # for performance we check count value, OrderByMarginImpact and ClearFulfilled are expensive to call
        if self.targetsCollection.Count > 0:
            
            for target in self.targetsCollection.OrderByMarginImpact(algorithm):
                symbol = target.Symbol

                # calculate remaining quantity to be ordered
                unorderedQuantity = OrderSizing.GetUnorderedQuantity(algorithm, target)
                
                # fetch our symbol data containing our VWAP indicator
                data = self.symbolData.get(symbol, None)
                if data is None: return
        
                # check order entry conditions  
                if self.PriceIsFavorable(data, unorderedQuantity):
                    # adjust order size to respect maximum order size based on a percentage of current volume
                    orderSize = OrderSizing.GetOrderSizeForPercentVolume(data.Security, self.MaximumOrderQuantityPercentVolume, unorderedQuantity)
                    if (data.Security.BidPrice < data.VWAP):    
                        price_est = round(statistics.median([data.VWAP, data.Security.BidPrice]), 4)    
                    elif (data.Security.AskPrice > data.VWAP):    
                        price_est = round(statistics.median([data.VWAP, data.Security.AskPrice]), 4)
                    
                    max_quantity = algorithm.CalculateOrderQuantity(symbol, 0.95)
          
                    # suggested amount divided by the total possible amount
                    try:
                        order_percent = round(float(orderSize/max_quantity), 4)
                    except:
                        if max_quantity == 0:
                            order_percent = 0.0
                        else:
                            cash = algorithm.Portfolio.Cash
                            max_quantity = int(cash/price_est)
                            order_percent = round(float(orderSize/max_quantity), 4)
                    
                    if ((orderSize != 0) and (abs(order_percent) >= 0.1)):
                        coef = abs(order_percent) * 0.5
                        if algorithm.Portfolio[symbol].Invested:
                            if coef <= 0.25:
                                coef = int(abs(float(order_percent))/0.025)*10
                        else:
                            rocsignal = all(x > 0.0 for x in [self.data[symbol].rocSignal_quick.Current.Value, self.data[symbol].rocSignal_fast.Current.Value, self.data[symbol].rocSignal_med.Current.Value, self.data[symbol].rocSignal_long.Current.Value])
                            volsignal = all(x > 0.0 for x in [self.data[symbol].volSignal_quick.Current.Value, self.data[symbol].volSignal_fast.Current.Value, self.data[symbol].volSignal_med.Current.Value, self.data[symbol].volSignal_long.Current.Value])
                            roc_vol_signal = rocsignal and volsignal
                            coef = int(abs(float(order_percent))/0.025)*5
                            
                        signals = (self.data[symbol].breakout or (self.data[symbol].vpnIndicator and (self.data[symbol].is_uptrend and self.data[symbol].trix_uptrend or (self.data[symbol].roc_vol_signal_up and self.data[symbol].entry_signal and self.data[symbol].kal_entry_signal and self.data[symbol].rsi_entry_signal and self.data[symbol].macd_entry_signal and self.data[symbol].williams_entry_signal))) or (self.data[symbol].macd_uptrend and self.data[symbol].rsi_uptrend))
                        slope_cond = (self.data[symbol].roc_slope > 0.00) and (self.data[symbol].vol_slope > 0.00) 
                        down_signals = ((self.data[symbol].breakdown or ((not self.data[symbol].vpnIndicator) and (self.data[symbol].is_downtrend and self.data[symbol].trix_downtrend) or (self.data[symbol].roc_vol_signal_down and self.data[symbol].exit_signal and self.data[symbol].kal_exit_signal and self.data[symbol].rsi_exit_signal and self.data[symbol].macd_exit_signal and self.data[symbol].williams_exit_signal and self.data[symbol].trix_exit_signal))) or (self.data[symbol].macd_downtrend and self.data[symbol].rsi_downtrend))
                        slope_down = (self.data[symbol].roc_slope > 0.00) and (self.data[symbol].vol_slope > 0.00)
                        if (slope_cond) and (signals):
                            if self.data[symbol].breakout or self.data[symbol].breakdown:
                                coef = coef * 30+10
                            else:
                                coef = coef * 30
                        elif (slope_cond) or (signals):
                            if self.data[symbol].breakout or self.data[symbol].breakdown:
                                coef = coef * 10+10
                            else:
                                coef = coef * 10
                        elif (slope_down) and (down_signals):
                            coef = 0
                        orderSize = OrderSizing.GetOrderSizeForPercentVolume(data.Security, self.MaximumOrderQuantityPercentVolume*coef, unorderedQuantity)
                        projected_cost = round(price_est * orderSize, 4) * 1.0

                        if (algorithm.Portfolio.Cash > projected_cost) and (orderSize != 0):
                            algorithm.MarketOrder(symbol, orderSize)                        
            self.targetsCollection.ClearFulfilled(algorithm)

    def OnSecuritiesChanged(self, algorithm, changes):
        '''Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm'''
        for removed in changes.RemovedSecurities:
            # clean up removed security data
            if removed.Symbol in self.symbolData:
                if self.IsSafeToRemove(algorithm, removed.Symbol):
                    data = self.symbolData.pop(removed.Symbol)
                    algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator)
            if removed.Symbol in self.data:
                if self.IsSafeToRemove(algorithm, removed.Symbol):
                    data = self.data.pop(removed.Symbol)
                    algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator)
            # if removed.Symbol in self.datafourHour:
            #     if self.IsSafeToRemove(algorithm, removed.Symbol):
            #         data = self.datafourHour.pop(removed.Symbol)
            #         algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator)
            # if removed.Symbol in self.datathirtyMin:
            #     if self.IsSafeToRemove(algorithm, removed.Symbol):
            #         data = self.datathirtyMin.pop(removed.Symbol)
            #         algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator)            

        for added in changes.AddedSecurities:
            if added.Symbol not in self.symbolData:
                self.symbolData[added.Symbol] = SymbolDataExecuteModel(algorithm, added)
            if added.Symbol not in self.data:
                self.data[added.Symbol] = SymbolData(algorithm, added.Symbol, Resolution.Daily)
            # if added.Symbol not in self.datafourHour:
            #     self.datafourHour[added.Symbol] = SymbolData(algorithm, added.Symbol, Resolution.Hour)
            # if added.Symbol not in self.datathirtyMin:
            #     self.datathirtyMin[added.Symbol] = SymbolData(algorithm, added.Symbol, Resolution.Minute)

    def PriceIsFavorable(self, data, unorderedQuantity):
        '''Determines if the current price is favorable in the favorable direction.'''
        sma = data.SMA.Current.Value
        deviations = self.deviations * data.STD.Current.Value
        sto = ((data.STO.StochK.Current.Value >= data.STO.StochD.Current.Value) and (data.STO_Med.StochK.Current.Value >= data.STO_Med.StochD.Current.Value)) or ((data.KWindow.crossedAbove(data.DWindow) and (data.KWindow_Med.crossedAbove(data.DWindow_Med))))
        if unorderedQuantity > 0:
            if (data.Security.BidPrice < data.VWAP) or (data.Security.BidPrice < sma - deviations) and sto:
                return True
        else:
            if (data.Security.AskPrice > data.VWAP) or (data.Security.AskPrice > sma + deviations) and sto:
                return True

        return False

    def SpreadIsFavorable(self, data):
        '''Determines if the spread is in desirable range.'''
        # Price has to be larger than zero to avoid zero division error, or negative price causing the spread percentage < 0 by error
        # Has to be in opening hours of exchange to avoid extreme spread in OTC period
        return data.Security.Price > 0 and data.Security.AskPrice > 0 and data.Security.BidPrice > 0 \
            and (data.Security.AskPrice - data.Security.BidPrice) / data.Security.Price <= self.acceptingSpreadPercent

    def IsSafeToRemove(self, algorithm, symbol):
        '''Determines if it's safe to remove the associated symbol data'''
        # confirm the security isn't currently a member of any universe
        return not any([kvp.Value.ContainsMember(symbol) for kvp in algorithm.UniverseManager])
        
class SymbolDataExecuteModel:
    def __init__(self, algorithm, security):
        self.Security = security
        self.period = 21
        self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, security.Resolution) # TradeBarConsolidator(timedelta(hours=4)) # 
        name = algorithm.CreateIndicatorName(security.Symbol, "VWAP", security.Resolution)
        self.vwap = IntradayVwap(name)
        algorithm.RegisterIndicator(security.Symbol, self.vwap, self.Consolidator)
        #  VolumeWeightedAveragePriceIndicator(int(8*1.0))
        smaName = algorithm.CreateIndicatorName(security.Symbol, f"SMA{self.period}", security.Resolution)
        self.SMA = SimpleMovingAverage(smaName, self.period)
        algorithm.RegisterIndicator(security.Symbol, self.SMA, self.Consolidator)

        smaName_med = algorithm.CreateIndicatorName(security.Symbol, f"SMA{self.period+13}", security.Resolution)
        self.SMA_Med = SimpleMovingAverage(smaName_med, self.period+13)
        algorithm.RegisterIndicator(security.Symbol, self.SMA_Med, self.Consolidator)

        stoName = algorithm.CreateIndicatorName(security.Symbol, f"STO{self.period}", security.Resolution)
        self.STO = Stochastic(smaName, self.period, 3, 3)
        algorithm.RegisterIndicator(security.Symbol, self.STO, self.Consolidator)
        self.DWindow = SmartRollingWindow("float", 5)
        self.KWindow = SmartRollingWindow("float", 5)

        stoName_med = algorithm.CreateIndicatorName(security.Symbol, f"STO{self.period+13}", security.Resolution)
        self.STO_Med = Stochastic(smaName_med, self.period+13, 3, 3)
        algorithm.RegisterIndicator(security.Symbol, self.STO_Med, self.Consolidator)
        self.DWindow_Med = SmartRollingWindow("float", 5)
        self.KWindow_Med = SmartRollingWindow("float", 5)

        stdName = algorithm.CreateIndicatorName(security.Symbol, f"STD{self.period}", security.Resolution)
        self.STD = StandardDeviation(stdName, self.period)
        algorithm.RegisterIndicator(security.Symbol, self.STD, self.Consolidator)

        # warmup our indicators by pushing history through the indicators
        history = algorithm.History(security.Symbol, 84, security.Resolution)
        if 'close' in history:
            for index, row in history.loc[security.Symbol].iterrows():
                tradeBar = TradeBar(index, row['open'], row['high'], row['low'], row['close'], row['volume'])
                median_price = round(float(statistics.median([row['open'], row['high'], row['low'], row['close']])), 4)
                self.SMA.Update(index, median_price)
                self.SMA_Med.Update(index, median_price)
                self.STD.Update(index, median_price)
                sma = self.SMA.Current.Value
                sma_bar = TradeBar(index, sma, sma, sma, sma, 0)
                self.STO.Update(sma_bar)
                if self.STO.IsReady:
                    self.KWindow.Add(self.STO.StochK.Current.Value)
                    self.DWindow.Add(self.STO.StochD.Current.Value)
                sma = self.SMA_Med.Current.Value
                sma_bar = TradeBar(index, sma, sma, sma, sma, 0)
                self.STO_Med.Update(sma_bar)
                if self.STO_Med.IsReady:
                    self.KWindow_Med.Add(self.STO_Med.StochK.Current.Value)
                    self.DWindow_Med.Add(self.STO_Med.StochD.Current.Value)

    @property
    def VWAP(self):
       return self.vwap.Value
       
    def dispose(self, algorithm):
        algorithm.SubscriptionManager.RemoveConsolidator(security.Symbol, self.consolidator)

class IntradayVwap:
    '''Defines the canonical intraday VWAP indicator'''
    def __init__(self, name):
        self.Name = name
        self.Value = 0.0
        self.lastDate = datetime.min
        self.sumOfVolume = 0.0
        self.sumOfPriceTimesVolume = 0.0

    @property
    def IsReady(self):
        return self.sumOfVolume > 0.0

    def Update(self, input):
        '''Computes the new VWAP'''
        success, volume, averagePrice = self.GetVolumeAndAveragePrice(input)
        if not success:
            return self.IsReady

        # reset vwap on daily boundaries
        if self.lastDate != input.EndTime.date():
            self.sumOfVolume = 0.0
            self.sumOfPriceTimesVolume = 0.0
            self.lastDate = input.EndTime.date()

        # running totals for Σ PiVi / Σ Vi
        self.sumOfVolume += volume
        self.sumOfPriceTimesVolume += averagePrice * volume

        if self.sumOfVolume == 0.0:
           # if we have no trade volume then use the current price as VWAP
           self.Value = input.Value
           return self.IsReady

        self.Value = self.sumOfPriceTimesVolume / self.sumOfVolume
        return self.IsReady

    def GetVolumeAndAveragePrice(self, input):
        '''Determines the volume and price to be used for the current input in the VWAP computation'''

        if type(input) is Tick:
            if input.TickType == TickType.Trade:
                return True, float(input.Quantity), float(input.LastPrice)

        if type(input) is TradeBar:
            if not input.IsFillForward:
                averagePrice = round(float(statistics.mean([input.Open, input.High, input.Low, input.Close])), 4)
                return True, float(input.Volume), averagePrice

        return False, 0.0, 0.0
        


#region imports
from AlgorithmImports import *
#endregion
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *

import sys
import tweepy
from tweepy import OAuthHandler
from time import sleep
import json

# Important - Don't name file 'tweepy.py'

# Used below to supress problems and continue instead of try/except/continue
from contextlib import suppress

# --- # --------------------------------------------------------------------- # --- #
# --- # --------------------------------------------------------------------- # --- #

# Set True to follow a defined list of twitter users.
# Set False to stream from all accounts based on defined keywords.

FollowerMode = True

# --- # --------------------------------------------------------------------- # --- #
# --- # --------------------------------------------------------------------- # --- #

# --- # ---------------------------------- # --- #
# --- # ---- Follower Mode Dictionary ---- # --- #
# --- # ---------------------------------- # --- #

# Lookup Twitter ID's here http://gettwitterid.com/ by entering the accounts @Usernamehandle (without the @)
# It doesn't care what you name them. The names are only displayed to the console.

idsdict = {'TT3Private'}

# --- # --------------------------------------------------------------------- # --- #
# --- # --------------------------------------------------------------------- # --- #

# --- # ------------------------------ # --- #
# --- # ---- Search Mode Keywords ---- # --- #
# --- # ------------------------------ # --- #

# --- # ----------------- # --- #
# --- # SEARCH BY KEYWORD # --- #
# --- # ----------------- # --- #

# Example
# search = ['breaking news']

# --- # ---------- # --- #
# --- # SEARCH ALL # --- #
# --- # ---------- # --- #

# [' '] and [''] yields no results. The only way to truly stream all of the tweets (unfiltered)
# requires a connection to the firehose(https://developer.twitter.com/en/docs/tweets/sample-realtime/overview/decahose.html), 
# which is granted only in specific use enterprise cases by Twitter.

search = ['.','a','@','\'','this','to',':(','?','!','$',
          'h','+','_','-','#','b','you', 'c',',','the',
          'i','/','lol','at','this','need','and','RT',
          'if','1', 'd','e','f','g'] # Feel free to expand on this. I believe there's a limit on how much you can add.


# --- # -------------------- # --- #
# --- # SEARCH BY USER INPUT # --- #
# --- # -------------------- # --- #

# search = [input('Enter keyword\n\n')]

# --- # -------------------------------------------------------------- # --- #
# --- # -------------------------------------------------------------- # --- #



# --- # ---------------------- # --- #
# --- # --- AUTHENTICATION --- # --- #
# --- # ---------------------- # --- #

consumer_key = self.GetParameter("Consumer_Api")
consumer_secret = self.GetParameter("Consumer_Api_Secret")
access_token = self.GetParameter("Access_Token")
access_token_secret = self.GetParameter("Access_Token_Secret")

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)

# --- # -------------------------------------------------------------- # --- #
# --- # -------------------------------------------------------------- # --- #

print('Listening for tweets...\n')

if FollowerMode == True:
    # gets all IDs from 'idsdict' and converts them to strings
    ids = [str(i) for i in list(idsdict.values())]

# Main Stream Listener Class
class MyStreamListener(tweepy.StreamListener):
    global ids
    global FollowerMode
    tweets = 0
    
    # on_status is a built in tweepy method to fetch tweets.
    # on_data is another one and shows more detailed information for analytical reasons,
    # but be aware that you will have to parse the json manually like data['text'], data['user']['location'], etc.
    # print out status or data(if using on_data) and run the script to fetch the full JSON to see everything that it can do.
    # You can find a good example of that here https://github.com/varadhbhatnagar/Emoyto
    def on_status(self, status):
        """
        # Use this if you plan to use the json functionality below. 
        # If you use this, tab the rest of this class below over by one indent(4 spaces)
        # with open ('tweets.json', 'a', encoding='utf-8') as f:        

        # Supress errors so if that specific tweet has an issue for whatever reason, it will skip it. Similar to try/except.
        # Don't use this if you want to debug/look for issues.
        """

        with suppress(Exception):
            
            userid = str(status.user.id)
            
            # "userid in ids" mentioned below removes all of the mentions and retweets and makes sure it only comes from the original account.
            # Tweepy has no built in way to exclude that to my knowledge based on stackoverflow answers.
            
            if FollowerMode == True and userid in ids:
                
                # You can do this for example - " if status.place.country == 'United States': ",
                # but most people don't have their country listed. status.user.location often shows 'state' or 'city, state' and/or country,
                # but their location is user set so it can really be something made up like 'outer space'. If it's that important,
                # you could always try and use an API to see if it's a valid location.
                
                print('-' * 80)
                
                # Prints the name for this ID that's defined in 'idsdict'
                with suppress(Exception):
                    print(list(idsdict.keys())[list(idsdict.values()).index(int(userid))])
                
                print('User: ' + status.user.screen_name)
                # Attempt to display location and/or country if it exists
                with suppress(Exception):
                    if status.user.location != None and status.user.location != 'None':
                        print('Location: ' + status.user.location)
                with suppress(Exception):
                    print('Country: ' + status.place.country)
                
                # Checks to see if tweet is 'extended'/long. If it is, it will display the full tweet.
                try:
                    text = status.extended_tweet['full_text']
                except AttributeError:
                    text = status.text
                print('Tweet: ' + text)
                sleep(0.015)
                
            elif FollowerMode == False:
                
                print('-' * 80)
                print('User: ' + status.user.screen_name)
                with suppress(Exception):
                    if status.user.location != None and status.user.location != 'None':
                        print('Location: ' + status.user.location)
                with suppress(Exception):
                    print('Country: ' + status.place.country)
                        
                try:
                    text = status.extended_tweet['full_text']
                except AttributeError:
                    text = status.text
                print('Tweet: ' + text)
                
                # Prevents the display from hiccups and keeps the scrolling smooth when scanning all
                sleep(0.016)
                            
            # --- # --------------------------------------------------------------------- # --- #
            # --- # --------------------------------------------------------------------- # --- #
            
            # Optional - Write tweet into json file. You can store just tweets for example
            # Make sure to un-comment the 'with f.open' above and tab the rest of the class below it.
          
            #json_str = json.dumps(status._json)
            #f.write(status.text + '\n')
            
            # --- # --------------------------------------------------------------------- # --- #
            # --- # --------------------------------------------------------------------- # --- #
            
            #  # Optional - Print something out every certain number of tweets to show how many tweets have came through.
            
            #  MyStreamListener.tweets += 1
            # if MyStreamListener.tweets % 1000 == 0:
            #     print(str(MyStreamListener.tweets) + ' Tweets')
            #     for i in range(15):
            #         print(f'|||||||||||||||||||||||||||||||||||----- {MyStreamListener.tweets} ------||||||||||||||||||||||||||||||||||||||| \n')
            #     sleep(1)
                
# Define the listener
listener = MyStreamListener()
stream = tweepy.Stream(auth, listener)


if FollowerMode == True:
    stream.filter(follow=ids)
else:
    stream.filter(languages=["en"], track = search )