mcpField
L
lightweight-charts

lightweight-charts

github.com/tradingview开发17.7k1.5k 次安装6 天前更新
代码仓库

关于 lightweight-charts

lightweight-charts 是一项帮助开发者使用 lightweight-charts API 的技能,适用于下游 npm 项目和上游源码仓库。它优先依据本地类型定义核对版本与 API,并提供 v5 用法及旧版迁移指导。

主要功能

  1. 01根据本地类型定义核实 API
  2. 02区分 npm 使用项目与源码仓库
  3. 03说明 v5 图表、序列和面板用法
  4. 04排查时间、交互和实时更新问题
  5. 05对照 v4 与 v5 的 API 变更

适用场景

  1. 01根据已安装版本编写图表代码
  2. 02排查时间戳单位或数据排序问题
  3. 03将旧版序列与标记代码迁移至 v5

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tradingview/lightweight-charts --skill lightweight-charts

安装后在 Claude Code 中输入/lightweight-charts确认已加载。

详细介绍

Lightweight Charts 技能

无论项目是下游 npm 消费者应用,还是上游 lightweight-charts 源码检出,使用方式都相同。请先判断当前属于哪种情况(见下文),并根据本地可用的类型定义解析所有 API 名称。

源码查找顺序

不要假设当前位于上游源码仓库中。

  1. 在消费者应用中,先检查已安装的包:
    • node_modules/lightweight-charts/package.json,确认实际版本。
    • node_modules/lightweight-charts/dist/typings.d.ts,查看 API 范围。
  2. 在上游仓库中,先检查 dist/typings.d.ts,再检查 src/(如果生成的输出不可用)。
  3. 官方文档和示例可作为辅助依据,但如果与本地类型定义不一致,以本地类型定义为准。

回答前请验证(复制粘贴):

node -p "require('./node_modules/lightweight-charts/package.json').version"
rg -n "createSeriesMarkers|addSeries|IPrimitivePaneRenderer|createImageWatermark|createOptionsChart" node_modules/lightweight-charts/dist/typings.d.ts
# upstream checkout instead of a consumer app:
rg -n "createSeriesMarkers|IPrimitivePaneRenderer|createImageWatermark|createOptionsChart" dist/typings.d.ts src

如果相关文件不可用,请说明无法验证的内容。不要臆造选项名称、方法、导出项或封装行为。

如果用户的代码使用 from lightweight_charts import ...(下划线)导入,使用 pandas/asyncio,或提到 Python 窗口封装,他们很可能使用的是第三方 Python 封装,而不是 TypeScript 包。给出 lightweight-charts v5 JavaScript 核心库的建议前,请先明确说明这一点。

心智模型

八个层次,按依赖顺序排列。大多数错误都源于混淆这些层次:

  1. 图表 — createChart(container, options) 返回一个 IChartApi。每个容器元素对应一个图表。
  2. 序列 — 通过 chart.addSeries(SeriesType, options, paneIndex?) 添加。每次调用都会返回一个 ISeriesApi。v5 要求显式导入序列类型。
  3. 刻度 — chart.timeScale()(ITimeScaleApi)、chart.priceScale(id)(IPriceScaleApi)。控制范围、刻度线、自动缩放和可见性。
  4. 数据模型 — { time, … } 数据点数组。Time = UTCTimestamp | BusinessDay | string。UTCTimestamp 的单位是秒,不是毫秒。每个序列中的时间必须唯一且按升序排列。
  5. 交互 — chart.subscribeCrosshairMove、chart.subscribeClick、timeScale.subscribeVisibleLogicalRangeChange 等。
  6. 布局 — chart.panes()、chart.addPane()。使用超出范围的 paneIndex 添加序列时,会自动创建窗格。
  7. 扩展 — 窗格图元、序列图元、自定义序列、水印、自定义渲染器(通过 fancy-canvas 使用 canvas)。
  8. 封装 — React/Vue/Web Components/iOS/Android。生命周期由封装负责;核心库与框架无关。

v5 基础

以下约定能避免照搬旧代码片段时常见的问题:

import { createChart, CandlestickSeries, LineSeries } from 'lightweight-charts';

const chart = createChart(container, { /* ChartOptions */ });
const candles = chart.addSeries(CandlestickSeries, { /* options */ });
candles.setData([
    { time: 1700000000, open: 1, high: 2, low: 0.5, close: 1.5 },
]);

与旧代码片段相比,v5 的主要变化:

  • chart.addSeries(SeriesType, options, paneIndex?) 取代 addLineSeries / addCandlestickSeries / …
  • createSeriesMarkers(series, markers) 取代 series.setMarkers(...)。返回的图元提供 .setMarkers(...) / .markers()。
  • createTextWatermark(pane, options) 和 createImageWatermark(pane, imageUrl, options) 取代 watermark 图表选项。
  • 序列类型会被 tree-shaking,因此 ESM 必须显式导入这些类型。

如果代码片段调用 addLineSeries 或 series.setMarkers,那么它使用的是 v4 或更早版本。转发前请确认用户使用的版本。

如果任务针对 v5,请直接展示 v5 API,不要混用 v4 语法来含糊其辞。

分类处理(问题 → API → 避免事项)

用户询问首先检查用以下内容作答避免使用
创建 v5 系列包版本 + 类型定义chart.addSeries(SeriesType, options)addLineSeries / addCandlestickSeries
标记是否导出 createSeriesMarkers标记图元 + 必需的 colorseries.setMarkers
时区格式化需求与数据语义在格式化器中使用 Intl.DateTimeFormat虚构的 timeScale.timezone
实时更新更新最后一根柱还是完整替换series.update(point)每次跳动都调用 setData
窗格paneIndex + chart.panes()v5 窗格 API、pane.setHeight私有组件 / DOM 技巧
单柱颜色数据点颜色字段数据点上的 color/borderColor/wickColor用额外系列 / 标记来重新着色
插件图元还是自定义系列脚手架 / 示例 + 公开的坐标 API臆造的渲染器名称
v4 → v5 替换对照表

过时的复制粘贴代码片段是最常见的问题。请直接按以下内容替换:

旧版或错误用法v5 替代方案
chart.addLineSeries(options)chart.addSeries(LineSeries, options)
chart.addCandlestickSeries(options)chart.addSeries(CandlestickSeries, options)
series.setMarkers(markers)createSeriesMarkers(series, markers)
chart 的 watermark 选项createTextWatermark(chart.panes()[0], options)
createImageWatermark(pane, options)createImageWatermark(pane, imageUrl, options)
IPanePrimitivePaneRendererIPrimitivePaneRenderer
timeScale: { timezone }使用 Intl.DateTimeFormat 的标签格式化器
time: Date.now()(毫秒)对于 UTCTimestamp,使用 time: Math.floor(Date.now() / 1000)

路由图(问题 → 查阅位置)

优先查看本地类型定义。以下仓库路径仅存在于上游 lightweight-charts 检出目录中;在使用者的应用中,应将其对应到已安装的包类型定义或官方文档。

任务权威来源
首个图表 / 序列website/docs/intro.mdx、website/tutorials/customization/
时间 / 范围行为website/docs/time-scale.md、website/docs/time-zones.md
实时数据 / 历史数据website/tutorials/demos/realtime-updates.{mdx,js}、website/tutorials/demos/infinite-history.{mdx,js}
空白区域 / 间隙website/tutorials/demos/whitespace.{mdx,js}
价格格式 / 区域设置website/tutorials/customization/price-format.mdx、website/tutorials/demos/custom-locale.{mdx,js}
双刻度 / 反转website/tutorials/how_to/two-price-scales.{mdx,js}、website/tutorials/how_to/inverted-price-scale.{mdx,js}
窗格 / 价格 + 成交量website/docs/panes.md、website/tutorials/how_to/panes.{mdx,js}、website/tutorials/how_to/price-and-volume.{mdx,js}
标记 / 工具提示 / 图例website/tutorials/how_to/series-markers.{mdx,js}、website/tutorials/how_to/tooltips.mdx、website/tutorials/how_to/legends.mdx
十字光标(程序化)website/tutorials/how_to/set-crosshair-position*.js
水印website/tutorials/how_to/watermark.mdx
插件 / 自定义序列 / 基元website/docs/plugins/、plugin-examples/src/plugins/
坐标转换 / 命中测试查看 dist/typings.d.ts 中的 subscribeClick、coordinateToPrice、timeScale().coordinateToLogical
非时间水平刻度 / 期权图表查看 dist/typings.d.ts 中的 createOptionsChart、createChartEx、IHorzScaleBehavior
像素级精准渲染website/docs/plugins/pixel-perfect-rendering/
React / Vue / Web Componentswebsite/tutorials/react/、website/tutorials/vuejs/、website/tutorials/webcomponents/
SSR / 脚本加载 / 打包工具已安装包的 dist/ 文件、包的 exports,以及框架中关于仅客户端组件的文档
iOS / Android 封装website/docs/ios.md、website/docs/android.md
版本迁移website/docs/migrations/from-v2-to-v3.md、from-v3-to-v4.md、from-v4-to-v5.md

如果用户的项目中不存在这些仓库路径,不要为了回答常见用法问题而让他们创建或克隆源码树。请使用 node_modules/lightweight-charts/dist/typings.d.ts 和简洁的 v5 示例。

易踩的坑(断言,而非类别)

时间
  • UTCTimestamp 的单位是秒。 使用 Math.floor(Date.now() / 1000),绝不要使用 Date.now()。
  • 没有内置的 timeScale.timezone 选项。 如需仅转换显示时区,请使用 timeScale.tickMarkFormatter 设置坐标轴标签,并使用 localization.timeFormatter(或自定义的工具提示格式化函数)设置十字光标/悬停标签。使用 Intl.DateTimeFormat(..., { timeZone })、Luxon 或 date-fns-tz,以支持安全处理夏令时的 IANA 时区格式化。
  • 预先平移时间戳会改变图表语义。 这样可以让刻度与选定时区对齐,但也会改变柱形在 UTC 时间轴上的位置。除非用户明确需要平移数据,否则应优先使用格式化函数来显示。
  • 系列数据必须严格按 time 升序排列,且值必须唯一。 重复项会被静默替换;顺序错误的数据点会被丢弃或引发错误。
  • 每个系列只能使用一种时间格式。 可以使用 BusinessDay('YYYY-MM-DD' 字符串或 { year, month, day })或 UTCTimestamp,但不要在同一系列中混用。
  • 无值数据点只包含 time(不包含 value/open/……)。可用它们创建间隔或对齐叠加层;不要将值设为 null。
  • setData 会替换数据集,并且可能重置可见范围。update(point) 会在 time > last 时追加,在 time === last 时替换。 update(point, true) 可更新已有的历史数据点,但速度较慢,且不会插入缺失的旧数据点。要在前面添加较早的历史数据,请构建新数组并再次调用 setData——没有前置插入方法;请保存并恢复可见逻辑范围,避免图表突然跳动。
  • timeScale.setVisibleLogicalRange 使用逻辑范围(柱形索引),而非基于时间的范围。 设置时间边界请使用 setVisibleRange,重置则使用 fitContent()。
刻度和窗格
  • **要显示两个价格刻度,**既需要通过 priceScaleId: 'right'/'left' 为系列指定价格刻度,也需要显示左侧刻度。使用 chart.applyOptions({ leftPriceScale: { visible: true }, rightPriceScale: { visible: true } }) 或 chart.priceScale('left').applyOptions({ visible: true })。
  • 叠加系列(priceScaleId: '' 或任何自定义 ID)默认使用自动缩放且隐藏的刻度。如需显示坐标轴标签,请显式设为可见。
  • autoscaleInfoProvider 会在每次布局时运行。 请保持轻量;不要在每次调用时分配内存。
  • 如果 paneIndex 等于当前窗格数,addSeries(_, _, paneIndex) 会创建窗格。 窗格顺序由创建顺序决定;可通过 chart.panes()[i].setHeight(px) 控制其大小。
  • 如果窗格可能移动,请保留窗格引用。 const mainPane = chart.panes()[0] 始终指向同一个 IPaneApi;在 swapPanes 或移动后,mainPane.paneIndex() 会返回其当前索引。
  • 使用公开的时间刻度事件同步独立图表。 使用 subscribeVisibleLogicalRangeChange 和 setVisibleLogicalRange;避免使用 _private__chartWidget、paneWidgets 或依赖 DOM 偏移的变通方法。
  • 可以读取价格刻度宽度,但无法直接设置。 使用 series.priceScale().width() 或 chart.priceScale(id).width() 测量宽度,以便对齐相邻容器。
标记和交互
  • v5 中的标记是一种图元。 const m = createSeriesMarkers(series, [...]); m.setMarkers([...])。v5 的 ISeriesApi 上不存在 series.setMarkers。
  • 标记的 time 必须与该系列中某个现有数据点的 time 相同。 未对齐的标记会被静默丢弃。
  • subscribeCrosshairMove 在数据范围之外触发时,param.time === undefined。读取前务必检查是否为 null 或 undefined。
  • 在柱形之间或第一个柱形之前,param.seriesData.get(series) 会返回 undefined。
  • 点击/十字光标坐标需要通过 API 转换。 使用 series.coordinateToPrice(y)/series.priceToCoordinate(price) 和 chart.timeScale().coordinateToLogical(x)/logicalToCoordinate(logical)/timeToCoordinate(time);不要根据画布的 DOM 几何信息推断价格或时间。
插件和自定义渲染
  • 从脚手架/示例开始编写插件。 在从头编写基础实现之前,优先使用 npm create lwc-plugin@latest 和 plugin-examples/。
  • 交互式绘图工具需要两个部分: 用于渲染的基础实现/自定义序列,以及将坐标转换为公共 API 所用坐标的图表交互处理器(subscribeClick、subscribeCrosshairMove、拖动状态)。
  • **CanvasRenderingTarget2D 从 fancy-canvas 导入,**而不是从 lightweight-charts 导入。
  • 像素对齐的描边使用 target.useBitmapCoordinateSpace(scope => …);CSS 像素逻辑使用 target.useMediaCoordinateSpace(scope => …)。混用会导致 HiDPI 显示器上出现模糊或亚像素抖动。
  • IPanePrimitive 视图会在整个窗格内渲染;ISeriesPrimitive 视图会被限制在序列范围内。 选择合适的基类。
  • 渲染器接口是 IPrimitivePaneRenderer,不是 IPanePrimitivePaneRenderer。 视图是 IPanePrimitivePaneView,其 renderer() 返回 IPrimitivePaneRenderer | null——这些名称有意采用不对称命名。没有 IPanePrimitivePaneRenderer 导出;不确定时,请在 dist/typings.d.ts 中 grep。
  • ICustomSeriesPaneView 比基础实现更重。 仅在需要真正的序列 API(数据、刻度、自动缩放)时使用。装饰用途请使用基础实现。
封装层(React/Vue 等)
  • **只在 useEffect / onMounted 中创建一次图表,**并在清理时调用 chart.remove() 销毁图表。每次渲染都重新创建会导致 DOM 重复并泄漏监听器。
  • **v5 中可以自动调整大小,**前提是 ResizeObserver 可用且设置了 autoSize: true。如需手动控制,请对容器订阅 ResizeObserver,并调用 chart.resize(width, height)(或 applyOptions({ width, height }))。
  • 不要在每次渲染时都根据 props 调用 setData。 增量更新请使用 series.update(...);只有数据集确实被替换时才调用 setData。
  • applyOptions 中的 undefined 表示“未指定”,而不是“重置”。 { priceFormatter: custom ? fmt : undefined } 会保留当前格式化器。要清除 localization 格式化器或 autoscaleInfoProvider,请传入 null。其他选项没有重置值:请显式传入默认值。
  • Next.js/SSR 必须仅在客户端运行。 将图表代码放入 'use client' 组件中,在 useEffect 中创建图表;必要时,从服务器渲染的页面通过 next/dynamic(..., { ssr: false }) 导入该组件。
  • 纯 HTML 不会解析 npm 包。 独立的 .js 构建会公开 window.LightweightCharts;浏览器中的 ESM 必须导入实际的 .mjs URL 或使用导入映射。只有在打包器/运行时解析包名时,import { createChart } from 'lightweight-charts' 才有效。

标准方案

实时更新
import { createChart, CandlestickSeries } from 'lightweight-charts';

const chart = createChart(container, {});
const series = chart.addSeries(CandlestickSeries, {});
series.setData(initialBars); // full history once

ws.onmessage = ev => {
    const bar = JSON.parse(ev.data); // { time, open, high, low, close }
    series.update(bar); // appends if time > last; replaces if time === last
};

update() 只处理最新柱。要在中间插入或添加到开头,请重建数组并调用 setData。

两个价格刻度(左侧 + 右侧)
const right = chart.addSeries(LineSeries, { priceScaleId: 'right' });
const left  = chart.addSeries(LineSeries, { priceScaleId: 'left' });
chart.applyOptions({
    leftPriceScale: { visible: true },
    rightPriceScale: { visible: true },
});
在单独的窗格中显示成交量
const price  = chart.addSeries(CandlestickSeries, {}, 0);
const volume = chart.addSeries(
    HistogramSeries,
    { priceFormat: { type: 'volume' } },
    1,
);
chart.panes()[1].setHeight(120);
按柱/按点设置颜色

将颜色设置在数据点上,而不是通过单独的 API 为单个柱设置颜色:

// Candlesticks: per-point color / borderColor / wickColor override series defaults
candles.setData([
    { time: 1700000000, open: 1, high: 2, low: 0.5, close: 1.5,
      color: '#26a69a', borderColor: '#26a69a', wickColor: '#26a69a' },
    { time: 1700003600, open: 1.5, high: 1.8, low: 1.1, close: 1.2,
      color: '#ef5350', borderColor: '#ef5350', wickColor: '#ef5350' },
]);

// Histogram (e.g. up/down volume): per-point color
volume.setData([
    { time: 1700000000, value: 100, color: 'rgba(38,166,154,0.5)' },
    { time: 1700003600, value: 80, color: 'rgba(239,83,80,0.5)' },
]);

Line/Area 的点也支持按点设置 color。省略点颜色时,会回退到序列选项。不要创建额外序列,也不要使用标记为柱重新着色。

序列标记
import { createSeriesMarkers } from 'lightweight-charts';

const markers = createSeriesMarkers(candles, [
    { time: 1700000000, position: 'aboveBar', color: '#e53935', shape: 'arrowDown', text: 'Sell' },
]);

markers.setMarkers([
    { time: 1700003600, position: 'belowBar', color: '#43a047', shape: 'arrowUp', text: 'Buy' },
]);

每个标记都必须指定 color(position、shape、time 也同样必需);text、size、id、price 是可选的。不要将标记对象放入 setData() 数据点中。v5 中不要调用 series.setMarkers(...)。

时区显示
const zone = 'America/New_York';
const formatTime = (timestamp: number) =>
    new Intl.DateTimeFormat('en-US', {
        timeZone: zone,
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false,
    }).format(new Date(timestamp * 1000));

chart.applyOptions({
    timeScale: {
        timeVisible: true,
        secondsVisible: true,
        tickMarkFormatter: (time) =>
            typeof time === 'number' ? formatTime(time) : String(time),
    },
    localization: {
        timeFormatter: (time) =>
            typeof time === 'number' ? formatTime(time) : String(time),
    },
});

不要臆造 timeScale: { timezone: 'America/New_York' }。如果用户需要按本地时区生成交易时段边界,请说明单独调整源时间戳与显示格式之间的取舍。

文本水印
import { createTextWatermark } from 'lightweight-charts';

createTextWatermark(chart.panes()[0], {
    horzAlign: 'center',
    vertAlign: 'center',
    lines: [{ text: 'DEMO', color: 'rgba(0, 0, 0, 0.25)', fontSize: 48 }],
});

旧的 watermark 图表选项不是 v5 API。

空白间隙
line.setData([
    { time: '2024-01-01', value: 10 },
    { time: '2024-01-02' }, // whitespace point: time only
    { time: '2024-01-03', value: 12 },
]);

不要使用 value: null、undefined 或 NaN 创建间隙。

同步独立图表或窗格
let syncing = false;

priceChart.timeScale().subscribeVisibleLogicalRangeChange((range) => {
    if (!range || syncing) return;
    syncing = true;
    indicatorChart.timeScale().setVisibleLogicalRange(range);
    syncing = false;
});

indicatorChart.timeScale().subscribeVisibleLogicalRangeChange((range) => {
    if (!range || syncing) return;
    syncing = true;
    priceChart.timeScale().setVisibleLogicalRange(range);
    syncing = false;
});

对于同一图表中的 v5 窗格,优先使用 chart.addSeries(SeriesType, options, paneIndex) 和 chart.panes()[i].setHeight(px)。如果原始窗格可能移动,请保留 IPaneApi 引用,并调用 pane.paneIndex(),不要永远假定索引为 0。

坐标转换
chart.subscribeClick((param) => {
    if (!param.point || param.time === undefined) return;

    const price = series.coordinateToPrice(param.point.y);
    const logical = chart.timeScale().coordinateToLogical(param.point.x);
    if (price === null || logical === null) return;

    // Store { time: param.time, price } or convert logical back later:
    const x = chart.timeScale().logicalToCoordinate(logical);
    const y = series.priceToCoordinate(price);
});

绘图工具和注释请使用公开的转换 API。仅凭 DOM 画布坐标无法确定图表价格/时间。

Next.js 仅客户端组件
'use client';

import { createChart, LineSeries } from 'lightweight-charts';
import type { ISeriesApi, LineData, Time } from 'lightweight-charts';
import { useEffect, useRef } from 'react';

export function LwcChart({ data }: { data: LineData<Time>[] }) {
    const ref = useRef<HTMLDivElement | null>(null);
    const seriesRef = useRef<ISeriesApi<'Line'> | null>(null);

    useEffect(() => {
        if (!ref.current) return;
        const chart = createChart(ref.current, { autoSize: true });
        const series = chart.addSeries(LineSeries);
        seriesRef.current = series;
        return () => {
            seriesRef.current = null;
            chart.remove();
        };
    }, []);

    useEffect(() => {
        seriesRef.current?.setData(data);
    }, [data]);

    return <div ref={ref} style={{ width: '100%', height: 320 }} />;
}

在服务端渲染的 Next.js 页面中,使用以下方式导入组件:

import dynamic from 'next/dynamic';

const LwcChart = dynamic(() => import('./LwcChart').then((m) => m.LwcChart), {
    ssr: false,
});
加载纯 HTML

没有打包器时,请使用独立的 IIFE 构建:

<div id="chart" style="width: 600px; height: 300px"></div>
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
<script>
  const chart = LightweightCharts.createChart(document.getElementById('chart'));
  const series = chart.addSeries(LightweightCharts.LineSeries);
</script>

根据环境选择加载方式:

  • 打包器(Vite/webpack/Next): 使用裸 import { createChart } from 'lightweight-charts'。软件包的 exports 会解析到正确的 ESM/CJS 构建。
  • 浏览器 <script>(无打包器): 上面的独立 IIFE 构建会公开 LightweightCharts 全局对象;这里不会解析裸模块说明符。
  • 浏览器原生 ESM(<script type="module">): 从具体的 .mjs URL(例如独立 .mjs)导入,或添加导入映射;裸 'lightweight-charts' 说明符无法解析。
  • SSR 框架: 仅在客户端加载(参见上面的 Next.js 示例)——切勿在服务端组件的模块顶层导入。
插件入门
npm create lwc-plugin@latest

请以生成的项目和 plugin-examples/ 为基础。首先确定扩展类型:

  • 原语(IPanePrimitive / ISeriesPrimitive,通过 attachPrimitive 附加)——用于绘图、叠加层、标签、带状区域、注释和命中测试。这是默认选择;除非需要真正的序列行为,否则请使用它。
  • 自定义序列(ICustomSeriesPaneView)——仅当扩展需要自己的数据、自动缩放或价格/时间刻度集成时使用。它更复杂;不要将其用于装饰。

对于可拖动的绘图,请使用 series.attachPrimitive(...) 或 pane.attachPrimitive(...) 附加原语,然后通过 subscribeClick / subscribeCrosshairMove 和坐标转换 API 管理交互。从 fancy-canvas 导入仅用于渲染器的 CanvasRenderingTarget2D。渲染器接口是 IPrimitivePaneRenderer(不是 IPanePrimitivePaneRenderer)。

非金融数据
import { createOptionsChart } from 'lightweight-charts';

const chart = createOptionsChart(container);

首先确定水平轴是否确实为非时间轴:

  • 真正的自定义水平刻度(期权链、按非时间键排列的传感器扫描):使用 createOptionsChart 创建数值 x 轴,或使用带有自定义 IHorzScaleBehavior 的 createChartEx(请在本地类型定义中核实两者)。
  • 仅 y 轴单位非金融(温度、负载),使用常规时间/类别轴:保留普通的 createChart 序列模型,并设置 priceFormat / localization.priceFormatter,使标签符合数据领域。
  • 实际有序的分类 x 轴:将类别映射为升序的 BusinessDay 字符串,比使用自定义刻度更简单——但请说明此时的轴是日期形式,而非真正的分类轴。
自定义价格格式化器
chart.applyOptions({
    localization: {
        priceFormatter: (p: number) => `$${p.toFixed(2)}`,
    },
});

如需按序列设置格式,请在序列选项中设置 priceFormat: { type: 'custom', formatter, minMove }——图表级别的 priceFormatter 是回退选项。

要恢复内置格式,请传入 null:chart.applyOptions({ localization: { priceFormatter: null } })。传入 undefined 会保留当前格式化器。

最小窗格原语
import type {
    IPanePrimitive,
    IPanePrimitivePaneView,
    IPrimitivePaneRenderer,
    Time,
} from 'lightweight-charts';
import type { CanvasRenderingTarget2D } from 'fancy-canvas';

class Overlay implements IPanePrimitive<Time> {
    paneViews(): IPanePrimitivePaneView[] { return [new View()]; }
}
class View implements IPanePrimitivePaneView {
    renderer(): IPrimitivePaneRenderer { return new Renderer(); }
}
class Renderer implements IPrimitivePaneRenderer {
    draw(target: CanvasRenderingTarget2D): void {
        target.useMediaCoordinateSpace(({ context, mediaSize }) => {
            context.fillStyle = 'rgba(0,0,0,0.05)';
            context.fillRect(0, 0, mediaSize.width, mediaSize.height);
        });
    }
}

chart.panes()[0].attachPrimitive(new Overlay());

可运行示例位于 plugin-examples/src/plugins/ 下——从那里复制结构,再从头编写。

示例

“每次收到新数据时,我的图表都会跳到最右侧。”

  1. **涉及的层:**数据模型(4)和时间刻度(3)。
  2. **疑点:**代码在每次更新时都调用 series.setData(...),而不是 series.update(...)。setData 会重置可见范围。
  3. **验证:**询问调用路径使用的是 setData 还是 update。如果是 setData,询问是否在调用前捕获并在调用后恢复可见逻辑范围——这是 v4 时代的变通方案。
  4. **修复:**增量更新时改用 series.update(bar);仅在替换全部历史数据时使用 setData;只有用户明确希望始终跟随最新数据时,才使用 chart.timeScale().scrollToRealTime()。
  5. 参考:website/tutorials/demos/realtime-updates.js、website/docs/time-scale.md。

代码生成规则

  • **默认使用 v5。**除非用户安装的包或提示词指定了较旧版本,否则假设使用 v5 API。如果用户使用的是 v4,请说明迁移到 v5 的要点,而不要混用语法。
  • **使用真实的选项名称。**在使用方应用中,搜索 node_modules/lightweight-charts/dist/typings.d.ts;在上游仓库中,搜索 dist/typings.d.ts 或 src/api/options/。图表、序列和刻度中存在许多名称相似的选项——请确认选项属于哪个层级。
  • **提供最精简的代码片段。**每个代码块只展示一个功能。在一个代码片段中组合标记、水印和自定义窗格原语,会让人难以分辨各个 API 的用途。
  • **匹配用户的框架。**使用 React 的用户需要 useEffect 生命周期;使用原生 JavaScript 的用户则不需要。
  • **不要凭空编造。**如果某个 API 名称未出现在已安装的类型定义或上游源码中,它就不存在。

回答规范

回答 lightweight-charts 相关问题时:

  1. 指明相关的 v5 API。
  2. 提供一个最精简的代码片段,而非大型演示。
  3. 指出该任务中最容易踩的坑。
  4. 说明检查了哪些本地源码(版本/类型定义),或声明无法验证。

评论

支持 Markdown