开始使用
用自有图表构建宿主页面,再将 iC Candle 嵌入应用加载为 iframe。
步骤 1 — 挂载 iframe
<iframe
id="iccandle"
src="https://embed-iccandle-app.iccandle.ai/zh?theme=light&header=false"
title="iC Candle"
allow="clipboard-write; payment"
style="width: 100%; height: 100%; border: 0;"
></iframe>
使用语言路径(/en、/zh 等)、可选的 theme(light | dark | system),以及在希望隐藏嵌入顶栏时使用的 header=false。
完整的扫描查询参数说明见 搜索参数。
不要用会阻止弹窗(Google / Cognito 登录)或顶层导航(Stripe Checkout)的方式 sandbox iframe。
步骤 2 — 发送父页面 origin(计费)
在 iframe load 时告知嵌入端你的 origin,以便 Stripe 可返回到你的域名:
const EMBED_ORIGIN = "https://embed-iccandle-app.iccandle.ai";
const iframe = document.getElementById("iccandle");
iframe.addEventListener("load", () => {
iframe.contentWindow.postMessage(
{ type: "parent-origin", origin: window.location.origin },
EMBED_ORIGIN,
);
// Stripe 重定向回宿主 ?payment=success 之后:
if (new URLSearchParams(location.search).get("payment") === "success") {
iframe.contentWindow.postMessage({ type: "payment-success" }, EMBED_ORIGIN);
}
});
步骤 3 — 监听嵌入消息
仅接受来自嵌入 origin 的消息。载荷可能是普通对象,也可能是 JSON 字符串,因此要先统一解析 event.data。
对于 iframe 宿主,当前嵌入协议主要使用带命名空间的包裹格式:
type EmbedMessage = {
name?: string;
data?: unknown;
type?: string;
pattern?: unknown;
};
大多数外发消息使用 name,例如 auth.signIn、selector.loading、chart.play、news.eventClicked、nav.click。
某些形态选择流程仍会直接发送对象而不是 name 包裹格式,因此宿主应同时检查 message.name 与 message.type。
共享类型
/** OHLC K 线,与 cacheCandle 及回放载荷一致 */
interface Candle {
o: number; // open
h: number; // high
l: number; // low
c: number; // close
timestamp: number; // K 线时间,unix 秒
}
/** 日历 / 经济事件(新闻流程) */
interface NewsEvent {
id: string;
timestamp: number; // unix ms
event_name: string;
metric: string;
forecast: string;
actual: string;
previous: string;
currency: string;
}
处理器骨架
window.addEventListener("message", (event) => {
if (event.origin !== EMBED_ORIGIN) return;
let message;
try {
message =
typeof event.data === "string" ? JSON.parse(event.data) : event.data;
} catch {
return;
}
switch (message?.name) {
case "auth.signIn":
localStorage.setItem("iccandle_token", message.data.idToken);
break;
case "selector.loading":
toggleHostLoading(message.data.isLoading);
break;
case "chart.play":
onChartReplay(message.data);
break;
case "chart.stop":
clearReplayAndOverlays();
break;
case "selector.closeResult":
clearReplayAndOverlays();
break;
case "nav.click":
onEmbedNav(message.data.href);
break;
case "news.eventClicked":
onNewsEvent(message.data.event, message.data.similarDetails);
break;
case "news.selectedEventPayloads":
persistSelectedNewsEvents(message.data);
break;
case "news.back":
case "news.backToSimilarEvents":
clearNewsReplayAndEventLine();
break;
case "news.iframeReady":
onEmbedReady();
break;
case "news.analyzeImpact":
onNewsAnalyzeImpact();
break;
case "pattern.classicPatternSelected":
drawPatternRange(message.data?.pattern);
break;
case "pattern.clearClassicPatternSelected":
clearPatternRange();
break;
case "pattern.custom_pattern_selected":
drawTrackedPatternRange(message.data?.pattern);
break;
case "pattern.clear_custom_pattern_selected":
clearTrackedPatternRange();
break;
}
});
name 消息(嵌入端 → 父页面)
name | 触发时机 | 载荷 | 宿主动作 |
|---|---|---|---|
auth.signIn | iframe 内登录完成(邮箱密码或 OAuth) | { name: "auth.signIn", data: { idToken: string } } | 将 data.idToken 存为 iccandle_token,供 Bearer API 调用 |
selector.loading | 扫描或 AI 准备开始 / 结束 | { name: "selector.loading", data: { isLoading: boolean } } | 显示 / 隐藏图表或页面上的加载 UI |
chart.play | 用户从新闻或图表分析流程启动回放 | { name: "chart.play", data: { isReplay: true, predictCandles?: Candle[] | null, playEndTimestamp: number | null, selectedCandles: Candle[] | null } } | 注入回放 K 线 / 生成 K 线,并在 playEndTimestamp 前阻止实时 tick |
chart.stop | 回放被清除或关闭 | { name: "chart.stop", data: { isReplay: false, predictCandles?: Candle[] | null, playEndTimestamp: null, selectedCandles: null } } 或 { name: "chart.stop", data: null } | 清除回放 K 线与回放专属叠加 |
selector.closeResult | 用户关闭当前结果 / 分析面板 | { name: "selector.closeResult", data: null } | 清除回放与结果专属叠加 |
nav.click | 用户点击嵌入端头部导航 | { name: "nav.click", data: { href: string } } | 可选,响应标签或路由切换 |
news.eventClicked | 用户选中一个日历 / 经济事件 | { name: "news.eventClicked", data: { event: NewsEvent, similarDetails: object | null } } | 将图表居中到事件位置,绘制竖线并高亮时间轴标记 |
news.selectedEventPayloads | 用户选择扫描器中的新闻事件 | { name: "news.selectedEventPayloads", data: NewsEvent[] } | 若图表支持时间轴标记则持久化这些事件 |
news.back | 用户离开新闻详情或相似事件视图 | { name: "news.back", data: null } | 清除事件竖线、标记与新闻回放叠加 |
news.backToSimilarEvents | 用户在新闻流程内返回 | { name: "news.backToSimilarEvents", data: null } | 与 news.back 相同,清理事件专属图表装饰 |
news.iframeReady | 相似事件 iframe 完成初始加载 | { name: "news.iframeReady", data: null } | 可选的宿主生命周期钩子 |
news.analyzeImpact | 用户请求新闻影响分析 | { name: "news.analyzeImpact", data: null } | 可选,按需接入分析相关宿主 UI |
pattern.classicPatternSelected | 用户选择一个经典 / 通用形态匹配 | { name: "pattern.classicPatternSelected", data: { pattern: object } } | 高亮对比形态区间 |
pattern.clearClassicPatternSelected | 用户清除经典 / 通用形态选择 | { name: "pattern.clearClassicPatternSelected", data: null } | 移除形态高亮 |
pattern.custom_pattern_selected | 用户选择一个追踪形态 | { name: "pattern.custom_pattern_selected", data: { pattern: object } } | 高亮追踪形态区间 |
pattern.clear_custom_pattern_selected | 用户清除追踪形态选择 | { name: "pattern.clear_custom_pattern_selected", data: null } | 移除追踪形态高亮 |
playEndTimestamp 使用 unix 秒。
pattern 对象来自扫描 / 形态追踪结果。宿主通常只需要其中的起止时间戳(或等价区间字段)来绘制高亮,可在浏览器开发者工具中查看一条实时消息后映射到你的图表 API。
步骤 4 — 认证
应用路由需要会话。无 cookie 打开嵌入端会重定向到 /{locale}/sign-in。
- 邮箱密码与 OAuth 在 iframe 内部完成(因提供方禁止被嵌,OAuth 使用弹窗)。
- 成功后嵌入端发送
auth.signIn,载荷为{ data: { idToken } }。 - 若宿主将调用
cacheCandle或其他 API,请将该 token 存为iccandle_token。
步骤 5 — 执行扫描
5a — 缓存 K 线
对所选窗口 POST https://scan-service.iccandle.ai/cacheCandle:
Authorization: Bearer <iccandle_token>
Content-Type: application/json
{
"candles": [
{ "o": 1.08, "h": 1.09, "l": 1.07, "c": 1.085, "timestamp": 1719878400 }
]
}
| 字段 | 说明 |
|---|---|
o, h, l, c | 开 / 高 / 低 / 收 |
timestamp | Unix 秒(K 线时间) |
响应包含 id — 用作 iframe URL 中的 cid。
5b — 导航 iframe
const params = new URLSearchParams({
tk: "10", // top_k(默认 10)
symbol: "EURUSD",
res: "60", // 匹配周期
ref_res: "60", // 缓存窗口的参考周期
cid: candleId, // 来自 cacheCandle
from: String(startTimestampSec * 1000), // ms — 新闻区间辅助
to: String(endTimestampSec * 1000), // ms — 新闻区间辅助
theme: "light",
header: "false",
model: "light", // AI 结果模型:light | pro
temperature: "1", // AI 结果温度:0.5 | 1 | 1.5
});
// 可选:&fs=EURUSD,GBPUSD &period=<unixSec>
iframe.src = `${EMBED_ORIGIN}/zh?${params}`;
首页扫描必填:symbol、res、ref_res、cid。缺失时嵌入端会跳过 /search。
所有受支持的查询参数、默认值与按路由划分的说明见 搜索参数。
步骤 6 — 根据消息驱动图表
| 消息 | 宿主职责 |
|---|---|
chart.play / chart.stop | 在图表上注入或清除回放 K 线 / 生成 K 线 |
pattern.classicPatternSelected / pattern.custom_pattern_selected | 高亮对比形态区间 |
pattern.clearClassicPatternSelected / pattern.clear_custom_pattern_selected | 移除该区间 |
news.eventClicked / news.selectedEventPayloads / news.back | 新闻标记、竖线、选中事件状态与事件回放清理 |
nav.click | 可选,响应用户切换嵌入标签(data.href)。使用 header=false 时不会发出。 |
图表库由你掌控;嵌入端仅通过 postMessage 表达意图。
最小布局
将图表与 iframe 并排(小屏可上下堆叠)。给 iframe 固定高度或 flex 子项,以便结果 UI 在框内滚动。
合作方嵌入时建议使用 header=false,让宿主导航保持主导,iframe 专注于扫描结果、形态追踪与新闻流程。
下一步
参见 搜索参数 了解扫描 URL 合约,窗口消息 了解完整消息目录,数据类型 了解共享载荷,以及 API 参考 了解路由与协议细节。