@iccandle/reactjs-widget
v0.2.28
Published
React scanner overlay for TradingView Charting Library — ICCandle pattern search, remote theming, and plugin iframe integration.
Downloads
5,379
Readme
@iccandle/reactjs-widget
React overlay for the TradingView Charting Library that adds ICCandle’s pattern scanner, results iframe, pattern tracker, and news/event integration.
The package ships ESM + CommonJS. Component styles are injected at runtime (no separate CSS import).
What it does
WidgetIccandle wraps your chart and renders:
| Surface | Role |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Scanner popup | Draggable overlay over the chart. Select a bar window with TradingView’s Date Range tool, then run a scan or subscribe to pattern alerts. |
| Results iframe | Side-by-side panel hosting the ICCandle embed app (embed-iccandle-app.iccandle.ai) — search results, similar events, auth, billing. |
| Chart sync | Bidirectional postMessage bridge: pattern selection, chart replay, news marks, sign-in tokens, loading state. |
Typical flow:
- User draws a Date Range on the chart (or activates the scanner).
- Scanner caches the selected candles and opens the results iframe with search parameters.
- The embed app returns matches; selecting a pattern redraws the range on the chart. Replay / news flows inject predicted candles via custom indicators.
Install
npm install @iccandle/reactjs-widget
# or
pnpm add @iccandle/reactjs-widgetPrerequisites
- React 18+ —
reactandreact-domare peer dependencies. - TradingView Charting Library — obtain under your own license, host the static assets (e.g.
/charting_library/), and bootstrap the widget yourself. This package does not ship the charting library. - Auth token — API calls (candle cache, pattern tracker) use
Bearerauth fromlocalStoragekeyiccandle_token. The embedded results app sets this via asign-in-successpostMessageafter login.
Quick start
import { useEffect, useRef, useState } from "react";
import type {
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
} from "charting_library/charting_library";
import { widget } from "charting_library/charting_library";
import {
WidgetIccandle,
withPlayChart,
getCustomIndicators,
} from "@iccandle/reactjs-widget";
const LIBRARY_PATH = "/charting_library/";
function App() {
const containerRef = useRef<HTMLDivElement>(null);
const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
null,
);
return (
<div style={{ height: "100vh", width: "100%" }}>
<WidgetIccandle chartWidget={chartWidget} theme="light" language="en">
<ChartHost containerRef={containerRef} onReady={setChartWidget} />
</WidgetIccandle>
</div>
);
}
function ChartHost({
containerRef,
chartRefs,
onReady,
}: {
containerRef: React.RefObject<HTMLDivElement | null>;
chartRefs: import("@iccandle/reactjs-widget").WidgetIccandleChartRefs;
onReady: (w: IChartingLibraryWidget | null) => void;
}) {
useEffect(() => {
const el = containerRef.current;
if (!el) return;
const options: ChartingLibraryWidgetOptions = {
container: el,
library_path: LIBRARY_PATH,
symbol: "EURUSD",
interval: "60" as ResolutionString,
datafeed: withPlayChart(yourDatafeed), // enables chart replay injection
locale: "en",
autosize: true,
drawings_access: {
type: "black",
tools: [{ name: "Date Range" }],
},
custom_indicators_getter: () => getCustomIndicators("light"),
};
const tv = new widget(options);
onReady(tv);
return () => {
try {
tv.remove();
} catch {
/* no-op */
}
onReady(null);
};
}, [containerRef, chartRefs, onReady]);
return <div ref={containerRef} style={{ height: "100%", width: "100%" }} />;
}Use a render-prop children when you need chartRefs for generated-candle studies (replay / predict). A plain React node also works if you do not need those refs.
For a full in-repo reference, see src/tradingview/TradingviewChart.tsx.
Features
Pattern scanner
- Subscribes to chart readiness, symbol, resolution, and drawing events.
- Manages a
date_rangemultipoint drawing so the user can adjust the bar window (default window size: 25 bars). - On scan: posts candles to
https://scan-service.iccandle.ai/cacheCandle, then navigates the results iframe with query params (ws,tk,et,symbol,tf,cid, filters, theme). - Optional advanced filters (symbols, top-k, lookback period, probability window) stored in
localStorageundersearch-filter.
Results iframe
- Loads
https://embed-iccandle-app.iccandle.ai/{language}?theme=...by default. - After scan / news navigation, the
srcupdates with search or similar-events paths. - On iframe load, the parent posts
{ type: "parent-origin", origin }so Stripe checkout can return to the host domain. If the host URL has?payment=success, it also posts{ type: "payment-success" }so the embed can refresh subscription state.
Pattern tracker
From the scanner popup, users can open a subscription modal to track a custom pattern (name + timeframes + symbols). Requires a valid iccandle_token.
News / economic events
- Clicking a timescale mark can open an event info modal (currency calendar events; skips holidays / early / sentiment-only marks).
- “Go to detail” loads similar events in the iframe and draws a vertical line on the chart.
- Marks can be driven from
localStoragekeystv:selected-news-eventsandtv:clicked-news-eventif your datafeed implementsgetTimescaleMarks(see below).
Chart replay (play candles)
Wrap your datafeed with withPlayChart and register custom indicators via getCustomIndicators so the embed can inject predicted / replay bars without live ticks fighting the playback.
API
Exports
| Name | Kind | Description |
| ------------------------------ | --------- | ------------------------------------------------------ |
| WidgetIccandle | Component | Chart overlay + results iframe |
| WidgetIccandleProps | Type | Props for WidgetIccandle |
| WidgetIccandleChartRefs | Type | Refs for highlight bars / generated-candle studies |
| WidgetLanguage | Type | Supported locale codes |
| withPlayChart | Function | Wraps a TradingView datafeed for replay bar injection |
| getCustomIndicators | Function | Returns ICCandle custom indicators (generated candles) |
| getGeneratedCandlesMaskColor | Function | Theme-aware mask color for generated candles |
WidgetIccandle props
| Prop | Type | Required | Description |
| --------------- | ----------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| chartWidget | IChartingLibraryWidget \| null | Yes | Live TradingView widget (null until ready). |
| children | ReactNode \| ((chartRefs) => ReactNode) | Yes | Chart UI, or render prop receiving WidgetIccandleChartRefs. |
| theme | "light" \| "dark" \| "system" | No | Scanner chrome + iframe theme. "system" follows prefers-color-scheme. |
| language | WidgetLanguage | No | UI + iframe locale. One of: en, zh, vi, th, ko, ja, mn, ru. Defaults to en. |
| onCloseResult | () => void | No | Called when the embed posts close-result. |
| iframeLoaded | boolean | No | When false, disables scan / track actions until the results iframe is ready. Defaults to enabled behavior in the scanner. |
IChartingLibraryWidget must be imported from your Charting Library typings — this package does not re-export TradingView types.
withPlayChart(datafeed)
Wraps subscribeBars so ICCandle can push replay bars and optionally block live ticks during playback.
import { withPlayChart } from "@iccandle/reactjs-widget";
datafeed: withPlayChart(myDatafeed),getCustomIndicators(theme?)
Returns a Promise of TradingView CustomIndicator[] used for generated / predicted candle overlays. Pass "light" or "dark" to match chart theme. Wire it into widget options:
custom_indicators_getter: () => getCustomIndicators("light"),Auth and storage
| Key | Purpose |
| ------------------------- | ------------------------------------------------------------------------------------------------------- |
| iccandle_token | Bearer token for scan cache, pattern tracker, and related APIs. Set by the embed via sign-in-success. |
| search-filter | Scanner advanced options (symbols, top_k, period, probability). |
| tv:selected-news-events | JSON array of news events used as timescale marks. |
| tv:clicked-news-event | Last clicked calendar event for mark highlighting. |
| tv:latest-symbol | Last chart symbol (used by the demo app / fallbacks). |
postMessage bridge
Messages are accepted only from the results origin (https://embed-iccandle-app.iccandle.ai).
| Direction | Type / action | Effect |
| -------------- | ---------------------------------------------------------- | ------------------------------------------------ |
| Embed → parent | play-chart | Inject / clear replay candles on the chart |
| Embed → parent | loading | Toggle scanner loading UI |
| Embed → parent | sign-in-success | Persist iccandle_token from payload.id_token |
| Embed → parent | close-result | Clears play state; calls onCloseResult |
| Embed → parent | pattern_selected / custom_pattern_selected | Draw date range for the compared pattern |
| Embed → parent | clear_pattern_selected / clear_custom_pattern_selected | Remove pattern date range |
| Embed → parent | eventClicked | Center chart, draw event line, show mark |
| Embed → parent | replay | Start / stop event replay candles |
| Embed → parent | back / back-to-similar-events | Clear event line / replay |
| Embed → parent | nav-click | Hide scanner on /news; restore otherwise |
| Parent → embed | parent-origin | Host origin for Stripe return |
| Parent → embed | payment-success | Refresh subscription after checkout |
Optional: timescale marks (news/events)
If your datafeed implements getTimescaleMarks, surface stored events as marks:
getTimescaleMarks: async (symbolInfo, from, to, onResult) => {
const marks = [];
try {
const allNewsEvents = JSON.parse(
localStorage.getItem("tv:selected-news-events") || "[]",
);
allNewsEvents?.forEach(({ id, timestamp, event_name, currency }) => {
if (!id || !Number.isFinite(timestamp) || timestamp <= 0) return;
if (marks.some((m) => String(m.id) === String(id))) return;
marks.push({
id,
time: timestamp / 1000,
color: "green",
label: event_name.slice(0, 1) || "N",
tooltip: [event_name],
...(currency ? { imageUrl: `/images/symbols/${currency}.svg` } : {}),
showLabelWhenImageLoaded: true,
});
});
} catch {
/* ignore */
}
onResult(marks);
};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.
Development (this repo)
| Script | Command | Purpose |
| ------------- | --------------------- | ----------------------------------------------- |
| Dev demo | pnpm run dev | Vite app with local charting library |
| Library build | pnpm run build | Emits dist/ (ESM, CJS, injected CSS, .d.ts) |
| Web build | pnpm run build:web | SPA build to dist-web/ |
| Deploy web | pnpm run deploy:web | Build + Vercel production deploy |
| Lint | pnpm run lint | ESLint |
| Preview web | pnpm run preview | Preview the web build |
prepublishOnly runs build before npm publish.
Troubleshooting
| Issue | What to check |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| Chart stays blank | library_path must serve TradingView static files; container must be mounted before new widget(...). |
| chartWidget is always null | Call setChartWidget after create (or inside onChartReady if required). |
| Scan fails / 401 | Ensure the user signed in via the results iframe so iccandle_token is set. |
| Date Range tool missing | Enable it in drawings_access (see Quick start). |
| Replay bars do not appear | Wrap the datafeed with withPlayChart and register getCustomIndicators. |
| Scanner actions disabled | Pass iframeLoaded={true} once the results iframe has loaded, or omit the prop if you do not gate on load. |
License
MIT. TradingView Charting Library is subject to its own license from TradingView.
