Stochastic Strategy Backtest using Python and REST API
In today’s tutorial, we will be using a stochastic indictor, REST API and FXCM’s Python wrapper, fxcmpy to create a strategy. The Jupyter notebook will be used to do a simple backtest of the strategy that will trigger trades based on the Percent K and Percent D lines of the stochastic indicator.
It is important to remember that the results of this backtest are hypothetical and may not reflect the true performance of a system, as past performance is not indicative of future results. Furthermore, the backtester does not to simulate every aspect of a strategy’s execution (such as transaction costs, market impact, price slippage, etc).
To get started on building the strategy, we begin by importing the necessary packages, each of which can quickly be installed using the “pip install” command from your command prompt. We will be using fxcmpy which is a Python wrapper, and pandas and numpy for analyzing our time series data and lastly datetime organize the date and time in an easy to read format.
import fxcmpy import pandas as pd import numpy as np import datetime as dt
We will then need to pull stochastic indicator using the pyti for technical indicators. We will also pull the two variables of the indicator at the same time. Next, we will import matplotlib for plotting out our results.
from pyti.stochastic import percent_k from pyti.stochastic import percent_d import matplotlib.pyplot as plt %matplotlib inline from matplotlib import style style.use('ggplot') pd.set_option('display.max_columns', None) pd.set_option('display.max_rows', None)
Now we will connect to FXCM’s REST API which will allow us to stream historical prices, live prices, and place live trades. We will use a config file to store the account access token for a secure connection to the API.
socket = fxcmpy.fxcmpy(config_file = 'fxcm.cfg')
We will go ahead and pull data for the GBP/USD by using this code:
data = socket.get_candles(instrument = 'GBP/USD', period = 'D1', start = dt.datetime(2017,1,1), end = dt.datetime(2018, 6, 10))
Logic: The stochastic indicator is composed of two main parts; the percent K line and the percent D line. Where %K = 100 x (Closing Price – Lowest Price of N Periods) / (Highest Price of N Periods – Lowest Price of N Periods) and %D = 3 – Period Moving Average of %K.
We will define the variables which will be based on the most recent 20 periods for our strategy first:
data['percent_k'] = percent_k(data['askclose'], 20) data['percent_d'] = percent_d(data['askclose'], 20)
Then we can plot the GBP/USD graph with the stochastics indicator below it:
fig = plt.figure(figsize=(12,8)) ax1 = fig.add_subplot(111, xlabel = 'Date',ylabel='Close') data['askclose'].plot(ax=ax1, color='r', lw=1) ss_plot = plt.figure(figsize=(50,8)) ax3 = ss_plot.add_subplot(111, ylabel='Percent') data['percent_k'].plot(ax=ax3, color='r') data['percent_d'].plot(ax=ax3, color='g') data['ovr'] = .80 data['ovr'].plot(ax=ax3, color = 'b', ) data['blw'] = .20 data['blw'].plot(ax=ax3, color = 'b',)
The logic of the strategy will be to enter a long position when Percent K cross over Percent D AND is greater than .80. We will create a signal when these two conditions are met and print the data frame indicator.
data['signal'] = np.where(np.logical_and(data['percent_k'] > data['percent_d'], data['percent_k']>.8),1,0) data['position'] = data['signal'].diff() data
As we can see from this dataframe, a “0” under the Signal column would indicate no signals were triggered and a “1” would indicate a signal. The next step is to take all of the rows with a “1” (which would indicate when a position was opened) and calculate the difference in pips and multiple this by the lot size. We will add a new column with this new information called “Total”.
pip_cost = 1 lot_size = 10 returns = 0 # Gets the number of pips that the market moved during the day data['difference (pips)'] = (data['askclose'] - data['askopen']) * 100 # Calculates the daily return while a position is active # 'Total' column records our running profit / loss for the strategy CountPL=False for i, row in data.iterrows(): if CountPL==True: returns += (row['difference (pips)'] * pip_cost * lot_size) data.loc[i,'total'] = returns else: data.loc[i,'total'] = returns if row['signal'] == 1: CountPL=True else: CountPL=False data
We can visualize this dataframe on a graph by using the matplotlib function. If you haven’t already, you would import that library. In the below code, we are creating the settings of the graph including the colors and indicating when a position was opened with a green star and when it was closed with a blue star.
import matplotlib.pyplot as plt %matplotlib inline fig = plt.figure(figsize=(12,8)) ax1 = fig.add_subplot(111, ylabel='Profits in $') data['total'].plot(ax=ax1, color='r', lw=2.) # Placing markers for our position entry ax1.plot(data.loc[data.position == 1.0].index, data.total[data.position == 1.0], '*', markersize=8, color='g') # Placing markers for our position exit ax1.plot(data.loc[data.position == -1.0].index, data.total[data.position == -1.0], '*', markersize=8, color='b') plt.show()
The profits of the stochastic strategy we are now plotted on the graph in USD. We triggered trades using the stochastic indicator on the GBP/USD. You can use this same method to easily backtest other strategies using various indicators. Don’t forget you can demo the REST API for free and it is simple to set up; just follow the three steps on this link. We can use this same method to backtest.
Want to access the full code? Check it out on our GitHub page.
Risk Warning: The FXCM Group does not guarantee accuracy and will not accept liability for any loss or damage which arise directly or indirectly from use of or reliance on information contained within the webinars. The FXCM Group may provide general commentary which is not intended as investment advice and must not be construed as such. FX/CFD trading carries a risk of losses in excess of your deposited funds and may not be suitable for all investors. Please ensure that you fully understand the risks involved.