Overall Statistics |
Total Trades 9143 Average Win 0.07% Average Loss -0.05% Compounding Annual Return 183.620% Drawdown 30.000% Expectancy 0.465 Net Profit 204.612% Sharpe Ratio 2.892 Probabilistic Sharpe Ratio 87.996% Loss Rate 38% Win Rate 62% Profit-Loss Ratio 1.38 Alpha -0.059 Beta 0.616 Annual Standard Deviation 0.434 Annual Variance 0.188 Information Ratio -2.686 Tracking Error 0.327 Treynor Ratio 2.037 Total Fees $450853.92 |
''' Ostirion Linear Controller BTC Price Demostration version 1.0 Copyright (C) 2021 Ostirion This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. Contact: www.ostirion.net/contact ''' import numpy as np import pandas as pd class CryptoController(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 1, 1) self.SetEndDate(2021, 1, 25) self.SetCash(1000000) self.SetBrokerageModel(BrokerageName.Bitfinex, AccountType.Margin) res = Resolution.Hour self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel()) self.Settings.RebalancePortfolioOnInsightChanges = True self.SetExecution(ImmediateExecutionModel()) self.UniverseSettings.Resolution = res # Crypto to trade: crypto = ['BTCUSD'] # K-Gain parameter, 4 as stated in the paper: try: self.K = float(self.GetParameter("K")) except: self.Debug('No K-gain parameter detected. Using default') self.K = float(4) # Create the symbols: self.crypto = [self.AddCrypto(ticker, res).Symbol for ticker in crypto] self.AddAlpha(CryptoControl(self.crypto, self.K)) class CryptoControl(AlphaModel): def __init__(self, crypto, K): self.Name = 'Crypto Controller' self.crypto = crypto self.K = K self.total_assets = len(self.crypto) self.init = False # Dictionaries for data: self.changes = {} self.loads = {} self.gains = {} # Low pass filter for very low weights, positions below # epsilon (0.005%) are set to 0 to avoid insights # that would result in no trade: self.epsilon = 0.005 # Subsampler cycle of insights, skipping every square root # of the gain to allow intermediate rebalancing. self.subsampler = int(np.sqrt(int(self.K))) self.sample = 0 def Update(self, algorithm, data): # Skip if no data in slice and clear insights: if not data: algorithm.Debug('No data. Skipping period.') return [] self.sample += 1 insights = [] # Initialize the rate of change indicator, # setting the defaults for the first value. if not self.init: request_data = 120 for symbol in self.crypto: h = algorithm.History self.loads[symbol] = (self.K*h(symbol, request_data, Resolution.Hour).loc[symbol]['close']).pct_change().sum() self.changes[symbol] = algorithm.ROC(symbol, 1) initial_load = sum(list(self.loads.values())) self.loads = {k:v/initial_load for k,v in self.loads.items()} self.init = True # Compute the gain g for each crypto symbol: for symbol in self.crypto: self.gains[symbol] = self.changes[symbol].Current.Value*self.K self.loads[symbol] = min(max(self.loads[symbol] + self.gains[symbol], 0), 1) # Subsample to avoid sync with rebalance and insight periods. if self.sample % self.subsampler != 0: return[] # Obtain insight time delta and magnitudes. t_delta = timedelta(hours=self.subsampler) magnitude_values = np.array(list(self.gains.values())) magnitude = abs(np.mean(magnitude_values))*100 for symbol in self.crypto: w = self.loads[symbol] g = self.gains[symbol] if w <= self.epsilon: continue if abs(g) <= self.epsilon/10000: continue insights.append(Insight(symbol, t_delta, InsightType.Price, InsightDirection.Up, magnitude, int(True), self.Name, w)) return insights
import matplotlib.dates as mdates import matplotlib.pyplot as plt import pandas as pd import numpy as np import seaborn as sns def plot_df(df, color='blue', size=(16, 7), legend='Close Price', y_label='Price in USD', title=None, kind='line'): plt.style.use('dark_background') plt.rcParams["figure.figsize"] = size ax = df.plot(kind=kind) plt.title(title) plt.ylabel(y_label) x = 0.1 y = 0.4 plt.text(x, y, 'www.ostirion.net', fontsize=15, transform=ax.transAxes) plt.legend(ncol=int(len(df.columns) / 2)) date_form = mdates.DateFormatter("%m-%Y") plt.xticks(rotation=45); plt.show() def plot_corr_hm(df, title='Title', size=(16, 7), annot = True): corr = df.corr() plt.style.use('dark_background') plt.rcParams["figure.figsize"] = size mask = np.triu(np.ones_like(corr, dtype=bool)) cmap = sns.color_palette("RdBu") ax = sns.heatmap(corr, mask=mask, vmax=.3, center=0, cmap=cmap, annot=annot, square=True, linewidths=0, cbar_kws={"shrink": .5}, fmt='g') ax.set_title(title) plt.setp(ax.get_yticklabels(), rotation=0); plt.setp(ax.get_xticklabels(), rotation=90); plt.show() def plot_cm(df, title='Title', size=(16,7)): plt.style.use('dark_background') plt.rcParams["figure.figsize"] = size cmap = sns.color_palette("Blues") ax = sns.heatmap(df, cmap=cmap, annot=True, linewidths=0, cbar_kws={"shrink": .5}, fmt='g') ax.set_title(title) plt.xlabel('Predicted') plt.ylabel('True') plt.setp(ax.get_xticklabels(), rotation=0); def plot_hm(df, title='Title', size=(16, 7), annot = True, x_rot=90): plt.style.use('dark_background') plt.rcParams["figure.figsize"] = size cmap = sns.color_palette("RdBu") ax = sns.heatmap(df, vmax=.3, center=0, cmap=cmap, annot=annot, square=True, linewidths=0, cbar_kws={"shrink": .5}, fmt='g') ax.set_title(title) plt.setp(ax.get_yticklabels(), rotation=0); plt.setp(ax.get_xticklabels(), rotation=x_rot); plt.show()