← 7TICS Book a call →
In production work / bridge
MT5 BRIDGE

Vendor-agnostic order routing layer between Nautilus strategies and multiple MT5 brokers. File-bridge EA, swap-point pattern, OCO that survives hedging-account quirks.

Every MT5 broker behaves differently. FundedNext and E8 use hedging accounts that silently ignore the position= field in OCO closes — submitting TRADE_ACTION_PENDING opens an opposing position instead of closing one, leaving naked exposure that survives the day. Wine-hosted MT5 terminals on Linux VPS die within minutes if you forget DISPLAY=:1 XAUTHORITY=... in the systemd unit. Single-process bridges create a single point of failure. The stack needs a routing layer that abstracts all of this.

        ┌────────────────────────────────────┐
        │      Nautilus strategy node        │
        │  (TRB London / Tokyo cells)        │
        └────────────────┬───────────────────┘
                         │ submit_order
                         ▼
        ┌────────────────────────────────────┐
        │     ExecutionService daemon        │
        │  (out-of-process, supervised)      │
        └────────────────┬───────────────────┘
                         │
            ┌────────────┼────────────┐
            ▼            ▼            ▼
        ┌───────┐    ┌───────┐    ┌───────┐
        │rpyc   │    │file-  │    │ direct│
        │relay  │    │bridge │    │  MT5  │
        │:18812 │    │ EA    │    │ python│
        └───┬───┘    └───┬───┘    └───┬───┘
            │            │            │
            ▼            ▼            ▼
        per-book STONEDESK_BRIDGE_DIR isolation
          (LF / FN / E8 / Derrick)
                         │
                         ▼
                  ┌──────────────┐
                  │ Wine MT5     │
                  │ + custom EA  │
                  └──────────────┘

OCO close on hedging brokers — TRADE_ACTION_SLTP

// nautilus-mt5-journal — fixed in commit dc46f36
def close_oco(self, position_id, sl_price, tp_price):
    # WRONG (silently opens opposing position on FN/E8):
    #   request = {"action": TRADE_ACTION_PENDING, "position": position_id, ...}
    # RIGHT:
    request = {
        "action": mt5.TRADE_ACTION_SLTP,
        "position": position_id,
        "sl": sl_price,
        "tp": tp_price,
    }
    return self.mt5.order_send(request)

Swap-point pattern — single attribute, duck-typed module

# router config picks transport at startup
if config.transport == "file_bridge":
    self._submit_mt5 = FileBridgeMT5(orders_dir=os.environ["STONEDESK_BRIDGE_DIR"])
elif config.transport == "rpyc":
    self._submit_mt5 = rpyc.connect("localhost", 18812).root
else:
    import MetaTrader5 as mt5
    self._submit_mt5 = mt5
# strategy code only knows self._submit_mt5.order_send(req)

MQL5 EA — atomic file write (string by value, not by ref)

// MQL5 'const string &' rejects rvalues — error 200 on inline build
// WRONG: AtomicWrite("/path", BuildPayload());
// RIGHT: pass string by value
bool AtomicWrite(const string path, const string payload) {
    string tmp = path + ".tmp";
    int h = FileOpen(tmp, FILE_WRITE | FILE_TXT);
    if (h == INVALID_HANDLE) return false;
    FileWriteString(h, payload);
    FileClose(h);
    return FileMove(tmp, 0, path, FILE_REWRITE);
}
4 brokers concurrent Zero-downtime swap Session-aware preflight WatchdogSec=900 for M10 bars
← stonedesk mt5 bridge gitnexus →