Overall Statistics |
Total Trades 17647 Average Win 0.09% Average Loss -0.09% Compounding Annual Return 87.405% Drawdown 35.200% Expectancy 0.250 Net Profit 457.436% Sharpe Ratio 1.636 Probabilistic Sharpe Ratio 68.472% Loss Rate 38% Win Rate 62% Profit-Loss Ratio 1.01 Alpha 0.541 Beta 0.315 Annual Standard Deviation 0.419 Annual Variance 0.176 Information Ratio 0.404 Tracking Error 0.559 Treynor Ratio 2.18 Total Fees $30759.34 Estimated Strategy Capacity $4000.00 Lowest Capacity Asset UNIUSD 2MN |
from AlgorithmImports import * class RebalancingPremiumsinCryptos(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 1, 1) #self.SetEndDate(2020, 6, 1) self.SetCash(100000) self.SetBrokerageModel(BrokerageName.FTX, AccountType.Cash) self.Settings.FreePortfolioValuePercentage = 0.05 self.week = -1 self.readyToTrade = False self.high = 0 self.SetWarmup(timedelta(40)) # Get crypto USD pairs available to trade on FTX self.tickers = [] url = "https://raw.githubusercontent.com/QuantConnect/Lean/master/Data/symbol-properties/symbol-properties-database.csv" spdb = self.Download(url).split('\n') # exclude FTX special pairs, stablecoins, and SPELL as it has a data issue on FTX Dec 11th 2021 matches = ["BULL", "BEAR", "HEDGE", "HALF", "SPELL", "USDCUSD", "USDTUSD", "DAIUSD","BTC","ETH","GBP","EUR"] #matches = ["DAIUSD", "USTUSD", "USDTUSD", "PAXUSD"] for line in spdb: csv = line.split(',') if csv[0] == 'ftx' and csv[1].endswith('USD') and not any(word in csv[1] for word in matches): self.tickers.append(Symbol.Create(csv[1], SecurityType.Crypto, Market.FTX)) self.bands_by_ticker = {} for symbol in self.tickers: self.bands_by_ticker[symbol] = BollingerBands(30, 2) self.AddUniverse(SecurityType.Crypto, 'Universe1', Resolution.Daily, Market.FTX, self.UniverseSettings, self.Selector) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(1, 0), self.DailyRebalance) # Configs self.stop_loss = False # Benchmark indicators self.BenchmarkCrypto = self.AddCrypto("BTCUSD", Resolution.Daily).Symbol self.macd = self.MACD("BTCUSD", 20, 50, 9, MovingAverageType.Exponential, Resolution.Daily) self.sma = self.SMA("BTCUSD", 40, Resolution.Daily) # Build universe: select the top 25 mean-reverting pairs (based on bollinger bands) with highest volume def Selector(self, dt): current_week = self.Time.isocalendar()[1] if current_week == self.week: return Universe.Unchanged self.week = current_week # 31 days so the last one is not in the BB when we look at where the price is history = self.History(self.tickers, 31, Resolution.Daily) volume_by_symbol = {} for symbol in self.tickers: try: if symbol not in history.index: continue cur_bands = self.bands_by_ticker[symbol] for time, data in history.loc[symbol].iterrows(): cur_bands.Update(time, data.close) if not cur_bands.IsReady:continue df = history.loc[symbol].iloc[-1] dollar_volume = df.close * df.volume price = df.close lower_band = cur_bands.LowerBand.Current.Value upper_band = cur_bands.UpperBand.Current.Value if math.isnan(dollar_volume) or (price < lower_band) or (price > upper_band): continue volume_by_symbol[symbol] = dollar_volume except: continue selected = sorted(volume_by_symbol.items(), key=lambda x: x[1], reverse=True) universe = [x[0].Value for x in selected][:25] # self.Debug(f"My universe: {universe}") return universe def OnSecuritiesChanged(self, changes): for security in changes.RemovedSecurities: self.Liquidate(security.Symbol) # self.Debug(f"Removed {security.Symbol} from the the universe") for security in changes.AddedSecurities: self.readyToTrade = True self.Debug(f"Added {security.Symbol} to the universe") # SetHoldings method applied daily to the symbols in ActiveSecurities def DailyRebalance(self): if self.readyToTrade: # Exit condition if self.Securities[self.BenchmarkCrypto].Price < self.sma.Current.Value: # f self.macd.Fast.Current.Value < self.macd.Slow.Current.Value: # self.Plot("Signals", "MACDExitCondition", 1) self.Liquidate() return # self.Debug(f"Daily rebalance method triggered at {self.Time}") # self.Plot("Signals", "MACDExitCondition", 0) universe_members = self.UniverseManager.Values[1].Members.get_Keys() if len(universe_members) == 0: return # weight = 1.0/len(self.ActiveSecurities) weight = 1.0/len(universe_members) targets = [] # for symbol in self.Portfolio.Keys: for symbol in universe_members: pnl = self.Securities[symbol].Holdings.UnrealizedProfitPercent if self.stop_loss and pnl < -0.30: self.Liquidate(symbol) continue targets.append(PortfolioTarget(symbol, weight)) if targets: self.SetHoldings(targets)