| Overall Statistics |
|
Total Trades 320 Average Win 2.40% Average Loss -1.79% Compounding Annual Return 21.850% Drawdown 11.000% Expectancy 0.461 Net Profit 251.136% Sharpe Ratio 1.425 Probabilistic Sharpe Ratio 84.363% Loss Rate 38% Win Rate 62% Profit-Loss Ratio 1.34 Alpha 0.142 Beta 0.01 Annual Standard Deviation 0.107 Annual Variance 0.011 Information Ratio -1.547 Tracking Error 0.623 Treynor Ratio 15.229 Total Fees $33465.44 Estimated Strategy Capacity $6300000.00 |
# The investment universe consists of 1 cryptocurrency, Bitcoin.
# The raw carry metric is defined as the negative of the sum total coin issuance over the preceding seven days,
# divided by the coins outstanding at the beginning of those seven days (the data are available at coinmetrics.io).
# After that, the raw carry factor is standardized by the volatility of the underlying currency return, separately for each currency.
# It is not possible to short cryptocurrencies, and the practical application would require, a long-only strategy.
# The portfolio is rebalanced weekly. Last but not least, there are two weighting schemes, the second one is risk-based,
#
# QC Implementation:
# - The raw carry metric is defined as sum total coin issuance over the preceding seven days, divided by the coins outstanding at the beginning of those seven days. => negative part is ommited.
class BitcoinAndLove(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetCash(100000)
# Coin issuance seven days data and data about first of those seven days
self.data = {}
self.period = 7
self.count_days = 1
self.mom_period = 14
self.SetBrokerageModel(BrokerageName.Bitfinex)
self.symbols = {'BTC' : 'BTCUSD'}
self.symbol = 'BTC'
self.currency = self.symbols[self.symbol]
data = self.AddCrypto(self.currency, Resolution.Daily, Market.Bitfinex)
self.AddData(CryptoNetworkData, self.symbol, Resolution.Daily)
self.data[self.symbol] = SymbolData(self.period)
self.mom = self.MOM(self.currency, self.mom_period, Resolution.Daily)
self.MAs = [
self.MOM(self.symbol, 1, Resolution.Daily),
self.MOM(self.symbol, 2, Resolution.Daily),
self.MOM(self.symbol, 4, Resolution.Daily),
self.MOM(self.symbol, 10, Resolution.Daily),
self.MOM(self.symbol, 20, Resolution.Daily)
]
self.SetBenchmark(self.currency)
self.Schedule.On(self.DateRules.EveryDay(self.currency), self.TimeRules.AfterMarketOpen(self.currency), self.Rebalance)
def OnData(self, data):
# Store daily data.
if self.symbol in data:
if data[self.symbol]:
coin_issuance = data[self.symbol].Price
if coin_issuance != 0:
self.data[self.symbol].update(coin_issuance)
def Rebalance(self):
if self.count_days == 7:
self.count_days = 1
else:
self.count_days = self.count_days + 1
return
price = self.Securities[self.currency].Price
if price == 0: return
long_signal_count = 0
for ma in self.MAs:
if ma.IsReady:
if price > ma.Current.Value:
long_signal_count += 1
else:
self.Liquidate()
w = (0.25 / len(self.MAs)) * long_signal_count
if self.data[self.symbol].is_ready() and self.mom.IsReady:
carry_metric = self.data[self.symbol].carry_metric()
if carry_metric > 0 and self.mom.Current.Value > 10:
self.SetHoldings(self.currency, w)
class SymbolData():
def __init__(self, period):
self.SevenDaysData = RollingWindow[float](period)
def update(self, value):
self.SevenDaysData.Add(value)
def carry_metric(self):
seven_days_data = [x for x in self.SevenDaysData]
# return -1 * (sum(seven_days_data) / seven_days_data[-1])
return (sum(seven_days_data) / seven_days_data[-1])
def is_ready(self):
return self.SevenDaysData.IsReady
# Crypto network data.
# NOTE: IMPORTANT: Data order must be ascending (datewise)
# Data source: https://coinmetrics.io/community-network-data/
class CryptoNetworkData(PythonData):
def GetSource(self, config, date, isLiveMode):
return SubscriptionDataSource("anstochibamu.github.io/wp-content/uploads/backtesting_data/cryptos/{0}_network_data.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
# File exmaple:
# date;AdrActCnt;BlkCnt;BlkSizeByte;BlkSizeMeanByte;CapMrktCurUSD;DiffMean;FeeMeanNtv;FeeMeanUSD;FeeMedNtv;FeeMedUSD;FeeTotNtv;FeeTotUSD;HashRate;IssContNtv;IssContPctAnn;IssContUSD;IssTotNtv;IssTotUSD;NVTAdj;NVTAdj90;PriceBTC;PriceUSD;ROI1yr;ROI30d;SplyCur;SplyExpFut10yrCMBI;SplyFF;TxCnt;TxTfrCnt;TxTfrValAdjNtv;TxTfrValAdjUSD;TxTfrValMeanNtv;TxTfrValMeanUSD;TxTfrValMedNtv;TxTfrValMedUSD;TxTfrValNtv;TxTfrValUSD;VtyDayRet180d;VtyDayRet30d;VtyDayRet60d
# 2020-10-04;472428;6373;236724055;37144.83838067;39770015476.68894984938121513;3253577961269075.5;0.0059853576088997875;2.108877310104448675043701222;0.002595411;0.914465555100157778991;5898.28262640756875328;2078197.362997773340673611063;239.9958183136524;13264.5625;4.28948;4673627.9956135026858125;13264.5625;4673627.9956135026858125;43.17691049;25.628952086342867;0.033035064603976656;352.339400233781;99.98284398079014;-8.878309096289607;112874164.65573;161002502.63354883;108729987.128107404906339777;985452;663574;2614225.134803792;921094516.0728433572443652976;6.26362963;2206.92350712073959853103;0.21962698328610455;77.3832395661807212638788078;4156381.766019126322793441;1464457058.581802442738964697;0.04140807102894915;0.04751249797286342;0.04822674537536073
def Reader(self, config, line, date, isLiveMode):
data = CryptoNetworkData()
data.Symbol = config.Symbol
if not line[0].isdigit(): return None
split = line.split(',')
data.Time = datetime.strptime(split[0], "%Y-%m-%d") + timedelta(days=1)
data['SplyCur'] = float(split[27])
data.Value = float(split[27])
return data