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",
"last_close": 1.05234,
"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 apply ROC to Heikin-Ashi closes:
\[ HA\_ROC(n)_t = \frac{HA_{Close_t} - HA_{Close_{t-n}}}{HA_{Close_{t-n}}} \times 100\% \]
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 (1, 2, or 3 bars into the future) |
| 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 current ROC(1)? |
roc2_step1_diff |
ROC(2) | +1 bar | Diff | Will ROC(2) on next bar be greater than current ROC(2)? |
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) in 2 bars be greater than current ROC(2)? |
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 current? |
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) in 2 bars be greater than current? |
ha_roc1_step2_updwn |
HA-ROC(1) | +2 bars | Sign | Will HA-ROC(1) be positive or negative 2 bars from now? |
ha_roc1_step3_diff |
HA-ROC(1) | +3 bars | Diff | Will HA-ROC(1) in 3 bars be greater than current? |
_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:
β On the NEXT bar, ROC(2) will be positive
β Close[t+1] > Close[t-1] β Bullish signal
roc2_step1_diff = 1 means:
β ROC(2) on NEXT bar will be greater than current ROC(2)
β Examples:
β’ 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:
# Subscribe to ONE symbol (separate message for each)
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():
# 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()
# Print raw JSON
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();
// Wait for Auth Success
if let Some(msg) = read.next().await {
println!("Auth: {}", msg.unwrap());
}
// Subscribe
let subscribe_msg = json!({
"action": "subscribe",
"symbol": "eurusd",
"timeframe": "1m"
});
write.send(subscribe_msg.to_string().into()).await.unwrap();
// Listen
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 <iostream>
#include <string>
#include <nlohmann/json.hpp> // Requires nlohmann/json
namespace beast = boost::beast;
namespace http = beast::http;
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> 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);
// Auth response
beast::flat_buffer buffer;
ws.read(buffer);
std::cout << "Auth: " << beast::make_printable(buffer.data()) << std::endl;
// Subscribe
json sub = {
{"action", "subscribe"},
{"symbol", "eurusd"},
{"timeframe", "1m"}
};
ws.write(net::buffer(sub.dump()));
// Loop
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
10 ML classifiers per prediction
70-88% historical accuracy across models
99.9% SLA guaranteed