Skip to main content

Configuration

Exports​

NameKindDescription
WidgetIccandleComponentScanner overlay (host owns the results iframe)
WidgetIccandlePropsTypeProps for WidgetIccandle
WidgetChartMaskColorsType{ light?: string; dark?: string } for chartMaskColors
WidgetLanguageTypeSupported locale codes
withPlayChartFunctionWraps a TradingView datafeed (or factory) for replay bar injection
getCustomIndicatorsFunctionReturns iC Candle custom indicators (generated candles)
GeneratedCandlesThemeType"light" | "dark" for getCustomIndicators
handleResultIframeLoadFunctionPosts parent-origin / payment-success to the results iframe on load
useICCandleAuthComposableHost-side session: { isAuthenticated, idToken } from iccandle_token
ICCandleAuthTypeReturn type of useICCandleAuth
WIDGET_RESULT_URLConst"https://embed-iccandle-app.iccandle.ai"
OPEN_PRICING_MESSAGE_TYPEConst"open-pricing" postMessage type
postOpenPricingToEmbedFunctionAsk the results iframe to open pricing. Returns false if no contentWindow.
postOpenPricingToParentFunctionForward pricing to window.parent when this widget is itself embedded.
postOpenPricingToWindowFunctionPost an open-pricing message to an arbitrary Window.
OpenPricingMessageType{ type, theme?, language? }

WidgetIccandle props​

PropTypeRequiredDescription
chartWidgetIChartingLibraryWidget | nullYesLive TradingView widget (null until ready). Template: :chart-widget.
submitCallback(iframeSrc: string) => voidYesCalled when a scan, news navigation, or pricing fallback should update the results iframe. Set your iframe src to this URL (optionally append header=false). Template: :submit-callback.
theme"light" | "dark" | "system"NoScanner chrome + iframe theme. "system" follows prefers-color-scheme. Defaults to "light".
chartMaskColorsWidgetChartMaskColorsNoPer-theme color that hides unrevealed predicted candles. Must match the chart pane background. Defaults: { light: "#ffffff", dark: "#0F0F0F" }. Template: :chart-mask-colors.
bg_darkstringNoEmbed page background for the dark theme. Hex, # optional. Omitted = no bg_dark query param. Template: bg_dark.
bg_lightstringNoEmbed page background for the light theme. Hex, # optional. Omitted = no bg_light query param. Template: bg_light.
languageWidgetLanguageNoUI + iframe locale. One of: en, zh, vi, th, ko, ja, mn, ru. Defaults to en.
onCloseResult() => voidNoCalled when the embed posts selector.closeResult. Template: :on-close-result (prop callback, not an emit).
iframeLoadedbooleanNoWhen false, disables scan / track actions until the results iframe is ready. Defaults to true. Template: :iframe-loaded.
resultIframeRef{ current: HTMLIFrameElement | null } | nullNoResults iframe box (not a Vue ref). Receives open-pricing and chart-resolution postMessages. Template: :result-iframe-ref.
isShowScanButtonbooleanNoShow Scan inside the popup. Defaults to true. Template: :is-show-scan-button.
isShowTrackerButtonbooleanNoShow Pattern Tracker inside the popup. Defaults to true. Template: :is-show-tracker-button.
isShowScannerPopupbooleanNoShow the scanner popup. Defaults to true. Template: :is-show-scanner-popup.
availableIntervalsstring[]NoResolutions allowed when drawing a pattern date range from embed selection. Template: :available-intervals.
onScanClick() => voidNoFired when the user clicks Scan, before login / config / scan logic. Template: :on-scan-click.

Default slot: place your chart UI as the child of WidgetIccandle. The slot also receives { chartRefs } (highlight bars + generated-candles study ids). The same chartRefs object is exposed via the component instance.

IChartingLibraryWidget must be imported from your Charting Library typings — this package does not re-export TradingView types.

submitCallback​

Required. The host owns the results iframe. When the user runs a scan, opens similar events from a news mark, or (if no iframe ref / parent) requests pricing, WidgetIccandle builds a full embed URL and passes it to submitCallback. Update your iframe src with that value.

Typical URLs look like:

https://embed-iccandle-app.iccandle.ai/{locale}?symbol=…&res=…&ref_res=…&cid=…&tk=…&from=…&to=…&theme=light
https://embed-iccandle-app.iccandle.ai/{locale}/news/similar-events/{id}?symbol=…&theme=light
https://embed-iccandle-app.iccandle.ai/{locale}?pricing=true&theme=light

Optional scan filters may also be appended: fs (symbol filter), period, model (light / standard / plus / pro), temp (0.5 / 1 / 1.5). When bg_dark / bg_light are set, those colors are appended on the same URLs.

<script setup lang="ts">
import { ref, watch } from "vue";
import {
WidgetIccandle,
handleResultIframeLoad,
WIDGET_RESULT_URL,
} from "@iccandle/vuejs-widget";

const iframeSrc = ref(`${WIDGET_RESULT_URL}/en?theme=light&header=false`);
const iframeLoaded = ref(false);
const resultIframeRef = ref<HTMLIFrameElement | null>(null);
const resultIframeBox = { current: null as HTMLIFrameElement | null };

watch(
resultIframeRef,
(el) => {
resultIframeBox.current = el;
},
{ immediate: true },
);

const submitCallback = (nextSrc: string) => {
const url = new URL(nextSrc);
url.searchParams.set("header", "false");
const next = url.toString();
if (iframeSrc.value !== next) {
iframeSrc.value = next;
}
};

const handleIframeLoad = () => {
iframeLoaded.value = true;
handleResultIframeLoad(resultIframeRef.value);
};
</script>

<template>
<WidgetIccandle
:chart-widget="chartWidget"
:submit-callback="submitCallback"
theme="light"
language="en"
:iframe-loaded="iframeLoaded"
:result-iframe-ref="resultIframeBox"
>
<!-- chart -->
</WidgetIccandle>
<iframe
ref="resultIframeRef"
:src="iframeSrc"
title="iC Candle results"
@load="handleIframeLoad"
/>
</template>

result-iframe-ref is a mutable { current } box, not a Vue ref. Sync it from the iframe element as shown above.

header=false​

Append header=false on the embed URL (initial src and inside submitCallback) to hide the embed app header when your host already provides navigation. Footer and in-page UI still render; nav.click messages from header tabs will not fire.

Scanner-built URLs include scan params and theme but do not add header=false automatically — set it in the host when needed.

See Search params → header for full embed query-param details.

handleResultIframeLoad(iframe)​

Call this from your results iframe @load handler. It posts the host origin to the embed (for Stripe return and so auth.signIn is addressed to the correct host) and, when the parent URL has ?payment=success, posts payment-success so the embed refreshes subscription state.

parent-origin is retried at 0 / 300 / 1000 / 2500 ms because the embed has no message buffering — its listener must be mounted before the message arrives.

import { handleResultIframeLoad } from "@iccandle/vuejs-widget";

handleResultIframeLoad(resultIframeRef.value);

useICCandleAuth()​

Host-side view of whether the results iframe is already signed in. Reads iccandle_token, rejects JWTs within 60 seconds of exp, and updates on auth.signIn / auth.signOut.

import { useICCandleAuth } from "@iccandle/vuejs-widget";

const { isAuthenticated, idToken } = useICCandleAuth();
FieldTypeDescription
isAuthenticatedRef<boolean>true when iccandle_token is present and unexpired
idTokenRef<string | null>Live identity token, or null when signed out

For corporate partners: watch isAuthenticated first, then call get-user-token only when it is false. See Seamless auth example.

withPlayChart(datafeed)​

Required for chart replay / predict. Wraps your TradingView datafeed’s subscribeBars so the widget can:

  1. Inject replay bars into the live tick stream when the embed posts chart.play, chart.stop, or replay payloads.
  2. Block live ticks while playback is active, so real-time updates do not overwrite predicted candles.

Signature​

withPlayChart(datafeed: IBasicDataFeed): IBasicDataFeed
ArgumentTypeDescription
datafeedIBasicDataFeedReady datafeed instance (must expose onReady).
factory, ...argsfunction overloadOptional: pass a factory plus args; withPlayChart invokes it and wraps the result.

Usage​

import { withPlayChart } from "@iccandle/vuejs-widget";

datafeed: withPlayChart(myDatafeed),
// or: withPlayChart(createDatafeed, arg1, arg2)

Notes​

  • The returned object is still a normal datafeed; only subscribeBars is wrapped. Other methods such as optional getTimescaleMarks are left unchanged.
  • Pair with getCustomIndicators — without both, replay / predict overlays will not work.

getCustomIndicators(theme?)​

Registers iC Candle’s custom studies used to draw generated / predicted candles on top of the main series. Returns a Promise of TradingView CustomIndicator[] for custom_indicators_getter.

Signature​

getCustomIndicators(theme?: "light" | "dark"): Promise<readonly CustomIndicator[]>
ArgumentTypeDefaultDescription
theme"light" | "dark""light"Pane mask color so predicted bars hide the underlying series. Must match the chart theme.

Studies returned​

Study nameRole
Generated Candles Background - By iC CandleHidden mask layer that paints over real bars at predicted timestamps.
Generated Candles - By iC CandleVisible OHLC overlay (bullish #00dfb9 / bearish #ffb84d) for predicted candles.

WidgetIccandle creates and drives these studies when the embed sends replay / predict data.

Usage​

import { getCustomIndicators } from "@iccandle/vuejs-widget";

const options: ChartingLibraryWidgetOptions = {
// ...
datafeed: withPlayChart(yourDatafeed),
custom_indicators_getter: () => getCustomIndicators("light"),
};

Keep getCustomIndicators(theme) in sync with the chart widget theme (and preferably with WidgetIccandle’s theme prop). After the studies are created, WidgetIccandle overrides the background-study color from chartMaskColors so unrevealed predicted bars match the pane.

Together with withPlayChart​

HelperJob
withPlayChartLets the widget push bars into the datafeed and mute live ticks during playback.
getCustomIndicatorsDraws the predicted candle visuals (mask + colored OHLC) as custom studies.

Both are required for replay / predict from the results iframe.

chartMaskColors​

During replay / predict, the widget draws generated candles and hides bars that have not been revealed yet by painting them in a solid mask color. That color must match the TradingView pane background (paneProperties.background); otherwise unrevealed bars show up as visible blocks ahead of the replay cursor.

Chart mask covering unrevealed predicted candles on a dark pane

KeyDefaultWhen it is used
light#fffffftheme="light", or theme="system" when the OS is in light mode
dark#0F0F0Ftheme="dark", or theme="system" when the OS is in dark mode

Only the entry for the active theme is applied. Each key is optional — omit one to keep its default. Any CSS color string the charting library accepts works; do not use transparency, or the real series bleeds through.

If your chart pane is not the default white / #0F0F0F, pass matching values:

<WidgetIccandle
:chart-widget="chartWidget"
:submit-callback="submitCallback"
theme="dark"
:chart-mask-colors="{ dark: '#2F2F2F', light: '#ffffff' }"
>
<!-- chart -->
</WidgetIccandle>

WidgetIccandle applies this color as an override on the Generated Candles Background study. Changing chartMaskColors or theme recreates those studies, so the host can update it when the chart background changes.

This is not the scan-window (date_range) fill — that uses --iccandle-primary on .iccandle-selector-widget.

bg_dark and bg_light​

Optional hex colors for the results iframe page background. The widget appends them as bg_dark and bg_light query params on embed URLs it builds: scan results, news detail, and the pricing fallback.

PropQuery paramApplied when
bg_darkbg_darkThe embed theme resolves to dark
bg_lightbg_lightThe embed theme resolves to light

A leading # is optional (#131722 and 131722 are the same). 3-, 6-, and 8-digit hex are accepted. An empty or non-hex value is dropped, so that param is not added. Omit a prop to leave that theme on the embed default. Omit both and the widget adds no bg_* params.

The host owns the initial iframe src. Include the same params there so the first load matches. After the embed sees a valid color it stores it, and later navigations that omit bg_* keep that color.

<script setup lang="ts">
import { ref } from "vue";
import { WIDGET_RESULT_URL } from "@iccandle/vuejs-widget";

const iframeSrc = ref(
`${WIDGET_RESULT_URL}/en?theme=dark&header=false&bg_dark=131722&bg_light=ffffff`,
);
</script>

<template>
<WidgetIccandle
:chart-widget="chartWidget"
:submit-callback="submitCallback"
theme="dark"
bg_dark="#131722"
bg_light="#ffffff"
>
<!-- chart -->
</WidgetIccandle>
</template>

These props set the embed page background. Replay masking on the chart stays on chartMaskColors.

See Search params → bg_dark and bg_light.

Pricing helpers​

When the user clicks upgrade in scanner settings, the widget posts open-pricing to the results iframe (resultIframeRef), then to window.parent, then falls back to submitCallback with ?pricing=true.

You can also trigger pricing yourself:

import { postOpenPricingToEmbed } from "@iccandle/vuejs-widget";

postOpenPricingToEmbed(resultIframeRef.value, {
theme: "light",
language: "en",
});

Auth and storage​

Use useICCandleAuth() to read the live session from the host. Corporate partners should watch isAuthenticated before calling get-user-token — see Seamless auth example.

KeyPurpose
iccandle_tokenBearer token for scan cache, pattern tracker, and related APIs. Set by the embed via auth.signIn; cleared on auth.signOut.
search-filterPersisted scanner advanced options (symbols, top_k, period, model, temperature).
search-filter-sessionSession-scoped copy of advanced filters when the user does not persist them.
scanner-always-showWhether the scanner stays visible ("true" / unset).
tv:selected-news-eventsJSON array of news events used as timescale marks.
tv:clicked-news-eventLast clicked calendar event for mark highlighting.
tv:latest-symbolLast chart symbol (used by the demo app / fallbacks).

postMessage bridge​

Messages are accepted only from the results origin (https://embed-iccandle-app.iccandle.ai). Embed → parent payloads use a structured { name, data } shape.

DirectionNameEffect
Embed → parentchart.play / chart.stopInject / clear replay candles on the chart
Embed → parentselector.loadingToggle scanner loading UI
Embed → parentauth.signInPersist iccandle_token from data.idToken
Embed → parentauth.signOutRemove iccandle_token
Embed → parentselector.closeResultClears play state; calls onCloseResult
Embed → parentpattern.classicPatternSelected / pattern.custom_pattern_selectedDraw date range for the compared pattern
Embed → parentpattern.clearClassicPatternSelected / pattern.clear_custom_pattern_selectedRemove pattern date range
Embed → parentnews.eventClickedCenter chart, draw event line, show mark
Embed → parentnews.back / news.backToSimilarEventsClear event line / replay
Embed → parentnav.clickClear date range on /news routes
Embed → parentchart.requestResolutionRe-post the current chart resolution to the iframe
Parent → embedparent-originHost origin for Stripe return
Parent → embedpayment-successRefresh subscription after checkout
Parent → embedopen-pricingAsk the embed to open pricing (requires resultIframeRef)
Parent → embedchart-resolutionCurrent chart resolution (posted when iframeLoaded becomes true)

Host → embed helpers use { type: "parent-origin" | "payment-success" | "open-pricing" | "chart-resolution", ... }.

Optional: timescale marks (news/events)​

News marks on the time axis are optional. WidgetIccandle does not add them. To show calendar / economic events, implement TradingView’s getTimescaleMarks on your datafeed — the object you pass into withPlayChart. withPlayChart only wraps subscribeBars, so your getTimescaleMarks still runs as-is.

News event mark on the time axis with a currency icon and vertical line

Advertise support in the datafeed onReady configuration (supports_timescale_marks: true), or the library will not call getTimescaleMarks.

The widget writes events to localStorage. Your implementation should read those keys and return marks:

KeyValueMark
tv:clicked-news-eventOne event JSONBlue mark, rich tooltip (actual / forecast / previous)
tv:selected-news-eventsEvent array JSONGreen marks

Event timestamp is unix milliseconds; TradingView marks use unix seconds (time: timestamp / 1000). Event shape: NewsEvent.

import type {
GetMarksCallback,
LibrarySymbolInfo,
TimescaleMark,
} from "charting_library/charting_library";

type NewsEventType = {
id: string;
timestamp: number; // unix ms
event_name: string;
currency?: string;
actual?: string;
forecast?: string;
previous?: string;
};

const yourDatafeed = {
// ...onReady, resolveSymbol, getBars, subscribeBars, ...
getTimescaleMarks: async (
_symbolInfo: LibrarySymbolInfo,
_from: number,
_to: number,
onResult: GetMarksCallback<TimescaleMark>,
) => {
const marks: TimescaleMark[] = [];

const pushEventMark = (
event: NewsEventType,
options?: { color?: TimescaleMark["color"]; richTooltip?: boolean },
) => {
const { id, timestamp, event_name, currency, actual, forecast, previous } =
event;
if (!id || !Number.isFinite(timestamp) || timestamp <= 0) return;
if (marks.some((m) => String(m.id) === String(id))) return;

const tooltip = options?.richTooltip
? [
event_name,
currency ? `Currency: ${currency}` : null,
actual ? `Actual: ${actual}` : null,
forecast ? `Forecast: ${forecast}` : null,
previous ? `Previous: ${previous}` : null,
].filter((line): line is string => Boolean(line))
: [event_name];

marks.push({
id,
time: timestamp / 1000,
color: options?.color ?? "green",
label: event_name.slice(0, 1) || "N",
tooltip,
...(currency
? {
imageUrl: `https://cdn.backprop.services/symbols/${currency}.svg`,
}
: {}),
showLabelWhenImageLoaded: true,
});
};

try {
const clickedEvent = JSON.parse(
localStorage.getItem("tv:clicked-news-event") || "null",
) as NewsEventType | null;
if (clickedEvent) {
pushEventMark(clickedEvent, { color: "blue", richTooltip: true });
}
} catch {
// no-op
}

try {
const allNewsEvents = JSON.parse(
localStorage.getItem("tv:selected-news-events") || "[]",
) as NewsEventType[];
allNewsEvents?.forEach((event) => pushEventMark(event));
} catch {
// no-op
}

onResult(marks);
},
};

Then wrap as usual: datafeed: withPlayChart(yourDatafeed).

Theming​

Scanner UI uses CSS variables on .iccandle-selector-widget:

  • --iccandle-primary
  • --iccandle-primary-gradient-end
  • --iccandle-background
  • --iccandle-border
  • --iccandle-text
  • --iccandle-secondary
  • --iccandle-font

Light and dark defaults live in the bundled stylesheet. The theme prop toggles the .iccandle-dark class and is forwarded to the iframe URL via submitCallback. Pass bg_dark and bg_light to set the embed page background.

Replay mask color is not a CSS variable: set chartMaskColors to match paneProperties.background.

Layout helpers​

Injected CSS includes split-pane classes used by the demo:

ClassRole
.iccandle-selector-widget__containerFlex row (stacks below 1024px). --chart-pane-size defaults to 60%.
.iccandle-selector-widget__chart-paneChart column.
.iccandle-selector-widget__resize-handleDrag handle; add --stacked for the stacked layout.
.iccandle-selector-widget__iframeResults pane.

Behavior summary​

  • Subscribes to chart readiness, resolution, symbol changes, visible range, drawings, and timescale-mark clicks.
  • Manages a date_range multipoint drawing so the user can adjust the bar window (default: 25 bars).
  • Posts candles to https://scan-service.iccandle.ai/cacheCandle, then calls submitCallback with an embed URL (symbol, res, ref_res, cid, tk, from, to, filters, theme, and bg_dark / bg_light when set).
  • Syncs with the host-owned results iframe at https://embed-iccandle-app.iccandle.ai via postMessage.