Skip to content

Instantly share code, notes, and snippets.

@bradens
Created October 28, 2025 17:12
Show Gist options
  • Select an option

  • Save bradens/bfe449f8ea88fca8a1952cfe242b5e21 to your computer and use it in GitHub Desktop.

Select an option

Save bradens/bfe449f8ea88fca8a1952cfe242b5e21 to your computer and use it in GitHub Desktop.
Codex Token bars Datafeed Example.
/**
* This an example datafeed.ts for the tradingview advanced charting library.
* It is not intended to be copy/pasted into your project, but rather used as a reference for how to implement the datafeed.
*/
// Import these types from your charting_library folder
import type {
HistoryCallback,
IBasicDataFeed,
LibrarySymbolInfo,
ResolutionString,
ResolveCallback,
SubscribeBarsCallback,
} from "charting_library";
import { CleanupFunction, Codex } from "@codex-data/sdk";
const sdk = new Codex(process.env.CODEX_API_KEY || "");
// Define the bar type TradingView expects
type TradingViewBar = {
time: number; // TradingView expects milliseconds
open: number;
high: number;
low: number;
close: number;
volume?: number;
}
const subs: Record<string, CleanupFunction> = {};
/**
*
* Call this from an effect whereever you are instantiating the trading view chart
*
* ex: useEffect(() => {
* const datafeed = createTradingViewDatafeed({
* fetchTokenDetails,
* fetchBars,
* onSubscriptionChange: setCurrentSubscription,
* prefetchedTimeRange,
* });
*
*
* @returns tradingview datafeed implementation
*/
export const createTradingViewDatafeed = (): IBasicDataFeed => {
return {
// This is used to resolve the symbol for price scales / and names in the top left.
resolveSymbol: async (
symbolName: string, // This depends on how you are setting the symbol in your charting library, but lets assume it's a tokenId:networkId string, to match Codex IDs
onSymbolResolvedCallback: ResolveCallback
) => {
// Default/fallback symbol info
let tokenDisplay = symbolName;
let tokenDescription = symbolName;
const [address, networkIdString] = symbolName.split(":");
const networkId = parseInt(networkIdString);
try {
const data = await sdk.queries.token({ input: { networkId: networkId, address } });
tokenDisplay = data.token.symbol || data.token.name || symbolName;
tokenDescription = data.token.name || data.token.symbol || symbolName;
} catch (error: unknown) {
console.error("Error resolving symbol", error);
return
}
const symbolInfo: LibrarySymbolInfo = {
ticker: symbolName, // Keep original ticker
name: tokenDisplay, // Use fetched symbol/name
description: tokenDescription, // Use fetched name/symbol
type: "crypto",
session: "24x7",
timezone: "Etc/UTC",
exchange: "Codex",
listed_exchange: "Codex",
minmov: 1,
minmove2: 1,
pricescale: 100000000, // Adjust these values to change the chart axes.
has_intraday: true,
has_weekly_and_monthly: true,
has_seconds: true,
volume_precision: 2,
data_status: "streaming",
supported_resolutions: [
"1S",
"5S",
"15S",
"30S",
"1",
"5",
"15",
"30",
"60",
"1D",
] as ResolutionString[],
format: "price",
};
setTimeout(() => onSymbolResolvedCallback(symbolInfo), 0);
},
getBars: async (
symbolInfo: LibrarySymbolInfo,
resolution: ResolutionString,
periodParams: {
from: number;
to: number;
countBack?: number;
},
onHistoryCallback: HistoryCallback,
_onErrorCallback: (reason: string) => void
) => {
const { from, to } = periodParams;
const adjustedTo =
to > Math.floor(Date.now() / 1000) ? Math.floor(Date.now() / 1000) : to;
// For the first data request, use server-calculated timestamps if available
// This ensures cache hit with prefetched data
const queryFrom = from;
const queryTo = adjustedTo;
const queryResolution = resolution.toString();
try {
const data = await sdk.queries.getBars({
symbol: symbolInfo.ticker,
from: queryFrom,
to: queryTo,
resolution: queryResolution,
removeEmptyBars: true,
removeLeadingNullValues: true,
});
const barsData = data?.getBars;
let tradingViewBars: TradingViewBar[] = [];
if (barsData?.t) {
tradingViewBars = barsData.t
.map((time: number, index: number) => {
const open = barsData.o?.[index];
const high = barsData.h?.[index];
const low = barsData.l?.[index];
const close = barsData.c?.[index];
const volume = barsData.volume?.[index];
if (
open == null ||
high == null ||
low == null ||
close == null
) {
return null;
}
// Create the bar object explicitly matching TradingViewBar
const bar: TradingViewBar = {
time: time * 1000,
open: open,
high: high,
low: low,
close: close,
};
// Add volume only if it exists, convert string to number
if (volume != null) {
bar.volume = parseFloat(volume);
}
return bar;
})
.filter(Boolean) as TradingViewBar[]; // Add type assertion
onHistoryCallback(tradingViewBars, {
noData: tradingViewBars.length === 0,
});
} else {
onHistoryCallback([], { noData: true });
}
} catch (error) {
_onErrorCallback(error instanceof Error ? error.message : "Unknown error");
}
},
subscribeBars: async (
symbolInfo: LibrarySymbolInfo,
resolution: ResolutionString,
onRealtimeCallback: SubscribeBarsCallback,
subscriberUID: string
) => {
const [address, networkIdString] = symbolInfo.ticker.split(":");
const networkId = parseInt(networkIdString);
subs[subscriberUID] = await sdk.subscriptions.onTokenBarsUpdated({
tokenId: address,
networkId: networkId,
}, {
next: (value) => {
// FIXME: Currently this just reads from the 1m resolution, and reading from multiple resolutions is left out for brevity.
const bar: TradingViewBar = {
time: value.data?.onTokenBarsUpdated?.timestamp ?? 0 * 1000, // TradingView expects milliseconds
open: value.data?.onTokenBarsUpdated?.aggregates?.r1?.usd?.o ?? 0,
high: value.data?.onTokenBarsUpdated?.aggregates?.r1?.usd?.h ?? 0,
low: value.data?.onTokenBarsUpdated?.aggregates?.r1?.usd?.l ?? 0,
close: value.data?.onTokenBarsUpdated?.aggregates?.r1?.usd?.c ?? 0,
volume: value.data?.onTokenBarsUpdated?.aggregates?.r1?.usd?.volume ? parseFloat(value.data?.onTokenBarsUpdated?.aggregates?.r1?.usd?.volume) : undefined,
}
onRealtimeCallback(bar)
},
error: (error) => {
console.error("Error subscribing to token bars", error);
},
complete: () => console.error("Subscription completed"),
});
},
unsubscribeBars: (_subscriberUID: string) => {
subs[_subscriberUID]();
},
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment