Overall Statistics
Total Trades
499
Average Win
1.32%
Average Loss
-0.57%
Compounding Annual Return
34.229%
Drawdown
19.500%
Expectancy
0.364
Net Profit
58.484%
Sharpe Ratio
1.358
Probabilistic Sharpe Ratio
60.313%
Loss Rate
59%
Win Rate
41%
Profit-Loss Ratio
2.33
Alpha
0.304
Beta
0.006
Annual Standard Deviation
0.225
Annual Variance
0.05
Information Ratio
0.331
Tracking Error
0.335
Treynor Ratio
47.744
Total Fees
$501.99
class EMVIndicatorAlgorithm(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2019, 1, 1)
        self.SetEndDate(2020, 7, 24)
        self.SetCash(100000)
        
        self.tickers = ["SPY", "TSLA"]
    
        self.emv_by_symbol = {}
        for ticker in self.tickers:
            symbol = self.AddEquity(ticker, Resolution.Daily).Symbol
            self.emv_by_symbol[symbol] = EMV(symbol, self)
            
        self.portfolioPercentage = 1/len(self.tickers)
        
        self.lastday = None
        
        EMVPlot = Chart("EMV Plot")
        EMVPlot.AddSeries(Series("Buy", SeriesType.Scatter, 0))
        EMVPlot.AddSeries(Series("Short", SeriesType.Scatter, 0))
        EMVPlot.AddSeries(Series("Sell", SeriesType.Scatter, 0))
        EMVPlot.AddSeries(Series("Price", SeriesType.Line, 0))
        self.AddChart(EMVPlot)
        
        EMVValuePlot = Chart("EMV Value Plot")
        EMVValuePlot.AddSeries(Series("EMV", SeriesType.Line, 0))
        self.AddChart(EMVValuePlot)
        

    def OnData(self, data):

        for symbol, emv in self.emv_by_symbol.items():
            
            if not data.ContainsKey(symbol) or data[symbol] is None:
                continue
            
            close = data[symbol].Close
            invested = self.Portfolio[symbol].Invested
            if not emv.IsReady:
                return
            
            '''Enters a long or short position'''
            if emv.value > 0 and not invested:
                self.SetHoldings(symbol, self.portfolioPercentage)
                self.Plot("EMV Plot", "Buy", close)
            if emv.value < 0 and not invested:
                self.SetHoldings(symbol, -(self.portfolioPercentage)/20) #Allows 5% of portfolioPercentage to short
                self.Plot("EMV Plot", "Short", close)
                
            '''Liquidates a long or short position'''    
            if emv.value < 0 and self.Portfolio[symbol].IsLong:
                self.Liquidate(symbol)
                self.Plot("EMV Plot", "Sell", close)
            if emv.value > 0 and self.Portfolio[symbol].IsShort:
                self.Liquidate(symbol)
                self.Plot("EMV Plot", "Sell", close)
            
            
            self.Plot("EMV Plot", "Price", close)
            self.Plot("EMV Value Plot", "EMV", emv.value)
                    
class EMV:
    def __init__(self, symbol, algorithm):
        self.symbol = symbol
        self.algorithm = algorithm
        self.value = None
        
        self.highs = RollingWindow[float](2)
        self.lows = RollingWindow[float](2)
        self.volume = None
        
        # Warm up indicator
        history = algorithm.History(symbol, 2, Resolution.Daily)
        for idx, row in history.iterrows():
            self.highs.Add(row.high)
            self.lows.Add(row.low)
            self.volume = row.volume
        self.update_value()
        
        # Setup daily consolidator
        self.consolidator = TradeBarConsolidator(timedelta(hours=24))
        self.consolidator.DataConsolidated += self.DailyHandler
        algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
       
    @property 
    def IsReady(self):
        return self.highs.IsReady and \
                self.lows.IsReady and \
                self.volume is not None and \
                self.value is not None
        
    def update_value(self):
        
        mov_mid_today = (self.highs[0] + self.lows[0]) / 2
        mov_mid_yesterday = (self.highs[1] + self.lows[1]) / 2
        
        MID = mov_mid_today - mov_mid_yesterday
        
        if self.volume/10000 == 0 or self.highs[0] - self.lows[0] == 0:
            self.value = None
            return 
        
        ratio = (self.volume/10000) / (self.highs[0] - self.lows[0])
        self.value = MID/ratio
        
    def DailyHandler(self, sender, consolidated):
        self.highs.Add(consolidated.High)
        self.lows.Add(consolidated.Low)
        self.volume = consolidated.Volume
        self.update_value()