Creating an algotrader/trading bot with Python – Part 3

As an Amazon Associate I earn from qualifying purchases.

5 Min Read

In the last post on creating an algotrader/trading bot with Python, I showed you how to specify your trading strategy for opening a trade. Now, let’s think about how we can close trades. There are many ways you can close trades – You could specify that after a certain amount of time, you should close the trade automatically. Alternatively, you could provide an exit condition and when met the trade will close. Or simply, define a stop loss & take profit and let the trade do its thing.

As a reminder, the basic flow of this algotrader/trading bot is as follows:

  1. Trader begins on a specific trading interval.
  2. Latest data is retrieved from the broker.
  3. Checks to see if any trades can be closed.
  4. Applies the strategy to the latest data and checks if a trade can be open.
  5. Repeat this process.

In this post, I will show you how to close a trade within a time limit and how to implement an exit condition.

Closing trades with an exit strategy

The next step on creating an algotrader/trading bot with Python is to define an exit strategy. Now that you know what trades are open, you can start defining when to close them. Our check_trades method currently looks like this:

def check_trades(time_frame, pair_data):
    for pair, data in pair_data.items():
        data['SMA'] = ta.SMA(data['close'], 10)
        data['EMA'] = ta.EMA(data['close'], 50)
        last_row = data.tail(1)
        for index, last in last_row.iterrows():
            if(last['close'] > last['EMA'] and last['close'] < last['SMA']):
                open_position(pair, "BUY", 1, 300, 100)

Let’s add some new code below last_row = data.tail(1). Start by getting the open positions and assigning it to a variable called open_positions passing in the pair:

def check_trades(time_frame, pair_data):
    for pair, data in pair_data.items():
        data['SMA'] = ta.SMA(data['close'], 10)
        data['EMA'] = ta.EMA(data['close'], 50)
        last_row = data.tail(1)
        open_positions = positions_get(pair)

Time to define our exit condition! Similarly to the open condition: BUY when price > 50EMA and price < 10SMA I will simply invert this strategy for closing the position. So, my strategy for closing will be: SELL when price < 50EMA and price > 10SMA. Let’s add that to our code. Under the current for loop add the following code:

        for index, last in last_row.iterrows():
            #Exit strategy
            if(last['close'] < last['EMA'] and last['close'] > last['SMA']):

Now that you have your exit condition, it is time to close any trades that match this condition and the current symbol. You can use the method close_symbol_by_pair created earlier.

        for index, last in last_row.iterrows():
            #Exit strategy
            if(last['close'] < last['EMA'] and last['close'] > last['SMA']):
                close_positon_by_symbol(pair)

That’s all! Now you have created an exit strategy for your trades and are closing them accordingly. In the next section, I will show you how to close a trade within a specified time limit.

Closing trades within specified time limit

So, what if your exit strategy is to close a trade regardless of price in a specified time limit? Using the time from the open_positions data frame and the current time you can calculate this. Let’s say that you set a stop loss and take profit for your trade and if neither or these have been hit after 2 hours, you want to close the trade. Let’s take a look at how you can do this.

Your check_trades function should look like this:

def check_trades(time_frame, pair_data):
    for pair, data in pair_data.items():
        data['SMA'] = ta.SMA(data['close'], 10)
        data['EMA'] = ta.EMA(data['close'], 50)
        last_row = data.tail(1)
        open_positions = positions_get()
        for index, last in last_row.iterrows():
            #Exit strategy
            if(last['close'] < last['EMA'] and last['close'] > last['SMA']):
                close_positon_by_symbol(pair)

            #Entry strategy
            if(last['close'] > last['EMA'] and last['close'] < last['SMA']):
                open_position(pair, "BUY", 1, 300, 100)

Start by getting the current local date time. As mentioned in part 1, every broker on the MT5 platform operates on their local timezone. So, to accurately calculate how much time has passed, you will need to convert your local current time to the brokers current time. In my case, my broker is in the Europe/Athens timezone. Again, you will be using pytz to convert to the brokers timezone. A full list of time zones for pytz.timezome can be found here. Add the calculated date time below the positions_get call:

def check_trades(time_frame, pair_data):
    for pair, data in pair_data.items():
        data['SMA'] = ta.SMA(data['close'], 10)
        data['EMA'] = ta.EMA(data['close'], 50)
        last_row = data.tail(1)
        open_positions = positions_get()
        current_dt = datetime.now().astimezone(pytz.timezone('Europe/Athens'))

Now, iterate over the open positions:

        open_positions = positions_get()
        current_dt = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
        for index, position in open_positions.iterrows():
            # Check to see if the trade has exceeded the time limit

From the open_positions data frame, extract time and ticket column while replacing the tzinfo for position['time']:

        open_positions = positions_get()
        current_dt = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
        for index, position in open_positions.iterrows():
            # Check to see if the trade has exceeded the time limit
            trade_open_dt = position['time'].replace(tzinfo = pytz.timezone('Europe/Athens'))
            deal_id = position['ticket']

As mentioned earlier, I want to close my trades after 2 hours. This can be achieved by subtracting the 2 dates and comparing them to a timedelta.

        open_positions = positions_get()
        current_dt = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
        for index, position in open_positions.iterrows():
            # Check to see if the trade has exceeded the time limit
            trade_open_dt = position['time'].replace(tzinfo = pytz.timezone('Europe/Athens'))
            deal_id = position['ticket']
            if(current_dt - trade_open_dt >= timedelta(hours = 2)):

If you want to specify minutes or even seconds for timedelta, you can do this as follows:

# Examples
timedelta(days=10)
timedelta(minutes=10)
timedelta(seconds=10)

If this condition is met, then you will want to close only that specific trade. Use the close_position method passing in the deal_id created in this post.

        last_row = data.tail(1)
        open_positions = positions_get()
        current_dt = datetime.now().astimezone(pytz.timezone('Europe/Athens'))
        for index, position in open_positions.iterrows():
            # Check to see if the trade has exceeded the time limit
            trade_open_dt = position['time'].replace(tzinfo = pytz.timezone('Europe/Athens'))
            deal_id = position['ticket']
            if(current_dt - trade_open_dt >= timedelta(hours = 2)):
                close_position(deal_id)

Now you can close your trade with a take profit or stop loss, a specific exit condition or by a time limit!

Testing the code

The easiest way to test this new functionality is to open a trade manually and wait for the exit condition to be hit or wait for the trade to exceed the specified time period. Here is the output from my test:

>>> connect(39672374)
Connected: Connecting to MT5 Client
>>> open_position("EURUSD", "BUY", 1, 800, 400)
EURUSD found!
Order successfully placed!
>>> live_trading()
Running trader at 2021-01-22 08:45:00.581690
Connected: Connecting to MT5 Client
Running trader at 2021-01-22 09:00:00.477947
Connected: Connecting to MT5 Client
Order successfully closed!
USDCAD is not visible, trying to switch on
USDCAD found!
Order successfully placed!

As you can see, I manually opened a trade at 8:45 am. It then exceeded the time limit of (15 minutes) and closed. I also had a USDCAD trade open during this time!

If you are interested in learning more about algo trading and trading systems, I highly recommend reading this book. I have taken some of my own trading ideas and strategies from this book. It also provided me a great insight into effective back testing. Check it out here.

That’s all for this post! In my next post, I will be talking about risk management and capital preservation. As always, if you have any questions or comments please feel free to post them below. Additionally, if you run into any issues please let me know.

2 thoughts on “Creating an algotrader/trading bot with Python – Part 3”

  1. Avatar

    I’ve noticed an issue with regards to the open_position:
    Keeps outputting:
    Failed to send order 🙁
    With result.retcode being 10019

    Instead of being == to mt5.TRADE_RETCODE_DONE (10009)

    1. Conor O'Hanlon

      In MT5 this error is called TRADE_RETCODE_NO_MONEY and has a code 10019. The reason for this error is the same for both versions of the platform — lack of free margin.

Leave a Comment

Your email address will not be published.

Subscribe to my newsletter to keep up to date with my latest posts

Holler Box