| 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)