Overall Statistics
Total Trades
710
Average Win
0.58%
Average Loss
-0.20%
Compounding Annual Return
230.802%
Drawdown
16.000%
Expectancy
1.521
Net Profit
185.438%
Sharpe Ratio
2.8
Loss Rate
35%
Win Rate
65%
Profit-Loss Ratio
2.89
Alpha
0.841
Beta
0.236
Annual Standard Deviation
0.31
Annual Variance
0.096
Information Ratio
2.404
Tracking Error
0.313
Treynor Ratio
3.679
Total Fees
$13955.22
import statsmodels.api as sm
import numpy

MAX_LEVERAGE = 1.0

alpha_samples = 10

volatility_samples = 15

long_only = True

MAX_VOLATILITY = 0.10

class BasicTemplateAlgorithm(QCAlgorithm):
    
    def __init__(self):
        
        pass
    
    def Initialize(self):
    
        self.SetCash(100000)
        
        self.SetStartDate(2017,1,1)
    
        #self.SetEndDate(2007,12,31)
        
        self.SetBrokerageModel(BrokerageName.GDAX)
        
        self.SetBenchmark("BTCUSD")

        #self.universe = ["BTCUSD"]
        self.universe = ["BTCUSD", "ETHUSD", "LTCUSD"]
        #self.universe = ["BTCUSD", "ETHBTC", "LTCBTC"]
        #self.universe = ["BTCUSD", "ETHBTC", "LTCBTC","ETHUSD","LTCUSD"]
    
        for symbol in self.universe:
            self.AddCrypto(symbol, Resolution.Minute)

        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(0, 1), Action(self.Rebalance))
    
    def OnData(self, slice):
        
        pass
    
    def min_variance(self, returns):
    
        cov = numpy.cov(returns)
        vu = numpy.ones(cov.shape[0])
        if (numpy.linalg.det(cov) != 0):
            num = numpy.linalg.solve(cov,vu)
            den = numpy.dot(vu, num)
            w = num / den
            return w / numpy.sum(numpy.absolute(w))
        else:
            return None
            
    def Rebalance(self):
        
        #try:
           
            last_price = {}
            prices = {}
            returns = {}
            for symbol in self.universe:
                last_price[symbol] = float(self.Securities[symbol].Price)
                prices[symbol] = numpy.asarray([float(x.Price) for x in self.History(symbol,alpha_samples,Resolution.Daily)] + [last_price[symbol]])
                returns[symbol] = numpy.nan_to_num(prices[symbol][1:] / prices[symbol][:-1] - 1.0)
 
            """
            TODO
            if (len(btc_returns) == len(eth_returns) == len(ltc_returns)):
                security_returns = numpy.vstack([btc_returns,eth_returns,ltc_returns])
            else:
                self.Log("Missing data")    
                return
            """
            security_returns = numpy.asarray([returns[symbol] for symbol in self.universe])
            
            if (len(security_returns) == 1):
                pfolio_weights = numpy.asarray([1.0])
            else:
                pfolio_weights = self.min_variance(security_returns[:,-alpha_samples:])
                if (pfolio_weights is None):
                    self.Log("Min_variance error")    
                    return
        
            # long only
            if (long_only == True):
                index = 0
                for weight,symbol in zip(pfolio_weights,symbol):
                    if (weight < 0):
                        if (symbol[-3:] == "BTC"):
                            pfolio_weights[0] -= weight # convention BTCUSD is first
                        pfolio_weights[index] = 0    
                    index += 1
                    
            pfolio_returns = numpy.nan_to_num((pfolio_weights[:,None]*security_returns).sum(axis=0))
        
            #self.Log("{0} {1} {2} {3} {4}".format(pfolio_returns[-1], pfolio_returns[-2], pfolio_returns[-3], pfolio_returns[-4], pfolio_returns[-5]))
            
            pfolio_std = numpy.std(pfolio_returns[-volatility_samples:])
            #pfolio_std = numpy.std(numpy.clip(pfolio_returns[-volatility_samples:],None,0.0))

            leverage = numpy.clip(MAX_VOLATILITY / (pfolio_std * numpy.sqrt(252)),0.0,MAX_LEVERAGE)
            #leverage = MAX_LEVERAGE
            
            for symbol,weight in zip(self.universe,pfolio_weights):
                if ((weight * leverage * float(self.Portfolio.TotalPortfolioValue) ) / last_price[symbol] < 0.01):
                    weight = 0.0

                self.SetHoldings(symbol, weight * leverage)
                self.Log("{0}  {1: 4.2f} % {2: 4.2f} ".format(symbol, (weight * leverage) / MAX_LEVERAGE * 100.0, last_price[symbol]))
            
            self.Log("CASH {0: 4.2f} %".format((MAX_LEVERAGE - leverage) / MAX_LEVERAGE * 100.0))
        
            
        #except Exception as e: pass