Overall Statistics |
Total Trades 1062 Average Win 2.55% Average Loss -1.25% Compounding Annual Return 14143.679% Drawdown 54.000% Expectancy 0.365 Net Profit 699.376% Sharpe Ratio 67.01 Probabilistic Sharpe Ratio 97.424% Loss Rate 55% Win Rate 45% Profit-Loss Ratio 2.04 Alpha 87.091 Beta 0.765 Annual Standard Deviation 1.299 Annual Variance 1.687 Information Ratio 71.418 Tracking Error 1.22 Treynor Ratio 113.761 Total Fees $9217.08 Estimated Strategy Capacity $200000.00 Lowest Capacity Asset XRPUSD E3 |
from io import StringIO from datetime import datetime import pandas as pd import numpy as np from scipy import optimize class CasualFluorescentYellowCaterpillar(QCAlgorithm): def Initialize(self): self.SetStartDate(2021, 4, 19) # Set Start Date self.SetEndDate(2021, 9, 18) self.SetCash(4000) self.symbols = ['BTCUSD','ETHUSD', 'XRPUSD', 'SOLUSD', 'LUNAUSD'] self.warm_up = len(self.symbols) self.warm_up_count = 1 self.window = 8 self.rebalance = 8 self.last_w = [0 for i in range(len(self.symbols))] self.use_last = True #Download returns from each strategy #Ris_csv = self.Download('https://raw.githubusercontent.com/sergiosierram/SharpSignal/main/data/CryptoDataReturns.csv') #self.Ris = pd.read_csv(StringIO(Ris_csv), index_col=0) #Initialize symbols self.initializeSymbols() #self.Ris = self.Ris.to_numpy() #.transpose() #set risk free asset rate of return self.Rf=0 # April 2019 average risk free rate of return in USA approx 3% annRiskFreeRate = self.Rf/100 #compute daily risk free rate in percentage self.r0 = (np.power((1 + annRiskFreeRate), (1.0 / 360.0)) - 1.0) * 100 self.portfolioSize = len(self.symbols) self.SetBrokerageModel(BrokerageName.Bitfinex) self.SetBenchmark("BTCUSD") return def OnData(self, data): if self.warm_up_count < self.warm_up: self.warm_up_count += 1 return self.warm_up_count += 1 if self.warm_up_count % self.rebalance == 0: try: #Ri = self.Ris[0:self.warm_up_count, :] #Ei = np.mean(Ri, axis = 0) if self.window == 0: self.window = self.warm_up_count Rix = self.History(self.symbols_objects, self.window) Ri = [] for symbol in self.symbols: d = Rix.loc[symbol]['close'].to_list() Ri.append(d) Ri = np.array(Ri).transpose() Ri = StockReturnsComputing(Ri, self.window, self.portfolioSize) Ei = np.mean(Ri, axis = 0) #self.Debug(Ei) except: self.Log(str("Error during data extraction")) return cov = np.cov(Ri, rowvar=False) #initialization xOptimal =[] minRiskPoint = [] expPortfolioReturnPoint =[] maxSharpeRatio = 0 #compute maximal Sharpe Ratio and optimal weights result = MaximizeSharpeRatioOptmzn(Ei, cov, self.r0, self.portfolioSize) xOptimal.append(result.x) w = list(xOptimal[0]) w = [ 0 if wx < 0.0000001 else wx for wx in w ] Rix2 = self.History(self.symbols_objects, self.window) adjust = [] for symbol in self.symbols: d1 = Rix.loc[symbol]['close'].to_list() d1 = d1[-1] d2 = Rix2.loc[symbol]['close'].to_list() d2 = d2[-1] adjust.append(0.9-((d2-d1)/d1)) if not self.use_last: self.Liquidate() targets = [] for i in range(len(w)): currency = self.symbols[i] if not self.use_last: self.SetHoldings(currency, w[i]) else: targets.append(PortfolioTarget(currency, adjust[i]*w[i])) if self.use_last: self.SetHoldings(targets) return def initializeSymbols(self): #self.symbols = [ name+"USD" for name in self.Ris.columns.tolist()] #self.Debug(str(self.symbols)) self.symbols_objects = [] for symbol in self.symbols: data = self.AddCrypto(symbol, Resolution.Hour, Market.Bitfinex) data.SetFeeModel(CustomFeeModel(self)) self.symbols_objects.append(data.Symbol) return def OnEndOfAlgorithm(self): self.Liquidate() try: #Ri = self.Ris[0:self.warm_up_count, :] #Ei = np.mean(Ri, axis = 0) if self.window == 0: self.window = self.warm_up_count Rix = self.History(self.symbols_objects, self.window) Ri = [] for symbol in self.symbols: d = Rix.loc[symbol]['close'].to_list() Ri.append(d) Ri = np.array(Ri).transpose() Ri = StockReturnsComputing(Ri, self.window, self.portfolioSize) Ei = np.mean(Ri, axis = 0) #self.Debug(Ei) except: self.Log(str("Error during data extraction")) return cov = np.cov(Ri, rowvar=False) #initialization xOptimal =[] minRiskPoint = [] expPortfolioReturnPoint =[] maxSharpeRatio = 0 #compute maximal Sharpe Ratio and optimal weights result = MaximizeSharpeRatioOptmzn(Ei, cov, self.r0, self.portfolioSize) xOptimal.append(result.x) w = list(xOptimal[0]) w = [ 0 if wx < 0.0000001 else wx for wx in w ] self.Debug(w) # Custom fee model. class CustomFeeModel(FeeModel): def GetOrderFee(self, parameters): fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.002 return OrderFee(CashAmount(fee, "USD")) def MaximizeSharpeRatioOptmzn(MeanReturns, CovarReturns, RiskFreeRate, PortfolioSize): # define maximization of Sharpe Ratio using principle of duality def f(x, MeanReturns, CovarReturns, RiskFreeRate, PortfolioSize): funcDenomr = np.sqrt(np.matmul(np.matmul(x, CovarReturns), x.T) ) funcNumer = np.matmul(np.array(MeanReturns),x.T)-RiskFreeRate func = -(funcNumer / funcDenomr) return func #define equality constraint representing fully invested portfolio def constraintEq(x): A=np.ones(x.shape) b=1 constraintVal = np.matmul(A,x.T)-b return constraintVal #define bounds and other parameters xinit=np.repeat(0.33, PortfolioSize) cons = ({'type': 'eq', 'fun':constraintEq}) lb = 0 ub = 1 bnds = tuple([(lb,ub) for x in xinit]) #invoke minimize solver opt = optimize.minimize (f, x0 = xinit, args = (MeanReturns, CovarReturns,\ RiskFreeRate, PortfolioSize), method = 'SLSQP', \ bounds = bnds, constraints = cons, tol = 10**-3) return opt def StockReturnsComputing(StockPrice, Rows, Columns): StockReturn = np.zeros([Rows-1, Columns]) for j in range(Columns): # j: Assets for i in range(Rows-1): # i: Daily Prices StockReturn[i,j]=((StockPrice[i+1, j]-StockPrice[i,j])/StockPrice[i,j])*100 return StockReturn