#!/usr/bin/env python3 """ MTP TradeLocker Auto-Sync ========================= Pushes your CLOSED TradeLocker trades into your My Trader Pro journal, the same way the MetaTrader Expert Advisor does. WHY THIS IS A SCRIPT AND NOT AN EA TradeLocker is a browser/mobile platform. It does not run MQL programs, so there is nothing to "drag onto a chart". Instead it exposes a REST API, and this script is the bridge: it reads your own closed trades and forwards them to your journal. It never places, modifies or closes an order. SETUP (about 3 minutes) 1. Install Python 3.11+ , then: pip install requests 2. Fill in the CONFIG block below. 3. Run it: python MTP-TradeLocker-Sync.py Leave it running. It checks every 60 seconds. (--once runs a single pass and exits, handy for a cron job.) YOUR CREDENTIALS Your TradeLocker password stays on your machine, in this file. It is sent only to TradeLocker's own login endpoint to obtain a session token. My Trader Pro never receives it and never asks for it. If you would rather not store it in a file, set the environment variables instead: TL_EMAIL, TL_PASSWORD, TL_SERVER, MTP_KEY """ import os import sys import time import json try: import requests except ImportError: sys.exit("Missing dependency. Run: pip install requests") # ----------------------------------------------------------------- CONFIG --- CONFIG = { # From your portal: Signals & Sync -> Auto-Sync -> your key "mtp_key": os.environ.get("MTP_KEY", "PASTE_YOUR_MTP_KEY_HERE"), "mtp_ingest": "https://mytraderpro.com/wp-json/mtp/v1/ingest", # Your TradeLocker login (the same three things you type to sign in) "tl_email": os.environ.get("TL_EMAIL", "you@example.com"), "tl_password": os.environ.get("TL_PASSWORD", "YOUR_TRADELOCKER_PASSWORD"), "tl_server": os.environ.get("TL_SERVER", "YOUR_SERVER_NAME"), # "demo" or "live" — must match where you actually trade "tl_env": os.environ.get("TL_ENV", "demo"), # Optional label so you can tell accounts apart inside MTP "account_label": "", "poll_seconds": 60, "lookback_days": 90, } # ----------------------------------------------------------------------------- BASE = { "demo": "https://demo.tradelocker.com/backend-api", "live": "https://live.tradelocker.com/backend-api", } def hold(): """Keep the console open. Double-clicking a .py on Windows opens a window that closes the instant the script ends — so an error message flashes past unread and the script looks 'broken'. Wait for a keypress instead.""" try: if os.name == "nt": print("") input("Press Enter to close this window...") except Exception: pass def fail(msg): print("") print("=" * 62) print(" MTP TradeLocker Sync — STOPPED") print("=" * 62) print(" " + msg) print("") hold() sys.exit(1) def login(cfg): """Exchange email/password/server for a JWT.""" base = BASE.get(cfg["tl_env"]) if not base: fail("tl_env must be 'demo' or 'live'.") r = requests.post(base + "/auth/jwt/token", timeout=30, json={ "email": cfg["tl_email"], "password": cfg["tl_password"], "server": cfg["tl_server"], }) if r.status_code == 401: fail("TradeLocker rejected the login. Check email, password and server name.") if not r.ok: fail("TradeLocker login failed (HTTP %s): %s" % (r.status_code, r.text[:200])) tok = r.json().get("accessToken") or r.json().get("access_token") if not tok: fail("Login succeeded but no access token came back: %s" % r.text[:200]) return base, tok def accounts(base, token): r = requests.get(base + "/auth/jwt/all-accounts", timeout=30, headers={"Authorization": "Bearer " + token}) if not r.ok: fail("Could not list accounts (HTTP %s): %s" % (r.status_code, r.text[:200])) data = r.json() accs = data.get("accounts") if isinstance(data, dict) else data if not accs: fail("No TradeLocker accounts found on this login.") return accs def orders_history(base, token, acc): """Closed orders for one account. Shapes vary by build, so be forgiving.""" acc_id = acc.get("id") or acc.get("accountId") acc_num = acc.get("accNum") or acc.get("accountNumber") or acc.get("accNumber") headers = {"Authorization": "Bearer " + token} if acc_num is not None: headers["accNum"] = str(acc_num) url = "%s/trade/accounts/%s/ordersHistory" % (base, acc_id) r = requests.get(url, headers=headers, timeout=30) if not r.ok: print(" ! history request failed (HTTP %s) %s" % (r.status_code, r.text[:160])) return [] body = r.json() rows = body.get("d", body) if isinstance(body, dict) else body if isinstance(rows, dict): rows = rows.get("ordersHistory") or rows.get("orders") or [] return rows or [] def acct_name(cfg, acc, multi): """The ROUTING KEY. The portal binds each of its journal accounts to this exact string and files every trade to its bound account automatically — so it must be UNIQUE per real account and STABLE across runs. Rules: * No label set -> "TradeLocker ". The env is included because demo and live are separate universes whose account numbers can collide, and a demo trade must never route into a live journal. * Label set, ONE account -> the label, exactly as typed. * Label set, MANY accounts -> "