Overall Statistics
Total Trades
6605
Average Win
0.32%
Average Loss
-0.50%
Compounding Annual Return
124.196%
Drawdown
34.600%
Expectancy
0.245
Net Profit
1461.773%
Sharpe Ratio
2.066
Probabilistic Sharpe Ratio
84.915%
Loss Rate
24%
Win Rate
76%
Profit-Loss Ratio
0.63
Alpha
0.741
Beta
0.401
Annual Standard Deviation
0.458
Annual Variance
0.209
Information Ratio
0.811
Tracking Error
0.537
Treynor Ratio
2.358
Total Fees
$38883.53
Estimated Strategy Capacity
$33000.00
Lowest Capacity Asset
DASHBTC XJ
import numpy as np
import pandas as pd
from collections import deque

class QuantumOptimizedPrism(QCAlgorithm):

    def Initialize(self):
        
        '''Look back for breakout'''
        self.Lookback = 168
        
        self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash)
        self.SetCash(15000)
        self.symbolList = ["BTCUSD", "ETHUSD", "BCHUSD", "LINKETH", "ETHBTC",
                            "BCHBTC", "ATOMBTC", "DASHBTC", "UNIUSD", "XLMBTC",
                            "MKRBTC", "LINKUSD", "ATOMUSD"]
                            
        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] =  0.25
            self.flashcheck[name] = 0
            
        self.SetStartDate(2018, 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.Log("{} - 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):
        if self.IsWarmingUp: return
        #if not self.rollingWindow.empty
        '''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:
            if not data.ContainsKey(symbol): return
            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):
        '''order = self.Transactions.GetOrderById(orderEvent.OrderId)
        for symbol in self.symbolList:
            if orderEvent.Status == OrderStatus.Invalid: 
                self.SetHoldings(symbol, 0.01*0.1)
        '''