Overall Statistics
Total Trades
Average Win
Average Loss
Compounding Annual Return
Net Profit
Sharpe Ratio
Probabilistic Sharpe Ratio
Loss Rate
Win Rate
Profit-Loss Ratio
Annual Standard Deviation
Annual Variance
Information Ratio
Tracking Error
Treynor Ratio
Total Fees
        NOTES re CT III from FUTs
                #FUCK need to add HIGHS and LOWS to ma -- so need 2 MAs
                #ha12 and la12
                #R1 = Average(High?! of data2-O of data2,12); 
                #S1 = Average(O of data2-L of data2,12);
                #Cent = (OpenD(0)+CloseD(1))/2; 
                #R2 = Cent + PercentRange*R1; 0 + .5 * R1
                #S2 = Cent - PercentRange*S1; 

                #if lower > upper3  #Done
                #and Close < S2 #DONE
                #and opend(0) > close //(15M Close)               #Do in OnData with TradeBar
        Simpler way -- FUCK ult osc and this bullshit trade bar fucking joke. Jesus.
        for c in sortedByVol:
            symbol = c.Symbol
            if symbol not in self.UOs:
                history = self.History(symbol, 51, Resolution.Daily)
                self.UOs[symbol] = Ultimate(symbol, history)
            self.UOs[symbol].Update(c.EndTime, c.AdjustedPrice)

            #sortedByUO = sorted(self.UOs.items(), key = lambda kv: v.uo_value, reverse=True)
            if self.UOs[symbol].uo_val > 100:
                self.longs += [symbol]
            elif self.UOs[symbol].uo_val < -100:
                self.shorts += [symbol]
            self.selected = self.longs + self.shorts
        FUCK UO 
        In OnData
        #U3 Version...
        for symbol in self.selected:
            if data.Bars.ContainsKey(symbol):
                if symbol not in self.U3s:
                    hist = self.History(symbol, 25, Resolution.Daily)
                    self.U3s[symbol] = U3(hist, symbol)
                self.U3s[symbol].Update(data[symbol].EndTime,data[symbol]) #data[symbol].EndTime,
        #End U3 Version...
        tradeBars = data.Bars
        ibmTradeBar = tradeBars['IBM']
class U3():
    def __init__(self,history, symbol=None):
        if symbol:
            self.symbol = symbol
        self.uo = UltimateOscillator(9,15,25)
        for time, price in history.items(): #Manual update...
            self.Update(time, price)
        #I think issue is -- ^^ is using history
        #whereas BELOW is using data['symbol']
    def Update(self,time, bar):
        #Accepts data[sym] as bar
        #Update in OnData w data['SPY'] AS BAR -> data["SPY"].EndTime , data["SPY"].Close, High, Low etc
        #Dont kno if I need this line ? Think I already have tradebar.
        #IF not work try with bar! BELOW
        bar = TradeBar(time, self.symbol, bar.open, bar.high, bar.low, bar.close, bar.Volume)
        self.uo.Update(bar) #Builtin UO Update method...
class DailyBars():
    def __init__(self, algorithm, history):
        self.dailyWindow = RollingWindow[TradeBar](10) #GENIUS
        self.dailyCons = TradeBarConsolidator(timedelta(days=1))
        algorithm.SubscriptionManager.AddConsolidator(symbol, self.dailyCons) 
        for bar in history.itertuples():
            tb = TradeBar(bar.Index[1], symbol, bar.open, bar.high, bar.low, bar.close, bar.volume)
    def IsReady(self):
        return self.dailyWindow.IsReady
#from Selection.EmaCrossUniverseSelectionModel import EmaCrossUniverseSelectionModel
from System import *
from QuantConnect import *
from QuantConnect.Data import *
from QuantConnect.Algorithm import *
from QuantConnect.Securities import *
from QuantConnect import Market
from datetime import datetime
import pandas as pd

class CalibratedOptimizedInterceptor(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2018, 1, 1)  # Set Start Date
        self.SetEndDate(2019, 1, 1)
        self.SetCash(100000)  # Set Strategy Cash
        # self.AddEquity("SPY", Resolution.Minute)
        fastPeriod = 10
        slowPeriod = 30
        count = 10
        self.UOs = {}
        self.BB15s = {}
        self.U3s = {}
        self.L3s = {}
        self.DOs = {}
        self.UniverseSettings.Resolution = Resolution.Daily 
    def OnEndOfDay(self): 
        '''Where on earth is this called?'''
        self.Debug(f'{self.Time.date()} -- {len(self.ActiveSecurities)}')
        #for security in self.ActiveSecurities.Values:
        #    self.Debug(f"{self.Time.date()} -- {security.Symbol.Value}")
    def SelectCoarse(self, coarse):
        sortedByVol = sorted([c for c in coarse if c.AdjustedPrice > 10], 
                        key = lambda x: x.DollarVolume, reverse=True)[:100]
        self.selected = []
        self.longs = []
        self.shorts = []
        #Alt method...
        new = [s.Symbol for s in sortedByVol if s.Symbol not in self.UOs]
        if new:
            history = self.History(new, 51, Resolution.Daily)
            if history.empty: return Universe.Unchanged
            history = history.close.unstack(0) 
            for sym in new:
                if sym in history: #Added?
                    self.UOs[sym] = DBB(history[sym].dropna())
        openD, lower, upper, ma12 = 0,0,0,0
        for c in sortedByVol:
            symbol = c.Symbol
            if symbol not in new and symbol in self.UOs:
                self.UOs[symbol].Update(c.EndTime, c.AdjustedPrice)

            #New for U2
            if symbol not in self.BB15s:
                history = self.History(symbol, 25, Resolution.Daily)
                self.BB15s[symbol] = BB(symbol, self, history) 

                lower = self.BB15s[symbol].lb.Current.Value
                upper = self.BB15s[symbol].ub.Current.Value
                ma12 = self.BB15s[symbol].MA.Current.Value
                #self.Debug(f'{symbol} -- U:{upper} L:{lower}  MA:{ma12}')
                This needs to be ha12 and la12 -- 2 MAs... -- 
                can just average the highs / lows in vecs?
                openD = history.open.iloc[-1]                                   #-1 or 0?
                #closeYD = history.close.iloc[-2] Maybe should do windows?
            if self.UOs[symbol].IsReady and self.BB15s[symbol]:
                upper3 = self.UOs[symbol].ub3 #Uses Current.Value already!
                lower3 = self.UOs[symbol].lb3
                closeY = self.UOs[symbol].closeDs[1]
                self.Debug(f'{symbol} -- U3:{upper3} L3:{lower3}  CloseY:{closeY}')
                self.Debug(f'{symbol} -- U:{upper} L:{lower}  MA:{ma12}')
                cent = (openD + closeY) / 2
                R2 = cent + .5 * ma12
                S2 = cent - .5 * ma12

                if lower > upper3 and c.AdjustedPrice > (ma12):   #Added in .5 here, to make R2 S2
                    self.longs += [symbol]
                if upper < lower3 and c.AdjustedPrice < (ma12):
                    self.shorts += [symbol]
            self.selected = self.longs + self.shorts
        return self.selected
    def SelectFine(self, fine):
    def OnSecuritiesChanged(self, changes):
        self.Log(f"Added Securities: {[security.Symbol.Value for security in changes.AddedSecurities]}")
        insights = []
        for security in changes.RemovedSecurities:
            #insights += [Insight.Price(security.Symbol, timedelta(weeks=99), InsightDirection.Flat)]
            #algorithm.SubscriptionManager.RemoveConsolidator(symbol, self.symbols[symbol].monthlyConsolidator)
            #algorithm.SubscriptionManager.RemoveConsolidator(symbol, self.symbols[symbol].dailyConsolidator)

    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
                data: Slice object keyed by symbol containing the stock data
        if len(self.selected) == 0: return
        qty = .99 / len(self.selected)

        self.selected = [sym for sym in self.selected if data.ContainsKey(sym)]
        bar = data.Bars
        #Get daily open... For Timing the entries of longs / shorts.
        for symbol in self.selected:
            if not data.ContainsKey(symbol): continue
            sbar = bar[symbol]
            if symbol not in self.DOs:
                hist = self.History(symbol, 5, Resolution.Daily)
                self.DOs[symbol] = DailyOpen(hist)
            self.DOs[symbol].Update(sbar.Open) #Think this is right?
            todaysOpen = self.DOs[symbol].todaysOpen
        #and opend(0) > close //(15M Close) -- NEED to save OPEN of day!
        #Long Entries
        self.longs = [sym for sym in self.longs if sym in self.selected ]

        for symbol in self.longs:
            if self.Portfolio[symbol].IsLong: continue
            if not data.ContainsKey(symbol): continue
            sbar = bar[symbol]
            #self.Debug(f'open > close ? -- {todaysOpen} > {sbar.Close}')
            if todaysOpen > sbar.Close:
                self.SetHoldings(symbol, qty)
        ''' LONG ONLY   
        #Short Entries -- really just daily timing of them.
        self.shorts = [sym for sym in self.shorts if sym in self.selected]
        for symbol in self.shorts:
            if self.Portfolio[symbol].IsShort: continue
            if not data.ContainsKey(symbol): continue
            sbar = bar[symbol]
            #self.Debug(f'open < close ? --  {todaysOpen} < {sbar.Close}')
            if todaysOpen < sbar.Close:
                self.SetHoldings(symbol, -qty)
                #MAY need to increment entries today?

class DBB():
    #Need rolling window -- to ADD past DAILY bbs to this...
    def __init__(self,history): #symbol,
        #self.window = RollingWindow[float](50)
        self.closeDs = RollingWindow[float](2) #This is DAILY close
        #self.opens = RollingWindow[float](2) #Cannot add this, bc only closes passed.
        self.uppers = RollingWindow[float](5) #New Addit!
        self.lowers = RollingWindow[float](5)
        self.ub3 = None
        self.lb3 = None
        #self.uo = UltimateOscillator(9, 15, 24)   #self.symbol,
        self.bb = BollingerBands(20,2, MovingAverageType.Simple)
        self.ub = self.bb.UpperBand
        self.lb = self.bb.LowerBand
        #Unstacked one...
        for time, price in history.items():
            self.Update(time, price)

    def Update(self,time, price): #time,
        self.uppers.Add(self.ub.Current.Value) #Addits...

        if self.IsReady:
            self.ub3 = self.uppers[3]
            self.lb3 = self.lowers[3]

    def IsReady(self):          #Addits...
        return self.bb.IsReady and self.uppers.IsReady and self.lowers.IsReady #self.uo.IsReady #and
class BB():
    '''15M BBs'''
    def __init__(self,symbol, algorithm, history):
        self.BB = BollingerBands(symbol,20,2, MovingAverageType.Simple) 
        # ADDED SYMBOL ^^^^ TO BB 
        self.symbol = symbol
        algorithm.AddEquity(self.symbol) #?? Need to add I guess.. OKAY!
        self.consolidator = TradeBarConsolidator(timedelta(minutes=15))
        algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator) #NEW
        algorithm.RegisterIndicator(symbol, self.BB, self.consolidator)
        self.ub = self.BB.UpperBand
        self.lb = self.BB.LowerBand
        #MA Cons (NEW)
        self.MA = SimpleMovingAverage(12)
        self.ma_cons = TradeBarConsolidator(timedelta(days=1))
        algorithm.RegisterIndicator(symbol, self.MA, self.ma_cons)
        #self.ma = self.MA #.Current.Value
        #ZO ADDITS ************************
        for bar in history.itertuples(): #Manual update...
            tbar = TradeBar(bar.Index[1], symbol, bar.open, bar.high, bar.low, bar.close, bar.volume)
            self.MA.Update(tbar.EndTime,tbar.Close) #JUST tbar input?
        # *********************************
        #Register / Subscribe to event Don't think this is needed? ^^ that is register
        #self.consolidator.DataConsolidated += self.OnDataConsolidated
    def IsReady(self):
        return self.MA.IsReady and self.BB.IsReady
    #Dont think I need this?
    def OnDataConsolidated(self, sender, bar):
        self.BB.Update(bar.EndTime, bar.Close)   #Might JUST need to be index and bar -- if it's JUST closes?
        self.MA.Update(bar.EndTime, bar.Close)
        self.ub = self.BB.UpperBand
        self.lb = self.BB.LowerBand
        self.ma = self.MA

class DailyOpen():
    '''CAN just add TRADEBAR to window, then call w keys -- self.pastBars[0].Open, for ex'''
    def __init__(self, history):
        self.opens = RollingWindow[float](5)
        self.todaysOpen = 0
        for bar in history.itertuples():
        #for time, price in history.items(): symbol, WAS in tradeBar
            #tbar = TradeBar(bar.Index[1], bar.open, bar.high, bar.low, bar.close, bar.volume)
            #self.Update(tbar.Open)                         #Could simply do self.opens.Add(price.Open)
    def Update(self, price):
        if self.opens.IsReady:
            self.todaysOpen = self.opens[0]
'''Better Version of ^^^^'''
class DailyBars():
    '''More efficient version of DailyOpens'''
    def __init__(self, history):
        self.daily_opens = RollingWindow[float](5)
        self.daily_closes = RollingWindow[float](5)
        self.daily_highs = RollingWindow[float](5)
        self.daily_lows = RollingWindow[float](5)
        for time, bar in history.items():
    def Update(self, bar):

class Momentum():
    def __init__(self, history):
        self.window = RollingWindow[float](275)
        self.mom_yr = 0
        self.mom_mo = 0
        for row in history.itertuples():
    def Update(self, price):
        if self.window.IsReady:
            self.mom_yr = (self.window[25] - self.window[275]) / self.window[275] #Was [-252] and [-25]
            self.mom_mo = (self.window[0] - self.window[25]) / self.window[25]
class SymbolData:
    def __init__(self, algorithm, symbol):
        self.algorithm = algorithm
        self.symbol = symbol
        # Define daily and monthly rolling windows
        self.monthlyWindow = RollingWindow[TradeBar](13)
        self.dailyWindow = RollingWindow[TradeBar](280)
        # Define daily and monthly consolidators
        self.monthlyConsolidator = algorithm.Consolidate(symbol, Calendar.Monthly, self.OnMonthlyData)
        self.dailyConsolidator = TradeBarConsolidator(timedelta(days = 1))
        # Register daily consolistor to algorithm
        algorithm.SubscriptionManager.AddConsolidator(symbol, self.dailyConsolidator)
        # Define and register ADX indicator
        self.adxThreshold = 25
        self.adx = AverageDirectionalIndex(20)
        algorithm.RegisterIndicator(symbol, self.adx, self.dailyConsolidator)
        # Use historical data to warmup rolling windows, consolidators, and indicators
        history = algorithm.History(symbol, 280, Resolution.Daily)
        for bar in history.itertuples():
            tbar = TradeBar(bar.Index[1], symbol, bar.open, bar.high, bar.low, bar.close, bar.volume)