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.

Real-time ML Predictions for Financial Markets

WebSocket API v1

Connection

We provide two separate WebSocket endpoints on the same domain based on asset type:

1. Forex, Commodities, Stocks / ETFs

WSS wss://finai.synlabs.pro/api/v1/markets/predictions

Symbols: eurusd, audusd, xauusd, spy

2. Cryptocurrencies

WSS wss://finai.synlabs.pro/api/v1/crypto/predictions

Symbols: btcusdt, ethusdt

Authentication

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

Success Response

{
  "type": "auth_success",
  "user_id": 1,
  "username": "your_username",
  "max_symbols": 10
}Response

Available Symbols

All Symbols

SymbolTypeEndpointDescription
eurusdForex/api/v1/marketsEuro / US Dollar
audusdForex/api/v1/marketsAustralian Dollar / US Dollar
xauusdCommodity/api/v1/marketsGold / US Dollar
spyETF/api/v1/marketsS&P 500 ETF
btcusdtCrypto/api/v1/cryptoBitcoin / Tether
ethusdtCrypto/api/v1/cryptoEthereum / Tether

Timeframes

All timeframes are supported on both endpoints:

ValueDescription
1m1 Minute
5m5 Minutes
15m15 Minutes
1h1 Hour
4h4 Hours
1d1 Day

Subscribe

Request

{
  "action": "subscribe",
  "symbol": "eurusd",
  "timeframe": "1m"
}Request

Response

{
  "type": "subscribed",
  "symbol": "eurusd",
  "timeframe": "1m",
  "active_subscriptions": 1,
  "max_symbols": 10
}Response

Prediction Message

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

Prediction Classes

ClassMeaningAction
0DOWNPrice will decrease → SELL
1UPPrice will increase → BUY

ML Models

Mathematical Foundation: Rate of Change (ROC)

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:

  • \(Close_t\) — closing price of the current bar
  • \(Close_{t-n}\) — closing price \(n\) bars ago
  • \(n\) — lookback period (number of bars)

ROC Periods Used:

NotationPeriodDescription
ROC(1)1 barShort-term momentum: price change from previous bar
ROC(2)2 barsMedium-term momentum: price change over 2 bars
ROC(3)3 barsExtended momentum: price change over 3 bars

Heikin-Ashi Smoothed Candles

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.

Prediction Targets

Each model predicts one of two target types:

Target TypeSuffixPredictionTrading 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} \]

Model Naming Convention

Each model name follows the pattern: {indicator}_{horizon}_{target}

ComponentValuesMeaning
Indicatorroc1, roc2, roc3, ha_roc1Which ROC indicator we predict (period: 1, 2, or 3 bars)
Horizonstep1, step2, step3How many bars ahead we predict
Targetdiff, updwndiff = will ROC increase or decrease?, updwn = will ROC be positive or negative?

Model Catalog

10 proprietary ML classifiers. Each predicts a specific ROC indicator behavior on a future bar:

ModelIndicatorHorizonTargetDescription
roc1_step1_diffROC(1)+1 barDiffWill ROC(1) on next bar be greater than ROC(1) on current bar?
roc2_step1_diffROC(2)+1 barDiffWill ROC(2) on next bar be greater than ROC(2) on current bar?
roc2_step1_updwnROC(2)+1 barSignWill ROC(2) be positive or negative on next bar?
roc2_step2_diffROC(2)+2 barsDiffWill ROC(2) at t+2 be greater than ROC(2) at t+1?
roc3_updwnROC(3)+1 barSignWill ROC(3) be positive or negative on next bar?
ha_roc1_step1_diffHA-ROC(1)+1 barDiffWill HA-ROC(1) on next bar be greater than HA-ROC(1) on current bar?
ha_roc1_step1_updwnHA-ROC(1)+1 barSignWill HA-ROC(1) be positive or negative on next bar?
ha_roc1_step2_diffHA-ROC(1)+2 barsDiffWill HA-ROC(1) at t+2 be greater than HA-ROC(1) at t+1?
ha_roc1_step2_updwnHA-ROC(1)+2 barsSignWill HA-ROC(1) be positive or negative at t+2?
ha_roc1_step3_diffHA-ROC(1)+3 barsDiffWill HA-ROC(1) at t+3 be greater than HA-ROC(1) at t+2?

How to Interpret Predictions

For _updwn models (Sign prediction):

Predicts whether the ROC indicator will be positive or negative on the future bar:

ClassMeaningInterpretation
1ROC > 0 (positive)Price will be HIGHER than n bars ago → Bullish
0ROC < 0 (negative)Price will be LOWER than n bars ago → Bearish

For _diff models (Difference prediction):

Predicts whether the ROC value will INCREASE or DECREASE compared to current bar:

ClassMeaningInterpretation
1ROC will increaseMomentum accelerating (ROC[future] > ROC[current])
0ROC will decreaseMomentum decelerating (ROC[future] < ROC[current])

Examples:

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)

Code Examples

Windows / Linux / macOS
pip install websockets
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
Windows / Linux / macOS
npm install ws
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
Cargo.toml Dependencies
tokio = { version = "1", features = ["full"] } tokio-tungstenite = { version = "0.20", features = ["native-tls"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" futures-util = "0.3" url = "2.4"
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
Linux (Debian/Ubuntu)
sudo apt install libboost-all-dev libssl-dev
macOS (Homebrew)
brew install boost openssl
Windows (vcpkg)
vcpkg install boost-beast openssl

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)

Performance

Latency

~140ms from candle close to prediction delivery. FINAI v2.0 inference path: 4–50 µs.

Models

10+ ML classifiers per prediction. 50+ model configurations in v2.0.

Accuracy

70–88% historical accuracy across models and timeframes.

Uptime

99.9% SLA guaranteed