跳到主要内容

开始使用

步骤 1 — 获取 TradingView Charting Library 访问权限​

@iccandle/vuejs-widget 依赖 TradingView Advanced Charts(Charting Library)。请按 TradingView 官方 Get started 申请访问、安装库,并将其作为静态文件托管。

将构建放到应用可加载的路径(例如 Vite 的 public/charting_library/)。图表库不会打包进 @iccandle/vuejs-widget;运行时通过 widget 选项中的 library_path 加载。

步骤 2 — 安装包​

npm install @iccandle/vuejs-widget
# 或
pnpm add @iccandle/vuejs-widget

确保已安装 vue,并满足 peer 版本要求(Vue 3.5+)。axios 为本包的运行时依赖。

步骤 3 — 使用回放辅助函数初始化 TradingView​

结果 iframe 的图表回放 / 预测需要配置以下两个辅助函数:

辅助函数位置作用
withPlayChartdatafeed包装 subscribeBars,使组件可注入回放 K 线,并在回放期间阻止实时 tick。
getCustomIndicatorscustom_indicators_getter注册 “Generated Candles” 指标,用于绘制预测 OHLC 叠加。

getCustomIndicators 的主题("light" / "dark")须与图表主题一致。签名与指标名称见 配置。

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, shallowRef } from "vue";
import type {
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
} from "charting_library/charting_library";
import { widget } from "charting_library/charting_library";
import { withPlayChart, getCustomIndicators } from "@iccandle/vuejs-widget";

const LIBRARY_PATH = "/charting_library/";

const containerRef = ref<HTMLDivElement | null>(null);
const chartWidget = shallowRef<IChartingLibraryWidget | null>(null);

let tv: IChartingLibraryWidget | null = null;

onMounted(() => {
const el = containerRef.value;
if (!el) return;

const options: ChartingLibraryWidgetOptions = {
container: el,
library_path: LIBRARY_PATH,
symbol: "EURUSD",
interval: "60" as ResolutionString,
datafeed: withPlayChart(yourDatafeed),
locale: "zh",
autosize: true,
drawings_access: {
type: "black",
tools: [{ name: "Date Range" }],
},
custom_indicators_getter: () => getCustomIndicators("light"),
};

tv = new widget(options);
chartWidget.value = tv;
});

onBeforeUnmount(() => {
try {
tv?.remove();
} catch {
/* no-op */
}
chartWidget.value = null;
tv = null;
});
</script>

<template>
<div ref="containerRef" style="height: 100%; min-height: 400px" />
</template>

你必须提供有效的 datafeed、symbol、interval、locale,以及 TradingView 配置与应用所需的其他选项。widget 与类型的导入路径因项目而异 — 请遵循 TradingView 文档。

withPlayChart 也接受工厂函数及其参数:withPlayChart(createDatafeed, arg1, arg2)。

时间轴新闻标记是可选的 — 若需要,请在 yourDatafeed 上实现 getTimescaleMarks(步骤 6)。

若集成仅在 onChartReady 后暴露实例,请在该回调中设置 chartWidget,而不是在 new widget(...) 后立即设置。

步骤 4 — 用 WidgetIccandle 包裹图表​

WidgetIccandle 包裹图表并渲染扫描器浮层。宿主负责结果 iframe:传入 submitCallback 以接收扫描或新闻导航后的嵌入 URL,并用该回调设置 iframe 的 src。当应用已提供导航栏时,建议使用 header=false。

传入 result-iframe-ref,以便组件向嵌入端发送 open-pricing 与 chart-resolution。在 iframe 加载时调用 handleResultIframeLoad,以便嵌入端获知父页面 origin。

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

const chartWidget = shallowRef<IChartingLibraryWidget | null>(null);
const iframeSrc = ref(`${WIDGET_RESULT_URL}/zh?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="zh"
:iframe-loaded="iframeLoaded"
:result-iframe-ref="resultIframeBox"
>
<div style="height: 100%; min-height: 400px" />
</WidgetIccandle>
<iframe
ref="resultIframeRef"
:src="iframeSrc"
title="iC Candle results"
@load="handleIframeLoad"
/>
</template>

详见 配置 → submitCallback 与 搜索参数 → header。

步骤 5 — 在绘图工具栏中禁用 Date Range​

扫描器以编程方式将 TradingView 的 Date Range 形状(date_range)用作 K 线窗口选择器。请在 drawings_access 中将其加入黑名单,避免用户从绘图工具栏再添加一个 Date Range:

drawings_access: {
type: "black",
tools: [{ name: "Date Range" }],
},

type: "black" 会隐藏列表中的绘图工具。组件仍会在图表上创建并更新选择器。

默认窗口大小为 25 根 K 线。

步骤 6 — 可选:在 datafeed 上添加新闻标记​

日历 / 经济事件标记不是组件内置功能。若要在时间轴上显示它们,请在你自己的 TradingView datafeed(步骤 3 中传给 withPlayChart 的对象)上实现 getTimescaleMarks。同时在 datafeed 的 onReady 配置中设置 supports_timescale_marks: true。

时间轴上带货币图标与竖线的新闻事件标记

组件将事件存入 localStorage(tv:clicked-news-event、tv:selected-news-events)。你的 datafeed 应读取这些键并返回 TradingView 标记。完整示例见 可选:时间轴标记。

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);
},

NewsEventType 与 NewsEvent 一致。从你的 Charting Library 类型中导入 LibrarySymbolInfo、GetMarksCallback 与 TimescaleMark。

认证​

用户通过嵌入式结果应用登录。成功后,嵌入端发送 auth.signIn,组件将 data.idToken 作为 iccandle_token 写入 localStorage。扫描缓存与形态追踪 API 使用该令牌作为 Bearer 凭证。auth.signOut 会清除该令牌。

无需向 WidgetIccandle 传入 API 密钥 prop — 认证由 iframe 登录流程处理。必须在 iframe 加载时调用 handleResultIframeLoad,以便在登录前(重试)发送 parent-origin;否则令牌可能无法落地。

企业合作伙伴:见 无缝认证示例。

主题与语言​

  • theme="light" / theme="dark" — 强制扫描器与 iframe 使用对应配色。
  • theme="system" — 跟随 prefers-color-scheme。
  • language — 可选 en、zh、vi、th、ko、ja、mn、ru(默认 en)。应用于扫描器 UI 与结果 iframe 语言。
  • chartMaskColors — 回放时用于隐藏尚未揭示的预测 K 线的面板匹配色。默认 { light: "#ffffff", dark: "#0F0F0F" }。若 TradingView 图表面板背景不同,请传入对应的十六进制颜色(见 chartMaskColors)。
  • bg_dark / bg_light — 各主题下结果 iframe 的十六进制页面背景(# 可选)。组件会把它们追加到扫描、新闻与定价 URL 上。请在初始 iframe src 上带上相同值,使首次加载一致(见 bg_dark 与 bg_light)。

完整示例​

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

const LIBRARY_PATH = "/charting_library/";
const availableIntervals = ["1", "5", "15", "30", "60"];

const chartWidget = shallowRef<IChartingLibraryWidget | null>(null);
const iframeSrc = ref(
`${WIDGET_RESULT_URL}/zh?theme=light&header=false&bg_dark=0F0F0F&bg_light=ffffff`,
);
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 },
);

function setChartWidget(widget: IChartingLibraryWidget | null) {
chartWidget.value = widget;
}

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);
};

function onCloseResult() {
/* 可选:嵌入端发送 selector.closeResult 时处理 */
}
</script>

<template>
<div class="iccandle-selector-widget__container">
<div class="iccandle-selector-widget__chart-pane">
<WidgetIccandle
:chart-widget="chartWidget"
:submit-callback="submitCallback"
theme="light"
language="zh"
:iframe-loaded="iframeLoaded"
:result-iframe-ref="resultIframeBox"
:chart-mask-colors="{ light: '#ffffff', dark: '#0F0F0F' }"
bg_dark="#0F0F0F"
bg_light="#ffffff"
:available-intervals="availableIntervals"
:on-close-result="onCloseResult"
>
<TradingviewChartContainer
:library-path="LIBRARY_PATH"
@chart-widget-ready="setChartWidget"
/>
</WidgetIccandle>
</div>
<iframe
ref="resultIframeRef"
:src="iframeSrc"
class="iccandle-selector-widget__iframe"
title="iC Candle results"
@load="handleIframeLoad"
/>
</div>
</template>

TradingviewChartContainer 使用 withPlayChart、getCustomIndicators 以及 Date Range 黑名单初始化 TradingView — 见 widget-vuejs-iccandle-app 演示(src/tradingview/TradingviewChart.vue)。

将 yourDatafeed 与 widget 选项替换为你的真实 datafeed 与 TradingView 配置。

功能概览​

形态扫描器​

  • 订阅图表就绪、品种、周期与绘图事件。
  • 管理 date_range 多点绘图以选择 K 线窗口。
  • 扫描时:将 K 线提交到缓存服务,再通过 submitCallback 传入带搜索参数的嵌入 URL(symbol、res、ref_res、cid、tk、from、to,以及可选的 fs / period / model / temp、theme,以及已设置时的 bg_dark / bg_light)。
  • 可选高级筛选(品种、top-k、回看周期、AI 模型、温度)保存在 localStorage 的 search-filter 下(用户不持久化时保存在 sessionStorage 的 search-filter-session)。

结果 iframe​

  • 宿主持有 iframe;初始 src 通常为 ${WIDGET_RESULT_URL}/{language}?theme=…&header=false,设置嵌入页背景时再加上 bg_dark / bg_light。
  • 扫描 / 新闻导航后,submitCallback 会收到下一个 URL — 据此设置 iframe src(如需隐藏顶栏请重新加上 header=false)。
  • iframe 加载后,调用 handleResultIframeLoad(iframe),父页面会(重试)发送 { type: "parent-origin", origin },并在 Stripe 返回后发送 { type: "payment-success" }。

形态追踪​

用户可从扫描器弹窗打开订阅模态框,追踪自定义形态(名称 + 周期 + 品种)。需要有效的 iccandle_token。若不需要该操作,可传 :is-show-tracker-button="false"。

新闻 / 经济事件​

  • 点击时间轴标记可打开事件信息模态框(货币日历事件;跳过假期 / early / 纯情绪标记)。
  • 「查看详情」在 iframe 中加载相似事件,并在图表上绘制垂直线。
  • 若 datafeed 实现了 getTimescaleMarks,可通过 localStorage 键 tv:selected-news-events 与 tv:clicked-news-event 驱动标记 — 详见 步骤 6 与 可选:时间轴标记。

常见模式​

模式做法
全页图表 + 结果使用打包注入的分栏类:.iccandle-selector-widget__container、__chart-pane、__iframe(小于 1024px 时纵向堆叠)。
主题同步从应用壳传入 theme(light / dark / system)。将 chartMaskColors 与 paneProperties.background 对齐。
嵌入页背景传入 bg_dark 与 bg_light,并在初始 iframe src 上带上相同的十六进制颜色。
语言同步传入 language 以匹配应用语言。
隐藏嵌入顶栏在初始 iframe URL 与 submitCallback 内追加 header=false。
等 iframe 就绪再扫描结果 iframe 加载完成后传 :iframe-loaded="true"(若不需要门控可省略该 prop)。
隐藏扫描 / 追踪:is-show-scan-button="false" / :is-show-tracker-button="false" / :is-show-scanner-popup="false"。
时间轴新闻标记可选:在你的 datafeed 上实现 getTimescaleMarks — 见 步骤 6。