Overall Statistics |
Total Trades
7806
Average Win
0.48%
Average Loss
-0.48%
Compounding Annual Return
2.968%
Drawdown
31.400%
Expectancy
0.029
Net Profit
50.643%
Sharpe Ratio
0.284
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.02
Alpha
0.036
Beta
0.016
Annual Standard Deviation
0.131
Annual Variance
0.017
Information Ratio
-0.224
Tracking Error
0.221
Treynor Ratio
2.339
Total Fees
$0.00
|
# https://quantpedia.com/strategies/short-term-reversal-with-futures/ from datetime import datetime, timedelta from collections import deque import numpy as np class Short_Term_Reversal(QCAlgorithm): def Initialize(self): self.SetStartDate(2005, 1, 1) self.SetEndDate(2019, 1, 1) self.SetCash(100000) self.symbols = ["CME_S1", # Soybean Futures, Continuous Contract "CME_W1", # Wheat Futures, Continuous Contract "CME_BO1", # Soybean Oil Futures, Continuous Contract "CME_C1", # Corn Futures, Continuous Contract "CME_LC1", # Live Cattle Futures, Continuous Contract "CME_FC1", # Feeder Cattle Futures, Continuous Contract "CME_GC1", # Gold Futures, Continuous Contract "CME_SI1", # Silver Futures, Continuous Contract "CME_PL1", # Platinum Futures, Continuous Contract "CME_CL1", # Crude Oil Futures, Continuous Contract "ICE_CC1", # Cocoa Futures, Continuous Contract "ICE_CT1", # Cotton No. 2 Futures, Continuous Contract "ICE_KC1", # Coffee C Futures, Continuous Contract "ICE_O1", # Heating Oil Futures, Continuous Contract "ICE_SB1", # Sugar No. 11 Futures, Continuous Contract "CME_BP1", # British Pound Futures, Continuous Contract #1 "CME_EC1", # Euro FX Futures, Continuous Contract #1 "CME_JY1", # Japanese Yen Futures, Continuous Contract #1 "CME_SF1", # Swiss Franc Futures, Continuous Contract #1 "CME_ES1", # E-mini S&P 500 Futures, Continuous Contract #1 "CME_TY1", # 10 Yr Note Futures, Continuous Contract #1 "CME_FV1", # 5 Yr Note Futures, Continuous Contract #1 ] self.lookup_period = 14 self.SetWarmUp(self.lookup_period) self.close = {} self.volume = {} self.open_interest = {} # True -> Quantpedia data # False -> Quandl free data - NOTE: Consider shorter backtest period in case you want to use quandl data. self.use_quantpedia_data = True if self.use_quantpedia_data: for symbol in self.symbols: data = self.AddData(QuantpediaFutures, symbol, Resolution.Daily) #data.SetLeverage(2) self.symbols = ['CHRIS/' + x for x in self.symbols] for symbol in self.symbols: self.AddData(QuandlFutures, symbol, Resolution.Daily) # Open interest and volume data if 'CME' in symbol: self.AddData(QuandlFuturesOpenInterestCME, symbol, Resolution.Daily) else: self.AddData(QuandlFuturesOpenInterest, symbol, Resolution.Daily) self.AddData(QuandlFuturesVolume, symbol, Resolution.Daily) self.close[symbol] = deque(maxlen=self.lookup_period) self.volume[symbol] = deque(maxlen=self.lookup_period) self.open_interest[symbol] = deque(maxlen=self.lookup_period) # NOTE: Need to do this because of multiple quandl symbol data integration. (settle, volume, open interest) sym = self.symbols[0] + '.QuandlFutures' self.Schedule.On(self.DateRules.Every(DayOfWeek.Wednesday), self.TimeRules.AfterMarketOpen(sym), self.Rebalance) def OnData(self, data): for symbol in self.symbols: sym = symbol + '.QuandlFutures' if self.Securities.ContainsKey(sym): # Price update price = self.Securities[sym].Close if price != 0: self.close[symbol].append(price) sym = symbol + '.QuandlFuturesVolume' if self.Securities.ContainsKey(sym): # Volume update vol = self.Securities[sym].Price self.volume[symbol].append(vol) # Open interest update open_interest_type = '' if 'CME' in symbol: open_interest_type = 'QuandlFuturesOpenInterestCME' else: open_interest_type = 'QuandlFuturesOpenInterest' sym = symbol + '.' + open_interest_type if self.Securities.ContainsKey(sym): oi = self.Securities[sym].Price self.open_interest[symbol].append(oi) def Rebalance(self): if self.IsWarmingUp: return volume_weekly_diff = {} oi_weekly_diff = {} returns = {} for symbol in self.symbols: if len(self.close[symbol]) == self.close[symbol].maxlen and len(self.volume[symbol]) == self.volume[symbol].maxlen and len(self.open_interest[symbol]) == self.open_interest[symbol].maxlen: # Return calc. prices = [x for x in self.close[symbol]] half = int(len(prices)/2) prices = prices[-half:] returns[symbol] = self.Return(prices) # Volume change calc. volumes = [x for x in self.volume[symbol]] volumes_t1 = volumes[-half:] t1_vol_mean = np.mean(volumes_t1) t1_vol_total = sum(volumes_t1) / t1_vol_mean volumes_t2 = volumes[:half] t2_vol_mean = np.mean(volumes_t2) t2_vol_total = sum(volumes_t2) / t2_vol_mean volume_weekly_diff[symbol] = t1_vol_total - t2_vol_total # Open interest change calc. interests = [x for x in self.open_interest[symbol]] t1_oi = interests[-half:] t1_oi_total = sum(t1_oi) t2_oi = interests[:half] t2_oi_total = sum(t2_oi) oi_weekly_diff[symbol] = t1_oi_total - t2_oi_total #else: return if len(returns) == 0 or len(volume_weekly_diff) == 0 or len(oi_weekly_diff) == 0: return volume_sorted = sorted(volume_weekly_diff.items(), key = lambda x: x[1], reverse = True) half = int(len(volume_sorted)/2) high_volume = [x[0] for x in volume_sorted[:half]] open_interest_sorted = sorted(oi_weekly_diff.items(), key = lambda x: x[1], reverse = True) half = int(len(open_interest_sorted)/2) low_oi = [x[0] for x in open_interest_sorted[-half:]] filtered = [x for x in high_volume if x in low_oi] sorted_filtered = sorted(filtered, key = lambda x : returns[x], reverse = True) half = int(len(sorted_filtered)/2) long = sorted_filtered[-half:] short = sorted_filtered[:half] if len(long) == 0 or len(short) == 0: return # Weighting weight = {} diff = {} avg_ret = np.average([returns[x] for x in long+short]) for symbol in long + short: diff[symbol] = np.absolute(returns[symbol] - avg_ret) if len(diff) == 0 : return total_diff = sum([x[1] for x in diff.items()]) ratio = 1 / total_diff for symbol in long + short: weight[symbol] = diff[symbol] * ratio if len(weight) == 0 : return # Trade execution self.Liquidate() for symbol in long: sym = symbol + '.QuandlFutures' if self.use_quantpedia_data: sym = symbol.split('/')[1] + '.QuantpediaFutures' self.SetHoldings(sym, weight[symbol]) for symbol in short: sym = symbol + '.QuandlFutures' if self.use_quantpedia_data: sym = symbol.split('/')[1] + '.QuantpediaFutures' self.SetHoldings(sym, -weight[symbol]) def Return(self, history): return (history[-1] - history[0]) / history[0] # Quantpedia data class QuantpediaFutures(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://quantpedia.com/backtesting_data/futures/{0}.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv) def Reader(self, config, line, date, isLiveMode): data = QuantpediaFutures() data.Symbol = config.Symbol try: if not line[0].isdigit(): return None split = line.split(';') data.Time = datetime.strptime(split[0], "%d.%m.%Y") + timedelta(days=1) data['settle'] = float(split[1]) data.Value = float(split[1]) except: return None return data # Quandl free data class QuandlFutures(PythonQuandl): def __init__(self): self.ValueColumnName = "Settle" class QuandlFuturesOpenInterest(PythonQuandl): def __init__(self): self.ValueColumnName = "Prev. Day Open Interest" class QuandlFuturesOpenInterestCME(PythonQuandl): def __init__(self): self.ValueColumnName = "Previous Day Open Interest" class QuandlFuturesVolume(PythonQuandl): def __init__(self): self.ValueColumnName = "Volume"