Overall Statistics |
Total Trades 346 Average Win 0.96% Average Loss -0.44% Compounding Annual Return 36.942% Drawdown 20.800% Expectancy 0.870 Net Profit 87.854% Sharpe Ratio 1.628 Probabilistic Sharpe Ratio 77.987% Loss Rate 41% Win Rate 59% Profit-Loss Ratio 2.18 Alpha 0.096 Beta 0.141 Annual Standard Deviation 0.159 Annual Variance 0.025 Information Ratio -1.637 Tracking Error 0.545 Treynor Ratio 1.836 Total Fees $1696.75 Estimated Strategy Capacity $210000.00 Lowest Capacity Asset SOLUSD 2MN |
from AlgorithmImports import * class RebalancingPremiumsinCryptos(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 1, 1) self.SetEndDate(2022, 1, 1) self.SetCash(100000) self.SetBrokerageModel(BrokerageName.FTX, AccountType.Cash) self.Settings.FreePortfolioValuePercentage = 0.05 self.week = -1 self.readyToTrade = False self.high = 0 # 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"] 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.Debug(f'Current FTX Cryptos with USD {len(self.tickers)}') 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) # self.SetRiskManagement(MaximumDrawdownPercentPortfolio(0.3)) # 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: self.Debug(f"Daily rebalance method triggered at {self.Time}") weight = 1.0/len(self.ActiveSecurities) targets = [] for symbol in self.ActiveSecurities.Keys: if self.CurrentSlice.ContainsKey(symbol) and self.CurrentSlice[symbol] is not None: #Drawdown Protection - don't let any crypto drop by more than 30% on a single day if self.Portfolio.Invested: if self.CurrentSlice[symbol].High > self.high: self.high = self.CurrentSlice[symbol].High close = self.CurrentSlice[symbol].Close dd = (close - self.high) / close if dd < -0.30: self.Liquidate(symbol) self.high = 0 else: targets.append(PortfolioTarget(symbol, weight)) if targets: self.SetHoldings(targets)