npm package: @iccandle/news
Description: React component that wraps an existing [TradingView Charting Library](https://www.tradingview.com/charting-library-docs/) widget and adds ICCandle's **economic calendar analysis UI**: an interactive panel that displays news events as timescale marks on th
Author: ICCandle
Latest version: 0.0.2

# @iccandle/news

React component that wraps an existing [TradingView Charting Library](https://www.tradingview.com/charting-library-docs/) widget and adds ICCandle's **economic calendar analysis UI**: an interactive panel that displays news events as timescale marks on the chart, draws pip-range visualizations (high/low rectangles, trend lines, pip annotations) around selected events, and lets users compare historical price reactions across similar news releases.

Published as ESM and CommonJS; component styles are bundled and injected at runtime (no separate CSS import).

## Install

```bash
npm install @iccandle/news
```

### Prerequisites

- **React 18+** — `react` and `react-dom` are **peer dependencies** (install them in your app).
- **TradingView Charting Library** — obtain it under your own license from TradingView, host the static assets (e.g. under `/charting_library/` in your public folder), and load the library at runtime. **This package does not ship the charting library.**
- **ICCandle API key** — issued by ICCandle for theme, chart data, news events, and scanner validation.

## Quick start

```tsx
import { useState } from "react";
import type { IChartingLibraryWidget } from "charting_library/charting_library";
import { NewsWidget } from "@iccandle/news-widget";

function App() {
  const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
    null,
  );
  const widgetKey = "your-iccandle-api-key";

  return (
    <NewsWidget
      chartWidget={chartWidget}
      widgetKey={widgetKey}
      defaultSymbol="XAU_USD"
      chartInterval="15"
      theme="dark"
    >
      {/* Your chart container + TradingView bootstrap; call setChartWidget when ready */}
      <div id="tv_chart_container" style={{ height: "100%" }} />
    </NewsWidget>
  );
}
```

Replace the chart placeholder with your TradingView initialization and pass the `IChartingLibraryWidget` instance when `onChartReady` (or equivalent) fires.

## Usage guide

### Step 1 — Install the package

```bash
npm install @iccandle/news
```

Ensure `react` and `react-dom` are installed and meet the peer version range.

### Step 2 — Host the Charting Library

1. Copy the TradingView Charting Library build into a path your app can serve as static files (e.g. `public/charting_library/` in Vite or Create React App).
2. Import the library constructor from that path in your bundler setup (see TradingView's integration docs for your framework). The library is **not** bundled inside `@iccandle/news-widget`; it loads at runtime via `library_path` (or equivalent) on the widget options.

### Step 3 — Bootstrap TradingView and capture the widget instance

Create a ref for the chart DOM node, instantiate the widget in `useEffect`, store the instance in React state, and remove it on cleanup:

```tsx
import { useEffect, useRef, useState } from "react";
import type {
  ChartingLibraryWidgetOptions,
  IChartingLibraryWidget,
  ResolutionString,
} from "charting_library/charting_library";
import { widget } from "charting_library/charting_library";

const LIBRARY_PATH = "/charting_library/"; // must match your hosted assets

// Inside your component:
const containerRef = useRef<HTMLDivElement>(null);
const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
  null,
);

useEffect(
  () => {
    const el = containerRef.current;
    if (!el) return;

    const options: ChartingLibraryWidgetOptions = {
      container: el,
      library_path: LIBRARY_PATH,
      symbol: "XAU_USD",
      interval: "15" as ResolutionString,
      datafeed: yourDatafeed,
      locale: "en",
      autosize: true,
    };

    const tv = new widget(options);
    setChartWidget(tv);

    return () => {
      try {
        tv.remove();
      } catch {
        /* no-op */
      }
      setChartWidget(null);
    };
  },
  [
    /* library_path, datafeed identity, or other inputs that should recreate the chart */
  ],
);
```

You must supply a valid `datafeed`, `symbol`, `interval`, `locale`, and any other options required by your TradingView license and app. Import paths for `widget` and types differ by setup (`charting_library/charting_library`, `./public/charting_library`, etc.—follow TradingView's docs for your bundler). If your integration only exposes the instance after `onChartReady`, call `setChartWidget` inside that callback instead of immediately after `new widget(...)`.

### Step 4 — Wrap the chart with `NewsWidget`

`NewsWidget` must wrap the same subtree that contains the chart container so the event panel and chart visualizations position correctly. Pass the live widget instance (or `null` while mounting):

```tsx
import { NewsWidget } from "@iccandle/news-widget";

const widgetKey = "your-iccandle-api-key";

<NewsWidget
  chartWidget={chartWidget}
  widgetKey={widgetKey}
  defaultSymbol="XAU_USD"
  chartInterval="15"
  theme="dark"
>
  <div ref={containerRef} style={{ height: "100%", minHeight: 400 }} />
</NewsWidget>;
```

When a news event is selected, the widget switches to a 50/50 split layout: the event analysis panel on the left and the chart (with pip-range overlays) on the right. On mobile (<1024px) the panels stack vertically.

### Step 5 — Interact with news events

Once the widget is mounted:

1. **Economic calendar events** appear as timescale marks on the chart's time axis.
2. **Clicking an event** opens the detail panel showing event metadata (actual, forecast, previous values), impact status, and a list of similar historical events.
3. **Pip-range visualization** draws automatically on the chart: green rectangles for upside range, red rectangles for downside range, dotted trend lines at highs/lows, and pip annotations at extremes.
4. **Candle window** is configurable (10–50 candles) to control how far after the event the range analysis extends.

**TypeScript:** import `NewsWidgetProps` if you wrap `NewsWidget` in your own component and want explicit prop typing. Import `IChartingLibraryWidget` from **your** Charting Library typings path (`charting_library/charting_library` or the path your project uses)—this package does not re-export TradingView types.

### Theme and remote branding

- **`theme="light"` / `"dark"`** — forces that palette for the widget chrome and chart overlays.
- On mount, the widget fetches your org's tokens from ICCandle (see [Widget key and remote theming](#widget-key-and-remote-theming)) and sets CSS custom properties on the widget root, e.g. `--iccandle-primary`, `--iccandle-border`, so the event panel and chart overlays match your configured light/dark branding.

### Full working example

Minimal end-to-end pattern: chart container ref, widget lifecycle, `NewsWidget` with event analysis. Replace `yourDatafeed` and widget options with your real datafeed and TradingView settings.

```tsx
import { useEffect, useRef, useState } from "react";
import type {
  ChartingLibraryWidgetOptions,
  IChartingLibraryWidget,
  ResolutionString,
} from "charting_library/charting_library";
import { widget } from "charting_library/charting_library";
import { NewsWidget } from "@iccandle/news-widget";

const LIBRARY_PATH = "/charting_library/";
const WIDGET_KEY = "your-iccandle-api-key";

export function ChartWithNewsWidget() {
  const containerRef = useRef<HTMLDivElement>(null);
  const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
    null,
  );

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    const options: ChartingLibraryWidgetOptions = {
      container: el,
      library_path: LIBRARY_PATH,
      symbol: "XAU_USD",
      interval: "15" as ResolutionString,
      datafeed: yourDatafeed,
      locale: "en",
      autosize: true,
      fullscreen: false,
    };

    const tv = new widget(options);
    setChartWidget(tv);

    return () => {
      try {
        tv.remove();
      } catch {
        /* no-op */
      }
      setChartWidget(null);
    };
  }, []);

  return (
    <div style={{ height: 600, width: "100%" }}>
      <NewsWidget
        chartWidget={chartWidget}
        widgetKey={WIDGET_KEY}
        defaultSymbol="XAU_USD"
        chartInterval="15"
        theme="dark"
      >
        <div ref={containerRef} style={{ height: "100%", width: "100%" }} />
      </NewsWidget>
    </div>
  );
}
```

For a concrete in-repo reference (custom datafeed, timezone, visibility handling), see [`src/tradingview/TradingviewChart.tsx`](src/tradingview/TradingviewChart.tsx) in this repository's dev app.

### Common patterns

| Pattern                            | Approach                                                                                                                                                                       |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Full-page chart**                | Set the parent container to `height: 100vh` and let `NewsWidget` fill the space with `autosize: true`.                                                                         |
| **Dashboard embed**                | Give the parent a fixed height (e.g. 600px) and `NewsWidget` handles the split layout internally.                                                                              |
| **News / events on the time axis** | If your datafeed implements `getTimescaleMarks`, you can sync marks with `localStorage` — see [Optional: timescale marks (news/events)](#optional-timescale-marks-newsevents). |                               |

### Troubleshooting

| Issue                            | What to check                                                                                                                                                                           |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Chart area stays blank           | `library_path` must point to the folder URL where TradingView static files are served; container ref must be mounted before `new widget(...)`.                                          |
| `chartWidget` is always `null`   | Ensure you call `setChartWidget` after the widget is created (or inside `onChartReady` if your integration requires it).                                                                |
| Event panel or theme looks wrong | Confirm `widgetKey` is valid; theme fetch uses `api-key: <widgetKey>`. Network or auth failures may leave defaults.                                                                     |
| CORS / network errors on APIs    | In local dev, browser calls to ICCandle APIs may be blocked by CORS unless you proxy; the widget should still function with fallback styling—verify in production or behind your proxy. |
| No events showing on chart       | The widget falls back to the `defaultSymbol` if selected symbol is unrecognized, but ensure `chartInterval` is one of the accepted values (`"1"`, `"5"`, `"15"`, `"30"`, `"60"`).                  |

## API

### Exports

| Name              | Kind      | Description                                       |
| ----------------- | --------- | ------------------------------------------------- |
| `NewsWidget`      | Component | Economic calendar analysis overlay for your chart |
| `NewsWidgetProps` | Type      | Props for `NewsWidget`                            |

### `NewsWidget` props

| Prop            | Type                                 | Required | Description                                                                                                                 |
| --------------- | ------------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `chartWidget`   | `IChartingLibraryWidget \| null`     | Yes      | Live TradingView widget instance (`null` until ready).                                                                      |
| `children`      | `ReactNode`                          | Yes      | Your chart UI (e.g. container + library bootstrap).                                                                         |
| `widgetKey`     | `string`                             | Yes      | ICCandle API key. Used for theme fetch, chart data, news events, and scanner validation.                                    |
| `defaultSymbol` | `string`                             | Yes      | Default trading symbol used to initialize the chart (e.g. `"XAU_USD"`, `"EURUSD"`, `"BTCUSDT"`). Also used as the fallback when a selected symbol is unrecognized or unavailable. |
| `chartInterval` | `"1" \| "5" \| "15" \| "30" \| "60"` | Yes      | Initial chart candle interval in minutes used when showing the chart. Defaults to `"15"`.                                                                       |
| `theme`         | `"light" \| "dark" \|`      | Yes       | Affects resolved theme tokens and chart overlays. |

### Widget key and remote theming

On mount, the component loads branding colors from ICCandle:

- **URL:** `https://api.iccandle.ai/corporate-client/v1/widgetStyle/search/user/?service_type=search`
- **Header:** `api-key: <widgetKey>`

CSS custom properties (`--iccandle-primary`, `--iccandle-border`, `--iccandle-background`, `--iccandle-text`, etc.) are applied on the widget root so the event panel and chart overlays match your configured light/dark tokens.

**Theme prop vs. API tokens:** The `theme` prop (`"light"` | `"dark"`) chooses which variant of those tokens to apply.

### Behavior summary

- Subscribes to chart readiness, resolution, symbol changes, and drawing events.
- Displays economic calendar events as interactive timescale marks on the chart.
- On event selection, switches to a split layout with an event detail panel and pip-range chart visualization.
- Draws pip-range rectangles (green for upside, red for downside), dotted trend lines, and pip annotations on the chart.
- Fetches similar historical events for comparison and shows event metadata (actual, forecast, previous values).
- Supports configurable candle windows (10–50 candles) for range analysis.
- Stores selected events in `localStorage` (key `iccandle:current-news-event`) for timescale mark sync.
- Listens for `window` `message` events for chart/news integration.

## Optional: timescale marks (news/events)

If your data feed implements `getTimescaleMarks`, you can surface stored events (e.g. from `localStorage` under `tv:current-news-event`) as marks on the time axis. Example shape:

```ts
getTimescaleMarks: async (
  symbolInfo: LibrarySymbolInfo,
  from: number,
  to: number,
  onResult: GetMarksCallback<TimescaleMark>,
) => {
  const marks: TimescaleMark[] = [];

  //...your code here

  try {
    const currentNewsEvent = JSON.parse(
      localStorage.getItem("iccandle:current-news-event") || "{}",
    ) as NewsEventType;

    const { id, timestamp, event_name, currency, forecast, previous, actual } =
      currentNewsEvent;

    if (!id || !Number.isFinite(timestamp) || timestamp <= 0) return;
    const hasAlready = marks.some((m) => String(m.id) === String(id));
    if (hasAlready) return;

    marks.push({
      id: id,
      time: timestamp / 1000,
      color: "green",
      label: event_name.slice(0, 1),
      tooltip: [
        event_name,
        `Current Forecast: ${forecast || "-"}`,
        `Previous Forecast: ${previous || "-"}`,
        `Previous Actual: ${actual || "-"}`,
      ],
      ...(currency ? { imageUrl: `/images/symbols/${currency}.svg` } : {}),
    });
  } catch {
    // no-op
  }
  onResult(marks);
};
```

Adjust paths and types to match your app and TradingView typings.

## License

MIT. TradingView Charting Library is subject to its own license from TradingView.