Real-Time OHLCV
This guide shows how to integrate Vybe Network’s OHLCV price data with the TradingView Charting Library. Vybe provides a REST API for historical OHLC data and a WebSocket for real-time trades. We’ll cover how to fetch and serve this data to a TradingView widget, with both frontend (React/JS) and backend (Node.js/Koa) examples, and best practices for reliable operation.
Historical OHLCV via Vybe REST API
Vybe’s REST endpoint for token price data is:
GET <https://api.vybenetwork.xyz/price/{mintAddress}/token-ohlcv>
This returns historical OHLC (Open/High/Low/Close) data for a given token’s USD price, aggregated across major liquidity markets docs.vybenetwork.com. You must include query parameters:
- resolution: bar length (e.g.
1
,5
,60
for minutes, orD
for daily). - timeStart, timeEnd: Unix timestamps (seconds) defining the range.
- Optional limit/page for pagination (default returns up to limit bars) docs.vybenetwork.com
For example, in JavaScript you might fetch 1-minute bars for a token:
const mint = "So11111111111111111111111111111111111111112"; // token mint address
const resolution = 1; // 1-minute bars
const timeStart = 1689508800; // e.g. June 16, 2023 00:00:00 UTC
const timeEnd = 1689595200; // one day later
fetch(`https://api.vybenetwork.xyz/price/${mint}/token-ohlcv` +
`?resolution=${resolution}&timeStart=${timeStart}&timeEnd=${timeEnd}`, {
headers: { "X-API-Key": "YOUR_VYBE_API_KEY" }
})
.then(res => res.json())
.then(data => {
// data is an array of {time, open, high, low, close, volume} bars
console.log("OHLCV bars:", data);
});
Each bar in the response will include time
(Unix ms or s – confirm format), open
, high
, low
, close
, and volume
. You can then feed these bars into TradingView’s getBars
implementation or directly initialize the chart with historical data.
Real-Time Data via Vybe WebSocket
Vybe’s WebSocket provides real-time trades (and other streams) at the endpoint:
wss://api.vybenetwork.xyz/live
Upon connecting, you must send a configuration message to specify which streams/filters you want. For example, to subscribe to live trades for all tokens, you can send:
{
"type": "configure",
"filters": {
"trades": [], // empty array => receive ALL trade events
"transfers": [], // receive all transfers (if needed)
"oraclePrices": [] // receive all price oracles (if needed)
}
}
An empty array for a stream means “no filter” (i.e. all messages of that type) docs.vybenetwork.com. You can narrow the stream by adding filter objects in the array. For example:
{
"type": "configure",
"filters": {
"trades": [
{ "programId": "YourProgramId", "tokenMintAddress": "So111...1112" }
]
}
}
This configures the socket to only send trade events from the specified program or token. Filters can include programId
, marketId
, or tokenMintAddress
fields inside each trade filter object docs.vybenetwork.com.
Connecting and Receiving Trades
This integration follows TradingView’s official Streaming Implementation structure: TradingView Streaming Implementation Guide.
In code, you would connect and send the config message. For example, in Node.js (using the ws
library) or an environment that supports custom headers:
const WebSocket = require('ws');
const ws = new WebSocket("wss://api.vybenetwork.xyz/live", {
headers: { "X-API-Key": "YOUR_VYBE_API_KEY" }
});
ws.on("open", () => {
console.log("Connected to Vybe WebSocket");
// Subscribe to all trades for demonstration
ws.send(JSON.stringify({
type: "configure",
filters: { trades: [] }
}));
});
ws.on("message", data => {
const msg = JSON.parse(data);
if (msg.type === "trade") {
// msg will contain fields like price, size, timestamp, etc.
console.log("Trade event:", msg);
}
});
(For browser clients, you can append the API key as a query param or use a proxy, since standard WebSocket doesn’t allow custom headers easily.)
Once connected, you’ll receive a continuous stream of trade events (one per swap/trade) in real-time. Each message typically has a timestamp, price, size (volume), and identifiers. You will need to aggregate these trades into time buckets (candles) in your chosen interval.
Frontend (React) Example
Below is a conceptual frontend flow (e.g. in React) showing how to load historical OHLCV, connect to Vybe’s WebSocket, aggregate trades into candles, and update the TradingView chart via its subscribeBars
/onRealtimeCallback
API.
import React, { useEffect, useRef } from 'react';
import { widget } from 'path-to-tradingview-charting-library';
function TradingChart({ mintAddress }) {
const chartRef = useRef(null);
const wsRef = useRef(null);
const currentCandle = useRef(null);
const subscriberUID = useRef(null);
useEffect(() => {
// 1. Initialize TradingView widget
const tvWidget = new widget({
symbol: mintAddress,
interval: "1", // default resolution (1 minute)
container_id: "tv_chart",
datafeed: {
// datafeed must implement getBars and subscribeBars
// We show a simplified version below
onReady: cb => cb({ supports_search: false }),
searchSymbols: () => {},
resolveSymbol: (symbolName, onResolve) => {
onResolve({
name: symbolName,
timezone: "Etc/UTC",
// ... other symbol info ...
minmov: 1,
pricescale: 100,
has_intraday: true,
supported_resolutions: ["1", "5", "60", "D"],
});
},
getBars: (symbolInfo, resolution, {from, to, countBack}, onResult) => {
// Fetch historical OHLCV from Vybe REST
fetch(`https://api.vybenetwork.xyz/price/${symbolInfo.name}/token-ohlcv`
+ `?resolution=${resolution}&timeStart=${from}&timeEnd=${to}`, {
headers: { "X-API-Key": "YOUR_VYBE_API_KEY" }
})
.then(res => res.json())
.then(bars => onResult(bars, { noData: bars.length===0 }));
},
subscribeBars: (symbolInfo, resolution, onRealtimeCallback, uid) => {
// Save callbacks and start WS if first subscription
subscriberUID.current = uid;
if (!wsRef.current) {
// Connect to Vybe WebSocket for live trades
const ws = new WebSocket("wss://api.vybenetwork.xyz/live");
wsRef.current = ws;
ws.onopen = () => {
// Subscribe to trades for this token
ws.send(JSON.stringify({
type: "configure",
filters: { trades: [{ tokenMintAddress: symbolInfo.name }] }
}));
};
ws.onmessage = event => {
const msg = JSON.parse(event.data);
if (msg.type === "trade" && msg.tokenMintAddress === symbolInfo.name) {
handleTrade(msg, resolution, onRealtimeCallback);
}
};
}
},
unsubscribeBars: uid => {
// Optionally close WS when no subscribers left
// (omitted for brevity)
}
},
// ... other widget options ...
});
chartRef.current = tvWidget;
return () => {
// Cleanup on unmount
if (wsRef.current) wsRef.current.close();
if (chartRef.current) chartRef.current.remove();
};
}, [mintAddress]);
// Helper to aggregate trades into 1-minute candles
function handleTrade(trade, resolution, onRealtimeCallback) {
// Determine bar timestamp (in ms) aligned to interval
const intervalMs = parseInt(resolution) * 60 * 1000;
const t = Math.floor(trade.ts / intervalMs) * intervalMs;
if (!currentCandle.current || currentCandle.current.time < t) {
// Emit the previous candle if exists
if (currentCandle.current) {
onRealtimeCallback(currentCandle.current);
}
// Start a new candle
currentCandle.current = {
time: t,
open: trade.price,
high: trade.price,
low: trade.price,
close: trade.price,
volume: trade.size
};
} else {
// Update existing candle
let c = currentCandle.current;
c.high = Math.max(c.high, trade.price);
c.low = Math.min(c.low, trade.price);
c.close = trade.price;
c.volume += trade.size;
// Update the last bar
onRealtimeCallback(c);
}
}
return <div id="tv_chart" style={{width: "100%", height: "500px"}} />;
}
Key points in the frontend code:
- Historical Bars: In
getBars
, we call Vybe’s REST/token-ohlcv
API usingfetch
(with your API key header) and pass the returned bars to the chart viaonResult
. - WebSocket Connection: In
subscribeBars
, we open a single WebSocket (if not already open) towss://api.vybenetwork.xyz/live
. Upon connection, we send a configure message withfilters.trades
set to our token’s mintAddress. - Aggregating Trades: Each incoming
msg
of type"trade"
includes a price, size, and timestamp (msg.ts
). We bucket these trades into candles of the chosenresolution
(e.g. 1 minute). When a new time bucket is detected, we push the completed candle to TradingView usingonRealtimeCallback
. Updating the last bar or adding a new bar in this way follows TradingView’s datafeed patterntradingview.com. - Time Handling: All timestamps are converted to milliseconds UTC (TradingView expects bar times in Unix ms) and aligned to the interval boundaries.
The code above illustrates the logic. In a production app, you’ll want to manage subscriptions and cleanup carefully. Note that TradingView’s subscribeBars
callback expects you to call onRealtimeCallback(bar)
whenever you want to update the most recent bar or append a new one tradingview.com.
Backend (Node.js/Koa) Example
Below is a simplified Node.js example using Koa and ws
to fetch historical data and relay real-time updates to frontend clients via a WebSocket server.
// server.js
const Koa = require('koa');
const Router = require('@koa/router');
const axios = require('axios');
const WebSocket = require('ws');
const app = new Koa();
const router = new Router();
// REST endpoint: fetch OHLCV for a token
router.get('/api/ohlcv/:mint', async (ctx) => {
const mint = ctx.params.mint;
const res = await axios.get(`https://api.vybenetwork.xyz/price/${mint}/token-ohlcv`, {
params: {
resolution: 1, // e.g. 1-minute
timeStart: ctx.query.from,
timeEnd: ctx.query.to
},
headers: { "X-API-Key": "YOUR_VYBE_API_KEY" }
});
ctx.body = res.data; // Array of bars
});
app.use(router.routes());
// Create a WebSocket server to broadcast candles to clients
const wss = new WebSocket.Server({ port: 4000 });
wss.on('connection', ws => {
console.log("Client connected to candle relay");
});
// Connect to Vybe WebSocket for trades
const vybeWs = new WebSocket("wss://api.vybenetwork.xyz/live", {
headers: { "X-API-Key": "YOUR_VYBE_API_KEY" }
});
let currentCandle = null;
vybeWs.on('open', () => {
console.log("Connected to Vybe WS");
// Subscribe to all trades (or add filters as needed)
vybeWs.send(JSON.stringify({ type: "configure", filters: { trades: [] } }));
});
vybeWs.on('message', data => {
const msg = JSON.parse(data);
if (msg.type === "trade") {
// Aggregate trades into 1-minute candles
const intervalMs = 60 * 1000;
const t = Math.floor(msg.ts / intervalMs) * intervalMs;
if (!currentCandle || currentCandle.time < t) {
// Broadcast previous candle
if (currentCandle) {
const payload = JSON.stringify({ candle: currentCandle });
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(payload);
}
});
}
// Start a new candle
currentCandle = {
time: t,
open: msg.price,
high: msg.price,
low: msg.price,
close: msg.price,
volume: msg.size
};
} else {
// Update existing candle
currentCandle.high = Math.max(currentCandle.high, msg.price);
currentCandle.low = Math.min(currentCandle.low, msg.price);
currentCandle.close = msg.price;
currentCandle.volume += msg.size;
// (Optionally broadcast updates on each trade or throttle as needed)
}
}
});
vybeWs.on('close', () => {
console.log("Vybe WS closed. Attempting reconnect...");
setTimeout(() => vybeWs.connect(), 1000);
});
vybeWs.on('error', err => {
console.error("Vybe WS error:", err);
});
// Start Koa server
app.listen(3000, () => console.log("Koa server running on port 3000"));
Explanation:
- The Koa app exposes a GET route
/api/ohlcv/:mint
which calls Vybe’s REST API for historical bars (usingaxios
) and returns the JSON data to the client. - A WebSocket server (
wss
) listens for frontend clients (e.g. your React app) and will send them completed candle data. - We open a Vybe WS connection (
vybeWs
) and send the"configure"
message to subscribe to all trades. InvybeWs.on('message')
, each trade is used to update a running 1-minute candle (currentCandle
). When a time interval rolls over, we broadcast the completed candle to all connected WebSocket clients. - Note the simple reconnect logic: on
"close"
or"error"
, we attempt to reconnect after a delay. This ensures resilience.
On the frontend, you would connect to your own WebSocket server at ws://your-backend:4000
to receive these candle messages in real-time, instead of directly connecting to Vybe’s socket.
Best Practices
- WebSocket Filtering: Always apply filters to the Vybe WebSocket to reduce data volume. For example, specify the
tokenMintAddress
orprogramId
in thetrades
filter so you only receive relevant trades. An emptytrades
array returns all trades docs.vybenetwork.com, which may be excessive if you only need one token. - UTC Timestamps: Ensure all timestamps you use are in UTC. TradingView expects bar
time
in Unix milliseconds UTC. Convert Vybe’s seconds to ms and align to the correct boundary (e.g. start of the minute) before sending to the chart. - Throttling Updates: Don’t emit a new bar on every trade. Aggregate all trades within the bar interval and emit one update per bar (or only when the bar closes). This avoids flooding the chart with tiny updates. If you do update the last bar on each trade, avoid exceeding the browser/UI’s capacity – you can also throttle with a short timeout.
- Multiple Intervals: If you need multiple resolutions (1m, 5m, 1h, etc.), you have two options: request different resolutions via the REST API when loading history, and/or maintain separate aggregators on each interval on your WebSocket side. For example, you could aggregate trades into both 1-minute and 5-minute candles simultaneously and serve them to the respective chart resolutions.
- Reconnect Logic: Implement robust reconnect logic for both Vybe’s WebSocket and your own (if used). In the Node example above, we catch
close
/error
events and attempt to reconnect docs.vybenetwork.com. Similarly, handle socket closures in the browser by re-opening and re-sending theconfigure
message. On reconnect, you may need to re-fetch the latest historical data or carefully resume aggregation. - Data Gaps: Be prepared for gaps in data. If your REST call returns no bars (e.g. for a newly launched token or an empty period), tell TradingView by returning
noData: true
or an empty array. Conversely, ensure your RESTgetBars
returns the full requested range to avoid “holes” (as per TradingView’s guidelines, return at least 2 bars or pad with extra bars if needed) tradingview.com. - Timezones and Session: Specify the chart’s timezone as UTC (or as appropriate) in the symbol info. Vybe’s data is timestamped in UTC, and on-chain data is 24/7, so set
session: "24x7"
inresolveSymbol
. - Caching (if needed): The TradingView library may cache data. Use
onResetCacheNeededCallback
to handle cases where your history changes (e.g. if reconnecting and loading missed bars) – see TradingView docs. - API Keys & Rate Limits: Ensure your Vybe API key has WebSocket access. Monitor your usage and respect any rate limits. For high-frequency updates, a higher tier may be required.
By following this guide and examples, you can wire up Vybe’s real-time price data into TradingView’s Charting Library. Fetch historical candles via the REST endpoint docs.vybenetwork.com and push live-updates through subscribeBars
as new candles form tradingview.com. With proper filtering and error handling, your chart will stay in sync with Solana’s on-chain markets with minimal latency.
Updated 1 day ago