mt5.order_send() in MetaTrader5 Python: place, change or close a trade

Sends a trade request to the broker: open a position, place a pending order, change stops, or close. Returns a result with a retcode, or None.

Signature

mt5.order_send(request)

What it does

order_send() takes a request dict. action says what to do: mt5.TRADE_ACTION_DEAL for a market order, mt5.TRADE_ACTION_PENDING for a pending order, mt5.TRADE_ACTION_SLTP to change a position's stops, mt5.TRADE_ACTION_MODIFY to change a pending order, and mt5.TRADE_ACTION_REMOVE to delete one.

To close a position, send a deal in the opposite direction with position set to the position's ticket.

Parameters

ParameterWhat it is
requestA dict with action and the fields it needs: symbol, volume, type, price, sl, tp, deviation, type_filling, type_time, expiration, magic, comment, order or position.

What it returns

  • A result with retcode, deal, order, volume, price, comment and the request you sent. Success codes are 10009 (done), 10008 (pending order placed) and 10010 (partly filled).
  • None when the request never got a reply. Check mt5.last_error(), and treat the outcome as unknown.

Example

import MetaTrader5 as mt5

if not mt5.initialize():
    raise SystemExit(mt5.last_error())

mt5.symbol_select("EURUSD", True)
tick = mt5.symbol_info_tick("EURUSD")
req = {
    "action": mt5.TRADE_ACTION_DEAL,
    "symbol": "EURUSD",
    "volume": 0.10,
    "type": mt5.ORDER_TYPE_BUY,
    "price": tick.ask,
    "sl": round(tick.ask - 0.0030, 5),
    "deviation": 10,
    "magic": 2410,
    "comment": "sig-7781",            # lets you find it again
    "type_time": mt5.ORDER_TIME_GTC,
    "type_filling": mt5.ORDER_FILLING_IOC,
}
res = mt5.order_send(req)
if res is None or res.retcode in (10031, 10012):
    # No clear answer: the order may have reached the broker.
    # Look for comment "sig-7781" in positions and deals before sending again.
    print("unknown outcome:", None if res is None else res.retcode, mt5.last_error())
elif res.retcode not in (10008, 10009, 10010):
    print("rejected:", res.retcode, res.comment)
else:
    print("filled at", res.price, "deal", res.deal)
mt5.shutdown()

Common mistakes

  • 10031 (no connection) or a None result does not mean the order failed. The request may have reached the broker before the link dropped. Look in positions_get() and history_deals_get() before you send again, or you can open the trade twice.
  • The filling mode must be one the symbol allows, or the send fails with 10030. Brokers differ, so read symbol_info().filling_mode rather than hard-coding one.
  • Select the symbol in Market Watch and use a fresh price. A stale price brings 10004 requote or 10021 off quotes.
  • Algo trading must be on in the terminal, or you get 10027.
  • expiration is a broker server time, not UTC.

The same with Tickwire on Linux

The package needs a Windows terminal on the same machine. Tickwire runs the terminal for you and gives you a REST API, so the code below runs on Linux, macOS or a server.

Tickwire's trade route takes one JSON body for MT4 and MT5, with actionType such as ORDER_TYPE_BUY, POSITION_MODIFY or POSITION_CLOSE_ID. It picks a filling mode the symbol allows, or takes your own order of preference in fillingModes.

Send an Idempotency-Key with every order. The reply always has numericCode, stringCode and an outcome of applied, no_change, partial, rejected or unknown. The HTTP status is 200 even when the broker refuses.

If the reply's outcome is unknown, the order may have reached the broker. Do not send it again with a new key. Read positions, orders and deals first. Sending the same Idempotency-Key again never places a second order; it returns the first answer. See When the answer is "unknown".

import os, requests

API = "https://api.tickwire.io"
h = {"auth-token": os.environ["TW_KEY"]}      # your Tickwire key
base = f"{API}/users/current/accounts/{os.environ['TW_ACCOUNT']}"

import uuid

key = str(uuid.uuid4())   # make it once per trading decision; reuse it on retry
r = requests.post(f"{base}/trade", json={
    "actionType": "ORDER_TYPE_BUY", "symbol": "EURUSD", "volume": 0.10,
    "clientId": "sig-7781",
}, headers={**h, "Idempotency-Key": key,
            "X-Actor": "my-script", "X-Reason": "signal 7781"})
res = r.json()
print(res["outcome"], res["stringCode"])
if res["outcome"] == "unknown":
    print("check positions and deals for clientId sig-7781 before acting")

Related error codes

All MetaTrader5 Python functions

MT5 from Python, on any machine

One REST API for MT4 and MT5, with idempotent orders and an audit log. Read the MT5 Python API page or the quickstart.