Overall Statistics
Total Trades
12
Average Win
0.27%
Average Loss
-0.11%
Compounding Annual Return
1.462%
Drawdown
0.700%
Expectancy
1.055
Net Profit
1.466%
Sharpe Ratio
1.359
Probabilistic Sharpe Ratio
64.897%
Loss Rate
40%
Win Rate
60%
Profit-Loss Ratio
2.42
Alpha
0.013
Beta
-0.008
Annual Standard Deviation
0.009
Annual Variance
0
Information Ratio
-1.07
Tracking Error
0.117
Treynor Ratio
-1.573
Total Fees
$0.00
import pandas as pd

class EMABasedStrategy(QCAlgorithm):
    
    def Initialize(self):       #Initialize Dates, Cash, Equities, Fees, Allocation, Parameters, Indicators, Charts
        
        # Set Start Date, End Date, and Cash
        #-------------------------------------------------------
        self.SetTimeZone(TimeZones.NewYork)     #EDIT: Added Timezon
        self.SetStartDate(2012, 1, 1)   # Set Start Date
        self.SetEndDate(2013, 1, 1)      # Set End Date
        self.SetCash(10000)            # Set Strategy Cash
        self.SetBenchmark("SPY")
        #-------------------------------------------------------
        
        # Set Custom Universe
        #-------------------------------------------------------
        self.AddUniverse(self.CoarseSelectionFilter, self.FineSelectionFilter)
        self.UniverseSettings.Resolution = Resolution.Hour    #Needs to change to Resolution.Minute once code works, leaving Daily for now to minimize data
        self.UniverseSettings.SetDataNormalizationMode = DataNormalizationMode.SplitAdjusted
        self.UniverseSettings.FeeModel = ConstantFeeModel(0.0)
        self.UniverseSettings.Leverage = 1
        self.SetBrokerageModel(BrokerageName.Alpaca, AccountType.Cash)      #EDIT: Added Brokerage, appears to have set fees to zero
        #-------------------------------------------------------
        
        # Set Contants 
        #-------------------------------------------------------
        self.EMA_Period_Fast = 5
        self.EMA_Period_Slow = 50
        self.EMA_Period_Medium = 10

        self.__numberOfSymbols     = 5
        self.__numberOfSymbolsFine = 2
        #-------------------------------------------------------
        
        # Define Percentage Allocation and variables
        #-------------------------------------------------------
        self.percentagebuy = 0.02
        self.percentagesell = 0.05
        self.stopLossPercent = 0.90
        self.indicators = {}
        self.buyOrderTicketBySymbol = {}
        self.sellOrderTicketBySymbol = {}
        self.trailingStopLossPrice = {}
        #-------------------------------------------------------
        
        self.Schedule.On(self.DateRules.On(2011, 12, 29), self.TimeRules.At(13, 0), self.SpecificTime);
        
        self.daily_equity = pd.Series([])
        
    def SpecificTime(self):
        #self.Log("SpecificTime: Fired at : {0}".format(self.Time)) 
        self.Liquidate()
        
    def CoarseSelectionFilter(self, coarse):
        sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)   # sort descending by daily dollar volume
        return [ x.Symbol for x in sortedByDollarVolume[:self.__numberOfSymbols] ]  # return the symbol objects of the top entries from our sorted collection
    
    def FineSelectionFilter(self, fine):  # sort the data by P/E ratio and take the top 'NumberOfSymbolsFine'
        sortedByPeRatio = sorted(fine, key=lambda x: x.OperationRatios.OperationMargin.Value, reverse=False)    # sort descending by P/E ratio
        self.universe = [ x.Symbol for x in sortedByPeRatio[:self.__numberOfSymbolsFine] ]  # take the top entries from our sorted collection
        return self.universe
    
    def OnSecuritiesChanged(self, changes):
        # Create indicator for each new security
        for security in changes.AddedSecurities:
            self.indicators[security.Symbol] = SymbolData(security.Symbol, self, self.EMA_Period_Fast, self.EMA_Period_Slow, self.EMA_Period_Medium)
        
        # for security in changes.RemovedSecurities:
        #     if security.Invested:
        #         self.Liquidate(security.Symbol, "Universe Removed Security")
            
        #     if security in self.indicators:
        #         self.indicators.pop(security.Symbol, None)
    
    
    def OnData(self, data):         #Entry Point for Data and algorithm - Check Data, Define Buy Quantity, Process Volume, Check Portfolio, Check RSI, Execute Buy/Sell orders, Chart Plots
        for symbol in self.universe:
            
            if not data.ContainsKey(symbol):    #Tested and Valid/Necessary
                continue
            
            if data[symbol] is None:            #Tested and Valid/Necessary
                continue
            
            if not symbol in self.indicators:    #Tested and Valid/Necessary
                continue
            
            # Ensure indicators are ready to update rolling windows
            if not self.indicators[symbol].slow_ema.IsReady:
                continue
            
            # Update EMA rolling windows
            self.indicators[symbol].fast_ema_window.Add(self.indicators[symbol].get_fast_EMA())
            self.indicators[symbol].slow_ema_window.Add(self.indicators[symbol].get_slow_EMA())
            self.indicators[symbol].medium_ema_window.Add(self.indicators[symbol].get_medium_EMA())
            
            # Check for Indicator Readiness within Rolling Window
            #-------------------------------------------------------
            if not (self.indicators[symbol].fast_ema_window.IsReady and self.indicators[symbol].slow_ema_window.IsReady and self.indicators[symbol].medium_ema_window.IsReady): 
                continue
            
            #EXECUTE TRADING LOGIC HERE -
            if self.Portfolio[symbol].Invested:
                if self.Portfolio[symbol].IsLong:
                    #self.Debug('symbol in long -->' + str(symbol) + '')
                    if self.indicators[symbol].is_buy_signal_liquidate():
                        self.Liquidate(symbol)
                    elif self.Portfolio[symbol].Price > self.buyOrderTicketBySymbol[symbol].AverageFillPrice + (self.buyOrderTicketBySymbol[symbol].AverageFillPrice*(1-self.stopLossPercent)) \
                        and self.Portfolio[symbol].Price > self.trailingStopLossPrice[symbol]: 
                        # self.Portfolio[symbol] returns EquityHoldings class
                        updateFields = UpdateOrderFields()
                        updateFields.StopPrice = self.Portfolio[symbol].Price * self.stopLossPercent
                        self.trailingStopLossPrice[symbol] = updateFields.StopPrice
                        self.buyOrderTicketBySymbol[symbol].Update(updateFields)
                elif self.indicators[symbol].is_sell_signal_liquidate() and self.Portfolio[symbol].IsShort:
                    #self.Debug('sell action  ->' + str(symbol))
                    self.Liquidate(symbol)
            if self.Portfolio.MarginRemaining > self.percentagebuy * self.Portfolio.TotalPortfolioValue:
                if not self.Portfolio[symbol].Invested:
                    if self.indicators[symbol].is_buy_signal():
                        self.buyquantity = round((self.percentagebuy*self.Portfolio.TotalPortfolioValue)/data[symbol].Close)
                        self.ticket = self.MarketOrder(symbol, self.buyquantity)
                        self.buyOrderTicketBySymbol[symbol] = self.ticket
                        self.trailingStopLossPrice[symbol] = self.ticket.AverageFillPrice
                        self.sellOrderTicketBySymbol.pop(symbol,None)
                        #self.Debug('trigger buy ordeer -->' + str(symbol))
                    elif self.indicators[symbol].is_sell_signal():
                        self.sellQuantity = round((self.percentagesell*self.Portfolio.TotalPortfolioValue)/data[symbol].Close)
                        self.ticket = self.MarketOrder(symbol, -1*self.sellQuantity)
                        self.sellOrderTicketBySymbol[symbol] = self.ticket
                        self.trailingStopLossPrice[symbol] = self.ticket.AverageFillPrice
                        self.buyOrderTicketBySymbol.pop(symbol,None)
                        #self.Debug('trigger sell ordeer -->' + str(symbol))
    def OnOrderEvent(self, orderEvent):
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        #self.Log("{0}: {1}: {2}".format(self.Time, order.Type, orderEvent))
        
    def OnEndOfDay(self):
        self.daily_equity = self.daily_equity.append(pd.Series(self.Portfolio.TotalPortfolioValue, index=[self.Time]))
        
    def OnEndOfAlgorithm(self):
        self.Log(self.daily_equity.to_string())

class SymbolData(object):
    
    rolling_window_length = 2
    
    def __init__(self, symbol, context, fast_ema_period, slow_ema_period, medium_ema_period):
        
        self.symbol = symbol
        self.fast_ema_period = fast_ema_period
        self.slow_ema_period = slow_ema_period
        self.medium_ema_period = medium_ema_period
        self.fast_ema = context.SMA(symbol, self.fast_ema_period, Resolution.Daily)    #, fillDataForward = True, leverage = 1, extendedMarketHours = False)
        self.slow_ema = context.SMA(symbol, self.slow_ema_period, Resolution.Daily)    #, fillDataForward = True, leverage = 1, extendedMarketHours = False)
        self.medium_ema = context.SMA(symbol, self.medium_ema_period, Resolution.Daily)
        self.fast_ema_window = RollingWindow[float](self.rolling_window_length)
        self.slow_ema_window = RollingWindow[float](self.rolling_window_length)
        self.medium_ema_window = RollingWindow[float](self.rolling_window_length)
        
        # Warm up EMA indicators
        history = context.History([symbol], slow_ema_period + self.rolling_window_length, Resolution.Hour)
        for time, row in history.loc[symbol].iterrows():
            self.fast_ema.Update(time, row["close"])
            self.slow_ema.Update(time, row["close"])
            self.medium_ema.Update(time, row["close"])
            
            # Warm up rolling windows
            if self.fast_ema.IsReady:
                self.fast_ema_window.Add(self.fast_ema.Current.Value)
            if self.slow_ema.IsReady:
                self.slow_ema_window.Add(self.slow_ema.Current.Value)
            if self.medium_ema.IsReady:
                self.medium_ema_window.Add(self.medium_ema.Current.Value)
    
    def get_fast_EMA(self):
        return self.fast_ema.Current.Value
        
    def get_slow_EMA(self):
        return self.slow_ema.Current.Value
        
    def get_medium_EMA(self):
        return self.medium_ema.Current.Value
        
    def is_buy_signal(self):
        if self.medium_ema_window[0] > self.slow_ema_window[0] and \
            self.medium_ema_window[1] < self.slow_ema_window[1]:
                if self.fast_ema_window[0] > self.slow_ema_window[0]:
                    return True

    def is_sell_signal(self):
        if self.medium_ema_window[0] < self.slow_ema_window[0] and \
            self.medium_ema_window[1] > self.slow_ema_window[1]:
                if self.fast_ema_window[0] < self.slow_ema_window[0]:
                    return True
    
    def is_buy_signal_liquidate(self):
        return self.fast_ema_window[0] < self.slow_ema_window[0] and \
            self.fast_ema_window[1] > self.slow_ema_window[1]
            
    def is_sell_signal_liquidate(self):
        return self.fast_ema_window[0] > self.slow_ema_window[0] and \
            self.fast_ema_window[1] < self.slow_ema_window[1]