Overall Statistics |
Total Orders 146 Average Win 10.68% Average Loss -10.44% Compounding Annual Return -55.949% Drawdown 78.600% Expectancy -0.046 Start Equity 10000000 End Equity 4394766.78 Net Profit -56.052% Sharpe Ratio -0.314 Sortino Ratio -0.391 Probabilistic Sharpe Ratio 5.246% Loss Rate 53% Win Rate 47% Profit-Loss Ratio 1.02 Alpha -0.24 Beta -0.017 Annual Standard Deviation 0.769 Annual Variance 0.591 Information Ratio -0.41 Tracking Error 0.777 Treynor Ratio 14.259 Total Fees $76883.69 Estimated Strategy Capacity $840000.00 Lowest Capacity Asset PA WHL6E0BVU5KX Portfolio Turnover 55.38% |
#region imports from AlgorithmImports import * from math import floor #endregion # https://quantpedia.com/Screener/Details/23 class CommodityMomentumCombinedWithTermStructureAlgorithm(QCAlgorithm): def initialize(self): self.set_start_date(2016, 1, 1) self.set_end_date(2017, 1, 1) self.set_cash(10000000) tickers = [ Futures.Softs.COCOA, Futures.Softs.COFFEE, Futures.Grains.CORN, Futures.Softs.COTTON_2, Futures.Grains.OATS, Futures.Softs.ORANGE_JUICE, Futures.Grains.SOYBEAN_MEAL, Futures.Grains.SOYBEAN_OIL, Futures.Grains.SOYBEANS, Futures.Softs.SUGAR_11, Futures.Grains.WHEAT, Futures.Meats.FEEDER_CATTLE, Futures.Meats.LEAN_HOGS, Futures.Meats.LIVE_CATTLE, Futures.Energies.CRUDE_OIL_WTI, Futures.Energies.HEATING_OIL, Futures.Energies.NATURAL_GAS, Futures.Energies.GASOLINE, Futures.Metals.GOLD, Futures.Metals.PALLADIUM, Futures.Metals.PLATINUM, Futures.Metals.SILVER ] for ticker in tickers: future = self.add_future(ticker) future.set_filter(timedelta(0), timedelta(days=90)) self.add_equity("SPY", Resolution.MINUTE) self.schedule.on(self.date_rules.month_start("SPY"), self.time_rules.after_market_open("SPY", 30), self._rebalance) def _rebalance(self): # Saves the Futures Chains chains = {} for chain in self.current_slice.future_chains: if chain.value.contracts.count < 2: continue chains[chain.value.symbol.value] = [i for i in chain.value] self.liquidate() roll_return = {} for symbol, chain in chains.items(): contracts = sorted(chain, key=lambda x: x.expiry) expiry_nearest = contracts[0].expiry price_nearest = float(contracts[0].last_price) if contracts[0].last_price>0 else 0.5*float(contracts[0].ask_price+contracts[0].bid_price) for x in contracts[1:]: roll_return[x] = (price_nearest-float(x.last_price))*365 / (x.expiry-expiry_nearest).days sorted_by_roll_return = sorted(roll_return, key=lambda x: roll_return[x], reverse =True) tertile = floor(1/3*len(sorted_by_roll_return)) high = sorted_by_roll_return[:tertile] low = sorted_by_roll_return[-tertile:] mean_return_high = {} for i in high: hist = self.history(i.symbol, timedelta(days=21), Resolution.MINUTE) if hist.empty: continue hist_close = hist['close'][i.expiry][i.symbol.id.to_string()] mean_return_high[i] = np.mean(hist_close.pct_change()) high_winners = sorted(mean_return_high, key=lambda x: mean_return_high[x], reverse=True)[:int(len(high)*0.5)] mean_return_low = {} for i in low: hist = self.history(i.symbol, timedelta(days=21), Resolution.MINUTE) if hist.empty: continue hist_close = hist['close'][i.expiry][i.symbol.id.to_string()] mean_return_low[i] = np.mean(hist_close.pct_change()) low_losers = sorted(mean_return_low, key=lambda x: mean_return_low[x], reverse=True)[-int(len(low)*0.5):] short_weight = 0.25/len(low_losers) for short in low_losers: self.set_holdings(short.symbol, -short_weight) long_weight = 0.25/len(high_winners) for long_ in high_winners: self.set_holdings(long_.symbol, long_weight)