Overall Statistics
Total Trades
5464
Average Win
0.30%
Average Loss
0.00%
Compounding Annual Return
-4.871%
Drawdown
4.800%
Expectancy
-0.841
Net Profit
-4.675%
Sharpe Ratio
-1.176
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
143.50
Alpha
-0.034
Beta
-0.023
Annual Standard Deviation
0.029
Annual Variance
0.001
Information Ratio
-0.252
Tracking Error
0.138
Treynor Ratio
1.496
Total Fees
$10108.40
# https://quantpedia.com/Screener/Details/100
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Data import SubscriptionDataSource
from QuantConnect.Algorithm import *
from QuantConnect.Python import PythonData, PythonQuandl
from sklearn import datasets, linear_model
from datetime import date, timedelta, datetime
from collections import deque
import statsmodels.api as sm
import decimal
import numpy as np

class TradeWtiBrentSpreadAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2018, 1, 1)
        self.SetEndDate(2018, 12, 15)#datetime.now())
        self.SetCash(1000000)
        # import the custom data 
        '''self.AddData(WTI, "WTI", Resolution.Daily)
        self.AddData(BRENT, "BRENT", Resolution.Daily)'''
        self.es = "SCF/CME_ES1_ON"
        self.AddData(QuandlFuture, self.es, Resolution.Daily)
        self.nq = "SCF/CME_NQ1_ON"
        self.AddData(QuandlFuture, self.nq, Resolution.Daily)
        # Subscribe and set our expiry filter for the futures chain
        futureES = self.AddFuture(Futures.Indices.SP500EMini)
        futureES.SetFilter(timedelta(0), timedelta(90))
        futureNQ = self.AddFuture(Futures.Indices.NASDAQ100EMini)
        futureNQ.SetFilter(timedelta(0), timedelta(90))
        benchmark = self.AddEquity("SPY");
        self.SetBenchmark(benchmark.Symbol);
        self.frontES = None
        self.frontNQ = None
        
        #hist = self.History(["WTI", "BRENT"], 400, Resolution.Daily)["value"].unstack(level=0).dropna()
        #Create the EMA spread for the liquidate signal
        '''self.wtiema = self.EMA("WTI", 7)
        self.brentema = self.EMA("BRENT", 7)
        self.wtibrentema = IndicatorExtensions.Minus(self.wtiema, self.brentema)
        self.SpreadSMA = SimpleMovingAverage(13)#for WTI|BRENT'''
        
        self.esema = self.EMA(self.es, 8)
        self.nqema = self.EMA(self.nq, 8)
        self.esnqema = IndicatorExtensions.Minus(self.nqema, self.esema)
        self.esnqSMA = SimpleMovingAverage(13)
        
        # Add the spread plot and mark the long/short spread point
        spreadPlot = Chart("Spread Plot")
        #spreadPlot.AddSeries(Series("Spread", SeriesType.Line, 0))
        #spreadPlot.AddSeries(Series("Long Spread Trade", SeriesType.Scatter, 0))
        #spreadPlot.AddSeries(Series("Short Spread Trade", SeriesType.Scatter, 0))
        #spreadPlot.AddSeries(Series("Spread EMA", SeriesType.Line, 0))
        #spreadPlot.AddSeries(Series("Spread SMA", SeriesType.Line, 0))
        spreadPlot.AddSeries(Series("Spread2", SeriesType.Line, 0))
        spreadPlot.AddSeries(Series("ESNQ EMA", SeriesType.Line, 0))
        spreadPlot.AddSeries(Series("ESNQ SMA", SeriesType.Line, 0))
        self.AddChart(spreadPlot)
        self.SetWarmup(timedelta(100))
    def OnData(self, data):
        #self.Log("Warming Up")
        if self.IsWarmingUp: return #for some reason this was stuck warming up...i added the time delta bit, see what happens next..
        '''self.Log("checcking for data contains key wti brent")
        if (data.ContainsKey("WTI") and data.ContainsKey("BRENT")): 
            
            self.SpreadSMA.Update(self.Time, data["WTI"].Price - data["BRENT"].Price) 
        if not self.SpreadSMA.IsReady: return'''
        #self.Plot("Spread Plot", "Spread", data["WTI"].Price - data["BRENT"].Price)
        #self.Log("checking for data contains key ES NQ")
        if (data.ContainsKey("SCF/CME_NQ1_ON") and data.ContainsKey("SCF/CME_ES1_ON")):
            #self.Log("checking the nasdaq sma and plotting spread")
            self.esnqSMA.Update(self.Time, data["SCF/CME_NQ1_ON"].Price - data["SCF/CME_ES1_ON"].Price)
            self.Plot("Spread Plot", "Spread2", data["SCF/CME_NQ1_ON"].Price - data["SCF/CME_ES1_ON"].Price)
            
        #self.Log("checking if ES NQ SMA is ready")
        if not self.esnqSMA.IsReady: return
        #self.Plot("Spread Plot", "Spread EMA", self.wtibrentema.Current.Value)
        #self.Plot("Spread Plot", "Spread SMA", self.SpreadSMA.Current.Value)
        tolerance = decimal.Decimal(0.005);
        #pread = self.Securities["WTI"].Price - self.Securities["BRENT"].Price
        spread2 = self.Securities[self.nq].Price - self.Securities[self.es].Price
        '''
        if spread > self.SpreadSMA.Current.Value and not (self.Portfolio["WTI"].IsShort and self.Portfolio["BRENT"].IsLong):
            self.SetHoldings("WTI", -0.2)
            self.SetHoldings("BRENT", 0.2)
            #self.Plot("Spread Plot", "Long Spread Trade", data["WTI"].Price - data["BRENT"].Price)
            #self.Debug()
            
        elif spread < self.SpreadSMA.Current.Value and not (self.Portfolio["WTI"].IsLong and self.Portfolio["BRENT"].IsShort):
            self.SetHoldings("WTI", 0.2)
            self.SetHoldings("BRENT", -0.2)
            #self.Plot("Spread Plot", "Short Spread Trade", data["WTI"].Price - data["BRENT"].Price)
            
        if self.Portfolio["WTI"].IsShort and self.Portfolio["BRENT"].IsLong and spread < self.wtibrentema.Current.Value:
            self.Liquidate()
            #self.Debug("the ema of wti/brent is " + str(self.wtibrentema.Current.Value))
        
        if self.Portfolio["WTI"].IsLong and self.Portfolio["BRENT"].IsShort and spread > self.wtibrentema.Current.Value:
            self.Liquidate()'''
        #for kvp in data.Bars:
        #    self.Debug("---> OnData: {}, {}, {}" .format(self.Time, kvp.Key.Value, kvp.Value.Close));
        self.Plot("Spread Plot", "ESNQ SMA", self.esnqSMA.Current.Value)
        self.Plot("Spread Plot", "ESNQ EMA", self.esnqema.Current.Value)
        for chain in data.FutureChains:
            if chain.Key.Value == Futures.Indices.SP500EMini:
                if self.frontES is None :# Get contracts expiring no earlier than in 90 days
                    contracts = list(filter(lambda x: x.Expiry > self.Time + timedelta(10), chain.Value))
            # if there is any contract, trade the front contract
                    if len(contracts) == 0: continue
                    self.frontES = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
        
            if chain.Key.Value == Futures.Indices.NASDAQ100EMini:
                if self.frontNQ is None:
                    # Get contracts expiring no earlier than in 90 days
                    contracts = list(filter(lambda x: x.Expiry > self.Time + timedelta(10), chain.Value))
                # if there is any contract, trade the front contract
                    if len(contracts) == 0: continue
                    self.frontNQ = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
        #self.Debug("self.frontES is " + str(self.frontES))
        #self.Debug("self.frontNQ is " + str(self.frontNQ))
        if self.frontES and self.frontNQ:
            #self.Log(str(spread2) + " is spread2 and " + str(self.esnqSMA.Current.Value) + " is ESNQSimpleMovingAverage")
            if spread2 > self.esnqSMA.Current.Value * (1 + tolerance) and not (self.Portfolio[self.frontNQ.Symbol].IsShort and self.Portfolio[self.frontES.Symbol].IsLong):       
                #self.Log(f'frontNQ is short and frontES is long: {self.Portfolio[self.frontNQ.Symbol].IsShort and self.Portfolio[self.frontES.Symbol].IsLong}')  
                self.SetHoldings(self.frontNQ.Symbol , -0.2)
                self.SetHoldings(self.frontES.Symbol , 0.2)    
                #self.Log("Absolute HOldings cost is " + str(self.Portfolio.TotalAbsoluteHoldingsCost))
                '''if (data.ContainsKey("SCF/CME_NQ1_ON") and data.ContainsKey("SCF/CME_ES1_ON")):    
                    self.Plot("Spread Plot", "Long Spread Trade", data[self.nq].Price - data[self.es].Price)'''
            elif spread2 < self.esnqSMA.Current.Value * (1 - tolerance) and not (self.Portfolio[self.frontNQ.Symbol].IsLong and self.Portfolio[self.frontES.Symbol].IsShort):
                #self.Log(f'frontNQ is short and frontES is long: {self.Portfolio[self.frontNQ.Symbol].IsShort and self.Portfolio[self.frontES.Symbol].IsLong}')
                self.SetHoldings(self.frontNQ.Symbol , 0.2)
                self.SetHoldings(self.frontES.Symbol , -0.2)    
                #self.Log("Absolute HOldings cost is " + str(self.Portfolio.TotalAbsoluteHoldingsCost))
                '''if (data.ContainsKey("SCF/CME_NQ1_ON") and data.ContainsKey("SCF/CME_ES1_ON")):
                    self.Plot("Spread Plot", "Short Spread Trade", data[self.nq].Price - data[self.es].Price)'''
            if self.Portfolio[self.frontNQ.Symbol].IsShort and self.Portfolio[self.frontES.Symbol].IsLong and spread2 < self.esnqema.Current.Value:
                #self.Log(f'Is it time to Liquidate?: {self.Portfolio[self.frontNQ.Symbol].IsShort and self.Portfolio[self.frontES.Symbol].IsLong}')  
                self.Liquidate(self.frontES.Symbol)
                self.Liquidate(self.frontNQ.Symbol)
                #self.Log("Liquidated")
            if self.Portfolio[self.frontNQ.Symbol].IsLong and self.Portfolio[self.frontES.Symbol].IsShort and spread2 > self.esnqema.Current.Value:
                #self.Log(f'Is it time to Liquidate?: {self.Portfolio[self.frontNQ.Symbol].IsShort and self.Portfolio[self.frontES.Symbol].IsLong}')
                self.Liquidate(self.frontES.Symbol)
                self.Liquidate(self.frontNQ.Symbol)
                #self.Log("Liquidated")
        
    '''def OnOrderEvent(self, orderEvent):
        self.Log(str(orderEvent))'''
            
class QuandlFuture(PythonQuandl):
    '''Custom quandl data type for setting customized value column name. Value column is used for the primary trading calculations and charting.'''
    def __init__(self):
        # Define ValueColumnName: cannot be None, Empty or non-existant column name
        # If ValueColumnName is "Close", do not use PythonQuandl, use Quandl:
        # self.AddData[QuandlFuture](self.crude, Resolution.Daily)
        self.ValueColumnName = "Settle"             
class WTI(PythonData):
    "Class to import WTI Spot Price(Dollars per Barrel) data from Dropbox"
    
    def GetSource(self, config, date, isLiveMode):
        return SubscriptionDataSource("https://www.dropbox.com/s/jpie3z6j0stp97d/wti-crude-oil-prices-10-year-daily.csv?dl=1", SubscriptionTransportMedium.RemoteFile)

    def Reader(self, config, line, date, isLiveMode):
        if not (line.strip() and line[1].isdigit()): return None
        index = WTI()
        index.Symbol = config.Symbol
        try:
            # Example File Format: (Data starts from 08/11/2008)
            # date     value
            # 8/11/08    114.44
            data = line.split(',')
            index.Time = datetime.strptime(data[0], "%Y-%m-%d")
            index.Value = Decimal(data[1])
        except:
            return None
            
        return index

class BRENT(PythonData):
    "Class to import BRENT Spot Price(Dollars per Barrel) data from Dropbox"
    
    def GetSource(self, config, date, isLiveMode):
        return SubscriptionDataSource("https://www.dropbox.com/s/w380c4n7xjmdqxl/brent-crude-oil-prices-10-year-daily.csv?dl=1", SubscriptionTransportMedium.RemoteFile)

    def Reader(self, config, line, date, isLiveMode):
        if not (line.strip() and line[1].isdigit()): return None
        index = BRENT()
        index.Symbol = config.Symbol
        try:
            # Example File Format: (Data starts from 08/11/2008)
            # date     value
            # 8/11/08    110.54
            data = line.split(',')
            index.Time = datetime.strptime(data[0], "%Y-%m-%d")
            index.Value = Decimal(data[1])
        except:
            return None
            
        return index