| Overall Statistics |
|
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio -1.329 Tracking Error 0.618 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset |
#region imports
from AlgorithmImports import *
#endregion
#region imports
from AlgorithmImports import *
#endregion
class SymbolData():
def __init__(self, network_symbol, period):
self.network_symbol = network_symbol
self.cap_mrkt_cur_usd = None
self.closes = RollingWindow[float](period)
self.last_update_cap = None
self.last_update_price = None
def update(self, close, date):
self.closes.Add(close)
self.last_update_price = date
def update_cap(self, cap_mrkt_cur_usd, date):
self.cap_mrkt_cur_usd = cap_mrkt_cur_usd
self.last_update_cap = date
def is_ready(self):
return self.closes.IsReady and self.cap_mrkt_cur_usd
def are_data_still_comming(self, curr_date, max_days_pause):
return (self.last_update_price - curr_date).days <= max_days_pause and (self.last_update_cap - curr_date).days <= max_days_pause
def performance(self):
closes = [x for x in self.closes]
return -(closes[0] - closes[-1]) / closes[-1]
# 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):
self.cap_mrkt_cur_usd_index = None
return SubscriptionDataSource("data.quantpedia.com/backtesting_data/crypto/{0}_network_data.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
# File exmaple:
# date,AdrActCnt,AdrBal1in100KCnt,AdrBal1in100MCnt,AdrBal1in10BCnt,AdrBal1in10KCnt,AdrBal1in10MCnt,AdrBal1in1BCnt,AdrBal1in1KCnt,AdrBal1in1MCnt,AdrBalCnt,AdrBalNtv0.001Cnt,AdrBalNtv0.01Cnt,AdrBalNtv0.1Cnt,AdrBalNtv100Cnt,AdrBalNtv100KCnt,AdrBalNtv10Cnt,AdrBalNtv10KCnt,AdrBalNtv1Cnt,AdrBalNtv1KCnt,AdrBalNtv1MCnt,AdrBalUSD100Cnt,AdrBalUSD100KCnt,AdrBalUSD10Cnt,AdrBalUSD10KCnt,AdrBalUSD10MCnt,AdrBalUSD1Cnt,AdrBalUSD1KCnt,AdrBalUSD1MCnt,AssetEODCompletionTime,BlkCnt,BlkSizeMeanByte,BlkWghtMean,BlkWghtTot,CapAct1yrUSD,CapMVRVCur,CapMVRVFF,CapMrktCurUSD,CapMrktFFUSD,CapRealUSD,DiffLast,DiffMean,FeeByteMeanNtv,FeeMeanNtv,FeeMeanUSD,FeeMedNtv,FeeMedUSD,FeeTotNtv,FeeTotUSD,FlowInExNtv,FlowInExUSD,FlowOutExNtv,FlowOutExUSD,FlowTfrFromExCnt,HashRate,HashRate30d,IssContNtv,IssContPctAnn,IssContPctDay,IssContUSD,IssTotNtv,IssTotUSD,NDF,NVTAdj,NVTAdj90,NVTAdjFF,NVTAdjFF90,PriceBTC,PriceUSD,ROI1yr,ROI30d,RevAllTimeUSD,RevHashNtv,RevHashRateNtv,RevHashRateUSD,RevHashUSD,RevNtv,RevUSD,SER,SplyAct10yr,SplyAct180d,SplyAct1d,SplyAct1yr,SplyAct2yr,SplyAct30d,SplyAct3yr,SplyAct4yr,SplyAct5yr,SplyAct7d,SplyAct90d,SplyActEver,SplyActPct1yr,SplyAdrBal1in100K,SplyAdrBal1in100M,SplyAdrBal1in10B,SplyAdrBal1in10K,SplyAdrBal1in10M,SplyAdrBal1in1B,SplyAdrBal1in1K,SplyAdrBal1in1M,SplyAdrBalNtv0.001,SplyAdrBalNtv0.01,SplyAdrBalNtv0.1,SplyAdrBalNtv1,SplyAdrBalNtv10,SplyAdrBalNtv100,SplyAdrBalNtv100K,SplyAdrBalNtv10K,SplyAdrBalNtv1K,SplyAdrBalNtv1M,SplyAdrBalUSD1,SplyAdrBalUSD10,SplyAdrBalUSD100,SplyAdrBalUSD100K,SplyAdrBalUSD10K,SplyAdrBalUSD10M,SplyAdrBalUSD1K,SplyAdrBalUSD1M,SplyAdrTop100,SplyAdrTop10Pct,SplyAdrTop1Pct,SplyCur,SplyExpFut10yr,SplyFF,SplyMiner0HopAllNtv,SplyMiner0HopAllUSD,SplyMiner1HopAllNtv,SplyMiner1HopAllUSD,TxCnt,TxCntSec,TxTfrCnt,TxTfrValAdjNtv,TxTfrValAdjUSD,TxTfrValMeanNtv,TxTfrValMeanUSD,TxTfrValMedNtv,TxTfrValMedUSD,VelCur1yr,VtyDayRet180d,VtyDayRet30d
# 2009-01-09,19,19,19,19,19,19,19,19,19,19,19,19,19,0,0,19,0,19,0,0,0,0,0,0,0,0,0,0,1614334886,19,215,860,16340,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,9.44495122962963E-7,0,950,36500,100,0,950,0,1,0,0,0,0,1,0,0,0,0,11641.53218269,1005828380.584716757433,0,0,950,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,950,950,950,950,950,950,950,950,950,950,950,950,950,0,0,0,0,0,0,0,0,0,0,0,0,0,950,50,50,950,17070250,950,1000,0,1000,0,0,0,0,0,0,0,0,0,0,0,0,0
def Reader(self, config, line, date, isLiveMode):
data = CryptoNetworkData()
data.Symbol = config.Symbol
if not line[0].isdigit():
self.cap_mrkt_cur_usd_index = None
# on header line try to set index of CapMrktCurUSD
split = line.split(',')
for index in range(len(split)):
if split[index] == 'CapMrktCurUSD':
# set index and break out of the cycle
self.cap_mrkt_cur_usd_index = index
break
return None
# check if csv file has CapMrktCurUSD in columns
if not self.cap_mrkt_cur_usd_index:
return None
split = line.split(',')
data.Time = datetime.strptime(split[0], "%Y-%m-%d") + timedelta(days=1)
data['CapMrktCurUSD'] = float(split[self.cap_mrkt_cur_usd_index])
data.Value = float(split[self.cap_mrkt_cur_usd_index])
return data
# Custom fee model.
class CustomFeeModel(FeeModel):
def __init__(self, algo):
self.algorithm = algo
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))
# region imports
from AlgorithmImports import *
# endregion
# https://quantpedia.com/strategies/price-based-value-in-cryptocurrencies/
#
# The investment universe consists of cryptocurrencies from CoinMarketCap.com. Stablecoins, coins with zero prices, zero market capitalization, or
# zero trading volumes in all periods are omitted. The value measure is constructed as the negative of the past 52-week return. Sort the cryptos
# into decile portfolios based on their value. Long the top decile and short the bottom decile. The strategy is rebalanced weekly and value-weighted.
#
# QC Implementation:
# - The investment universe consists of 23 cryptocurrencies with market cap data available.
# - Top and bottom deciles are treaded.
# - Portfolio is equally weighted.
class PricebasedValueinCryptocurrencies(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 1)
self.SetCash(100000)
self.data = {}
self.period = 52*7
self.max_days_pause = 5 # max days, which crypto can stop receiving data and won't be removed from trade selection
self.quantiles = 5
self.portfolio_percentage = 1
self.SetBrokerageModel(BrokerageName.Binance)
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(CryptoCoarseFundamentalUniverse(Market.Binance, self.UniverseSettings, self.UniverseSelectionFilter))
self.Schedule.On(self.DateRules.WeekStart(), self.TimeRules.At(10,10), self.Rebalance)
def OnSecuritiesChanged(self, changes):
pass
def OnData(self, data):
curr_date = self.Time.date()
# daily updating of crypto prices and market capitalization(CapMrktCurUSD)
for crypto, symbol_obj in self.data.items():
network_symbol = symbol_obj.network_symbol
if crypto in data.Bars and data[crypto]:
# get crypto price
price = data.Bars[crypto].Value
self.data[crypto].update(price, curr_date)
if network_symbol in data and data[network_symbol]:
# get market capitalization
cap_mrkt_cur_usd = data[network_symbol].Value
self.data[crypto].update_cap(cap_mrkt_cur_usd, curr_date)
def Rebalance(self):
curr_date = self.Time.date()
performance = {}
for crypto, symbol_obj in self.data.items():
# crypto doesn't have enough data
if not symbol_obj.is_ready() or not symbol_obj.are_data_still_comming(curr_date, self.max_days_pause):
continue
# calculate performance for current crypto
performance[crypto] = symbol_obj.performance()
# not enough cryptos for selection
if len(performance) < self.quantiles:
self.Liquidate()
return
# perform selection
quantile = int(len(performance) / self.quantiles)
sorted_by_perf = [x[0] for x in sorted(performance.items(), key=lambda item: item[1])]
# long top quantile
long = sorted_by_perf[-quantile:]
# short bottom quantile
short = sorted_by_perf[:quantile]
# trade execution
invested = [x.Key for x in self.Portfolio if x.Value.Invested]
for crypto in invested:
if crypto not in long + short:
self.Liquidate(crypto)
long_c = len(long)
short_c = len(short)
# trade long
for crypto in long:
self.SetHoldings(crypto, 1/long_c)
# trade short
for crypto in short:
self.SetHoldings(crypto, -1/short_c)
def UniverseSelectionFilter(self, data):
filtered = [datnum for datnum in data
if datnum.VolumeInUsd and datnum.VolumeInUsd > 5000000]
filteredtwice = [datnum for datnum in filtered if datnum.Symbol.Value.find("USDT") != -1]
sorted_by_volume = sorted(filtered, key = lambda datnum: datnum.VolumeInUsd, reverse=True)[:50]
return [datnum.Symbol for datnum in sorted_by_volume]