| 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)