Close a trade with MT5 using Python

As an Amazon Associate I earn from qualifying purchases.

5 Min Read

In an earlier post, I showed you how to open a trade using the MT5 API. Now let’s talk about how to close a trade with the MT5 platform using python. In this tutorial, you be will using 2 methods from the MT5 API: positions_get and order_send.

Getting the current open positions

Let’s start by implementing the positions_get method from the MT5 API. If you followed along with the previous post you should have something like the code below:

import MetaTrader5 as mt5
from datetime import datetime

def connect(account):
    account = int(account)
    mt5.initialize()
    authorized=mt5.login(account)

    if authorized:
        print("Connected: Connecting to MT5 Client")
    else:
        print("Failed to connect at account #{}, error code: {}"
              .format(account, mt5.last_error()))

def open_position(pair, order_type, size, tp_distance=None, stop_distance=None):
    symbol_info = mt5.symbol_info(pair)
    if symbol_info is None:
        print(pair, "not found")
        return

    if not symbol_info.visible:
        print(pair, "is not visible, trying to switch on")
        if not mt5.symbol_select(pair, True):
            print("symbol_select({}}) failed, exit",pair)
            return
    print(pair, "found!")

    point = symbol_info.point
    
    if(order_type == "BUY"):
        order = mt5.ORDER_TYPE_BUY
        price = mt5.symbol_info_tick(pair).ask
        if(stop_distance):
            sl = price - (stop_distance * point)
        if(tp_distance):
            tp = price + (tp_distance * point)
            
    if(order_type == "SELL"):
        order = mt5.ORDER_TYPE_SELL
        price = mt5.symbol_info_tick(pair).bid
        if(stop_distance):
            sl = price + (stop_distance * point)
        if(tp_distance):
            tp = price - (tp_distance * point)

    request = {
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": pair,
        "volume": float(size),
        "type": order,
        "price": price,
        "sl": sl,
        "tp": tp,
        "magic": 234000,
        "comment": "",
        "type_time": mt5.ORDER_TIME_GTC,
        "type_filling": mt5.ORDER_FILLING_IOC,
    }

    result = mt5.order_send(request)

    if result.retcode != mt5.TRADE_RETCODE_DONE:
        print("Failed to send order :(")
    else:
        print ("Order successfully placed!")

Before we get started, you will need to import pandas. Install pandas by using the link here or by running the following command:

pip install pandas

At the top of the file, specify the import as follows:

import MetaTrader5 as mt5
from datetime import datetime
import pandas as pd

With this out of the way, the next thing you will want to do is create a wrapper method for the positions_get method. Start by defining the method signature and calling the method from the API. You will notice that I have declared an optional parameter called symbol. If left blank it will return all open positions. Otherwise, it will return open positions filtered by the current symbol. You can use the print method to temporarily validate this call is working as expected:

def positions_get(symbol=None):
    if(symbol is None):
        res = mt5.positions_get()
    else:
        res = mt5.positions_get(symbol=symbol)
    print(res)

To test that this works, let’s open a trade in MT5 and call the method:

>>> connect(39672374)
Connected: Connecting to MT5 Client
>>> open_position("EURUSD", "BUY", 1, 300, 100)
EURUSD found!
Order successfully placed!
>>> positions_get()
(TradePosition(ticket=830008618, time=1610638806, time_msc=1610638806090, time_update=1610638806, time_update_msc=1610638806090, type=0, magic=234000, identifier=830008618, reason=3, volume=1.0, price_open=1.21235, sl=1.21135, tp=1.21535, price_current=1.21228, swap=0.0, profit=-7.0, symbol='EURUSD', comment='', external_id=''),)

As you can see, we have our trade details but it is not very human readable. To fix this issue, you will want to convert this result to a DataFrame. Remove the print statement that was previously added and then add the following code:

def positions_get(symbol=None):
    if(symbol is None):
	    res = mt5.positions_get()
    else:
        res = mt5.positions_get(symbol=symbol)

    if(res is not None and res != ()):
        df = pd.DataFrame(list(res),columns=res[0]._asdict().keys())
        df['time'] = pd.to_datetime(df['time'], unit='s')
        return df
    
    return pd.DataFrame()

Let’s try running that again:

>>> connect(39672374)
Connected: Connecting to MT5 Client
>>> res = positions_get()
>>> print(res)
      ticket                time       time_msc  ...  symbol  comment  external_id
0  830008618 2021-01-14 15:40:06  1610638806090  ...  EURUSD 

Now you can see our positions print in table form. Let’s leave this for now and come back to it later.

MT5 API Implementation of Closing a trade

Close a trade with MT5 using Python is tricky due to the need of the deal id. Therefore, the above method is needed. In this example, I will show you how to close all trades for one symbol but this could easily be extended to include other filters such as a date/time.

You will need to create 2 methods to close a position – starting with the generic close_position method that takes a deal id. The first step in closing a position is to get the open position details. Using the method positions_get previously created, we can filter by deal_id and get the details about that specific trade:

def close_position(deal_id):
    open_positions = positions_get()
    open_positions = open_positions[open_positions['ticket'] == deal_id]

Once we have the required row, you should extract the following parameters: type, symbol and volume:

    order_type  = open_positions["type"][0]
    symbol = open_positions['symbol'][0]
    volume = open_positions['volume'][0]

After this, you will need to get the current bid or ask price depending on if you are buying or selling. You will need to invert the order_type from the open position. I.e. if the position was a buy you need to sell out of your position and vice versa:

    if(order_type == mt5.ORDER_TYPE_BUY):
        order_type = mt5.ORDER_TYPE_SELL
        price = mt5.symbol_info_tick(symbol).bid
    else:
        order_type = mt5.ORDER_TYPE_BUY
        price = mt5.symbol_info_tick(symbol).ask

Similarly to opening a trade with the MT5 API you need to create a request. The request is a dictionary with the following keys:

    • action
    • symbol
    • volume
    • type
    • price
    • magic
    • comment
    • type_time
    • type_filling

Below is an example of what a close request should look like:

    close_request={
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": symbol,
        "volume": float(volume),
        "type": order_type,
        "position": deal_id,
        "price": price,
        "magic": 234000,
        "comment": "Close trade",
        "type_time": mt5.ORDER_TIME_GTC,
        "type_filling": mt5.ORDER_FILLING_IOC,
    }

Now submit the request:

    result = mt5.order_send(close_request)

Finally, check if the close order was successful or not. This is done by checking the response retcode:

    if result.retcode != mt5.TRADE_RETCODE_DONE:
        print("Failed to close order :(")
    else:
        print ("Order successfully closed!")

Next, create a method called close_positions_by_symbol which will allow you to specify a symbol and close all trades that refer to that symbol. Again, you will use the positions_get method but this time, pass in the symbol:

def close_positions_by_symbol(symbol):
    open_positions = positions_get(symbol)

It is important to note that this can return multiple trades with the same symbol. You will want to extract the ticket id and pass it into the close_position method for each trade. This is done by using a method called apply, alongside a lambda function:

def close_positions_by_symbol(symbol):
    open_positions = positions_get(symbol)
    open_positions['ticket'].apply(lambda x: close_position(x))

Testing the python code

If you have been following this post, you should have code similar to what is below:

import MetaTrader5 as mt5
from datetime import datetime
import pandas as pd

def connect(account):
    account = int(account)
    mt5.initialize()
    authorized=mt5.login(account)

    if authorized:
        print("Connected: Connecting to MT5 Client")
    else:
        print("Failed to connect at account #{}, error code: {}"
              .format(account, mt5.last_error()))

def open_position(pair, order_type, size, tp_distance=None, stop_distance=None):
    symbol_info = mt5.symbol_info(pair)
    if symbol_info is None:
        print(pair, "not found")
        return

    if not symbol_info.visible:
        print(pair, "is not visible, trying to switch on")
        if not mt5.symbol_select(pair, True):
            print("symbol_select({}}) failed, exit",pair)
            return
    print(pair, "found!")

    point = symbol_info.point
    
    if(order_type == "BUY"):
        order = mt5.ORDER_TYPE_BUY
        price = mt5.symbol_info_tick(pair).ask
        if(stop_distance):
            sl = price - (stop_distance * point)
        if(tp_distance):
            tp = price + (tp_distance * point)
            
    if(order_type == "SELL"):
        order = mt5.ORDER_TYPE_SELL
        price = mt5.symbol_info_tick(pair).bid
        if(stop_distance):
            sl = price + (stop_distance * point)
        if(tp_distance):
            tp = price - (tp_distance * point)

    request = {
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": pair,
        "volume": float(size),
        "type": order,
        "price": price,
        "sl": sl,
        "tp": tp,
        "magic": 234000,
        "comment": "",
        "type_time": mt5.ORDER_TIME_GTC,
        "type_filling": mt5.ORDER_FILLING_IOC,
    }

    result = mt5.order_send(request)

    if result.retcode != mt5.TRADE_RETCODE_DONE:
        print("Failed to send order :(")
    else:
        print ("Order successfully placed!")

def positions_get(symbol=None):
    if(symbol is None):
	    res = mt5.positions_get()
    else:
        res = mt5.positions_get(symbol=symbol)

    if(res is not None and res != ()):
        df = pd.DataFrame(list(res),columns=res[0]._asdict().keys())
        df['time'] = pd.to_datetime(df['time'], unit='s')
        return df
    
    return pd.DataFrame()

def close_position(deal_id):
    open_positions = positions_get()
    open_positions = open_positions[open_positions['ticket'] == deal_id]
    order_type  = open_positions["type"][0]
    symbol = open_positions['symbol'][0]
    volume = open_positions['volume'][0]

    if(order_type == mt5.ORDER_TYPE_BUY):
        order_type = mt5.ORDER_TYPE_SELL
        price = mt5.symbol_info_tick(symbol).bid
    else:
        order_type = mt5.ORDER_TYPE_BUY
        price = mt5.symbol_info_tick(symbol).ask
	
    close_request={
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": symbol,
        "volume": float(volume),
        "type": order_type,
        "position": deal_id,
        "price": price,
        "magic": 234000,
        "comment": "Close trade",
        "type_time": mt5.ORDER_TIME_GTC,
        "type_filling": mt5.ORDER_FILLING_IOC,
    }

    result = mt5.order_send(close_request)
    
    if result.retcode != mt5.TRADE_RETCODE_DONE:
        print("Failed to close order :(")
    else:
        print ("Order successfully closed!")

def close_positons_by_symbol(symbol):
    open_positions = positions_get(symbol)
    open_positions['ticket'].apply(lambda x: close_position(x))

Now, let’s try executing our code. For this test I will connect to MT5, opening 2 trades on EURUSD and close both:

>>> connect(39672374)
Connected: Connecting to MT5 Client
>>> open_position("EURUSD", "BUY", 1, 300, 100)
EURUSD found!
Order successfully placed!
>>> open_position("EURUSD", "BUY", 5, 200, 50)
EURUSD found!
Order successfully placed!
>>> close_positions_by_symbol("EURUSD")
Order successfully closed!
Order successfully closed!

That’s all for this post! 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.

13 thoughts on “Close a trade with MT5 using Python”

  1. Avatar

    Change result = mt5.order_send(request) to result = mt5.order_send(close_request) for the close_positions method when you first mentioned it.

    1. Avatar

      Maybe you do this later, but I think you could add more checking to avoid error like you do in open_position. If you market is closed this will not work, but this should not break the program.

      In close_position_by_symbol:
      if open_positions.empty:

      In positions_get:
      try:
      res = mt5.positions_get(symbol=symbol)
      except:
      print(f”failed to find {symbol}”)

  2. Avatar

    Hello, I’ve followed along fairly well so far until now. When running the commands listed at the bottom to close the position I get

    >>> close_positions_by_symbol(“EURUSD”)
    Traceback (most recent call last):
    File “”, line 1, in
    NameError: name ‘close_positions_by_symbol’ is not defined

    Here is a copy of my code
    https://pastebin.com/HgV4LMdD
    With accountnumber substituted for my actual number. Any ideas? Thank you

  3. Avatar

    Hi Conor, I’m having some difficulty with this issue I’m facing Can you assist me with this please? Thanks, in advance.

    Connected: Connecting to MT5 Client
    USDCHF found!
    Order successfully placed!
    Traceback (most recent call last):
    File “C:\Python\Python39\lib\site-packages\pandas\core\indexes\base.py”, line 3080, in get_loc
    return self._engine.get_loc(casted_key)
    File “pandas\_libs\index.pyx”, line 70, in pandas._libs.index.IndexEngine.get_loc
    File “pandas\_libs\index.pyx”, line 101, in pandas._libs.index.IndexEngine.get_loc
    File “pandas\_libs\hashtable_class_helper.pxi”, line 1625, in pandas._libs.hashtable.Int64HashTable.get_item
    File “pandas\_libs\hashtable_class_helper.pxi”, line 1632, in pandas._libs.hashtable.Int64HashTable.get_item
    KeyError: 0

    The above exception was the direct cause of the following exception:

    Traceback (most recent call last):
    File “C:\Python\Python39\Scripts\CloseTrade.py”, line 121, in
    close_positions_by_symbol(“USDCHF”)
    File “C:\Python\Python39\Scripts\CloseTrade.py”, line 117, in close_positions_by_symbol
    open_positions[‘ticket’].apply(lambda x: close_position(x))
    File “C:\Python\Python39\lib\site-packages\pandas\core\series.py”, line 4135, in apply
    mapped = lib.map_infer(values, f, convert=convert_dtype)
    File “pandas\_libs\lib.pyx”, line 2467, in pandas._libs.lib.map_infer
    File “C:\Python\Python39\Scripts\CloseTrade.py”, line 117, in
    open_positions[‘ticket’].apply(lambda x: close_position(x))
    File “C:\Python\Python39\Scripts\CloseTrade.py”, line 84, in close_position
    order_type = open_positions[“type”][0]
    File “C:\Python\Python39\lib\site-packages\pandas\core\series.py”, line 851, in __getitem__
    return self._get_value(key)
    File “C:\Python\Python39\lib\site-packages\pandas\core\series.py”, line 959, in _get_value
    loc = self.index.get_loc(label)
    File “C:\Python\Python39\lib\site-packages\pandas\core\indexes\base.py”, line 3082, in get_loc
    raise KeyError(key) from err
    KeyError: 0

      1. Avatar

        Strange, but it runs the open trade method first and then proceeds to try to close the trade but fails. I know for sure it’s opening the trade as I can even see it in MT5 itself.

        1. Avatar
          Dan Nykjær Jakobsen

          I had the same issues I fix it by changing the lines 84 – 86. I changed the last index to .iloc[0] instead of the [0]

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