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

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

Timeframes

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

πŸ“¨ 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",
  "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

Prediction Classes

Class Meaning Action
0 DOWN Price will decrease β†’ SELL
1 UP Price 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:

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

πŸ•―οΈ 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 apply ROC to Heikin-Ashi closes:

\[ HA\_ROC(n)_t = \frac{HA_{Close_t} - HA_{Close_{t-n}}}{HA_{Close_{t-n}}} \times 100\% \]

🎯 Prediction Targets

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

🏷️ Model Naming Convention

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?

πŸ“‹ Model Catalog

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?

πŸ“Š How to Interpret Predictions

For _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

For _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])

Examples:

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)

πŸ‘¨β€πŸ’» Code Examples

πŸͺŸ Windows / 🐧 Linux / 🍎 Mac
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:
        # 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
πŸͺŸ Windows / 🐧 Linux / 🍎 Mac
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
[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();

    // 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
🐧 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 <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)

πŸ“ˆ Performance

⚑ Latency

~140ms from candle close to prediction delivery

🧠 Models

10 ML classifiers per prediction

πŸ“Š Accuracy

70-88% historical accuracy across models

πŸ”’ Uptime

99.9% SLA guaranteed