Overall Statistics
Total Orders
359
Average Win
5.15%
Average Loss
-0.80%
Compounding Annual Return
53.984%
Drawdown
34.800%
Expectancy
2.183
Start Equity
100000.00
End Equity
868570.26
Net Profit
768.570%
Sharpe Ratio
1.328
Sortino Ratio
1.454
Probabilistic Sharpe Ratio
66.661%
Loss Rate
57%
Win Rate
43%
Profit-Loss Ratio
6.41
Alpha
0.233
Beta
0.178
Annual Standard Deviation
0.293
Annual Variance
0.086
Information Ratio
-0.824
Tracking Error
0.598
Treynor Ratio
2.192
Total Fees
$0.00
Estimated Strategy Capacity
$870000.00
Lowest Capacity Asset
BSVUSD E3
Portfolio Turnover
0.41%
Drawdown Recovery
330
# region imports
from AlgorithmImports import *
# endregion

class CasualOrangeEagle(QCAlgorithm):

    def initialize(self): #all crypto use UTC time zone 
        self.set_start_date(2015, 1, 1)
        self.set_end_date(2020, 1, 1)
        self.set_cash(100000)

        self.AddRiskManagement(TrailingStopRiskManagementModel(0.40))

        self.Settings.FreePortfolioValuePercentage = 0.025
        self.positionSizeUSD = 10000
        self.rsiEntryThreshold = 60
        self.rsiExitThreshold = 40
        self.minimumVolume = 1000000

        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 = []
        for ticker in universe:
            try:
                self.pairs.append(self.Pair(self, ticker, self.minimumVolume))
            except Exception as e:
                self.Debug(f"Skipping {ticker}: {e}")
        self.SetBenchmark("BTCUSD")
        self.SetWarmup(30)

    def on_data(self, data: Slice):
        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 Below Threshold")
                continue

            if not pair.Investable():
                continue

            if rsi > self.rsiEntryThreshold and self.Portfolio.MarginRemaining > self.positionSizeUSD:
                self.Buy(symbol, self.positionSizeUSD / self.Securities[symbol].Price)
                if rsi < self.rsiExitThreshold:
                    self.liquidate(symbol, "RSI Below Threshold")
            elif rsi < self.rsiExitThreshold and self.Portfolio.MarginRemaining > self.positionSizeUSD:
                self.Sell(symbol, self.positionSizeUSD / self.Securities[symbol].Price) 
                if rsi > self.rsiEntryThreshold:
                    self.liquidate(symbol, "RSI Below Threshold")    


    class Pair:
        def __init__(self, algorithm, ticker, minimumVolume):
            self.symbol = algorithm.AddCrypto(ticker, Resolution.DAILY, Market.BITFINEX).Symbol
            self.rsi = algorithm.RSI(self.symbol, 7, MovingAverageType.SIMPLE, Resolution.DAILY)
            self.volume = IndicatorExtensions.times(algorithm.SMA(self.symbol, 50, Resolution.DAILY, Field.VOLUME), algorithm.SMA(self.symbol, 50, Resolution.DAILY, Field.CLOSE))
            self.minimumVolume = minimumVolume


        def Investable(self):
            return (self.volume.Current.Value > self.minimumVolume)