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
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()

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
for i, row in data.iterrows():
  if CountPL==True:
    returns += (row['difference (pips)'] * pip_cost * lot_size)
    data.loc[i,'total'] = returns
    data.loc[i,'total'] = returns
  if row['signal'] == 1:


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.position == 1.0],
    '*', markersize=8, color='g')

# Placing markers for our position exit

ax1.plot(data.loc[data.position == -1.0].index,[data.position == -1.0],
    '*', markersize=8, color='b')

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.