Overall Statistics |
Total Trades 19321 Average Win 0.11% Average Loss -0.10% Compounding Annual Return 266.796% Drawdown 68.400% Expectancy 0.332 Net Profit 1255.009% Sharpe Ratio 3.035 Probabilistic Sharpe Ratio 85.668% Loss Rate 39% Win Rate 61% Profit-Loss Ratio 1.17 Alpha 1.403 Beta 0.649 Annual Standard Deviation 0.709 Annual Variance 0.502 Information Ratio 1.603 Tracking Error 0.624 Treynor Ratio 3.312 Total Fees $32066.91 Estimated Strategy Capacity $120000.00 Lowest Capacity Asset MOBUSD 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.Portfolio.Keys: pnl = self.Securities[symbol].Holdings.UnrealizedProfitPercent if pnl < -0.30: self.Liquidate(symbol) targets.append(PortfolioTarget(symbol, weight)) if targets: self.SetHoldings(targets)