| Overall Statistics |
|
Total Trades 205 Average Win 15.78% Average Loss -10.59% Compounding Annual Return -98.023% Drawdown 98.700% Expectancy -0.235 Net Profit -98.045% Sharpe Ratio -0.563 Probabilistic Sharpe Ratio 0.538% Loss Rate 69% Win Rate 31% Profit-Loss Ratio 1.49 Alpha -0.854 Beta 0.506 Annual Standard Deviation 1.439 Annual Variance 2.07 Information Ratio -0.624 Tracking Error 1.439 Treynor Ratio -1.599 Total Fees $62095.80 Estimated Strategy Capacity $1000.00 Lowest Capacity Asset PA WHL6E0BVU5KX |
#region imports
from AlgorithmImports import *
#endregion
# https://quantpedia.com/Screener/Details/23
from datetime import timedelta
from math import floor
from decimal import Decimal
import numpy as np
class CommodityMomentumCombinedWithTermStructureAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2016, 1, 1)
self.SetEndDate(2017, 1, 1)
self.SetCash(10000000)
tickers = [
Futures.Softs.Cocoa,
Futures.Softs.Coffee,
Futures.Grains.Corn,
Futures.Softs.Cotton2,
Futures.Grains.Oats,
Futures.Softs.OrangeJuice,
Futures.Grains.SoybeanMeal,
Futures.Grains.SoybeanOil,
Futures.Grains.Soybeans,
Futures.Softs.Sugar11,
Futures.Grains.Wheat,
Futures.Meats.FeederCattle,
Futures.Meats.LeanHogs,
Futures.Meats.LiveCattle,
Futures.Energies.CrudeOilWTI,
Futures.Energies.HeatingOil,
Futures.Energies.NaturalGas,
Futures.Energies.Gasoline,
Futures.Metals.Gold,
Futures.Metals.Palladium,
Futures.Metals.Platinum,
Futures.Metals.Silver
]
for ticker in tickers:
future = self.AddFuture(ticker)
future.SetFilter(timedelta(0), timedelta(days = 90))
self.chains = {}
self.AddEquity("SPY", Resolution.Minute)
self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY", 30), self.Rebalance)
self.rebalance = False
def OnData(self,slice):
if not self.rebalance: return
# Saves the Futures Chains
for chain in slice.FutureChains:
if chain.Value.Contracts.Count < 2:
continue
if chain.Value.Symbol.Value not in self.chains:
self.chains[chain.Value.Symbol.Value] = [i for i in chain.Value]
self.chains[chain.Value.Symbol.Value] = [i for i in chain.Value]
self.Liquidate()
roll_return = {}
for symbol, chain in self.chains.items():
contracts = sorted(chain, key = lambda x: x.Expiry)
expiry_nearest = contracts[0].Expiry
price_nearest = float(contracts[0].LastPrice) if contracts[0].LastPrice>0 else 0.5*float(contracts[0].AskPrice+contracts[0].BidPrice)
for x in contracts[1:]:
roll_return[x] = (price_nearest-float(x.LastPrice))*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.ToString()]
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.ToString()]
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.5/len(low_losers)
for short in low_losers:
self.SetHoldings(short.Symbol, -short_weight)
long_weight = 0.5/len(high_winners)
for long in high_winners:
self.SetHoldings(long.Symbol, long_weight)
self.rebalance = False
def Rebalance(self):
self.rebalance = True