LSTM forecasting of US-Singapore Exchange Rate using Fed Fund Rate and SIBOR (99.88% Accurate!)

Isaac Chan
7 min readDec 15, 2019

As of writing, I would be leaving in 2 weeks time to the United States for my exchange program at the University of Michigan!

So of course I would need to prepare enough US currency for my trip, and finding out when was the best time to convert SGD to USD was on our minds. Hence, I decided to use a Long-Short Term Memory Recurrent Neural Network to forecast the exchange rate.

In this post, I would only cover the background of the data, the codes, the rationale for choosing several parameters and a discussion of the final results. I would not share about the theory behind LSTMs, as there are many who have covered it elsewhere. Instead, I’ll share more on how the LSTM has been fitted to this specific context.

Source: World Atlas

Why Long-Short Term Memory?

Before LSTM came to be used, there was a popular time-series model known as Auto-Regressive Integrated Moving Average (ARIMA) which was used heavily. However, LSTM have shown to outperform the ARIMA constantly.

The ability to “remember” information and sequences, “forgetting” more unessential ones and combining “memory” with new inputs is certainly a very powerful concept.

Apart from a variety of applications, LSTMs have been heavily applied in finance as well, especially in forecasting share prices. With lots of time-series data available, LSTMs are prime to make use of such information for useful predictions.

Source: John Hopkins Medicine

Data: Historical Rates, Federal Funds Rate and SIBOR

In this mini-project, I not only used the historical US-SGD exchange rate, I also used the Federal Funds Rate (DFF) and the Singapore Interbank Offered Rate (SIBOR).

DFF refers to the interest rate that US banks charge other banks for lending them money from their reserve balances on an overnight basis. SIBOR is similar, where it is a daily reference rate based on the interest rates at which banks offer to lend unsecured funds to other banks in the Singapore.

These two rates are heavily watched by investors, since these rates act as a “floor” for the interest rates of many other financial products. In Singapore, certain loans have interest rates that are charged by “SIBOR + spread”, where spread is a range of rates above the SIBOR.

Since interest rates are tied closely to the exchange rate, especially for Singapore, I figured that combining these 3 datasets would provide better predictions.

Relationships between the 3 Rates

Daily Exchange Rate and DFF for the last 1,500 days

First, I plotted the exchange rate and the DFF. Visually, we can’t immediately see a strong correlation between these two rates. At least in both cases, both rates had risen over time. We can see that the DFF had risen quite substantially, which reflects how the interest rate policy of the US government has changed to reflect the recovering economy since the crisis.

Daily Exchange Rate and SIBOR

Next, the exchange rate and SIBOR are plotted. Compared to the DFF, SIBOR seems to have a stronger relationship with the exchange rate. We also notice that the SIBOR is much less volatile than the DFF. Moreover, both the DFF and SIBOR have both risen over time, reflecting how growth in the two economies have shaped interest rates.

I avoided using r² to measure the relationships between these variables, as their relationships are most likely non-linear, and the effect of the variables on each other happen over many days, leaving some time lag.

Data Preprocessing

import pandas as pd
import numpy as np
DFF=pd.read_csv("DFF.csv")
DFF["DATE"]=pd.to_datetime(DFF["DATE"])
ER=pd.read_csv("us-dollar-singapore-exchange-rate-historical-chart.csv")[3:]
ER.columns = ['Date', 'ER']
ER['Date']=pd.to_datetime(ER["Date"])
SIBOR=pd.read_csv("SIBOR.csv").iloc[:,:2]
SIBOR["SIBOR DATE"]=pd.to_datetime(SIBOR["SIBOR DATE"])
data=pd.merge(ER,SIBOR , left_on='Date', right_on='SIBOR DATE',how='inner')
data=pd.merge(data,DFF , left_on='Date', right_on='DATE',how='inner')
data=data.drop(columns=['SIBOR DATE', 'DATE'])
data['ER']=pd.to_numeric(data['ER'])

Nothing really interesting happens here per se, as I’m just merging the 3 datasets into one dataset. Some functions are also applied to convert strings to date format to facilitate the merging.

Training and Test Set

dataset_train=data.iloc[:-30,:] # Set test set size of 30
dataset_test=data.iloc[-30:,:] # Set test set size of 30
training_set = dataset_train.iloc[:, 1:4].values # Selecting the relevant columns
# Feature Scaling to normalise the data
from sklearn.preprocessing import MinMaxScaler
sc = MinMaxScaler(feature_range = (0, 1))
training_set_scaled = sc.fit_transform(training_set)

I will train the model from all the data except the last 30 days, with these 30 days being used as our test set to validate our model’s prediction. Feature scaling is also applied to normalise the data.

Time Steps

# Creating a matrix based on 60 timesteps. Hence, the model is trained on n-60th day exchange rate to predict the nth day's rate
X_train = []
y_train = []
time_steps=60
for i in range(time_steps, len(dataset_train.index)):
X_train.append(training_set_scaled[i-time_steps:i,:3])
y_train.append(training_set_scaled[i, 0])
X_train, y_train = np.array(X_train), np.array(y_train)
# Reshaping the data
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 3))

Time steps of 60 days is chosen here. This means the model will use the results from Day 1 to Day 60 to predict the rate at Day 61. Such a figure can certainly be toggled with to achieve better results.

Additionally, the data is appended into a list to form arrays within a list, which is then reshaped into another array meant to be fed into the LSTM.

# Building the RNN
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import Dropout
from sklearn.metrics import mean_squared_error
from math import sqrt
regressor = Sequential()regressor.add(LSTM(units = 60, return_sequences = True, input_shape = (X_train.shape[1], 3)))
regressor.add(Dropout(0.2))
regressor.add(LSTM(units = 60, return_sequences = True))
regressor.add(Dropout(0.2)) # 0.2 is a good number to use
regressor.add(LSTM(units = 60))
regressor.add(Dropout(0.2))
regressor.add(Dense(units = 1))regressor.compile(optimizer = 'adam', loss = 'mean_squared_error')# Fitting the RNN to the Training set. Can change the number of epochs and batch sizeregressor.fit(X_train, y_train, epochs = 200, batch_size = 32)

Here, all the important libraries are imported. Four layers of neural network are chosen here, with one input layer, one output layer and two hidden layers. 50 neurons were chosen, and a dropout regularisation rate of 0.2 is chosen to prevent overfitting.

The “adam” optimiser was chosen, as it seemed to be perform better than the “rmsprop” optimiser here. Also, a batch_size of 32 had been selected, which is the number of observations processed before the model is updated.

Predicting the Exchange Rate

real_rate = dataset_test.iloc[:, 1:2].valuesdataset_total = pd.concat((dataset_train.iloc[:,1:4], dataset_test.iloc[:,1:4]), axis = 0) 
inputs = np.array(dataset_total.iloc[len(dataset_total) - len(dataset_test) - time_steps:,:3])
inputs = inputs.reshape(-1,3)
inputs = sc.transform(inputs)
X_test = []
for i in range(time_steps,len(dataset_test.index)+time_steps):
X_test.append(inputs[i-time_steps:i, 0:3])
X_test = np.array(X_test)
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 3))
predicted_rate = regressor.predict(X_test)
sc.fit_transform(training_set[:,0:1])
predicted_rate=sc.inverse_transform(predicted_rate)
real_rate=real_rate.astype(np.float)
Mean_Deviation=sqrt(mean_squared_error(predicted_rate, real_rate))/np.mean(real_rate)*100
print("Mean deviation from actual exchange rate is "+str(round(Mean_Deviation, 3))+"%")

Finally, the model is used to predict the exchange rate for the following 30 days. The Root Mean Squared Error (RMSE) is calculated, which is the average error of the predicted rate VS the actual rate. For this iteration, the RMSE is 0.0034, which means that the predicted rate is 0.0034 units off the actual rate.

This value is then used as a percentage against the median exchange rate within the test set, as a measure of how far off the predicted results are compared to the real results. Here, we have a percentage of 0.25%, meaning that predicted rate is 0.25% off the actual rate.

Analysis of Results

Real VS Predicted Results

When we compare the predicted results (blue line) agains the actual results (red line), we can see that the trend is well mirrored by the model.

However, we do see that the model lags behind by a day when a change in trend is produced. The model also consistently underestimates the exchange rate, producing the RMSE mentioned earlier.

Error Analysis

Error Over Time

For the most part, the model underpredicts what the real exchange rate would be. Also, the model seems to overpredict more for the first 15 days, and underpredicts for the remaining 15 days. Interestingly, the number of over and under predictions are the same.

Capitalising On Results

Source: Nerd’s Magazine

A “99.88%” accuracy sounds good, but is such an accuracy really indicative of how good the model performs?

For the most part, the range of the exchange rate floats between 1.24 and 1.45 over the last 5 years, which is a narrow band width of 0.2. Thus, if predicted value was 0.2 off the real value, this would simply represent an error percentage of 15.7%, or an accuracy rate of 84%, which sounds misleading. Since the exchange rate has such a narrow band of movement, the RMSE, though seemingly low, may not be that good a measurement.

However, I believe that you can still make use of the model, by ensuring that the predicted buy price is lower than what you normally expect, and your sell price is higher than normally expected. This might help to ensure you would not get too high a buy price or too low a sell price if the model makes a substantially wrong decision. Lastly, the predictions seem to hug the value of the real exchange rate very closely, with an error rate of 0.12%.

To Conclude…

In this post, I shared on how LSTM can be used to forecast currency rates. I’m sure much more analysis can be done here, such as using different time horizons and maybe even more features. For now, I’ll use the model to trade some currencies and hopefully make a profit :)

References

Deep Learning A-Z™: Hands-On Artificial Neural Networks

--

--

Isaac Chan

An NLP Data Scientist always seeking to improve his skills :)