Crypto-market price actions often revolves around the news. Good or bad? It does not matter. However, the recent long-term battle between the SEC and Ripple seemed to reignite the markets. On July 13, 2023, XRP/USDT suddenly shoot up, dragging a number of not so obvious cryptos up along. This was the implication of the court’s verdict in the lawsuit. This observation led me to formulate a certain hypothesis that I decided to test: is it possible to create a crypto investment portfolio that will perform surprisingly well during forthcoming crypto summer 2024-2026?
Using data collected from CoinMarketCap and Binance, with a help of Python, we are going to analyse the Top 100+ crypto-assets given their reaction to XRP. Next, we will construct an attractive crypto portfolio and suggest performance measures for its on-going monitoring. The entire faith in this portfolio we base on the assumption that there are direct or latent links between the other portfolio’s members and XRP itself, including long-term business coupling.
1. Background Story
XRP is a digital asset and cryptocurrency created by the company Ripple Labs. It is often referred to as the native cryptocurrency of the Ripple network, which is a payment protocol and blockchain-based platform designed to facilitate fast and low-cost cross-border transactions. XRP was created in 2012 by Jed McCaleb and Chris Larsen. Unlike some other cryptocurrencies like Bitcoin and Ethereum, XRP does not rely on traditional blockchain mining for transaction validation. Instead, the XRP ledger uses a consensus algorithm known as the Ripple Protocol Consensus Algorithm (RPCA) to validate and settle transactions.
The primary purpose of XRP is to serve as a bridge currency for facilitating international money transfers and remittances. It aims to provide a faster and more efficient alternative to traditional payment methods like SWIFT, which can be slower and costlier for cross-border transactions. By using XRP as an intermediary currency, financial institutions can potentially settle transactions more quickly and with lower fees.
One unique feature of XRP is that it was pre-mined, meaning that all 100 billion XRP tokens were created and exist from the start. Ripple Labs holds a significant portion of the XRP supply, and it periodically releases portions of the XRP into the market to fund its operations and support the ecosystem.
XRP’s legal status has been a subject of debate and regulatory scrutiny in some jurisdictions. The U.S. Securities and Exchange Commission (SEC) filed a lawsuit against Ripple Labs, alleging that XRP should be considered a security and that the company conducted an unregistered securities offering. Ripple Labs, on the other hand, denied the SEC’s allegations and contended that XRP is a digital asset, like Bitcoin and Ethereum, which the SEC has already classified as commodities rather than securities. Ripple’s defense argued that XRP has a distinct use case and utility as a digital currency, and it should not be considered a security.
The case has been closely watched by the cryptocurrency community and industry stakeholders because its outcome could potentially impact the regulatory landscape for other cryptocurrencies. If the court were to rule in favor of the SEC and classify XRP as a security, it might subject Ripple Labs to fines and penalties for the alleged securities violations. Additionally, the ruling could set a precedent that might affect how other cryptocurrencies are regulated and traded in the U.S.
Luckily, on July 13 2023, Ripple scored a partial victory in its fight with the SEC in a court ruling that brought a modicum of regulatory clarity for the cryptocurrency industry. XRP was up 75% by late afternoon, according to Refinitiv Eikon data.
2. Data
Regardless of the current ranking, that day, a great deal of cryptocurrencies were pumped along XRP/USDT price action itself. In this section, first, we are going to scan top 100+ cryptocurrencies followed by fetching their price time-series for further in-depth analysis.
2.1. Top 150 Cryptocurrencies using CoinMarketCap Ranking
In my previous article, How To Check Crypto Market Cap using Python?, we proposed fetching data of cryptocurrencies making use of CryptoCompare resources. It turns out that their data are not precisely aligned with the crypto ranking provided by the popular CoinMarketCap.com portal.
Therefore, in this edition, we gonna use the CoinMarketCap’s API endpoint of CoinMarketCap ID map instead. A notable comment we need to make here is that a free access to the data is limited and requires paid subscription plans. Thankfully, the aforementioned API’s endpoint allows to grab cryptos sorted by their market capitalisation (market cap) with a free plan. A sole requirement is to create your own API Key and save it securely.
In my case, I have stored my access keys inside mycryptokeys.py file listing:
binance_api_key = 'your_key' binance_api_secret = 'your_key' cmc_api_key = 'your_key'
required in this research.
Let’s begin as usual from the import of required libraries followed by our first function:
# XRP-based Crypto Investment Portfolio Inspired by Ripple vs SEC Lawsuit # (c) 2023 QuantAtRisk.com # required libraries and resources from requests import Request, Session from requests.exceptions import ConnectionError, Timeout, TooManyRedirects import json from binance.client import Client from datetime import datetime from datetime import datetime as dt import pandas as pd from matplotlib import pyplot as plt import mycryptokeys def get_cmc_coins_by_mcap(n=100): """ Fetches cryptocurrency data from the CoinMarketCap API based on market capitalization ranking. Args: n (int, optional): The maximum rank of cryptocurrencies to include in the results. Default is 100. Returns: tuple or None: A tuple containing a pandas DataFrame and a list of cryptocurrency tickers (symbols) or None if an error occurred. The DataFrame contains the following columns: - 'fetched_on': The date when the data was fetched from the API in the format 'YYYY-MM-DD'. - 'ranking': The rank of the cryptocurrency based on market capitalization. - 'symbol': The ticker symbol of the cryptocurrency. Note: - This function fetches data from the CoinMarketCap API using a valid API key stored inside mycryptokeys.cmc_api_key. - The fetched data includes information on multiple cryptocurrencies, but only the top 'n' based on market capitalization ranking are returned. - If an error occurs during the data retrieval process, the function returns None. """ url = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/map' parameters = { 'start' : '1', 'sort' : 'cmc_rank', 'limit' : '5000' } headers = { 'Accepts': 'application/json', 'X-CMC_PRO_API_KEY': mycryptokeys.cmc_api_key, } session = Session() session.headers.update(headers) err = None try: response = session.get(url, params=parameters) data = json.loads(response.text) fetched_on = dt.now().strftime('%Y-%m-%d') except (ConnectionError, Timeout, TooManyRedirects) as err: pass if data['status']['error_message'] is None: data = data['data'] # list with len(list) = parameters['limit'] lst = list() for i, _ in enumerate(data): lst.extend([[fetched_on, int(data[i]['rank']), data[i]['symbol']]]) df = pd.DataFrame(lst, columns=['fetched_on', 'ranking', 'symbol']) df = df[df.ranking <= n] lst = df['symbol'].values # plain list with crypto tickers return (df, lst) else: return (None, None)
We fetch data as simple as:
# fetch CMC map data coins_df, coins = get_cmc_coins_by_mcap(n=150) print(list(coins))
what returns:
['BTC', 'ETH', 'USDT', 'XRP', 'BNB', 'USDC', 'DOGE', 'ADA', 'SOL', 'TRX', 'MATIC', 'LTC', 'DOT', 'BCH', 'WBTC', 'TON', 'SHIB', 'AVAX', 'DAI', 'XLM', 'LINK', 'BUSD', 'LEO', 'UNI', 'ATOM', 'XMR', 'TUSD', 'ETC', 'OKB', 'FIL', 'ICP', 'MNT', 'HBAR', 'LDO', 'LDO', 'APT', 'ARB', 'CRO', 'VET', 'NEAR', 'NEAR', 'QNT', 'MKR', 'AAVE', 'OP', 'GRT', 'ALGO', 'AXS', 'EGLD', 'STX', 'XDC', 'SAND', 'EOS', 'THETA', 'SNX', 'IMX', 'XTZ', 'MANA', 'APE', 'USDD', 'FTM', 'BSV', 'RNDR', 'INJ', 'CRV', 'CRV', 'NEO', 'FLOW', 'XEC', 'RPL', 'KAVA', 'KCS', 'COMP', 'CHZ', 'USDP', 'CFX', 'PEPE', 'GALA', 'KLAY', 'GMX', 'ZEC', 'PAXG', 'MIOTA', 'XAUT', 'LUNC', 'BTT', 'FXS', 'HT', 'HT', 'CSPR', 'SUI', 'MINA', 'GT', 'GUSD', 'TWT', 'AR', 'DASH', 'NEXO', 'WOO', 'WOO', 'ZIL', 'DYDX', 'RUNE', 'CAKE', '1INCH', 'ENJ', 'GNO', 'MASK', 'FLR', 'LRC', 'BAT', 'CVX', 'BONE', 'ROSE', 'AGIX', 'MX', 'TFUEL', 'QTUM', 'ENS', 'XEM', 'XCH', 'ANKR', 'WLD', 'WLD', 'CELO', 'OSMO', 'BLUR', 'GMT', 'ASTR', 'RVN', 'BAL', 'BTG', 'YFI', 'DCR', 'HNT', 'T', 'HOT', 'WAVES', 'OCEAN', 'JST', 'SFP', 'FLOKI', 'ICX', 'LUNA', 'GLM', 'SXP', 'KSM', 'JASMY', 'AUDIO', 'ETHW', 'SC', 'IOTX', 'SSV', 'FET', 'ZRX', 'HIVE']
Interestingly, by inspection of coins_df DataFrame, you can spot that the ranking data are incomplete i.e. for requested Top 150 coins sorted by market cap, in this case, for some reason, we obtain 156 records:
In order to tune these results a bit, I propose the following two steps: elimination of stablecoins and getting rid of coins that appear in the list more than once. The latter may be the case due to technical issue of unknown reason. It’s better to self-protect yourself against undesirable outcomes:
def elimite_stablecoins(coins): """ Eliminates stablecoins from a list of cryptocurrencies. Given a list of cryptocurrencies, this function filters out and removes any stablecoins from the list. Args: coins (list): A list of cryptocurrency tickers (symbols). Returns: list: A new list containing the input cryptocurrencies without stablecoins and duplicates. """ stable = ['BUSD', 'USDT', 'USDC', 'TUSD', 'USDP', 'USDS', 'UST', 'USTC'] without = [c for c in coins if c not in stable] res = [] [res.append(x) for x in without if x not in res] return res coins_corr = elimite_stablecoins(coins)
That leads us to 145 assets left in the list. Now, we are ready for fetching time-series for these assets.
2.2. Downloading Historical Price-Series from Binance Archives
Currently, Binance provides candlestick charts, trading, and aggregated trading data for all Spot and Futures pairs (including USDⓈ-M and COIN-M Futures). In order to download historical crypto spot market data files via API, you can refer to this GitHub link, the data types, and their corresponding endpoints. Since there are lots of information down there, let me help you to point at what we need for this research.
We wish to grab price time-series for any coin in the circle of our interest. Binance allows for the manual extraction of data via URL solution delivering data in a .zip file format, where the general request’s format is:
<base_url>/data/spot/monthly/klines/<symbol_in_uppercase>/<interval>/<symbol_in_uppercase>- <interval>-<year>-<month>.zip
For example, by asking for
https://data.binance.vision/data/spot/daily/klines/XRPUSDT/15m/XRPUSDT-15m-2023-07-13.zip
we will receive XRP/USDT data with a 15 minute sampling, requested for specific day of July 13, 2023. It is obvious this method is a painful method and we need automate our requests given a longer list of currencies. Therefore, first, let’s write a dedicated function that will allow us to fetch price/traded volume time-series for an individual currency:
def get_binance_timeseries(cpair='BTCUSDT', interval='1d', t_start='2015-01-01', t_end=None): """ Fetches historical time series data for a specified cryptocurrency trading pair from Binance. Args: cpair (str, optional): The cryptocurrency trading pair to fetch data for. Default is 'BTCUSDT'. interval (str, optional): The time interval for the data. Default is '1d' (daily). t_start (str, optional): The starting date for the data in the format 'YYYY-MM-DD'. Default is '2015-01-01'. t_end (str, optional): The ending date for the data in the format 'YYYY-MM-DD'. If not provided, the current date is used. Returns: pandas DataFrame or None: A DataFrame containing the historical price and volume data for the specified trading pair, or None if an error occurred. Note: - This function uses the Binance API to fetch historical price and volume data for a specified trading pair and time interval. - To access the Binance API, you need to provide a valid API key and API secret from mycryptokeys.binance_api_key. - The data is returned as a DataFrame with the following columns: 'open_time': The timestamp of the data (UTC) in milliseconds. 'open': The opening price of the cryptocurrency. 'high': The highest price during the time interval. 'low': The lowest price during the time interval. 'close': The closing price of the cryptocurrency. 'volume': The trading volume during the time interval. 'close_time': The timestamp of the closing time (UTC) in milliseconds. 'qav': Quote asset volume (not used in the function). 'num_trades': The number of trades during the time interval. 'taker_base_vol': Taker buy base asset volume (not used in the function). 'taker_quote_vol': Taker buy quote asset volume (not used in the function). 'ignore': Ignore (not used in the function). - The data is converted to the appropriate data types, and the timestamps are adjusted to the local time zone. - If an error occurs during the data retrieval process, the function returns None. """ # client configuration api_key = mycryptokeys.binance_api_key api_secret = mycryptokeys.binance_api_key client = Client(api_key, api_secret, testnet=False) if t_end is None: t_end = datetime.now().strftime('%Y-%m-%d') try: klines = client.get_historical_klines(cpair, interval, t_start, t_end) data = pd.DataFrame(klines) except: data = None # error fetching data if data is not None: # create colums name cols = ['open_time','open', 'high', 'low', 'close', 'volume','close_time', 'qav','num_trades','taker_base_vol', 'taker_quote_vol', 'ignore'] if data.size > 0: data.columns = cols data[cols[1:-1]] = data[cols[1:-1]].astype(float) # change the timestamp (provided as UTC) data.index = [datetime.fromtimestamp(x/1000.0 + 0.001) for x in data.close_time] data.index = pd.to_datetime(data.index, format='%Y-%m-%d %H:%M:%S') else: data = None return data
We have an option of choosing sampling frequency of data (mind that: it’s free of charge!) to be one of the following possibilities:
1s, 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1mo
where 1mo (one month) is used instead of 1M to supprt non-case sensitive file systems (it does not work as described on Github page). Check the yellow box for an exemplary use cases.
In this example we fetch Binance archive for spot time-series of XRP traded versus three different stablecoins (USDC, USDT, BUSD) with one month, one day, and 15 minute sampling, respectively.
xrp_1M = get_binance_timeseries(cpair='XRPUSDC',interval='1M') xrp_1d = get_binance_timeseries(cpair='XRPUSDT',interval='1d') xrp_15min = get_binance_timeseries(cpair='XRPBUSD', interval='15m', t_start='2023-01-01', t_end='2023-04-30') plt.figure(figsize=(10,4)) plt.plot(xrp_1d.close, label='XRP/USDT (daily)') plt.plot(xrp_1M.close, label='XRP/USDC (monthly)') plt.plot(xrp_15min.close, label='XRP/BUSD (15 min)') plt.legend(loc=2); plt.grid()
Given that, we are ready to repeat the execution of this function in a loop over the list of cryptocurrencies we selected earlier. Here is a handy function that incorporates get_binance_timeseries() and merge all time-series in a single DataFrame:
def get_binance_multi_timeseries(coin_list, vs_currency='USDT', interval='1d', t_start='2015-01-01', t_end=None): """ Fetches historical time series data for multiple cryptocurrencies against a specified quote currency from Binance. Given a list of cryptocurrencies and a quote currency, this function fetches historical price data at a specific time interval from Binance for each cryptocurrency in the list. Args: coin_list (list): A list of cryptocurrency tickers (symbols) to fetch data for. vs_currency (str, optional): The quote currency against which the cryptocurrencies' prices are listed. Default is 'USDT'. interval (str, optional): The time interval for the data. Default is '1d' (daily). t_start (str, optional): The starting date for the data in the format 'YYYY-MM-DD'. Default is '2015-01-01'. t_end (str, optional): The ending date for the data in the format 'YYYY-MM-DD'. If not provided, the current date is used. Returns: pandas DataFrame: A DataFrame containing the historical price data for each cryptocurrency against the specified quote currency. Note: - This function calls the 'get_binance_timeseries' function to fetch historical price data for each cryptocurrency in the 'coin_list'. - For each cryptocurrency, the 'get_binance_timeseries' function is called with the provided 'vs_currency', 'interval', 't_start', and 't_end'. - The resulting data for each cryptocurrency is extracted to create a new DataFrame containing the 'Close' prices, with column names representing each cryptocurrency against the quote currency. - If any of the cryptocurrencies' data cannot be fetched or is missing, the corresponding column will contain NaN values in the returned DataFrame. - The function prints the progress of data retrieval for each cryptocurrency, indicating if data was successfully fetched ('appended') or skipped if an error occurred ('skipped'). - The returned DataFrame contains the date-time index and 'Close' prices of each cryptocurrency against the quote currency. """ df = pd.DataFrame() for coin in coin_list: cpair = coin + vs_currency print(cpair + '... ', end='') data = get_binance_timeseries(cpair=cpair, interval='5m', t_start=t_start, t_end=t_end) if data is not None: # extract solely 'Close' prices from 'data' DataFrame tmp = pd.DataFrame(data['close']) tmp.rename(columns = {'close' : cpair}, inplace=True) if df.size == 0: df = tmp.copy() else: df = pd.merge(left=df, right=tmp, left_index=True, right_index=True) print('appended') else: print('skipped') return df
This allow us to grab all 145 crypto-coins price data between July 13 and July 14, 2023 versus USDT in a single frame:
df = get_binance_multi_timeseries(coins_corr, vs_currency='USDT', interval='5m', t_start='2023-07-13 14:00', t_end='2023-07-14 14:00') display(df)
It is worth saving the above two function for your own use and further testing. Both of them are very often sought solutions address ever-problematic access to free historical price data for various cryptocurrencies. Keep in mind that by using them, we limit ourselves to Binance’s records. For more broader data collection explore CryptoCompare.com as provided earlier in one of my articles, e.g. Scanning Exchanges for Available Crypto Market Prices Data or Cryptocurrency Time Series for N-CryptoAsset Portfolio Analysis in Python.
3. Empirical Crypto Investment Portfolio Selection
Investing in cryptocurrencies can be an exhilarating journey, but it’s crucial to approach the market with careful consideration and strategic planning. As the cryptocurrency space continues to grow and evolve, building a well-rounded portfolio becomes essential to managing risk and potentially reaping rewards.
As usual, diversification is a fundamental principle of investing, and it rings true in the cryptocurrency market as well. Instead of putting all your eggs in one basket, you should consider spreading investments across a variety of cryptocurrencies. A diversified portfolio could include a mix of established cryptocurrencies, as well as promising altcoins with strong use cases and reputable development teams. By diversifying, you can reduce the impact of volatility on your overall portfolio and increase the potential for long-term growth.
Selection of a smart portfolio is extremely challenging. In our case, we aim at the empirical selection of assets using the abovementioned case study of XRP’s sudden surge in its price versus USD on the afternoon of July 13, 2023. As a starting point we pick up 16:00 UTC which will serve us as a baseline for measuring local profit-and-loss (PnL) for all 145 coins within the following 24+ hours:
# localised PnL pnl = df/df.iloc[0] - 1
Let’s assume we are interested in top 20 gainers within given timeframe, the selection of portfolio candidates follows:
# top twenty gainers top20 = pnl.max().sort_values(ascending=False).head(20) top20_coins = [c[:-4] for c in top20.index] # a plain list of coins print(top20)
i.e.
XLMUSDT 0.949846 XRPUSDT 0.918587 SOLUSDT 0.362332 SNXUSDT 0.330583 ADAUSDT 0.299654 LDOUSDT 0.293284 PEPEUSDT 0.220000 INJUSDT 0.208032 AVAXUSDT 0.204856 APEUSDT 0.197333 NEARUSDT 0.186428 ALGOUSDT 0.162393 OPUSDT 0.152516 MATICUSDT 0.151551 LINKUSDT 0.140613 SANDUSDT 0.139798 RNDRUSDT 0.131139 MANAUSDT 0.129204 FLOWUSDT 0.129088 WOOUSDT 0.128945
where we can see that XRP and XLM were clear winner within the first 5 hours, followed by moderate gainers with PnLs between 12% and 36%. What is more surprising here that XLM (Stellar) went off-chart (blue top line below), leaving our superstar XRP (orange line) in the second place. Visually, we can present these outcomes as
plt.figure(figsize=(12,5), dpi=77) for i, col in enumerate(pnl.columns): if col == 'XRPUSDT': color = '#ff5a19' elif col in top20: color = '#2c97f1' else: color = (.7,.7,.7) plt.plot(pnl.iloc[:,i], color=color, label=col) #plt.legend(loc=2) plt.ylabel('PnL since 2023-07-13 16:05 UTC', fontsize=14) plt.grid(); plt.tight_layout();
4. Portfolio Performance: First Look
When an investor considers investing in a set of assets, the key question is with what weights?. The weights represent a fraction of available capital an investor is willing to put into each asset. The simplest scheme here refers to the use of equal weights ($1/N$ where $N$ is the total number of assets in portfolio). More refined methods can be found in a number of industry textbooks and scientific publications.
Here, I will not force any portfolio optimisation approach. Instead, let’s track the performance (PnL) assuming we decided to buy all of these cryptocurrencies to form our experimental portfolio. We verify that by checking the latest price, updated every 6 hours:
df_p = get_binance_multi_timeseries(top20_coins, vs_currency='USDT', interval='6h', t_start='2023-07-14 14:05') pnl_perf = df_p / df_p.iloc[0] - 1 # check latest performance print(pnl_perf.iloc[-1,:].sort_values(ascending=False))
revealing, for the moment of writing this article (July 27, 2023 14:15 UTC), the following results:
SNXUSDT 0.093050 LINKUSDT 0.088770 XLMUSDT 0.088112 OPUSDT 0.042788 ALGOUSDT -0.061917 XRPUSDT -0.075191 SANDUSDT -0.082728 FLOWUSDT -0.093846 SOLUSDT -0.094741 APEUSDT -0.095043 MANAUSDT -0.101965 ADAUSDT -0.121308 NEARUSDT -0.130019 AVAXUSDT -0.131631 MATICUSDT -0.144876 RNDRUSDT -0.178287 INJUSDT -0.186401 WOOUSDT -0.187475 LDOUSDT -0.200832 PEPEUSDT -0.240000
Interestingly, only 4 crypto-assets kept gaining while others turned into losers. I will try to update these results once or two times a quarter from now on in order to keep a track record.
The essential reasoning behind the above model (portfolio selection) is to prove or rule out any significant crypto-business relationship among XRP and the remaining 19 assets.
5. Discussion
In particular, a closer investigation of XRP$-$XLM relationship seems to be curious. Both XRP (the cryptocurrency associated with Ripple Labs) and XLM (the cryptocurrency associated with Stellar Development Foundation) are two separate digital assets with different use cases and origins, but they do share some similarities in terms of their initial distribution and regulatory scrutiny by SEC. These assets were not mined like traditional cryptocurrencies such as Bitcoin or Ethereum. Instead, they were pre-mined, meaning that all the coins in their respective supplies were created and distributed by their creators before being made available to the public. A significant portion of both XRP and XLM tokens were held by the respective companies (Ripple Labs and Stellar Development Foundation) at the time of their creation.
Both XRP and XLM have faced regulatory scrutiny by the SEC regarding their classification as “securities.” In December 2020, the SEC filed a lawsuit against Ripple Labs, alleging that XRP was an unregistered security and that the company conducted an unregistered securities offering by selling XRP. We mentioned that in the beginning. In contrast, Stellar Development Foundation reached a non-prosecution agreement with the SEC in 2019, in which the SEC did not pursue enforcement action against Stellar. As part of the agreement, Stellar made certain undertakings, including a commitment to comply with securities laws in the future. This agreement indicates that the SEC viewed Stellar’s XLM cryptocurrency differently from XRP in terms of its regulatory status.
Regardless of similarities and differneces, XLM reaction to the recent court decision in XRP case turned itself into extremely optimistic wave and on July 13 all investors into XRP and XLM warmly welcomed the news.
As for the remaining 16 coins, it would be interesting to find out how closely their future performance profiles follow XRP. If it occurs that we sole witnessed a random ride in price, uncorrelated over longer time horizons, it will be a valuable lesson for all of us, too.
Explore Further
→ Mining Monero (XMR): Earning Passive Income from your Mac
→ Hacking 1-Minute Cryptocurrency Candlesticks: (1) Capturing Binance Exchange Live Data
→ Best Crypto to Invest In: The Rules