Overall Statistics
Total Trades
7986
Average Win
0.35%
Average Loss
-0.18%
Compounding Annual Return
-83.012%
Drawdown
84.400%
Expectancy
-0.234
Net Profit
-83.094%
Sharpe Ratio
-2.859
Probabilistic Sharpe Ratio
0.000%
Loss Rate
74%
Win Rate
26%
Profit-Loss Ratio
1.92
Alpha
-0.694
Beta
-0.224
Annual Standard Deviation
0.262
Annual Variance
0.069
Information Ratio
-3.394
Tracking Error
0.295
Treynor Ratio
3.346
Total Fees
$14774.10
import clr
import decimal as d
import pandas as pd


class FuturesMovingAverage (QCAlgorithm):

    def Initialize(self):
        
        self.contract = None

        self.SetStartDate(2019, 1, 1)    
        self.SetEndDate(2020, 1, 1)      
        self.SetCash(100000)             
        self.SetWarmUp(TimeSpan.FromDays(5)) 
        self.SetTimeZone("America/New_York") 
        
        self.new_day = True
        self.reset = True
        
        
        
        
        CL = self.AddFuture(Futures.Energies.CrudeOilWTI)
        CL.SetFilter(TimeSpan.Zero, TimeSpan.FromDays(360))
        
        self.consolidators = {}
        self.movingAverages = {}
        
        
        self.sma = None
        self.sma_period = 60
        
        self.window = RollingWindow[TradeBar](3)
        
    def OnData(self, slice):
        
        if not self.InitUpdateContract(slice):
            return
    
        
        if self.reset:
            self.reset = False
        
        
        

    def InitUpdateContract(self, slice):
        
        
        if not self.new_day:
            return True
            
        if self.contract != None and (self.contract.Expiry - self.Time).days >= 3: 
            return True
            
        for chain in slice.FutureChains.Values:
            
            idx = 0
            if self.contract != None:
                self.Log('Expiry days away {} - {}'.format((self.contract.Expiry-self.Time).days, self.contract.Expiry))
            if self.contract != None and (self.contract.Expiry - self.Time).days < 3:
                idx = 1
            
            contracts = list(chain.Contracts.Values)
            
            chain_contracts = list(contracts) 
            chain_contracts = sorted(chain_contracts, key=lambda x: x.Expiry)
            
            if len(chain_contracts) < 2:
                return False
                
            first = chain_contracts[idx]
            second = chain_contracts[idx+1]
            
            if (first.Expiry - self.Time).days >= 3:
                self.contract = first
            elif (first.Expiry - self.Time).days < 3 and (second.Expiry - self.Time).days >= 3:
                self.contract = second
            self.Log("Setting contract to: {}".format(self.contract.Symbol.Value))
            
            self.new_day = False
            self.reset = True

            if self.contract.Symbol not in self.consolidators:
                self.consolidators[self.contract.Symbol] = self.Consolidate(
                    self.contract.Symbol, timedelta(minutes=5), self.OnFiveMinutes)
            if self.contract.Symbol not in self.movingAverages:
                self.movingAverages[self.contract.Symbol] = self.SMA(self.contract.Symbol, self.sma_period, Resolution.Minute)
                history = self.History(self.contract.Symbol, self.sma_period, Resolution.Minute)
                
                if not history.empty:
                    history = history.close.unstack(1)
                    for index, close in history[self.contract.Symbol].items():
                        time = index[1]
                        self.movingAverages[self.contract.Symbol].Update(time, close)
            
                
            
            return True
        return False
        
    def OnFiveMinutes(self, bar):
        
        self.window.Add(bar)
        
        if not self.window.IsReady: 
            return

        currentBar = self.window[0]  
        nearBar = self.window[1]
        farBar = self.window[2]
        
      
        if bar.Symbol != self.contract.Symbol:
            return

        sma = self.movingAverages.get(bar.Symbol, None)
        if sma is None or not sma.IsReady:
            return

        near = nearBar.Close
        far = farBar.Close
        
        sma = sma.Current.Value
        price = bar.Close    

        holdings = self.Portfolio[bar.Symbol].Quantity

        if not self.Portfolio.Invested:
            if far < sma and near > sma and price > near:
                self.MarketOrder(bar.Symbol, 1)
            if far > sma and near < sma and price < near:
                self.MarketOrder(bar.Symbol, -1)
                
        if holdings > 0 and price < sma:
            self.Liquidate(bar.Symbol)
            
        if holdings < 0 and price > sma:
            self.Liquidate(bar.Symbol)
            
 


    def OnEndOfDay(self):
        self.new_day = True