Skip to content

Commercial

Moving from TradingView or Highcharts

Six ways products embed a chart today, and what the move costs from each one.

Read this page as Markdown, or take the whole documentation in one file.

Find Your Integration, Not Your API

Almost every product that draws financial charts has arrived at one of six shapes. The API mapping is on the migration page; this page is about the shape you are in, because that is what decides how long the move takes and what is easy to miss.

Find the row that describes you.

Your shape Typical signs Effort
A widget in a trading terminal TradingView Advanced Charts in an iframe, one chart, saved layouts per user A week
A chart on every instrument page One chart per symbol, server-rendered pages, no drawings A day or two
A research or screening product Highcharts Stock, many small charts, comparisons, exports Two to four days
A portfolio tracker Performance lines, allocation pies, risk matrices Two to four days
A white-label platform you resell One codebase, many brokers or prop firms, your customers' branding A week, plus a licensing conversation
Charts in a dashboard Highcharts for everything, finance is one section A day per chart family

Everything below assumes one fact that catches everybody once: timestamps are milliseconds since the Unix epoch, in UTC. Not seconds, not business-day objects, not local dates.

A Widget in a Trading Terminal

You embed TradingView Advanced Charts, users draw on it, and you save their layouts, studies and drawings against their account so a chart follows them between devices.

What carries over as it is. Your datafeed. The resolveSymbol, getBars, subscribeBars and searchSymbols shapes are deliberately close, and tradingViewDatafeed wraps an existing one so you can run it unchanged on day one.

import { tradingViewWidget } from "@ortex-charts/migrate";

// Your existing widget options and your existing datafeed.
const shell = tradingViewWidget({
  container: "chart",
  symbol: "BTCUSD",
  interval: "60",
  datafeed: myTradingViewDatafeed,
  disabled_features: ["header_symbol_search"],
});

What you have to decide. Where saved state lives. Advanced Charts offers a save/load server contract and a chart storage service; ORTEX Charts has neither, on purpose. shell.getLayout() returns JSON and shell.setLayout(json) puts it back, so the store is the one you already have: a column on the user row, a document, a key in your own service.

const shell = createChartShell(el, {
  symbol, datafeed,
  layout: await api.loadChartLayout(userId),
  onLayoutChange: (layout) => void api.saveChartLayout(userId, layout),
  drawingsStore: {
    load: (sym) => api.loadDrawings(userId, sym),
    save: (sym, drawings) => api.saveDrawings(userId, sym, drawings),
  },
});

Drawings are stored per symbol rather than inside the layout, because a trend line on one instrument means nothing on another.

What will bite. Three things, in the order people hit them.

  • Callbacks became promises. getBars returns { bars, noMoreData } instead of calling onHistoryCallback.
  • There is no iframe. The chart is in your DOM, which is mostly a gift: your fonts, your CSS variables, your dev tools. It also means your CSS resets now apply to it. If your app sets * { box-sizing: content-box } or a global canvas rule, you will see it.
  • Feature flags became options. The disabled_features string list maps onto typed switches. fromTVWidgetOptions translates the common ones and tells you what it could not.

What you gain that matters here. No attribution requirement on a licensed build, and no restriction to services offered free of charge. The free Advanced Charts agreement requires the TradingView mark to stay visible and the service to be free to the public, which a subscription terminal is not.

A Chart on Every Instrument Page

One chart per token, ticker or fund. Users read it; few of them draw on it. The page is often server-rendered and the chart is the only interactive thing on it.

This is the fastest migration of the six, and the one where payload matters most: the chart is on the critical path of a page a search engine sends people to.

import { createFinancialChart } from "@ortex-charts/financial";

const chart = createFinancialChart(el, {
  data: bars,                       // straight from your API
  seriesType: "candlestick",
  volume: true,
  theme: "dark",
  tooltip: { mode: "hovered", template: "{name} {value}\n1d {value % value[-1]}" },
});

Drop the shell if you do not need it. createFinancialChart is the chart without the toolbar; a page with its own period buttons and its own symbol header does not need a second set. That is 59.0 KB rather than 89.9.

Server rendering. There is no server renderer, here or in the alternatives. Render the page, mount the chart on the client, and give the container a fixed height so the page does not jump when it appears.

Instrument pages are where the free plan usually stops applying. If the page carries advertising or sits behind any kind of subscription, it is a commercial use.

A Research or Screening Product

Highcharts Stock behind a screener: many charts, often small, frequently compared against each other, and usually exportable.

Start with the translator. It takes the options object you already have.

import { translateHighcharts } from "@ortex-charts/migrate";

const { options, data, unsupported } = translateHighcharts(myHighchartsOptions);
console.warn("did not carry over:", unsupported);   // read this list once, then delete the line
const chart = createFinancialChart(el, { ...options, data });

unsupported is the point of it. It names what has no equivalent rather than dropping it silently, so the port is a checklist.

Three things to plan for.

  • Comparisons. A Highcharts compare axis becomes a second series plus a percentage or indexed scale: rightPriceScale: { mode: "percentage" }, or "indexedTo100".
  • Exporting. There is no exporting server. downloadPNG(canvas) and rowsToCSV cover image and data export from the browser; a PDF report is your renderer's job.
  • Many small charts. If a chart is a cell in a results table, it should not be a chart at all: @ortex-charts/lite draws a sparkline in 3.3 KB with no engine behind it.

A Portfolio Tracker

Performance lines, allocation by sector or asset, contribution and risk matrices. Only some of what you draw is a price chart.

The financial chart covers the performance line. The rest is @ortex-charts/viz, which shares the engine, the theme and the maths:

What you draw now What draws it here
Highcharts line or area with a percentage axis createFinancialChart with rightPriceScale: { mode: "percentage" }
Pie or donut of allocation createPieChart
Heatmap of returns by month or sector createHeatmap
Treemap of holdings createTreemap
Bar chart of contribution createCategoryChart
Scatter of risk against return createXYChart with kind: "scatter"
Sankey of flows in and out createSankeyChart

translateHighchartsCategory and translateHighchartsPie take the category and pie options directly, so the non-price charts move with the same checklist as the price ones.

Watch the benchmark line. A tracker almost always draws the portfolio against an index. Two series on a percentage scale, both anchored at the same first bar, is the correct shape; align: "exact" keeps a benchmark with different trading days on the portfolio's timeline rather than inventing bars for it.

A White-Label Platform You Resell

You build one trading platform and sell it to brokers or prop firms, each with their own branding. The chart inside it is TradingView, and every one of your customers inherits that relationship.

This is the shape where the licence, not the API, is the reason to move.

  • The mark is removable. A White-label licence removes the ORTEX mark for one product on one domain. Your customers see your platform, not ours.
  • One licence, many deployments. If you ship the same product to fifty brokers, that is an OEM conversation rather than fifty licences: redistribution rights, a source drop, and a price that reflects the deployment rather than the seat count.
  • Your customers do not need their own agreement. Under OEM the chart is part of your product.

Technically it is the terminal migration above, with one addition: whatever your branding system already does per customer, the chart follows.

createChartShell(el, {
  symbol, datafeed,
  theme: brokerTheme,                    // your customer's palette, as tokens
  branding: { visible: false },          // with a white-label or OEM key
  toolbar: { symbol: false },            // their header already names the instrument
});

Set the licence key once at start-up with setLicenseKey, not per chart.

Charts in a Dashboard

Highcharts draws everything you have, and the financial charts are one section among many. You are not replacing Highcharts everywhere; you are replacing it where it is weakest.

Move the price and time-series charts, keep the rest, and let the two coexist. Nothing here registers globals or touches the DOM outside its own container. When the financial section is done, the rest of @ortex-charts/viz is there if you want it, and the two libraries can run on one page for as long as you like.

The one thing worth doing on day one is the theme: pull your palette into theme tokens so the new charts match the old ones while both are on screen.

What Every Migration Should Check Before It Ships

  • Timestamps in milliseconds, UTC. Every conversion bug traces back here.
  • The first paint on your slowest page. That is the number that justified the move.
  • A symbol with a short history. Week-over-week comparisons and indicators need enough bars; make sure they degrade rather than throw.
  • A symbol with gaps. Halts, holidays and thin instruments.
  • Your saved layouts. Load an old one, change something, save it, reload.
  • The licence key on the deployed origin, not just on localhost.
  • The mark, if you expect it to be gone. It goes when the key says it can, and not before.