Overall Statistics
Total Trades
4459
Average Win
0.40%
Average Loss
-0.41%
Compounding Annual Return
89.939%
Drawdown
46.500%
Expectancy
0.166
Net Profit
206.252%
Sharpe Ratio
1.478
Probabilistic Sharpe Ratio
54.403%
Loss Rate
41%
Win Rate
59%
Profit-Loss Ratio
0.98
Alpha
0.853
Beta
-0.005
Annual Standard Deviation
0.572
Annual Variance
0.327
Information Ratio
-0.706
Tracking Error
0.831
Treynor Ratio
-155.833
Total Fees
$0.00
Estimated Strategy Capacity
$470000.00
Lowest Capacity Asset
ZECUSD E3
class CreativeRedHornet(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 1, 1)
        self.SetEndDate(2021, 9, 28)
        self.SetCash(100000)
        self.positionSizeUSD = 10000
        self.Settings.FreePortfolioValuePercentage = 0.05
        
        self.rsiEntryThreshold = 10 # enter position if rsi rises below this threshold
        self.rsiExitThreshold = 60 # exit position if rsi rises above this threshold
        self.minimumVolume = 1000000 # filters out symbols with 30 day avg daily dollar volume less than this 
        
        # add data for all tickers
        universe = ['BTCUSD', 'LTCUSD', 'ETHUSD', 'ETCUSD', 'RRTUSD', 'ZECUSD', 'XMRUSD', 'XRPUSD', 'EOSUSD', 'SANUSD', 'OMGUSD', 'NEOUSD', 'ETPUSD', 'BTGUSD', 'SNTUSD', 'BATUSD', 'FUNUSD', 'ZRXUSD', 'TRXUSD', 'REQUSD', 'LRCUSD', 'WAXUSD', 'DAIUSD', 'BFTUSD', 'ODEUSD', 'ANTUSD', 'XLMUSD', 'XVGUSD', 'MKRUSD', 'KNCUSD', 'LYMUSD', 'UTKUSD', 'VEEUSD', 'ESSUSD', 'IQXUSD', 'ZILUSD', 'BNTUSD', 'XRAUSD', 'VETUSD', 'GOTUSD', 'XTZUSD', 'MLNUSD', 'PNKUSD', 'DGBUSD', 'BSVUSD', 'ENJUSD', 'PAXUSD']
        self.pairs = [ Pair(self, ticker, self.minimumVolume) for ticker in universe ]
        self.SetBenchmark("BTCUSD") 
        self.SetWarmup(55)
 
    def ProcessLiquidations(self, data):

        for pair in self.pairs: 
            if not pair.rsi.IsReady:
                return
            
            symbol = pair.symbol
            rsi = pair.rsi.Current.Value 
            
            if self.Portfolio[symbol].Invested:
                if not pair.Investable():
                    self.Liquidate(symbol, "Not enough $ volume")
                elif rsi > self.rsiExitThreshold:
                    self.Liquidate(symbol, "RSI above threshold")
                continue
                

    def ProcessBuys(self, data):
        for pair in self.pairs: 
            if not pair.rsi.IsReady:
                return
            symbol = pair.symbol
            rsi = pair.rsi.Current.Value 
            if rsi > self.rsiEntryThreshold and self.Portfolio.MarginRemaining > self.positionSizeUSD:
                if pair.Investable():
                    self.Buy(symbol, self.positionSizeUSD / self.Securities[symbol].Price)

 
    def OnData(self, data):
        
        self.ProcessLiquidations(data)
        self.ProcessBuys(data)

   

class Pair:
    def __init__(self, algorithm, ticker, minimumVolume): 
        self.symbol = algorithm.AddCrypto(ticker, Resolution.Daily, Market.Bitfinex).Symbol
        self.rsi    = algorithm.RSI(self.symbol, 4,  MovingAverageType.Simple, Resolution.Daily)
        self.sma10 = algorithm.SMA(self.symbol, 10, Resolution.Daily, Field.Close)
        self.sma50 = algorithm.SMA(self.symbol, 50, Resolution.Daily, Field.Close)
        self.dollarVolume = IndicatorExtensions.Times(algorithm.SMA(self.symbol, 30, Resolution.Daily, Field.Volume), algorithm.SMA(self.symbol, 30, Resolution.Daily, Field.Close))
        self.minimumVolume = minimumVolume
    
    def IsInUpTrend(self):
        return (self.sma10.Current.Value > self.sma50.Current.Value)
        
    def Investable(self):
        canInvest = True 
        if self.dollarVolume.Current.Value < self.minimumVolume: 
            canInvest = False
        if not self.IsInUpTrend():
            canInvest = False
        return canInvest