Analysing Indonesia's Inflation in the Context of Global Markets and Establishing a Benchmark Return for Indonesian Investors: An Exploratory Data Analysis
1. Introduction to the research space¶
I grew up in Indonesia, where the Indonesian Rupiah (IDR) is the local currency. Since primary school, I have always saved up and stored my money in the piggy bank. I kept doing this until high school, but then I realised the canteen prices keep rising while portions shrank. I discussed this situation with my parents, and they said during their generation it was much worse. Reflecting on the history, I knew this event would keep on happening in the future. Through some research, I found that this phenomenon is called inflation, where it is a general increase in prices and decline in the purchasing power of money. Therefore, I invest in fixed deposits (time deposits) which have roughly the same return rate as the inflation in Indonesia at that time.
I thought I had successfully mitigated the effects of inflation by investing in fixed deposits. Until I went to study abroad in Singapore. I may beat Indonesia's inflation rate, but I did not account for the exchange rate devaluation as well. Therefore, I made this project to determine the average return my investment portfolio should generate to outpace Indonesia's inflation and to keep up with the global market condition. Specifically, the exchange rate trends between IDR and the world’s top five key currencies: the United States Dollar (USD), Euro (EUR), British Pound (GBP), Japanese Yen (JPY), and Canadian Dollar (CAD) (investopedia, 2024).
1.1 Aims and Objectives¶
- The primary focus of this project is to explore the inflation rate while factoring in the effects of currency depreciation
- Create a simple and accessible report that is accessible for all individuals with different technical expertise
- Establish a benchmark for return on investment that outperform inflation and currency exchange devaluation
1.2 Acquire a Dataset¶
- Exchange Rates: Historical IDR exchange rates against the five key currencies (USD, EUR, GBP, JPY, CAD).
- USD/IDR Histrocial Data Investing.com
- USD/IDR Histrocial Data Yahoo Finance
- EUR/IDR Histrocial Data Investing.com
- EUR/IDR Histrocial Data Yahoo Finance
- GBP/IDR Histrocial Data Investing.com
- GBP/IDR Histrocial Data Yahoo Finance
- JPY/IDR Histrocial Data Investing.com
- JPY/IDR Histrocial Data Yahoo Finance
- CAD/IDR Histrocial Data Investing.com
- CAD/IDR Histrocial Data Yahoo Finance
- Historical inflation rates: Indonesia and globally
1.3 Utilise the Dataset¶
This project will utilise the datasets acquired into three parts:
- Data cleaning
- Handling missing, and unnessesary values
- Perform calculations to modify the datasets to fit with the project objectives
- Ensure all data is correctly formatted for analysis
- Data visualisation
- Using plotting library such as matplotlib and seaborn to provide a clear and informative illustration of the datasets
- Highlights key trends and insights within the datasets
- Data analysis
- Find interesting insights, potential correlations, and find the inflation relative to the global market rate
- Understanding why data behaves in a certain way
1.4 Writing Style to Communicate Ideas and Concepts¶
Throughout the project, the writing style will be logical, concise, and make complex concepts easy to understand. Additionally, this report will be explaining why certain approaches were chosen and how they were implemented. For example, I will include formulas to explain how specific calculations are performed and the reasoning behind them. Decisions will be driven by data, ensuring every step of the projects is backed up with rationale and transparency.
1.5 Summary of the Area of Research¶
This project focuses on addressing the financial challenges of inflation and currency devaluation faced by Indonesians. By gathering historical data on inflation and currency devaluation rates and applying analytical techniques, thie research aims to provide actionable insights. The outcomes of this project are intended to optimise Indonesians' investment portfolios to overcome the inflation and currency devaluation rate.
2. Project background¶
2.1 Why the field is of interest¶
I have chosen the economy field because I am interested in the current situation of Indonesia's economy and I want to make an impact to the society through my research. I believe most people are aware of the inflation rate. However not everyone aware that the inflation rate is calculated relatively based on the country's currency (Indonesia Rupiah in this case). This method of calculation does not take into account the exchange rate depreciation of the currency. Therefore, I am interested in finding the inflation rate of Indonesia in the context of global market. By undertaking this research, I can provide a benchmark for return on investment to outperform inflation and currency exchange devaluation.
2.2 Previously explored topics¶
Many of the existing work has currently concentrated on either inflation or exchange rate but not both. Limited research has been done on the combined effect of inflation and currency devaluation, where both phenomena are a regular concern for the average Indonesian. Additionally, the research timeframe includes Indonesia’s 1998 political instability, which caused extreme economic volatility, such as hyperinflation and currency depreciation. However, such conditions are unlikely to recur due to globalisation, increased political stability, and stronger democratic institutions. Including this period may skew results.
One example of the study I found Explaining Higher Inflation in Indonesia: A Regional Comparison Focuses more on the inflation rate in Indonesia while ignoring the effects of currency devaluation.
Thus, this study aims to fill that gap by providing an integrated view of inflation rate and currency devaluation rate. Additionally, providing an optimal benchmark for return on investment to overcome the inflation and currency exchange devaluation in Indonesia.
2.3 Scope of Work¶
INCLUSION CRITERIA - This study focuses on calculating the inflation rate in Indonesia while accounting for its currency devaluation. Currencies like USD, EUR, JPY, GBP, and CAD will be combined to represent the global market. As for the data, this research will rely on historical data for inflation and exchange rates from the year 2000 - 2023. The analysis will include trends, anomaly highlights, correlations, and visualisations to get the insights and perform calculations to find the inflation rate that is relative to the global market.
EXCLUSION CRITERIA - However, this project will not build a machine learning model to predict future inflation and exchange rates, and this project will not explore the macroeconomic factors, such as monetary policies, that could influence the inflation and exchange rates.
2.4 Steps in Analytical Data Processing Pipeline.¶
- Acquire Indonesia and world's inflation rate and top 5 global currency exchange rate datasets
- By calling inflation API
- Download csv file from currency website
- Data cleaning, validation, and transformation to enhance data quality
- Ensuring data is accurate and consistent
- Removing missing values
- Removing unnessesary values
- Determine the time scope of this project
- Perform calculations within the datasets to make them fit with the project objectives
- Prepare data for visualisation by transforming it into pandas dataframe
- Data visualisation
- Using plotting library such as matplotlib and seaborn to provide a clear and informative illustration of the datasets
- Data analysis and Reporting
- Find interesting insights, potential correlations, and find the inflation relative to the global market rate
- Understanding why data behaves in a certain way
- Draw conclusion based on the analysis results and recommend actionable strategies to mitigate the effects
2.5 Evaluation of Aims and Objectives Based on Chosen Approach¶
The success of this project will be measured by the clarity of insights provided such as the reasons behind the findings, how well the it is presented, and without any bias (logical and data-driven). Additionally, the accuracy of calculations for example, instead of just using average, we can also use compound annual growth rate (CAGR) approach to measure the average annual growth rate of an investment over a specific period of time. By using different approaches and combine them together to get even more accurate results. The insights generated should not only provide a theoretical understanding of Indonesia's inflation and currency depreciation but also offer practical strategies for investors to safeguard their wealth with the benchmark return calculated. Lastly, the project will be evaluated based on the impact of the findings.
3. Ethics of Use of Data¶
3.1 Where the Data Has Come From¶
The data used in this project has been sourced from publicly available platforms, and the following statements confirm that its use complies with the terms and conditions set by each provider:
Yahoo Finance
"It’s an open-source tool that uses Yahoo’s publicly available APIs, and is intended for research and educational purposes."
Investing.com
"Subject to the terms and conditions set forth herein, the Group grants you a non-exclusive, non-transferable, non-sublicensable, revocable and limited license to access and use the Tools solely for your personal, non-commercial use (the “Tools License”)"
World Bank Group
"The World Bank Group makes data publicly available according to open data standards and licenses datasets under the Creative Commons Attribution 4.0 International license (CC-BY 4.0). The Creative Commons Attribution 4.0 International license allows users to copy, modify and distribute data in any format for any purpose, including commercial use."
3.2 Considerations About Usage/Reusage of Data¶
The data that were gathered in this project will be processed through the data analysis pipeline where modification will be made. While the raw data itself is publicly available, the analysis and insights generated from processing this data, such as algorithms or any visualisations, may establish new forms of intellectual property.
Data sources such as Yahoo Finance, Investing.com, and the World Bank will be properly attributed.
3.3 Considerations Around Implications of Utilising Data for Purpose¶
As this research pertains to financial markets, particularly forex, it is important to acknowledge that readers may be influenced to consider forex trading. However, forex trading carries risks for financial loss. This project is strictly limited to analysing historical data and does not guarantee future outcomes or provide trading recommendations.
I will also remind the reader to not use this analysis fully in making investment decisions. This project is only based on historical price data to generate a benchmark for an optimal investing portfolio return in Indonesia. Therefore, this research will communicate the findings transparently and objectively with a clear understanding of the risks involved.
3.4 Considerations of the Data Processing Pipeline¶
All of the data is accessible in the root directory of this project. There will be a CSV that stores currency data and inflation data. All of the data is integrated into this notebook and is readily accessible.
For financial data, there are typically no sensitive datasets (with no personal details). Since the data consists of historical financial records, there is no need for anonymisation of the data.
Every step in modifying the data is documented with a description, comments, and example output. The data pipeline is designed to be straightforward and does not contain any concerns related to restricted access or data privacy.
3.5 Considerations of Potential Biases of the Dataset¶
The dataset primarily focuses on inflation and top five currencies data. Since this project only focuses on the top five currencies, it may introduce a bias toward big and developed markets only. Additionally, there is only one Asian currency (Japanese yen) included in the analysis, which may cause an imbalance in weightage in calculation towards the western countries. However, this case is handled objectively with the scope of this project to pick the top five currencies.
4. Data Relavance and the Use of Data¶
4.1 Origin of Data¶
- Inflation rate data from World Bank
- By
pip installthewbgapiandimportit - Get the inflation data of Indonesia and the world in dataframe by using
wbgapimodule
- By
- Currency exchange rate historical data from investing.com
- Go to the investing.com website and download the csv file for IDR to USD, EUR, GBP, JPY, and CAD then save it locally
- The implementation of this part will be in the data exploration section.
- Currency exchange rate historical data from Yahoo Finance
- By
pip installtheyfinanceandimportit - Get the currency data of Indonesia and top five currencies in dataframe by using
yfinancemodule - The implementation of this part will be in the data exploration section.
- By
- Top articles during significant rise in inflation data period from Google Search
- Webscraping with
BeautifulSoup - The implementation of this part will be in the data exploration section.
- Webscraping with
# To fetch data for inflation rate in Indonesia and the world
import wbgapi as wb
# Importing pandas for data manipulation and making data structure (data frame)
import pandas as pd
Please note - On 14-Dec-2024, I have used the wbgapi module to fetch inflation data and store it in a csv file. This locally stored csv file will be used for fallback when the API call fails. Below are the code I used to call and turn it into a pandas dataframe:
# try:
# inflation_data = wb.data.DataFrame('FP.CPI.TOTL.ZG', ['IDN', 'WLD'], time=range(1960, 2024))
# inflation_data = inflation_data.reset_index()
# inflation_data.to_csv('data/inflation_data.csv', index=False)
# except:
# print("API call failed")
Fetching data from the World Bank API with error handling
# Try fetch data with API
try:
# Use API fetched data from 1960 to 2023
inflation_data = wb.data.DataFrame('FP.CPI.TOTL.ZG', ['IDN', 'WLD'], time=range(1960, 2024))
print("API call successfully")
except:
# Use locally stored data if API fails
print("API call failed, attempting to use locally stored data")
try:
inflation_data = pd.read_csv("data/inflation_data.csv")
print("Locally stored data loaded successfully")
except FileNotFoundError:
print("Locally stored data not found")
# inflation_data.info()
display(inflation_data)
API call successfully
| YR1960 | YR1961 | YR1962 | YR1963 | YR1964 | YR1965 | YR1966 | YR1967 | YR1968 | YR1969 | ... | YR2014 | YR2015 | YR2016 | YR2017 | YR2018 | YR2019 | YR2020 | YR2021 | YR2022 | YR2023 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| economy | |||||||||||||||||||||
| IDN | 39.590444 | 13.691932 | 131.397849 | 145.910781 | 108.994709 | 306.76311 | 1136.254112 | 106.0 | 128.843042 | 15.47987 | ... | 6.394925 | 6.363121 | 3.525805 | 3.808798 | 3.198346 | 3.030587 | 1.920968 | 1.560130 | 4.209464 | 3.670131 |
| WLD | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | 2.354491 | 1.443857 | 1.605539 | 2.254277 | 2.438737 | 2.216776 | 1.905664 | 3.475403 | 7.930929 | 5.635669 |
2 rows × 64 columns
4.2 The Appropriateness of the Data Source¶
The datasets gathered are from reputable sources such as the World Bank and Investing.com, which are widely recognised as credible, accurate, and reliable sources for global economic data. The datasets acquired are also in a very detailed format where I can freely do data manipulation to it. Moreover, the dataset's coverage is wide as it includes the countries needed and the data itself within the time scope of this project. The datasets gathered aligned ideally with the project objectives of finding the inflation rate relative to the global market.
4.3 Identifiable Case for Working with This Data¶
The selected datasets are equipped with the necessary data that align with the project objectives. The datasets include the following:
- Year: To limit the time scope of this project, which is from 2003-2023
- Inflation rate: the annual percentage of Indonesia's and the world's inflation rate
- Currency exchange rate: from USD, EUR, GBP, JPY, and CAD to IDR
- The currency data not only provide the exchange rate but also the monthly volatility (Open, Close, High, Low) of the currency, which I can use to analyse the currency volatility and generate the annual data.
4.4 Format of Data¶
The World Bank inflation data was fetched using the function in the wbgapi library, which returned a pandas dataframe where it is well-suited for numerical and statistical analysis. On the other hand, the currency exchange data from Investing.com was downloaded in CSV format and then loaded into a Pandas DataFrame one by one. After every currency pair has its own dataframe, a dictionary was created to store all the currency pair dataframes for easy access.
4.5 Other Datasets Consideration¶
Inflation datasets:
- macrotrends.net
- This dataset lacks the data accuracy where the data only provide up to four decimal points, as compared to world bank data which provides up to 14 decimal points. Additionally, the datasets only provide one region at a time, therefore I need to combine indonesia and world datasets to make it into one dataframe which cause more hassle.
- tradingeconomics.com
- To access datasets from this website, I will need to pay (subscribe). Although this website provides a lot of indicator such as CPI housing utilities, CPI transportation, food inflation, etc, This project scope is only to analyse based on the historical data of inflation and currency exchange rate.
- Bank Indonesia
- The inflation dataset from this website is in monthly and only from 2002-2023 which is not suitable for this project.
| Source | Strengths | Weaknesses |
|---|---|---|
| macrotrends.net | Provides data for multiple regions | Lacks data accuracy (only up to four decimal points); datasets only provide one region at a time, requiring combination of Indonesia and world datasets |
| tradingeconomics.com | Offers a variety of indicators such as CPI housing utilities, CPI transportation, food inflation, etc. | Requires a subscription to access datasets; project scope is limited to historical data of inflation and currency exchange rate |
| Bank Indonesia | Provides monthly inflation data | Data is only available from 2002-2023, which is not suitable for this project |
Currency exchange datasets:
- Yahoo Finance
- The earliest currency exchange data point from Yahoo Finance are only from 2001 onwards, while this project requires data from 2000 onwards.
- fxtop.com
- The selected (USD/IDR) dataset have data from 1967 to 2023. However, the dataset is not the close price, which is needed for the analysis of this project.
| Source | Strengths | Weaknesses |
|---|---|---|
| Yahoo Finance | Provides currency exchange data | The earliest data point is from 2001 onwards, while this project requires data from 2000 onwards |
| fxtop.com | The selected (USD/IDR) dataset has data from 1967 to 2023 | The dataset is not the close price, which is needed for the analysis of this project |
5. Exploration of Datasets¶
5.1 Removing Illegal Values¶
Inflation data:
- The original inflation dataset have the prefix 'YR' which indicates year in the column header. By removing the prefix, it will make the dataset more consistent and reliable.
- The NaN on WLD row is due to the data unavailability for the year 1960 until 1981, therefore I will leave it NaN and clarify during the scope determination phase.
# Process the column headers to remove the 'YR' prefix and convert them to integers
inflation_data.columns = inflation_data.columns.str.replace(r'^YR', '', regex=True)
inflation_data.columns.astype(int)
display(inflation_data)
| 1960 | 1961 | 1962 | 1963 | 1964 | 1965 | 1966 | 1967 | 1968 | 1969 | ... | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| economy | |||||||||||||||||||||
| IDN | 39.590444 | 13.691932 | 131.397849 | 145.910781 | 108.994709 | 306.76311 | 1136.254112 | 106.0 | 128.843042 | 15.47987 | ... | 6.394925 | 6.363121 | 3.525805 | 3.808798 | 3.198346 | 3.030587 | 1.920968 | 1.560130 | 4.209464 | 3.670131 |
| WLD | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | 2.354491 | 1.443857 | 1.605539 | 2.254277 | 2.438737 | 2.216776 | 1.905664 | 3.475403 | 7.930929 | 5.635669 |
2 rows × 64 columns
Currency data:
- To validate all five currency data, a function will be used since I will be calling it multiple times.
- For the scope of this project, I will only use the "Date" and "Price" columns for analysis. Hence, to save memory, I will remove the other columns.
- All of the data is in string format, therefore I will convert it to datetime for "Date" and float for "Price".
# Clean the monthly to annual close price currency of investing.com data
def clean_investing_data(file_path):
# Read the CSV file
try:
df = pd.read_csv(file_path)
except FileNotFoundError:
print(f"File from {file_path} not found.")
return None
# Remove unneccessary columns
df.drop(["Open", "High", "Low", "Vol.", "Change %"], axis="columns", inplace=True)
# Convert date string to datetime
df['Date'] = pd.to_datetime(df['Date'])
# Filter rows where the month is December to get the close price
df = df[df['Date'].dt.month == 12]
# Transform 'Date' to 'Year' column
df = df.rename(columns={'Date': 'Year'})
df['Year'] = df['Year'].dt.year
# Convert string to float on 'Price' column
if df['Price'].dtype == 'object':
df['Price'] = df['Price'].str.replace(',', '', regex=False).astype(float)
# Sort the time in ascending order
df = df.iloc[::-1].reset_index(drop=True)
return df
# Example usage for USD/IDR
clean_investing_data("data/USD_IDR Historical Data.csv").head(4)
| Year | Price | |
|---|---|---|
| 0 | 1988 | 1725.0 |
| 1 | 1990 | 1901.0 |
| 2 | 1991 | 1992.0 |
| 3 | 1992 | 2067.5 |
5.2 Data Format for Analysis¶
- The inflation data is extracted from the World Bank API and Yahoo Finance API and is stored in a pandas dataframe.
- All of the data extracted from CSV are in pandas dataframe format with corresponding types for each column.
5.3 Depth of Exploration¶
To increase accuracy and make the currency dataset more reliable, a comparison between two providers will be made. The goal is to get the best out of two datasets. Investing.com provides more historical data (~1988 onwards), while Yahoo Finance provides real-time data with their API, "Adjusted Close" price, and more accuracy with more decimal points. With that being said, in cases where mismatches are found, the data from Yahoo Finance will be prioritised.
Get Forex Data from Yahoo Finance API
# Fetch data for currency data from Yahoo Finance
import yfinance as yf
# Download historical exchange rate data for USD/IDR
test = yf.download('USDIDR=X', start=None, end='2023-12-31', interval='1mo')
# Reset index to get date column
test.reset_index(inplace=True)
# Display the raw data from API
display(test.head(4))
[*********************100%***********************] 1 of 1 completed
| Price | Date | Adj Close | Close | High | Low | Open | Volume |
|---|---|---|---|---|---|---|---|
| Ticker | USDIDR=X | USDIDR=X | USDIDR=X | USDIDR=X | USDIDR=X | USDIDR=X | |
| 0 | 2001-07-01 | 9500.0 | 9500.0 | 11490.0 | 9265.0 | 11315.0 | 0 |
| 1 | 2001-08-01 | 8863.0 | 8863.0 | 9840.0 | 8325.0 | 9563.0 | 0 |
| 2 | 2001-09-01 | 9708.0 | 9708.0 | 9820.0 | 8685.0 | 9055.0 | 0 |
| 3 | 2001-10-01 | 10475.0 | 10475.0 | 10655.0 | 9625.0 | 10035.0 | 0 |
Above are the raw data from the Yahoo Finance API.
Now, we are implementing a function to clean the forex data, which will ensure that the dataset is prepared for the comparison stage.
This function will be called to clean all five currencies for this project.
Function to Get and Clean Yahoo Finance Data
# Function to return cleaned dataframe of the currency
def clean_yahoo_data(currency_code):
# Concatenate the input to form the ticker
ticker = f"{currency_code}IDR=X"
# Get historical price from Yahoo Finance API
try:
data = yf.download(
ticker,
start=None,
end='2023-12-31',
interval='1mo',
auto_adjust=True
)
# Handling empty data
if data.empty:
print(f"Data is empty for currency code: {currency_code}")
return None
except Exception as e:
print(f"An error occurred on {currency_code}: {e}")
return None
# Reset index to make Date a column
data.reset_index(inplace=True)
# Ensure the date is in datetime format
data['Date'] = pd.to_datetime(data['Date'])
# Group by year and get the last row of each year (December)
yearly_data = data.groupby(data['Date'].dt.year)['Close'].last().reset_index()
# Look for anomalies
yearly_data['% Change'] = yearly_data['Close'].pct_change()
# Value for identifying anomalies
threshold = 0.9
# Extract the rows with anomaly
anomalies = yearly_data[abs(yearly_data['% Change']) > threshold]
# If have anomalies
if not anomalies.empty:
print(f"\nAnomalies Detected in {currency_code}:\n\n{anomalies}")
# Drop rows with anomalies
yearly_data = yearly_data.drop(index=anomalies.index - 1)
# Create a new DataFrame with just the columns we want
yearly_data = yearly_data[['Date', 'Close']].copy()
# Rename columns
yearly_data.columns = ['Year', 'Price']
return yearly_data.reset_index(drop=True)
# Example usage for EUR/IDR
test = clean_yahoo_data("EUR")
display(test.tail(8))
[*********************100%***********************] 1 of 1 completed
Anomalies Detected in EUR: Price Date Close % Change Ticker EURIDR=X 13 2016 144.508667 -0.990252 14 2017 16033.632812 109.952742
| Year | Price | |
|---|---|---|
| 11 | 2014 | 15085.000000 |
| 12 | 2017 | 16033.632812 |
| 13 | 2018 | 16652.000000 |
| 14 | 2019 | 15595.000000 |
| 15 | 2020 | 17013.494141 |
| 16 | 2021 | 16133.000000 |
| 17 | 2022 | 16695.000000 |
| 18 | 2023 | 16893.000000 |
Above is the output from the clean_yahoo_data function.
Currently, there is cleaned investing.com data from clean_investing_data that was implemented in section 4.1 and this cleaned Yahoo Finance data.
Therefore, it is possible to combine those two data together to achieve higher reliability with the combine_df function. combine_df will be called later to merge every currency.
Function to combine Investing.com and Yahoo Finance Data
# Combine both dataframe
def combine_df(superset_df, subset_df):
"""
superset_df is the df with full time period
subset_df is the df with more accurate data but is a subset of superset_df
"""
# Filter out rows in superset_df that overlap with subset_df
unique_df = superset_df[~superset_df["Year"].isin(subset_df["Year"])]
# Concatenate unique_df and subset_df
combined_df = pd.concat([unique_df, subset_df], ignore_index=True)
# Sort by year
combined_df.sort_values(by="Year", inplace=True, ignore_index=True)
return combined_df
# Example usage
investing_data = clean_investing_data("data/USD_IDR Historical Data.csv")
yahoo_data = clean_yahoo_data("USD")
display(combine_df(investing_data, yahoo_data).tail(10))
[*********************100%***********************] 1 of 1 completed
| Year | Price | |
|---|---|---|
| 26 | 2015 | 13845.000000 |
| 27 | 2016 | 13518.000000 |
| 28 | 2017 | 13477.000000 |
| 29 | 2018 | 14553.000000 |
| 30 | 2019 | 13919.000000 |
| 31 | 2020 | 13832.559570 |
| 32 | 2021 | 14285.200195 |
| 33 | 2022 | 15502.200195 |
| 34 | 2023 | 15383.099609 |
| 35 | 2024 | 16053.600000 |
Apply the functions to top five currencies (USD/IDR, JPY/IDR, EUR/IDR, CAD/IDR, GBP/IDR)
# Investing.com cleaned data
investing_data = {
"USD/IDR": clean_investing_data("data/USD_IDR Historical Data.csv"),
"JPY/IDR": clean_investing_data("data/JPY_IDR Historical Data.csv"),
"EUR/IDR": clean_investing_data("data/EUR_IDR Historical Data.csv"),
"CAD/IDR": clean_investing_data("data/CAD_IDR Historical Data.csv"),
"GBP/IDR": clean_investing_data("data/GBP_IDR Historical Data.csv")
}
# Yahoo Finance cleaned data
yahoo_data = {
"USD/IDR": clean_yahoo_data("USD"),
"JPY/IDR": clean_yahoo_data("JPY"),
"EUR/IDR": clean_yahoo_data("EUR"),
"CAD/IDR": clean_yahoo_data("CAD"),
"GBP/IDR": clean_yahoo_data("GBP")
}
# Investing.com and Yahoo Finance data combined
combined_forex_data = {
"USD/IDR": combine_df(investing_data["USD/IDR"], yahoo_data["USD/IDR"]),
"JPY/IDR": combine_df(investing_data["JPY/IDR"], yahoo_data["JPY/IDR"]),
"EUR/IDR": combine_df(investing_data["EUR/IDR"], yahoo_data["EUR/IDR"]),
"CAD/IDR": combine_df(investing_data["CAD/IDR"], yahoo_data["CAD/IDR"]),
"GBP/IDR": combine_df(investing_data["GBP/IDR"], yahoo_data["GBP/IDR"])
}
[*********************100%***********************] 1 of 1 completed [*********************100%***********************] 1 of 1 completed [*********************100%***********************] 1 of 1 completed [*********************100%***********************] 1 of 1 completed [*********************100%***********************] 1 of 1 completed
Anomalies Detected in EUR: Price Date Close % Change Ticker EURIDR=X 13 2016 144.508667 -0.990252 14 2017 16033.632812 109.952742
5.4 Checks for Out of Bound Values¶
This section will ensure the data used for analysis are aligned with the research objectives and within acceptable ranges.
# Visualize Indonesia's Annual Inflation
import matplotlib.pyplot as plt
# Get the inflation data for Indonesia and the world
indonesia_inflation = inflation_data.iloc[0]
world_inflation = inflation_data.iloc[1]
# Visualize Indonesia's Annual Inflation from 1960-2023
plt.figure(figsize=(14, 6))
# Add world data
plt.plot(inflation_data.columns, indonesia_inflation, marker='o', linestyle='-', label='IDN')
plt.plot(inflation_data.columns, world_inflation, marker='o', linestyle='-', label='WLD')
plt.axvspan(inflation_data.columns[0], '1981', color='gray', alpha=0.2, label="Data not available for World Inflation")
# Add Indonesia inflation mean horizontal line
indonesia_inflation_mean = indonesia_inflation.mean()
print(f'Indonesia Mean Inflation: {round(indonesia_inflation_mean, 2)}%')
plt.axhline(y=indonesia_inflation_mean, color='red', linestyle='--', label='Indonesia Mean Inflation')
# Set y axis to log scale to see the data in more detail
plt.yscale('log')
plt.title("Indonesia's Inflation, Consumer Price (annual %)")
plt.xlabel('Year')
# Set x axis tick labels to show every 3 years
plt.xticks(ticks=inflation_data.columns[::3])
plt.ylabel('Inflation (%)')
plt.grid(True)
plt.tight_layout()
plt.legend()
plt.show()
Indonesia Mean Inflation: 41.99%
Based on the visualisation of the inflation data, we can see that the inflation rate has spiked up and down very rapidly from 1960 to 1971. The volatility is so high that I would need to set the graph into a logarithmic scale in order to preview the data with more detail.
We can see that the spiked-up inflation rates are in 1966, 1974, 1998, 2008, and 2022. Let's explore what happened in those years via Google search. To get the top article that is related to the economic events happening in those years, a web scraping library like BeautifulSoup will be utilised to get the top article of those years. By doing this approach, it will give us rough insights on what is going on in the economy in those years.
# Import BeautifulSoup for web scraping
from bs4 import BeautifulSoup
# Import requests for making HTTP requests
import requests
# tdqm for progress bar
from tqdm import tqdm
# Function to scrape the top result for events for a specific year in a specific region from Google search
def get_economic_events(year, region):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
}
# Replace the google search query with year and region (e.g. Indonesia or world)
url = f"https://www.google.com/search?q=what+happpened+in+{year}+economy+in+{region}"
try:
# Send the HTTP request
response = requests.get(url, headers=headers)
if response.status_code == 200:
# Parse the HTML content
html = BeautifulSoup(response.content, 'html.parser')
# Find the first article block
result = html.find('div', class_='N54PNb')
# Extract title and description with handling for missing values
# Extract the title
if result and result.find("h3"):
title = result.find("h3").text
else:
title = "Not available"
# Extract the description
if result and result.find("div", class_="VwiC3b"):
description = result.find("div", class_="VwiC3b").text
else:
description = "Not available"
# Store data in a dictionary
search_results = {
"title": title,
"description": description
}
return search_results
else:
# Handle error status codes
print(f"Failed to retrieve the page. Status code: {response.status_code}")
return None
except Exception as e:
# Handle any exception
print(f"An error occurred: {e}")
return None
# List of years and regions to scrape
anomaly_years = ["1966", "1974", "1998", "2008", "2022"]
regions = ["Indonesia", "world"]
# Store results in a list of dictionaries
events_data = []
# Scrape data for all anomaly years
for year in tqdm(anomaly_years, desc="Scraping data", unit="year"):
# Scrape data for each region on the same year
for region in regions:
try:
event = get_economic_events(year, region)
if event:
# Add year and region to the dictionary and append to the list
event['year'] = year
event['region'] = region
events_data.append(event)
except Exception as e:
print(f"Error retrieving data for {year} in {region}: {e}")
# Convert list of dictionaries into Pandas DataFrame
economic_event_articles = pd.DataFrame(events_data)
# Reorder columns for readability
column_order = ["year", "region", "title", "description"]
economic_event_articles = economic_event_articles.reindex(columns=column_order)
# Adjust pandas settings to display the column max width and avoid truncation
pd.set_option('display.max_colwidth', None)
# Display the DataFrame
display(economic_event_articles)
Scraping data: 100%|██████████| 5/5 [00:05<00:00, 1.07s/year]
| year | region | title | description | |
|---|---|---|---|---|
| 0 | 1966 | Indonesia | Indonesia: Economic Stabilization, 1966-69 in | 1 Des 1970 — THE INDONESIAN economy suffered from rapid inflation during the first half of the 1960's, culminating in an increase of over 1,500 per cent in ... |
| 1 | 1966 | world | The 1966 Financial Crisis | Levy Economics Institute | As a result of Fed intervention, the economy continued to expand, new financial practices emerged and were validated, leverage ratios increased, memories of the ... |
| 2 | 1974 | Indonesia | INDONESIA 1974: A MOMENTOUS YEAR Gary Hansen | oleh G Hansen · 1975 · Dirujuk 25 kali — tion of economic developments in Indonesia. Although somewhat over- shadowed by the January demonstrations, the world-wide rise in oil prices. |
| 3 | 1974 | world | 1973–1975 recession | The 1973–1975 recession or 1970s recession was a period of economic stagnation in much of the Western world during the 1970s |
| 4 | 1998 | Indonesia | Asian Financial Crisis - Cause & Effect | In January 1998 the rupiah lost half of its value within the time-span of five days only, causing Indonesians to hoard food. This second IMF agreement contained ... |
| 5 | 1998 | world | Developments in the World Economy in 1997/98 | The sharp declines in private capital flows to emerg- ing market economies in Asia, although cushioned to some extent by an increase in official financing flows ... |
| 6 | 2008 | Indonesia | Not available | Not available |
| 7 | 2008 | world | 2007–2008 financial crisis | The 2007–2008 financial crisis, or the global financial crisis (GFC), was the most severe worldwide economic crisis since the 1929 Wall Street crash that began ... |
| 8 | 2022 | Indonesia | Indonesia's Economy Sees Rebound in 2022 following ... | Indonesia is projected to experience strong growth of 5.2 percent in 2022 thanks to the post-COVID-19 reopening of the economy and commodity price rises. |
| 9 | 2022 | world | World Economic Outlook, October 2022 | 11 Okt 2022 — Global economic activity is experiencing a broad-based and sharper-than-expected slowdown, with inflation higher than seen in several decades. |
As a result, I have extracted some news for the period that had a significant inflation rate. However, it is still not enough to determine what really happened in those years. Therefore, I also did some research on those years and came out with this summary:
Internal impact (Indonesia):
- 1961-1966: The political tensions and military conflicts during the Indonesia–Malaysia Confrontation.
- 1998: May 1998 riots happening in Indonesia because of political instability leading to the resignation of the president at that time
Global impact:
- 1973-1974: Oil Crisis during Arab-Israeli War
- High oil prices caused Indonesia's oil exports to increase significantly.
- 2008: Global Financial Crisis
- A series of crises happened, from the fall of housing prices to the bankruptcy of the big banks and hedge funds.
- 2022: Russia-Ukraine War
The research above showed that the inflation rate impacted by Indonesia's events is mostly political instability. An incident like the political situation in Indonesia will be less likely to happen again due to globalisation, increased political stability, and stronger democratic institutions. Therefore, the scope of this project would be from the year 1999 and onwards.
# Trim inflation to 1999-2023
inflation_data_1999_2023 = inflation_data.loc[:, '1999':]
# x axis (Year) and y axis (Inflation %)
year_1999_2023 = inflation_data_1999_2023.columns
indonesia_inflation_1999_2023 = inflation_data_1999_2023.iloc[0]
world_inflation_1999_2023 = inflation_data_1999_2023.iloc[1]
# Calculate standard deviation and mean
std_dev = indonesia_inflation_1999_2023.std()
indonesia_mean_inflation = indonesia_inflation_1999_2023.mean()
world_mean_inflation = world_inflation_1999_2023.mean()
# Visualize Indonesia Annual Inflation from 1999-2023
plt.figure(figsize=(14, 6))
plt.plot(year_1999_2023, indonesia_inflation_1999_2023, marker='o', linestyle='-', label='Indonesia Inflation %')
plt.plot(year_1999_2023, world_inflation_1999_2023, marker='o', linestyle='-', label='World Inflation %')
# Plot indonesia standard deviations
plt.axhline(y=indonesia_mean_inflation - std_dev, color='magenta', linestyle='--', label="-1 Indonesia Standard Deviation")
plt.axhline(y=indonesia_mean_inflation + std_dev, color='purple', linestyle='--', label="+1 Indonesia Standard Deviation")
plt.axhline(y=indonesia_mean_inflation + (2 * std_dev), color='green', linestyle='--', label="+2 Indonesia Standard Deviation")
plt.axhline(y=indonesia_mean_inflation + (3 * std_dev), color='blue', linestyle='--', label="+3 Indonesia Standard Deviation")
# Mean inflation of Indonesia and World from 1999-2023
plt.axhline(y=indonesia_mean_inflation, color='red', linestyle='solid', label='Indonesia Mean Inflation')
plt.title("Indonesia Inflation, Consumer Price (annual %)")
plt.xlabel('Year')
plt.xticks(rotation=45)
plt.ylabel('Inflation (%)')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# Print summary statistics
print(f"Indonesia Standard Deviation Inflation: {round(std_dev, 2)}")
print(f"Indonesia Mean Inflation: {round(indonesia_mean_inflation, 2)}%")
print(f"Indonesia Min Inflation: {round(indonesia_inflation_1999_2023.min(), 2)}%")
print(f"Indonesia Max Inflation: {round(indonesia_inflation_1999_2023.max(), 2)}%")
print(f"World Mean Inflation: {round(world_mean_inflation, 2)}%")
print(f"World Min Inflation: {round(world_inflation_1999_2023.min(), 2)}%")
print(f"World Max Inflation: {round(world_inflation_1999_2023.max(), 2)}%")
Indonesia Standard Deviation Inflation: 4.29 Indonesia Mean Inflation: 6.55% Indonesia Min Inflation: 1.56% Indonesia Max Inflation: 20.48% World Mean Inflation: 3.62% World Min Inflation: 1.44% World Max Inflation: 8.95%
Make the data more relevant: Since the inflation peak is in the year 1998 (max value in this time period: 20.48%), it will affect the year after it as well (recovery year). The inflation rate in 1999 is considered not relevant since the spread is too big compared to the subsequent years. This is also backed up by the fact that the inflation rate of 1999 is the max value in this period where the spread between the mean and max value is excessive.
Additionally, in a normal distribution, 99.7% of data falls within ±3 standard deviations from the mean. Values beyond 3 SDs are rare and often considered unusual or extreme. Therefore, this project timeline will be from the year 2000 to 2023, as this period is more relevant to the typical situation in Indonesia currently.
# Plot the new time scope (2000 - 2023)
# Trim inflation data to 2000-2023
inflation_data_2000_2023 = inflation_data.loc[:, '2000':]
# x axis (Year) and y axis (Inflation %)
year_2000_2023 = inflation_data_2000_2023.columns
indonesia_inflation_2000_2023 = inflation_data_2000_2023.iloc[0]
world_inflation_2000_2023 = inflation_data_2000_2023.iloc[1]
# Calculate standard deviation and mean
std_dev = inflation_data_2000_2023.std(axis=1).values[0]
indonesia_mean_inflation = indonesia_inflation_2000_2023.mean()
world_mean_inflation = world_inflation_2000_2023.mean()
# Visualize Indonesia's Annual Inflation from 2000-2023
plt.figure(figsize=(14, 6))
plt.plot(year_2000_2023, indonesia_inflation_2000_2023, marker='o', linestyle='-', label='Indonesia Inflation %')
plt.plot(year_2000_2023, world_inflation_2000_2023, marker='o', linestyle='-', label='World Inflation %')
# Mean inflation of Indonesia and World from 2000-2023
plt.axhline(y=indonesia_mean_inflation, color='red', linestyle='--', label='Indonesia Mean Inflation')
plt.axhline(y=world_mean_inflation, color='purple', linestyle='--', label='World Mean Inflation')
plt.title("Indonesia's Inflation, Consumer Price (annual %)")
plt.xlabel('Year')
plt.xticks(year_2000_2023)
plt.ylabel('Inflation (%)')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
print(f"Indonesia Mean Inflation: {round(indonesia_mean_inflation, 2)}%")
print(f"World Mean Inflation: {round(world_mean_inflation, 2)}%")
Indonesia Mean Inflation: 5.97% World Mean Inflation: 3.65%
As for the current time period (2000-2023), the data is more relevant to the situation in Indonesia currently. During this period, there are two major events that happened globally and affected the inflation rate of the world, including Indonesia. Therefore, these global events should be included in the project timeline.
5.5 Data Appropriateness for Further Analysis¶
Data has been appropriately prepared for further analysis by storing it in a pandas dataframe. Pandas DataFrames allow us to modify the DataFrame to add our own calculation to a new column. With a dataframe that is sorted by time, it is easier to process the data for visualisation with matplotlib or seaborn.
Data is in an appropriate format for a machine learning pipeline as well, particularly for time-series forecasting. Since the data is in a time series format, it is possible to do machine learning predictions. However, in the scope of this project, I will only use the data for exploratory data analysis, and if I wanted to carry out this project for further analysis, I would consider adding more data points for training and more features such as GDP, interest rate, etc., for a more accurate prediction.
6. Clear Rhetoric for Modifications to Data¶
After validating the data on section 4.1, Removing Illegal Values, I will continue to transform the data to improve clarity and draw interesting insights from it.
- There is a justification that is reasonable for the modifications with clear summaries as to how and why the data has been adapted for purpose.
- The modifications add value or utility in some capacity (e.g., descriptive power, performance improvement for analysis).
- Data has been changed in a systematic way that shows clear understanding and justification in exploration of concepts and ideas.
- Changes to data utilise advanced techniques, for example, by comparing a static CSV dataset against API fetched data to increase accuracy.
6.1 Transform Forex Data¶
In order to get a better understanding of the behaviour of the global currencies, I have decided to add columns for Yearly Percentage Change and Cumulative Percentage Change from 2000-2023 for all currency data.
Cumulative Percentage Change:
This tracks the overall price movement since the start of the period, which in this case is 2000. This helps understand the long-term trend and gives a broader perspective of how each currency has performed towards IDR over time.Formula:
$Cumulative Percentage Change = (\frac{Current Price}{Base Year Price} - 1) \times 100$The base year price is the December 1999 close price since the close price of December 1999 is the starting price of the year 2000.
Therefore, each row's percentage change is calculated relative to this value.Yearly Percentage Change:
By having Yearly Percentage Change column, I can measure the annual rate of change in the price, expressed as a percentage, and see how different currencies behave differently over the years (e.g., the volatility, spread, and I can compare with annual inflation data to find the relationships).Formula:
$Yearly Percentage Change = ( \frac{Current Year Price - Previous Year Price}{Previous Year Price}) \times 100$But instead of manually calculating the percentage change for each row, I will use the
.pct_change()method, which calculates the percentage change between the current and previous row. This will be useful in later analysis.
By adding these columns, we can get a better understanding of the behaviour of the global currencies.
- Cumulative Percentage Change Plotting:
# Function to clean, format, add yearly and cumulative percentage change to forex historical data
def process_forex_data(currency_pair):
# Make a copy of the combined forex data (to avoid any future conflicts)
df = combined_forex_data[currency_pair].copy()
# Filter data to ensure that it is still within the time period of this project
start_date = 1999
end_date = 2023
df = df[(df['Year'] >= start_date) & (df['Year'] <= end_date)]
# Get the close price of 1999 as the initial value
price_1999 = df['Price'].iloc[0]
# Add a new column for Percentage Change since the initial value and store the value
df['Cumulative Percentage Change'] = (df['Price'] / price_1999 - 1) * 100
# Add a new column for Yearly Percentage Change and store the value
df['Yearly Percentage Change'] = df['Price'].pct_change() * 100
# Delete the first row, since it is 1999 which was only used for calculating yearly percentage change column.
df = df.drop(index=df.index[:1])
df.reset_index(drop=True, inplace=True)
return df
Store all of the output from the process_forex_data function into a dictionary for easy access
# USD, EUR, GBP, JPY, and CAD 2000-2023
historical_data = {
"USD/IDR": process_forex_data("USD/IDR"),
"JPY/IDR": process_forex_data("JPY/IDR"),
"EUR/IDR": process_forex_data("EUR/IDR"),
"CAD/IDR": process_forex_data("CAD/IDR"),
"GBP/IDR": process_forex_data("GBP/IDR")
}
# Example output for EURIDR
display(historical_data["EUR/IDR"].head(4))
| Year | Price | Cumulative Percentage Change | Yearly Percentage Change | |
|---|---|---|---|---|
| 0 | 2000 | 9117.700000 | 29.514624 | 29.514624 |
| 1 | 2001 | 9268.500000 | 31.656700 | 1.653926 |
| 2 | 2002 | 9395.300000 | 33.457862 | 1.368075 |
| 3 | 2003 | 10594.099609 | 50.486507 | 12.759567 |
Since historical_data will be used later on during the visualisation for yearly percentage change, I will make a new data frame specifically for cumulative percentage change.
# Cumulative Percentage Change from 2000-2023 in Pandas DataFrame
cumulative_changes_df = pd.DataFrame({
"Year": historical_data["USD/IDR"]["Year"],
"USD/IDR": historical_data["USD/IDR"]["Cumulative Percentage Change"],
"JPY/IDR": historical_data["JPY/IDR"]["Cumulative Percentage Change"],
"EUR/IDR": historical_data["EUR/IDR"]["Cumulative Percentage Change"],
"CAD/IDR": historical_data["CAD/IDR"]["Cumulative Percentage Change"],
"GBP/IDR": historical_data["GBP/IDR"]["Cumulative Percentage Change"]
})
display(cumulative_changes_df.head(4))
| Year | USD/IDR | JPY/IDR | EUR/IDR | CAD/IDR | GBP/IDR | |
|---|---|---|---|---|---|---|
| 0 | 2000 | 38.461538 | 23.840187 | 29.514624 | 33.574902 | 28.038739 |
| 1 | 2001 | 49.624329 | 15.600761 | 31.656700 | 35.187592 | 33.926502 |
| 2 | 2002 | 28.085868 | 10.273672 | 33.457862 | 17.766902 | 27.562906 |
| 3 | 2003 | 20.276205 | 14.650964 | 50.486507 | 34.178340 | 33.126962 |
# Visualize Indonesia's Annual Inflation from 1960-2023
plt.figure(figsize=(14, 6))
plt.plot(cumulative_changes_df["Year"], cumulative_changes_df["USD/IDR"], marker='o', linestyle='-', label='USD/IDR')
plt.plot(cumulative_changes_df["Year"], cumulative_changes_df["JPY/IDR"], marker='o', linestyle='-', label='JPY/IDR')
plt.plot(cumulative_changes_df["Year"], cumulative_changes_df["EUR/IDR"], marker='o', linestyle='-', label='EUR/IDR')
plt.plot(cumulative_changes_df["Year"], cumulative_changes_df["CAD/IDR"], marker='o', linestyle='-', label='CAD/IDR')
plt.plot(cumulative_changes_df["Year"], cumulative_changes_df["GBP/IDR"], marker='o', linestyle='-', label='GBP/IDR')
plt.title("Key Global Currencies towards Indonesia's Rupiah (IDR) Cumulative Percentage change from 2000-2023")
plt.xlabel('Year')
plt.xticks(cumulative_changes_df["Year"])
plt.ylabel('Cumulative % Change')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
print("Cumulative Percentage Change from 2000-2023: ")
print(f"USD/IDR : {round(cumulative_changes_df['USD/IDR'].iloc[-1], 2)}%")
print(f"JPY/IDR : {round(cumulative_changes_df['JPY/IDR'].iloc[-1], 2)}%")
print(f"EUR/IDR : {round(cumulative_changes_df['EUR/IDR'].iloc[-1], 2)}%")
print(f"CAD/IDR : {round(cumulative_changes_df['CAD/IDR'].iloc[-1], 2)}%")
print(f"GBP/IDR : {round(cumulative_changes_df['GBP/IDR'].iloc[-1], 2)}%")
Cumulative Percentage Change from 2000-2023: USD/IDR : 120.15% JPY/IDR : 59.62% EUR/IDR : 139.96% CAD/IDR : 141.26% GBP/IDR : 73.71%
Key Insights from the Cumulative Percentage Change Graph:
This graph shows how the top five currencies (USD, JPY, EUR, CAD, GBP) performed against the Indonesian Rupiah (IDR) from 2000 to 2023. The better the performance of those currencies indicated that IDR depreciated more against them during this period. Among the top five currencies, EUR/IDR (green) performs the best, and JPY/IDR (red) performs the worst in the long term. For example, if I had 1,000,000 IDR worth of EUR at the start of 2000, it would be worth approximately 2,413,400 IDR by the end of 2023 due to a 141.34% cumulative increase. This shows that the EUR has significantly increased over the years, which led to the devaluation of the IDR. Essentially, we would need more IDR to buy the same amount of EUR.
Apart from that, we can also see that JPY has been underperforming from 2020 to 2023. During this period, other currencies had been increasing since then, but only JPY is in the downward trend.
“The main driver is the rate differential between the US and Japan,” Min Joo Kang. Source: aljazeera
Based on a quote from Min Joo Kang in this article, she mentioned that the primary cause of the weakness of JPY is due to the low interest rate.
- Yearly Percentage Change Plotting:
Make a new data frame specifically for yearly percentage change to avoid conflict with the cumulative percentage change.
# Yearly Percentage Change from 2000-2023 in Pandas DataFrame
yearly_forex_data = pd.DataFrame({
"Year": historical_data["USD/IDR"]["Year"],
"USD/IDR": historical_data["USD/IDR"]["Yearly Percentage Change"],
"JPY/IDR": historical_data["JPY/IDR"]["Yearly Percentage Change"],
"EUR/IDR": historical_data["EUR/IDR"]["Yearly Percentage Change"],
"CAD/IDR": historical_data["CAD/IDR"]["Yearly Percentage Change"],
"GBP/IDR": historical_data["GBP/IDR"]["Yearly Percentage Change"]
}).set_index("Year")
display(yearly_forex_data.head(4))
| USD/IDR | JPY/IDR | EUR/IDR | CAD/IDR | GBP/IDR | |
|---|---|---|---|---|---|
| Year | |||||
| 2000 | 38.461538 | 23.840187 | 29.514624 | 33.574902 | 28.038739 |
| 2001 | 8.062016 | -6.653273 | 1.653926 | 1.207330 | 4.598424 |
| 2002 | -14.395026 | -4.608178 | 1.368075 | -12.886308 | -4.751559 |
| 2003 | -6.097209 | 3.969480 | 12.759567 | 13.935526 | 4.361814 |
Grouped Bar Chart for Yearly Percentage Change
# Plot the grouped bar chart
# Documentation reference: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.plot.html
yearly_forex_data.plot(kind='bar', width=0.8, figsize=(14, 6))
# Alternating background colors for clarity and readability
for i in range(len(yearly_forex_data)):
if i % 2 == 0:
plt.axvspan(i - 0.5, i + 0.5, color='lightgray', alpha=0.5, zorder=0)
# Title and labels
plt.title("Key Global Currencies towards Indonesia's Rupiah (IDR) Yearly Percentage Change from 2000-2023")
plt.xlabel('Year')
plt.ylabel('% Change')
plt.xticks(rotation=0)
# Add legend and layout
plt.legend()
plt.tight_layout()
plt.show()
Key Insights from the Grouped Bar Chart:
- In the year 2000, all of the currency pairs had significant changes. This is because when I was deciding the time period for this project, I excluded the year 1999 because it is a recovery year that had extreme changes. Since this is the yearly change, which is calculated based on the previous year, therefore the year 2000 had significant changes as well but still within the normal range (99.7% of data falls within ±3 standard deviations from the mean).
- The narrowing of the spread has become more pronounced over the years, as there were fewer extreme changes, even though there were global pandemics in 2000 and the Russia-Ukraine war in 2022. This indicates that the trend is moving to a more stable and less sensitive.
Heat Map for Yearly Percentage Change
# Plot the heatmap
import seaborn as sns
# Plot the heatmap
plt.figure(figsize=(20, 6))
# Transpose and plot
sns.heatmap(yearly_forex_data.T, annot=True, fmt=".1f", cmap='coolwarm', linewidths=0.5)
plt.title('Yearly Percentage Change Heatmap (2000-2023)')
plt.xlabel('Year')
plt.ylabel('Currency Pair')
plt.show()
Key Insights from Heat Map:
Interestingly, the Japanese yen rose significantly (darkest red: 45.3% in 2008) during the financial crisis that happened in the US. This phenomenon shows that the Japanese economy becomes stronger when the Western countries' economies are weaker. This case also reflects the year 2013 when the western countries rose significantly (USD, EUR, and GBP rose more than 18% in 2008), but Japan did not rise that much (JPY rose around 4%). This behaviour shows that Europe and America share a similar level of economic situation, which made them closely related in some cases.
A quote from 9DashLine states that
"In contrast to other Asian currencies, the yen appreciated during the 2008 global financial crisis from over 124 in June 2007 to 108 to the USD in August 2008 and below 90 by December 2009, mainly due to repatriation and liquidity crises in the West fuelling demand for the yen."
We can conclude that the rise of JPY in 2008 was mainly because of Japanese investors and companies moving their money back to Japan and treating Japan as a safe haven. Safe haven means it is an asset that people think is safe at that point in time.
Box Plot for Yearly Percentage Change
# Box plot for yearly percentage change
plt.figure(figsize=(10, 6))
# Using the dataframe to plot the box plot
sns.boxplot(data=yearly_forex_data, palette=['blue', 'green', 'red', 'purple', 'orange'])
plt.title("Yearly Percentage Change (2000-2023)", fontsize=14)
plt.xlabel("Currency Pair", fontsize=12)
plt.ylabel("Yearly Percentage Change (%)", fontsize=12)
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.show()
# Iterate over each column and calculate the median
print("Median yearly percentage change from 2000-2023:")
for column in yearly_forex_data.columns:
median_value = yearly_forex_data[column].median()
print(f"{column}: {median_value:.2f}%")
Median yearly percentage change from 2000-2023: USD/IDR: 2.47% JPY/IDR: 2.54% EUR/IDR: 1.51% CAD/IDR: 1.53% GBP/IDR: 2.79%
Key insights from the box plot:
- The medians of all of the currencies are very similar, ranging from 1.51% to 2.79%.
- The presence of outliers in USD, JPY, CAD, and GBP indicates occasional extreme changes.
- EUR/IDR shows the largest interquartile range (IQR), indicating higher variability.
- USD/IDR and CAD/IDR appear to have tighter IQRs; this means they have more stability in their yearly changes.
Violin Plot for Yearly Percentage Change
# Violin plot for yearly percentage change
plt.figure(figsize=(10, 6))
# Using the dataframe to plot the violin plot
sns.violinplot(data=yearly_forex_data)
plt.title("Distribution of Yearly Percentage Changes")
plt.xlabel("Currency")
plt.ylabel("Depreciation Rate (%)")
plt.grid(True)
plt.show()
Key insights from violin plot: By plotting a violin plot, we can visualise how the data is distributed. Most of the data lies near the median, indicating a concentration of values around the median value.
# Calculate the correlation matrix for the yearly forex data
correlation = yearly_forex_data.corr()
# Generate a heatmap of the correlation matrix
plt.figure(figsize=(8, 6))
sns.heatmap(correlation, annot=True, cmap='coolwarm')
plt.title('Correlation Matrix of Currency Depreciation')
plt.show()
Correlation Plot Key Insights JPY has the smallest correlation to all of the currencies. This indicates that JPY is moving in the same trend as other currencies, but the relationship is not very strong. Personally, I believe it is because Japan is in the Asian region, which has different behaviour compared to the western countries.
7. Data analysis¶
This section onwards will take the cleaned and explored data to calculate the inflation rate of Indonesia relative to the global market.
7.1 Find Average Yearly Inflation Rate from 2000-2023¶
Average Inflation Rate of Indonesia has already been calculated in section 5.4. It was declared as indonesia_mean_inflation
print(f"Indonesia Average Yearly Inflation: {round(indonesia_mean_inflation, 2)}%")
Indonesia Average Yearly Inflation: 5.97%
print(f"Indonesia Yearly Inflation: \n{round(indonesia_inflation_1999_2023.head(6), 2)}%")
Indonesia Yearly Inflation: 1999 20.48 2000 3.69 2001 11.50 2002 11.90 2003 6.76 2004 6.06 Name: IDN, dtype: float64%
7.2 Find IDR Depreciation Rate from 2000-2023¶
There are two method chosen to calculate yearly IDR depreciation rate which are normal average and CAGR (Compounding Annual Growth Rate). These methods will further elaborated in their sub-sections.
Scatter plot to see the distribution area of the data
7.2.1 Find Average Yearly IDR Depreciation Rate from 2000-2023¶
# Preview yearly_forex_data dataframe
display(yearly_forex_data.head(4))
| USD/IDR | JPY/IDR | EUR/IDR | CAD/IDR | GBP/IDR | |
|---|---|---|---|---|---|
| Year | |||||
| 2000 | 38.461538 | 23.840187 | 29.514624 | 33.574902 | 28.038739 |
| 2001 | 8.062016 | -6.653273 | 1.653926 | 1.207330 | 4.598424 |
| 2002 | -14.395026 | -4.608178 | 1.368075 | -12.886308 | -4.751559 |
| 2003 | -6.097209 | 3.969480 | 12.759567 | 13.935526 | 4.361814 |
Add average of all currencies column
# Make a copy of the dataframe to prevent future conflicts with other analysis
yearly_forex_data_with_avg = yearly_forex_data.copy()
# Average value of the row (all currencies on the particular year)
yearly_forex_data_with_avg['Average'] = yearly_forex_data_with_avg.mean(axis=1)
display(yearly_forex_data_with_avg.head(4))
| USD/IDR | JPY/IDR | EUR/IDR | CAD/IDR | GBP/IDR | Average | |
|---|---|---|---|---|---|---|
| Year | ||||||
| 2000 | 38.461538 | 23.840187 | 29.514624 | 33.574902 | 28.038739 | 30.685998 |
| 2001 | 8.062016 | -6.653273 | 1.653926 | 1.207330 | 4.598424 | 1.773684 |
| 2002 | -14.395026 | -4.608178 | 1.368075 | -12.886308 | -4.751559 | -7.054599 |
| 2003 | -6.097209 | 3.969480 | 12.759567 | 13.935526 | 4.361814 | 5.785836 |
Visualise the DataFrame with Scatter Plot:
# Plot a scatter plot for top five currencies depreciation vs inflation on the corresponding year
plt.figure(figsize=(10, 6))
for currency_pair in historical_data.keys():
plt.scatter(
indonesia_inflation_1999_2023.iloc[1:],
yearly_forex_data[currency_pair],
label=f"{currency_pair} Depreciation vs Inflation"
)
# Set up and show the plot
plt.xlabel('Indonesia Inflation Rate (%)')
plt.ylabel('IDR Depreciation rate (%)')
plt.title('Indonesia Annual Inflation vs Average IDR Depreciation 2000 - 2023')
plt.grid(True, linestyle='--')
plt.legend()
plt.show()
Scatter Plot Key Insights
The data mainly scattered in two distinct areas:
- Low inflation and low depreciation rate zone:
- Indicating that most of the data lies in the 2 to 4% inflation range with a -10 to 10% depreciation rate area.
- Moderate inflation and low depreciation rate zone:
- Another major cluster lies in the area of 10 to 12% inflation with a -10 to 10% depreciation rate area.
- Low inflation and low depreciation rate zone:
# Calculate each currency pair average and store it in the dictionary
yearly_mean = {}
for i in yearly_forex_data.columns:
yearly_mean[i] = yearly_forex_data[i].mean()
# Calculate the overall mean
overall_yearly_mean = sum(yearly_mean.values()) / len(yearly_mean)
# Plot the horizontal bar chart
plt.figure(figsize=(10, 6))
# Create a horizontal bar chart for mean percentage change
mean_percentage_change_chart = plt.barh(
yearly_forex_data.columns,
yearly_mean.values(),
color=['blue', 'green', 'red', 'purple', 'orange'],
zorder=2
)
# Display the labels of percentage values on each bar
for bar in mean_percentage_change_chart:
plt.text(bar.get_width() - (bar.get_width() * 0.05),
bar.get_y() + bar.get_height()/2,
f'{bar.get_width():.2f}%',
va='center',
ha='right',
color='white',
fontweight='bold')
# Average value line
plt.axvline(
overall_yearly_mean,
color='black',
linestyle='--',
linewidth=1.5,
label=f'Overall Average: {round(overall_yearly_mean, 2)}%'
)
# Set up and show the plot
plt.grid(axis='x')
plt.xlabel('% Change')
plt.ylabel('Currency Pair')
plt.title('Average Yearly Percentage Change (2020-2023)')
plt.legend()
plt.tight_layout()
plt.show()
print(f"IDR Average Depreciation Rate: {round(overall_yearly_mean, 2)}%")
IDR Average Depreciation Rate: 3.65%
IDR Depreciation Rate Insights
The IDR average depreciation rate is at 3.65%, which aligns well with the scatter plot analysis. The value lies in the midpoint of this band, which is in the range of -10 to 10%.
7.2.2 Find IDR Depreciation Rate from 2000-2023 with CAGR Method¶
CAGR represents the consistent annual growth rate over a specified time period.
Formula:
$CAGR = [\left( \frac{FV}{IV} \right)^{\frac{1}{n}} - 1] \times 100$
Where:
- $FV$ = Final Value
- $IV$ = Initial Value
- $n$ = Number of years
Steps:
- Calculate the CAGR for each currency pair
- Average all results
# Create a new dataframe to calculate the CAGR for each currency
forex_cagr_data = pd.DataFrame({
"Currency Pair": historical_data.keys(),
})
# Number of years
year_count = 2023 - 2000
# Get the initial values
initial_values = []
for key in historical_data.keys():
price = historical_data[key]['Price'][0]
percentage_change = historical_data[key]["Yearly Percentage Change"][0]
initial_value = price / (1 + percentage_change / 100)
initial_values.append(initial_value)
# Get the final values
final_values = []
for key in historical_data.keys():
price_2023 = historical_data[key].loc[historical_data[key]['Year'] == 2023, 'Price'].values[0]
final_values.append(price_2023)
# Store the values in the dataframe
forex_cagr_data['Initial Value'] = initial_values
forex_cagr_data['Final Value'] = final_values
# Calculate the CAGR for every currency
CAGR_values = (((forex_cagr_data['Final Value'] / forex_cagr_data['Initial Value']) ** (1 / year_count)) - 1) * 100
# Store the values in the dataframe
forex_cagr_data['CAGR'] = CAGR_values
# Preview dataframe
display(forex_cagr_data)
| Currency Pair | Initial Value | Final Value | CAGR | |
|---|---|---|---|---|
| 0 | USD/IDR | 6987.50 | 15383.099609 | 3.490613 |
| 1 | JPY/IDR | 68.33 | 109.068024 | 2.053952 |
| 2 | EUR/IDR | 7039.90 | 16893.000000 | 3.879019 |
| 3 | CAD/IDR | 4832.30 | 11658.270508 | 3.903356 |
| 4 | GBP/IDR | 11306.50 | 19640.048828 | 2.429891 |
# Plot the horizontal bar chart
plt.figure(figsize=(10, 6))
# Create a horizontal bar chart for mean percentage change
forex_CAGR_chart = plt.barh(
forex_cagr_data['Currency Pair'],
forex_cagr_data['CAGR'],
color=['blue', 'green', 'red', 'purple', 'orange'],
zorder=2
)
# Display the labels of percentage values on each bar
for bar in forex_CAGR_chart:
plt.text(
bar.get_width() - (bar.get_width() * 0.05),
bar.get_y() + bar.get_height() / 2,
f'{bar.get_width():.2f}%',
va='center',
ha='right',
color='white',
fontweight='bold'
)
overall_forex_cagr_mean = forex_cagr_data['CAGR'].mean()
# Average value line
plt.axvline(
overall_forex_cagr_mean,
color='black',
linestyle='--',
linewidth=1.5,
label=f'Overall Average: {round(overall_forex_cagr_mean, 2)}%'
)
# Set up and show the plot
plt.grid(axis='x')
plt.xlabel('Compounding Annual Growth Rate (CAGR)')
plt.ylabel('Currency Pair')
plt.title('Average IDR Depreciation from 2020-2023')
plt.legend()
plt.tight_layout()
plt.show()
print(f"IDR Average Depreciation Rate with CAGR method: {round(overall_forex_cagr_mean, 2)}%")
IDR Average Depreciation Rate with CAGR method: 3.15%
7.2.3 Calculate the Final IDR Depreciation Rate¶
To determine the final depreciation rate of IDR, i will combine the results obtained from the CAGR method and the yearly average method. By taking the average of both results, we can get a more balanced answer.
Formula:
$IDR Depreciation Rate = \frac{Yearly Forex Change Mean + Forex CAGR}{2}$
idr_depreciaton_rate = (overall_forex_cagr_mean + overall_yearly_mean)/2
print(f"IDR Depreciation Rate: {round(idr_depreciaton_rate, 2)}%")
IDR Depreciation Rate: 3.4%
7.3 Find Indonesia Inflation Rate Relative to the Global Market¶
To calculate the main answer for this project, I will sum the Indonesia inflation rate and IDR depreciation rate.
indonesia_global_inflation_rate = indonesia_mean_inflation + idr_depreciaton_rate
print(f"Indonesia Inflation Rate Relative to the Global Market: {round(indonesia_global_inflation_rate, 2)}%")
Indonesia Inflation Rate Relative to the Global Market: 9.37%
8. Summary¶
In conclusion, 9.37% should be the investment portfolio return benchmark for Indonesians if they want to outperform domestic inflation and mitigate the impact of IDR depreciation against key global currencies. This figure accounts for both the average inflation rate and the historical depreciation of the IDR.
However, it is important to note that this analysis is based solely on historical price data and is intended to bring out a benchmark for an optimal investing portfolio return in Indonesia. Readers should not rely fully on this analysis for making investment decisions, as future market conditions may be different from the past. Reader should always consider other factors before making investment choices.
References:¶
Chen, James. 2024. "Key Currency: What it Means, How it Works, Example." Last modified September 28 2024. Investopedia.
Palomba, Geremia. 2012. "Explaining Higher Inflation in Indonesia: A Regional Comparison." Published on January 19 2012. IMF eLibrary.
Aroussi, Ran. 2024. "Yfinance Documentation." Last modified 2024. GitHub.
Investing.com. 2024. "Terms And Conditions." Identification for Development. Last modified November 14 2024. GitHub.
World Bank. 2024. "Data Access And Licensing." Identification for Development. Last modified 2024. World Bank.
World Bank. 2019. "Attribution." Identification for Development. Last modified 2019. World Bank.
World Bank. 2024. "API Basic Call Structures." Identification for Development. Last modified 2024. World Bank.
Pandas. "pandas.DataFrame.plot." Accessed on 25 Dec 2024. Pandas Documentation.
Dataset Sources
Bank Indonesia. 2024. "Data Inflasi." Accessed on 10 Dec 2024. Bank Indonesia.
Fxtop. 2024. "Historical Exchange Rates." Accessed on 10 Dec 2024. Fxtop.
Investing.com. 2024. "USD/IDR Historical Data." Accessed on 10 Dec 2024. Investing.com.
Investing.com. 2024. "EUR/IDR Historical Data." Accessed on 10 Dec 2024. Investing.com.
Investing.com. 2024. "GBP/IDR Historical Data." Accessed on 10 Dec 2024. Investing.com.
Investing.com. 2024. "JPY/IDR Historical Data." Accessed on 10 Dec 2024. Investing.com.
Investing.com. 2024. "CAD/IDR Historical Data." Accessed on 10 Dec 2024. Investing.com.
Macrotrend. 2024. "Indonesia Inflation Rate 1960-2025." Identification for Development. Accessed on 10 Dec 2024. Macrotrend.
Trading Economics. 2024. "Indonesia Inflation Rate." Accessed on 10 Dec 2024. Trading Economics.
World Bank Group. 2024. "Inflation, consumer prices (annual %) - Indonesia." Accessed on 10 Dec 2024. World Bank.
Yahoo Finance. 2024. "USD/IDR Historical Data." Accessed on 10 Dec 2024. Yahoo Finance.
Yahoo Finance. 2024. "EUR/IDR Historical Data." Accessed on 10 Dec 2024. Yahoo Finance.
Yahoo Finance. 2024. "GBP/IDR Historical Data." Accessed on 10 Dec 2024. Yahoo Finance.
Yahoo Finance. 2024. "JPY/IDR Historical Data." Accessed on 10 Dec 2024. Yahoo Finance.
Yahoo Finance. 2024. "CAD/IDR Historical Data." Accessed on 10 Dec 2024. Yahoo Finance.