Overall Statistics
Total Trades
204
Average Win
0.24%
Average Loss
-0.16%
Compounding Annual Return
2.718%
Drawdown
4.300%
Expectancy
0.277
Net Profit
3.240%
Sharpe Ratio
0.452
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.45
Alpha
0.118
Beta
-5.051
Annual Standard Deviation
0.058
Annual Variance
0.003
Information Ratio
0.136
Tracking Error
0.058
Treynor Ratio
-0.005
Total Fees
$205.74
class BensdorbMeanReversionLong(QCAlgorithm):
    cur_sec={}
    indic={}
    indic_adx={}

    def Initialize(self):
        self.SetStartDate(2017, 1, 18)  # Set Start Date
        self.SetEndDate(2018, 12, 30)  # Set Start Date
        self.SetCash(10000)  # Set Strategy Cash
        self.__numberOfSymbols = 10
        self.UniverseSettings.Resolution = Resolution.Daily
        self.UniverseSettings.Leverage = 1
        self.AddUniverse(self.CoarseSelectionFunction)     
        
    def CoarseSelectionFunction(self, coarse):
        filtered = [ x for x in coarse 
                      if x.Price > 1 and x.DollarVolume > 2500000 ]
        #self.Debug("------------")
        #self.Debug(len(filtered))
        
        #indicators without adx 
        #self.indic.clear()
        for cf in filtered:
            if cf.Symbol not in self.indic:
                self.indic[cf.Symbol] = SymbolData(cf.Symbol,self)
            #
            indic_cond=self.indic[cf.Symbol]
            indic_cond.update(cf.EndTime, cf.AdjustedPrice)
        
        filtered=[x for x in self.indic.values() if x.is_above_ema \
            and x.is_rsi_cond ]
        #adx indicator
        #self.indic_adx.clear()
        for indic in filtered:
            symb=indic.symbol
            #if symb not in self.indic_adx:
            try:
               self.indic_adx[symb] = SymbolDataADX(indic)
               self.AddEquity(str(symb.Value),Resolution.Daily)
               self._updateADX(symb)
            except Exception as e:
               self.Debug(e)
               self.Log(e)
        #
        filtered_adx=[x for x in self.indic_adx.values() if x.is_adx_cond \
            and x.is_atr_cond]
        #
        if len(filtered_adx)>0:
            self.Debug("###")
            self.Debug(len(filtered_adx))
        #
        values=sorted(filtered_adx, key = lambda x: x.symb_data.rsi, \
            reverse=False)[:self.__numberOfSymbols]
        return [ x.symb_data.symbol for x in values]
    
    def OnSecuritiesChanged(self, changes):
        self.changes = changes
    
        for security in changes.AddedSecurities:
            symb=security.Symbol
            if not security.IsTradable:
                continue
            try:
                price=security.Close
                if price>0 and symb not in self.cur_sec:
                    self.cur_sec[symb]={'entry_price' : price,\
                        'stop_price': .0, \
                        'profit_price': price + price * 0.03, \
                        'bars_since_entry' : 0}
            except Exception as e:
                self.Debug(e)
                self.Log(e)
                
    def OnData(self, data):
        lst_to_del=[]
        
        for symb in self.cur_sec:
            symb_str=str(symb.Value)
            try:
                if (data.ContainsKey(symb_str))==False or data[symb_str]==None:
                    continue
                price=data[symb_str].Close
                #enter long
                if self.cur_sec[symb]['entry_price'] <price - price * 0.01  \
                    and self.cur_sec[symb]['bars_since_entry']==1 :
                    self.SetHoldings(symb, 0.1)
                #exit long
                elif price>self.cur_sec[symb]['profit_price'] \
                    or price < self.cur_sec[symb]['stop_price'] \
                    or self.cur_sec[symb]['bars_since_entry'] > 4 :
                    #
                    self.Liquidate(symbolToLiquidate=symb_str)
                    lst_to_del.append(symb)
                    self.cur_sec[symb]['bars_since_entry']=0
                else:
                    if self.cur_sec[symb]['bars_since_entry']>2:
                        self._updateADX(symb)
                        self.cur_sec[symb]['stop_price']= price -\
                            self.indic_adx[symb].atr.Current.Value / price * 2.5
                    self.cur_sec[symb]['bars_since_entry']+=1
                #
            except Exception as e:
                    self.Debug(e)
                    self.Log(e)
                    
        for symb in lst_to_del:
            self.cur_sec.pop(symb)
            if symb in self.indic:
                self.indic.pop(symb)
            if symb in self.indic_adx:
                self.indic_adx.pop(symb)
            self.RemoveSecurity(str(symb.Value))

    def _updateADX(self,symb):
        try:
            self.AddEquity(str(symb.Value),Resolution.Daily)
            tradeBarHistory = self.History(str(symb.Value), 7, Resolution.Daily)
            for tradeBar in tradeBarHistory:
                self.indic_adx[symb].update(tradeBar);
        except Exception as e:
            self.Debug(e)
            self.Log(e) 
        
class SymbolData(object):
    def __init__(self, symbol,strat):
        self.strat=strat
        self.price=0
        self.symbol=symbol
        self.ema = ExponentialMovingAverage(150)
        self.rsi =RelativeStrengthIndex(3)
        self.is_above_ema = False
        self.is_rsi_cond= False

    def update(self,time, value):
        #self.strat.Debug("indic value: " + str(value))
        #self.strat.Debug("time: " + str(time))
        if self.ema.Update(time,value) and self.rsi.Update(time,value):
            self.price=value
            ema = self.ema.Current.Value
            rsi = self.rsi.Current.Value
            self.is_above_ema = value > ema
            #self.strat.Debug("ema: " + str(ema))
            self.is_rsi_cond = rsi < 30
            
class SymbolDataADX(object):
    def __init__(self, symb_data):
        self.symb_data=symb_data
        self.atr= AverageTrueRange("",10)
        self.adx = AverageDirectionalIndex("",7)
        self.is_adx_cond =False
        self.is_atr_cond =False

    def update(self, bar):
        if self.adx.Update(bar) and self.atr.Update(bar):
            #
            atr=self.atr.Current.Value
            adx=self.adx.Current.Value
            #self.symb_data.strat.Debug("update atr: "+ str(atr/bar.Close))
            self.is_atr_cond = atr/bar.Close > 0.04
            self.is_adx_cond = adx > 45