Mobula pair data API allows you to fetch real-time and historical price data for any on-chain pair. You can use this data to feed Trading View charting library.

What you’ll need

  1. Access to trading view codebase repo (need manual approval from Trading View)
  2. An API key from the Dashboard (only for production use, you can use the API without an API key in development mode)
  3. A react app (or any other framework) to display the chart

Walkthrough

1

Identify your asset

Pick your asset symbol, name or address. If it is a symbol/name, make sure to check case sensitivity and to respect the asset name as listed on Mobula curated token list (explorable here). If it is an address, make sure to check the blockchain supported (check here for the full list) and to respect the blockchain ID format.

You can also use pair address directly (if using asset, we will route to the largest liquidity pool for this asset).

2

Setup Chart Component

Let’s first setup the main chart component - you must host the trading view lib files inside the public folder of your app, in our example, we host them at static/charting_library.


const ChartBox = ({
baseAsset,
mobile = false,
custom_css_url = "../themed.css",
extraCss,
}: ChartBoxProps) => {
const ref = useRef<HTMLDivElement>(null);
const { resolvedTheme } = useTheme();
const isWhiteMode = resolvedTheme === "light";

useEffect(() => {
if (!baseAsset) return () => {};

let freshWidget: IChartingLibraryWidget | null = null;
import("../../../../../../../../public/static/charting_library").then(
  ({ widget: Widget }) => {
    if (!ref.current) return;

    freshWidget = new Widget({
      // Datafeed
      datafeed: Datafeed(baseAsset),
      symbol: baseAsset?.symbol + "/USD",
      interval: "60" as ResolutionString,

      // Settings
      container: ref.current,
      container_id: ref.current.id,
      library_path: "/static/charting_library/",

      // UI & Behavior
      locale: "en",
      fullscreen: false,
      disabled_features: [
        "header_saveload",
        "header_symbol_search",
        ...(mobile ? ["left_toolbar"] : []),
      ],
      timezone: Intl.DateTimeFormat().resolvedOptions()
        .timeZone as Timezone,
      autosize: true,

      // Theme
      theme: isWhiteMode ? "Light" : "Dark",
      // TO DO: Overrites don't work rn.
      overrides: overrides(isWhiteMode),
      custom_css_url,
    });
  }
);

return () => {
  freshWidget?.remove();
};

}, [baseAsset, custom_css_url, mobile]);

return (

<div ref={ref}></div>
); };

3

Fetch Mobula Historical Data API

We can then implement the fetching logic to get the historical data from Mobula API. We will use the market/history/pair endpoint to fetch the historical data for the last 300 datapoints of our resolution.


export const supportedResolutions = [
  "1",
  "5",
  "15",
  "30",
  "60",
  "120",
  "240",
  "24H",
  "7D",
  "30D",
];

const lastBarsCache = new Map();

export const Datafeed = (baseAsset: Asset) => ({
  onReady: (callback: Function) => {
    callback({ supported_resolutions: supportedResolutions });
  },
  resolveSymbol: (symbolName: string, onResolve: Function) => {
    const params = {
      name: symbolName,
      description: "",
      type: "crypto",
      session: "24x7",
      ticker: symbolName,
      minmov: 1,
      pricescale: Math.min(
        10 ** String(Math.round(10000 / baseAsset.price)).length,
        10000000000000000
      ),
      has_intraday: true,
      intraday_multipliers: ["1", "15", "30", "60"],
      supported_resolution: supportedResolutions,
      volume_precision: 8,
      data_status: "streaming",
    };
    onResolve(params);
  },
  getBars: async (
    symbolInfo,
    resolution: string,
    periodParams,
    onResult: Function
  ) => {
    const response = await GET(
      "/api/1/market/history/pair",
      {
        asset: baseAsset.contracts[0],
        blockchain: baseAsset.blockchains[0],
        from: periodParams.from * 1000,
        to: periodParams.to * 1000,
        amount: periodParams.countBack,
        usd: true,
        period: resolution,
      },
      false,
      {
        headers: { Authorization: "YOUR-API-KEY" },
      }
    );
    const data = await response.json();

    onResult(data.data, {
      noData: data.data.length !== periodParams.countBack,
    });

    if (periodParams.firstDataRequest) {
      lastBarsCache.set(baseAsset.name, data.data[data.data.length - 1]);
    }
  },
  searchSymbols: () => {},
  subscribeBars: () => {},
  unsubscribeBars: () => {},
  getMarks: () => ({}),
  getTimeScaleMarks: () => ({}),
  getServerTime: () => ({}),
});
4

Fetch Mobula Real Time Data API

We can then implement the fetching logic to get the real time data from Mobula API. We will use the pair WSS endpoint to fetch the real time updates & trades for our pair.

subscribeBars: (
    symbolInfo,
    resolution,
    onRealtimeCallback,
    subscriberUID,
    onResetCacheNeededCallback
  ) => {
    console.log("Subscribinnnng");
    const socket = new WebSocket(
      process.env.NEXT_PUBLIC_PRICE_WSS_ENDPOINT as string
    );

    socket.addEventListener("open", () => {
      socket.send(
        JSON.stringify({
          type: "pair",
          authorization: process.env.NEXT_PUBLIC_PRICE_KEY,
          payload: {
            address: "0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc", // baseAsset.contracts[0],
            blockchain: baseAsset.blockchains[0],
            interval: 5,
          },
        })
      );
    });

    socket.addEventListener("message", (event) => {
      const {data} = JSON.parse(event.data);
      const { priceUSD: price, date: timestamp } = data;

      const lastDailyBar = lastBarsCache.get(baseAsset.name);
      const nextDailyBarTime = getNextBarTime(resolution, lastDailyBar.time);
      let bar: Bar;

      if (timestamp >= nextDailyBarTime) {
        bar = {
          time: nextDailyBarTime,
          open: price,
          high: price,
          low: price,
          close: price,
        };
      } else {
        bar = {
          ...lastDailyBar,
          high: Math.max(lastDailyBar.high, price),
          low: Math.min(lastDailyBar.low, price),
          close: price,
        };
      }

      console.log("BAR", bar, data);

      onRealtimeCallback(bar);
    });

    sockets.set(baseAsset.name, socket);
  },
  unsubscribeBars: (subscriberUID) => {
    sockets.get(baseAsset.name).close();
  }

We also have to implement the getNextBarTime function to get the next bar time for our resolution.

export const getNextBarTime = (resolution: string, time: number) => {
  const date = new Date(time * 1000);
  const utcDate = new Date(
    Date.UTC(
      date.getFullYear(),
      date.getMonth(),
      date.getDate(),
      date.getHours(),
      date.getMinutes()
    )
  );

  switch (resolution) {
    case "1":
    case "3":
    case "5":
    case "15":
    case "30":
      utcDate.setMinutes(utcDate.getMinutes() + 1);
      break;
    case "60":
    case "120":
    case "240":
    case "360":
    case "720":
    case "D":
      utcDate.setHours(utcDate.getHours() + 1);
      break;
    case "W":
      utcDate.setDate(utcDate.getDate() + 7);
      break;
    case "M":
      utcDate.setMonth(utcDate.getMonth() + 1);
      break;
  }

  return Math.floor(utcDate.getTime() / 1000);
};

Need help?

Can’t find what you’re looking for? Reach out to us, response times < 1h.