mmflow Charts
Data feeds
Feed the chart engine with mmflow live data or a host-owned DataFeed that supplies candles, trade updates, and optional order-flow payloads.
import { createDataFeed, type DataFeed } from "@mmflow/charts";
export const customFeed: DataFeed = createDataFeed({
async fetchCandles({ symbol, resolution, from, to }) {
const res = await fetch(
`/api/candles?symbol=${symbol}&resolution=${resolution}&from=${from}&to=${to}`,
);
return res.json();
},
subscribe(symbol, resolution, onUpdate) {
const source = new EventSource(
`/api/trades?symbol=${symbol}&resolution=${resolution}`,
);
source.addEventListener("trade", (event) => {
onUpdate(JSON.parse(event.data));
});
return () => source.close();
},
});Required contract
The current DataFeed type requires candle history and a live subscription function. Optional methods unlock richer layers when your source provides those payloads.
fetchCandles(range)
Returns Candle rows with time, open, high, low, close, and volume for the requested symbol and resolution.
subscribe(symbol, resolution, onUpdate)
Streams FeedUpdate ticks and returns an unsubscribe function for cleanup.
fetchCandlesColumns(range)
Optional typed-array OHLCV path for hosts that already store columnar data.
fetchHistoryCandles(params)
SDK helper for the public /api/v1/history/candles route. Use fetchHistoryCandlesResponse when you need archive coverage metadata.
fetchHistoryEvents(params)
SDK helper for /api/v1/history/events replay overlays. It returns archive-backed whale, inferred liquidation, and funding-change events with source fallback when available.
fetchHistoryTrades(params)
SDK helper for /api/v1/history/trades. It returns bounded recent HL trade rows when archive or recent-source coverage exists.
fetchHistoryFootprint(params)
SDK helper for /api/v1/history/footprint. It returns bounded trade-derived footprint bars and trade coverage metadata.
fetchHistoryOrderbookSnapshots(params)
SDK helper for /api/v1/history/orderbook-snapshots. It returns sparse archived/current-window L2 snapshots without claiming continuous book reconstruction.
fetchHistoryStatus(params)
SDK helper for /api/v1/history/status. It returns cheap read-only layer health, freshness, archive/source, and coverage summaries.
Historical windows
Replay and history surfaces use bounded from/towindows instead of the legacy hours-based HL proxy. The v1 route reads from archive coverage first where available, falls back only to bounded source coverage, and reports local/dev KV absence throughmeta.archive.available = false. The playground passes abort signals through these SDK helpers so changing replay setup can cancel stale layer requests without affecting successful layers. Use fetchHistoryStatusResponse when you need freshness and source-quality summaries without loading a full replay window.
import {
fetchHistoryCandles,
fetchHistoryCandlesResponse,
fetchHistoryEvents,
fetchHistoryEventsResponse,
fetchHistoryFootprint,
fetchHistoryFootprintResponse,
fetchHistoryOrderbookSnapshots,
fetchHistoryOrderbookSnapshotsResponse,
fetchHistoryStatus,
fetchHistoryStatusResponse,
fetchHistoryTrades,
fetchHistoryTradesResponse,
} from "@mmflow/sdk";
const candles = await fetchHistoryCandles({
symbol: "BTC",
venue: "hl",
resolution: "1m",
from: 1730000000000,
to: 1730003600000,
});
const response = await fetchHistoryCandlesResponse({
symbol: "BTC",
venue: "hl",
resolution: "1m",
from: 1730000000000,
to: 1730003600000,
});
const events = await fetchHistoryEvents({
symbol: "BTC",
venue: "hl",
from: 1730000000000,
to: 1730003600000,
types: ["whale", "liquidation", "funding"],
});
const eventResponse = await fetchHistoryEventsResponse({
symbol: "BTC",
venue: "hl",
from: 1730000000000,
to: 1730003600000,
types: ["whale"],
});
const trades = await fetchHistoryTrades({
symbol: "BTC",
venue: "hl",
from: 1730000000000,
to: 1730003600000,
minUsd: 100000,
});
const tradeResponse = await fetchHistoryTradesResponse({
symbol: "BTC",
venue: "hl",
limit: 1000,
});
const footprintBars = await fetchHistoryFootprint({
symbol: "BTC",
venue: "hl",
resolution: "1m",
from: 1730000000000,
to: 1730003600000,
priceStep: "auto",
});
const footprintResponse = await fetchHistoryFootprintResponse({
symbol: "BTC",
venue: "hl",
resolution: "5m",
});
const orderbookSnapshots = await fetchHistoryOrderbookSnapshots({
symbol: "BTC",
venue: "hl",
depth: 50,
});
const orderbookResponse = await fetchHistoryOrderbookSnapshotsResponse({
symbol: "BTC",
venue: "hl",
depth: 50,
limit: 100,
});
const statusLayers = await fetchHistoryStatus({
symbol: "BTC",
venue: "hl",
layers: ["candles", "events", "trades", "footprint", "orderbook"],
});
const statusResponse = await fetchHistoryStatusResponse({
symbol: "BTC",
venue: "hl",
});
console.log(
response.meta.historySource,
events.length,
eventResponse.meta.historySource,
trades.length,
tradeResponse.meta.historySource,
footprintBars.length,
footprintResponse.meta.trades?.historySource,
orderbookSnapshots.length,
orderbookResponse.meta.historySource,
statusLayers.length,
statusResponse.data.overall,
);Optional order-flow feed methods
These methods are confirmed on the DataFeed interface, but your feed only needs to implement the ones its product uses.
customFeed.fetchFootprint = async (symbol, opts) => {
return fetch("/api/footprint").then((res) => res.json());
};
customFeed.fetchVolumeProfile = async (symbol, opts) => {
return fetch("/api/profile").then((res) => res.json());
};
customFeed.subscribeOrderBook = (symbol, onBook) => {
const socket = new WebSocket(`wss://example.com/depth?symbol=${symbol}`);
socket.onmessage = (event) => onBook(JSON.parse(event.data));
return () => socket.close();
};Repo starter template
Copy frontend/examples/custom-data-feed for an offline, deterministic createDataFeed template that uses no private repo imports or network calls.
Continue building
Move through the chart SDK docs without leaving the developer flow.