Skip to content
Velo

Channels

Live market data arrives through channels. Choose an indicator and a target, attach listeners, and open a feed with velo.watch().

PartPurpose
ChannelDescribes the indicator, target, and listeners for its updates.
FeedGroups channels into one subscription request.
WatcherManages the connections and the channels being subscribed to.

Building channels and feeds sends nothing. velo.watch() starts the connections and resolves to a watcher, while data arrives through each channel's listeners.

import { Velo } from 'velo-sdk';
 
const velo = new Velo({ apiKey: process.env.VELO_API_KEY! });
const product = {
  exchange: 'binance-futures',
  coin: 'BTC',
  product: 'BTCUSDT',
} as const;
 
const price = velo.channels.price(product).on({
  data: (row) => console.log(row.time, row.close_price),
  error: ({ reason }) => console.error('Price channel error:', reason),
});
 
const openInterest = velo.channels.openInterest({ coin: product.coin }, { metric: 'coins' }).on({
  data: (entries) => console.table(entries),
  error: ({ reason }) => console.error('Open interest channel error:', reason),
});
 
const feed = velo.channels.feed([price, openInterest]);
const watcher = await velo.watch(feed, {
  on: { error: (error) => console.error('Connection error:', error) },
});

Scope

A channel's target determines its scope:

ScopeTargetDescription
Single{ exchange, coin, product }Subscribes to one product on one exchange (e.g. BTCUSDT on Binance Futures).
Aggregated{ coin }Subscribes to a coin across exchanges, returning one entry per exchange (e.g. BTC on Binance Futures and Hyperliquid).

Use the catalog to find products. See the Channels reference for supported scopes and markets.

Listeners

Attach listeners with .on({ data, error }). A data listener is required before adding a channel to a feed; error is optional. The data is typed to the channel's indicator, target, and options.

Each indicator field is a number, or null when the server sends no value for it. Check for null before doing arithmetic with a field.

.on() returns a new channel with the listeners attached. Keep that returned value to unsubscribe it later.

Message metadata

The data listener receives (data, message): decoded data and a ChannelMessage with these fields:

FieldDescription
kindIdentifies the channel's decoder, such as price
channelThe wire name used for the subscription
timestampThe server's tick timestamp in Unix milliseconds, when present
dataThe same decoded data as the first argument
frameThe complete, unmodified server frame

Error handling

Subscription and decoding failures go to the channel's error listener. Connection failures go to the watcher's error listener. A channel error does not disconnect other channels.

Channel errors include the channel's wire name and a reason:

ReasonMeaning
rejectedThe server does not recognize the channel name. The channel receives no data until the next connection, which subscribes to it again.
decodeA frame could not be decoded and was skipped. The event also includes the decoder's error and the frame.
failingThree frames in a row could not be decoded. Further failures are not reported until a frame decodes again. The subscription stays open, and data resumes with the next frame that decodes.

Managing subscriptions

Use watcher.subscribe(channel) and watcher.unsubscribe(channel) to change channels while the feed is running. Keep the channel returned by .on() to unsubscribe it later:

const volume = velo.channels.volume(product).on({
  data: (row) => console.log(row.buy_dollar_volume, row.sell_dollar_volume),
  error: ({ reason }) => console.error('Volume channel error:', reason),
});
 
watcher.subscribe(volume);
 
// Later:
watcher.unsubscribe(volume);
watcher.close();

unsubscribe() requires the same channel object passed to the feed or subscribe(). A watcher must keep at least one channel; use close() to stop the last one.

Connections retry automatically by default. Reconnecting restores the current subscriptions but does not replay missed updates. See velo.watch() for connection options.

Understanding update timing

For built-in single channels, time is the start of the current minute in Unix milliseconds. Updates for the same product and minute share the same time.

Volume, trade counts, and liquidation counts and volumes accumulate within the minute and reset for the next one. Replace stored values with each update to avoid double-counting them.

Aggregated entries contain each exchange's latest values, without time or product. Exchanges can roll into a new minute at different times, so a message can contain values from different minutes. Use single channels when you need rows aligned by minute.