Overall Statistics |
Total Trades 657 Average Win 0.23% Average Loss -0.14% Compounding Annual Return 3.567% Drawdown 45.000% Expectancy 1.133 Net Profit 113.776% Sharpe Ratio 0.273 Loss Rate 19% Win Rate 81% Profit-Loss Ratio 1.65 Alpha 0.041 Beta 0.142 Annual Standard Deviation 0.193 Annual Variance 0.037 Information Ratio -0.127 Tracking Error 0.248 Treynor Ratio 0.371 Total Fees $0.00 |
from QuantConnect.Python import PythonQuandl from datetime import timedelta import numpy as np import pandas as pd 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 MonthlyConsolidator = self.Consolidate(ticker, CalendarType.Monthly, self.CalendarHandler) # Create a SymbolData object for each security to store relevant indicators and calculated quantity of contracts to Buy/Sell self.Data[data.Symbol] = SymbolData() # Set decay rate equal to 5 months and warm up period period = 150 self.SetWarmUp(period) # 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.IsWarmingUp: return if self.Time < self.nextRebalance: 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.Of and pass the momentum indicator values to get the volatility self.mom = Momentum("MonthMOM", 1) self.vol = IndicatorExtensions.Of(ExponentialMovingAverage("Vol", 5), self.mom) self.Quantity = 0 def Update(self, bar): self.ema.Update(bar.Time, bar.Value) self.mom.Update(bar.Time, bar.Value) self.vol.Update(bar.Time, self.mom.Current.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