Overall Statistics
Total Trades
1593
Average Win
0.17%
Average Loss
-0.09%
Compounding Annual Return
239.800%
Drawdown
31.400%
Expectancy
0.976
Net Profit
113.892%
Sharpe Ratio
3.314
Probabilistic Sharpe Ratio
83.051%
Loss Rate
34%
Win Rate
66%
Profit-Loss Ratio
1.98
Alpha
1.023
Beta
0.536
Annual Standard Deviation
0.486
Annual Variance
0.236
Information Ratio
1.123
Tracking Error
0.458
Treynor Ratio
3.004
Total Fees
$729.34
import numpy as np
import pandas as pd
from collections import deque

class QuantumOptimizedPrism(QCAlgorithm):

    def Initialize(self):
        
        '''Look back for breakout'''
        self.Lookback = 175
        
        self.SetBrokerageModel(BrokerageName.Bitfinex, AccountType.Cash)
        self.SetCash(10000)
        self.symbolList = ["BTCUSD","ETHUSD","LTCUSD"]
        self.rollingWindow = {}
        self.weights = {}
        self.flashcheck = {}
        
        for name in self.symbolList:
            self.AddCrypto(name, Resolution.Hour, Market.GDAX)
            self.rollingWindow["close_top_{0}".format(name)] = deque(maxlen=self.Lookback)
            self.weights[name] =  1 / len(self.symbolList)
            self.flashcheck[name] = 0
            
        self.SetStartDate(2020, 1, 1)
        self.SetBenchmark("BTCUSD")
        self.SetWarmUp(self.Lookback)
        
    def flashcrashcheck(self, symbol, price):
        '''Check for significant price change'''
        pchange = (price - self.flashcheck[symbol]) /  ((price + self.flashcheck[symbol])/2)  * 100
        self.flashcheck[symbol] = price
        
        if pchange >= 10:
            flash = True
            self.Debug("{} - FlashCrash: {}".format(self.Time, pchange))
        else:
            flash = False
            
        return flash
        
    def indicator(self, sym):
        '''Rolling quantile for upper and lower bounds'''
        top = pd.Series(self.rollingWindow["close_top_"+str(sym)]).quantile(0.99)
        bot = pd.Series(self.rollingWindow["close_top_"+str(sym)]).quantile(0.01)
        
        return top, bot

    def OnData(self, data):
        
        '''The data is bugged on this day for BTC'''
        if self.Time.day == 10 and self.Time.month == 8 and self.Time.year == 2018:
            return
        
        for symbol in self.symbolList:
            sym_price = data[symbol].Price
            stop = self.flashcrashcheck(symbol, sym_price)
            self.rollingWindow["close_top_{0}".format(symbol)].appendleft(sym_price)
            
            if not self.IsWarmingUp and not stop:
                top, bot = self.indicator(symbol)
                
                if sym_price >= top:
                    self.SetHoldings(symbol, self.weights[symbol])
                    
                elif sym_price <= bot:
                    self.SetHoldings(symbol, 0)
                else:
                    pass

            
    def OnOrderEvent(self, orderEvent):
        self.Debug("{} {}".format(self.Time, orderEvent.ToString()))

    def OnEndOfAlgorithm(self):
        self.Log("{} - TotalPortfolioValue: {}".format(self.Time, self.Portfolio.TotalPortfolioValue))
        self.Log("{} - CashBook: {}".format(self.Time, self.Portfolio.CashBook))