# Moving from TradingView

> One real Charting Library widget ported end to end: the drop-in, what it ignores, and what onChartReady becomes.

Section: Commercial. Source: https://ortexcharts.com/docs/from-tradingview

## The Widget Every Terminal Has

This page ports one real TradingView Charting Library integration: a `new TradingView.widget`
call with a theme, feature flags, overrides and a save/load service, a JS-API datafeed with
callbacks and timescale marks, and an `onChartReady` block that adds two studies and a shape.
The [API mapping](https://ortexcharts.com/docs/migration#from-tradingview-advanced-charts) is the table to keep open
beside it; this page is the port itself.

```ts
const widget = new TradingView.widget({
  container: "chart",
  library_path: "/charting_library/",
  symbol: "IBM",
  interval: "D",
  datafeed,                                   // onReady, searchSymbols, resolveSymbol, getBars, subscribeBars, getTimescaleMarks
  locale: "en",
  theme: "Dark",
  timezone: "America/New_York",
  autosize: true,
  disabled_features: ["header_symbol_search", "header_compare", "use_localstorage_for_settings", "header_saveload"],
  enabled_features: ["study_templates", "hide_left_toolbar_by_default"],
  overrides: { "mainSeriesProperties.candleStyle.upColor": "#26a69a", "mainSeriesProperties.candleStyle.downColor": "#ef5350", "paneProperties.background": "#131722" },
  studies_overrides: { "volume.volume.color.0": "#ef5350", "volume.volume.color.1": "#26a69a" },
  charts_storage_url: "https://saveload.example.com",
  charts_storage_api_version: "1.1",
  client_id: "terminal",
  user_id: userId,
});
widget.onChartReady(() => {
  const chart = widget.activeChart();
  chart.createStudy("Moving Average Exponential", false, false, [21]);
  chart.createStudy("Relative Strength Index", false, false, [14]);
  chart.createShape({ time: t, price: 250 }, { shape: "horizontal_line", text: "Target 250", lock: true });
});
```

## Step One: The Drop-In

```bash
npm install @ortex-charts/migrate
```

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

const shell = tradingViewWidget(widgetOptions);
console.info("no counterpart:", shell.ignored);
```

`widgetOptions` is the typed `ChartingLibraryWidgetOptions` from above, and `datafeed` the
typed `IBasicDataFeed` inside it, passed as they are; the translator's input types accept
both without a cast. The datafeed runs unchanged: `onReady` is awaited once, `resolveSymbol`
once per symbol, `getBars` is asked in seconds and answered in milliseconds as before, and
`subscribeBars` streams into the chart. The chart is on screen with history, live bars, the
dark theme, the New York time zone and no symbol box.

`shell.ignored` reads:

```text
studies_overrides
charts_storage_url
charts_storage_api_version
client_id
user_id
disabled_features: use_localstorage_for_settings
disabled_features: header_saveload
enabled_features: study_templates
```

Everything not in that list was mapped: `symbol` and `interval`, `theme`, `timezone`,
`locale`, `header_symbol_search` to `toolbar.symbol: false`, `header_compare` to
`compare: false`, `hide_left_toolbar_by_default` to `drawingRailCollapsed: true`, and the
three overrides to the `up`, `down` and `background` theme tokens. `library_path` is
neither, because there is no bundle to load.

`fromTVWidgetOptions(widgetOptions)` returns the same shell options and the same `ignored`
list without creating anything.

## Step Two: What Is Left by Hand

Everything in `ignored`, plus what lived in `onChartReady` and in the datafeed's optional
methods.

**The `onChartReady` block.** The shell is ready when `tradingViewWidget` returns, so the
block becomes the lines after it. `createStudy` is `shell.addIndicator`; `createShape` is
`shell.drawings.add`.

```ts
shell.addIndicator("ema", { length: 21 });
shell.addIndicator("rsi", { length: 14 });
shell.drawings?.add({ kind: "horizontalLine", points: [{ time: t, price: 250 }], style: { text: "Target 250" }, locked: true });
```

Study names become indicator ids: `Moving Average Exponential` is `ema`, `Relative Strength
Index` is `rsi`, `Bollinger Bands` is `bb`, `MACD` is `macd`. The [indicators](https://ortexcharts.com/docs/indicators)
page lists all 51, and `listIndicators()` is the same list at runtime.

**Timescale marks.** `getTimescaleMarks` has no wrapper; the earnings badges it drew are
[events](https://ortexcharts.com/docs/markers#events) with `style: "lane"`:

```ts
shell.chart.setEvents(earnings.map((time) => ({ time, style: "lane", group: "Earnings", text: "E", tooltip: "Earnings" })));
```

**The save/load service.** `charts_storage_url`, `client_id` and `user_id` pointed at
TradingView's storage contract. There is none here, on purpose: `shell.getLayout()` is JSON
and `onLayoutChange` fires when it changes, so the store is the one you already have.
`onLayoutChange` is a creation option, so it goes in the second argument:

```ts
const shell = tradingViewWidget(widgetOptions, {
  layout: await api.loadChartLayout(userId),
  onLayoutChange: (layout) => void api.saveChartLayout(userId, layout),
  drawingsStore: { load: (s) => api.loadDrawings(userId, s), save: (s, d) => api.saveDrawings(userId, s, d) },
});
```

Drawings are stored per symbol rather than inside the layout; see
[the shell and layouts](https://ortexcharts.com/docs/shell).

**`studies_overrides`.** Indicator colors are per call: `shell.addIndicator("ema", { length: 21 },
{ colors: { ema: "#4fc3f7" } })`. The volume pane's colors are the `volumeUp` and
`volumeDown` theme tokens.

**Resolutions.** The widget read `supported_resolutions` from the datafeed and offered only
those. `tradingViewWidget` is synchronous, as the widget constructor was, and the feed's list
is not, so it offers the shell's default resolutions: a daily-only feed would show minute
buttons. `tradingViewWidgetAsync` waits for the list first:

```ts
const shell = await tradingViewWidgetAsync(widgetOptions);   // resolutions: what the symbol, else onReady, declared
```

**Feature flags with no counterpart.** `use_localstorage_for_settings` and `header_saveload`
describe TradingView's own storage, which does not exist here. `study_templates` is a
[layout](https://ortexcharts.com/docs/shell#layouts-the-chart-never-persists-the-host-does) you store.

## Step Three: The Finished Port

```ts
import { tradingViewWidgetAsync } from "@ortex-charts/migrate";

const shell = await tradingViewWidgetAsync(widgetOptions, {
  layout: await api.loadChartLayout(userId),
  onLayoutChange: (layout) => void api.saveChartLayout(userId, layout),
});

shell.addIndicator("ema", { length: 21 });
shell.addIndicator("rsi", { length: 14 });
shell.drawings?.add({ kind: "horizontalLine", points: [{ time: t, price: 250 }], style: { text: "Target 250" }, locked: true });
shell.chart.setEvents(earnings.map((time) => ({ time, style: "lane", group: "Earnings", text: "E" })));
```

The datafeed object is the one from the widget, untouched. Delete the `library_path`
directory from `public/`: that was 6 MB.

## Keeping Your Own Shell

A product that already has its own toolbar wants the datafeed and nothing else.
`tradingViewDatafeed` wraps it, and `supportedResolutions` asks it what it serves:

```ts
import { tradingViewDatafeed } from "@ortex-charts/migrate";
import { createChartShell } from "@ortex-charts/ui";

const feed = tradingViewDatafeed(datafeed);
const shell = createChartShell(el, {
  datafeed: feed,
  symbol: "IBM",
  resolution: "1D",
  resolutions: await feed.supportedResolutions("IBM"),
  toolbar: { symbol: false, resolution: false },
});
```

## From Lightweight Charts

There is no translator for Lightweight Charts and none is needed: the mental models are the
same and the [mapping table](https://ortexcharts.com/docs/migration#from-tradingview-lightweight-charts) is exact.
The one idiom to know is the overlay volume, a histogram on `priceScaleId: ""` with
`scaleMargins`, which is `createFinancialChart` with `volume: true`; and times, which were
seconds there and are milliseconds here.

```ts
const fc = createFinancialChart(el, { theme: "dark", timeZone: "America/New_York", resolution: "1D", data: bars, volume: true });
fc.addIndicator("sma", { length: 20 });                                       // was addSeries(LineSeries) + your own SMA
fc.chart.addPriceLine(fc.main.id, { price: 250, lineStyle: "dashed", title: "Target" }); // was series.createPriceLine
fc.setMarkers(markers);                                                       // was createSeriesMarkers
fc.chart.timeScale.fitContent();                                              // a property, not a method
fc.chart.subscribeCrosshairMove((e) => e.seriesValues.get(fc.main.id));      // was param.seriesData.get(series)
```

## Before You Ship

- **Read `shell.ignored` once**, then delete the log line.
- **Studies with no counterpart.** Advanced Charts ships about 110 studies against 51 built
  in and 78 more in `@ortex-charts/indicators-extra`; the long tail is mostly variations.
  Budget for the ones you have to [register](https://ortexcharts.com/docs/indicators#writing-your-own).
- **Your CSS resets now apply.** There is no iframe. A global `canvas` rule or
  `* { box-sizing: content-box }` will show.
- **Load an old layout, change something, save it, reload.**
- **A symbol with a short history**, so the indicators degrade rather than throw.
- **The licence key on the deployed origin**, not just on localhost.
