Python 3 and a terminal. No MetaTrader, no broker, no account and no money.
macOS and Linux already include Python; on Windows install it from python.org with
“Add python.exe to PATH” ticked, then check with
python3 --version.
This page deliberately does not walk you around the interface. Menus move between versions, the platform's own tooltips describe them better than any article, and none of it is what makes MetaTrader confusing. What is confusing is the model underneath — and that you can build and run.
Nothing here is trading advice, and no figure describes a real account. If you want to follow along in the platform as well, install MetaTrader 5 and open a DEMO account; the last step suggests what to look at.
What is MetaTrader 5?
MetaTrader 5 (MT5) is the most widely used multi-asset trading platform in the world. Developed by MetaQuotes Software, it supports trading in forex, stocks, futures, commodities, and cryptocurrencies. MT5 is the successor to MetaTrader 4, offering a more powerful architecture, more timeframes, more order types, and a significantly improved programming language (MQL5).
Whether you are a manual trader using charts and indicators or a developer building automated trading systems, MT5 provides the complete environment you need. Brokers worldwide offer MT5 as their primary platform, giving you access to real-time market data, order execution, and a rich ecosystem of custom tools.
While MT4 remains popular for forex, MT5 is the future. It supports more asset classes, has a faster backtesting engine, and MQL5 is a far more capable programming language than MQL4. New brokers increasingly offer MT5 only.
Key Features of MT5
- Multi-asset support — Trade forex, stocks, futures, options, and crypto from one platform
- 21 timeframes — From 1-minute to monthly charts, plus tick charts
- 38 built-in indicators — Moving averages, RSI, MACD, Bollinger Bands, and more
- 6 order types — Market, limit, stop, stop-limit, trailing stop, and fill-or-kill
- Strategy Tester — Multi-threaded backtesting engine with optimization support
- MetaEditor IDE — Built-in development environment for MQL5 programming
- MQL5 Market — Marketplace for buying and selling indicators and Expert Advisors
- Algorithmic trading — Full support for automated trading via Expert Advisors
- Economic calendar — Built-in news and event tracking
How to Download and Install MT5
MT5 is provided through brokers. Sign up with a broker that offers MT5 (most major brokers do). You can also use a demo account to practice without risking real money.
Your broker will provide a download link, or you can download MT5 directly from the MetaQuotes website. MT5 is available for Windows, macOS, Linux (via Wine), iOS, and Android.
Run the installer, then enter your broker account credentials (server, login number, and password). For demo accounts, you can create one directly from the platform.
The MT5 Interface Overview
When you first open MT5, you will see several key areas:
- Chart Window — The main area displaying price charts. You can open multiple charts in tabs or tile them.
- Market Watch — Left panel showing real-time bid/ask prices for all available instruments.
- Navigator — Left panel (below Market Watch) listing your accounts, indicators, Expert Advisors, and scripts.
- Toolbox — Bottom panel showing your open trades, account history, alerts, and the journal log.
- Toolbar — Top bar with quick-access buttons for timeframes, chart types, drawing tools, and indicators.
Demo vs Live Accounts
MT5 supports two types of accounts:
Demo accounts use virtual money and are perfect for learning the platform, testing indicators, and developing strategies without any financial risk. Always start with a demo account when learning.
Live accounts trade with real money through your broker. Only move to a live account once you are consistently profitable in demo and fully understand the risks involved.
The majority of retail traders lose money. Never trade with funds you cannot afford to lose. Use demo accounts extensively before considering live trading.
What You Can Build with MT5
MT5 is not just a trading platform — it is a development platform. Using the MQL5 programming language and the built-in MetaEditor IDE, you can create:
- Custom Indicators — Visualize market data in unique ways that built-in indicators cannot
- Expert Advisors (EAs) — Fully automated trading systems that execute trades based on your strategy
- Scripts — One-time utilities that perform a task and exit (e.g., close all positions)
- Libraries — Reusable code modules shared across multiple programs
This tutorial series will teach you all of these, starting from the basics and building up to professional-level indicator and EA development.
Understand What MetaTrader Is Doing Underneath, in Three Steps
Learning where the buttons are takes an afternoon and the platform's own tooltips will do it better than any article. What takes longer, and what nothing on screen explains, is the model underneath: why a trade you placed shows as three different things, why the same code behaves differently on two accounts, and why the broker can close your positions while the big number at the top of the screen still looks healthy. In the next half hour you will build all three models and watch them behave. Every line of output below came from running these files.
Go: open a terminal in a folder you can write to — cd ~/Desktop on macOS or Linux, cd %USERPROFILE%\Desktop on Windows.
Do: save this as odp.py and run python3 odp.py.
"""MT5's three-way model. Getting this wrong confuses everything after it."""
# An ORDER is a request. A DEAL is an execution. A POSITION is what you hold.
orders, deals, positions = [], [], {}
def send_order(oid, symbol, side, volume, price):
orders.append((oid, symbol, side, volume, price, "placed"))
return oid
def execute(oid, deal_id, fill_price):
for i, (o, sym, side, vol, price, state) in enumerate(orders):
if o != oid:
continue
orders[i] = (o, sym, side, vol, price, "filled")
deals.append((deal_id, oid, sym, side, vol, fill_price))
# Netting: one position per symbol, adjusted by each deal.
held = positions.get(sym, 0.0)
positions[sym] = round(held + (vol if side == "BUY" else -vol), 2)
if positions[sym] == 0.0:
del positions[sym]
send_order(101, "EURUSD", "BUY", 0.50, 1.1000); execute(101, 9001, 1.1001)
send_order(102, "EURUSD", "BUY", 0.30, 1.1010); execute(102, 9002, 1.1011)
send_order(103, "EURUSD", "SELL", 0.80, 1.1050); execute(103, 9003, 1.1049)
send_order(104, "GBPUSD", "SELL", 0.20, 1.2700) # placed, never filled
print("ORDERS -- requests you sent")
for o in orders:
print(" #%-4d %-7s %-4s %.2f @ %.4f %s" % o)
print()
print("DEALS -- executions that happened")
for d in deals:
print(" #%-4d from order #%-4d %-7s %-4s %.2f @ %.4f" % d)
print()
print("POSITIONS -- what you currently hold")
if positions:
for sym, vol in positions.items():
print(" %-7s %+.2f" % (sym, vol))
else:
print(" (none)")
print()
print("orders sent :", len(orders))
print("deals executed :", len(deals))
print("positions held :", len(positions))
print()
print("Three orders became three deals and ZERO positions -- the third deal")
print("closed exactly what the first two opened. The fourth order is still")
print("waiting and is not a position at all.")
print()
print("This is why 'my trade disappeared' and 'I have two positions but one")
print("order' are both normal. In MetaTrader 5 the three are separate things:")
print(" an ORDER is an instruction (may never be filled)")
print(" a DEAL is one execution (immutable history)")
print(" a POSITION is your current holding (built from deals)")
You should see: four orders, three deals and no positions:
ORDERS -- requests you sent
#101 EURUSD BUY 0.50 @ 1.1000 filled
#102 EURUSD BUY 0.30 @ 1.1010 filled
#103 EURUSD SELL 0.80 @ 1.1050 filled
#104 GBPUSD SELL 0.20 @ 1.2700 placed
DEALS -- executions that happened
#9001 from order #101 EURUSD BUY 0.50 @ 1.1001
#9002 from order #102 EURUSD BUY 0.30 @ 1.1011
#9003 from order #103 EURUSD SELL 0.80 @ 1.1049
POSITIONS -- what you currently hold
(none)
orders sent : 4
deals executed : 3
positions held : 0
Three orders became three deals and ZERO positions -- the third deal
closed exactly what the first two opened. The fourth order is still
waiting and is not a position at all.
This is why 'my trade disappeared' and 'I have two positions but one
order' are both normal. In MetaTrader 5 the three are separate things:
an ORDER is an instruction (may never be filled)
a DEAL is one execution (immutable history)
a POSITION is your current holding (built from deals)
MetaTrader 5 keeps three separate records and the terminal has a separate tab for each, which is the source of most early confusion:
- An order is an instruction you sent. It may sit unfilled forever, and order #104 above is doing exactly that.
- A deal is one execution that really happened. Deals are history and never change.
- A position is what you currently hold, assembled from deals.
So “my trade vanished” is usually correct and not a fault. Three deals produced zero positions here because the third closed what the first two opened — the deals remain in the history tab forever, and the position tab is empty because you hold nothing.
This also explains a common surprise for anyone arriving from MetaTrader 4, where an order was the position. In MT5 they are different records with different identifiers, which is why an EA that closes “order 101” will not find it.
If not: python3: command not found on Windows means Python was installed without
“Add python.exe to PATH”; try py odp.py. If a position remains,
the volumes no longer cancel — 0.50 plus 0.30 against 0.80 is what empties it.
Go: the same folder.
Do: save this as netting.py and run python3 netting.py.
"""Netting or hedging: the account setting that changes what your EA does."""
def apply(deals, mode):
if mode == "netting":
net = 0.0
for side, vol in deals:
net += vol if side == "BUY" else -vol
return [("EURUSD", round(net, 2))] if round(net, 2) != 0 else []
positions = []
for side, vol in deals:
if mode == "hedging":
positions.append(("EURUSD " + side, vol))
return positions
DEALS = [("BUY", 0.50), ("BUY", 0.30), ("SELL", 0.20)]
print("the same three deals, on two kinds of account:")
print()
for mode in ("netting", "hedging"):
result = apply(DEALS, mode)
print("%-9s -> %d position(s)" % (mode, len(result)))
for name, vol in result:
print(" %-14s %+.2f" % (name, vol))
print()
print("Netting: one position per symbol. A SELL against an open BUY reduces")
print("it. You cannot be long and short the same instrument at once.")
print()
print("Hedging: every deal opens its own position with its own ticket, stop")
print("and target. You can hold opposing positions simultaneously.")
print()
print("Why this matters to an EA: 'close my position' is one operation on a")
print("netting account and a loop on a hedging one. And a strategy that")
print("opens a second position expecting to scale in will, on a netting")
print("account, silently just enlarge the first one -- with the original")
print("stop-loss still attached to the whole thing.")
print()
print("Check with AccountInfoInteger(ACCOUNT_MARGIN_MODE). Do not assume:")
print("it is set by the broker per account, and the same EA meets both.")
You should see: the same three deals producing one position or three:
the same three deals, on two kinds of account:
netting -> 1 position(s)
EURUSD +0.60
hedging -> 3 position(s)
EURUSD BUY +0.50
EURUSD BUY +0.30
EURUSD SELL +0.20
Netting: one position per symbol. A SELL against an open BUY reduces
it. You cannot be long and short the same instrument at once.
Hedging: every deal opens its own position with its own ticket, stop
and target. You can hold opposing positions simultaneously.
Why this matters to an EA: 'close my position' is one operation on a
netting account and a loop on a hedging one. And a strategy that
opens a second position expecting to scale in will, on a netting
account, silently just enlarge the first one -- with the original
stop-loss still attached to the whole thing.
Check with AccountInfoInteger(ACCOUNT_MARGIN_MODE). Do not assume:
it is set by the broker per account, and the same EA meets both.
This is a broker setting, not a preference of yours, and the same Expert Advisor will meet both. On a netting account there is at most one position per symbol and an opposing deal reduces it. On a hedging account every deal opens its own position with its own ticket, stop and target, and you can be long and short simultaneously.
The consequence for code is larger than it looks. “Close my position” is a single operation on netting and a loop on hedging. Worse, a strategy that opens a second position to scale in will, on a netting account, simply enlarge the first — with the original stop-loss still attached to the whole, larger, holding.
Ask rather than assume: AccountInfoInteger(ACCOUNT_MARGIN_MODE) tells you which one
you are on, and an EA meant to be shared should handle both or refuse to start on the one it does
not support.
If not: if both modes report the same count, the mode argument is not reaching
the branch — netting sums the deals into one net figure, hedging keeps each as its own
entry.
Go: the same folder.
Do: save this as margin.py and run python3 margin.py.
"""Balance, equity, margin, free margin -- and which one closes your trades."""
def account(balance, positions, prices, leverage=30):
used_margin = 0.0
floating = 0.0
for sym, vol, entry, contract in positions:
notional = vol * contract * prices[sym]
used_margin += notional / leverage
floating += (prices[sym] - entry) * vol * contract
equity = balance + floating
free = equity - used_margin
level = (equity / used_margin * 100.0) if used_margin else float("inf")
return balance, floating, equity, used_margin, free, level
POSITIONS = [("EURUSD", 1.0, 1.1000, 100000)]
print("%-10s %9s %9s %9s %9s %9s %9s" %
("PRICE", "BALANCE", "FLOATING", "EQUITY", "MARGIN", "FREE", "LEVEL%"))
print("-" * 74)
for price in (1.1000, 1.0950, 1.0900, 1.0850, 1.0800, 1.0750):
b, f, e, m, fr, lv = account(10000.0, POSITIONS, {"EURUSD": price})
flag = ""
if lv < 50: flag = " <- STOP OUT"
elif lv < 100: flag = " <- margin call"
print("%-10.4f %9.2f %9.2f %9.2f %9.2f %9.2f %8.1f%s"
% (price, b, f, e, m, fr, lv, flag))
print()
print("BALANCE money from CLOSED trades. It does not move while a trade is open.")
print("EQUITY balance plus the profit or loss of open trades. This is real.")
print("MARGIN the deposit the broker holds against your open positions.")
print("FREE equity minus margin -- what is left to open anything else.")
print("LEVEL equity / margin. The broker acts on THIS number.")
print()
_, _, e0, m0, _, lv0 = account(10000.0, POSITIONS, {"EURUSD": 1.1000})
_, _, e1, m1, _, lv1 = account(10000.0, POSITIONS, {"EURUSD": 1.0750})
print("Balance never changed in that table. Equity fell by %.0f and the"
% (e0 - e1))
print("margin level went from %.1f%% to %.1f%%." % (lv0, lv1))
print()
print("Traders watch balance because it is the big number at the top. The")
print("broker watches the margin level, and when it falls far enough your")
print("positions are closed for you at whatever price is available -- not at")
print("your stop, and not at a moment of your choosing.")
You should see: balance frozen while equity falls by 2,500:
PRICE BALANCE FLOATING EQUITY MARGIN FREE LEVEL%
--------------------------------------------------------------------------
1.1000 10000.00 0.00 10000.00 3666.67 6333.33 272.7
1.0950 10000.00 -500.00 9500.00 3650.00 5850.00 260.3
1.0900 10000.00 -1000.00 9000.00 3633.33 5366.67 247.7
1.0850 10000.00 -1500.00 8500.00 3616.67 4883.33 235.0
1.0800 10000.00 -2000.00 8000.00 3600.00 4400.00 222.2
1.0750 10000.00 -2500.00 7500.00 3583.33 3916.67 209.3
BALANCE money from CLOSED trades. It does not move while a trade is open.
EQUITY balance plus the profit or loss of open trades. This is real.
MARGIN the deposit the broker holds against your open positions.
FREE equity minus margin -- what is left to open anything else.
LEVEL equity / margin. The broker acts on THIS number.
Balance never changed in that table. Equity fell by 2500 and the
margin level went from 272.7% to 209.3%.
Traders watch balance because it is the big number at the top. The
broker watches the margin level, and when it falls far enough your
positions are closed for you at whatever price is available -- not at
your stop, and not at a moment of your choosing.
Balance is the money from closed trades, so it does not move at all while a position is open. That is why it is reassuring and why it is the wrong number to watch: in the table above it reads 10,000.00 throughout while the account is losing 2,500.
Equity is what you actually have, and the margin level — equity divided by the margin the broker is holding — is the number the broker acts on. When it falls far enough, positions are closed for you, at whatever price the market offers, not at your stop and not at a moment you chose.
Two practical consequences. First, the account panel's most prominent figure is the least
informative one; add equity and margin level to what you watch. Second, an EA that sizes positions
from AccountInfoDouble(ACCOUNT_BALANCE) will keep sizing as though nothing has
happened while the account is deep in a drawdown — size from
ACCOUNT_EQUITY instead, and the position size shrinks automatically as things go
badly, which is the behaviour you want.
If not: if the margin level does not fall, the floating profit is not being added to equity — equity is balance plus the open profit or loss, and it is the falling equity rather than the slightly falling margin that moves the ratio.
Without scrolling up: someone says their account is fine because the balance is unchanged at 10,000, but the broker has just closed two of their positions without being asked. Explain what happened, and name the two numbers they should have been watching instead. Answer: the balance was unchanged because balance only records closed trades — step 3 showed it reading 10,000.00 throughout while the account lost 2,500 on open positions. What the broker was watching is the margin level: equity divided by the margin held against the open positions. As the open losses grew, equity fell, the ratio fell with it, and at the broker's stop-out threshold positions were closed automatically at whatever price was available. The two numbers to watch are equity, which is balance plus open profit and loss and is what the account is really worth, and the margin level, which is the one that triggers the action. It is also worth checking whether the account is netting or hedging, from step 2, because on a netting account those “two positions” may never have been two — and an EA sizing from balance rather than equity would have kept opening full-size trades the whole way down.
Now do it without the page: open your own platform — a demo account is fine and is the right place for this
— and find the four figures from step 3 in the account panel: balance, equity, margin and free
margin. With no positions open, equity equals balance and the margin level is undefined. Open one
small position and watch which numbers move and which do not. Then find
ACCOUNT_MARGIN_MODE in the platform's own documentation and determine which kind of
account you have, because every later page assumes you know.
Next Steps
Now that you have MT5 installed and understand its purpose, the next tutorial covers how to navigate charts, switch between timeframes, and use the Market Watch window effectively. If you are already comfortable with the platform interface, you can skip ahead to the technical analysis or MQL5 programming sections.