Algo Trading with REST API and Python – Developing a SMA Crossover Trend Strategy

In this article, we will code a closed-bar Simple Moving Average (SMA) crossover strategy using Python and FXCM’s Rest API. This strategy will buy when the Fast SMA crosses over the Slow SMA, and sell when the Fast SMA crosses under the Slow SMA. Parameters will include symbol/instrument, timeframe, Fast & Slow SMA Periods, close on opposing signal switch, lot size, and stop/limit distance.

If you already have a copy of the “Python Strategy Template.py” you can go to Step 2. In a previous article, we developed a strategy template that makes future strategy development much easier to accomplish. We recommend readers go through this article at least once to understand how it works.

Step 2. Import SMA logic from pyti.

In our template, we already import the fxcmpy, time, and datetime modules. For this strategy, we also need to import the Simple Moving Average logic using a module called pyti. If you have never used pyti before, you will want to make sure this module is installed on your machine by opening up a command prompt and running the command “pip install pyti”. The screenshot below shows I already have pyti installed.

We now need to import the SMA logic into our code. We will add this just below our other import statements.

``````import fxcmpy
import time
import datetime as dt
from pyti.simple_moving_average import simple_moving_average as sma``````

We need to add 6 additional parameters to give our SMA crossover strategy all the inputs it needs. For our SMA logic, we will need Fast and Slow SMA Periods as well as a switch named “close_on_opposing_signal” that will allow to choose how we want the strategy to handle trades when an opposing signal occurs. We also want to add parameters for trade size, stoploss distance and limit distance.

``````###### USER PARAMETERS ######
token = 'INSERT-TOKEN-HERE'
symbol = 'GBP/USD'
timeframe = "m1" # (m1,m5,m15,m30,H1,H2,H3,H4,H6,H8,D1,W1,M1)
fast_sma_periods = 10
slow_sma_periods = 20
close_on_opposing_signal = True
amount = 1
stop = -10
limit = 30
#############################``````

Before we write our trading logic inside the Update() function, there are a few ‘utility’ functions we need to add to our code that will make writing our strategy’s logic much simpler.

The enter() Function

First, let’s add the enter() function. When called, the enter() function places a market order. To place a Buy market order, use enter(“B”). For a Sell market order, use enter(“S”). The function is already coded to accept the amount, stop & limit parameters we created in our User Parameters section.

``````# This function places a market order in the direction BuySell, "B" = Buy, "S" = Sell, uses symbol, amount, stop, limit
direction = True;
direction = False;
try:
except:
else:

The exit() Function

Next, let’s add the exit() function. It works very similarly to the enter function. To close out all buy trades for our traded symbol, call exit(“B”). To close out all sell trades for our traded symbol, call exit(“S”). If we want to close out both buy and sell trades, we just call exit() with no arguments.

``````# This function closes all positions that are in the direction BuySell, "B" = Close All Buy Positions, "S" = Close All Sell Positions, uses symbol
openpositions = con.get_open_positions(kind='list')
for position in openpositions:
if position['currency'] == symbol:
try:
except:
else:

The crossesOver() Function

The next function is a workhorse for many different strategies. crossesOver() checks to see if one data stream’s value crossed over another data stream’s value in the previous candle/bar. When we execute the crossesOver() function with Fast SMA’s data stream as stream1 and the Slow SMA’s data stream as stream2, the function will return a True value if the Fast SMA crossed over the Slow SMA in the prevous candle. It will return false if it did not crossover.

``````# Returns true if stream1 crossed over stream2 in most recent candle, stream2 can be integer/float or data array
def crossesOver(stream1, stream2):
# If stream2 is an int or float, check if stream1 has crossed over that fixed number
if isinstance(stream2, int) or isinstance(stream2, float):
if stream1[len(stream1)-1] <= stream2:
return False
else:
if stream1[len(stream1)-2] > stream2:
return False
elif stream1[len(stream1)-2] < stream2:
return True
else:
x = 2
while stream1[len(stream1)-x] == stream2:
x = x + 1
if stream1[len(stream1)-x] < stream2:
return True
else:
return False
# Check if stream1 has crossed over stream2
else:
if stream1[len(stream1)-1] <= stream2[len(stream2)-1]:
return False
else:
if stream1[len(stream1)-2] > stream2[len(stream2)-2]:
return False
elif stream1[len(stream1)-2] < stream2[len(stream2)-2]:
return True
else:
x = 2
while stream1[len(stream1)-x] == stream2[len(stream2)-x]:
x = x + 1
if stream1[len(stream1)-x] < stream2[len(stream2)-x]:
return True
else:
return False``````

The crossesUnder() Function

This function is the mirror opposite of crossesOver(). It returns a True value if the first data stream crossed under the second; returns false if it did not crossunder.

``````# Returns true if stream1 crossed under stream2 in most recent candle, stream2 can be integer/float or data array
def crossesUnder(stream1, stream2):
# If stream2 is an int or float, check if stream1 has crossed under that fixed number
if isinstance(stream2, int) or isinstance(stream2, float):
if stream1[len(stream1)-1] >= stream2:
return False
else:
if stream1[len(stream1)-2] < stream2:
return False
elif stream1[len(stream1)-2] > stream2:
return True
else:
x = 2
while stream1[len(stream1)-x] == stream2:
x = x + 1
if stream1[len(stream1)-x] > stream2:
return True
else:
return False
# Check if stream1 has crossed under stream2
else:
if stream1[len(stream1)-1] >= stream2[len(stream2)-1]:
return False
else:
if stream1[len(stream1)-2] < stream2[len(stream2)-2]:
return False
elif stream1[len(stream1)-2] > stream2[len(stream2)-2]:
return True
else:
x = 2
while stream1[len(stream1)-x] == stream2[len(stream2)-x]:
x = x + 1
if stream1[len(stream1)-x] > stream2[len(stream2)-x]:
return True
else:
return False``````

The final utility function we will add is the counOpenTrades() function. As the name suggests, this function counts how many open positions we have in our account for the strategy’s symbol. Calling countOpenTrades(“B”) returns the number of open buy trades/tickets, countOpenTrades(“S”) returns the number of open sell trades/tickets, countOpenTrades() returns the number of all open trades/tickets.

``````# Returns number of Open Positions for symbol in the direction BuySell, returns total number of both Buy and Sell positions if no direction is specified
openpositions = con.get_open_positions(kind='list')
counter = 0
for position in openpositions:
if position['currency'] == symbol:
counter+=1
return counter``````

Step 5. Writing our Trading Logic Inside the Update() Function

The Update() function is processed every time a candle/bar closes and will determine when we open a trade as well as when they are closed. The first thing we want to do is calculate our Fast SMA and Slow SMA streams. We can easily do this by calling our sma calculation module (that we imported via pyti). We will also print the most recent close price and SMA values so we can visually confirm that our strategy is updating properly.

``````# This function is run every time a candle closes
def Update():
print(str(dt.datetime.now()) + "	 " + timeframe + " Bar Closed - Running Update Function...")

# Calculate Indicators
iFastSMA = sma(pricedata['bidclose'], fast_sma_periods)
iSlowSMA = sma(pricedata['bidclose'], slow_sma_periods)

# Print Price/Indicators
print("Close Price: " + str(pricedata['bidclose'][len(pricedata)-1]))
print("Fast SMA: " + str(iFastSMA[len(iFastSMA)-1]))
print("Slow SMA: " + str(iSlowSMA[len(iSlowSMA)-1]))``````

Next, we need to code our entry logic. If the Fast SMA crosses over the Slow SMA, we buy. If the Fast SMA crosses under the Slow SMA, we sell. We will use our crossesOver(), crossesUnder() and enter() functions. Also, note that we want to exit opposing positions if close_on_opposing_signal equals True and an opposing position exists. We use the countOpenTrades() function to see if we have any opposing positions and the exit() function to close them out if we do.

``````	# TRADING LOGIC
if crossesOver(iFastSMA,iSlowSMA):
if close_on_opposing_signal and countOpenTrades("S") > 0:
exit("S")
enter("B")
# If Fast SMA crosses under Slow SMA, Open Sell Trade
if crossesUnder(iFastSMA,iSlowSMA):
print("	  SELL SIGNAL!")
if close_on_opposing_signal and countOpenTrades("B") > 0:
exit("B")
enter("S")

print(str(dt.datetime.now()) + "	 " + timeframe + " Update Function Completed.\n")``````

Step 6. Run our strategy inside our command console.

The last step is to run our strategy inside our command console. The python file we created I saved on to my desktop, so I execute the strategy by calling it like this:

The strategy is now up and running and will open and close trades per our rules!

What Next?

This SMA Crossover strategy is a classic trend trading strategy, but there are definitely ways it can be expanded upon and improved. Please edit and make this strategy your own! Also, make sure to check out our other Python strategies we have available at https://github.com/fxcm/RestAPI

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.