Overall Statistics
Total Trades
39
Average Win
1.32%
Average Loss
-0.58%
Compounding Annual Return
-0.440%
Drawdown
6.000%
Expectancy
-0.270
Net Profit
-1.318%
Sharpe Ratio
-0.086
Probabilistic Sharpe Ratio
1.108%
Loss Rate
78%
Win Rate
22%
Profit-Loss Ratio
2.28
Alpha
-0.008
Beta
0.005
Annual Standard Deviation
0.03
Annual Variance
0.001
Information Ratio
-1.871
Tracking Error
0.625
Treynor Ratio
-0.513
Total Fees
$965.83
Estimated Strategy Capacity
$280000.00
Lowest Capacity Asset
AGLDUSD XJ
# This guy is using a strategy for cypto that looks at relative strength out of a basket of cryptos to pick the best ones.
# Could be a good starting point.
# https://www.youtube.com/watch?v=yuZBBX47xK0&list=PLtqRgJ_TIq8Y6YG8G-ETIFW_36mvxMLad&index=14&t=135s

import pandas as pd
from io import StringIO

class JumpingRedOrangeCobra(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2019, 1, 1)  # Set Start Date
        self.SetEndDate(2022, 1, 1)  #Set End Date
        self.SetCash(100000)  # Set Strategy Cash
        
        # SetBrokerageModel accounts for fees, etc. When commented out Fees are $0, so seems to check out.
        # Note: test with and without this line, some people said it was screwing up backtests in the forum
        self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash)
        
        # Helper variables
        self.Settings.FreePortfolioValuePercentage = 0.05  # Reserve 5% of the portfolio to always be in cash (helps for paying fees without overdraft, etc.)
        self.positionSizeUSD = 5000  # Allocation of single position
        self.rsiEntryThreshold = 70  # RSI Entry Value
        self.rsiExitThreshold = 60  # RSI Exit Value
        self.minimumVolume = 1000000  # Min volume for filtering out illiquid cryptos
        
        # List of all the USD pairs of cryptos on the exchange (need to figure out if there is a way to do this programatically)
        # Check this CSV for available coins: https://github.com/QuantConnect/Lean/blob/master/Data/symbol-properties/symbol-properties-database.csv
        # Manual Universe:
        # universe = ['BTCUSD', 'LTCUSD', 'ETHUSD', 'YFIUSD', 'MKRUSD', 'LINKUSD', 'UNIUSD', 'BCHUSD']
        
        # Try to pull the universe from the CSV file: WHY DOESN'T THIS WORK!?
        url = 'https://raw.githubusercontent.com/QuantConnect/Lean/master/Data/symbol-properties/symbol-properties-database.csv'
        content = self.Download(url)
        stringData = StringIO(content)
        df = pd.read_csv(stringData)
        gdax = df[df["market"].str.contains('gdax')]
        gdax = gdax[gdax["symbol"].str.endswith("USD")].reset_index()
        universe = gdax['symbol'].tolist()
        
        
        # Iterate through the list of these tickers using the Pair class attributes below
        self.pairs = [ Pair(self, ticker, self.minimumVolume) for ticker in universe ]
        self.SetBenchmark("BTCUSD")   # Set benchmark to BTC
        self.SetWarmup(30)  # Set warmup to 30 bars since our SMAs require this length of time to calculate
        
        
    def OnData(self, data):
        for pair in self.pairs:
            if not pair.rsi.IsReady:  # make sure RSI is ready
                return
            
            # Put symbol and RSI in a local variable since we'll need those in the next few lines
            symbol = pair.symbol
            rsi = pair.rsi.Current.Value
            
            # Check if we are invested in a given symbol and see if we might want to sell because of low volume or low RSI
            if self.Portfolio[symbol].Invested:
                if not pair.Investable():
                    self.Liquidate(symbol, "Not enough volume") # These tags show up in the orders list
                elif rsi < self.rsiExitThreshold:
                    self.Liquidate(symbol, "RSI below threshiold")
                continue
            
            # We can now assume we have no active position in the pair in question so we need to see if we should enter it
            # Is the pair investable (i.e. volume checks)? If not continue out of the for loop.
            if not pair.Investable():
                continue
            
            # If the pair passes the investable check, now check to see if the RSI is above our threshold and if we have enough money. If so, buy it
            if rsi > self.rsiEntryThreshold and self.Portfolio.MarginRemaining > self.positionSizeUSD:
                self.Buy(symbol, self.positionSizeUSD / self.Securities[symbol].Price)
        
# Since we want to access the properties of the above tickers, create a class to refer to these properties
class Pair:
    def __init__(self, algorithm, ticker, minimumVolumne):
        self.symbol = algorithm.AddCrypto(ticker, Resolution.Daily, Market.GDAX).Symbol
        self.rsi = algorithm.RSI(self.symbol, 14, MovingAverageType.Simple, Resolution.Daily)
        # Volume times price tells us average daily volume in dollars
        self.volume = IndicatorExtensions.Times(algorithm.SMA(self.symbol, 30, Resolution.Daily, Field.Volume),
                                                algorithm.SMA(self.symbol, 30, Resolution.Daily, Field.Close))
        self.minimumVolume = minimumVolumne
        
    # Create method the returns true if the instrument is investable (i.e. passes our volume tests)
    def Investable(self):
        return (self.volume.Current.Value > self.minimumVolume)  # returns true if investable, otherwise false