| 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