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]