Skip to content

feat(plugin-chart-echarts): add native candlestick chart plugin#39990

Open
Chaithanya5gif wants to merge 5 commits into
apache:masterfrom
Chaithanya5gif:feat/echarts-candlestick
Open

feat(plugin-chart-echarts): add native candlestick chart plugin#39990
Chaithanya5gif wants to merge 5 commits into
apache:masterfrom
Chaithanya5gif:feat/echarts-candlestick

Conversation

@Chaithanya5gif
Copy link
Copy Markdown

Summary

This PR implements a native ECharts-based Candlestick (K-Line) chart plugin for Apache Superset. Currently, Superset lacks a native financial charting solution within the ECharts plugin library.

Technical Details

  • Added EchartsCandlestickChartPlugin to @superset-ui/plugin-chart-echarts.
  • Implemented OHLC (Open, High, Low, Close) metric selection in the control panel.
  • Built a robust transformProps handler with international financial color standards (Green=Up, Red=Down) and rich interactive tooltips.
  • Registered the plugin globally in MainPreset.ts.

Why this is needed

Financial data visualization is a core requirement for many enterprise BI users. This plugin provides a high-performance, interactive way to visualize OHLC data using the proven ECharts engine already present in Superset.

@dosubot dosubot Bot added the viz:charts:echarts Related to Echarts label May 9, 2026
@netlify
Copy link
Copy Markdown

netlify Bot commented May 9, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 63dc887
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/69ff33172824790008c5fa48
😎 Deploy Preview https://deploy-preview-39990--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Comment thread superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/transformProps.ts Outdated
@bito-code-review
Copy link
Copy Markdown
Contributor

The flagged issue is correct. The transformProps function returns props without the required interaction fields (groupby, labelMap, onContextMenu), and selectedValues is hardcoded as an empty object instead of deriving from filterState. This mismatch will cause runtime errors when allEventHandlers tries to access groupby.length and other fields, preventing the Interactive/Drill behaviors from working.

To resolve, align with other interactive ECharts plugins by adding the missing fields to the destructuring and return statement.

superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/transformProps.ts

export default function transformProps(
  chartProps: EchartsCandlestickChartProps,
): CandlestickChartTransformedProps {
  const { width, height, formData, queriesData, hooks, filterState } = chartProps;
  const { data = [] } = queriesData[0];
  const { setDataMask = () => {}, emitCrossFilters, onContextMenu } = hooks;

superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/transformProps.ts

const xAxisLabel = getXAxisLabel(chartProps.rawFormData) || '__timestamp';

  const timeFormatter = getTimeFormatter('%Y-%m-%d %H:%M:%S');
  const numberFormatter = getNumberFormatter();

  const transformedData = data
    .map(datum => {
      const time = datum[xAxisLabel];
      const o = datum[openLabel];
      const c = datum[closeLabel];
      const l = datum[lowLabel];
      const h = datum[highLabel];
      
      if (o == null || c == null || l == null || h == null) {
        return null;
      }
      
      return [time, o, c, l, h];
    })
    .filter(Boolean) as [any, number, number, number, number][];

  const series: CandlestickSeriesOption[] = [
    {
      name: 'OHLC',
      type: 'candlestick',
      data: transformedData.map(row => row.slice(1)),
      itemStyle: {
        // International standard: Green for Up, Red for Down
        color: '#14b143', 
        color0: '#ef232a',
        borderColor: '#14b143',
        borderColor0: '#ef232a',
      },
    },
  ];

  const echartOptions: EChartsCoreOption = {
    grid: {
      ...defaultGrid,
      bottom: zoomable ? '15%' : '10%',
    },
    xAxis: {
      type: 'category',
      data: transformedData.map(row => timeFormatter(row[0])),
      scale: true,
      boundaryGap: false,
      axisLine: { onZero: false },
      splitLine: { show: false },
      min: 'dataMin',
      max: 'dataMax',
    },
    yAxis: {
      ...defaultYAxis,
      scale: true,
      splitArea: {
        show: true,
      },
      axisLabel: { formatter: numberFormatter },
    },
    tooltip: {
      ...getDefaultTooltip(refs),
      trigger: 'axis',
      axisPointer: {
        type: 'cross',
      },
      formatter: (params: CallbackDataParams[]) => {
        const p = params[0];
        const [o, c, l, h] = p.value as number[];
        return `
          <div style="padding: 3px;">
            <p style="margin-bottom: 5px;"><strong>${sanitizeHtml(p.name)}</strong></p>
            <div style="display: flex; justify-content: space-between;">
              <span style="margin-right: 10px;">Open:</span>
              <strong>${numberFormatter(o)}</strong>
            </div>
            <div style="display: flex; justify-content: space-between;">
              <span style="margin-right: 10px;">Close:</span>
              <strong>${numberFormatter(c)}</strong>
            </div>
            <div style="display: flex; justify-content: space-between;">
              <span style="margin-right: 10px;">Low:</span>
              <strong>${numberFormatter(l)}</strong>
            </div>
            <div style="display: flex; justify-content: space-between;">
              <span style="margin-right: 10px;">High:</span>
              <strong>${numberFormatter(h)}</strong>
            </div>
          </div>
        `;
      },
    },
    series,
    dataZoom: zoomable
      ? [
          {
            type: 'inside',
            start: 0,
            end: 100,
          },
          {
            show: true,
            type: 'slider',
            top: '90%',
            start: 0,
            end: 100,
          },
        ]
      : [],
  };

  const groupby = [getXAxisColumn(formData)].filter(Boolean);
  const labelMap = { [xAxisLabel]: xAxisLabel };

  return {
    formData,
    width,
    height,
    echartOptions,
    setDataMask,
    refs,
    coltypeMapping,
    selectedValues: filterState?.selectedValues || {},
    groupby,
    labelMap,
    emitCrossFilters,
    onContextMenu,
  };
}

Copy link
Copy Markdown
Contributor

@bito-code-review bito-code-review Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #7d3f19

Actionable Suggestions - 1
  • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/types.ts - 1
Review Details
  • Files reviewed - 9 · Commit Range: 63dc887..90c89ac
    • superset-frontend/packages/superset-ui-core/src/chart/types/VizType.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/EchartsCandlestick.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/buildQuery.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/controlPanel.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/index.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/types.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/index.ts
    • superset-frontend/src/visualizations/presets/MainPreset.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@netlify
Copy link
Copy Markdown

netlify Bot commented May 10, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit b659a84
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a040ac512d74000081de6ad
😎 Deploy Preview https://deploy-preview-39990--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Copy Markdown
Contributor

@bito-code-review bito-code-review Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #e426aa

Actionable Suggestions - 1
  • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/transformProps.ts - 1
    • CWE-1321: Object injection vulnerability via dynamic property access · Line 61-67
Review Details
  • Files reviewed - 3 · Commit Range: 63dc887..bb05eee
    • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/controlPanel.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/types.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +61 to +67
let xAxisLabel = getXAxisLabel(chartProps.rawFormData) || '__timestamp';
if (
isPhysicalColumn(chartProps.rawFormData?.x_axis) &&
isDefined(verboseMap[xAxisLabel])
) {
xAxisLabel = verboseMap[xAxisLabel];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CWE-1321: Object injection vulnerability via dynamic property access

Lines 64 and 66 use dynamic property access on verboseMap object without validation, creating an object injection vulnerability (CWE-1321). Validate or sanitize xAxisLabel before using it as an object key to prevent potential security issues.

Code suggestion
Check the AI-generated fix before applying
Suggested change
let xAxisLabel = getXAxisLabel(chartProps.rawFormData) || '__timestamp';
if (
isPhysicalColumn(chartProps.rawFormData?.x_axis) &&
isDefined(verboseMap[xAxisLabel])
) {
xAxisLabel = verboseMap[xAxisLabel];
}
let xAxisLabel = getXAxisLabel(chartProps.rawFormData) || '__timestamp';
if (
isPhysicalColumn(chartProps.rawFormData?.x_axis) &&
isDefined(verboseMap[xAxisLabel]) &&
Object.prototype.hasOwnProperty.call(verboseMap, xAxisLabel)
) {
const safeLabel = verboseMap[xAxisLabel];
if (typeof safeLabel === 'string') {
xAxisLabel = safeLabel;
}
}

Code Review Run #e426aa


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a native ECharts Candlestick (OHLC/K-Line) chart plugin and wires it into Superset so it can be selected as a visualization type.

Changes:

  • Registers a new Candlestick viz type and adds the plugin to MainPreset.
  • Introduces the Candlestick plugin implementation (buildQuery, control panel, transformProps, chart component).
  • Exports the plugin and its transformProps from @superset-ui/plugin-chart-echarts.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
superset-frontend/src/visualizations/presets/MainPreset.ts Registers the new candlestick plugin in the main preset.
superset-frontend/plugins/plugin-chart-echarts/src/index.ts Exposes the new plugin and transformProps exports.
superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/types.ts Adds form-data/types and defaults for the candlestick plugin.
superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/transformProps.ts Implements ECharts option-building, data mapping, tooltip, and zoom.
superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/index.ts Declares plugin metadata and lazy-load entrypoint.
superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/controlPanel.ts Adds OHLC metric controls and chart options.
superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/buildQuery.ts Builds query context for OHLC metrics + time column.
superset-frontend/plugins/plugin-chart-echarts/src/Candlestick/EchartsCandlestick.tsx Renders the chart using the shared Echart component.
superset-frontend/packages/superset-ui-core/src/chart/types/VizType.ts Adds VizType.Candlestick.

Comment on lines +60 to +83

let xAxisLabel = getXAxisLabel(chartProps.rawFormData) || '__timestamp';
if (
isPhysicalColumn(chartProps.rawFormData?.x_axis) &&
isDefined(verboseMap[xAxisLabel])
) {
xAxisLabel = verboseMap[xAxisLabel];
}

const timeFormatter = getTimeFormatter('%Y-%m-%d %H:%M:%S');
const numberFormatter = getNumberFormatter();

const transformedData = data
.map(datum => {
const time = datum[xAxisLabel];
const o = datum[openLabel];
const c = datum[closeLabel];
const l = datum[lowLabel];
const h = datum[highLabel];

if (o == null || c == null || l == null || h == null) {
return null;
}

Comment on lines +93 to +100
itemStyle: {
// International standard: Green for Up, Red for Down
color: '#14b143',
color0: '#ef232a',
borderColor: '#14b143',
borderColor0: '#ef232a',
},
},
Comment on lines +19 to +31
import { buildQueryContext, getXAxisColumn } from '@superset-ui/core';
import { CandlestickQueryFormData } from './types';

export default function buildQuery(formData: CandlestickQueryFormData) {
return buildQueryContext(formData, baseQueryObject => {
// Collect all OHLC metrics and remove duplicates
const metrics = Array.from(
new Set(
[formData.open, formData.close, formData.high, formData.low].filter(
Boolean,
),
),
);
Comment on lines +72 to +86
const transformedData = data
.map(datum => {
const time = datum[xAxisLabel];
const o = datum[openLabel];
const c = datum[closeLabel];
const l = datum[lowLabel];
const h = datum[highLabel];

if (o == null || c == null || l == null || h == null) {
return null;
}

return [time, o, c, l, h];
})
.filter(Boolean) as [any, number, number, number, number][];
@codecov
Copy link
Copy Markdown

codecov Bot commented May 13, 2026

Codecov Report

❌ Patch coverage is 5.35714% with 53 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.11%. Comparing base (e2a8a88) to head (b659a84).
⚠️ Report is 20 commits behind head on master.

Files with missing lines Patch % Lines
...in-chart-echarts/src/Candlestick/transformProps.ts 0.00% 43 Missing ⚠️
...art-echarts/src/Candlestick/EchartsCandlestick.tsx 0.00% 4 Missing ⚠️
...plugin-chart-echarts/src/Candlestick/buildQuery.ts 0.00% 4 Missing ⚠️
...gins/plugin-chart-echarts/src/Candlestick/index.ts 66.66% 1 Missing ⚠️
...gins/plugin-chart-echarts/src/Candlestick/types.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #39990      +/-   ##
==========================================
- Coverage   64.14%   64.11%   -0.03%     
==========================================
  Files        2590     2596       +6     
  Lines      138033   138089      +56     
  Branches    32019    32030      +11     
==========================================
- Hits        88537    88533       -4     
- Misses      47977    48037      +60     
  Partials     1519     1519              
Flag Coverage Δ
javascript 66.94% <5.35%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants