diff --git a/packages/f2/src/components/candlestick/candlestickView.tsx b/packages/f2/src/components/candlestick/candlestickView.tsx
new file mode 100644
index 000000000..ce0b8795e
--- /dev/null
+++ b/packages/f2/src/components/candlestick/candlestickView.tsx
@@ -0,0 +1,87 @@
+import { jsx } from '@antv/f-engine';
+import { deepMix } from '@antv/util';
+
+export default (props) => {
+ const { records, animation, y0, onClick } = props;
+ return (
+
+ {records.map((record) => {
+ const { key, children } = record;
+ return (
+
+ {children.map((item) => {
+ const { key, xMin, xMax, yMin, yMax, x, y, color, shape } = item;
+ if (isNaN(xMin) || isNaN(xMax) || isNaN(yMin) || isNaN(yMax)) {
+ return null;
+ }
+ return (
+
+
+
+
+ );
+ })}
+
+ );
+ })}
+
+ );
+};
diff --git a/packages/f2/src/components/candlestick/index.tsx b/packages/f2/src/components/candlestick/index.tsx
new file mode 100644
index 000000000..17264b7f0
--- /dev/null
+++ b/packages/f2/src/components/candlestick/index.tsx
@@ -0,0 +1,5 @@
+import withCandlestick, { CandlestickProps } from './withCandlestick';
+import CandlestickView from './candlestickView';
+
+export { CandlestickProps, CandlestickView };
+export default withCandlestick(CandlestickView);
diff --git a/packages/f2/src/components/candlestick/withCandlestick.tsx b/packages/f2/src/components/candlestick/withCandlestick.tsx
new file mode 100644
index 000000000..d613d10d7
--- /dev/null
+++ b/packages/f2/src/components/candlestick/withCandlestick.tsx
@@ -0,0 +1,86 @@
+import { jsx, ComponentType } from '@antv/f-engine';
+import { isNil, mix } from '@antv/util';
+import Geometry, { GeometryProps } from '../geometry';
+import { DataRecord } from '../../chart/Data';
+
+// 默认配色
+const COLORS = [
+ '#E62C3B', // 上涨
+ '#0E9976', // 下跌
+ '#999999', // 平盘
+];
+
+export interface CandlestickProps
+ extends GeometryProps {
+ /**
+ * 柱子的显示比例,默认 0.5
+ */
+ sizeRatio?: number;
+}
+
+export default (View: ComponentType) => {
+ return class Candlestick<
+ TRecord extends DataRecord = DataRecord,
+ IProps extends CandlestickProps = CandlestickProps
+ > extends Geometry {
+ getDefaultCfg() {
+ return {
+ geomType: 'candlestick',
+ };
+ }
+
+ getSize() {
+ const { attrs, props } = this;
+ const { sizeRatio = 0.5 } = props;
+ const { x } = attrs;
+ const { scale } = x;
+ const { values } = scale;
+
+ return (1 / values.length) * sizeRatio;
+ }
+
+ mapping() {
+ const records = super.mapping();
+ const { props } = this;
+ const { coord } = props;
+ const y0 = this.getY0Value();
+ const defaultSize = this.getSize();
+ const colorAttr = this.getAttr('color');
+
+ const colors = colorAttr ? colorAttr.range : COLORS;
+
+ for (let i = 0, len = records.length; i < len; i++) {
+ const record = records[i];
+ const { children } = record;
+ for (let j = 0, len = children.length; j < len; j++) {
+ const child = children[j];
+ const { normalized, size: mappedSize } = child;
+
+ // 没有指定size,则根据数据来计算默认size
+ if (isNil(mappedSize)) {
+ const { x, y, size = defaultSize } = normalized;
+ mix(child, coord.convertRect({ x, y, y0, size: size }));
+ } else {
+ const { x, y } = child;
+ const rect = { x, y, y0, size: mappedSize };
+ mix(child, coord.transformToRect(rect));
+ }
+
+ // 处理颜色
+ const { y } = normalized;
+ const [open, close] = y;
+ child.color = close > open ? colors[0] : close < open ? colors[1] : colors[2];
+
+ mix(child.shape, this.getSelectionStyle(child));
+ }
+ }
+ return records;
+ }
+
+ render() {
+ const { props } = this;
+ const records = this.mapping();
+ return ;
+ }
+ };
+};
diff --git a/packages/f2/src/components/index.ts b/packages/f2/src/components/index.ts
index 748c126ac..197936bcc 100644
--- a/packages/f2/src/components/index.ts
+++ b/packages/f2/src/components/index.ts
@@ -25,3 +25,4 @@ export { default as PieLabel, PieLabelProps, withPieLabel, PieLabelView } from '
export { default as Gauge, GaugeProps, withGauge, GaugeView } from './gauge';
export { default as Zoom, ZoomProps } from './zoom';
export { default as ScrollBar, ScrollBarProps, withScrollBar, ScrollBarView } from './scrollBar';
+export { default as Candlestick } from './candlestick';
diff --git a/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-basic-1-snap.png b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-basic-1-snap.png
new file mode 100644
index 000000000..f474bd8fa
Binary files /dev/null and b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-basic-1-snap.png differ
diff --git a/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-color-1-snap.png b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-color-1-snap.png
new file mode 100644
index 000000000..ad0f9b8c7
Binary files /dev/null and b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-color-1-snap.png differ
diff --git a/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-1-snap.png b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-1-snap.png
new file mode 100644
index 000000000..7531e2c98
Binary files /dev/null and b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-1-snap.png differ
diff --git a/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-ratio-1-snap.png b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-ratio-1-snap.png
new file mode 100644
index 000000000..ab21c88a4
Binary files /dev/null and b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-ratio-1-snap.png differ
diff --git a/packages/f2/test/components/candlestick/basic.test.tsx b/packages/f2/test/components/candlestick/basic.test.tsx
new file mode 100644
index 000000000..00f8fb58a
--- /dev/null
+++ b/packages/f2/test/components/candlestick/basic.test.tsx
@@ -0,0 +1,101 @@
+import { jsx } from '../../../src';
+import { Canvas, Chart, Candlestick, Axis } from '../../../src';
+import { createContext, delay } from '../../util';
+const context = createContext();
+
+const data = [
+ {
+ time: '2017-10-24',
+ value: [20, 34, 10, 38], // [open, close, lowest, highest]
+ },
+ {
+ time: '2017-10-25',
+ value: [40, 35, 30, 50],
+ },
+ {
+ time: '2017-10-26',
+ value: [31, 38, 33, 44],
+ },
+ {
+ time: '2017-10-27',
+ value: [38, 38, 5, 42],
+ },
+ {
+ time: '2017-10-28',
+ value: [38, 15, 5, 42],
+ },
+];
+
+describe('candlestick', () => {
+ it('basic', async () => {
+ const { props } = (
+
+ );
+
+ const canvas = new Canvas(props);
+ await canvas.render();
+
+ await delay(1000);
+ expect(context).toMatchImageSnapshot();
+ });
+
+ it('color', async () => {
+ const { props } = (
+
+ );
+
+ const canvas = new Canvas(props);
+ await canvas.render();
+
+ await delay(1000);
+ expect(context).toMatchImageSnapshot();
+ });
+
+ it('size', async () => {
+ const { props } = (
+
+ );
+
+ const canvas = new Canvas(props);
+ await canvas.render();
+
+ await delay(1000);
+ expect(context).toMatchImageSnapshot();
+ });
+
+ it('sizeRatio', async () => {
+ const { props } = (
+
+ );
+
+ const canvas = new Canvas(props);
+ await canvas.render();
+
+ await delay(1000);
+ expect(context).toMatchImageSnapshot();
+ });
+});
diff --git a/site/.dumirc.ts b/site/.dumirc.ts
index 426e604e7..10dee0dc1 100644
--- a/site/.dumirc.ts
+++ b/site/.dumirc.ts
@@ -302,6 +302,14 @@ export default defineConfig({
en: 'Relation Charts',
},
},
+ {
+ slug: 'candlestick',
+ icon: 'candlestick',
+ title: {
+ zh: 'K 线图',
+ en: 'Candlestick Charts',
+ },
+ },
{
slug: 'component',
icon: 'component',
diff --git a/site/docs/api/chart/candlestick.zh.md b/site/docs/api/chart/candlestick.zh.md
new file mode 100644
index 000000000..bdb510979
--- /dev/null
+++ b/site/docs/api/chart/candlestick.zh.md
@@ -0,0 +1,68 @@
+---
+title: K 线图 - Candlestick
+order: 5
+---
+
+用于 K 线图, 继承自 [几何标记 Geometry](geometry)
+
+## Usage
+
+```jsx
+import { Axis, Candlestick, Canvas, Chart, jsx } from '@antv/f2';
+
+const data = [
+ {
+ time: '2017-10-24',
+ // 格式为:[open, close, lowest, highest]
+ value: [20, 34, 10, 38],
+ },
+ {
+ time: '2017-10-25',
+ value: [40, 35, 30, 50],
+ },
+ {
+ time: '2017-10-26',
+ value: [31, 38, 33, 44],
+ },
+ {
+ time: '2017-10-27',
+ value: [38, 15, 5, 42],
+ },
+];
+
+const { props } = (
+
+);
+```
+
+## 数据结构说明
+
+y 轴字段格式为:`[open, close, lowest, highest]` 分别代表:`[开盘价, 收盘价, 最低价, 最高价]`
+
+## Props
+
+几何标记统一 Props 详见:[几何标记](geometry#props)
+
+### color
+
+设置「涨」、「跌」、「平盘」颜色,格式为:`[上涨颜色, 下跌颜色, 平盘颜色]`, 默认值为: `['#E62C3B', '#0E9976', '#999999']`
+
+```jsx
+
+```
+
+### sizeRatio
+
+矩形的大小比例,范围 `[0, 1]`, 默认为 `0.5`, 表示矩形的宽度和空白处各占 `50%`
+
+```jsx
+
+```
+
+## 方法
diff --git a/site/examples/candlestick/candlestick/demo/base.jsx b/site/examples/candlestick/candlestick/demo/base.jsx
new file mode 100644
index 000000000..ea63b3262
--- /dev/null
+++ b/site/examples/candlestick/candlestick/demo/base.jsx
@@ -0,0 +1,37 @@
+/** @jsx jsx */
+import { Axis, Candlestick, Canvas, Chart, jsx } from '@antv/f2';
+
+const context = document.getElementById('container').getContext('2d');
+
+const data = [
+ {
+ time: '2017-10-24',
+ // 格式为:[open, close, lowest, highest]
+ value: [20, 34, 10, 38],
+ },
+ {
+ time: '2017-10-25',
+ value: [40, 35, 30, 50],
+ },
+ {
+ time: '2017-10-26',
+ value: [31, 38, 33, 44],
+ },
+ {
+ time: '2017-10-27',
+ value: [38, 15, 5, 42],
+ },
+];
+
+const { props } = (
+
+);
+
+const canvas = new Canvas(props);
+canvas.render();
diff --git a/site/examples/candlestick/candlestick/demo/meta.json b/site/examples/candlestick/candlestick/demo/meta.json
new file mode 100644
index 000000000..c3c4092a8
--- /dev/null
+++ b/site/examples/candlestick/candlestick/demo/meta.json
@@ -0,0 +1,13 @@
+{
+ "title": {
+ "zh": "中文分类",
+ "en": "Category"
+ },
+ "demos": [
+ {
+ "filename": "base.jsx",
+ "title": "基础 K 线图",
+ "screenshot": "https://gw.alipayobjects.com/zos/finxbff/compress-tinypng/9e61f9de-14eb-4ecc-becf-1bb2a9c29ea9.png"
+ }
+ ]
+}
diff --git a/site/examples/candlestick/candlestick/index.en.md b/site/examples/candlestick/candlestick/index.en.md
new file mode 100644
index 000000000..73a61fee6
--- /dev/null
+++ b/site/examples/candlestick/candlestick/index.en.md
@@ -0,0 +1,5 @@
+---
+title: Candlestick Chart
+order: 0
+icon: candlestick
+---
diff --git a/site/examples/candlestick/candlestick/index.zh.md b/site/examples/candlestick/candlestick/index.zh.md
new file mode 100644
index 000000000..b1a82c07b
--- /dev/null
+++ b/site/examples/candlestick/candlestick/index.zh.md
@@ -0,0 +1,5 @@
+---
+title: K 线图
+order: 0
+icon: candlestick
+---