As part of the transition to FINAI v2.0 (Light Cone), this documentation will be updated soon — including FIX protocol specifications and new endpoints. You can already request the updated documentation by contacting us.
SynLabs FINAI
Real-time ML Predictions for Financial Markets
WebSocket API v1We provide two separate WebSocket endpoints on the same domain based on asset type:
Symbols: eurusd, audusd, xauusd, spy
Symbols: btcusdt, ethusdt
Connect with your API key as a query parameter:
# For Forex/Stocks:
wss://finai.synlabs.pro/api/v1/markets/predictions?api_key=YOUR_API_KEY
# For Crypto:
wss://finai.synlabs.pro/api/v1/crypto/predictions?api_key=YOUR_API_KEY
Or authenticate after connection:
{
"action": "auth",
"api_key": "YOUR_API_KEY"
}JSON
{
"type": "auth_success",
"user_id": 1,
"username": "your_username",
"max_symbols": 10
}Response
| Symbol | Type | Endpoint | Description |
|---|---|---|---|
eurusd | Forex | /api/v1/markets | Euro / US Dollar |
audusd | Forex | /api/v1/markets | Australian Dollar / US Dollar |
xauusd | Commodity | /api/v1/markets | Gold / US Dollar |
spy | ETF | /api/v1/markets | S&P 500 ETF |
btcusdt | Crypto | /api/v1/crypto | Bitcoin / Tether |
ethusdt | Crypto | /api/v1/crypto | Ethereum / Tether |
All timeframes are supported on both endpoints:
| Value | Description |
|---|---|
1m | 1 Minute |
5m | 5 Minutes |
15m | 15 Minutes |
1h | 1 Hour |
4h | 4 Hours |
1d | 1 Day |
{
"action": "subscribe",
"symbol": "eurusd",
"timeframe": "1m"
}Request
{
"type": "subscribed",
"symbol": "eurusd",
"timeframe": "1m",
"active_subscriptions": 1,
"max_symbols": 10
}Response
When a candle closes, you receive predictions from 10 ML models for the next candle:
{
"type": "prediction",
"symbol": "eurusd",
"timeframe": "1m",
"timestamp": "2025-12-14T13:36:00.150Z",
"date_open": "2025-12-14T13:36:00Z",
"date_close": "2025-12-14T13:37:00Z",
"predictions": {
"roc1_step1_diff": { "class": 1, "prob": 0.78 },
"roc2_step1_diff": { "class": 1, "prob": 0.72 },
"roc2_step1_updwn": { "class": 0, "prob": 0.81 },
"roc2_step2_diff": { "class": 1, "prob": 0.69 },
"roc3_updwn": { "class": 1, "prob": 0.74 },
"ha_roc1_step1_diff": { "class": 1, "prob": 0.83 },
"ha_roc1_step1_updwn": { "class": 1, "prob": 0.79 },
"ha_roc1_step2_diff": { "class": 0, "prob": 0.71 },
"ha_roc1_step2_updwn": { "class": 1, "prob": 0.76 },
"ha_roc1_step3_diff": { "class": 1, "prob": 0.68 }
},
"accuracy": {
"roc1_step1_diff": { "accuracy": 76.5, "total": 100 },
"roc2_step1_diff": { "accuracy": 74.2, "total": 100 },
"roc2_step1_updwn": { "accuracy": 78.1, "total": 100 },
"roc2_step2_diff": { "accuracy": 71.8, "total": 100 },
"roc3_updwn": { "accuracy": 73.4, "total": 100 },
"ha_roc1_step1_diff": { "accuracy": 79.3, "total": 100 },
"ha_roc1_step1_updwn": { "accuracy": 77.6, "total": 100 },
"ha_roc1_step2_diff": { "accuracy": 72.9, "total": 100 },
"ha_roc1_step2_updwn": { "accuracy": 75.1, "total": 100 },
"ha_roc1_step3_diff": { "accuracy": 70.4, "total": 100 }
},
"latency_ms": 142.5
}Prediction
| Class | Meaning | Action |
|---|---|---|
0 | DOWN | Price will decrease → SELL |
1 | UP | Price will increase → BUY |
All predictions are based on the Rate of Change (ROC) — a classic momentum oscillator that measures the percentage change in price over a specified period:
ROC Formula:
\[ ROC(n)_t = \frac{Close_t - Close_{t-n}}{Close_{t-n}} \times 100\% \]
Where:
| Notation | Period | Description |
|---|---|---|
ROC(1) | 1 bar | Short-term momentum: price change from previous bar |
ROC(2) | 2 bars | Medium-term momentum: price change over 2 bars |
ROC(3) | 3 bars | Extended momentum: price change over 3 bars |
In addition to standard OHLC prices, we compute predictions on Heikin-Ashi candles — a Japanese technique that smooths price action and better reveals trend direction:
Heikin-Ashi Formulas:
\[ HA_{Close} = \frac{Open + High + Low + Close}{4} \]
\[ HA_{Open} = \frac{HA_{Open_{t-1}} + HA_{Close_{t-1}}}{2} \]
\[ HA_{High} = \max(High, HA_{Open}, HA_{Close}) \]
\[ HA_{Low} = \min(Low, HA_{Open}, HA_{Close}) \]
Then we calculate the Heikin-Ashi body percentage (internal candle change):
\[ HA\_prchange = \frac{HA_{Close} - HA_{Open}}{HA_{Open}} \times 100\% \]
Note: This is NOT the classical ROC between candles. It measures the percentage change within a single Heikin-Ashi candle (open to close), capturing trend strength and candle body size.
Each model predicts one of two target types:
| Target Type | Suffix | Prediction | Trading Signal |
|---|---|---|---|
| Sign | _updwn |
Will ROC be positive or negative? | 1 = ROC > 0 (UP), 0 = ROC < 0 (DOWN) |
| Difference | _diff |
Will ROC increase or decrease? | 1 = ROC↑ (momentum accelerating), 0 = ROC↓ (momentum slowing) |
Diff Target Formula:
\[ diff_{t+1} = \begin{cases} 1 & \text{if } ROC_{t+1} > ROC_t \\ 0 & \text{if } ROC_{t+1} \leq ROC_t \end{cases} \]
Each model name follows the pattern: {indicator}_{horizon}_{target}
| Component | Values | Meaning |
|---|---|---|
| Indicator | roc1, roc2, roc3, ha_roc1 | Which ROC indicator we predict (period: 1, 2, or 3 bars) |
| Horizon | step1, step2, step3 | How many bars ahead we predict |
| Target | diff, updwn | diff = will ROC increase or decrease?, updwn = will ROC be positive or negative? |
10 proprietary ML classifiers. Each predicts a specific ROC indicator behavior on a future bar:
| Model | Indicator | Horizon | Target | Description |
|---|---|---|---|---|
roc1_step1_diff | ROC(1) | +1 bar | Diff | Will ROC(1) on next bar be greater than ROC(1) on current bar? |
roc2_step1_diff | ROC(2) | +1 bar | Diff | Will ROC(2) on next bar be greater than ROC(2) on current bar? |
roc2_step1_updwn | ROC(2) | +1 bar | Sign | Will ROC(2) be positive or negative on next bar? |
roc2_step2_diff | ROC(2) | +2 bars | Diff | Will ROC(2) at t+2 be greater than ROC(2) at t+1? |
roc3_updwn | ROC(3) | +1 bar | Sign | Will ROC(3) be positive or negative on next bar? |
ha_roc1_step1_diff | HA-ROC(1) | +1 bar | Diff | Will HA-ROC(1) on next bar be greater than HA-ROC(1) on current bar? |
ha_roc1_step1_updwn | HA-ROC(1) | +1 bar | Sign | Will HA-ROC(1) be positive or negative on next bar? |
ha_roc1_step2_diff | HA-ROC(1) | +2 bars | Diff | Will HA-ROC(1) at t+2 be greater than HA-ROC(1) at t+1? |
ha_roc1_step2_updwn | HA-ROC(1) | +2 bars | Sign | Will HA-ROC(1) be positive or negative at t+2? |
ha_roc1_step3_diff | HA-ROC(1) | +3 bars | Diff | Will HA-ROC(1) at t+3 be greater than HA-ROC(1) at t+2? |
_updwn models (Sign prediction):Predicts whether the ROC indicator will be positive or negative on the future bar:
| Class | Meaning | Interpretation |
|---|---|---|
1 | ROC > 0 (positive) | Price will be HIGHER than n bars ago → Bullish |
0 | ROC < 0 (negative) | Price will be LOWER than n bars ago → Bearish |
_diff models (Difference prediction):Predicts whether the ROC value will INCREASE or DECREASE compared to current bar:
| Class | Meaning | Interpretation |
|---|---|---|
1 | ROC will increase | Momentum accelerating (ROC[future] > ROC[current]) |
0 | ROC will decrease | Momentum decelerating (ROC[future] < ROC[current]) |
roc2_step1_updwn = 1 means:
→ The model predicts positive 2-bar ROC sign on the next evaluation step
• ROC(2) = -0.3% → ROC(2) = +0.2% → class 1 (turned positive)
• ROC(2) = +0.4% → ROC(2) = +0.6% → class 1 (remains positive)
• ROC(2) = -0.6% → ROC(2) = -0.1% → class 0 (still negative)
• ROC(2) = +0.6% → ROC(2) = -0.1% → class 0 (turned negative)
roc2_step1_diff = 1 means:
→ ROC(2) on NEXT bar will be greater than current ROC(2)
• ROC = -0.5% → ROC = -0.4% → class 1 (increased: -0.4 > -0.5)
• ROC = +0.5% → ROC = +0.6% → class 1 (increased: +0.6 > +0.5)
• ROC = +0.5% → ROC = +0.4% → class 0 (decreased: +0.4 < +0.5)
import asyncio
import websockets
import json
async def connect():
# Forex/Stocks: wss://finai.synlabs.pro/api/v1/markets/predictions?api_key=KEY
# Crypto: wss://finai.synlabs.pro/api/v1/crypto/predictions?api_key=KEY
uri = "wss://finai.synlabs.pro/api/v1/markets/predictions?api_key=YOUR_KEY"
async with websockets.connect(uri) as ws:
await ws.send(json.dumps({
"action": "subscribe",
"symbol": "eurusd",
"timeframe": "1m"
}))
while True:
msg = await ws.recv()
data = json.loads(msg)
if data.get("type") == "prediction":
print(f"{data['symbol']} {data['timeframe']}")
for model, pred in data["predictions"].items():
dir = "UP" if pred["class"] == 1 else "DOWN"
print(f" {model}: {dir} ({pred['prob']:.0%})")
asyncio.run(connect())Python
import asyncio
import websockets
import json
async def connect_json():
uri = "wss://finai.synlabs.pro/api/v1/markets/predictions?api_key=YOUR_KEY"
async with websockets.connect(uri) as ws:
await ws.send(json.dumps({
"action": "subscribe",
"symbol": "eurusd",
"timeframe": "1m"
}))
while True:
msg = await ws.recv()
print(json.dumps(json.loads(msg), indent=2, ensure_ascii=False))
asyncio.run(connect_json())Python
const WebSocket = require('ws');
const ws = new WebSocket(
"wss://finai.synlabs.pro/api/v1/markets/predictions?api_key=YOUR_KEY"
);
ws.onopen = () => {
ws.send(JSON.stringify({
action: "subscribe", symbol: "spy", timeframe: "1h"
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === "prediction") {
console.log(`${data.symbol} ${data.timeframe}`);
for (const [model, pred] of Object.entries(data.predictions)) {
const dir = pred.class === 1 ? "UP" : "DOWN";
console.log(` ${model}: ${dir} (${pred.prob})`);
}
}
};Node.js
const WebSocket = require('ws');
const ws = new WebSocket(
// Use /api/v1/crypto/predictions for Crypto
"wss://finai.synlabs.pro/api/v1/markets/predictions?api_key=YOUR_KEY"
);
ws.onopen = () => {
ws.send(JSON.stringify({
action: "subscribe", symbol: "eurusd", timeframe: "1m"
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.dir(data, { depth: null, colors: true });
};Node.js
use futures_util::{SinkExt, StreamExt};
use serde_json::json;
use tokio_tungstenite::connect_async;
use url::Url;
#[tokio::main]
async fn main() {
let url = "wss://finai.synlabs.pro/api/v1/markets/predictions?api_key=YOUR_KEY";
let (ws_stream, _) = connect_async(Url::parse(url).unwrap()).await.expect("Failed to connect");
let (mut write, mut read) = ws_stream.split();
if let Some(msg) = read.next().await {
println!("Auth: {}", msg.unwrap());
}
let subscribe_msg = json!({
"action": "subscribe", "symbol": "eurusd", "timeframe": "1m"
});
write.send(subscribe_msg.to_string().into()).await.unwrap();
while let Some(msg) = read.next().await {
let data = msg.unwrap();
if data.is_text() {
let v: serde_json::Value = serde_json::from_str(data.to_text().unwrap()).unwrap();
if v["type"] == "prediction" {
println!("{} {}", v["symbol"], v["timeframe"]);
println!("{}", serde_json::to_string_pretty(&v).unwrap());
}
}
}
}Rust
Using Boost.Beast and OpenSSL (Modern C++17):
// Minimal example structure using Boost.Beast
#include <boost/beast/core.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <nlohmann/json.hpp>
namespace beast = boost::beast;
namespace websocket = beast::websocket;
namespace net = boost::asio;
namespace ssl = boost::asio::ssl;
using tcp = boost::asio::ip::tcp;
using json = nlohmann::json;
int main() {
try {
auto const host = "finai.synlabs.pro";
auto const port = "443";
auto const target = "/api/v1/markets/predictions?api_key=YOUR_KEY";
net::io_context ioc;
ssl::context ctx{ssl::context::tlsv12_client};
ctx.set_default_verify_paths();
tcp::resolver resolver{ioc};
websocket::stream<beast::ssl_stream<tcp::socket>> ws{ioc, ctx};
auto const results = resolver.resolve(host, port);
net::connect(beast::get_lowest_layer(ws), results);
ws.next_layer().handshake(ssl::stream_base::client);
ws.handshake(host, target);
beast::flat_buffer buffer;
ws.read(buffer); // auth response
json sub = {{"action","subscribe"},{"symbol","eurusd"},{"timeframe","1m"}};
ws.write(net::buffer(sub.dump()));
while(true) {
buffer.consume(buffer.size());
ws.read(buffer);
std::cout << beast::make_printable(buffer.data()) << std::endl;
}
} catch(std::exception const& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}C++ (Boost)
~140ms from candle close to prediction delivery. FINAI v2.0 inference path: 4–50 µs.
10+ ML classifiers per prediction. 50+ model configurations in v2.0.
70–88% historical accuracy across models and timeframes.
99.9% SLA guaranteed