From 3f65c8f8fd0a3fbd338f42c7c5105ec2c3c03ce8 Mon Sep 17 00:00:00 2001 From: 019327 Date: Mon, 21 Jul 2025 16:18:07 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=8D=95=E6=A0=B9=E8=9C=A1=E7=83=9B=E6=9F=B1=E6=89=80?= =?UTF-8?q?=E5=8D=A0=E7=9A=84=E7=A9=BA=E9=97=B4=E7=9A=84=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E5=80=BC=E5=92=8C=E6=9C=80=E5=B0=8F=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Chart.ts | 10 +++++++++- src/Options.ts | 6 ++++++ src/Store.ts | 34 ++++++++++++++++++++++++++++++---- src/index.ts | 4 ++-- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/Chart.ts b/src/Chart.ts index 05b30a60c..1336e2864 100644 --- a/src/Chart.ts +++ b/src/Chart.ts @@ -25,7 +25,7 @@ import type Crosshair from './common/Crosshair' import type { ActionType, ActionCallback } from './common/Action' import type { DataLoader } from './common/DataLoader' import type VisibleRange from './common/VisibleRange' -import type { Formatter, DecimalFold, LayoutChild, Options, ThousandsSeparator, ZoomAnchor } from './Options' +import type { Formatter, DecimalFold, LayoutChild, Options, ThousandsSeparator, BarSpaceLimit, ZoomAnchor } from './Options' import Animation from './common/Animation' import { createId } from './common/utils/id' import { createDom } from './common/utils/dom' @@ -686,6 +686,14 @@ export default class ChartImp implements Chart { return this._chartStore.getBarSpace() } + setBarSpaceLimit (limit: Partial): void { + this._chartStore.setBarSpaceLimit(limit) + } + + getBarSpaceLimit (): BarSpaceLimit { + return this._chartStore.getBarSpaceLimit() + } + getVisibleRange (): VisibleRange { return this._chartStore.getVisibleRange() } diff --git a/src/Options.ts b/src/Options.ts index a64712e17..b9e0a2313 100644 --- a/src/Options.ts +++ b/src/Options.ts @@ -90,6 +90,11 @@ export interface ZoomAnchor { xAxis: 'cursor_point' | 'last_bar' } +export interface BarSpaceLimit { + min: number + max: number +} + export interface Options { locale?: string timezone?: string @@ -99,4 +104,5 @@ export interface Options { decimalFold?: Partial layout?: LayoutChild[] zoomAnchor?: Partial + barSpaceLimit?: Partial } diff --git a/src/Store.ts b/src/Store.ts index bbd5d4a50..4d9d965b4 100644 --- a/src/Store.ts +++ b/src/Store.ts @@ -37,7 +37,7 @@ import { logWarn } from './common/utils/logger' import { UpdateLevel } from './common/Updater' import type { DataLoader, DataLoaderGetBarsParams, DataLoadMore, DataLoadType } from './common/DataLoader' -import type { Options, Formatter, ThousandsSeparator, DecimalFold, FormatDateType, FormatDateParams, FormatBigNumber, FormatExtendText, FormatExtendTextParams, ZoomAnchor } from './Options' +import type { Options, Formatter, ThousandsSeparator, DecimalFold, BarSpaceLimit, FormatDateType, FormatDateParams, FormatBigNumber, FormatExtendText, FormatExtendTextParams, ZoomAnchor } from './Options' import type { IndicatorOverride, IndicatorCreate, IndicatorFilter } from './component/Indicator' import type IndicatorImp from './component/Indicator' @@ -53,7 +53,7 @@ import { PaneIdConstants } from './pane/types' import type Chart from './Chart' -const BarSpaceLimitConstants = { +const DEFAULT_BAR_SPACE_LIMIT = { MIN: 1, MAX: 50 } @@ -114,6 +114,8 @@ export interface Store { setRightMinVisibleBarCount: (barCount: number) => void setBarSpace: (space: number) => void getBarSpace: () => BarSpace + setBarSpaceLimit: (limit: Partial) => void + getBarSpaceLimit: () => BarSpaceLimit getVisibleRange: () => VisibleRange setDataLoader: (dataLoader: DataLoader) => void overrideIndicator: (override: IndicatorCreate) => boolean @@ -247,6 +249,14 @@ export default class StoreImp implements Store { */ private _barSpace = DEFAULT_BAR_SPACE + /** + * Bar space limit + */ + private readonly _barSpaceLimit: BarSpaceLimit = { + min: DEFAULT_BAR_SPACE_LIMIT.MIN, + max: DEFAULT_BAR_SPACE_LIMIT.MAX + } + /** * The space of the draw bar */ @@ -369,7 +379,7 @@ export default class StoreImp implements Store { this._chart = chart this._calcOptimalBarSpace() this._lastBarRightSideDiffBarCount = this._offsetRightDistance / this._barSpace - const { styles, locale, timezone, formatter, thousandsSeparator, decimalFold, zoomAnchor } = options ?? {} + const { styles, locale, timezone, formatter, thousandsSeparator, decimalFold, barSpaceLimit, zoomAnchor } = options ?? {} if (isValid(styles)) { this.setStyles(styles) } @@ -389,6 +399,9 @@ export default class StoreImp implements Store { if (isValid(zoomAnchor)) { this.setZoomAnchor(zoomAnchor) } + if (isValid(barSpaceLimit)) { + this.setBarSpaceLimit(barSpaceLimit) + } } setStyles (value: string | DeepPartial): void { @@ -768,8 +781,21 @@ export default class StoreImp implements Store { } } + setBarSpaceLimit (limit: Partial): void { + if (isValid(limit.min) && isNumber(limit.min)) { + this._barSpaceLimit.min = Math.max(0, limit.min) + } + if (isValid(limit.max) && isNumber(limit.max)) { + this._barSpaceLimit.max = Math.max(this._barSpaceLimit.min, limit.max) + } + } + + getBarSpaceLimit (): BarSpaceLimit { + return { ...this._barSpaceLimit } + } + setBarSpace (barSpace: number, adjustBeforeFunc?: () => void): void { - if (barSpace < BarSpaceLimitConstants.MIN || barSpace > BarSpaceLimitConstants.MAX || this._barSpace === barSpace) { + if (barSpace < this._barSpaceLimit.min || barSpace > this._barSpaceLimit.max || this._barSpace === barSpace) { return } this._barSpace = barSpace diff --git a/src/index.ts b/src/index.ts index 672f722c2..453a1a8b2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,7 +48,7 @@ import type { ActionType } from './common/Action' import type { IndicatorSeries } from './component/Indicator' import type { OverlayMode } from './component/Overlay' -import type { FormatDateType, Options, ZoomAnchor } from './Options' +import type { FormatDateType, Options, BarSpaceLimit, ZoomAnchor } from './Options' import ChartImp, { type Chart, type DomPosition } from './Chart' import { checkCoordinateOnArc } from './extension/figure/arc' @@ -174,6 +174,6 @@ export { registerXAxis, registerYAxis, utils, type LineType, type PolygonType, type TooltipShowRule, type TooltipShowType, type FeatureType, type TooltipFeaturePosition, type CandleTooltipRectPosition, - type CandleType, type FormatDateType, type ZoomAnchor, + type CandleType, type FormatDateType, type BarSpaceLimit, type ZoomAnchor, type DomPosition, type ActionType, type IndicatorSeries, type OverlayMode } From a8a2bf9bb25d6d3ecd244ab337bae218ad33d9d4 Mon Sep 17 00:00:00 2001 From: 019327 Date: Mon, 21 Jul 2025 16:41:31 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0setBarSpaceLimit?= =?UTF-8?q?=E5=92=8CgetBarSpaceLimit=E6=96=87=E6=A1=A3=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vitepress/config/share.js | 2 ++ .../references/instance/getBarSpaceLimit.md | 3 +++ .../references/instance/setBarSpaceLimit.md | 7 ++++++ .../samples/getBarSpaceLimit/index.data.js | 20 +++++++++++++++ .../api/samples/getBarSpaceLimit/index.js | 19 ++++++++++++++ .../api/samples/getBarSpaceLimit/index.vue | 11 ++++++++ .../samples/setBarSpaceLimit/index.data.js | 20 +++++++++++++++ .../api/samples/setBarSpaceLimit/index.js | 21 ++++++++++++++++ .../api/samples/setBarSpaceLimit/index.vue | 11 ++++++++ docs/api/instance/getBarSpaceLimit.md | 23 +++++++++++++++++ docs/api/instance/setBarSpace.md | 2 +- docs/api/instance/setBarSpaceLimit.md | 25 +++++++++++++++++++ docs/en-US/api/instance/getBarSpaceLimit.md | 23 +++++++++++++++++ docs/en-US/api/instance/setBarSpaceLimit.md | 25 +++++++++++++++++++ 14 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 docs/@views/api/references/instance/getBarSpaceLimit.md create mode 100644 docs/@views/api/references/instance/setBarSpaceLimit.md create mode 100644 docs/@views/api/samples/getBarSpaceLimit/index.data.js create mode 100644 docs/@views/api/samples/getBarSpaceLimit/index.js create mode 100644 docs/@views/api/samples/getBarSpaceLimit/index.vue create mode 100644 docs/@views/api/samples/setBarSpaceLimit/index.data.js create mode 100644 docs/@views/api/samples/setBarSpaceLimit/index.js create mode 100644 docs/@views/api/samples/setBarSpaceLimit/index.vue create mode 100644 docs/api/instance/getBarSpaceLimit.md create mode 100644 docs/api/instance/setBarSpaceLimit.md create mode 100644 docs/en-US/api/instance/getBarSpaceLimit.md create mode 100644 docs/en-US/api/instance/setBarSpaceLimit.md diff --git a/docs/.vitepress/config/share.js b/docs/.vitepress/config/share.js index c0241a5a9..9b21a4622 100644 --- a/docs/.vitepress/config/share.js +++ b/docs/.vitepress/config/share.js @@ -49,6 +49,8 @@ export function getInstanceApiMenus (lang = '') { { text: 'setRightMinVisibleBarCount', link: `${prefix}/setRightMinVisibleBarCount` }, { text: 'setBarSpace', link: `${prefix}/setBarSpace` }, { text: 'getBarSpace', link: `${prefix}/getBarSpace` }, + { text: 'setBarSpaceLimit', link: `${prefix}/setBarSpaceLimit` }, + { text: 'getBarSpaceLimit', link: `${prefix}/getBarSpaceLimit` }, { text: 'setSymbol', link: `${prefix}/setSymbol` }, { text: 'getSymbol', link: `${prefix}/getSymbol` }, { text: 'setPeriod', link: `${prefix}/setPeriod` }, diff --git a/docs/@views/api/references/instance/getBarSpaceLimit.md b/docs/@views/api/references/instance/getBarSpaceLimit.md new file mode 100644 index 000000000..a15524b08 --- /dev/null +++ b/docs/@views/api/references/instance/getBarSpaceLimit.md @@ -0,0 +1,3 @@ +```typescript +() => BarSpaceLimit +``` \ No newline at end of file diff --git a/docs/@views/api/references/instance/setBarSpaceLimit.md b/docs/@views/api/references/instance/setBarSpaceLimit.md new file mode 100644 index 000000000..01fcb314c --- /dev/null +++ b/docs/@views/api/references/instance/setBarSpaceLimit.md @@ -0,0 +1,7 @@ +```typescript +interface BarSpaceLimit { + min: number; + max: number; +} +(limit: BarSpaceLimit) => void +``` \ No newline at end of file diff --git a/docs/@views/api/samples/getBarSpaceLimit/index.data.js b/docs/@views/api/samples/getBarSpaceLimit/index.data.js new file mode 100644 index 000000000..031b0d66f --- /dev/null +++ b/docs/@views/api/samples/getBarSpaceLimit/index.data.js @@ -0,0 +1,20 @@ +import fs from 'fs' + +export default { + watch: ['./index.js'], + load (watchedFiles) { + return watchedFiles.reduce((data, file) => { + const result = fs.readFileSync(file, 'utf-8') + let key + if (file.match('index.js')) { + key = 'js' + } else if (file.match('index.css')) { + key = 'css' + } else { + key = 'html' + } + data[key] = result + return data + }, {}) + } +} diff --git a/docs/@views/api/samples/getBarSpaceLimit/index.js b/docs/@views/api/samples/getBarSpaceLimit/index.js new file mode 100644 index 000000000..538fa6155 --- /dev/null +++ b/docs/@views/api/samples/getBarSpaceLimit/index.js @@ -0,0 +1,19 @@ +import { init } from 'klinecharts' + +const chart = init('getBarSpaceLimit-chart') + +chart.setSymbol({ ticker: 'TestSymbol' }) +chart.setPeriod({ span: 1, type: 'day' }) +chart.setDataLoader({ + getBars: ({ + callback + }) => { + fetch('https://klinecharts.com/datas/kline.json') + .then(res => res.json()) + .then(dataList => { + callback(dataList) + }) + } +}) + +const barSpaceLimit = chart.getBarSpaceLimit() diff --git a/docs/@views/api/samples/getBarSpaceLimit/index.vue b/docs/@views/api/samples/getBarSpaceLimit/index.vue new file mode 100644 index 000000000..f6f2ab849 --- /dev/null +++ b/docs/@views/api/samples/getBarSpaceLimit/index.vue @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/docs/@views/api/samples/setBarSpaceLimit/index.data.js b/docs/@views/api/samples/setBarSpaceLimit/index.data.js new file mode 100644 index 000000000..031b0d66f --- /dev/null +++ b/docs/@views/api/samples/setBarSpaceLimit/index.data.js @@ -0,0 +1,20 @@ +import fs from 'fs' + +export default { + watch: ['./index.js'], + load (watchedFiles) { + return watchedFiles.reduce((data, file) => { + const result = fs.readFileSync(file, 'utf-8') + let key + if (file.match('index.js')) { + key = 'js' + } else if (file.match('index.css')) { + key = 'css' + } else { + key = 'html' + } + data[key] = result + return data + }, {}) + } +} diff --git a/docs/@views/api/samples/setBarSpaceLimit/index.js b/docs/@views/api/samples/setBarSpaceLimit/index.js new file mode 100644 index 000000000..d35ec63bc --- /dev/null +++ b/docs/@views/api/samples/setBarSpaceLimit/index.js @@ -0,0 +1,21 @@ +import { init } from 'klinecharts' + +const chart = init('setBarSpaceLimit-chart') +chart.setBarSpaceLimit({ + min: 2, + max: 10 +}) + +chart.setSymbol({ ticker: 'TestSymbol' }) +chart.setPeriod({ span: 1, type: 'day' }) +chart.setDataLoader({ + getBars: ({ + callback + }) => { + fetch('https://klinecharts.com/datas/kline.json') + .then(res => res.json()) + .then(dataList => { + callback(dataList) + }) + } +}) diff --git a/docs/@views/api/samples/setBarSpaceLimit/index.vue b/docs/@views/api/samples/setBarSpaceLimit/index.vue new file mode 100644 index 000000000..d7360a6fb --- /dev/null +++ b/docs/@views/api/samples/setBarSpaceLimit/index.vue @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/docs/api/instance/getBarSpaceLimit.md b/docs/api/instance/getBarSpaceLimit.md new file mode 100644 index 000000000..f4cccaacd --- /dev/null +++ b/docs/api/instance/getBarSpaceLimit.md @@ -0,0 +1,23 @@ +--- +outline: deep +--- + +# getBarSpaceLimit() +`getBarSpaceLimit` 获取图表单根蜡烛柱所占空间最大值/最小值。 + +## 参考 {#reference} + + +### 参数 {#parameters} +`getBarSpaceLimit` 不接收任何参数。 + +### 返回值 {#returns} +`getBarSpaceLimit` 返回一个包含单根蜡烛柱所占空间信息的对象 `BarSpaceLimit` 。 + +## 用法 {#usage} + + +### 基本使用 {#basic} + \ No newline at end of file diff --git a/docs/api/instance/setBarSpace.md b/docs/api/instance/setBarSpace.md index f11a66ce0..fbb55116c 100644 --- a/docs/api/instance/setBarSpace.md +++ b/docs/api/instance/setBarSpace.md @@ -9,7 +9,7 @@ outline: deep ### 参数 {#parameters} -- `space` 空间大小,范围在 1 到 50 之间。 +- `space` 空间大小,默认范围在 1 到 50 之间,可以通过`setBarSpaceLimit`设置。 ### 返回值 {#returns} `setBarSpace` 返回 `undefined` 。 diff --git a/docs/api/instance/setBarSpaceLimit.md b/docs/api/instance/setBarSpaceLimit.md new file mode 100644 index 000000000..e37a7ff69 --- /dev/null +++ b/docs/api/instance/setBarSpaceLimit.md @@ -0,0 +1,25 @@ +--- +outline: deep +--- + +# setBarSpaceLimit(limit) +`setBarSpaceLimit` 设置图表单根蜡烛柱所占的空间最大值和最小值 + +## 参考 {#reference} + + +### 参数 {#parameters} +- `limit` 空间大小限制。 + - `min` 最小值。 + - `max` 最大值。 + +### 返回值 {#returns} +`setBarSpaceLimit` 返回 `undefined` 。 + +## 用法 {#usage} + + +### 基本用法 {#basic} + \ No newline at end of file diff --git a/docs/en-US/api/instance/getBarSpaceLimit.md b/docs/en-US/api/instance/getBarSpaceLimit.md new file mode 100644 index 000000000..6b9414b51 --- /dev/null +++ b/docs/en-US/api/instance/getBarSpaceLimit.md @@ -0,0 +1,23 @@ +--- +outline: deep +--- + +# getBarSpaceLimit() +`getBarSpaceLimit` get limit size about the space occupied by a single candlestick on the chart. + +## Reference {#reference} + + +### Parameters {#parameters} +`getBarSpaceLimit` does not accept any parameters. + +### Returns {#returns} +`getBarSpaceLimit` returns an object containing information about the space occupied by a single candlestick `BarSpaceLimit`. + +## Usage {#usage} + + +### Basic usage {#basic} + \ No newline at end of file diff --git a/docs/en-US/api/instance/setBarSpaceLimit.md b/docs/en-US/api/instance/setBarSpaceLimit.md new file mode 100644 index 000000000..a9d18da6f --- /dev/null +++ b/docs/en-US/api/instance/setBarSpaceLimit.md @@ -0,0 +1,25 @@ +--- +outline: deep +--- + +# setBarSpaceLimit(limit) +`setBarSpaceLimit` set the limit size of the space that a single candlestick on the chart should occupy. + +## Reference {#reference} + + +### Parameters {#parameters} +- `limit` The limit size of the space + - `min` min value of the space + - `max` max value of the space + +### Returns {#returns} +`setBarSpaceLimit` returns `undefined` 。 + +## Usage {#usage} + + +### Basic usage {#basic} + \ No newline at end of file From e76df358ee4941d1591e8c22cef70f20f8a98402 Mon Sep 17 00:00:00 2001 From: Abdulbasit Mamman Date: Thu, 20 Nov 2025 17:50:44 +0100 Subject: [PATCH 3/8] feat(hiekin_ashi): re-write getDataList and getVisibleRangeDataList to allow parse to heikin_ashi --- src/Chart.ts | 4 +-- src/Store.ts | 86 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/Chart.ts b/src/Chart.ts index 1336e2864..d5b962625 100644 --- a/src/Chart.ts +++ b/src/Chart.ts @@ -702,8 +702,8 @@ export default class ChartImp implements Chart { this._chartStore.resetData() } - getDataList (): KLineData[] { - return this._chartStore.getDataList() + getDataList (mutateToCandleType = false): KLineData[] { + return this._chartStore.getDataList(mutateToCandleType) } setDataLoader (dataLoader: DataLoader): void { diff --git a/src/Store.ts b/src/Store.ts index 4d9d965b4..da18d3b2e 100644 --- a/src/Store.ts +++ b/src/Store.ts @@ -105,7 +105,7 @@ export interface Store { getSymbol: () => Nullable setPeriod: (period: Period) => void getPeriod: () => Nullable - getDataList: () => KLineData[] + getDataList: (mutateToCandleType?: boolean) => KLineData[] setOffsetRightDistance: (distance: number) => void getOffsetRightDistance: () => number setMaxOffsetLeftDistance: (distance: number) => void @@ -525,18 +525,94 @@ export default class StoreImp implements Store { return this._period } - getDataList (): KLineData[] { - return this._dataList + getDataList (mutateToCandleType = false): KLineData[] { + if (!mutateToCandleType || this._styles.candle.type !== 'heikin_ashi') { + return this._dataList + } + + let prevHaBar: Nullable = null + return this._dataList.map(value => { + const data = this.ohlcvToHeikinAshi(value, prevHaBar) + prevHaBar = data + return data + }) } - getVisibleRangeDataList (): VisibleRangeData[] { - return this._visibleRangeDataList + getVisibleRangeDataList (mutateToCandleType = false): VisibleRangeData[] { + if (!mutateToCandleType || this._styles.candle.type !== 'heikin_ashi') { + return this._visibleRangeDataList + } + + const result: VisibleRangeData[] = [] + let prevHaBar: Nullable = null + const list = this._visibleRangeDataList + const len = list.length + + for (let i = 0; i < len; i++) { + const curOrig: Nullable = list[i].data.current ?? null + if (curOrig === null) { + continue + } + const curHa = this.ohlcvToHeikinAshi(curOrig, prevHaBar) + const prevHa = prevHaBar + + let nextHa: Nullable = null + if (i + 1 < len) { + const nextOrig: Nullable = list[i + 1].data.current ?? null + nextHa = nextOrig === null ? null : this.ohlcvToHeikinAshi(nextOrig, curHa) + } else { + nextHa = null + } + + result.push({ + dataIndex: list[i].dataIndex, + x: list[i].x, + data: { + prev: prevHa, + current: curHa, + next: nextHa + } + }) + + prevHaBar = curHa + } + + return result } getVisibleRangeHighLowPrice (): Array<{ price: number; x: number }> { return this._visibleRangeHighLowPrice } + private ohlcvToHeikinAshi (currentBar: KLineData, prevHaBar: Nullable): KLineData { + if (prevHaBar === null) { + const firstHaBar: KLineData = { + timestamp: currentBar.timestamp, + open: (currentBar.open + currentBar.close) / 2, // Some methods use currentBar.o, but averaging is more common + high: currentBar.high, + low: currentBar.low, + close: (currentBar.open + currentBar.high + currentBar.low + currentBar.close) / 4 + } + + return firstHaBar + } + + const haClose = (currentBar.open + currentBar.high + currentBar.low + currentBar.close) / 4 + const haOpen = (prevHaBar.open + prevHaBar.close) / 2 + const haHigh = Math.max(currentBar.high, haOpen, haClose) + const haLow = Math.min(currentBar.low, haOpen, haClose) + + const newHaBar: KLineData = { + timestamp: currentBar.timestamp, + open: haOpen, + high: haHigh, + low: haLow, + close: haClose + } + + return newHaBar + } + private _addData ( data: KLineData | KLineData[], type: DataLoadType, From 814da33306d02e423a54ef5a40dedce443528627 Mon Sep 17 00:00:00 2001 From: Abdulbasit Mamman Date: Thu, 20 Nov 2025 17:52:51 +0100 Subject: [PATCH 4/8] feat(heikin_ashi): add 'heikin_ashi' to CandleType --- src/common/Styles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/Styles.ts b/src/common/Styles.ts index 82a341aa2..8b045c6b6 100644 --- a/src/common/Styles.ts +++ b/src/common/Styles.ts @@ -237,7 +237,7 @@ export interface CandleTooltipStyle extends TooltipStyle { rect: CandleTooltipRectStyle } -export type CandleType = 'candle_solid' | 'candle_stroke' | 'candle_up_stroke' | 'candle_down_stroke' | 'ohlc' | 'area' +export type CandleType = 'candle_solid' | 'candle_stroke' | 'candle_up_stroke' | 'candle_down_stroke' | 'ohlc' | 'area' | 'heikin_ashi' export type CandleColorCompareRule = 'current_open' | 'previous_close' From ad7a59c6a9769b4f8761fd20b5baac806bb880a7 Mon Sep 17 00:00:00 2001 From: Abdulbasit Mamman Date: Thu, 20 Nov 2025 17:55:43 +0100 Subject: [PATCH 5/8] feat(heikin_ashi): ensure getDataList and getVisibleRangeDataList return hiekin ashi if required --- src/view/CandleBarView.ts | 4 +++- src/view/CandleLastPriceLabelView.ts | 3 ++- src/view/CandleLastPriceLineView.ts | 3 ++- src/view/ChildrenView.ts | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/view/CandleBarView.ts b/src/view/CandleBarView.ts index c1f56a5e2..94e77b646 100644 --- a/src/view/CandleBarView.ts +++ b/src/view/CandleBarView.ts @@ -55,6 +55,7 @@ export default class CandleBarView extends ChildrenView { halfOhlcSize = Math.floor(ohlcSize / 2) } const yAxis = pane.getAxisComponent() + this.eachChildren((visibleData, barSpace) => { const { x, data: { current, prev } } = visibleData if (isValid(current)) { @@ -86,7 +87,8 @@ export default class CandleBarView extends ChildrenView { const correction = barSpace.gapBar % 2 === 0 ? 1 : 0 let rects: Array>> = [] switch (type) { - case 'candle_solid': { + case 'candle_solid': + case 'heikin_ashi': { rects = this._createSolidBar(x, priceY, barSpace, colors, correction) break } diff --git a/src/view/CandleLastPriceLabelView.ts b/src/view/CandleLastPriceLabelView.ts index bd3cc1608..3e7ec7df0 100644 --- a/src/view/CandleLastPriceLabelView.ts +++ b/src/view/CandleLastPriceLabelView.ts @@ -35,8 +35,9 @@ export default class CandleLastPriceLabelView extends View { if (priceMarkStyles.show && lastPriceMarkStyles.show && lastPriceMarkTextStyles.show) { const precision = chartStore.getSymbol()?.pricePrecision ?? SymbolDefaultPrecisionConstants.PRICE const yAxis = pane.getAxisComponent() as YAxis - const dataList = chartStore.getDataList() + const dataList = chartStore.getDataList(true) const data = dataList[dataList.length - 1] + if (isValid(data)) { const { close, open } = data const comparePrice = lastPriceMarkStyles.compareRule === 'current_open' ? open : (dataList[dataList.length - 2]?.close ?? close) diff --git a/src/view/CandleLastPriceLineView.ts b/src/view/CandleLastPriceLineView.ts index 3d08764e3..14de1bd59 100644 --- a/src/view/CandleLastPriceLineView.ts +++ b/src/view/CandleLastPriceLineView.ts @@ -28,8 +28,9 @@ export default class CandleLastPriceView extends View { const lastPriceMarkLineStyles = lastPriceMarkStyles.line if (priceMarkStyles.show && lastPriceMarkStyles.show && lastPriceMarkLineStyles.show) { const yAxis = pane.getAxisComponent() as YAxis - const dataList = chartStore.getDataList() + const dataList = chartStore.getDataList(true) const data = dataList[dataList.length - 1] + if (isValid(data)) { const { close, open } = data const comparePrice = lastPriceMarkStyles.compareRule === 'current_open' ? open : (dataList[dataList.length - 2]?.close ?? close) diff --git a/src/view/ChildrenView.ts b/src/view/ChildrenView.ts index ec517159d..1b02d84ea 100644 --- a/src/view/ChildrenView.ts +++ b/src/view/ChildrenView.ts @@ -27,7 +27,7 @@ export default abstract class ChildrenView extends View { protected eachChildren (childCallback: EachChildCallback): void { const pane = this.getWidget().getPane() const chartStore = pane.getChart().getChartStore() - const visibleRangeDataList = chartStore.getVisibleRangeDataList() + const visibleRangeDataList = chartStore.getVisibleRangeDataList(true) const barSpace = chartStore.getBarSpace() const dataLength = visibleRangeDataList.length let index = 0 From 8ba588a5f94bc623ad159781a66a3b4897ebc86a Mon Sep 17 00:00:00 2001 From: Abdulbasit Mamman Date: Thu, 20 Nov 2025 17:57:05 +0100 Subject: [PATCH 6/8] feat(heikin_ashi): add 'heikin_ashi' to the candle type options in styleOptions docs --- docs/@views/styles/config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/@views/styles/config.md b/docs/@views/styles/config.md index 42ea74eac..cfa6d84bd 100644 --- a/docs/@views/styles/config.md +++ b/docs/@views/styles/config.md @@ -18,7 +18,7 @@ const styles = { } }, candle: { - // 'candle_solid' | 'candle_stroke' | 'candle_up_stroke' | 'candle_down_stroke' | 'ohlc' | 'area' + // 'candle_solid' | 'candle_stroke' | 'candle_up_stroke' | 'candle_down_stroke' | 'ohlc' | 'area' | 'heikin_ashi' type: 'candle_solid', bar: { // 'current_open' | 'previous_close' From cdf596fb58e61774c71d7a7981c4900365c51d76 Mon Sep 17 00:00:00 2001 From: Abdulbasit Mamman Date: Fri, 23 Jan 2026 09:37:59 +0100 Subject: [PATCH 7/8] feat(implement-heikin-ashi-bars): mutate data to candleType for heikin ashi --- src/view/CandleBarView.ts | 2 +- src/view/ChildrenView.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/view/CandleBarView.ts b/src/view/CandleBarView.ts index 94e77b646..fe44b2638 100644 --- a/src/view/CandleBarView.ts +++ b/src/view/CandleBarView.ts @@ -152,7 +152,7 @@ export default class CandleBarView extends ChildrenView { this.createFigure(rect, handler ?? undefined)?.draw(ctx) }) } - }) + }, true) } } diff --git a/src/view/ChildrenView.ts b/src/view/ChildrenView.ts index 1b02d84ea..c04fe37fc 100644 --- a/src/view/ChildrenView.ts +++ b/src/view/ChildrenView.ts @@ -24,10 +24,10 @@ export type EachChildCallback = ( ) => void export default abstract class ChildrenView extends View { - protected eachChildren (childCallback: EachChildCallback): void { + protected eachChildren (childCallback: EachChildCallback, mutateToCandleType: boolean = false): void { const pane = this.getWidget().getPane() const chartStore = pane.getChart().getChartStore() - const visibleRangeDataList = chartStore.getVisibleRangeDataList(true) + const visibleRangeDataList = chartStore.getVisibleRangeDataList(mutateToCandleType) const barSpace = chartStore.getBarSpace() const dataLength = visibleRangeDataList.length let index = 0 From a03059abc674af276f721d52d4bce4610722e767 Mon Sep 17 00:00:00 2001 From: Abdulbasit Mamman Date: Fri, 23 Jan 2026 09:38:50 +0100 Subject: [PATCH 8/8] feat(implement-heikin-ashi-bars): mutate data to candleType for heikin ashi --- src/view/ChildrenView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/ChildrenView.ts b/src/view/ChildrenView.ts index c04fe37fc..f55813f87 100644 --- a/src/view/ChildrenView.ts +++ b/src/view/ChildrenView.ts @@ -24,7 +24,7 @@ export type EachChildCallback = ( ) => void export default abstract class ChildrenView extends View { - protected eachChildren (childCallback: EachChildCallback, mutateToCandleType: boolean = false): void { + protected eachChildren (childCallback: EachChildCallback, mutateToCandleType = false): void { const pane = this.getWidget().getPane() const chartStore = pane.getChart().getChartStore() const visibleRangeDataList = chartStore.getVisibleRangeDataList(mutateToCandleType)