Skip to content

[WIP]add candlestick chart in vchart-extension #4090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/assets/examples/zh/candlestick/candlestick-basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
category: examples
group: candlestick chart
title: K线图
keywords: candlestick, k线, 股票, 金融
link: '../guide/candlestick/introduction'
option: Candlestick#basic
---

# K 线图

K 线图基本用法

## 关键配置

- `type: 'candlestick'`
- `xField`, `openField`, `closeField`, `highField`, `lowField`
- `data`

## 代码演示

```javascript livedemo template=vchart
const data = [
{ time: '2024-07-01', open: 100, close: 130, high: 135, low: 90 },
{ time: '2024-07-02', open: 130, close: 80, high: 140, low: 75 },
{ time: '2024-07-03', open: 80, close: 150, high: 155, low: 70 },
{ time: '2024-07-04', open: 150, close: 140, high: 160, low: 105 },
{ time: '2024-07-05', open: 140, close: 170, high: 180, low: 115 }
];

const spec = {
type: 'candlestick',
xField: 'time',
openField: 'open',
closeField: 'close',
highField: 'high',
lowField: 'low',
data: [{ values: data }]
};

const chart = new VChart(spec, {
dom: document.getElementById(CONTAINER_ID)
});

window['chart'] = chart;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ICandlestickChartSpec } from '../../../../src/charts/candlestick/interface';
import { registerCandlestickChart } from '../../../../src/charts/candlestick/candlestick';
import VChart from '@visactor/vchart';

const data = [
{ time: '2024-07-01', open: 100, close: 130, high: 135, low: 90 },
{ time: '2024-07-02', open: 130, close: 80, high: 140, low: 75 },
{ time: '2024-07-03', open: 80, close: 150, high: 155, low: 70 },
{ time: '2024-07-04', open: 150, close: 140, high: 160, low: 105 },
{ time: '2024-07-05', open: 140, close: 170, high: 180, low: 115 },
{ time: '2024-07-06', open: 170, close: 170, high: 175, low: 95 },
{ time: '2024-07-07', open: 170, close: 100, high: 175, low: 95 },
{ time: '2024-07-08', open: 100, close: 160, high: 210, low: 90 }
];

const spec: ICandlestickChartSpec = {
type: 'candlestick',
xField: 'time',
openField: 'open',
closeField: 'close',
highField: 'high',
lowField: 'low',
data: [
{
values: data
}
]
};

const run = () => {
registerCandlestickChart();
const cs = new VChart(spec, {
dom: document.getElementById('chart') as HTMLElement,
onError: err => {
console.error(err);
}
});
console.time('renderTime');
cs.renderSync();
console.timeEnd('renderTime');
window['vchart'] = cs;
console.log(cs);
};
run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { CartesianChartSpecTransformer, setDefaultCrosshairForCartesianChart } from '@visactor/vchart';
import type { ICandlestickChartSpec } from './interface';

export class CandlestickChartSpecTransformer<
T extends ICandlestickChartSpec = ICandlestickChartSpec
> extends CartesianChartSpecTransformer<T> {
protected _getDefaultSeriesSpec(spec: T): any {
const dataFields = [spec.openField, spec.highField, spec.lowField, spec.closeField];
const seriesSpec = super._getDefaultSeriesSpec(spec, [
'candlestick',
'openField',
'highField',
'lowField',
'closeField',
'candlestickColor'
]);
seriesSpec.yField = dataFields;
return seriesSpec;
}

transformSpec(spec: T): void {
super.transformSpec(spec);
if (!spec.axes) {
spec.axes = [
{
orient: 'bottom'
},
{
orient: 'left'
}
];
}
setDefaultCrosshairForCartesianChart(spec);
}
}
34 changes: 34 additions & 0 deletions packages/vchart-extension/src/charts/candlestick/candlestick.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { CandlestickChartSpecTransformer } from './candlestick-transformer';
import { ICandlestickChartSpec } from './interface';
import { registerCandlestickSeries } from './series/candlestick';
import {
BaseChart,
Factory,
registerMarkTooltipProcessor,
registerDimensionTooltipProcessor,
registerDimensionEvents,
registerDimensionHover,
getCartesianDimensionInfo,
getDimensionInfoByValue,
getCartesianCrosshairRect
} from '@visactor/vchart';
import { CANDLESTICK_CHART_TYPE, CANDLESTICK_SERIES_TYPE } from './series/constant';
export class CandlestickChart<T extends ICandlestickChartSpec = ICandlestickChartSpec> extends BaseChart<T> {
static readonly type: string = CANDLESTICK_CHART_TYPE;
static readonly seriesType: string = CANDLESTICK_SERIES_TYPE;
static readonly transformerConstructor = CandlestickChartSpecTransformer; // CandlestickChartSpecTransformer;
protected _setModelOption() {
this._modelOption.getDimensionInfo = getCartesianDimensionInfo;
this._modelOption.getDimensionInfoByValue = getDimensionInfoByValue;
this._modelOption.getRectByDimensionData = getCartesianCrosshairRect;
}
}

export const registerCandlestickChart = () => {
registerDimensionTooltipProcessor();
registerMarkTooltipProcessor();
registerDimensionEvents();
registerDimensionHover();
registerCandlestickSeries();
Factory.registerChart(CandlestickChart.type, CandlestickChart);
};
3 changes: 3 additions & 0 deletions packages/vchart-extension/src/charts/candlestick/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './candlestick';
export * from './interface';
export * from './candlestick-transformer';
8 changes: 8 additions & 0 deletions packages/vchart-extension/src/charts/candlestick/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { IChartExtendsSeriesSpec, ICartesianChartSpec } from '@visactor/vchart';
import type { ICandlestickSeriesSpec } from './series/interface';

export interface ICandlestickChartSpec extends ICartesianChartSpec, IChartExtendsSeriesSpec<ICandlestickSeriesSpec> {
type: 'candlestick';
/** 系列配置 */
series?: ICandlestickSeriesSpec[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { registerLine, registerRect } from '@visactor/vrender-kits';
import { GlyphMark, registerGlyphMark, IMarkRaw, IMarkStyle } from '@visactor/vchart';
import {
createLine,
createRect,
type IGlyph,
type ILineGraphicAttribute,
IRectGraphicAttribute
} from '@visactor/vrender-core';
import { Factory, Datum } from '@visactor/vchart';
import type { ICandlestickMarkSpec } from './interface';

export type ICandlestickMark = IMarkRaw<ICandlestickMarkSpec>;
export const CANDLESTICK_MARK_TYPE = 'candlestick';

export class CandlestickMark extends GlyphMark<ICandlestickMarkSpec> implements ICandlestickMark {
static readonly type = CANDLESTICK_MARK_TYPE;
readonly type = CandlestickMark.type;

setGlyphConfig(cfg: any): void {
super.setGlyphConfig(cfg);
this._subMarks = {
line: { type: 'line', defaultAttributes: { x: 0, y: 0 } },
box: { type: 'rect' }
};
this._positionChannels = ['x', 'boxWidth', 'open', 'close', 'high', 'low'];
this._channelEncoder = null;
this._positionEncoder = (glyphAttrs: any, datum: Datum, g: IGlyph) => {
const {
x = g.attribute.x,
boxWidth = (g.attribute as any).boxWidth,
open = (g.attribute as any).open,
close = (g.attribute as any).close,
low = (g.attribute as any).low,
high = (g.attribute as any).high
} = glyphAttrs;
const attributes: any = {};
attributes.line = {
points: [
{
x: x,
y: low
},
{
x: x,
y: high
}
]
};
attributes.box = {
x: x - boxWidth / 2,
x1: x + boxWidth / 2,
y: Math.min(open, close),
y1: Math.max(open, close),
// 开盘收盘相同时绘制水平线
drawStrokeWhenZeroWH: true
};
return attributes;
};
}

protected _getDefaultStyle() {
const defaultStyle: IMarkStyle<ICandlestickMarkSpec> = {
...super._getDefaultStyle(),
boxWidth: 40
};
return defaultStyle;
}
}

export const registerCandlestickMark = () => {
registerGlyphMark();
registerLine();
registerRect();
Factory.registerGraphicComponent('line', (attrs: ILineGraphicAttribute) => createLine(attrs));
Factory.registerGraphicComponent('rect', (attrs: IRectGraphicAttribute) => createRect(attrs));
Factory.registerMark(CandlestickMark.type, CandlestickMark);
};
32 changes: 32 additions & 0 deletions packages/vchart-extension/src/charts/candlestick/mark/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Datum, ICommonSpec } from '@visactor/vchart';

export interface ICandlestickMarkSpec extends ICommonSpec {
/**
* box描边宽度
*/
lineWidth?: number;
/**
* box宽度
*/
boxWidth?: number;
/**
* 盒子填充颜色,为空则不填充
*/
boxFill?: string | ((datum: Datum) => string);
/**
* 最低价
*/
low?: (datum: Datum) => number;
/**
* 收盘价
*/
close?: (datum: Datum) => number;
/**
* 开盘价
*/
open?: (datum: Datum) => number;
/**
* 最高价
*/
high?: (datum: Datum) => number;
}
Loading