配置
导出
| 名称 | 类型 | 说明 |
|---|---|---|
WidgetIccandle | 组件 | 扫描器浮层(宿主负责结果 iframe) |
WidgetIccandleProps | 类型 | WidgetIccandle 的 props |
WidgetChartMaskColors | 类型 | chartMaskColors 的 { light?: string; dark?: string } |
WidgetLanguage | 类型 | 支持的语言代码 |
withPlayChart | 函数 | 包装 TradingView datafeed(或工厂函数)以注入回放 K 线 |
getCustomIndicators | 函数 | 返回 iC Candle 自定义指标(生成 K 线) |
GeneratedCandlesTheme | 类型 | getCustomIndicators 的 "light" | "dark" |
handleResultIframeLoad | 函数 | 在 iframe 加载时向结果 iframe 发送 parent-origin / payment-success |
useICCandleAuth | Hook | 宿主侧会话:来自 iccandle_token 的 { isAuthenticated, idToken } |
ICCandleAuth | 类型 | useICCandleAuth 的返回类型 |
ICCANDLE_TOKEN_KEY | 常量 | "iccandle_token" |
WIDGET_RESULT_URL | 常量 | "https://embed-iccandle-app.iccandle.ai" |
WidgetIccandle props
| Prop | 类型 | 必需 | 说明 |
|---|---|---|---|
chartWidget | IChartingLibraryWidget | null | 是 | 实时 TradingView widget(就绪前为 null)。 |
children | ReactNode | ((chartRefs) => ReactNode) | 是 | 图表 UI,或接收高亮 K 线 / 生成 K 线指标 refs 的 render prop。 |
submitCallback | (iframeSrc: string) => void | 是 | 扫描、新闻导航或定价回退需要更新结果 iframe 时调用。将 iframe 的 src 设为该 URL(可选追加 header=false)。 |
theme | "light" | "dark" | "system" | 否 | 扫描器与 iframe 主题。"system" 跟随 prefers-color-scheme。 |
chartMaskColors | WidgetChartMaskColors | 否 | 按主题隐藏尚未揭示的预测 K 线的颜色。必须与图表面板背景一致。默认:{ light: "#ffffff", dark: "#0F0F0F" }。 |
bg_dark | string | 否 | 深色主题下的嵌入页背景。十六进制,# 可选。省略则不附加 bg_dark 查询参数。 |
bg_light | string | 否 | 浅色主题下的嵌入页背景。十六进制,# 可选。省略则不附加 bg_light 查询参数。 |
language | WidgetLanguage | 否 | UI 与 iframe 语言。可选:en、zh、vi、th、ko、ja、mn、ru。默认 en。 |
onCloseResult | () => void | 否 | 嵌入端发送 selector.closeResult 时调用。 |
iframeLoaded | boolean | 否 | 为 false 时,在结果 iframe 就绪前禁用扫描 / 追踪操作。默认扫描器行为为可用。 |
resultIframeRef | RefObject<HTMLIFrameElement | null> | 否 | 结果 iframe 的 ref。用于接收 open-pricing 与 chart-resolution postMessage。 |
isShowScanButton | boolean | 否 | 在弹窗中显示扫描。默认 true。 |
isShowTrackerButton | boolean | 否 | 在弹窗中显示形态追踪。默认 true。 |
isShowScannerPopup | boolean | 否 | 显示扫描器弹窗。默认 true。 |
availableIntervals | string[] | 否 | 从嵌入端选择形态时允许绘制日期范围的周期。 |
onScanClick | () => void | 否 | 用户点击扫描时触发,发生在登录 / 配置 / 扫描逻辑之前。 |
IChartingLibraryWidget 必须从你的 Charting Library 类型定义导入 — 本包不重新导出 TradingView 类型。
submitCallback
必需。宿主负责结果 iframe。用户执行扫描、从新闻标记打开相似事件,或(在无 iframe ref / 父窗口时)请求定价时,WidgetIccandle 会构建完整嵌入 URL 并传给 submitCallback。请用该值更新 iframe 的 src。
常见 URL 形态:
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
可选扫描筛选也可能追加:fs(品种筛选)、period、model(light / standard / plus / pro)、temp(0.5 / 1 / 1.5)。设置了 bg_dark / bg_light 时,这些颜色也会追加到同一批 URL 上。
import { handleResultIframeLoad, WIDGET_RESULT_URL } from "@iccandle/reactjs-widget";
const [iframeSrc, setIframeSrc] = useState(
`${WIDGET_RESULT_URL}/zh?theme=light&header=false`,
);
const resultIframeRef = useRef<HTMLIFrameElement | null>(null);
const submitCallback = (nextSrc: string) => {
const url = new URL(nextSrc);
url.searchParams.set("header", "false");
setIframeSrc((prev) => (prev === url.toString() ? prev : url.toString()));
};
const handleIframeLoad = () => {
setIframeLoaded(true);
handleResultIframeLoad(resultIframeRef.current);
};
<WidgetIccandle
chartWidget={chartWidget}
submitCallback={submitCallback}
theme="light"
language="zh"
iframeLoaded={iframeLoaded}
resultIframeRef={resultIframeRef}
>
{/* 图表 */}
</WidgetIccandle>
<iframe
ref={resultIframeRef}
src={iframeSrc}
title="iC Candle results"
onLoad={handleIframeLoad}
/>
header=false
在嵌入 URL(初始 src 以及 submitCallback 内)追加 header=false,可在宿主已提供导航时隐藏嵌入应用顶栏。页脚与页内 UI 仍会渲染;顶栏标签触发的 nav.click 消息不会发出。
扫描器生成的 URL 会包含扫描参数与 theme,但不会自动添加 header=false — 需要时由宿主自行设置。
完整查询参数说明见 搜索参数 → header。
handleResultIframeLoad(iframe)
在结果 iframe 的 onLoad 中调用。它会向嵌入端发送宿主 origin(用于 Stripe 返回,并使 auth.signIn 指向正确宿主);当父页面 URL 带有 ?payment=success 时,还会发送 payment-success 以刷新订阅状态。
parent-origin 会在 0 / 300 / 1000 / 2500 ms 重试,因为嵌入端没有消息缓冲 — 必须先挂上监听器再收到消息。
import { handleResultIframeLoad } from "@iccandle/reactjs-widget";
handleResultIframeLoad(resultIframeRef.current);
useICCandleAuth()
宿主侧用于判断结果 iframe 是否已经登录。读取 iccandle_token,在 JWT exp 前 60 秒视为过期,并在 auth.signIn / auth.signOut 时更新。
import { useICCandleAuth } from "@iccandle/reactjs-widget";
const { isAuthenticated, idToken } = useICCandleAuth();
| 字段 | 类型 | 说明 |
|---|---|---|
isAuthenticated | boolean | 存在且未过期的 iccandle_token 时为 true |
idToken | string | null | 当前身份令牌;未登录时为 null |
企业合作伙伴:先检查 isAuthenticated,仅在其为 false 时调用 get-user-token。见 无缝认证示例。
withPlayChart(datafeed)
图表回放 / 预测所必需。包装 TradingView datafeed 的 subscribeBars,使组件可以:
- 注入回放 K 线 — 当嵌入端发送
chart.play、chart.stop或回放载荷时,写入实时 tick 流。 - 阻止实时 tick — 回放期间屏蔽实盘更新,避免覆盖预测 K 线。
签名
withPlayChart(datafeed: IBasicDataFeed): IBasicDataFeed
| 参数 | 类型 | 说明 |
|---|---|---|
datafeed | IBasicDataFeed | 已就绪的 datafeed 实例(须提供 onReady)。 |
factory, ...args | 函数重载 | 可选:传入工厂函数及其参数;withPlayChart 会调用并包装返回值。 |
用法
import { withPlayChart } from "@iccandle/reactjs-widget";
datafeed: withPlayChart(myDatafeed),
// 或: withPlayChart(createDatafeed, arg1, arg2)
说明
- 返回值仍是普通 datafeed;仅包装了
subscribeBars。可选的getTimescaleMarks等方法保持不变。 - 需与
getCustomIndicators一起使用 — 缺一不可,否则回放 / 预测叠加无法生效。
getCustomIndicators(theme?)
注册 iC Candle 自定义指标,用于在主系列上绘制生成 / 预测 K 线。返回 TradingView CustomIndicator[] 的 Promise,供 custom_indicators_getter 使用。
签名
getCustomIndicators(theme?: "light" | "dark"): Promise<readonly CustomIndicator[]>
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
theme | "light" | "dark" | "light" | 面板遮罩颜色,用于在预测时间点盖住底层 K 线。须与图表主题一致。 |
返回的指标
| 指标名称 | 作用 |
|---|---|
Generated Candles Background - By iC Candle | 隐藏的遮罩层,在预测时间戳处覆盖真实 K 线。 |
Generated Candles - By iC Candle | 可见 OHLC 叠加(上涨 #00dfb9 / 下跌 #ffb84d),绘制预测 K 线。 |
当嵌入端发送回放 / 预测数据时,WidgetIccandle 会创建并驱动这些指标。
用法
import { getCustomIndicators } from "@iccandle/reactjs-widget";
const options: ChartingLibraryWidgetOptions = {
// ...
datafeed: withPlayChart(yourDatafeed),
custom_indicators_getter: () => getCustomIndicators("light"),
};
保持 getCustomIndicators(theme) 与图表主题一致(并尽量与 WidgetIccandle 的 theme prop 一致)。指标创建后,WidgetIccandle 会用 chartMaskColors 覆盖背景指标颜色,使尚未揭示的预测 K 线与面板背景一致。
与 withPlayChart 配合
| 辅助函数 | 作用 |
|---|---|
withPlayChart | 允许组件向 datafeed 推送 K 线,并在回放期间屏蔽实时 tick。 |
getCustomIndicators | 以自定义指标绘制预测 K 线视觉效果(遮罩 + 彩色 OHLC)。 |
结果 iframe 的回放 / 预测功能需要两者同时配置。
chartMaskColors
回放 / 预测时,组件会绘制生成 K 线,并用实心遮罩色盖住尚未揭示的 K 线。该颜色必须与 TradingView 图表面板背景(paneProperties.background)一致;否则未揭示的 K 线会在回放光标前方显示为色块。

| 键 | 默认 | 使用时机 |
|---|---|---|
light | #ffffff | theme="light",或 theme="system" 且系统为浅色 |
dark | #0F0F0F | theme="dark",或 theme="system" 且系统为深色 |
仅当前主题对应的值会生效。每个键都是可选的 — 省略则使用默认值。图表库接受的任意 CSS 颜色字符串均可;不要使用透明色,否则真实系列会透出来。
若图表面板不是默认的白色 / #0F0F0F,请传入匹配的值:
<WidgetIccandle
chartWidget={chartWidget}
submitCallback={submitCallback}
theme="dark"
chartMaskColors={{ dark: "#131722", light: "#ffffff" }}
>
{/* 图表 */}
</WidgetIccandle>
WidgetIccandle 会将该颜色作为 Generated Candles Background 指标的覆盖色。更改 chartMaskColors 或 theme 会重建这些指标,因此宿主可在图表背景变化时同步更新。
这不是扫描窗口(date_range)的填充色 — 那由 .iccandle-selector-widget 上的 --iccandle-primary 控制。
bg_dark 与 bg_light
可选的十六进制颜色,用于结果 iframe 的页面背景。组件会在它构建的嵌入 URL 上追加 bg_dark 与 bg_light 查询参数:扫描结果、新闻详情,以及定价回退。
| Prop | 查询参数 | 生效时机 |
|---|---|---|
bg_dark | bg_dark | 嵌入主题解析为深色 |
bg_light | bg_light | 嵌入主题解析为浅色 |
开头的 # 可省略(#131722 与 131722 相同)。接受 3、6、8 位十六进制。空值或非十六进制会被丢弃,对应参数不会追加。省略某个 prop 时,该主题使用嵌入端默认背景。两个都省略时,组件不追加任何 bg_* 参数。
初始 iframe src 由宿主负责。请在那里带上相同参数,这样首次加载就一致。嵌入端收到有效颜色后会保存它,之后省略 bg_* 的导航仍沿用该颜色。
const [iframeSrc, setIframeSrc] = useState(
`${WIDGET_RESULT_URL}/zh?theme=dark&header=false&bg_dark=131722&bg_light=ffffff`,
);
<WidgetIccandle
chartWidget={chartWidget}
submitCallback={submitCallback}
theme="dark"
bg_dark="#131722"
bg_light="#ffffff"
>
{/* 图表 */}
</WidgetIccandle>
这些 prop 设置的是嵌入页背景。图表上的回放遮罩仍由 chartMaskColors 控制。
认证与存储
| 键 | 用途 |
|---|---|
iccandle_token | 扫描缓存、形态追踪及相关 API 的 Bearer 令牌。由嵌入端通过 auth.signIn 写入;auth.signOut 时清除。 |
search-filter | 持久化的扫描器高级选项(symbols、top_k、period、model、temperature)。 |
search-filter-session | 用户不持久化时的会话级筛选副本。 |
scanner-always-show | 扫描器是否保持可见("true" / 未设置)。 |
tv:selected-news-events | 用作时间轴标记的新闻事件 JSON 数组。 |
tv:clicked-news-event | 最近点击的日历事件,用于标记高亮。 |
tv:latest-symbol | 最近的图表品种(演示应用 / 回退使用)。 |
postMessage 桥接
仅接受来自结果源(https://embed-iccandle-app.iccandle.ai)的消息。嵌入 → 父页面载荷使用 { name, data } 结构。
| 方向 | Name | 效果 |
|---|---|---|
| 嵌入 → 父页面 | chart.play / chart.stop | 在图表上注入 / 清除回放 K 线 |
| 嵌入 → 父页面 | selector.loading | 切换扫描器加载 UI |
| 嵌入 → 父页面 | auth.signIn | 从 data.idToken 持久化 iccandle_token |
| 嵌入 → 父页面 | auth.signOut | 移除 iccandle_token |
| 嵌入 → 父页面 | selector.closeResult | 清除回放状态;调用 onCloseResult |
| 嵌入 → 父页面 | pattern.classicPatternSelected / pattern.custom_pattern_selected | 为对比形态绘制日期范围 |
| 嵌入 → 父页面 | pattern.clearClassicPatternSelected / pattern.clear_custom_pattern_selected | 移除形态日期范围 |
| 嵌入 → 父页面 | news.eventClicked | 居中图表、绘制事件线、显示标记 |
| 嵌入 → 父页面 | news.back / news.backToSimilarEvents | 清除事件线 / 回放 |
| 嵌入 → 父页面 | nav.click | 在 /news 路由上清除日期范围 |
| 嵌入 → 父页面 | chart.requestResolution | 向 iframe 重新发送当前图表周期 |
| 父页面 → 嵌入 | parent-origin | Stripe 返回用的宿主 origin |
| 父页面 → 嵌入 | payment-success | 结账后刷新订阅状态 |
| 父页面 → 嵌入 | open-pricing | 请求嵌入端打开定价(需要 resultIframeRef) |
| 父页面 → 嵌入 | chart-resolution | 当前图表周期(iframeLoaded 变为 true 时发送) |
宿主 → 嵌入辅助函数使用 { type: "parent-origin" | "payment-success" | "open-pricing" | "chart-resolution", ... }。
升级点击会在设置了 resultIframeRef 时向结果 iframe 发送 open-pricing。失败则转发到 window.parent。若组件未被嵌套,则回退为带 ?pricing=true 的 submitCallback。
可选:时间轴标记(新闻/事件)
时间轴上的新闻标记是可选的。WidgetIccandle 不会自动添加它们。若要在图表上显示日历 / 经济事件,请在你自己的 datafeed(传给 withPlayChart 的对象)上实现 TradingView 的 getTimescaleMarks。withPlayChart 只包装 subscribeBars,因此你的 getTimescaleMarks 会原样生效。

请在 datafeed 的 onReady 配置中声明支持(supports_timescale_marks: true),否则图表库不会调用 getTimescaleMarks。
组件会将事件写入 localStorage。你的实现应读取这些键并返回标记:
| 键 | 值 | 标记 |
|---|---|---|
tv:clicked-news-event | 单个事件 JSON | 蓝色标记,富文本提示(实际 / 预测 / 前值) |
tv:selected-news-events | 事件数组 JSON | 绿色标记 |
事件的 timestamp 为 unix 毫秒;TradingView 标记使用 unix 秒(time: timestamp / 1000)。事件结构见 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);
},
};
然后照常包装:datafeed: withPlayChart(yourDatafeed)。
主题
扫描器 UI 在 .iccandle-selector-widget 上使用 CSS 变量:
--iccandle-primary--iccandle-primary-gradient-end--iccandle-background--iccandle-border--iccandle-text--iccandle-secondary--iccandle-font
浅色与深色默认值在打包样式表中。theme prop 会切换 .iccandle-dark 类,并通过 submitCallback 转发到 iframe URL。传入 bg_dark 与 bg_light 可设置嵌入页背景。
回放遮罩色不是 CSS 变量:请用 chartMaskColors 与 paneProperties.background 对齐。
布局辅助类
打包样式包含演示应用使用的分栏类:
| 类名 | 作用 |
|---|---|
.iccandle-selector-widget__container | 横向 flex(小于 1024px 时纵向堆叠)。--chart-pane-size 默认 60%。 |
.iccandle-selector-widget__chart-pane | 图表面板。 |
.iccandle-selector-widget__resize-handle | 拖动手柄;堆叠布局加 --stacked。 |
.iccandle-selector-widget__iframe | 结果面板。 |
行为摘要
- 订阅图表就绪、周期、品种变更、可见范围、绘图事件与时间轴标记点击。
- 管理
date_range多点绘图,供用户调整 K 线窗口(默认:25 根)。 - 将 K 线提交到
https://scan-service.iccandle.ai/cacheCandle,再通过submitCallback传入嵌入 URL(symbol、res、ref_res、cid、tk、from、to、筛选、theme,以及已设置时的bg_dark/bg_light)。 - 与宿主持有的结果 iframe(
https://embed-iccandle-app.iccandle.ai)通过postMessage同步。