MetaTrader 5 — Live & Historical OHLCV Data
X-API-Key: 1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw
In N8N, use an HTTP Request node: Method GET, paste the URL above as URL.
Add Header: X-API-Key = 1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw.
Query parameters are already in the URL — no need to add them separately.
pip install requests pandas
import requests
url = "https://mt5api.invoker4916.io.vn/api/v1/candles?symbol=EURUSDm&timeframe=M15&count=100"
response = requests.get(url, headers={
"X-API-Key": "1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw"
})
data = response.json()
print(f"Loaded {data['count']} candles from {data['from_dt']} to {data['to_dt']}")
print(data["candles"][0])
url = "https://mt5api.invoker4916.io.vn/api/v1/candles?symbol=EURUSDm&timeframe=M15&count=100"
import requests
import pandas as pd
url = "https://mt5api.invoker4916.io.vn/api/v1/candles?symbol=EURUSDm&timeframe=H1&count=500"
headers = {"X-API-Key": "1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw"}
data = requests.get(url, headers=headers).json()
df = pd.DataFrame(data["candles"])
df["time"] = pd.to_datetime(df["time"])
df = df.iloc[::-1].reset_index(drop=True) # oldest first
# Simple Moving Averages
df["sma20"] = df["close"].rolling(20).mean()
df["sma50"] = df["close"].rolling(50).mean()
# RSI
delta = df["close"].diff()
gain = delta.where(delta > 0, 0).rolling(14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
df["rsi"] = 100 - (100 / (1 + gain / loss))
# Latest values
latest = df.iloc[-1]
print(f"{latest['time'].date()} | Close: {latest['close']:.5f} | RSI: {latest['rsi']:.1f}")
print(f"SMA20: {latest['sma20']:.5f} | SMA50: {latest['sma50']:.5f}")
# Golden cross signal
if latest["sma20"] > latest["sma50"]:
print(">>> GOLDEN CROSS — BUY signal")
elif latest["sma20"] < latest["sma50"]:
print(">>> DEATH CROSS — SELL signal")
pip install sseclient-py
import sseclient, requests, json
url = "https://mt5api.invoker4916.io.vn/api/v1/subscribe"
params = {"symbol": "XAUUSDm", "timeframe": "M1", "api_key": "1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw"}
resp = requests.get(url, params=params,
headers={"Accept": "text/event-stream"}, stream=True)
client = sseclient.SSEClient(resp)
for event in client.events():
msg = json.loads(event.data)
if msg["type"] == "snapshot":
print(f"Loaded {len(msg['candles'])} historical candles")
elif msg["type"] == "candle":
c = msg["data"]
print(f"{c['time']} | Close: {c['close']} | Vol: {c['tick_volume']}")
import requests
import pandas as pd
resp = requests.get(
"https://mt5api.invoker4916.io.vn/api/v1/candles",
params={"symbol": "EURUSDm", "timeframe": "M15", "count": 10000},
headers={"X-API-Key": "1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw"}
)
df = pd.DataFrame(resp.json()["candles"])
df["time"] = pd.to_datetime(df["time"])
df = df.iloc[::-1].reset_index(drop=True) # oldest first
# RSI
delta = df["close"].diff()
gain = delta.where(delta > 0, 0).rolling(14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
df["rsi"] = 100 - (100 / (1 + gain / loss))
# Bollinger Bands
df["sma20"] = df["close"].rolling(20).mean()
df["bb_lower"] = df["sma20"] - 2 * df["close"].rolling(20).std()
df["signal"] = (df["rsi"] < 30) & (df["close"] < df["bb_lower"])
print("Entry signals:", df[df["signal"]].shape[0])
import sseclient, requests, json
resp = requests.get(
"https://mt5api.invoker4916.io.vn/api/v1/subscribe",
params={"symbol": "XAUUSDm", "timeframe": "M1", "api_key": "1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw"},
headers={"Accept": "text/event-stream"}, stream=True
)
client = sseclient.SSEClient(resp)
for event in client.events():
msg = json.loads(event.data)
if msg["type"] == "candle":
c = msg["data"]
o, h, l, cl = c.get("open"), c.get("high"), c.get("low"), c.get("close")
body = abs(cl - o)
lower_wick = min(o, cl) - l
upper_wick = h - max(o, cl)
# Pin bar: lower wick > 2x body, tiny upper wick
if lower_wick > body * 2 and upper_wick < body * 0.5:
print(f">>> BULLISH PIN BAR at {c.get('time')} | Close: {cl}")
else:
print(f"Close: {cl} | Vol: {c.get('tick_volume')}")
# N8N Workflow:
# 1. Schedule Trigger → Daily at 08:00
# 2. HTTP Request
# GET /api/v1/candles?symbol=XAUUSDm&timeframe=H4&count=5
# Header: X-API-Key = 1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw
# 3. Code (JS):
# const candles = $json.candles;
# const closes = candles.map(c => c.close);
# const avg = (closes.reduce((a,b) => a+b, 0) / closes.length).toFixed(2);
# const latest = closes[closes.length-1];
# return { latest, avg, signal: latest > avg ? "BUY" : "SELL" };
# 4. Telegram Node:
# Message: "{{ $json.signal }} signal | XAUUSD H4 close: {{ $json.latest }}"
# N8N Workflow:
# 1. Wait (Webhook) — receives manual /push trigger
# 2. HTTP Request
# GET /api/v1/candles?symbol=XAUUSDm&timeframe=M5&count=2&source=mql5
# Header: X-API-Key = 1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw
# 3. Code (JS) — detect engulfing:
const candles = $input.first().json.candles;
const prev = candles[1], curr = candles[0];
const body = Math.abs(curr.close - curr.open);
const prevBody = Math.abs(prev.close - prev.open);
const engulfing = curr.close > prev.open && curr.open < prev.close
&& body > prevBody;
if (engulfing) {
return { json: { alert: "BULLISH ENGULFING", close: curr.close } };
}
# 4. Discord Node → Post: {{ $json.alert }} at {{ $json.close }}
import requests
API_KEY = "1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw"
BASE = "https://mt5api.invoker4916.io.vn/api/v1/candles"
def get_candles(symbol, tf, count=200):
r = requests.get(BASE, params={"symbol": symbol, "timeframe": tf, "count": count},
headers={"X-API-Key": API_KEY})
return r.json()["candles"]
def sma(closes, period):
return sum(closes[-period:]) / period
def rsi(closes, period=14):
deltas = [closes[i] - closes[i-1] for i in range(1, len(closes))]
gain = sum([d for d in deltas[-period:] if d > 0]) / period
loss = abs(sum([d for d in deltas[-period:] if d < 0]) / period)
return 100 - (100 / (1 + gain / loss)) if loss != 0 else 50
# Step 1: D1 trend (200 SMA)
d1 = get_candles("EURUSDm", "D1", 200)
d1_closes = [c["close"] for c in reversed(d1)]
trend_up = d1_closes[-1] > sma(d1_closes, 200)
# Step 2: M15 entry (RSI)
m15 = get_candles("EURUSDm", "M15", 100)
m15_closes = [c["close"] for c in reversed(m15)]
r = rsi(m15_closes)
# Signal
if trend_up and r < 40:
print(f"LONG — D1 up, RSI={r:.1f}")
elif not trend_up and r > 60:
print(f"SHORT — D1 down, RSI={r:.1f}")
else:
print("No signal")
| GET | /api/v1/health | MT5 + watcher status. Use in uptime monitors |
| GET | /api/v1/connect | Force MT5 reconnect after terminal restart |
| GET | /api/v1/symbols | List all 348 tradeable symbols |
| GET | /api/v1/timeframes | M1–MN1 supported timeframes |
| GET | /api/v1/candles | Fetch OHLCV (max 10,000). Core endpoint |
| GET | /api/v1/push | Force MQL5 EA to push latest candles |
| GET | /api/v1/subscribe | SSE stream — live tick-by-tick data |
| symbol | EURUSDm, XAUUSDm, BTCUSDm, US500m... |
| timeframe | M1 M5 M15 H1 H4 D1 W1 MN1 |
| count | 1–10,000 candles |
| source | mt5 (historical, default) or mql5 (live) |
Header: X-API-Key: 1azwfMHrRd03Z4LUnVh08eaKPkFfZNNgwh8THhn1iWw
SSE subscribe uses query param instead: ?api_key=YOUR_KEY (browsers block custom headers on EventSource)
{
"symbol": "EURUSDm",
"timeframe": "M15",
"count": 5,
"from_dt": "2026-03-27T03:00:00",
"to_dt": "2026-03-27T03:56:00",
"candles": [
{ "time": "2026-03-27T03:00:00",
"open": 1.15337, "high": 1.15352,
"low": 1.15325, "close": 1.15347,
"tick_volume": 99 },
...
],
"source": "mt5"
}