Analysis of Event Risk Hedging Using Cryptocurrency

One area of risk that investors generally wish to avoid but that is very difficult to reduce at a cheap price is Event Risk. Event risk can be defined as risks from rare events that have large impacts in market prices. Some events that cause large swings in market prices include, for example, financial crises, where markets such as equity markets typically have large reductions in market value in a very short period of time. For investors such as retirees, who need to draw down from their portfolios, a large reduction in portfolio value in a short period of time, and which is expected to recover in the medium-term, could have drastic effects on retirement savings. Thus, there is often a desire to reduce risks of portfolio declines from events.

While risk of various events will naturally differ depending on the composition of the portfolio, in this project I am going to look specifically at event risk of an equity portfolio, particularly a diversified portfolio based on large-cap equities, such as a portfolio based on the S&P 500 index, since large-cap equities comprise a large portion of many people's portfolio. One popular and easy to use strategy to reduce the event risk of a large-cap equity portfolio is through put options. However, given the popularity of this strategy, the costs of persistent put options on a portfolio can be costly to returns in the long run, especially during long periods of a bull market, such as what has occurred within the US equity markets in the past decade. As a result, there is a desire to find investments with high correlation to your event risk while at the same time being cheaper than the put option strategy.

This project is an exploration at the viability of using cryptocurrency strategies as a hedge to event risks. Should cryptocurrencies be seen to have proven effective historically at event risk hedging, it is conceivable that in the future investors may start incorporating cryptocurrency strategies within their portfolios in larger scale for diversification purposes.

Gathering the Data

The first step in this project is to get the data sets that will be used for the analysis. For this project I am using a series of data sets from https://www.kaggle.com/, a very popular repository for data sets and data science tools. Below are a list links to all the data sets that are used within this project:

These data sets include price data for many cryptocurrencies, the S&P 500 index, gold (via the SPDR Gold Trust), oil (via Brent Crude), and wheat.

Importing Libraries and Setting Environment Data

Below I am importing all of the relevant Python libraries for this project. The directory structure for this project is as follows:

|--project
  |--jupyter_notebook.ipynb
  |--data
    |--crypto-markets.csv
    |--gld_price_data.csv
    |--BrentOilPrices.csv
    |--wheat_200910-201803.csv
In [1]:
# Importing the relevant python libraries
import os
import pandas
import os.path
import tabulate
import numpy
import plotly.express as px
from pprint import pprint
from IPython.display import display

# Setting some directory constants
PROJECT_DIRECTORY = os.getcwd()
DATA_DIRECTORY = os.path.join(PROJECT_DIRECTORY, "data")

Formatting the Data

The data for this research comes from a variety of different data sets, and as a result needs to be formatted into a common data structure that can be used for the analysis. In this step, I am formatting the data from each data set into a pandas.DataFrame structure of the form of:

[dateIndex, priceOrStatisticValue1, priceOrStatisticValue2, ... priceOrStatisticValueN]

The goal is to get a date as the index for each DataFrame and then calculate the relevant statistics for each DataFrame depending on the nature of the data. Then, I will be adding these DataFrames to a running List of DataFrames that will ultimately be joined into a single DataFrame at the end of the formatting step. The RunningDataFrameList variable will contain the list of all the DataFrames:

In [2]:
RunningDataFrameList = list()

The Cryptocurreny Data Set

https://www.kaggle.com/jessevent/all-crypto-currencies

This data set includes daily open, high, low, and closing prices for around 2000 different cryptocurrencies spanning from 2013 to 2018. Since the vast majority of those cryptocurrencies are very lightly traded, almost all of them will be excluded and only the most commonly used cryptocurrencies will be used for this analysis.

In [3]:
# Reading the Crypto Markets CSV from the data folder
allCryptocurrenciesCsvPath = os.path.join(DATA_DIRECTORY, "crypto-markets.csv")
allCryptocurrenciesDataFrame = pandas.read_csv(allCryptocurrenciesCsvPath)

# Since the data contains over 2000 cryptocurrencies, the vast majority of
# them very thinly traded, this dictionary contains a listing of select 
# liquid and major cryptocurrencies that will be used to analyze event risk
cryptocurrencyFilterDictionary = {
    "BTC":"Bitcoin",
    "XRP":"Ripple",
    "ETH":"Ethereum",
    "BCH":"Bitcoin Cash",
    "LTC":"Litecoin",
    "USDT":"Tether",
    "XMR":"Monero",
    "ETC":"Ethereum Classic",
}

print("Prior to filtering:")
print("-"*80)
print("Total Cryptocurrencies:      ", len(allCryptocurrenciesDataFrame["symbol"].unique()))
print("Partial Cryptocurrency List: ", allCryptocurrenciesDataFrame["symbol"].unique())
print()
print()

# Filtering out all cryptocurrencies that are not one of the keys in the
# cryptocurrency filter dictionary
allCryptocurrenciesDataFrame = allCryptocurrenciesDataFrame.loc[allCryptocurrenciesDataFrame["symbol"].isin(list(cryptocurrencyFilterDictionary.keys()))]

print("After filtering:")
print("-"*80)
print("Total Cryptocurrencies:      ", len(allCryptocurrenciesDataFrame["symbol"].unique()))
print("Partial Cryptocurrency List: ", allCryptocurrenciesDataFrame["symbol"].unique())
Prior to filtering:
--------------------------------------------------------------------------------
Total Cryptocurrencies:       2005
Partial Cryptocurrency List:  ['BTC' 'XRP' 'ETH' ... '42' 'BTWTY' 'NANOX']


After filtering:
--------------------------------------------------------------------------------
Total Cryptocurrencies:       8
Partial Cryptocurrency List:  ['BTC' 'XRP' 'ETH' 'BCH' 'LTC' 'USDT' 'XMR' 'ETC']

The data set also includes a lot of extraneous columns, and so below essentially everything except closing price, date, and cryptocurrency ticker are removed:

In [4]:
# Dropping all irrelevant values from the DataFrame. For this analysis, for 
# financial market data I will only be using the date and closing price.
allCryptocurrenciesDataFrame = allCryptocurrenciesDataFrame.drop([
    "slug",
    "name",
    "ranknow",
    "open",
    "high",
    "low",
    "volume",
    "market",
    "close_ratio",
    "spread"
], axis=1)

# Making the date the index for the DataFrame and then dropping the date column
allCryptocurrenciesDataFrame.index = allCryptocurrenciesDataFrame["date"]
allCryptocurrenciesDataFrame = allCryptocurrenciesDataFrame.drop("date", axis=1)

display(allCryptocurrenciesDataFrame.head())
symbol close
date
2013-04-28 BTC 134.21
2013-04-29 BTC 144.54
2013-04-30 BTC 139.00
2013-05-01 BTC 116.99
2013-05-02 BTC 105.21

Below is a list of all the remaining cryptocurrencies and columns after filtering out all of the unneeded data:

In [5]:
# Splitting the DataFrame into a list of DataFrames for each of the individual
# cryptocurrency
cryptocurrencyDataFrameList = list()

for cryptocurrencySymbol in allCryptocurrenciesDataFrame["symbol"].unique():
    cryptocurrencyDataFrameList.append(
        allCryptocurrenciesDataFrame.loc[allCryptocurrenciesDataFrame["symbol"] == cryptocurrencySymbol]
    )
    
for cryptocurrencyDataFrame in cryptocurrencyDataFrameList:
    display(cryptocurrencyDataFrame.head())
symbol close
date
2013-04-28 BTC 134.21
2013-04-29 BTC 144.54
2013-04-30 BTC 139.00
2013-05-01 BTC 116.99
2013-05-02 BTC 105.21
symbol close
date
2013-08-04 XRP 0.005882
2013-08-05 XRP 0.005613
2013-08-06 XRP 0.004680
2013-08-07 XRP 0.004417
2013-08-08 XRP 0.004254
symbol close
date
2015-08-07 ETH 2.770000
2015-08-08 ETH 0.753325
2015-08-09 ETH 0.701897
2015-08-10 ETH 0.708448
2015-08-11 ETH 1.070000
symbol close
date
2017-07-23 BCH 413.06
2017-07-24 BCH 440.70
2017-07-25 BCH 406.90
2017-07-26 BCH 365.82
2017-07-27 BCH 385.48
symbol close
date
2013-04-28 LTC 4.35
2013-04-29 LTC 4.38
2013-04-30 LTC 4.30
2013-05-01 LTC 3.80
2013-05-02 LTC 3.37
symbol close
date
2015-02-25 USDT 1.210000
2015-02-26 USDT 1.210000
2015-03-02 USDT 0.606502
2015-03-03 USDT 0.606229
2015-03-06 USDT 1.000000
symbol close
date
2014-05-21 XMR 1.60
2014-05-22 XMR 2.10
2014-05-23 XMR 2.96
2014-05-24 XMR 3.70
2014-05-25 XMR 3.14
symbol close
date
2016-07-24 ETC 0.928814
2016-07-25 ETC 0.602402
2016-07-26 ETC 2.550000
2016-07-27 ETC 1.600000
2016-07-28 ETC 1.730000

Since I will be using 1-day, 7-day, and 30-day returns to identify event risk, below I am using a series of operations to calculate those returns for each of the DataFrames.

In [6]:
# Finally, I am taking all of the DataFrames and creating new DataFrames that include a 1-day, 7-day, 
# and 3-0day returns for each cryptocurrency, and then adding these new DataFrames to the Running DataFrame List
for cryptocurrencyDataFrame in cryptocurrencyDataFrameList:
    
    # Getting all the cryptocurrency symbols
    cryptocurrencySymbol = cryptocurrencyDataFrame["symbol"].unique()[0]
    
    # Removing the symbols column
    cryptocurrencyDataFrame = cryptocurrencyDataFrame.drop("symbol", axis=1)
    
    # Renaming the close to the cryptocurrency symbol for this frame
    cryptocurrencyDataFrame = cryptocurrencyDataFrame.rename(columns={"close":cryptocurrencySymbol})
    
    # Converting the data within the DataFrame into a list for easier data manipulation. This list
    # will be in the form of [[rowData1], [rowData2], ... [rowDataN]]
    individualCryptocurrencyDataList = [list(row) for row in cryptocurrencyDataFrame.itertuples()]
    
    # Creating a new temporary list that will contain dictionaries that will be used to create the new
    # DataFrames. The dictionaries will be in the form of:
    # {
    #   "date": Date,
    #   "BTC": Price,
    #   "BTC_1_Day_Return": 1DayReturn,
    #   "BTC_7_Day_Return": 7DayReturn,
    #   "BTC_30_Day_Return": 30DayReturn,
    # }
    modifiedInidividualCryptocurrencyDataList = list()
    
    for i in range(len(individualCryptocurrencyDataList)):
        
        # Date
        dataDate = individualCryptocurrencyDataList[i][0]
        
        # Price
        dataValue = individualCryptocurrencyDataList[i][1]
        
        # 1 Day Return or None if not able to calculate
        if i < 1:
            data1day = None
        else:
            data1day = (individualCryptocurrencyDataList[i][1] / individualCryptocurrencyDataList[i - 1][1]) - 1
        
        # 7 Day Return or None if not able to calculate
        if i < 6:
            data7day = None
        else:
            data7day = (individualCryptocurrencyDataList[i][1] / individualCryptocurrencyDataList[i - 6][1]) - 1
        
        # 30 Day Return or None if not able to calculate
        if i < 29:
            data30day = None
        else:
            data30day = (individualCryptocurrencyDataList[i][1] / individualCryptocurrencyDataList[i - 29][1]) - 1
        
        # Adding all of the data to a list
        modifiedInidividualCryptocurrencyDataList.append({
            "date":dataDate,
            cryptocurrencySymbol:dataValue,
            cryptocurrencySymbol+"_1_Day_Return":data1day,
            cryptocurrencySymbol+"_7_Day_Return":data7day,
            cryptocurrencySymbol+"_30_Day_Return":data30day,
        })
    
    # Creating a new DataFrame with the new data
    newCryptocurrencyDataFrame = pandas.DataFrame(modifiedInidividualCryptocurrencyDataList)
    newCryptocurrencyDataFrame.index = newCryptocurrencyDataFrame["date"]
    newCryptocurrencyDataFrame = newCryptocurrencyDataFrame.drop("date", axis=1)
    
    # Adding it to the Running List of DataFrames
    RunningDataFrameList.append(newCryptocurrencyDataFrame)
    
    display(newCryptocurrencyDataFrame.tail())
    
BTC BTC_1_Day_Return BTC_30_Day_Return BTC_7_Day_Return
date
2018-11-25 4009.97 0.033295 -0.381214 -0.176849
2018-11-26 3779.13 -0.057567 -0.417375 -0.151114
2018-11-27 3820.72 0.011005 -0.396661 -0.169800
2018-11-28 4257.42 0.114298 -0.327875 -0.024856
2018-11-29 4278.85 0.005034 -0.322711 -0.015702
XRP XRP_1_Day_Return XRP_30_Day_Return XRP_7_Day_Return
date
2018-11-25 0.374551 -0.002091 -0.179243 -0.216693
2018-11-26 0.355451 -0.050994 -0.231301 -0.182356
2018-11-27 0.360163 0.013256 -0.184255 -0.195438
2018-11-28 0.390557 0.084390 -0.121868 -0.083372
2018-11-29 0.379562 -0.028152 -0.153485 -0.068395
ETH ETH_1_Day_Return ETH_30_Day_Return ETH_7_Day_Return
date
2018-11-25 116.45 0.026082 -0.429754 -0.219399
2018-11-26 108.33 -0.069729 -0.472513 -0.168866
2018-11-27 110.01 0.015508 -0.442281 -0.195245
2018-11-28 122.44 0.112990 -0.380239 -0.033699
2018-11-29 117.54 -0.040020 -0.404499 -0.046715
BCH BCH_1_Day_Return BCH_30_Day_Return BCH_7_Day_Return
date
2018-11-25 184.58 0.021529 -0.578132 -0.452204
2018-11-26 182.04 -0.013761 -0.587286 -0.186232
2018-11-27 179.06 -0.016370 -0.571186 -0.241110
2018-11-28 190.07 0.061488 -0.549522 -0.101196
2018-11-29 180.98 -0.047824 -0.572010 -0.127513
LTC LTC_1_Day_Return LTC_30_Day_Return LTC_7_Day_Return
date
2018-11-25 30.89 0.057153 -0.405161 -0.156932
2018-11-26 29.41 -0.047912 -0.433770 -0.109867
2018-11-27 31.07 0.056443 -0.366823 -0.103836
2018-11-28 34.63 0.114580 -0.296710 0.075466
2018-11-29 33.80 -0.023968 -0.316343 0.050995
USDT USDT_1_Day_Return USDT_30_Day_Return USDT_7_Day_Return
date
2018-11-25 0.980969 0.000760 -0.012212 -0.001792
2018-11-26 0.977243 -0.003798 -0.021243 0.008716
2018-11-27 0.984260 0.007180 -0.012639 -0.003285
2018-11-28 0.998622 0.014592 0.001958 0.000435
2018-11-29 0.997377 -0.001247 0.009366 0.015070
XMR XMR_1_Day_Return XMR_30_Day_Return XMR_7_Day_Return
date
2018-11-25 58.05 -0.005312 -0.441290 -0.199200
2018-11-26 53.22 -0.083204 -0.493046 -0.199579
2018-11-27 57.13 0.073469 -0.437087 -0.171308
2018-11-28 63.52 0.111850 -0.380595 -0.035530
2018-11-29 61.20 -0.036524 -0.410575 -0.081495
ETC ETC_1_Day_Return ETC_30_Day_Return ETC_7_Day_Return
date
2018-11-25 4.76 -0.002096 -0.504683 -0.232258
2018-11-26 4.32 -0.092437 -0.550468 -0.214545
2018-11-27 4.53 0.048611 -0.501101 -0.212174
2018-11-28 4.99 0.101545 -0.446171 -0.065543
2018-11-29 4.93 -0.012024 -0.451002 -0.053743

Here I am creating a function that will perform this same return calculation for other data sets that will be analyzed within this project.

In [7]:
# Since I will be calculating 1day, 7day, and 30day returns elsewhere, I am extracting out some operations from above
# into a function. Note that in the cryptocurrency data set, all days were given, and so we were able to simply go
# backwards in the index to find the correct date. However, some assets (such as stock markets) are not liquidly priced
# on weekends or holidays, and so this function will adjust by picking the closest date.
def calculatePeriodicReturnsDataFrame(
    dateAndPriceList: list,
    dateFormat: str = "%Y-%m-%d",
    symbol: str = None,
) -> pandas.DataFrame:
    
    """
    This function takes a list of this form:
    [
      [date1, value1],
      [date2, value2],
      ...
      [dateN, valueN],
    ]
    
    and returns a pandas.DataFrame with the price, 1day, 7day, and 30day returns as columns, and with the date
    as an index
    
    Args:
        dateAndPriceList (list): is a 2-dimensional list of lists of date and price
        
    Returns:
        pandas.DataFrame: A DataFrame with date as the index and price, 1day return, 7day return, and 30day return
            as the columns
    """
    
    from datetime import datetime
    from datetime import timedelta
    import collections
    
    # Modifying the original date and price list to interpolate any data points that don't exist within
    # the date range of the list. Interpolation here will be to simply use the most recent data point temporally
    # to fill in older data points.
    
    # Getting the highest and lowest date, with the assumption that dates are ordered
    upperBoundDate = datetime.strptime(dateAndPriceList[-1][0], dateFormat)
    lowerBoundDate = datetime.strptime(dateAndPriceList[0][0], dateFormat)
    
    # Creating a dictionary of dates
    dateAndPriceDict = dict(dateAndPriceList)
    
    # Filling in every date within the dictionary that is between the upper and lower bound that
    # is not within the dictionary already.
    movableDateValue = None
    for i in range((upperBoundDate - lowerBoundDate).days - 1):
        dateKey = datetime.strftime(upperBoundDate - timedelta(days=i + 1), dateFormat)
        if dateKey not in dateAndPriceDict.keys():
            dateAndPriceDict[dateKey] = movableDateValue
        else:
            movableDateValue = dateAndPriceDict[dateKey]
    
    
    dataList = list()
    
    # Iterating through the data and creating various periodic returns
    for i in range(len(dateAndPriceList)):
        
        # Date
        dataDate = datetime.strptime(dateAndPriceList[i][0], dateFormat)
        dataDate = datetime.strftime(dataDate, "%Y-%m-%d")
        
        # Price
        dataValue = dateAndPriceList[i][1]
        
        # 1 Day Return or None if not able to calculate
        if i < 1:
            data1day = None
        else:
            data1day = (dateAndPriceList[i][1] / dateAndPriceList[i - 1][1]) - 1
        
        # 7 Day Return or None if not able to calculate
        if i < 6:
            data7day = None
        else:
            data7day = (dateAndPriceList[i][1] / dateAndPriceList[i - 6][1]) - 1
        
        # 30 Day Return or None if not able to calculate
        if i < 29:
            data30day = None
        else:
            data30day = (dateAndPriceList[i][1] / dateAndPriceList[i - 29][1]) - 1
        
        # Adding all of the data to a list
        dataList.append(collections.OrderedDict([
            ["date", dataDate],
            [symbol, dataValue],
            [symbol + "_1_Day_Return", data1day],
            [symbol + "_7_Day_Return", data7day],
            [symbol + "_30_Day_Return", data30day],
        ]))
        
    
    # Creating a new DataFrame with the new data
    df = pandas.DataFrame(dataList)
    df.index = df["date"]
    df = df.drop("date", axis=1)
    
    return df
    

Gold and S&P Price Data Set

https://www.kaggle.com/altruistdelhite04/gold-price-data

The Gold and S&P Price Data Set contains the daily closing price data of gold and the S&P 500 index, among other data, for periods of time from 2008 to 2018.

From this data set, I will only be using the gold prices and S&P 500 values, so here I am removing all other columns except for these ones.

In [8]:
# Reading the Gold CSV from the data folder and creating a DataFrame
goldCsvPath = os.path.join(DATA_DIRECTORY, "gld_price_data.csv")
goldDataFrame = pandas.read_csv(goldCsvPath)

# Removing excess columns for assets that won't be used
goldDataFrame = goldDataFrame.drop([
    "USO",
    "SLV",
    "EUR/USD",
], axis=1)

display(goldDataFrame.head())
Date SPX GLD
0 1/2/2008 1447.160034 84.860001
1 1/3/2008 1447.160034 85.570000
2 1/4/2008 1411.630005 85.129997
3 1/7/2008 1416.180054 84.769997
4 1/8/2008 1390.189941 86.779999

Like the other data sets, I am calculating periodic returns and then adding these DataFrames to the running list of DataFrames.

In [9]:
# Creating 2 DataFrames, one for the Gold data and one for the S&P 500 data, including calculating 1-day, 7-day,
# and 30-days returns for each DataFrame, and then adding them to the Running DataFrame List
goldDateAndValueList = list([row[0], row[2]] for row in goldDataFrame.values)
snpDateAndValueList = list([row[0], row[1]] for row in goldDataFrame.values)

newGoldDataFrame = calculatePeriodicReturnsDataFrame(goldDateAndValueList, "%m/%d/%Y", "GLD")
newSnpDataFrame = calculatePeriodicReturnsDataFrame(snpDateAndValueList, "%m/%d/%Y", "SNP")

RunningDataFrameList.append(newGoldDataFrame)
RunningDataFrameList.append(newSnpDataFrame)

display(newGoldDataFrame.tail())
display(newSnpDataFrame.tail())
GLD GLD_1_Day_Return GLD_7_Day_Return GLD_30_Day_Return
date
2018-05-08 124.589996 0.000161 -0.003041 0.002252
2018-05-09 124.330002 -0.002087 -0.002087 -0.016999
2018-05-10 125.180000 0.006837 0.011883 -0.006350
2018-05-14 124.489998 -0.005512 0.006793 -0.024450
2018-05-16 122.543800 -0.015633 -0.013970 -0.044716
SNP SNP_1_Day_Return SNP_7_Day_Return SNP_30_Day_Return
date
2018-05-08 2671.919922 -0.000266 0.001867 -0.016570
2018-05-09 2697.790039 0.009682 0.018784 -0.005214
2018-05-10 2723.070068 0.009371 0.025716 0.030026
2018-05-14 2730.129883 0.002593 0.035839 0.054813
2018-05-16 2725.780029 -0.001593 0.036525 0.025288

Oil Data Set

https://www.kaggle.com/mabusalah/brent-oil-prices

The Oil Data Set contains daily price data of oil (specifically Brent Crude) from 1987 to 2020.

Creating and displaying the DataFrame from the CSV.

In [10]:
# Reading the Oil CSV from the data folder and creating a DataFrame
oilCsvPath = os.path.join(DATA_DIRECTORY, "BrentOilPrices.csv")
oilDataFrame = pandas.read_csv(oilCsvPath)

display(oilDataFrame)
Date Price
0 20-May-87 18.63
1 21-May-87 18.45
2 22-May-87 18.55
3 25-May-87 18.60
4 26-May-87 18.63
... ... ...
8317 19-Feb-20 59.72
8318 20-Feb-20 59.72
8319 21-Feb-20 58.60
8320 24-Feb-20 56.04
8321 25-Feb-20 56.71

8322 rows × 2 columns

Creating a new DataFrame from this with 1-day, 7-day, and 30-day returns.

In [11]:
# Creating a new DataFrame for the Oil data set, including 1-day, 7-day, and 30-day returns
oilDateAndValueList = list([row[0], row[1]] for row in oilDataFrame.values)

newOilDataFrame = calculatePeriodicReturnsDataFrame(oilDateAndValueList, "%d-%b-%y", "OIL")

RunningDataFrameList.append(newOilDataFrame)

display(newOilDataFrame.tail())
OIL OIL_1_Day_Return OIL_7_Day_Return OIL_30_Day_Return
date
2020-02-19 59.72 0.041325 0.105926 -0.103034
2020-02-20 59.72 0.000000 0.075261 -0.105586
2020-02-21 58.60 -0.018754 0.040114 -0.086374
2020-02-24 56.04 -0.043686 -0.023183 -0.130489
2020-02-25 56.71 0.011956 -0.019367 -0.103966

Wheat Data Set

https://www.kaggle.com/nickwong64/daily-wheat-price

The wheat data set includes OHLC data for wheat from 2009 to 2018.

Creating a DataFrame from the wheat data and dropping all unneeded columns.

In [12]:
# Reading the Wheat CSV from the data folder and creating a DataFrame
wheatCsvPath = os.path.join(DATA_DIRECTORY, "wheat_200910-201803.csv")
wheatDataFrame = pandas.read_csv(wheatCsvPath)

wheatDataFrame = wheatDataFrame.drop([
    "open",
    "high",
    "low",
], axis=1)

display(wheatDataFrame)
date close
0 2018-03-12 490.00
1 2018-03-09 490.25
2 2018-03-08 499.25
3 2018-03-07 497.50
4 2018-03-06 506.00
... ... ...
2267 2009-10-19 517.50
2268 2009-10-18 497.50
2269 2009-10-16 497.75
2270 2009-10-15 505.38
2271 2009-10-14 513.50

2272 rows × 2 columns

Creating a new DataFrame from this with 1-day, 7-day, and 30-day returns.

In [13]:
# Creating a new DataFrame for the Wheat data set, including 1day, 7day, and 30day returns
wheatDateAndValueList = list([row[0], row[1]] for row in wheatDataFrame.values)

newWheatDataFrame = calculatePeriodicReturnsDataFrame(wheatDateAndValueList, "%Y-%m-%d", "WHEAT")

RunningDataFrameList.append(newWheatDataFrame)

display(newWheatDataFrame.tail())
WHEAT WHEAT_1_Day_Return WHEAT_7_Day_Return WHEAT_30_Day_Return
date
2009-10-19 517.50 -0.002660 -0.018269 -0.105260
2009-10-18 497.50 -0.038647 -0.091125 -0.141501
2009-10-16 497.75 0.000503 -0.090037 -0.108183
2009-10-15 505.38 0.015329 -0.085086 -0.101946
2009-10-14 513.50 0.016067 -0.053247 -0.091360

Combining all the DataFrames

Now that all of the DataFrames have been collected into a common format, we can start manipulating the data into a format that can be analyzed to proxy event risk. In the first step I'll combine all the DataFrames into a single large DataFrame

In [14]:
# Running joins on all the individual DataFrames into a single large DataFrame
df = RunningDataFrameList[0]

for i in range(1, len(RunningDataFrameList)):
    df = df.join(RunningDataFrameList[i])

print("="*80)
print("All Columns in the DataFrame:")
print("="*80)
print(df.columns)
print()
print()

print("="*80)
print("Partial Snapshot of the DataFrame")
print("="*80)
display(df)
================================================================================
All Columns in the DataFrame:
================================================================================
Index(['BTC', 'BTC_1_Day_Return', 'BTC_30_Day_Return', 'BTC_7_Day_Return',
       'XRP', 'XRP_1_Day_Return', 'XRP_30_Day_Return', 'XRP_7_Day_Return',
       'ETH', 'ETH_1_Day_Return', 'ETH_30_Day_Return', 'ETH_7_Day_Return',
       'BCH', 'BCH_1_Day_Return', 'BCH_30_Day_Return', 'BCH_7_Day_Return',
       'LTC', 'LTC_1_Day_Return', 'LTC_30_Day_Return', 'LTC_7_Day_Return',
       'USDT', 'USDT_1_Day_Return', 'USDT_30_Day_Return', 'USDT_7_Day_Return',
       'XMR', 'XMR_1_Day_Return', 'XMR_30_Day_Return', 'XMR_7_Day_Return',
       'ETC', 'ETC_1_Day_Return', 'ETC_30_Day_Return', 'ETC_7_Day_Return',
       'GLD', 'GLD_1_Day_Return', 'GLD_7_Day_Return', 'GLD_30_Day_Return',
       'SNP', 'SNP_1_Day_Return', 'SNP_7_Day_Return', 'SNP_30_Day_Return',
       'OIL', 'OIL_1_Day_Return', 'OIL_7_Day_Return', 'OIL_30_Day_Return',
       'WHEAT', 'WHEAT_1_Day_Return', 'WHEAT_7_Day_Return',
       'WHEAT_30_Day_Return'],
      dtype='object')


================================================================================
Partial Snapshot of the DataFrame
================================================================================
BTC BTC_1_Day_Return BTC_30_Day_Return BTC_7_Day_Return XRP XRP_1_Day_Return XRP_30_Day_Return XRP_7_Day_Return ETH ETH_1_Day_Return ... SNP_7_Day_Return SNP_30_Day_Return OIL OIL_1_Day_Return OIL_7_Day_Return OIL_30_Day_Return WHEAT WHEAT_1_Day_Return WHEAT_7_Day_Return WHEAT_30_Day_Return
date
2013-04-28 134.21 NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2013-04-29 144.54 0.076969 NaN NaN NaN NaN NaN NaN NaN NaN ... 0.026804 0.026493 102.88 0.004786 0.039822 -0.052147 715.25 -0.020541 0.010768 0.036219
2013-04-30 139.00 -0.038328 NaN NaN NaN NaN NaN NaN NaN NaN ... 0.036300 0.027693 101.53 -0.013122 0.024831 -0.050323 730.25 0.014758 0.035082 0.049014
2013-05-01 116.99 -0.158345 NaN NaN NaN NaN NaN NaN NaN NaN ... 0.012928 0.012455 98.34 -0.031419 -0.009169 -0.091715 719.63 -0.013192 -0.006214 0.055362
2013-05-02 105.21 -0.100692 NaN NaN NaN NaN NaN NaN NaN NaN ... 0.011914 0.023637 100.32 0.020134 -0.003873 -0.057231 729.25 0.010391 0.036603 0.063620
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2018-11-25 4009.97 0.033295 -0.381214 -0.176849 0.374551 -0.002091 -0.179243 -0.216693 116.45 0.026082 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2018-11-26 3779.13 -0.057567 -0.417375 -0.151114 0.355451 -0.050994 -0.231301 -0.182356 108.33 -0.069729 ... NaN NaN 59.70 0.034841 -0.085618 -0.258661 NaN NaN NaN NaN
2018-11-27 3820.72 0.011005 -0.396661 -0.169800 0.360163 0.013256 -0.184255 -0.195438 110.01 0.015508 ... NaN NaN 59.58 -0.002010 -0.071094 -0.254411 NaN NaN NaN NaN
2018-11-28 4257.42 0.114298 -0.327875 -0.024856 0.390557 0.084390 -0.121868 -0.083372 122.44 0.112990 ... NaN NaN 57.97 -0.027022 -0.057398 -0.278082 NaN NaN NaN NaN
2018-11-29 4278.85 0.005034 -0.322711 -0.015702 0.379562 -0.028152 -0.153485 -0.068395 117.54 -0.040020 ... NaN NaN 58.29 0.005520 -0.054501 -0.274820 NaN NaN NaN NaN

2042 rows × 48 columns

Checking Skewness and Kurtosis

Here I'm checking the Skewness and Kurtosis of the financial data within the DataFrame. What we'll typically find in financial return data sets is that these data sets are generally non-normal, with some level of Skewness and Kurtosis. The Skewness and Kurtosis can be indicative of events within the markets, as typically large positive or negative returns in short periods of time are caused by market moving events.

In Pandas, the skew() and kurt() functions calculate Skewness and Kurtosis, with values at 0 for Skewness indicating symmetrical distributions and values at 0 for Kurtosis indicating that there aren't fat tails within the distribution. Below we can see some level of Skewness and Kurtosis in the equity markets, providing a case for diversifying away this event risk. However, notice the massive level of Skewness and Kurtosis in the cryptocurrency markets. These levels of Skewness and Kurtosis will likely make many cryptocurrencies unsuitable for event risk reduction.

In [15]:
# Calculating skewness and kurtosis for all columns in the DataFrame
print("="*80)
print("Skewness")
print("="*80)
display(df.skew().sort_values().to_frame())
print()

print("="*80)
print("Kurtosis")
print("="*80)
display(df.kurt().sort_values().to_frame())
================================================================================
Skewness
================================================================================
0
USDT -10.138862
SNP_1_Day_Return -0.972926
SNP_7_Day_Return -0.714990
SNP_30_Day_Return -0.594546
WHEAT_7_Day_Return -0.406082
WHEAT_1_Day_Return -0.225449
GLD -0.222250
GLD_7_Day_Return -0.165453
OIL_30_Day_Return -0.132258
GLD_30_Day_Return 0.106058
GLD_1_Day_Return 0.172518
ETH_1_Day_Return 0.262187
OIL_7_Day_Return 0.347757
WHEAT_30_Day_Return 0.425674
OIL_1_Day_Return 0.465145
BTC_1_Day_Return 0.495120
SNP 0.525255
OIL 0.540363
WHEAT 0.691561
ETC 0.754617
BTC_7_Day_Return 1.280888
BCH_1_Day_Return 1.451343
ETH 1.502035
XMR_1_Day_Return 1.614428
BCH 1.637178
XMR_7_Day_Return 1.881334
ETC_30_Day_Return 1.888610
ETH_30_Day_Return 1.968226
BTC 2.017056
XMR 2.054447
BCH_7_Day_Return 2.209482
BCH_30_Day_Return 2.228723
ETH_7_Day_Return 2.292006
LTC 2.693662
ETC_7_Day_Return 2.827419
XMR_30_Day_Return 3.752723
XRP 3.979198
LTC_1_Day_Return 4.852829
BTC_30_Day_Return 4.884040
XRP_30_Day_Return 5.035727
XRP_1_Day_Return 6.066452
XRP_7_Day_Return 6.203629
LTC_7_Day_Return 6.380284
LTC_30_Day_Return 8.700232
USDT_1_Day_Return 9.150345
USDT_30_Day_Return 14.897765
ETC_1_Day_Return 16.270069
USDT_7_Day_Return 18.890282
================================================================================
Kurtosis
================================================================================
0
OIL -1.100416
WHEAT -0.698106
SNP -0.364892
ETC 0.161747
GLD 0.163701
GLD_30_Day_Return 0.404051
OIL_30_Day_Return 0.849412
WHEAT_1_Day_Return 0.980568
WHEAT_7_Day_Return 1.030053
WHEAT_30_Day_Return 1.036040
GLD_7_Day_Return 1.100769
SNP_30_Day_Return 1.161007
ETH 1.862586
OIL_7_Day_Return 2.130835
SNP_7_Day_Return 2.809756
BCH 3.004230
OIL_1_Day_Return 3.103932
GLD_1_Day_Return 3.180738
XMR 3.887892
BTC 3.908089
ETC_30_Day_Return 4.111710
ETH_30_Day_Return 4.385810
BCH_30_Day_Return 5.542306
BTC_7_Day_Return 6.631708
XMR_7_Day_Return 6.989929
SNP_1_Day_Return 7.049971
BCH_1_Day_Return 7.458332
BCH_7_Day_Return 7.583009
LTC 7.820343
ETH_7_Day_Return 9.379650
BTC_1_Day_Return 10.007810
XMR_1_Day_Return 11.654221
ETH_1_Day_Return 13.286047
ETC_7_Day_Return 18.204210
XMR_30_Day_Return 20.954044
XRP 22.825490
XRP_30_Day_Return 30.580268
BTC_30_Day_Return 35.109893
LTC_7_Day_Return 66.853495
LTC_1_Day_Return 67.034438
XRP_7_Day_Return 67.192475
XRP_1_Day_Return 97.119538
LTC_30_Day_Return 97.802875
USDT 249.236971
USDT_30_Day_Return 322.629709
ETC_1_Day_Return 387.185732
USDT_7_Day_Return 457.514636
USDT_1_Day_Return 638.516826

Detecting Events

To capture the presence of events, I am using an IQR-score with a threshold of 3.0 to indicate an outlier. Since concerns regarding event risk are typically asymmetric (people want to avoid negative events, but want the benefits of the positive events), I am filtering out positive events and only including negative events. These two functions calculate left-sided IQR scores for a specified Series, and then displays correlations and partial DataFrames for two specified columns within our main DataFrame. Once these functions are in place, we can start analyzing event risk correlations.

In [16]:
# Creating a function to calculate IQR-Scores for a column
def calculateIQRSeries(
    ser: pandas.Series,
    iqrThreshold: float = 1.5,
) -> pandas.Series:
    
    """
    This function takes a pandas.Series and calculates whether every value falls to
    the left of the first quartile times a specified threshold (with the default
    threshold being 1.5). For example, if the first quartile is -10 then in the
    Series returned will contain True cells for all cells with values below
    -10 - abs(-10 * 1.5) = -10 - 15 = -25.
    
    Args:
        ser (pandas.Series): The Series that will have the IQR operations performed on
        
    Returns:
        pandas.Series: a Series with True values in all cells that fall to the left
            of the IQR score.
    """
    
    iqrSer = (ser < (ser.quantile(0.25) - abs(ser.quantile(0.25) * iqrThreshold)))
    iqrSer.name = ser.name + "_IQR_Outlier"
    
    return iqrSer
In [17]:
def displayDfCorrAndValues(
    df: pandas.DataFrame,
    colName1: str, 
    colName2: str,
) -> None:

    """
    This function takes a pandas.DataFrame and two columns from the DataFrame and
    calculates their event risk correlations, as well as displaying them within
    a jupyter notebook.
    
    Args:
        df (pandas.DataFrame): The DataFrame that will be used for the calculations
        colName1 (str): the name of the column within the DataFrame that will be used
            to calculate event risk and IQR scores
        colName2 (str): the name of the other column that will be used to calculate
            correlations relative to the events of the first column (as measured by
            their IQR scores)
            
    Returns:
        None
    """
    
    # Adding the IQR Score column to the DataFrame
    df = pandas.concat([df, calculateIQRSeries(df[colName1])], axis=1)
    
    # Filtering the DataFrame for only the two columns and with only outliers remaining
    analysisDf = df[[colName1, colName2, colName1 + "_IQR_Outlier"]]
    analysisDf = analysisDf[analysisDf[colName1 + "_IQR_Outlier"]].dropna()
    analysisDf = analysisDf.drop(colName1 + "_IQR_Outlier", axis=1)
    
    # Displaying the correlations and the filtered, outlier only DataFrame
    display(analysisDf.corr())
    display(analysisDf.head())
    display(analysisDf.tail())

US Equity Markets Event Risk Hedging Analysis

Below is the analysis for event risk hedging of a large-cap US Equity market index versus various cryptocurrencies, gold, oil, and wheat.

S&P 500 Events Versus Bitcoin Returns

Below we can see a slight positive correlation between S&P 500 events and Bitcoin returns when looking at 7-day and 30-day returns. While not negative, which is what we would hope for to get the full diversification benefits, the slight positive correlation will still provide some diversification benefits from negative events. However, the heavy skewness and kurtosis still pose a problem, as they add in their own seperate event risks.

In [18]:
displayDfCorrAndValues(df, "SNP_7_Day_Return", "BTC_7_Day_Return")
print("="*80)
displayDfCorrAndValues(df, "SNP_30_Day_Return", "BTC_30_Day_Return")
SNP_7_Day_Return BTC_7_Day_Return
SNP_7_Day_Return 1.000000 0.098486
BTC_7_Day_Return 0.098486 1.000000
SNP_7_Day_Return BTC_7_Day_Return
date
2013-06-03 -0.017218 -0.052558
2013-06-05 -0.025210 -0.055512
2013-06-06 -0.022590 -0.085271
2013-06-11 -0.017094 -0.104809
2013-06-12 -0.017008 -0.083475
SNP_7_Day_Return BTC_7_Day_Return
date
2018-03-28 -0.041201 -0.088674
2018-03-29 -0.026203 -0.193017
2018-04-02 -0.023380 -0.095651
2018-04-26 -0.014577 0.049253
2018-04-30 -0.022369 -0.047120
================================================================================
SNP_30_Day_Return BTC_30_Day_Return
SNP_30_Day_Return 1.00000 0.14861
BTC_30_Day_Return 0.14861 1.00000
SNP_30_Day_Return BTC_30_Day_Return
date
2013-09-03 -0.021045 0.287611
2013-09-04 -0.017486 0.187260
2013-10-08 -0.017764 -0.008733
2014-01-31 -0.015497 0.034310
2014-02-03 -0.037417 -0.117511
SNP_30_Day_Return BTC_30_Day_Return
date
2018-05-01 -0.034436 0.287305
2018-05-02 -0.040643 0.238705
2018-05-03 -0.044433 0.421664
2018-05-07 -0.014851 0.334517
2018-05-08 -0.016570 0.363933

S&P 500 Events Versus Ethereum Returns

The slight positive correlation for 7-day returns and negative correlation for 30-day returns provides some justification for event risk hedging with Ethereum, but like most cryptocurrencies analyzed, skewness and kurtosis are quite high, and the market cap of Ethereum puts constraints on using Ethereum to event risk hedge for large investors.

In [19]:
displayDfCorrAndValues(df, "SNP_7_Day_Return", "ETH_7_Day_Return")
print("="*80)
displayDfCorrAndValues(df, "SNP_30_Day_Return", "ETH_30_Day_Return")
SNP_7_Day_Return ETH_7_Day_Return
SNP_7_Day_Return 1.000000 0.164142
ETH_7_Day_Return 0.164142 1.000000
SNP_7_Day_Return ETH_7_Day_Return
date
2015-08-20 -0.023195 -0.202186
2015-08-24 -0.092443 0.128440
2015-08-25 -0.103572 -0.095238
2015-08-26 -0.077020 -0.205479
2015-08-27 -0.052105 -0.178571
SNP_7_Day_Return ETH_7_Day_Return
date
2018-03-28 -0.041201 -0.173096
2018-03-29 -0.026203 -0.284737
2018-04-02 -0.023380 -0.141518
2018-04-26 -0.014577 0.076480
2018-04-30 -0.022369 -0.053999
================================================================================
SNP_30_Day_Return ETH_30_Day_Return
SNP_30_Day_Return 1.000000 -0.435693
ETH_30_Day_Return -0.435693 1.000000
SNP_30_Day_Return ETH_30_Day_Return
date
2015-09-08 -0.072909 0.750305
2015-09-09 -0.087507 0.130841
2015-09-10 -0.078765 -0.040984
2015-09-14 -0.076210 -0.442279
2015-09-15 -0.059016 -0.212992
SNP_30_Day_Return ETH_30_Day_Return
date
2018-05-01 -0.034436 0.743207
2018-05-02 -0.040643 0.648277
2018-05-03 -0.044433 1.048510
2018-05-07 -0.014851 0.881901
2018-05-08 -0.016570 0.889092

S&P 500 Events Versus Litecoin Returns

Litecoin had slight positive correlations with S&P 500 negative events, but suffers from high skewness and kurtosis and an even lower market cap available, making it very unlikely to be suitable for event risk hedging at present.

In [20]:
displayDfCorrAndValues(df, "SNP_7_Day_Return", "LTC_7_Day_Return")
print("="*80)
displayDfCorrAndValues(df, "SNP_30_Day_Return", "LTC_30_Day_Return")
SNP_7_Day_Return LTC_7_Day_Return
SNP_7_Day_Return 1.00000 0.13179
LTC_7_Day_Return 0.13179 1.00000
SNP_7_Day_Return LTC_7_Day_Return
date
2013-06-03 -0.017218 -0.116505
2013-06-05 -0.025210 -0.037037
2013-06-06 -0.022590 -0.070946
2013-06-11 -0.017094 -0.160839
2013-06-12 -0.017008 -0.134545
SNP_7_Day_Return LTC_7_Day_Return
date
2018-03-28 -0.041201 -0.195027
2018-03-29 -0.026203 -0.316690
2018-04-02 -0.023380 -0.121299
2018-04-26 -0.014577 -0.011958
2018-04-30 -0.022369 -0.104355
================================================================================
SNP_30_Day_Return LTC_30_Day_Return
SNP_30_Day_Return 1.000000 0.141707
LTC_30_Day_Return 0.141707 1.000000
SNP_30_Day_Return LTC_30_Day_Return
date
2013-09-03 -0.021045 -0.081272
2013-09-04 -0.017486 -0.003546
2013-10-08 -0.017764 -0.274725
2014-01-31 -0.015497 -0.140916
2014-02-03 -0.037417 -0.183377
SNP_30_Day_Return LTC_30_Day_Return
date
2018-05-01 -0.034436 0.244573
2018-05-02 -0.040643 0.127240
2018-05-03 -0.044433 0.363567
2018-05-07 -0.014851 0.401207
2018-05-08 -0.016570 0.388121

S&P 500 Events Versus Gold Returns

Precious metals have historically provided diversification from equity market event risk, providing offsetting returns in times of recessions to the losses typically incurred on equities. The negative correlations, size, maturity, and stability of the gold markets, and lower skewness and kurtosis, make it suitable for event risk hedging. While the largest cost for this strategy will be lower returns due to the low returns of gold (gold typically provides mostly inflation hedging returns in the long run), and while cryptocurrencies have had extraordinary returns in recent history, there is not enough historical data and maturity within the cryptocurrency markets to justify the hedge versus gold.

In [21]:
displayDfCorrAndValues(df, "SNP_7_Day_Return", "GLD_7_Day_Return")
print("="*80)
displayDfCorrAndValues(df, "SNP_30_Day_Return", "GLD_30_Day_Return")
SNP_7_Day_Return GLD_7_Day_Return
SNP_7_Day_Return 1.000000 -0.127596
GLD_7_Day_Return -0.127596 1.000000
SNP_7_Day_Return GLD_7_Day_Return
date
2013-06-03 -0.017218 0.027318
2013-06-05 -0.025210 0.006760
2013-06-06 -0.022590 0.022773
2013-06-11 -0.017094 -0.025238
2013-06-12 -0.017008 -0.016556
SNP_7_Day_Return GLD_7_Day_Return
date
2018-03-28 -0.041201 0.011423
2018-03-29 -0.026203 -0.005455
2018-04-02 -0.023380 0.010160
2018-04-26 -0.014577 -0.021761
2018-04-30 -0.022369 -0.025499
================================================================================
SNP_30_Day_Return GLD_30_Day_Return
SNP_30_Day_Return 1.00000 -0.34991
GLD_30_Day_Return -0.34991 1.00000
SNP_30_Day_Return GLD_30_Day_Return
date
2013-09-03 -0.021045 0.098036
2013-09-04 -0.017486 0.084394
2013-10-08 -0.017764 -0.012403
2014-01-31 -0.015497 0.021087
2014-02-03 -0.037417 0.056610
SNP_30_Day_Return GLD_30_Day_Return
date
2018-05-01 -0.034436 -0.015831
2018-05-02 -0.040643 -0.010008
2018-05-03 -0.044433 -0.002568
2018-05-07 -0.014851 -0.002403
2018-05-08 -0.016570 0.002252

S&P 500 Events Versus Oil Returns

Oil provides a low positive correlation to S&P events, but itself has events associated with it that may correlate with recessions. During economic downturns, energies tend to decline in value as economic activities that utilize these energies declines. Thus, oil is unlikely to provide better event risk hedges than an asset like gold. Additionally, cryptocurrencies may provide better event risk hedging than energies from downturns, but still perhaps worse than gold given the gold market's desirable characteristics.

In [22]:
displayDfCorrAndValues(df, "SNP_7_Day_Return", "OIL_7_Day_Return")
print("="*80)
displayDfCorrAndValues(df, "SNP_30_Day_Return", "OIL_30_Day_Return")
SNP_7_Day_Return OIL_7_Day_Return
SNP_7_Day_Return 1.000000 0.307944
OIL_7_Day_Return 0.307944 1.000000
SNP_7_Day_Return OIL_7_Day_Return
date
2013-06-03 -0.017218 0.011646
2013-06-05 -0.025210 -0.002506
2013-06-06 -0.022590 0.012042
2013-06-11 -0.017094 -0.001279
2013-06-12 -0.017008 0.010486
SNP_7_Day_Return OIL_7_Day_Return
date
2018-03-27 -0.036971 0.058778
2018-03-28 -0.041201 0.032273
2018-03-29 -0.026203 0.012766
2018-04-26 -0.014577 0.022515
2018-04-30 -0.022369 0.017422
================================================================================
SNP_30_Day_Return OIL_30_Day_Return
SNP_30_Day_Return 1.000000 0.262737
OIL_30_Day_Return 0.262737 1.000000
SNP_30_Day_Return OIL_30_Day_Return
date
2013-09-03 -0.021045 0.056923
2013-09-04 -0.017486 0.068558
2013-10-08 -0.017764 -0.040361
2014-01-31 -0.015497 -0.023650
2014-02-03 -0.037417 -0.049933
SNP_30_Day_Return OIL_30_Day_Return
date
2018-04-30 -0.042404 0.173779
2018-05-01 -0.034436 0.154736
2018-05-02 -0.040643 0.108182
2018-05-03 -0.044433 0.077770
2018-05-08 -0.016570 0.074316

S&P 500 Events Versus Wheat Returns

The slight negative correlations in wheat for 7-day returns and slight positive correlation in 30-day returns provides some justification for event risk hedging using agricultural commodities. Correlations for wheat are similar to the correlations for Litecoin, but wheat would still likely be preferred given maturity, liquidity, and size of the market versus the Ethereum market.

In [23]:
displayDfCorrAndValues(df, "SNP_7_Day_Return", "WHEAT_7_Day_Return")
print("="*80)
displayDfCorrAndValues(df, "SNP_30_Day_Return", "WHEAT_30_Day_Return")
SNP_7_Day_Return WHEAT_7_Day_Return
SNP_7_Day_Return 1.000000 -0.041295
WHEAT_7_Day_Return -0.041295 1.000000
SNP_7_Day_Return WHEAT_7_Day_Return
date
2013-06-03 -0.017218 0.019034
2013-06-05 -0.025210 0.022972
2013-06-06 -0.022590 0.025502
2013-06-11 -0.017094 -0.012764
2013-06-12 -0.017008 -0.025719
SNP_7_Day_Return WHEAT_7_Day_Return
date
2018-02-06 -0.055507 -0.017592
2018-02-07 -0.049875 -0.001083
2018-02-08 -0.085987 -0.003275
2018-02-09 -0.071733 -0.019105
2018-02-12 -0.038423 0.013646
================================================================================
SNP_30_Day_Return WHEAT_30_Day_Return
SNP_30_Day_Return 1.000000 0.136554
WHEAT_30_Day_Return 0.136554 1.000000
SNP_30_Day_Return WHEAT_30_Day_Return
date
2013-09-03 -0.021045 -0.064144
2013-09-04 -0.017486 -0.057247
2013-10-08 -0.017764 0.079003
2014-01-31 -0.015497 -0.191704
2014-02-03 -0.037417 -0.164164
SNP_30_Day_Return WHEAT_30_Day_Return
date
2016-11-04 -0.028911 0.013456
2017-04-13 -0.027968 -0.002322
2017-04-17 -0.013817 -0.039886
2017-04-18 -0.017175 -0.016605
2017-04-19 -0.015636 -0.025611

Conclusion

This analysis shows that currently there is not enough evidence to indicate that a trading strategy involving cryptocurrencies can be used to reduce event risk of an equity portfolio in a more cost effective way than other methods of event risk hedging (such as by using precious metals, other commodities, or option strategies). While cryptocurrencies have had extraordinary returns within the past 10 years, there is no guarantee given the underlying fundamentals of cryptocurrencies that cryptocurrencies will produce those same returns in the future. Additionally, given the small size and relatively short history of the market compared to other markets that can provide hedges to event risk (especially the gold markets), cryptocurrencies are unlikely to be a good choice to use for hedging event risk.

As the cryptocurrency markets mature in the future and become more stable and liquid, there might be a case for cheaper event risk hedging with cryptocurrencies than, or in addition to, other markets. For example, Ethereum had decently high negative correlations with S&P 500 events in 30-day returns. Additionally skewness and kurtosis may be reduced in the future for cryptocurrencies as the markets mature, meaning there could be some viability to event risk hedging in the future with cryptocurrencies.

Serverless Cryptocurrency Event Risk Hedging Analyzer

To test other data sets for event risk hedging capabilities of cryptocurrencies, please check out the application I built in tandem with this project, which can be found here.

Contact

For any questions or comments, please feel free to reach out to me at anthony.m.mancini@protonmail.com