As an Amazon Associate I earn from qualifying purchases.
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.
Change result = mt5.order_send(request) to result = mt5.order_send(close_request) for the close_positions method when you first mentioned it.
Thanks for noticing this. I have fixed it.
In the write up “request” should be “close_request”. In the full code it is correct.
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}”)
Yeah this kind of stuff is coming later in the series
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
It looks like you have not implemented the `close_positions_by_symbol` method in this tutorial 🙂
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
Hard to say.. It looks like when close trade is called a trade has not been open yet.
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.
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]
I’m getting same, Shawn. No idea as to why.
Do you guys see the trade opened in MT5 before close is called?