Overall Statistics
Total Trades
753
Average Win
17.09%
Average Loss
-22.90%
Compounding Annual Return
3.366%
Drawdown
83.300%
Expectancy
-0.418
Net Profit
104.967%
Sharpe Ratio
0.266
Loss Rate
67%
Win Rate
33%
Profit-Loss Ratio
0.75
Alpha
0.06
Beta
0.387
Annual Standard Deviation
0.346
Annual Variance
0.12
Information Ratio
0.022
Tracking Error
0.357
Treynor Ratio
0.238
Total Fees
$0.00
from QuantConnect.Python import PythonQuandl
import numpy as np

class CommodityFutureTrendFollowing(QCAlgorithm):
    '''
    Two Centuries of Trend Following

    This paper demonstrates the existence of anomalous excess returns based on trend following strategies
    across commodities, currencies, stock indices, and bonds, and over very long time scales.

    Reference:
    [1] Y. Lempérière, C. Deremble, P. Seager, M. Potters, J. P. Bouchaud, "Two centuries of trend following", April 15, 2014.
        URL: https://arxiv.org/pdf/1404.3274.pdf
    '''
    def Initialize(self):

        self.SetStartDate(1998,1, 1)
        self.SetEndDate(2019, 9, 1)
        self.SetCash(25000)

        tickers = ["CHRIS/CME_W1",   # Wheat Futures, Continuous Contract #1
                   "CHRIS/CME_C1",   # Corn Futures, Continuous Contract #1
                   "CHRIS/CME_LC1",  # Live Cattle Futures, Continuous Contract #1 
                   "CHRIS/CME_CL1",  # Crude Oil Futures, Continuous Contract #1
                   "CHRIS/CME_NG1",  # Natural Gas (Henry Hub) Physical Futures, Continuous Contract #1
                   "CHRIS/LIFFE_W1", # White Sugar Future, Continuous Contract #1
                   "CHRIS/CME_HG1"]  # Copper Futures, Continuous Contract #1

        # Container to store the SymbolData object for each security
        self.Data = {}

        for ticker in tickers:
            # Add Quandl data and set desired leverage
            data = self.AddData(QuandlFutures, ticker, Resolution.Daily)
            data.SetLeverage(3) 

            # Create a monthly consolidator for each security
            self.Consolidate(data.Symbol, CalendarType.Monthly, self.CalendarHandler)

            # Create a SymbolData object for each security to store relevant indicators 
            # and calculate quantity of contracts to Buy/Sell
            self.Data[data.Symbol] = SymbolData()

        # Set decay rate equal to 5 months (150 days) and warm up period
        self.SetWarmUp(150)

        # Set monthly rebalance
        self.nextRebalance = self.Time

    def CalendarHandler(self, bar):
        '''
        Event Handler that updates the SymbolData object for each security when a new monthly bar becomes available
        '''
        self.Data[bar.Symbol].Update(bar)

    def OnData(self, data):
        '''
        Buy/Sell security every month
        '''
        if self.Time < self.nextRebalance or self.IsWarmingUp:
            return

        for symbol in data.Keys:
            symbolData = self.Data[symbol]
            if symbolData.Quantity != 0:
                self.MarketOrder(symbol, symbolData.Quantity)

        self.nextRebalance = Expiry.EndOfMonth(self.Time)


class QuandlFutures(PythonQuandl):
    def __init__(self):
        self.ValueColumnName = "Settle"


class SymbolData:
    '''
    Contains the relevant indicators used to calculate number of contracts to Buy/Sell
    '''
    def __init__(self):
        self.ema = ExponentialMovingAverage("MonthEMA", 5)

        # Volatility estimation is defined as the EMA of absolute monthly price changes
        # Use Momentum indicator to get absolute monthly price changes
        # Then use the IndicatorExtensions.EMA and pass the momentum indicator values to get the volatility
        self.mom = Momentum("MonthMOM", 1)
        # Note: self.vol will automatically be updated with self.mom
        self.vol = IndicatorExtensions.EMA(self.mom, 5)

        self.Quantity = 0


    def Update(self, bar):
        self.ema.Update(bar.Time, bar.Value)
        self.mom.Update(bar.Time, bar.Value)
        
        if self.ema.IsReady and self.vol.IsReady:
            # Equation 1 in [1]
            signal = (bar.Value - self.ema.Current.Value) / self.vol.Current.Value
            # Equation 2 in [1]
            self.Quantity = np.sign(signal)/abs(self.vol.Current.Value)

        return self.Quantity != 0