Euros vs. Dollars: Covid Times

An quick analysis of the EUR-USD exchage rates from 2016-21

Index

1

Introduction

image.png Source: FXEMPIRE

The foreign exchange market or Forex allows in trading foreign currency. Unlike the stock market where shares can be brought and sold, the forex requires buying one currency in exchange of another. Depending on how much a currency is exchanged for, one could make or lose money in the foreign exchange market. e.g. If the EUR-USD curreny rate is 1.5, it means that for every 1 Euro someone doing the exchange would get US$1.5

Like the stock market the Forex is subject to market risks. Issues in a country or other parameters like inflation can cause the value of a currency to fall. Covid-19 was one such crisis that affected Forex markets worldwide.

In this project we are going to do a quick analysis of the impact of Covid on EUR-USD exchange rate. The goal will be to:

  • Compare how the EUR-USD exchange rate was affected by Covid-19 versus its trends before the crisis hit.
  • Create a plot that encompasses the entire history of the trends.

2

Reading the Data

The data for the EU exchange rates for the period 1999-2021 is provided by Daria Chemkaeva who put together the data set and made it available on Kaggle. The dataset is regularly updated. The dataset used for this project was downloaded on January 2021.

We shall assess the data below.

In [1]:
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt

exchange_rates = pd.read_csv('euro-daily-hist_1999_2020.csv')
exchange_rates
Out[1]:
Period\Unit: [Australian dollar ] [Bulgarian lev ] [Brazilian real ] [Canadian dollar ] [Swiss franc ] [Chinese yuan renminbi ] [Cypriot pound ] [Czech koruna ] [Danish krone ] ... [Romanian leu ] [Russian rouble ] [Swedish krona ] [Singapore dollar ] [Slovenian tolar ] [Slovak koruna ] [Thai baht ] [Turkish lira ] [US dollar ] [South African rand ]
0 2021-01-08 1.5758 1.9558 6.5748 1.5543 1.0827 7.9184 NaN 26.163 7.4369 ... 4.8708 90.8000 10.0510 1.6228 NaN NaN 36.8480 9.0146 1.2250 18.7212
1 2021-01-07 1.5836 1.9558 6.5172 1.5601 1.0833 7.9392 NaN 26.147 7.4392 ... 4.8712 91.2000 10.0575 1.6253 NaN NaN 36.8590 8.9987 1.2276 18.7919
2 2021-01-06 1.5824 1.9558 6.5119 1.5640 1.0821 7.9653 NaN 26.145 7.4393 ... 4.8720 90.8175 10.0653 1.6246 NaN NaN 36.9210 9.0554 1.2338 18.5123
3 2021-01-05 1.5927 1.9558 6.5517 1.5651 1.0803 7.9315 NaN 26.227 7.4387 ... 4.8721 91.6715 10.0570 1.6180 NaN NaN 36.7760 9.0694 1.2271 18.4194
4 2021-01-04 1.5928 1.9558 6.3241 1.5621 1.0811 7.9484 NaN 26.141 7.4379 ... 4.8713 90.3420 10.0895 1.6198 NaN NaN 36.7280 9.0579 1.2296 17.9214
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
5694 1999-01-08 1.8406 NaN NaN 1.7643 1.6138 NaN 0.58187 34.938 7.4433 ... 1.3143 27.2075 9.1650 1.9537 188.8400 42.560 42.5590 0.3718 1.1659 6.7855
5695 1999-01-07 1.8474 NaN NaN 1.7602 1.6165 NaN 0.58187 34.886 7.4431 ... 1.3092 26.9876 9.1800 1.9436 188.8000 42.765 42.1678 0.3701 1.1632 6.8283
5696 1999-01-06 1.8820 NaN NaN 1.7711 1.6116 NaN 0.58200 34.850 7.4452 ... 1.3168 27.4315 9.3050 1.9699 188.7000 42.778 42.6949 0.3722 1.1743 6.7307
5697 1999-01-05 1.8944 NaN NaN 1.7965 1.6123 NaN 0.58230 34.917 7.4495 ... 1.3168 26.5876 9.4025 1.9655 188.7750 42.848 42.5048 0.3728 1.1790 6.7975
5698 1999-01-04 1.9100 NaN NaN 1.8004 1.6168 NaN 0.58231 35.107 7.4501 ... 1.3111 25.2875 9.4696 1.9554 189.0450 42.991 42.6799 0.3723 1.1789 6.9358

5699 rows × 41 columns

In [2]:
exchange_rates.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5699 entries, 0 to 5698
Data columns (total 41 columns):
 #   Column                    Non-Null Count  Dtype  
---  ------                    --------------  -----  
 0   Period\Unit:              5699 non-null   object 
 1   [Australian dollar ]      5699 non-null   object 
 2   [Bulgarian lev ]          5297 non-null   object 
 3   [Brazilian real ]         5431 non-null   object 
 4   [Canadian dollar ]        5699 non-null   object 
 5   [Swiss franc ]            5699 non-null   object 
 6   [Chinese yuan renminbi ]  5431 non-null   object 
 7   [Cypriot pound ]          2346 non-null   object 
 8   [Czech koruna ]           5699 non-null   object 
 9   [Danish krone ]           5699 non-null   object 
 10  [Estonian kroon ]         3130 non-null   object 
 11  [UK pound sterling ]      5699 non-null   object 
 12  [Greek drachma ]          520 non-null    object 
 13  [Hong Kong dollar ]       5699 non-null   object 
 14  [Croatian kuna ]          5431 non-null   object 
 15  [Hungarian forint ]       5699 non-null   object 
 16  [Indonesian rupiah ]      5699 non-null   object 
 17  [Israeli shekel ]         5431 non-null   object 
 18  [Indian rupee ]           5431 non-null   object 
 19  [Iceland krona ]          3292 non-null   float64
 20  [Japanese yen ]           5699 non-null   object 
 21  [Korean won ]             5699 non-null   object 
 22  [Lithuanian litas ]       4159 non-null   object 
 23  [Latvian lats ]           3904 non-null   object 
 24  [Maltese lira ]           2346 non-null   object 
 25  [Mexican peso ]           5699 non-null   object 
 26  [Malaysian ringgit ]      5699 non-null   object 
 27  [Norwegian krone ]        5699 non-null   object 
 28  [New Zealand dollar ]     5699 non-null   object 
 29  [Philippine peso ]        5699 non-null   object 
 30  [Polish zloty ]           5699 non-null   object 
 31  [Romanian leu ]           5637 non-null   float64
 32  [Russian rouble ]         5699 non-null   object 
 33  [Swedish krona ]          5699 non-null   object 
 34  [Singapore dollar ]       5699 non-null   object 
 35  [Slovenian tolar ]        2085 non-null   object 
 36  [Slovak koruna ]          2608 non-null   object 
 37  [Thai baht ]              5699 non-null   object 
 38  [Turkish lira ]           5637 non-null   float64
 39  [US dollar ]              5699 non-null   object 
 40  [South African rand ]     5699 non-null   object 
dtypes: float64(3), object(38)
memory usage: 1.8+ MB

The dataset seems to have the following issues:

  • The column names are inconsistent to the Python standards and need correction.
  • The date column is a string. Converting it to DateTime data type would make analysis easier.
  • A number of columns have null data which will be individually corrected based on the currencies we will be evaluating.
  • Some columns have the string data type while others are of type float. This will be corrected on a need-only basis.

We shall rename the columns and correct the date column to make them more consistent.

In [3]:
#Clean the column names
exchange_rates.rename(columns = {"Period\\Unit:":"dates"},inplace = True)
exchange_rates.columns = exchange_rates.columns.str.replace(' ', '_')

#Remove square brackets
exchange_rates.columns = exchange_rates.columns.str.replace('[\[\]]','',regex=True).str.lower()

#Remove the last underscore from column names
exchange_rates.columns = exchange_rates.columns.str[:-1]
exchange_rates.columns
Out[3]:
Index(['date', 'australian_dollar', 'bulgarian_lev', 'brazilian_real',
       'canadian_dollar', 'swiss_franc', 'chinese_yuan_renminbi',
       'cypriot_pound', 'czech_koruna', 'danish_krone', 'estonian_kroon',
       'uk_pound_sterling', 'greek_drachma', 'hong_kong_dollar',
       'croatian_kuna', 'hungarian_forint', 'indonesian_rupiah',
       'israeli_shekel', 'indian_rupee', 'iceland_krona', 'japanese_yen',
       'korean_won', 'lithuanian_litas', 'latvian_lats', 'maltese_lira',
       'mexican_peso', 'malaysian_ringgit', 'norwegian_krone',
       'new_zealand_dollar', 'philippine_peso', 'polish_zloty', 'romanian_leu',
       'russian_rouble', 'swedish_krona', 'singapore_dollar',
       'slovenian_tolar', 'slovak_koruna', 'thai_baht', 'turkish_lira',
       'us_dollar', 'south_african_rand'],
      dtype='object')
In [4]:
#Set the date column to datetime format
exchange_rates['date']= pd.to_datetime(exchange_rates['date'])
exchange_rates.sort_values('date', inplace=True)
exchange_rates.reset_index(drop=True, inplace=True)
exchange_rates
Out[4]:
date australian_dollar bulgarian_lev brazilian_real canadian_dollar swiss_franc chinese_yuan_renminbi cypriot_pound czech_koruna danish_krone ... romanian_leu russian_rouble swedish_krona singapore_dollar slovenian_tolar slovak_koruna thai_baht turkish_lira us_dollar south_african_rand
0 1999-01-04 1.9100 NaN NaN 1.8004 1.6168 NaN 0.58231 35.107 7.4501 ... 1.3111 25.2875 9.4696 1.9554 189.0450 42.991 42.6799 0.3723 1.1789 6.9358
1 1999-01-05 1.8944 NaN NaN 1.7965 1.6123 NaN 0.58230 34.917 7.4495 ... 1.3168 26.5876 9.4025 1.9655 188.7750 42.848 42.5048 0.3728 1.1790 6.7975
2 1999-01-06 1.8820 NaN NaN 1.7711 1.6116 NaN 0.58200 34.850 7.4452 ... 1.3168 27.4315 9.3050 1.9699 188.7000 42.778 42.6949 0.3722 1.1743 6.7307
3 1999-01-07 1.8474 NaN NaN 1.7602 1.6165 NaN 0.58187 34.886 7.4431 ... 1.3092 26.9876 9.1800 1.9436 188.8000 42.765 42.1678 0.3701 1.1632 6.8283
4 1999-01-08 1.8406 NaN NaN 1.7643 1.6138 NaN 0.58187 34.938 7.4433 ... 1.3143 27.2075 9.1650 1.9537 188.8400 42.560 42.5590 0.3718 1.1659 6.7855
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
5694 2021-01-04 1.5928 1.9558 6.3241 1.5621 1.0811 7.9484 NaN 26.141 7.4379 ... 4.8713 90.3420 10.0895 1.6198 NaN NaN 36.7280 9.0579 1.2296 17.9214
5695 2021-01-05 1.5927 1.9558 6.5517 1.5651 1.0803 7.9315 NaN 26.227 7.4387 ... 4.8721 91.6715 10.0570 1.6180 NaN NaN 36.7760 9.0694 1.2271 18.4194
5696 2021-01-06 1.5824 1.9558 6.5119 1.5640 1.0821 7.9653 NaN 26.145 7.4393 ... 4.8720 90.8175 10.0653 1.6246 NaN NaN 36.9210 9.0554 1.2338 18.5123
5697 2021-01-07 1.5836 1.9558 6.5172 1.5601 1.0833 7.9392 NaN 26.147 7.4392 ... 4.8712 91.2000 10.0575 1.6253 NaN NaN 36.8590 8.9987 1.2276 18.7919
5698 2021-01-08 1.5758 1.9558 6.5748 1.5543 1.0827 7.9184 NaN 26.163 7.4369 ... 4.8708 90.8000 10.0510 1.6228 NaN NaN 36.8480 9.0146 1.2250 18.7212

5699 rows × 41 columns

3

Euro vs. Dollar!

Analysing the EUR-USD exchange rate

Now that we have a cleaner dataset we can begin analysis on the EUR-USD exchange rates for the period between 2016-2021. We would have to begin by first analysing the dataset itself as it was observed to have a number of null values. Besides the null values we may also need to remove any invalid values as well.

In [5]:
exchange_rates['us_dollar'].value_counts()
Out[5]:
-         62
1.2276     9
1.1215     8
1.1305     7
1.1268     6
          ..
1.2978     1
0.9745     1
1.1990     1
1.1093     1
1.1319     1
Name: us_dollar, Length: 3528, dtype: int64

There are 62 data points for which the exchange rate value is a hyphen(-) which will need to be removed.

In [6]:
#Consolidate and clean the us_dollar column
euro_dollar = exchange_rates.loc[:,['date','us_dollar']]

#Consider only datapoints with no hyphen
euro_dollar = euro_dollar[euro_dollar['us_dollar'] != '-' ].copy()
euro_dollar['us_dollar'] = euro_dollar['us_dollar'].astype('float')
euro_dollar.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 5637 entries, 0 to 5698
Data columns (total 2 columns):
 #   Column     Non-Null Count  Dtype         
---  ------     --------------  -----         
 0   date       5637 non-null   datetime64[ns]
 1   us_dollar  5637 non-null   float64       
dtypes: datetime64[ns](1), float64(1)
memory usage: 132.1 KB

The EUR-USD exchange rates are cleaner. The data in the dataset is for daily exchange rates. Our assessment is based on a long term view of about five years. Taking this in to consideration we shall calculate the mean value for each day based on the rolling average method with a rolling window of 90 days.

Based on this we could also plot the exchange rate movement over the above mentioned period.

In [7]:
#Calculate the rolling mean by setting the rolling window = 90 days (Quarterly)
euro_dollar['rolling_mean'] = euro_dollar['us_dollar'].rolling(90).mean()

#Calculate the min and max for each year
euro_dollar['year'] = euro_dollar['date'].dt.year
In [8]:
#Calculate the rolling mean by setting the rolling window = 90 days (Quarterly)
euro_dollar['rolling_mean'] = euro_dollar['us_dollar'].rolling(90).mean()
euro_dollar = euro_dollar[euro_dollar['date'].dt.year.between(2016,2021)]

#Set the layout
fig,ax = plt.subplots(figsize = (9,5))
plt.style.use('seaborn-whitegrid')
plt.title("EU-US exchange rates between 2016-2021")
for each_spine in ["top",'bottom','right','left']:
    ax.spines[each_spine].set_visible(False)
ax.tick_params(left=False, bottom = False)

#Plot the graph
ax.plot(euro_dollar['date'],euro_dollar['rolling_mean'])

#Demarcate the Pre-Covid and Covid era
ax.axvline(x = pd.Timestamp('2020-01-01'), ymin = 0.01, linestyle = 'dashed', alpha = 0.5)
plt.show()

Now that we have a clearer picture of the exchange rate movement over the period specified, we can analyse the impact of Covid-19 on the the same.

In [9]:
#Capture the min and max values of exchanges rates over the 5 year period
df = pd.DataFrame(columns = ['mini','mini_date','maxa','maxa_date'],
                  index = [2016,2017,2018,2019,2020])
for each_year in df.index:
    mini = euro_dollar[(euro_dollar['date'].dt.year==each_year)]['rolling_mean'].min()
    mini_date = euro_dollar[(euro_dollar['date'].dt.year==each_year) & (euro_dollar['rolling_mean']== mini)]['date'].iloc[0]
    maxa = euro_dollar[(euro_dollar['date'].dt.year==each_year)]['rolling_mean'].max()
    maxa_date = euro_dollar[(euro_dollar['date'].dt.year==each_year) & (euro_dollar['rolling_mean']== maxa)]['date'].iloc[0]
    df.loc[each_year] = [mini,mini_date,maxa,maxa_date]

mean = round(euro_dollar[(euro_dollar['date'].dt.year.between(2016,2021))]['rolling_mean'].mean(),4)
Out[9]:
1.1324
In [12]:
fig,((ax1,ax2,ax3,ax4,ax5,ax6)) = plt.subplots(nrows = 6, ncols = 1, figsize = (17,14))
ax = [ax1,ax2,ax3,ax4,ax5,ax6]

#Set dates for vertical lines
x_dates = ['2015-12-10','2016-12-10','2017-12-10','2018-12-10','2019-12-10','2020-12-10']

#Set color scheme
colormin = '#ffff00'
colormax = '#00ffbf'
colorplot = '#ff0000'
covmin = '#ff4d4d'
covmax = '#e60000'
covplot = '#00ff00'
v_color = '#8000ff'
h_color = '#1a0000'

#Create the default plots
plt.style.use('fivethirtyeight')
for each_ax in ax:
    for each_spine in ["top",'bottom','right','left']:
        each_ax.spines[each_spine].set_visible(False)
    each_ax.tick_params(left = False, 
                        bottom = False, 
                        labelbottom = False, 
                        labelleft = False)
    each_ax.grid(b=None)
    each_ax.set_ylabel(None)
    each_ax.set_xlabel(None)
    each_ax.plot(euro_dollar['date'],euro_dollar['rolling_mean'],
                 alpha = 0.1,
                 color = covplot)

#Set start and end dates for each plot
start_date = ['2016-01-01','2017-01-01','2018-01-01','2019-01-01','2020-01-01']
end_date = ['2016-12-31','2017-12-31','2018-12-31','2019-12-31','2020-12-31']

#Set arrow and box for annotations
arrowmin = dict(arrowstyle="wedge,tail_width=0.5", 
                alpha=0.7, 
                color=colormin)
arrowmax = dict(arrowstyle="wedge,tail_width=0.5", 
                alpha=0.7, 
                color=colormax)
arrowmin_cov = dict(arrowstyle="wedge,tail_width=0.5", 
                    alpha=0.7, 
                    color=covmin)
arrowmax_cov = dict(arrowstyle="wedge,tail_width=0.5", 
                    alpha=0.7, 
                    color=covmax)
bboxmin=dict(boxstyle="round", 
             alpha=0.8, 
             color=colormin)
bboxmax=dict(boxstyle="round", 
             alpha=0.8, 
             color=colormax)
bboxmin_cov=dict(boxstyle="round", 
             alpha=0.8, 
             color=covmin)
bboxmax_cov=dict(boxstyle="round", 
             alpha=0.8, 
             color=covmax)

#Draw plot for individual periods
for i,each_ax in zip(range(5),ax[:5]):
    if i==0:
        #Title
        each_ax.text(s = 'EURO-USD Rates were above Above Average during Covid-19',
                     x = pd.Timestamp('2015-10-10'),
                     y = 1.38,
                     size=22,
                     weight='bold')
        #Sub-title
        each_ax.text(s = 'The best and the worst of the EURO-USD exchange rate for every year between 2016-2021',
                     x = pd.Timestamp('2015-10-10'),
                     y = 1.32,
                     size=18)
    
    #Extract data related to each year
    timeline = euro_dollar[(euro_dollar['date']>=start_date[i]) & (euro_dollar['date']<=end_date[i])][['date','rolling_mean']]
    
    #Plot data
    each_ax.plot(timeline['date'],timeline['rolling_mean'],
                 color = covplot if i<4 else colorplot,linewidth=3.5)
    
    #Specify the timeline on the plot
    each_ax.axvline(x = pd.Timestamp(start_date[i]), ymax=0.95,
                    color = v_color,
                    linestyle = 'dashed',
                    alpha = 0.5)
    each_ax.text(x = pd.Timestamp(x_dates[i]),y = 1.24,
                 s = str(pd.Timestamp(start_date[i]).year),
                 font = "fantasy",
                 size='x-large',
                 weight='bold')
    each_ax.text(x = pd.Timestamp(x_dates[i+1]),y = 1.24,
                 s = str(pd.Timestamp(start_date[i]).year+1),
                 font = "fantasy",
                 size='x-large',weight='bold')
    each_ax.axvline(x = pd.Timestamp(end_date[i]), ymax=0.95,
                    linestyle = 'dashed',
                    color = v_color,
                    alpha = 0.5)    
    
    #Annotate the min rates
    each_ax.annotate(text = str(round(df.iloc[i]['mini'],4)), 
                     xy = (df.iloc[i]['mini_date'],df.iloc[i]['mini']),
                     xytext = (df.iloc[i]['mini_date']+pd.Timedelta(90, unit = 'D'), df.iloc[i]['mini']),
                     va="center", arrowprops=arrowmin if i<4 else arrowmin_cov,
                     bbox = bboxmin if i<4 else bboxmin_cov,size = 13)
    
    #Annotate the max rates
    each_ax.annotate(text = str(round(df.iloc[i]['maxa'],4)), 
                     xy = (df.iloc[i]['maxa_date'],df.iloc[i]['maxa']),
                     xytext = (df.iloc[i]['maxa_date']+pd.Timedelta(90, unit = 'D'), df.iloc[i]['maxa']),
                     va="center", arrowprops=arrowmax if i<4 else arrowmax_cov,
                     bbox = bboxmax if i<4 else bboxmax_cov, size = 13)

#Summary plot
timeline1 = euro_dollar[(euro_dollar['date']>=start_date[0]) & (euro_dollar['date']<=end_date[4])][['date','rolling_mean']]
timeline2 = euro_dollar[(euro_dollar['date']>=start_date[-1]) & (euro_dollar['date']<=end_date[-1])][['date','rolling_mean']]

#Pre-Covid plot
ax6.plot(timeline1['date'],timeline1['rolling_mean'],
             color = covplot,linewidth=3.5)
#Covid Plot
ax6.plot(timeline2['date'],timeline2['rolling_mean'],
         color = colorplot,
         linewidth=3.5)

#2016 v.line    
ax6.axvline(x = pd.Timestamp(start_date[0]), ymax=0.95,
            color = v_color,
            alpha = 0.5)
#2016 Text
ax6.text(x = pd.Timestamp(x_dates[0]),y = 1.24,
         s = '2016',
         font = "fantasy",
         size='x-large',
         weight = 'bold')

#Pre-Covid title
ax6.text(x = pd.Timestamp(x_dates[2]),y = 1.24,
         s = 'Pre-Covid',
         font = 'monospace',
         size = 22,
         alpha = 0.5)

#2020 Text
ax6.text(x = pd.Timestamp(x_dates[4]),y = 1.24,
         s = '2020',
         font = "fantasy",
         size='x-large',
         weight = 'bold')

#Covid title
ax6.text(x = pd.Timestamp('2020-5-3'),y = 1.24,
         s = 'Covid',
         font = 'monospace',
         size = 22,
         alpha = 0.5)

#2021 Text
ax6.text(x = pd.Timestamp(x_dates[5]),y = 1.24,
         s = '2021',
         font = "fantasy",
         size='x-large',
         weight = 'bold')

#2021 v.line
ax6.axvline(x = pd.Timestamp(end_date[-1]), ymax=0.95,
            color = v_color,
            alpha = 0.5)

#Comparison h.line
#df.loc[2020]['maxa']
ax6.axhline(y = mean,xmin=0.04,xmax=0.95,
            linestyle = 'dashed',
            color = h_color,
            alpha = 0.6)

#Tick settings
ax6.tick_params(bottom = False,left = True,
                labelleft = True,
                labelsize = 'large')
#df.loc[2020]['maxa']
ax6.set_yticks([round(mean,4)])

#Signature
ax6.text(x = pd.Timestamp('2015-6-1'), y = 1.01, s = '©maX'+ ' ' * 265 + 'Source: European Central Bank',
        color = '#f0f0f0', backgroundcolor = '#4d4d4d',
        size=13)    
plt.tight_layout(h_pad = 2)
plt.show()