An Expert Advisor that only sees the MetaTrader terminal is enough for some rules. Many products are not that simple: you want Python for research, a risk service that sees several accounts, a news calendar, a CRM of clients whose accounts you manage under a licence, or alerts that are not the terminal’s inbox. That means a bridge: MQL on one side, an API or a local process on the other, with the terminal still as the only place orders are sent unless you have a broker API as well.
This guide is for founders and CTOs hiring an agency to connect MT4/MT5 bots to external systems without turning OnTick into an HTTP client from 2008. DayDecoder builds this as software, from Ahmedabad, for overseas teams. It is not a way to “beat the market from Python”.
Risk disclaimer. Trading foreign exchange, CFDs and other leveraged products can result in the loss of some or all of your capital. Leverage multiplies losses as well as gains. Bridges, APIs, VPS processes and Python research code can fail, delay, or duplicate orders. Backtests and paper results do not predict live results. This article is about integration architecture. It is not investment, trading or financial advice, and DayDecoder does not promise profits. Only risk money you can afford to lose, and get regulated advice in your jurisdiction if you need it.
Key takeaways
- Keep order placement in one authorised place. A Python script and an EA both sending orders is how you double a position.
- Prefer a local bridge (named pipe, socket, or file with locking) on the VPS over calling public HTTP from MQL on every tick.
- Treat the wire as unreliable: sequence numbers, idempotent commands, and a dead-man switch if Python disappears.
- Secrets stay on the VPS or a secret manager, not in an MQH committed to a shared repo.
- Compliance sits with you: who may see account data, and whether a third-party API is allowed under your broker agreement.
When you actually need a bridge
| Need | Bridge? | Usually enough instead |
|---|---|---|
| A moving-average cross on one symbol | No | A self-contained EA. See building MT4/MT5 Expert Advisors |
| Research in Python, live in MQL | Maybe | Export rules to MQL once they are stable; bridge only if the model must update live |
| One risk budget across several MT accounts | Yes | A risk process that can pause EAs |
| Telegram or Slack alerts | Small yes | A local notifier, not a trading brain |
| Broker REST/WebSocket API plus MT | Careful | Pick one execution venue per strategy, or you will desynchronise |
A shape that does not fight MetaTrader
MetaTrader’s process is the terminal. MQL runs inside it. Python runs beside it on the same VPS, or in a service the VPS can reach. A clean split:
- Market data out: EA (or a dedicated “pump” EA) writes ticks or bar closes to a local queue.
- Decision in Python or a service: reads the queue, applies a model or a portfolio rule, writes a command: flatten, pause, set max lots, or “signal long but do not send”.
- Execution in MQL: only the EA talks to the trade server, after its own spread, session and risk checks.
- State back: positions, rejects, and heartbeats so Python knows the truth is the terminal, not its last intent.
If Python can also call a broker HTTP API “just in case”, you now have two execution paths. Don’t, unless that is a documented failover with locks.
Transport options
Local socket or named pipe
Best default on a Windows VPS next to MT5. Low latency, no public internet, you control the port. Use a simple length-prefixed JSON or protobuf. Reconnect with backoff. MQL5 can use sockets; keep the protocol tiny.
File drop with atomic rename
Ugly and robust. Python writes cmd-uuid.json.tmp then renames. EA polls on timer, not on every tick. Good for low-frequency commands (pause, flatten, change risk). Bad for tick-by-tick chatter.
ZeroMQ or similar
Common in the MT community via unofficial bindings. Fine if you accept the operational load (DLL, bitness, who upgrades it). Treat unofficial DLLs as a supply-chain decision, not a convenience.
HTTPS to your Laravel or Node.js app
Useful when the brain is a multi-tenant product in the cloud (clients, permissions, an audit log). Then the VPS agent is a worker that pulls commands, not a public webhook the whole internet can poke. Authenticate, pin TLS, and never put the MT password in the web app. This is ordinary app integration work plus a trading fail-closed policy.
The command protocol
Commands need: id, timestamp, account, symbol, action, payload, expiry. The EA acks with the same id and the resulting ticket or error. If the ack is lost, Python must query state, not resend a market order blindly. Idempotency keys are as important here as in any payment API.
Include a heartbeat. If Python misses N heartbeats, the EA should flatten, pause, or keep last-known-good — that choice belongs in the spec, written down, not in a Slack argument at 03:00. “Keep trading the last signal” is a decision with a loss attached.
Python’s job, and what it should not do
Python is excellent at pandas research, feature pipelines, and calling model runtimes. It is a poor place to silently retry market orders. Pin dependencies. Run under a service manager (NSSM, WinSW, or a Linux supervisor if you use a wine/VPS setup that supports it). Log to rotating files. Do not run research notebooks on the live VPS.
Environment variables for API keys. A separate user account from the person who RDP’s in to watch YouTube. If you load a model file, checksum it against the git tag.
Security and compliance
- The bridge listens on localhost unless you have a real reason not to.
- No EA input called
ApiKeythat shows up in screenshots. - Your broker’s terms may restrict expert advisors, VPS, or third-party signal routing. Read them.
- If you manage other people’s money, this is a regulated activity in most of the US, UK, EU and Australia. Software does not replace that licence. We build software; we are not your compliance officer.
- Audit logs: who changed risk, who paused, which version was running.
Failure modes we test on purpose
- Python killed mid-command.
- Terminal disconnected from the broker.
- Clock skew after a VPS pause.
- Duplicate delivery of “open long”.
- Symbol suffix differences (
EURUSDvsEURUSD.m). - Partial fills and requotes, if the venue has them.
If an agency cannot describe those tests, they are demoing a happy path. Happy paths do not survive a Friday close.
A sensible delivery sequence
- Self-contained EA with logs (even if the signal is later replaced).
- Heartbeat + pause command over a local socket. No orders from Python yet.
- Read-only position snapshot to Python.
- One idempotent command type (flatten or pause) in demo.
- Signals from Python, still executed only in MQL, still capped by EA risk.
- Only then extra APIs (calendar, warehouse of features). See algo bot architecture for the wider pattern.
DayDecoder has 10+ years shipping production software for 103 customers. Bridges are integration work: timeouts, identity, and a kill switch. Write to contact@daydecoder.com with which side owns execution if you want a design review.
Message size, clocks and what not to stream
Do not stream every tick to Python unless the research truly needs it. Bar-close plus a heartbeat is enough for many rules and kinder to disks. If you do stream ticks, cap the queue, drop with a counter (never silently), and never let Python’s garbage collection pause become your trading clock. The EA’s local time and Python’s UTC must be converted explicitly. A DST bug looks like a “strategy that only fails in March”.
Payloads should be versioned. {"v":1,"type":"pause"} is boring and good. Optional fields you add later must not change the meaning of v1. If you use files, rotate them. A 20 GB JSONL on the desktop of an RDP user is an incident, not a log.
Who is allowed to pause, and how you prove it
A pause command is a privileged action. If the Python process is a multi-tenant SaaS talking to many MT accounts, the user who hits pause in the web UI must map to an account they own, through your ordinary Laravel policies, not through a query string. Audit it. If it is a single internal bot, a local pause file plus two people who know the path is enough. In both cases the EA should log who (or which system) asked.
Flatten is even more privileged. Some teams require a second factor (a physical button, a second operator). Software cannot invent your governance, but it can refuse to flatten from an unauthenticated HTTP port on the public internet. That port should not exist.
Testing the bridge without gambling
Contract tests: given a recorded snapshot of positions, Python emits a command, the EA stub records what it would have sent to CTrade. Integration tests on demo: kill Python, confirm the dead-man behaviour you specified; restore Python, confirm no duplicate market order. Chaos: pull the network cable, sleep the VM, fill the disk. If this sounds like ordinary backend testing, that is the point. Trading does not exempt you from it; it makes the bill for skipping it more colourful.
We put these tests in the same repo as the MQL and the Python. CI cannot run MetaTrader easily; CI can run the protocol parsers and the risk functions. The rest is a checklist on demo, signed by a human.
Need MT5 talking to Python or your Laravel app without a second order path? That is a scoped build. See custom software development or contact us.
When the web app is Laravel
If operators live in a Laravel product, the VPS agent pulls commands over HTTPS with mTLS or a rotating token, tagged with account id. Laravel stores intent, audit, and who clicked pause. It does not place the order. The EA still owns CTrade. That split is how you keep web deploys from becoming trading deploys. A Horizon job should not be your tick loop.
Rate-limit the pull. Do not let a chatty dashboard become a denial of service against your own terminal. Cache the last known positions in Redis if you must show them on a page, with a timestamp so nobody mistakes a 30-second-old snapshot for a fill. If Redis is stale, show a warning, not a green badge. Operators will believe the badge.
Deploys of the Laravel app should not restart the EA. That sounds obvious until a shared VPS reboot script does both. Separate systemd/NSSM units, separate healthchecks, separate people who may restart them. Document it in the same runbook as flatten. Add a one-line heartbeat to the chart comment or a file so a human RDP session can see “Python alive / stale” without reading a logfile first. Humans under stress do not tail logs; they look at the chart. Use that.
FAQ
Can Python send the orders through the broker’s REST API instead?
Sometimes, if the broker offers it and you accept a different fill path than the terminal. Mixing REST and EA orders on the same account without a lock is a defect. Choose one execution owner per strategy.
Is a DLL from a forum a good bridge?
It can work and it can steal a password. Treat it like any native dependency: origin, bitness, updates, and whether you can build it from source. We prefer protocols we can read in git.
Will this make the strategy faster?
A bridge adds latency and failure modes. Use it because you need Python or a central risk process, not because “Python is faster than MQL” as a slogan. Many rules are slower in a round trip.
Can we run the Python brain in AWS and MT on a cheap VPS?
Yes, with TLS, auth, and a clear disconnect policy. You now depend on two networks. For some products that is correct; for a single-account EA it is usually more theatre than need.
Who owns the Python and the MQL?
Custom code should live in your repositories when paid. Third-party licences stay third-party. We default to working in your git org, same as other custom software work.
Written by
Prakash Tank
Founder & Full-Stack Architect, DayDecoder
Prakash started as a certified Laravel developer and grew into a full-stack architect who owns a project end to end: requirements, architecture, development, deployment, and support. He designs event-driven systems with...
More about Prakash