Documentation Index
Fetch the complete documentation index at: https://docs.easierprop.com/llms.txt
Use this file to discover all available pages before exploring further.
Connection
wss://api.easierprop.com/ws?apiKey=sk_your_key
Subscribe to a Channel
Send a JSON message to start streaming:
{
"type": "subscribe",
"channel": "quotes",
"account_id": "70f60784-20f1-45ba-9a04-8e01c0b810c3",
"symbols": ["EURUSD", "GBPUSD"],
"interval_ms": 500
}
Server confirms:
{
"type": "subscribed",
"channel": "quotes",
"account_id": "70f60784-20f1-45ba-9a04-8e01c0b810c3",
"symbols": ["EURUSD", "GBPUSD"]
}
Receive Events
Quote Ticks
Quote data is wrapped in a data field containing the upstream quote object:
{
"type": "quote",
"account_id": "70f60784-...",
"data": {
"ask": 1.08544,
"bid": 1.08542,
"last": 0.0,
"symbol": "EURUSD",
"time": "2026-04-01T12:30:00",
"volume": 0.0
}
}
Order Updates
Sent when an order is opened, modified, or closed. Order data is wrapped in a data field:
{
"type": "order_update",
"account_id": "70f60784-...",
"data": {
"ticket": 12345678,
"symbol": "EURUSD",
"orderType": "Buy",
"lots": 0.01,
"openPrice": 1.08542,
"profit": 3.20,
"swap": 0.0,
"commission": -0.07
}
}
Profit Updates
Live equity and P&L, wrapped in a data field:
{
"type": "profit",
"account_id": "70f60784-...",
"data": {
"balance": 10000.00,
"equity": 10142.50,
"profit": 142.50,
"margin": 500.00,
"freeMargin": 9642.50
}
}
Heartbeat
Periodic account health snapshot with balance, equity, margin, and position count:
{
"type": "heartbeat",
"account_id": "70f60784-...",
"balance": 10000.00,
"equity": 10142.50,
"free_margin": 9642.50,
"margin": 500.00,
"position_count": 3,
"profit": 142.50
}
Subscribe to heartbeat:
{
"type": "subscribe",
"channel": "heartbeat",
"account_id": "70f60784-20f1-45ba-9a04-8e01c0b810c3"
}
New Position
Fires when a new position is opened on the account. The full order object is delivered in the data field:
{
"type": "new_position",
"account_id": "70f60784-...",
"data": {
"ticket": 12345680,
"symbol": "GBPUSD",
"orderType": "Buy",
"lots": 0.02,
"openPrice": 1.26110,
"profit": 0.0
}
}
Subscribe to new positions:
{
"type": "subscribe",
"channel": "new_position",
"account_id": "70f60784-20f1-45ba-9a04-8e01c0b810c3"
}
Alerts
Register price, spread, or P&L alerts to be notified when conditions are met. Alerts are one-shot: they fire once and are automatically removed.
Price Alert
Register a price alert to trigger when a symbol’s price crosses a threshold:
{
"type": "price_alert",
"account_id": "70f60784-...",
"symbol": "EURUSD",
"direction": "above",
"price": 1.09000
}
Server confirms registration:
{
"type": "alert_registered",
"alert_id": "a1b2c3d4-...",
"alert_type": "price_alert"
}
When triggered:
{
"type": "price_alert_triggered",
"account_id": "70f60784-...",
"alert_id": "a1b2c3d4-...",
"current_ask": 1.09010,
"current_bid": 1.09008,
"direction": "above",
"symbol": "EURUSD",
"target_price": 1.09000
}
Spread Alert
Register a spread alert to trigger when the spread exceeds a threshold in pips:
{
"type": "spread_alert",
"account_id": "70f60784-...",
"symbol": "EURUSD",
"max_pips": 2.0
}
When triggered:
{
"type": "spread_alert_triggered",
"account_id": "70f60784-...",
"alert_id": "e5f6g7h8-...",
"ask": 1.08560,
"bid": 1.08530,
"max_pips": 2.0,
"spread_pips": 3.0,
"symbol": "EURUSD"
}
P&L Alert
Register a P&L alert to trigger when account profit crosses a threshold:
{
"type": "pl_alert",
"account_id": "70f60784-...",
"condition": "profit_below",
"value": -500.00
}
When triggered:
{
"type": "pl_alert_triggered",
"account_id": "70f60784-...",
"alert_id": "i9j0k1l2-...",
"condition": "profit_below",
"current_value": -520.00,
"threshold": -500.00
}
Cancel Alert
Cancel a previously registered alert before it triggers:
{
"type": "cancel_alert",
"alert_id": "a1b2c3d4-..."
}
Aggregate Feed
Use subscribe_aggregate to receive aggregate events for selected stream types across every account owned by the API key. This is separate from the normal per-account subscribe message and does not include an account_id field.
{
"type": "subscribe_aggregate",
"channels": ["orders", "profit"],
"interval_ms": 1000
}
Aggregate events are delivered as:
{
"type": "aggregate",
"account_id": "70f60784-...",
"channel": "profit",
"data": {
"equity": 10142.50,
"profit": 142.50
}
}
To stop aggregate delivery:
{
"type": "unsubscribe_aggregate"
}
Unsubscribe
{
"type": "unsubscribe",
"channel": "quotes",
"account_id": "70f60784-...",
"symbols": ["EURUSD"]
}
Unsubscribe from everything on an account:
{
"type": "unsubscribe_all",
"account_id": "70f60784-..."
}
Available Channels
| Stream | Description | symbols required | interval_ms default |
|---|
quotes | Real-time bid/ask ticks | yes | 500ms |
orders | Order open/modify/close events | no | — |
profit | Live equity and P&L | no | 1000ms |
heartbeat | Account health snapshot | no | 5000ms |
new_position | New position opened | no | — |
ohlc | Live OHLC candle updates | yes | Uses interval_ms as timeframe |
order_book | Market depth / order book | yes | 500ms |
tick_value | Tick value updates | yes | 500ms |
Aggregate delivery uses subscribe_aggregate with a channels array instead of a normal channel value.
Multi-Account Streaming
Subscribe to multiple accounts on the same WebSocket:
// Stream quotes from account A
ws.send(JSON.stringify({
type: "subscribe",
channel: "quotes",
account_id: "account-a-uuid",
symbols: ["EURUSD"]
}));
// Stream profit from account B
ws.send(JSON.stringify({
type: "subscribe",
channel: "profit",
account_id: "account-b-uuid",
interval_ms: 1000
}));
// Stream order fills from both
ws.send(JSON.stringify({
type: "subscribe",
channel: "orders",
account_id: "account-a-uuid"
}));
ws.send(JSON.stringify({
type: "subscribe",
channel: "orders",
account_id: "account-b-uuid"
}));
Every message includes account_id so you can route it to the correct handler.
Error Events
{
"type": "error",
"code": "SESSION_ERROR",
"message": "No active session for this account"
}
Error codes: SESSION_ERROR, INVALID_MESSAGE
Keepalive
Full JavaScript Example
const ws = new WebSocket("wss://api.easierprop.com/ws?apiKey=sk_your_key");
ws.onopen = () => {
console.log("Connected to Easier Prop WebSocket");
// Stream live quotes
ws.send(JSON.stringify({
type: "subscribe",
channel: "quotes",
account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3",
symbols: ["EURUSD", "GBPUSD", "USDJPY"],
interval_ms: 500
}));
// Monitor order fills
ws.send(JSON.stringify({
type: "subscribe",
channel: "orders",
account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3"
}));
// Track live P&L
ws.send(JSON.stringify({
type: "subscribe",
channel: "profit",
account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3",
interval_ms: 1000
}));
// Heartbeat for account health
ws.send(JSON.stringify({
type: "subscribe",
channel: "heartbeat",
account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3"
}));
// Price alert
ws.send(JSON.stringify({
type: "price_alert",
account_id: "70f60784-20f1-45ba-9a04-8e01c0b810c3",
symbol: "EURUSD",
direction: "above",
price: 1.09000
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case "subscribed":
console.log(`Subscribed to ${msg.channel}`);
break;
case "quote":
console.log(`${msg.data.symbol}: ${msg.data.bid} / ${msg.data.ask}`);
break;
case "order_update":
console.log(`Order ${msg.data.ticket}: ${msg.data.orderType} ${msg.data.symbol} P&L=${msg.data.profit}`);
break;
case "profit":
console.log(`Equity: ${msg.data.equity} | Profit: ${msg.data.profit}`);
break;
case "heartbeat":
console.log(`Heartbeat: balance=${msg.balance} equity=${msg.equity} positions=${msg.position_count}`);
break;
case "new_position":
console.log(`New position: ${msg.data.ticket} ${msg.data.orderType} ${msg.data.symbol}`);
break;
case "alert_registered":
console.log(`Alert registered: ${msg.alert_id} (${msg.alert_type})`);
break;
case "price_alert_triggered":
console.log(`Price alert! ${msg.symbol} crossed ${msg.target_price} (${msg.direction})`);
break;
case "spread_alert_triggered":
console.log(`Spread alert! ${msg.symbol} spread=${msg.spread_pips} pips`);
break;
case "pl_alert_triggered":
console.log(`P&L alert! ${msg.condition} ${msg.threshold}, current=${msg.current_value}`);
break;
case "error":
console.error(`Error: ${msg.message}`);
break;
}
};
ws.onclose = () => console.log("Disconnected");
Full Python Example
import asyncio
import json
import websockets
API_KEY = "sk_your_key"
ACCOUNT_ID = "70f60784-20f1-45ba-9a04-8e01c0b810c3"
async def main():
uri = f"wss://api.easierprop.com/ws?apiKey={API_KEY}"
async with websockets.connect(uri) as ws:
# Subscribe to quotes
await ws.send(json.dumps({
"type": "subscribe",
"channel": "quotes",
"account_id": ACCOUNT_ID,
"symbols": ["EURUSD"],
"interval_ms": 500
}))
# Subscribe to P&L
await ws.send(json.dumps({
"type": "subscribe",
"channel": "profit",
"account_id": ACCOUNT_ID,
"interval_ms": 1000
}))
# Subscribe to heartbeat
await ws.send(json.dumps({
"type": "subscribe",
"channel": "heartbeat",
"account_id": ACCOUNT_ID
}))
# Register a price alert
await ws.send(json.dumps({
"type": "price_alert",
"account_id": ACCOUNT_ID,
"symbol": "EURUSD",
"direction": "above",
"price": 1.09000
}))
async for message in ws:
msg = json.loads(message)
if msg["type"] == "quote":
d = msg["data"]
print(f"{d['symbol']}: {d['bid']} / {d['ask']}")
elif msg["type"] == "profit":
d = msg["data"]
print(f"Equity: {d['equity']} | P&L: {d['profit']}")
elif msg["type"] == "heartbeat":
print(f"Heartbeat: balance={msg['balance']} positions={msg['position_count']}")
elif msg["type"] == "price_alert_triggered":
print(f"Price alert! {msg['symbol']} crossed {msg['target_price']}")
elif msg["type"] == "error":
print(f"Error: {msg['message']}")
asyncio.run(main())
Session Behavior
- Sessions auto-connect when you subscribe. No need to call
/connect first.
- If the MT5 session drops, the gateway reconnects automatically and resumes streaming.
- On WebSocket disconnect, all subscriptions for that client are cleaned up.
- Upstream quote subscriptions are ref-counted. Multiple clients on the same symbol share one upstream feed.