| Overall Statistics |
|
Total Trades 26323 Average Win 0.10% Average Loss -0.10% Compounding Annual Return 108.051% Drawdown 71.000% Expectancy 0.140 Net Profit 468.636% Sharpe Ratio 1.601 Probabilistic Sharpe Ratio 54.229% Loss Rate 43% Win Rate 57% Profit-Loss Ratio 1.01 Alpha 0.632 Beta 0.739 Annual Standard Deviation 0.74 Annual Variance 0.548 Information Ratio 0.723 Tracking Error 0.606 Treynor Ratio 1.602 Total Fees $51048.62 Estimated Strategy Capacity $46000.00 Lowest Capacity Asset TOMOUSD 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)