Currency Rate Analysis
Contents
Currency Rate Analysis¶
Money often costs too much. –Ralph Waldo Emerson
A very used expression in our life, and if utilized just right, also very useful.
In this notebook, we query a REST API to get the latest currency rates for many currencies. Using dynamic API compared to loading static files allows for updated data to be processed.
We will start with removing code warnings to make it easier to read the notebook.
import warnings
warnings.filterwarnings('ignore')
Loading data using API¶
First of all, let’s get our data.
In order to do so, let’s start with installing all the needed libraries and get the right imports.
%pip install forex-python --quiet
Note: you may need to restart the kernel to use updated packages.
import pandas as pd
import numpy as np
from forex_python.converter import CurrencyRates
from datetime import datetime
Let’s get the data
In that case, the function here is given by the provider of this API, and for more methods, you can go to forext-python usage page
usd_rates = (
CurrencyRates()
.get_rates('USD')
)
usd_rates
{'EUR': 0.9454476694714947,
'JPY': 127.95688758627209,
'BGN': 1.8491065519523493,
'CZK': 23.324194005861774,
'DKK': 7.036399735274652,
'GBP': 0.8019287132457218,
'HUF': 362.0402760707195,
'PLN': 4.383568119504585,
'RON': 4.6777914342441145,
'SEK': 9.919164224260186,
'CHF': 0.9719202042166966,
'ISK': 130.94450222180203,
'NOK': 9.702183984116479,
'HRK': 7.122530017963506,
'TRY': 15.902524345277488,
'AUD': 1.416280608868299,
'BRL': 4.915287888815354,
'CAD': 1.2788125177271437,
'CNY': 6.678453247612744,
'HKD': 7.847121111846458,
'IDR': 14656.320317670416,
'INR': 77.6795877848161,
'KRW': 1267.4482367400963,
'MXN': 19.884088115722793,
'MYR': 4.388957171220572,
'NZD': 1.5616904604330148,
'PHP': 52.17074784910655,
'SGD': 1.3792190602250165,
'THB': 34.30462323910371,
'ZAR': 15.801361444644039}
Let’s turn it into something more readable and easier to manipulate, by converting its type from Dictionary to DataFrame, and give its columns proper informative names.
usd_rates_df = (
pd
.DataFrame
.from_dict(usd_rates, orient='index')
.rename(columns={0 : 'Rate'})
)
usd_rates_df
Rate | |
---|---|
EUR | 0.945448 |
JPY | 127.956888 |
BGN | 1.849107 |
CZK | 23.324194 |
DKK | 7.036400 |
GBP | 0.801929 |
HUF | 362.040276 |
PLN | 4.383568 |
RON | 4.677791 |
SEK | 9.919164 |
CHF | 0.971920 |
ISK | 130.944502 |
NOK | 9.702184 |
HRK | 7.122530 |
TRY | 15.902524 |
AUD | 1.416281 |
BRL | 4.915288 |
CAD | 1.278813 |
CNY | 6.678453 |
HKD | 7.847121 |
IDR | 14656.320318 |
INR | 77.679588 |
KRW | 1267.448237 |
MXN | 19.884088 |
MYR | 4.388957 |
NZD | 1.561690 |
PHP | 52.170748 |
SGD | 1.379219 |
THB | 34.304623 |
ZAR | 15.801361 |
This looks like a useful table to have if you work with multiple currencies in your organization
Getting rates for a specific date¶
We will use the date functionality from the API provider.
Call the currency API
Request exchange rates
for USD
for 2020-05-23
usd_rate_on_date = (
CurrencyRates()
.get_rates(
'USD',
datetime(2020, 5, 23)
)
)
usd_rate_on_date
{'EUR': 0.917094644167278,
'USD': 1,
'JPY': 107.53851797505503,
'BGN': 1.7936537050623624,
'CZK': 24.954145267791635,
'DKK': 6.839508437270726,
'GBP': 0.8213774761555392,
'HUF': 320.43286867204694,
'PLN': 4.146093176815848,
'RON': 4.44194790902421,
'SEK': 9.663701393983859,
'CHF': 0.9712949376375641,
'ISK': 143.34189288334557,
'NOK': 10.003484959647835,
'HRK': 6.95570432868672,
'RUB': 71.45442039618489,
'TRY': 6.807318415260455,
'AUD': 1.530997798972854,
'BRL': 5.581162876008804,
'CAD': 1.400678650036684,
'CNY': 7.134721203228173,
'HKD': 7.7559611151870875,
'IDR': 14901.999266324283,
'ILS': 3.5270542920029344,
'INR': 75.86803008070433,
'KRW': 1241.2967718268526,
'MXN': 22.96863536316948,
'MYR': 4.362527512839325,
'NZD': 1.6388481291269257,
'PHP': 50.76027146001467,
'SGD': 1.4234225972120322,
'THB': 31.894717534849594,
'ZAR': 17.6518708730741}
Now, try to use the functions showed prior in order to convert it into a dataframe.
# Enter your code here
And in case you wondered how to do so.
usd_rate_on_date = (
pd
.DataFrame
.from_dict(usd_rate_on_date, orient='index')
.rename(columns={0 : 'Rate'})
)
usd_rate_on_date
Rate | |
---|---|
EUR | 0.917095 |
USD | 1.000000 |
JPY | 107.538518 |
BGN | 1.793654 |
CZK | 24.954145 |
DKK | 6.839508 |
GBP | 0.821377 |
HUF | 320.432869 |
PLN | 4.146093 |
RON | 4.441948 |
SEK | 9.663701 |
CHF | 0.971295 |
ISK | 143.341893 |
NOK | 10.003485 |
HRK | 6.955704 |
RUB | 71.454420 |
TRY | 6.807318 |
AUD | 1.530998 |
BRL | 5.581163 |
CAD | 1.400679 |
CNY | 7.134721 |
HKD | 7.755961 |
IDR | 14901.999266 |
ILS | 3.527054 |
INR | 75.868030 |
KRW | 1241.296772 |
MXN | 22.968635 |
MYR | 4.362528 |
NZD | 1.638848 |
PHP | 50.760271 |
SGD | 1.423423 |
THB | 31.894718 |
ZAR | 17.651871 |
Putting it all together¶
Create a Dataframe
From dictionary
Taken from the currency rate API
for USD
based on string date of 2020-05-23
as a single column
rename the column to Rate
usd_rate_on_date = (
pd
.DataFrame
.from_dict(
CurrencyRates()
.get_rates(
'USD',
datetime.strptime('2020-05-23', '%Y-%m-%d')
),
orient='index')
.rename(columns={0 : 'Rate'})
)
usd_rate_on_date
Rate | |
---|---|
EUR | 0.917095 |
USD | 1.000000 |
JPY | 107.538518 |
BGN | 1.793654 |
CZK | 24.954145 |
DKK | 6.839508 |
GBP | 0.821377 |
HUF | 320.432869 |
PLN | 4.146093 |
RON | 4.441948 |
SEK | 9.663701 |
CHF | 0.971295 |
ISK | 143.341893 |
NOK | 10.003485 |
HRK | 6.955704 |
RUB | 71.454420 |
TRY | 6.807318 |
AUD | 1.530998 |
BRL | 5.581163 |
CAD | 1.400679 |
CNY | 7.134721 |
HKD | 7.755961 |
IDR | 14901.999266 |
ILS | 3.527054 |
INR | 75.868030 |
KRW | 1241.296772 |
MXN | 22.968635 |
MYR | 4.362528 |
NZD | 1.638848 |
PHP | 50.760271 |
SGD | 1.423423 |
THB | 31.894718 |
ZAR | 17.651871 |
Deal conversion¶
Now, let’s create a calculator that get’s the amount of money we got/payed and in which currency
Call the API
For conversion rate
from USD
to EUR
of $100
on 2020-05-23
(
CurrencyRates()
.convert(
'USD',
'EUR',
100,
datetime.strptime('2020-05-23', '%Y-%m-%d')
)
)
91.7094644167278
Now this data is useful while trying to calculate all sort of financial related data.
Additional rates sources¶
And for cases when the wanted currency is not available through the API, there is always the option of scrapping it.
Here is example of scrapping ILS rate to USD from one of the popular financial websites in Israel.
Create dataframes from HTML tables in the newspaper currency website
Take the first table (index 0)
Take the value in cell 1,1
(
pd
.read_html('https://www.globes.co.il/portal/instrument.aspx?instrumentid=10463')
[0]
.iloc[1,1]
)
'3.359'
In this example we have scrapped the currency rate from a non-API based website for our use.
As for this example, this technique is relevent for many optional analysis.
Analysis Use-case¶
Now, use case to show how useful descion based data regarding currency could be.
In order to show that, we will scrap a dataframe of the rates of the ILS-USD for the last almost full year.
Read the table from the exchange-rate.org website
Rename the column to be more meaningful
Filter out table lines that are invalid (the Rate column has more than 20 characters)
Remove all the characters from the Rate column that are not a digit (\d) or dot (.) using regular expression syntax
convert it to a float type
df = (
pd
.read_html('https://www.exchange-rates.org/history/ILS/USD/T')
[0]
.rename(columns = {0 : 'Date', 1: 'Day', 2 : 'Rate', 3 : 'Notes'})
# .query('Rate.str.len() < 20')
.assign(Rate = lambda x : x.Rate.str.replace('[^\d\.]','',regex=True)
.astype(float))
)
df
Date | Day | Rate | Notes | |
---|---|---|---|---|
0 | 5/20/2022 | Friday | 3.36283 | USD ILS rate for 5/20/2022 |
1 | 5/19/2022 | Thursday | 3.38042 | USD ILS rate for 5/19/2022 |
2 | 5/18/2022 | Wednesday | 3.37941 | USD ILS rate for 5/18/2022 |
3 | 5/17/2022 | Tuesday | 3.34471 | USD ILS rate for 5/17/2022 |
4 | 5/16/2022 | Monday | 3.37882 | USD ILS rate for 5/16/2022 |
... | ... | ... | ... | ... |
125 | 11/26/2021 | Friday | 3.18176 | USD ILS rate for 11/26/2021 |
126 | 11/25/2021 | Thursday | 3.16222 | USD ILS rate for 11/25/2021 |
127 | 11/24/2021 | Wednesday | 3.14962 | USD ILS rate for 11/24/2021 |
128 | 11/23/2021 | Tuesday | 3.13881 | USD ILS rate for 11/23/2021 |
129 | 11/22/2021 | Monday | 3.09072 | USD ILS rate for 11/22/2021 |
130 rows × 4 columns
Now let’s check in which day of the week, does the rate of the ILS is “stronger”, in accordance to the USD.
This data could be useful in order to maximize the rate when doing currency conversions on a regular basis.
Now let’s try to check for the days with the highest average rate of the ILS in comparing to the USD.
In order to do so, we will aggregate with Group-by, all of the values by the Day of the week, and check for the average (mean) value for each day.
Beside that, let’s also orgnaize our data by some order, in order case descending.
(
df
.groupby(['Day'])
['Rate']
.mean()
.sort_values(ascending=False)
)
Day
Friday 3.219182
Thursday 3.218715
Monday 3.213923
Tuesday 3.211181
Wednesday 3.209393
Name: Rate, dtype: float64
And the result is? Well, Sunday.
Our guess is that its related to the fact that Sunday is weekend in the US and therefore, trade volumes are lower.
This, we assess, reduces the affect of the general trend.
Another test, would be to do the same, but with the median value for each week day.
(
df
.groupby(['Day'])
['Rate']
.median()
.sort_values(ascending=False)
)
Day
Tuesday 3.212945
Friday 3.211785
Monday 3.211590
Thursday 3.209310
Wednesday 3.196990
Name: Rate, dtype: float64
Analysis output¶
Bottom line, if you are converting ILS to USD, try doing it on Sunday. You will probably get the better rate. On the other hand, if you are trading USD to ILS, just don’t do it on Sunday.
One more note, did you notice how we used and scrapped many types of websites, side by our API.
The reason? None of them was sufficient so we had to dig deeper. and thats OK.
So, one last tip from us to you, don’t be afraid of looking for your data source.
Data Visualization¶
Line Chart¶
Now, let’s try to better see and and possibly explorate, the results we just recieved.
First, few actions needs to be done in prior.
import matplotlib.pyplot as plt
usd_to_ils_rates = (
pd
.read_html('https://www.exchange-rates.org/history/ILS/USD/T')
[0]
.rename(columns = {0 : 'Date', 1: 'Day', 2 : 'Rate', 3 : 'Notes'})
# .query('Rate.str.len() < 20')
.assign(Rate = lambda x : x.Rate.str.replace('[^\d\.]','',regex=True).astype(float))
.assign(Date = lambda x : pd.to_datetime(x.Date))
.set_index('Date')
)
usd_to_ils_rates
Day | Rate | Notes | |
---|---|---|---|
Date | |||
2022-05-20 | Friday | 3.36283 | USD ILS rate for 5/20/2022 |
2022-05-19 | Thursday | 3.38042 | USD ILS rate for 5/19/2022 |
2022-05-18 | Wednesday | 3.37941 | USD ILS rate for 5/18/2022 |
2022-05-17 | Tuesday | 3.34471 | USD ILS rate for 5/17/2022 |
2022-05-16 | Monday | 3.37882 | USD ILS rate for 5/16/2022 |
... | ... | ... | ... |
2021-11-26 | Friday | 3.18176 | USD ILS rate for 11/26/2021 |
2021-11-25 | Thursday | 3.16222 | USD ILS rate for 11/25/2021 |
2021-11-24 | Wednesday | 3.14962 | USD ILS rate for 11/24/2021 |
2021-11-23 | Tuesday | 3.13881 | USD ILS rate for 11/23/2021 |
2021-11-22 | Monday | 3.09072 | USD ILS rate for 11/22/2021 |
130 rows × 3 columns
(
usd_to_ils_rates
['Rate']
.plot(title='Exchange Rates: USD-ILS')
);
Scatter Plot¶
Now let’s try something else, to demonstrate the differences between the days.
(
usd_to_ils_rates
.plot
.scatter(
x='Day',
y='Rate',
c='Red',
title='Exchange Rates: USD-ILS across the week days'
)
);
Something here looks odd. it appears there are less trading ‘Sundays’, which could be the reason for our former result.
Let’s check it.
(
usd_to_ils_rates
.groupby('Day')
['Rate']
.count()
)
Day
Friday 26
Monday 26
Thursday 26
Tuesday 26
Wednesday 26
Name: Rate, dtype: int64
Yep, totally true. therefore, let’s check for the all the other days excluding Sunday.
Filtering the Dataframe to exclude Sundays.
(
usd_to_ils_rates
.query('Day != "Sunday"')
.plot
.scatter(
x='Day',
y='Rate',
c='Green',
title='Exchange Rates: USD-ILS across the working days'
)
);
And based on the most lower points, we can now see that the lowest days occurs in the mid-of-the-week days.
Analyze Across Weeks using Groupby¶
Another cool way to possible be able to better identifiy the aforementioned trend, check the max & min weekly rate, and on which day did it happen.
First, let’s set up our data in a way that will allow us to analyze it.
And by a grouped table, which will show the day of each week which the highest, and later the lowest rate occured on.
(
usd_to_ils_rates
.assign(Week = usd_to_ils_rates.index.week)
.groupby(['Week'])
['Day', 'Rate']
.max()
.style
.background_gradient(cmap='summer', subset=['Rate'])
)
Day | Rate | |
---|---|---|
Week | ||
1 | Wednesday | 3.115250 |
2 | Wednesday | 3.136660 |
3 | Wednesday | 3.140750 |
4 | Wednesday | 3.207520 |
5 | Wednesday | 3.206050 |
6 | Wednesday | 3.251370 |
7 | Wednesday | 3.262140 |
8 | Wednesday | 3.253240 |
9 | Wednesday | 3.276830 |
10 | Wednesday | 3.295400 |
11 | Wednesday | 3.291900 |
12 | Wednesday | 3.230900 |
13 | Wednesday | 3.216810 |
14 | Wednesday | 3.245360 |
15 | Wednesday | 3.218350 |
16 | Wednesday | 3.251140 |
17 | Wednesday | 3.343850 |
18 | Wednesday | 3.417140 |
19 | Wednesday | 3.464200 |
20 | Wednesday | 3.380420 |
47 | Wednesday | 3.181760 |
48 | Wednesday | 3.171570 |
49 | Wednesday | 3.163360 |
50 | Wednesday | 3.143060 |
51 | Wednesday | 3.172170 |
52 | Wednesday | 3.123020 |
(
usd_to_ils_rates
.assign(Week = usd_to_ils_rates.index.week)
.groupby(['Week'])
['Day', 'Rate']
.min()
.style
.background_gradient(cmap='summer', subset=['Rate'])
)
Day | Rate | |
---|---|---|
Week | ||
1 | Friday | 3.094070 |
2 | Friday | 3.101660 |
3 | Friday | 3.108650 |
4 | Friday | 3.174190 |
5 | Friday | 3.163880 |
6 | Friday | 3.193020 |
7 | Friday | 3.187010 |
8 | Friday | 3.223520 |
9 | Friday | 3.208720 |
10 | Friday | 3.250110 |
11 | Friday | 3.238470 |
12 | Friday | 3.212440 |
13 | Friday | 3.182330 |
14 | Friday | 3.213450 |
15 | Friday | 3.205370 |
16 | Friday | 3.221920 |
17 | Friday | 3.292740 |
18 | Friday | 3.358250 |
19 | Friday | 3.401920 |
20 | Friday | 3.344710 |
47 | Friday | 3.090720 |
48 | Friday | 3.130300 |
49 | Friday | 3.094200 |
50 | Friday | 3.090750 |
51 | Friday | 3.147230 |
52 | Friday | 3.099640 |
Analysis conclusion¶
And here is the answer for our question - which days are the best day to conduct a currency conversion between USD and ILS to maximize your profits: Wednsday for USD to ILS and Friday to ILS to USD