Predicting price movement for currency pairs¶

Title: Predicting price movement for currency pairs
Author: Michal Bezak, Tangent Works
Industry: Finance
Area: Trading - Forex
Type: Forecasting / Classification

Description¶

Trading on financial markets is very risky endeavor and, when proper risk management is not in place, losses are just around the corner. It requires many elements to construct profitable trading strategy while risk management and position sizing should be certainly in the mix. One of the essential building blocks is mechanism estimating price movement.

Each market has different level of volatility and risk. One of the most liquid markets in the world is Forex where currency pairs are traded, e.g. EURUSD, NOKEUR etc. Forex is traded 24 hours a day 5 days a week and allows traders to open and close positions at rapid speed.

Our Use case demonstrates how TIM can support forecasting of direction of price movement on hourly basis. TIM will forecast price movement (direction) for the next hour. With this information, traders would be more confident to open new position. To evaluate business results we take very simple approach - each hour position is liquidated completely.

Business parameters¶

Business objective: Make profit from trading on forex intra-day market
Value: Direct input into decision making process when opening positions - understanding movement of price in upcoming hour
KPI: Profit / Loss

Disclaimer: Please note that very purpose of this notebook is demonstration only and must not be taken as advice to be followed (e.g. to trade real money); We provide full disclaimer at the very bottom of this page, read it carefully.

In [2]:
import logging
import pandas as pd
import plotly as plt
import plotly.express as px
import plotly.graph_objects as go
import numpy as np
import json
import pickle
import datetime

import tim_client

Credentials and logging

(Do not forget to fill in your credentials in the credentials.json file)

In [3]:
with open('credentials.json') as f:
    credentials_json = json.load(f)                     # loading the credentials from credentials.json

TIM_URL = 'https://timws.tangent.works/v4/api'          # URL to which the requests are sent

SAVE_JSON = False                                       # if True - JSON requests and responses are saved to JSON_SAVING_FOLDER
JSON_SAVING_FOLDER = 'logs/'                            # folder where the requests and responses are stored

LOGGING_LEVEL = 'INFO'
In [4]:
level = logging.getLevelName(LOGGING_LEVEL)
logging.basicConfig(level=level, format='[%(levelname)s] %(asctime)s - %(name)s:%(funcName)s:%(lineno)s - %(message)s')
logger = logging.getLogger(__name__)
In [5]:
credentials = tim_client.Credentials(credentials_json['license_key'], credentials_json['email'], credentials_json['password'], tim_url=TIM_URL)
api_client = tim_client.ApiClient(credentials)

api_client.save_json = SAVE_JSON
api_client.json_saving_folder_path = JSON_SAVING_FOLDER
[INFO] 2021-01-22 13:56:00,271 - tim_client.api_client:save_json:66 - Saving JSONs functionality has been disabled
[INFO] 2021-01-22 13:56:00,274 - tim_client.api_client:json_saving_folder_path:75 - JSON destination folder changed to logs

We will collect results for evaluation in "results".

In [6]:
results = { 'long': {}, 'short': {} }

Dataset¶

Source dataset contains values for USDCHF currency pair (open, high, low, close, volume).

Two additional columns were added:

  • change - change of USDCHF closing price against value at previous timestamp,
  • direction - if change is >0 it is 1, otherwise 0; direction is our target to be predicted.

Sampling and gaps¶

Data are sampled on hourly basis and contain gaps because Forex is not traded during weekends.

Data¶

Column name Description Type Availability
timestamp Timestamp on hourly basis - GMT Timestamp column
direction 1 if change is >0, otherwise 0 Target t-1
change Change against previous timestamp for c_usdchf Predictor t-1
c_usdchf Price at which trading closed for given hour (USDCHF), our target Predictor t-1
o_usdchf Price at which particular hour opened (USDCHF) Predictor t-1
h_usdchf The highest price in given interval Predictor t-1
l_usdchf The lowest price in given interval (USDCHF) Predictor t-1
v_usdchf Volume traded (USDCHF) Predictor t-1

Forecasting situations¶

TIM detects forecasting situation from current "shape" of data, e.g. if last target value is available at 15:00 timestamp, it will start forecasting as of 16:00. It will also takes the last 15:00 timestamp as reference point against which availability per each column in dataset is determined - this rule is then followed for back-testing when calculating results for out-of-sample interval.

In our case all values are aligned until t-1.

We wanted to back-test all forecasting situations during the day (i.e. all 24 hours), so there are 24 versions of dataset available.

Package of CSV files used in experiments can be downloaded here.

Source¶

Raw data used in this demonstration were downloaded from INVESTING.COM website.

In [7]:
SITUATIONS = { h: 'data_'+str(h)+'_4B.csv' for h in range(0,24) }
  
SITUATIONS
Out[7]:
{0: 'data_0_4B.csv',
 1: 'data_1_4B.csv',
 2: 'data_2_4B.csv',
 3: 'data_3_4B.csv',
 4: 'data_4_4B.csv',
 5: 'data_5_4B.csv',
 6: 'data_6_4B.csv',
 7: 'data_7_4B.csv',
 8: 'data_8_4B.csv',
 9: 'data_9_4B.csv',
 10: 'data_10_4B.csv',
 11: 'data_11_4B.csv',
 12: 'data_12_4B.csv',
 13: 'data_13_4B.csv',
 14: 'data_14_4B.csv',
 15: 'data_15_4B.csv',
 16: 'data_16_4B.csv',
 17: 'data_17_4B.csv',
 18: 'data_18_4B.csv',
 19: 'data_19_4B.csv',
 20: 'data_20_4B.csv',
 21: 'data_21_4B.csv',
 22: 'data_22_4B.csv',
 23: 'data_23_4B.csv'}

We will run all cells below (until Evaluation section) 24 times, to simulate results for each forecasting situation.

Cell below sets the current situation, i.e. to simulate hour at which we will be forecasting.

In [974]:
situation = 23

Read dataset per given situation.

In [975]:
data = tim_client.load_dataset_from_csv_file('data/'+SITUATIONS[ situation ], sep=',')
In [976]:
data.head()
Out[976]:
timestamp direction c_usdchf o_usdchf h_usdchf l_usdchf v_usdchf change
0 2016-02-08 03:00:00 1.0 0.9938 0.9932 0.9938 0.9929 0 0.0004
1 2016-02-08 04:00:00 0.0 0.9938 0.9938 0.9941 0.9932 0 0.0000
2 2016-02-08 05:00:00 0.0 0.9937 0.9938 0.9942 0.9934 0 -0.0001
3 2016-02-08 06:00:00 0.0 0.9936 0.9938 0.9942 0.9935 0 -0.0001
4 2016-02-08 07:00:00 1.0 0.9938 0.9936 0.9942 0.9936 0 0.0002
In [977]:
data.tail()
Out[977]:
timestamp direction c_usdchf o_usdchf h_usdchf l_usdchf v_usdchf change
24226 2019-12-12 18:00:00 1.0 0.98660 0.98610 0.98690 0.98555 1829 0.00040
24227 2019-12-12 19:00:00 0.0 0.98610 0.98645 0.98705 0.98595 2013 -0.00050
24228 2019-12-12 20:00:00 0.0 0.98580 0.98615 0.98730 0.98560 2234 -0.00030
24229 2019-12-12 21:00:00 0.0 0.98475 0.98600 0.98640 0.98460 2384 -0.00105
24230 2019-12-12 22:00:00 1.0 0.98510 0.98480 0.98535 0.98475 1012 0.00035
In [978]:
data.shape
Out[978]:
(24231, 8)

Visualisation of closing price.

In [979]:
fig = plt.subplots.make_subplots(rows=1, cols=1, shared_xaxes=True, vertical_spacing=0.02)  

fig.add_trace( go.Scatter( x = data.loc[:, "timestamp"], y=data.loc[:, "c_usdchf"], name = "USDCHF", line=dict(color='blue')), row=1, col=1) 

fig.update_layout(height=500, width=1000, title = 'Closing price: USDCHF')                           

fig.show()