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 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.
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)
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'
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__)
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".
results = { 'long': {}, 'short': {} }
Source dataset contains values for USDCHF currency pair (open, high, low, close, volume).
Two additional columns were added:
Data are sampled on hourly basis and contain gaps because Forex is not traded during weekends.
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 |
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.
Raw data used in this demonstration were downloaded from INVESTING.COM website.
SITUATIONS = { h: 'data_'+str(h)+'_4B.csv' for h in range(0,24) }
SITUATIONS
{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.
situation = 23
Read dataset per given situation.
data = tim_client.load_dataset_from_csv_file('data/'+SITUATIONS[ situation ], sep=',')
data.head()
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 |
data.tail()
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 |
data.shape
(24231, 8)
Visualisation of closing price.
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()