Skip to content

Commit b2688b9

Browse files
committed
refactor: refactor indicator module,add shouldUpdate callback.
1 parent 039f425 commit b2688b9

File tree

11 files changed

+137
-280
lines changed

11 files changed

+137
-280
lines changed

src/Chart.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,8 @@ export default class ChartImp implements Chart {
495495
const id = pane.getId()
496496
const paneIndicatorData = {}
497497
const indicators = this._chartStore.getIndicatorStore().getInstances(id)
498-
indicators.forEach(indicator => {
498+
indicators.forEach(proxy => {
499+
const indicator = proxy.getIndicator()
499500
const result = indicator.result
500501
paneIndicatorData[indicator.name] = result[crosshair.dataIndex ?? result.length - 1]
501502
})
@@ -711,7 +712,7 @@ export default class ChartImp implements Chart {
711712

712713
createIndicator (value: string | IndicatorCreate, isStack?: boolean, paneOptions?: Nullable<PaneOptions>, callback?: () => void): Nullable<string> {
713714
const indicator = isString(value) ? { name: value } : value
714-
if (getIndicatorClass(indicator.name) === null) {
715+
if (getIndicatorClass(indicator.name as string) === null) {
715716
logWarn('createIndicator', 'value', 'indicator not supported, you may need to use registerIndicator to add one!!!')
716717
return null
717718
}

src/common/ExcludePickPartial.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
* limitations under the License.
1313
*/
1414

15-
type ExcludePickPartial<T, K extends keyof T> = Partial<Omit<T, K>> & Pick<T, K>
15+
import type PickRequired from './PickRequired'
16+
17+
type ExcludePickPartial<T, K extends keyof T> = PickRequired<Partial<T>, K>
1618

1719
export default ExcludePickPartial

src/component/Indicator.ts

Lines changed: 82 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ import type VisibleRange from '../common/VisibleRange'
2020
import type BarSpace from '../common/BarSpace'
2121
import type Crosshair from '../common/Crosshair'
2222
import { type IndicatorStyle, type IndicatorPolygonStyle, type SmoothLineStyle, type RectStyle, type TextStyle, type TooltipIconStyle, type LineStyle, type LineType, type PolygonType, type TooltipLegend } from '../common/Styles'
23+
import { isNumber, isValid, merge, clone, isArray, isBoolean } from '../common/utils/typeChecks'
2324

2425
import { type XAxis } from './XAxis'
2526
import { type YAxis } from './YAxis'
2627

2728
import { formatValue } from '../common/utils/format'
28-
import { isValid, merge, clone } from '../common/utils/typeChecks'
2929

3030
import { type ArcAttrs } from '../extension/figure/arc'
3131
import { type RectAttrs } from '../extension/figure/rect'
@@ -79,6 +79,8 @@ export interface IndicatorFigure<D = any> {
7979
styles?: IndicatorFigureStylesCallback<D>
8080
}
8181

82+
export type IndicatorShouldUpdateReturn = boolean | { calc: boolean, draw: boolean }
83+
8284
export type IndicatorRegenerateFiguresCallback<D = any> = (calcParams: any[]) => Array<IndicatorFigure<D>>
8385

8486
export interface IndicatorTooltipData {
@@ -187,6 +189,11 @@ export interface Indicator<D = any> {
187189
*/
188190
styles: Nullable<Partial<IndicatorStyle>>
189191

192+
/**
193+
* Should update, should calc or draw
194+
*/
195+
shouldUpdate: (prev: Indicator<D>, current: Indicator<D>) => IndicatorShouldUpdateReturn
196+
190197
/**
191198
* Indicator calculation
192199
*/
@@ -211,6 +218,11 @@ export interface Indicator<D = any> {
211218
* Calculation result
212219
*/
213220
result: D[]
221+
222+
/**
223+
* Others
224+
*/
225+
[key: string]: any
214226
}
215227

216228
export type IndicatorTemplate<D = any> = ExcludePickPartial<Omit<Indicator<D>, 'result'>, 'name' | 'calc'>
@@ -284,204 +296,103 @@ export function eachFigures<D> (
284296
})
285297
}
286298

287-
export default abstract class IndicatorImp<D = any> implements Indicator<D> {
288-
name: string
289-
shortName: string
290-
precision: number
291-
calcParams: any[]
292-
shouldOhlc: boolean
293-
shouldFormatBigNumber: boolean
294-
visible: boolean
295-
zLevel: number
296-
extendData: any
297-
series: IndicatorSeries
298-
figures: Array<IndicatorFigure<D>>
299-
minValue: Nullable<number>
300-
maxValue: Nullable<number>
301-
styles: Nullable<Partial<IndicatorStyle>>
302-
regenerateFigures: Nullable<IndicatorRegenerateFiguresCallback<D>>
303-
createTooltipDataSource: Nullable<IndicatorCreateTooltipDataSourceCallback>
304-
draw: Nullable<IndicatorDrawCallback<D>>
305-
306-
result: D[] = []
307-
308-
private _precisionFlag: boolean = false
309-
310-
constructor (indicator: IndicatorTemplate) {
311-
const {
312-
name, shortName, series, calcParams, figures, precision,
313-
shouldOhlc, shouldFormatBigNumber, visible, zLevel,
314-
minValue, maxValue, styles, extendData,
315-
regenerateFigures, createTooltipDataSource, draw
316-
} = indicator
317-
this.name = name
318-
this.shortName = shortName ?? name
319-
this.series = series ?? IndicatorSeries.Normal
320-
this.precision = precision ?? 4
321-
this.calcParams = calcParams ?? []
322-
this.figures = figures ?? []
323-
this.shouldOhlc = shouldOhlc ?? false
324-
this.shouldFormatBigNumber = shouldFormatBigNumber ?? false
325-
this.visible = visible ?? true
326-
this.zLevel = zLevel ?? 0
327-
this.minValue = minValue ?? null
328-
this.maxValue = maxValue ?? null
329-
this.styles = clone(styles ?? {})
330-
this.extendData = extendData
331-
this.regenerateFigures = regenerateFigures ?? null
332-
this.createTooltipDataSource = createTooltipDataSource ?? null
333-
this.draw = draw ?? null
334-
}
335-
336-
setShortName (shortName: string): boolean {
337-
if (this.shortName !== shortName) {
338-
this.shortName = shortName
339-
return true
340-
}
341-
return false
342-
}
343-
344-
setSeries (series: IndicatorSeries): boolean {
345-
if (this.series !== series) {
346-
this.series = series
347-
return true
348-
}
349-
return false
350-
}
351-
352-
setPrecision (precision: number, flag?: boolean): boolean {
353-
const f = flag ?? false
354-
const optimalPrecision = Math.floor(precision)
355-
if (optimalPrecision !== this.precision && precision >= 0 && (!f || (f && !this._precisionFlag))) {
356-
this.precision = optimalPrecision
357-
if (!f) {
358-
this._precisionFlag = true
359-
}
360-
return true
361-
}
362-
return false
363-
}
364-
365-
setCalcParams (params: any[]): boolean {
366-
this.calcParams = params
367-
this.figures = this.regenerateFigures?.(params) ?? this.figures
368-
return true
299+
export default class IndicatorImp<D = any> {
300+
private _prevIndicator: Indicator<D>
301+
private readonly _indicator: Indicator<D> = {
302+
name: '',
303+
shortName: '',
304+
precision: 4,
305+
calcParams: [],
306+
shouldOhlc: false,
307+
shouldFormatBigNumber: false,
308+
visible: true,
309+
zLevel: 0,
310+
extendData: null,
311+
series: IndicatorSeries.Normal,
312+
figures: [],
313+
minValue: null,
314+
maxValue: null,
315+
styles: {},
316+
regenerateFigures: null,
317+
createTooltipDataSource: null,
318+
shouldUpdate: (prev, current) => {
319+
const calc = JSON.stringify(prev.calcParams) !== JSON.stringify(current.calcParams) ||
320+
prev.figures !== current.figures ||
321+
prev.calc !== current.calc
322+
const draw = calc ||
323+
prev.shortName !== current.shortName ||
324+
prev.series !== current.series ||
325+
prev.minValue !== current.minValue ||
326+
prev.maxValue !== current.maxValue ||
327+
prev.precision !== current.precision ||
328+
prev.shouldOhlc !== current.shouldOhlc ||
329+
prev.shouldFormatBigNumber !== current.shouldFormatBigNumber ||
330+
prev.visible !== current.visible ||
331+
prev.zLevel !== current.zLevel ||
332+
prev.extendData !== current.extendData ||
333+
prev.regenerateFigures !== current.regenerateFigures ||
334+
prev.createTooltipDataSource !== current.createTooltipDataSource ||
335+
prev.draw !== current.draw
336+
337+
return { calc, draw }
338+
},
339+
calc: () => [],
340+
draw: null,
341+
result: []
369342
}
370343

371-
setShouldOhlc (shouldOhlc: boolean): boolean {
372-
if (this.shouldOhlc !== shouldOhlc) {
373-
this.shouldOhlc = shouldOhlc
374-
return true
375-
}
376-
return false
377-
}
344+
private _lockSeriesPrecision: boolean = false
378345

379-
setShouldFormatBigNumber (shouldFormatBigNumber: boolean): boolean {
380-
if (this.shouldFormatBigNumber !== shouldFormatBigNumber) {
381-
this.shouldFormatBigNumber = shouldFormatBigNumber
382-
return true
346+
constructor (indicator: IndicatorTemplate<D>) {
347+
this.override(indicator)
348+
this._indicator.shortName ??= this._indicator.name
349+
if (isArray(indicator.figures)) {
350+
this._indicator.figures = indicator.figures
383351
}
384-
return false
385352
}
386353

387-
setVisible (visible: boolean): boolean {
388-
if (this.visible !== visible) {
389-
this.visible = visible
390-
return true
391-
}
392-
return false
354+
getIndicator (): Indicator<D> {
355+
return this._indicator
393356
}
394357

395-
setZLevel (zLevel: number): boolean {
396-
if (this.zLevel !== zLevel) {
397-
this.zLevel = zLevel
398-
return true
358+
override (indicator: IndicatorCreate<D>): void {
359+
this._prevIndicator = clone(this._indicator)
360+
merge(this._indicator, indicator)
361+
if (isNumber(indicator.precision)) {
362+
this._lockSeriesPrecision = true
399363
}
400-
return false
401364
}
402365

403-
setStyles (styles: Partial<IndicatorStyle>): boolean {
404-
merge(this.styles, styles)
405-
return true
406-
}
407-
408-
setExtendData (extendData: any): boolean {
409-
if (this.extendData !== extendData) {
410-
this.extendData = extendData
411-
return true
366+
setSeriesPrecision (precision: number): void {
367+
if (!this._lockSeriesPrecision) {
368+
this._indicator.precision = precision
412369
}
413-
return false
414370
}
415371

416-
setFigures (figures: IndicatorFigure[]): boolean {
417-
if (this.figures !== figures) {
418-
this.figures = figures
419-
return true
372+
shouldUpdate (): ({ calc: boolean, draw: boolean, sort: boolean }) {
373+
const sort = this._prevIndicator.zLevel !== this._indicator.zLevel
374+
const result = this._indicator.shouldUpdate(this._prevIndicator, this._indicator)
375+
if (isBoolean(result)) {
376+
return { calc: result, draw: result, sort }
420377
}
421-
return false
378+
return { ...result, sort }
422379
}
423380

424-
setMinValue (value: Nullable<number>): boolean {
425-
if (this.minValue !== value) {
426-
this.minValue = value
427-
return true
428-
}
429-
return false
430-
}
431-
432-
setMaxValue (value: Nullable<number>): boolean {
433-
if (this.maxValue !== value) {
434-
this.maxValue = value
435-
return true
436-
}
437-
return false
438-
}
439-
440-
setRegenerateFigures (callback: Nullable<IndicatorRegenerateFiguresCallback>): boolean {
441-
if (this.regenerateFigures !== callback) {
442-
this.regenerateFigures = callback
443-
return true
444-
}
445-
return false
446-
}
447-
448-
setCreateTooltipDataSource (callback: Nullable<IndicatorCreateTooltipDataSourceCallback>): boolean {
449-
if (this.createTooltipDataSource !== callback) {
450-
this.createTooltipDataSource = callback
451-
return true
452-
}
453-
return false
454-
}
455-
456-
setDraw (callback: Nullable<IndicatorDrawCallback>): boolean {
457-
if (this.draw !== callback) {
458-
this.draw = callback
459-
return true
460-
}
461-
return false
462-
}
463-
464-
async calcIndicator (dataList: KLineData[]): Promise<boolean> {
381+
async calc (dataList: KLineData[]): Promise<boolean> {
465382
try {
466-
const result = await this.calc(dataList, this)
467-
this.result = result
383+
const result = await this._indicator.calc(dataList, this._indicator)
384+
this._indicator.result = result
468385
return true
469386
} catch (e) {
470387
return false
471388
}
472389
}
473390

474-
abstract calc (dataList: KLineData[], indicator: Indicator<D>): D[] | Promise<D[]>
475-
476-
static extend<D> (template: IndicatorTemplate): IndicatorConstructor<D> {
391+
static extend<D> (template: IndicatorTemplate<D>): IndicatorConstructor<D> {
477392
class Custom extends IndicatorImp<D> {
478393
constructor () {
479394
super(template)
480395
}
481-
482-
calc (dataList: KLineData[], indicator: Indicator<D>): D[] | Promise<D[]> {
483-
return template.calc(dataList, indicator)
484-
}
485396
}
486397
return Custom
487398
}

src/component/YAxis.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
5252
let specifyMax = Number.MIN_SAFE_INTEGER
5353
let indicatorPrecision = Number.MAX_SAFE_INTEGER
5454
const indicators = chartStore.getIndicatorStore().getInstances(parent.getId())
55-
indicators.forEach(indicator => {
55+
indicators.forEach(proxy => {
56+
const indicator = proxy.getIndicator()
5657
if (!shouldOhlc) {
5758
shouldOhlc = indicator.shouldOhlc ?? false
5859
}
@@ -262,10 +263,11 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
262263
if (this.isInCandle()) {
263264
precision = chartStore.getPrecision().price
264265
} else {
265-
indicators.forEach(tech => {
266-
precision = Math.max(precision, tech.precision)
266+
indicators.forEach(proxy => {
267+
const indicator = proxy.getIndicator()
268+
precision = Math.max(precision, indicator.precision)
267269
if (!shouldFormatBigNumber) {
268-
shouldFormatBigNumber = tech.shouldFormatBigNumber
270+
shouldFormatBigNumber = indicator.shouldFormatBigNumber
269271
}
270272
})
271273
}
@@ -342,10 +344,11 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
342344
const indicators = chartStore.getIndicatorStore().getInstances(pane.getId())
343345
let techPrecision = 0
344346
let shouldFormatBigNumber = false
345-
indicators.forEach(tech => {
346-
techPrecision = Math.max(tech.precision, techPrecision)
347+
indicators.forEach(proxy => {
348+
const indicator = proxy.getIndicator()
349+
techPrecision = Math.max(indicator.precision, techPrecision)
347350
if (!shouldFormatBigNumber) {
348-
shouldFormatBigNumber = tech.shouldFormatBigNumber
351+
shouldFormatBigNumber = indicator.shouldFormatBigNumber
349352
}
350353
})
351354
let precision = 2

0 commit comments

Comments
 (0)