Overall Statistics |
Total Trades 206 Average Win 0.15% Average Loss -0.20% Compounding Annual Return -1.022% Drawdown 4.400% Expectancy -0.154 Net Profit -1.701% Sharpe Ratio -0.29 Probabilistic Sharpe Ratio 2.215% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 0.77 Alpha -0.005 Beta -0.029 Annual Standard Deviation 0.023 Annual Variance 0.001 Information Ratio -0.486 Tracking Error 0.137 Treynor Ratio 0.234 Total Fees $654.55 Estimated Strategy Capacity $1200000000.00 Lowest Capacity Asset ZM XAB1S6F46C6D |
#region imports from AlgorithmImports import * #endregion # https://quantpedia.com/Screener/Details/118 from QuantConnect.Python import PythonQuandl import numpy as np class TimeSeriesMomentumEffect(QCAlgorithm): def Initialize(self): self.SetStartDate(2018, 1, 1) self.SetEndDate(2019, 9, 1) self.SetCash(1000000) symbols = [Futures.Meats.LiveCattle, Futures.Meats.LeanHogs, Futures.Energies.BrentCrude, Futures.Energies.LowSulfurGasoil, Futures.Softs.Cotton2, Futures.Softs.Coffee, Futures.Softs.Cocoa, Futures.Softs.Sugar11, Futures.Grains.Wheat, Futures.Grains.Corn, Futures.Grains.Soybeans, Futures.Grains.SoybeanMeal, Futures.Grains.SoybeanOil, ] # Last trading date tracker to achieve rebalancing the portfolio every month self.RebalancingTime = datetime.min self.period = 252 self.symbol_data = {} for symbol in symbols: future = self.AddFuture(symbol, resolution = Resolution.Daily, extendedMarketHours = True, dataNormalizationMode = DataNormalizationMode.BackwardsRatio, dataMappingMode = DataMappingMode.OpenInterest, contractDepthOffset = 0 ) future.SetLeverage(1) self.symbol_data[future.Symbol] = SymbolData(self, future, self.period) def OnData(self, data): ''' Monthly rebalance at the beginning of each month. ''' # Rollover for future contract mapping change for symbol, symbol_data in self.symbol_data.items(): if data.SymbolChangedEvents.ContainsKey(symbol): changed_event = data.SymbolChangedEvents[symbol] old_symbol = changed_event.OldSymbol new_symbol = changed_event.NewSymbol tag = f"Rollover - Symbol changed at {self.Time}: {old_symbol} -> {new_symbol}" quantity = self.Portfolio[old_symbol].Quantity # Rolling over: to liquidate any position of the old mapped contract and switch to the newly mapped contract self.Liquidate(old_symbol, tag = tag) self.MarketOrder(new_symbol, quantity // self.Securities[new_symbol].SymbolProperties.ContractMultiplier, tag = tag) # skip if less than 30 days passed since the last trading date if self.Time < self.RebalancingTime: return # dataframe that contains the historical data for all securities history = self.History(list(self.symbol_data.keys()), self.period, Resolution.Daily) history = history.droplevel([0]).unstack(level=0).close.pct_change().dropna() # Compute the inverse of the volatility # The weights are the normalized inverse of the volatility vol_inv = 1 / history.std(ddof=1) vol_sum = vol_inv.sum() weights = (vol_inv / vol_sum).fillna(0).to_dict() #np.sign(roc.Current.Value) tells us whether to long or short for symbol, symbol_data in self.symbol_data.items(): symbol_id = symbol.ID.ToString() if symbol_id in weights: weight = np.sign(symbol_data.Value) * weights[symbol_id] *.5 mapped = symbol_data.Mapped qty = self.CalculateOrderQuantity(mapped, np.clip(weight, -1, 1)) multiplier = self.Securities[mapped].SymbolProperties.ContractMultiplier order_qty = (qty - self.Portfolio[mapped].Quantity) // multiplier self.MarketOrder(mapped, order_qty) # Set next rebalance time self.RebalancingTime = Expiry.EndOfMonth(self.Time) class SymbolData: def __init__(self, algorithm, future, period): self._future = future self.Symbol = future.Symbol self.ROC = algorithm.ROC(future.Symbol, period) algorithm.WarmUpIndicator(future.Symbol, self.ROC, Resolution.Daily) @property def Value(self): return self.ROC.Current.Value @property def Mapped(self): return self._future.Mapped @property def IsReady(self): return self.ROC.IsReady