MT5 history_deals_get in Python: from deals to positions
Deals are executions; traders think in positions. Here is how to convert one into the other with the Python package, and the traps that give wrong numbers.
What this is for
The official MetaTrader5 Python package lets a script read your account history from a running terminal. The function history_deals_get returns deals, which are individual executions. If you want one row per closed trade, you have to turn deals into positions. This page shows the approach AI Trading Journal itself uses, and the traps along the way.
The basics
Run the MetaTrader 5 terminal on the same Windows PC and log in. In Python: initialise the connection, request deals for a date range, and shut down when finished. Dates can be Python datetime objects.
import MetaTrader5 as mt5
from datetime import datetime
if not mt5.initialize():
raise SystemExit(f"initialize failed: {mt5.last_error()}")
deals = mt5.history_deals_get(datetime(2026, 1, 1), datetime.now()) or ()
for d in deals[:5]:
print(d.ticket, d.position_id, d.symbol, d.type, d.entry, d.volume, d.price, d.profit, d.commission, d.swap)
mt5.shutdown()
Gotchas that cause wrong numbers
- Profit excludes commission and swap. A deal's
profitfield is separate fromcommissionandswap. For the true result of a trade add all three. - Balance rows are deals too. Deposits, withdrawals and credits come back as deals with other types. Keep only
DEAL_TYPE_BUYandDEAL_TYPE_SELL. - Entries and exits are separate deals. Check
entry:DEAL_ENTRY_INopens,DEAL_ENTRY_OUT(and out-by or in/out) closes. A partly closed position has several exit deals. - Group by position_id. Every deal carries the ID of the position it belongs to. That is what turns executions into trades.
- Stop loss and take profit are not on the deal. MT5 tracks them on the order. Use
history_orders_get(position=position_id)and take the last non-zero stop and target if they moved. - Fetch a position's deals by position.
history_deals_get(position=position_id)returns all deals for it regardless of the date range, which also handles a trade opened on an earlier day than it closed. - Time zones. MT5 time stamps follow the broker's server clock. Convert them deliberately and check the result against your terminal before you trust it.
From deals to one row per position
CLOSING = (mt5.DEAL_ENTRY_OUT, mt5.DEAL_ENTRY_OUT_BY, mt5.DEAL_ENTRY_INOUT)
trades, seen = [], set()
for d in deals:
if d.type not in (mt5.DEAL_TYPE_BUY, mt5.DEAL_TYPE_SELL) or d.entry not in CLOSING:
continue
if d.position_id in seen:
continue
seen.add(d.position_id)
pos = mt5.history_deals_get(position=d.position_id) or ()
entries = [x for x in pos if x.entry == mt5.DEAL_ENTRY_IN]
exits = sorted((x for x in pos if x.entry in CLOSING), key=lambda x: x.time)
if not entries or not exits:
continue
trades.append({
"position": d.position_id,
"symbol": entries[0].symbol,
"volume": sum(x.volume for x in entries),
"open": entries[0].time,
"close": exits[-1].time,
"profit": sum(x.profit + x.commission + x.swap for x in exits),
})
This is a simplified version of the logic, written for explanation. Check field names against the version of the package you have installed, and test on a demo account.
Then what?
Once you have positions, calculating statistics is straightforward; see MT5 trading statistics, and note how hedging and netting change the picture in MT5 hedging vs netting. If you would rather not write and maintain this, AI Trading Journal does it for you and saves each position in your own journal on your PC (with an optional copy in your own Notion workspace).
Prefer not to dig through the History tab? AI Trading Journal reads your closed trades from your own MT5 terminal and saves one entry in your own journal on your PC (and one Notion row, if you connect Notion) per closed position about every 5 minutes, with open and close time, size, prices, stop, target and profit. It is read-only, needs no broker login, and is Windows-only, with a 7-day free trial and a one-time $19.99.
This page is technical education, not trading or investment advice. The MetaTrader5 package is provided by MetaQuotes; we are not affiliated with MetaQuotes.
Include commission on entry deals
The simplified example above sums profit, commission and swap on the exit deals only. Some brokers charge commission on the entry deal as well as, or instead of, the exit. To get the full cost of a position, add up the commission and swap of every deal that belongs to it, not only the exits:
total = sum(x.profit + x.commission + x.swap + getattr(x, "fee", 0.0) for x in pos)
Here pos is the list of all deals returned for the position. The fee field exists in the package for some accounts, so the example reads it defensively. Compare the totals with the Profit, Commission and Swap columns in your terminal for a few trades before you trust the script.
Getting stop loss and take profit
Deals do not carry the stop and target. Orders do. This snippet takes the most recent non-zero stop and target set on the orders of a position:
orders = mt5.history_orders_get(position=position_id) or ()
orders = sorted(orders, key=lambda o: o.time_setup, reverse=True)
sl = next((o.sl for o in orders if o.sl), 0.0)
tp = next((o.tp for o in orders if o.tp), 0.0)
If you moved the stop, this returns the last one set, which may not be the one that was active when the trade closed. For a planned-versus-actual comparison you would also keep the first non-zero stop. Test the result against the trade's detail in the terminal.
Converting server time
The time on a deal is a number of seconds that reflects the broker's server clock. To work in your own time zone, treat the number as UTC to get a datetime, then subtract the server's offset from UTC, which you can read from your broker's documentation or work out from a known trade:
from datetime import datetime, timezone, timedelta
SERVER_OFFSET_HOURS = 3 # example only: check your broker
t = datetime.fromtimestamp(d.time, tz=timezone.utc) - timedelta(hours=SERVER_OFFSET_HOURS)
Some brokers shift their offset for daylight saving, so a fixed number can be wrong for part of the year. If you group trades by session, check a handful against the terminal around the dates when your broker's clocks change.
Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
| initialize() returns False | The terminal is not running, not logged in, or the package cannot find it | Start MT5 and log in; print mt5.last_error() |
| history_deals_get returns None or an empty tuple | No deals in the range, a wrong date range, or an error | Widen the dates; print mt5.last_error() |
| Positions with no entry deal | The position opened before the date range | Fetch by position ID so the range does not matter |
| Totals differ from the terminal | Commission or swap missing, or balance deals included | Sum all deals of each position; filter balance types |
| Times look hours off | Server time not converted | Compare with the History tab for one trade |
Test it against the terminal
Before you rely on any script, pick five closed trades and compare symbol, volume, open and close time, prices and net result with the History tab (Positions view). Include one trade with a partial close and one with commission. A script that matches the terminal on those is probably right; one that does not will give you statistics you cannot trust. For what to calculate once the positions are right, see MT5 trading statistics.
To understand the identifiers you group by, read MT5 deal, order and position IDs and the magic number for splitting EA and manual trades.
Frequently asked questions
How do I get MT5 trade history in Python?
Does the MT5 deal profit include commission and swap?
How do I get stop loss and take profit in Python?
What is the difference between a deal and a position in MT5?
Related guides
- A local trading journal for MT5
- P&L calendar for MT5
- Trade notes, ratings and screenshots
- Trading progress and goals
- Back up your journal and move it to a new PC
- An offline trading journal for Windows
- How to set monthly trading goals
- Import your whole MT5 history
- How to tag trades
- Trade screenshots for your journal
- All trading journal comparisons
Skip the code: journal MT5 positions automatically
Free for 7 days, no card required. After that, a one-time $19.99 purchase. No subscription. Also on the Microsoft Store.
One-time $19.99, no monthly fee · your MT5 login never leaves your PC · trades go into your own journal on your PC, not through our servers.
Start free 7-day trial