@363045841yyt/klinechart-core 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -201
- package/README.zh-CN.md +201 -201
- package/dist/controllers/index.d.ts +1 -0
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/index.js +1 -0
- package/dist/controllers/index.js.map +1 -1
- package/dist/engine/chart.d.ts +11 -19
- package/dist/engine/chart.d.ts.map +1 -1
- package/dist/engine/chart.js +92 -109
- package/dist/engine/chart.js.map +1 -1
- package/dist/engine/renderers/Indicator/indicatorData.d.ts +1 -0
- package/dist/engine/renderers/Indicator/indicatorData.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/indicatorData.js +1 -1
- package/dist/engine/renderers/Indicator/indicatorData.js.map +1 -1
- package/dist/engine/renderers/webgl/candleSurface.js +47 -47
- package/dist/engine/subPaneManager.d.ts +4 -0
- package/dist/engine/subPaneManager.d.ts.map +1 -1
- package/dist/engine/subPaneManager.js +13 -0
- package/dist/engine/subPaneManager.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -2
- package/dist/version.js.map +1 -1
- package/package.json +129 -122
- package/src/__tests__/signal.test.ts +124 -124
- package/src/config/chartSettings.ts +66 -66
- package/src/controllers/__tests__/drawing.test.ts +214 -214
- package/src/controllers/__tests__/indicatorSelector.test.ts +481 -481
- package/src/controllers/__tests__/toolbar.test.ts +225 -225
- package/src/controllers/createChartController.ts +665 -665
- package/src/controllers/createDrawingController.ts +96 -96
- package/src/controllers/createIndicatorSelectorController.ts +307 -307
- package/src/controllers/createToolbarController.ts +146 -146
- package/src/controllers/index.ts +20 -19
- package/src/controllers/types.ts +284 -284
- package/src/engine/__tests__/chart.dpr.test.ts +401 -401
- package/src/engine/__tests__/paneRenderer.resize.test.ts +92 -92
- package/src/engine/chart-store.ts +121 -121
- package/src/engine/chart.d.ts +617 -617
- package/src/engine/chart.ts +2803 -2815
- package/src/engine/controller/__tests__/interaction.dpr.test.ts +259 -259
- package/src/engine/controller/interaction.ts +722 -722
- package/src/engine/controller/markerInteraction.ts +130 -130
- package/src/engine/controller/pinchTracker.ts +82 -82
- package/src/engine/controller/tooltipPosition.ts +48 -48
- package/src/engine/draw/__tests__/pixelAlign.spec.ts +176 -176
- package/src/engine/draw/pixelAlign.ts +259 -259
- package/src/engine/drawing/index.ts +655 -655
- package/src/engine/drawing/interaction.ts +842 -842
- package/src/engine/drawing/plugin.ts +343 -343
- package/src/engine/indicators/__tests__/__fixtures__/golden/atr.json +38 -38
- package/src/engine/indicators/__tests__/__fixtures__/golden/dema.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/hma.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/index.ts +55 -55
- package/src/engine/indicators/__tests__/__fixtures__/golden/kama.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/tema.json +14 -14
- package/src/engine/indicators/__tests__/__fixtures__/golden/wma.json +40 -40
- package/src/engine/indicators/__tests__/__fixtures__/synthetic.ts +65 -65
- package/src/engine/indicators/__tests__/_propertyAssertions.ts +76 -76
- package/src/engine/indicators/__tests__/atr.test.ts +153 -153
- package/src/engine/indicators/__tests__/calculators.test.ts +614 -614
- package/src/engine/indicators/__tests__/cmf-mfi.test.ts +100 -100
- package/src/engine/indicators/__tests__/dema.test.ts +73 -73
- package/src/engine/indicators/__tests__/donchian.test.ts +70 -70
- package/src/engine/indicators/__tests__/hma.test.ts +73 -73
- package/src/engine/indicators/__tests__/ichimoku.test.ts +105 -105
- package/src/engine/indicators/__tests__/kama.test.ts +80 -80
- package/src/engine/indicators/__tests__/keltner.test.ts +65 -65
- package/src/engine/indicators/__tests__/pivot-fib.test.ts +110 -110
- package/src/engine/indicators/__tests__/roc.test.ts +68 -68
- package/src/engine/indicators/__tests__/sar.test.ts +86 -86
- package/src/engine/indicators/__tests__/scheduler.test.ts +831 -831
- package/src/engine/indicators/__tests__/soa.test.ts +533 -533
- package/src/engine/indicators/__tests__/structure.test.ts +110 -110
- package/src/engine/indicators/__tests__/supertrend.test.ts +65 -65
- package/src/engine/indicators/__tests__/tema.test.ts +68 -68
- package/src/engine/indicators/__tests__/trix.test.ts +70 -70
- package/src/engine/indicators/__tests__/volatility.test.ts +117 -117
- package/src/engine/indicators/__tests__/volume.test.ts +115 -115
- package/src/engine/indicators/__tests__/volumeProfile.test.ts +74 -74
- package/src/engine/indicators/__tests__/vwap.test.ts +69 -69
- package/src/engine/indicators/__tests__/wma.test.ts +112 -112
- package/src/engine/indicators/__tests__/zones.test.ts +95 -95
- package/src/engine/indicators/atrState.ts +27 -27
- package/src/engine/indicators/bollState.ts +51 -51
- package/src/engine/indicators/calculators.ts +2593 -2593
- package/src/engine/indicators/cciState.ts +25 -25
- package/src/engine/indicators/chaikinVolState.ts +32 -32
- package/src/engine/indicators/cmfState.ts +27 -27
- package/src/engine/indicators/demaState.ts +27 -27
- package/src/engine/indicators/donchianState.ts +43 -43
- package/src/engine/indicators/eneState.ts +43 -43
- package/src/engine/indicators/expmaState.ts +43 -43
- package/src/engine/indicators/fastkState.ts +25 -25
- package/src/engine/indicators/fibState.ts +41 -41
- package/src/engine/indicators/hmaState.ts +27 -27
- package/src/engine/indicators/hvState.ts +28 -28
- package/src/engine/indicators/ichimokuState.ts +70 -70
- package/src/engine/indicators/indicator.worker.ts +169 -169
- package/src/engine/indicators/indicatorDefinitionRegistry.ts +62 -62
- package/src/engine/indicators/indicatorMetadata.ts +110 -110
- package/src/engine/indicators/indicatorRegistry.ts +106 -106
- package/src/engine/indicators/indicatorRuntime.ts +1548 -1548
- package/src/engine/indicators/kamaState.ts +34 -34
- package/src/engine/indicators/keltnerState.ts +49 -49
- package/src/engine/indicators/kstState.ts +42 -42
- package/src/engine/indicators/maState.ts +36 -36
- package/src/engine/indicators/macdState.ts +76 -76
- package/src/engine/indicators/mfiState.ts +27 -27
- package/src/engine/indicators/momState.ts +25 -25
- package/src/engine/indicators/obvState.ts +25 -25
- package/src/engine/indicators/parkinsonState.ts +28 -28
- package/src/engine/indicators/pivotState.ts +51 -51
- package/src/engine/indicators/pvtState.ts +25 -25
- package/src/engine/indicators/rocState.ts +27 -27
- package/src/engine/indicators/rsiState.ts +65 -65
- package/src/engine/indicators/sarState.ts +41 -41
- package/src/engine/indicators/scheduler.ts +1205 -1205
- package/src/engine/indicators/soa.ts +352 -352
- package/src/engine/indicators/stateComposer.ts +1262 -1262
- package/src/engine/indicators/stochState.ts +26 -26
- package/src/engine/indicators/structureState.ts +69 -69
- package/src/engine/indicators/supertrendState.ts +37 -37
- package/src/engine/indicators/temaState.ts +27 -27
- package/src/engine/indicators/trixState.ts +35 -35
- package/src/engine/indicators/vmaState.ts +27 -27
- package/src/engine/indicators/volumeProfileState.ts +63 -63
- package/src/engine/indicators/vwapState.ts +29 -29
- package/src/engine/indicators/wmaState.ts +27 -27
- package/src/engine/indicators/wmsrState.ts +25 -25
- package/src/engine/indicators/workerProtocol.ts +613 -613
- package/src/engine/indicators/zonesState.ts +47 -47
- package/src/engine/layout/pane.ts +161 -161
- package/src/engine/marker/registry.ts +265 -265
- package/src/engine/paneRenderer.ts +169 -169
- package/src/engine/renderers/Indicator/atr.ts +237 -237
- package/src/engine/renderers/Indicator/boll.ts +317 -317
- package/src/engine/renderers/Indicator/cci.ts +275 -275
- package/src/engine/renderers/Indicator/chaikinVol.ts +138 -138
- package/src/engine/renderers/Indicator/cmf.ts +137 -137
- package/src/engine/renderers/Indicator/dema.ts +136 -136
- package/src/engine/renderers/Indicator/donchian.ts +137 -137
- package/src/engine/renderers/Indicator/ene.ts +271 -271
- package/src/engine/renderers/Indicator/expma.ts +197 -197
- package/src/engine/renderers/Indicator/fastk.ts +316 -316
- package/src/engine/renderers/Indicator/fib.ts +141 -141
- package/src/engine/renderers/Indicator/hma.ts +136 -136
- package/src/engine/renderers/Indicator/hv.ts +124 -124
- package/src/engine/renderers/Indicator/ichimoku.ts +181 -181
- package/src/engine/renderers/Indicator/index.ts +241 -241
- package/src/engine/renderers/Indicator/indicatorData.ts +650 -650
- package/src/engine/renderers/Indicator/kama.ts +136 -136
- package/src/engine/renderers/Indicator/keltner.ts +137 -137
- package/src/engine/renderers/Indicator/kst.ts +302 -302
- package/src/engine/renderers/Indicator/ma.ts +200 -200
- package/src/engine/renderers/Indicator/macd.ts +477 -477
- package/src/engine/renderers/Indicator/macdLegend.ts +141 -141
- package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +272 -272
- package/src/engine/renderers/Indicator/mfi.ts +142 -142
- package/src/engine/renderers/Indicator/mom.ts +311 -311
- package/src/engine/renderers/Indicator/obv.ts +123 -123
- package/src/engine/renderers/Indicator/parkinson.ts +124 -124
- package/src/engine/renderers/Indicator/pivot.ts +131 -131
- package/src/engine/renderers/Indicator/pvt.ts +123 -123
- package/src/engine/renderers/Indicator/roc.ts +143 -143
- package/src/engine/renderers/Indicator/rsi.ts +390 -390
- package/src/engine/renderers/Indicator/sar.ts +113 -113
- package/src/engine/renderers/Indicator/scale/atr_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/cci_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/fastk_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/indicator_scale.ts +204 -204
- package/src/engine/renderers/Indicator/scale/kst_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/macd_scale.ts +22 -22
- package/src/engine/renderers/Indicator/scale/mom_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/rsi_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/stoch_scale.ts +19 -19
- package/src/engine/renderers/Indicator/scale/volume_scale.ts +26 -26
- package/src/engine/renderers/Indicator/scale/wmsr_scale.ts +19 -19
- package/src/engine/renderers/Indicator/stoch.ts +359 -359
- package/src/engine/renderers/Indicator/structure.ts +126 -126
- package/src/engine/renderers/Indicator/subPaneConfig.ts +265 -265
- package/src/engine/renderers/Indicator/supertrend.ts +115 -115
- package/src/engine/renderers/Indicator/tema.ts +136 -136
- package/src/engine/renderers/Indicator/trix.ts +158 -158
- package/src/engine/renderers/Indicator/vma.ts +124 -124
- package/src/engine/renderers/Indicator/volumeProfile.ts +125 -125
- package/src/engine/renderers/Indicator/vwap.ts +123 -123
- package/src/engine/renderers/Indicator/wma.ts +136 -136
- package/src/engine/renderers/Indicator/wmsr.ts +328 -328
- package/src/engine/renderers/Indicator/zones.ts +104 -104
- package/src/engine/renderers/__tests__/boll.renderer.test.ts +314 -314
- package/src/engine/renderers/__tests__/ene.renderer.test.ts +305 -305
- package/src/engine/renderers/__tests__/expma.renderer.test.ts +279 -279
- package/src/engine/renderers/__tests__/ma.renderer.test.ts +426 -426
- package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +502 -502
- package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +173 -173
- package/src/engine/renderers/candle.ts +459 -459
- package/src/engine/renderers/crosshair.ts +69 -69
- package/src/engine/renderers/customMarkers.ts +162 -162
- package/src/engine/renderers/extremaMarkers.ts +246 -246
- package/src/engine/renderers/gridLines.ts +90 -90
- package/src/engine/renderers/lastPrice.ts +97 -97
- package/src/engine/renderers/paneTitle.ts +136 -136
- package/src/engine/renderers/subVolume.ts +236 -236
- package/src/engine/renderers/timeAxis.ts +121 -121
- package/src/engine/renderers/webgl/candleSurface.ts +955 -955
- package/src/engine/renderers/webgl/sharedWebGLSurface.ts +146 -146
- package/src/engine/renderers/yAxis.ts +105 -105
- package/src/engine/scale/__tests__/logFormula.spec.ts +148 -148
- package/src/engine/scale/logFormula.ts +130 -130
- package/src/engine/scale/price.ts +39 -39
- package/src/engine/scale/priceScale.ts +264 -264
- package/src/engine/subPaneManager.ts +442 -427
- package/src/engine/theme/colors.ts +642 -642
- package/src/engine/theme/fonts.ts +20 -20
- package/src/engine/utils/klineConfig.ts +49 -49
- package/src/engine/utils/tickCount.ts +11 -11
- package/src/engine/utils/tickPosition.ts +214 -214
- package/src/engine/utils/zoom.ts +83 -83
- package/src/engine/viewport/viewport.ts +67 -67
- package/src/index.ts +3 -3
- package/src/plugin/ConfigManager.ts +93 -93
- package/src/plugin/EventBus.ts +77 -77
- package/src/plugin/HookSystem.ts +106 -106
- package/src/plugin/PluginHost.ts +243 -243
- package/src/plugin/PluginRegistry.ts +92 -92
- package/src/plugin/StateStore.ts +73 -73
- package/src/plugin/index.ts +19 -19
- package/src/plugin/rendererPluginManager.ts +368 -368
- package/src/plugin/stateKeys.ts +8 -8
- package/src/plugin/types.ts +526 -526
- package/src/reactivity/index.ts +2 -2
- package/src/reactivity/signal.ts +119 -119
- package/src/semantic/controller.ts +251 -251
- package/src/semantic/drawShape.ts +260 -260
- package/src/semantic/index.ts +28 -28
- package/src/semantic/schema.json +256 -256
- package/src/semantic/types.ts +251 -251
- package/src/semantic/validator.ts +349 -349
- package/src/types/kLine.ts +13 -13
- package/src/types/price.ts +56 -56
- package/src/types/volumePrice.ts +33 -33
- package/src/utils/dateFormat.ts +208 -208
- package/src/utils/kLineDraw/axis.ts +562 -562
- package/src/utils/priceToY.ts +34 -34
- package/src/utils/volumePrice.ts +202 -202
- package/src/version.ts +1 -1
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
type SparseSeries = readonly (number | undefined)[]
|
|
2
|
-
|
|
3
|
-
export function assertWarmupThenDefined(series: SparseSeries, expectedWarmup: number, label: string): void {
|
|
4
|
-
for (let i = 0; i < expectedWarmup; i++) {
|
|
5
|
-
if (series[i] !== undefined) {
|
|
6
|
-
throw new Error(`${label}: index ${i} should be warm-up (undefined), got ${series[i]}`)
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
for (let i = expectedWarmup; i < series.length; i++) {
|
|
10
|
-
if (series[i] === undefined) {
|
|
11
|
-
throw new Error(`${label}: index ${i} should be defined after warm-up, got undefined`)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function assertNonNegative(series: SparseSeries, label: string): void {
|
|
17
|
-
for (let i = 0; i < series.length; i++) {
|
|
18
|
-
const v = series[i]
|
|
19
|
-
if (v !== undefined && v < 0) {
|
|
20
|
-
throw new Error(`${label}: index ${i} should be ≥ 0, got ${v}`)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function assertBounded(
|
|
26
|
-
series: SparseSeries,
|
|
27
|
-
min: number,
|
|
28
|
-
max: number,
|
|
29
|
-
label: string,
|
|
30
|
-
): void {
|
|
31
|
-
for (let i = 0; i < series.length; i++) {
|
|
32
|
-
const v = series[i]
|
|
33
|
-
if (v === undefined) continue
|
|
34
|
-
if (v < min || v > max) {
|
|
35
|
-
throw new Error(`${label}: index ${i} value ${v} outside [${min}, ${max}]`)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function assertMonotonicByDirection(
|
|
41
|
-
series: SparseSeries,
|
|
42
|
-
directions: readonly number[],
|
|
43
|
-
label: string,
|
|
44
|
-
): void {
|
|
45
|
-
if (series.length !== directions.length) {
|
|
46
|
-
throw new Error(`${label}: series and directions length mismatch (${series.length} vs ${directions.length})`)
|
|
47
|
-
}
|
|
48
|
-
let prev: number | undefined
|
|
49
|
-
for (let i = 0; i < series.length; i++) {
|
|
50
|
-
const v = series[i]
|
|
51
|
-
if (v === undefined) {
|
|
52
|
-
prev = undefined
|
|
53
|
-
continue
|
|
54
|
-
}
|
|
55
|
-
if (prev !== undefined) {
|
|
56
|
-
const expectedDir = directions[i]!
|
|
57
|
-
const actualDir = Math.sign(v - prev)
|
|
58
|
-
if (expectedDir !== 0 && actualDir !== expectedDir && actualDir !== 0) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
`${label}: at index ${i} expected direction ${expectedDir}, value moved from ${prev} to ${v} (dir=${actualDir})`,
|
|
61
|
-
)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
prev = v
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function assertFiniteOrUndefined(series: SparseSeries, label: string): void {
|
|
69
|
-
for (let i = 0; i < series.length; i++) {
|
|
70
|
-
const v = series[i]
|
|
71
|
-
if (v === undefined) continue
|
|
72
|
-
if (!Number.isFinite(v)) {
|
|
73
|
-
throw new Error(`${label}: index ${i} non-finite value ${v}`)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
1
|
+
type SparseSeries = readonly (number | undefined)[]
|
|
2
|
+
|
|
3
|
+
export function assertWarmupThenDefined(series: SparseSeries, expectedWarmup: number, label: string): void {
|
|
4
|
+
for (let i = 0; i < expectedWarmup; i++) {
|
|
5
|
+
if (series[i] !== undefined) {
|
|
6
|
+
throw new Error(`${label}: index ${i} should be warm-up (undefined), got ${series[i]}`)
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
for (let i = expectedWarmup; i < series.length; i++) {
|
|
10
|
+
if (series[i] === undefined) {
|
|
11
|
+
throw new Error(`${label}: index ${i} should be defined after warm-up, got undefined`)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function assertNonNegative(series: SparseSeries, label: string): void {
|
|
17
|
+
for (let i = 0; i < series.length; i++) {
|
|
18
|
+
const v = series[i]
|
|
19
|
+
if (v !== undefined && v < 0) {
|
|
20
|
+
throw new Error(`${label}: index ${i} should be ≥ 0, got ${v}`)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function assertBounded(
|
|
26
|
+
series: SparseSeries,
|
|
27
|
+
min: number,
|
|
28
|
+
max: number,
|
|
29
|
+
label: string,
|
|
30
|
+
): void {
|
|
31
|
+
for (let i = 0; i < series.length; i++) {
|
|
32
|
+
const v = series[i]
|
|
33
|
+
if (v === undefined) continue
|
|
34
|
+
if (v < min || v > max) {
|
|
35
|
+
throw new Error(`${label}: index ${i} value ${v} outside [${min}, ${max}]`)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function assertMonotonicByDirection(
|
|
41
|
+
series: SparseSeries,
|
|
42
|
+
directions: readonly number[],
|
|
43
|
+
label: string,
|
|
44
|
+
): void {
|
|
45
|
+
if (series.length !== directions.length) {
|
|
46
|
+
throw new Error(`${label}: series and directions length mismatch (${series.length} vs ${directions.length})`)
|
|
47
|
+
}
|
|
48
|
+
let prev: number | undefined
|
|
49
|
+
for (let i = 0; i < series.length; i++) {
|
|
50
|
+
const v = series[i]
|
|
51
|
+
if (v === undefined) {
|
|
52
|
+
prev = undefined
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
if (prev !== undefined) {
|
|
56
|
+
const expectedDir = directions[i]!
|
|
57
|
+
const actualDir = Math.sign(v - prev)
|
|
58
|
+
if (expectedDir !== 0 && actualDir !== expectedDir && actualDir !== 0) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`${label}: at index ${i} expected direction ${expectedDir}, value moved from ${prev} to ${v} (dir=${actualDir})`,
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
prev = v
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function assertFiniteOrUndefined(series: SparseSeries, label: string): void {
|
|
69
|
+
for (let i = 0; i < series.length; i++) {
|
|
70
|
+
const v = series[i]
|
|
71
|
+
if (v === undefined) continue
|
|
72
|
+
if (!Number.isFinite(v)) {
|
|
73
|
+
throw new Error(`${label}: index ${i} non-finite value ${v}`)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -1,153 +1,153 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { calcATRData } from '../calculators'
|
|
3
|
-
import {
|
|
4
|
-
empty,
|
|
5
|
-
singleBar,
|
|
6
|
-
shortSequence,
|
|
7
|
-
constantPrice,
|
|
8
|
-
pureUptrend,
|
|
9
|
-
pureDowntrend,
|
|
10
|
-
sideways,
|
|
11
|
-
spikeAtBar19,
|
|
12
|
-
gapUp,
|
|
13
|
-
} from './__fixtures__/synthetic'
|
|
14
|
-
import { ATR_GOLDEN, assertSeriesClose } from './__fixtures__/golden'
|
|
15
|
-
import {
|
|
16
|
-
assertNonNegative,
|
|
17
|
-
assertFiniteOrUndefined,
|
|
18
|
-
} from './_propertyAssertions'
|
|
19
|
-
|
|
20
|
-
describe('calcATRData — Wilder ATR(period)', () => {
|
|
21
|
-
describe('edge cases', () => {
|
|
22
|
-
it('empty input returns empty array', () => {
|
|
23
|
-
expect(calcATRData(empty, 14)).toEqual([])
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('single bar with period 14 returns [undefined]', () => {
|
|
27
|
-
expect(calcATRData(singleBar, 14)).toEqual([undefined])
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('shorter than period returns all undefined', () => {
|
|
31
|
-
const out = calcATRData(shortSequence, 14)
|
|
32
|
-
expect(out).toHaveLength(shortSequence.length)
|
|
33
|
-
for (const v of out) expect(v).toBeUndefined()
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('period = 0 or negative returns all undefined', () => {
|
|
37
|
-
expect(calcATRData(pureUptrend, 0)).toEqual(
|
|
38
|
-
Array.from({ length: pureUptrend.length }, () => undefined),
|
|
39
|
-
)
|
|
40
|
-
expect(calcATRData(pureUptrend, -3)).toEqual(
|
|
41
|
-
Array.from({ length: pureUptrend.length }, () => undefined),
|
|
42
|
-
)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('period = 1 produces TR series (no smoothing of length-1 average)', () => {
|
|
46
|
-
const out = calcATRData(pureUptrend, 1)
|
|
47
|
-
expect(out).toHaveLength(pureUptrend.length)
|
|
48
|
-
for (let i = 0; i < out.length; i++) {
|
|
49
|
-
expect(out[i]).toBeCloseTo(2, 9)
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
describe('golden values vs known fixtures', () => {
|
|
55
|
-
it('constantPrice → ATR(14) is 0 after warm-up', () => {
|
|
56
|
-
const out = calcATRData(constantPrice, 14)
|
|
57
|
-
assertSeriesClose(out, ATR_GOLDEN.constantPrice!.series)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('pureUptrend → ATR(14) is 2 after warm-up', () => {
|
|
61
|
-
const out = calcATRData(pureUptrend, 14)
|
|
62
|
-
assertSeriesClose(out, ATR_GOLDEN.pureUptrend!.series)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('spikeAtBar19 → matches Wilder smoothing exactly', () => {
|
|
66
|
-
const out = calcATRData(spikeAtBar19, 14)
|
|
67
|
-
assertSeriesClose(out, ATR_GOLDEN.spikeAtBar19!.series)
|
|
68
|
-
})
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
describe('mathematical properties (PR 0 baseline)', () => {
|
|
72
|
-
it('ATR ≥ 0 across all fixtures', () => {
|
|
73
|
-
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19, gapUp]) {
|
|
74
|
-
assertNonNegative(calcATRData(fx, 14), 'ATR series')
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('ATR yields only finite or undefined values', () => {
|
|
79
|
-
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19, gapUp]) {
|
|
80
|
-
assertFiniteOrUndefined(calcATRData(fx, 14), 'ATR series')
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('warm-up region is exactly [0, period-1)', () => {
|
|
85
|
-
const period = 14
|
|
86
|
-
const out = calcATRData(pureUptrend, period)
|
|
87
|
-
for (let i = 0; i < period - 1; i++) expect(out[i]).toBeUndefined()
|
|
88
|
-
for (let i = period - 1; i < out.length; i++) expect(out[i]).toBeDefined()
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('symmetry: uptrend and downtrend with identical TR shape yield identical ATR', () => {
|
|
92
|
-
const up = calcATRData(pureUptrend, 14)
|
|
93
|
-
const down = calcATRData(pureDowntrend, 14)
|
|
94
|
-
expect(up.length).toBe(down.length)
|
|
95
|
-
for (let i = 0; i < up.length; i++) {
|
|
96
|
-
if (up[i] === undefined) {
|
|
97
|
-
expect(down[i]).toBeUndefined()
|
|
98
|
-
} else {
|
|
99
|
-
expect(down[i]).toBeCloseTo(up[i]!, 9)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('ATR responds to volatility spike (post-spike > pre-spike)', () => {
|
|
105
|
-
const out = calcATRData(spikeAtBar19, 14)
|
|
106
|
-
const preSpike = out[18]
|
|
107
|
-
const postSpike = out[19]
|
|
108
|
-
expect(preSpike).toBeDefined()
|
|
109
|
-
expect(postSpike).toBeDefined()
|
|
110
|
-
expect(postSpike!).toBeGreaterThan(preSpike!)
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('ATR decays back toward baseline after spike absorbs', () => {
|
|
114
|
-
const out = calcATRData(spikeAtBar19, 14)
|
|
115
|
-
expect(out[20]!).toBeLessThan(out[19]!)
|
|
116
|
-
expect(out[21]!).toBeLessThan(out[20]!)
|
|
117
|
-
expect(out[22]!).toBeLessThan(out[21]!)
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
describe('extensional consistency (live ≡ historical)', () => {
|
|
122
|
-
it('extending data by one bar preserves all earlier ATR values', () => {
|
|
123
|
-
for (let n = 14; n < pureUptrend.length; n++) {
|
|
124
|
-
const prefix = pureUptrend.slice(0, n)
|
|
125
|
-
const extended = pureUptrend.slice(0, n + 1)
|
|
126
|
-
const prefixATR = calcATRData(prefix, 14)
|
|
127
|
-
const extendedATR = calcATRData(extended, 14)
|
|
128
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
129
|
-
if (prefixATR[i] === undefined) {
|
|
130
|
-
expect(extendedATR[i]).toBeUndefined()
|
|
131
|
-
} else {
|
|
132
|
-
expect(extendedATR[i]).toBeCloseTo(prefixATR[i]!, 12)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
it('bar-by-bar feeding matches batch computation on spike fixture', () => {
|
|
139
|
-
const batch = calcATRData(spikeAtBar19, 14)
|
|
140
|
-
for (let n = 1; n <= spikeAtBar19.length; n++) {
|
|
141
|
-
const partial = calcATRData(spikeAtBar19.slice(0, n), 14)
|
|
142
|
-
expect(partial).toHaveLength(n)
|
|
143
|
-
for (let i = 0; i < n; i++) {
|
|
144
|
-
if (batch[i] === undefined) {
|
|
145
|
-
expect(partial[i]).toBeUndefined()
|
|
146
|
-
} else {
|
|
147
|
-
expect(partial[i]).toBeCloseTo(batch[i]!, 12)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
})
|
|
152
|
-
})
|
|
153
|
-
})
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { calcATRData } from '../calculators'
|
|
3
|
+
import {
|
|
4
|
+
empty,
|
|
5
|
+
singleBar,
|
|
6
|
+
shortSequence,
|
|
7
|
+
constantPrice,
|
|
8
|
+
pureUptrend,
|
|
9
|
+
pureDowntrend,
|
|
10
|
+
sideways,
|
|
11
|
+
spikeAtBar19,
|
|
12
|
+
gapUp,
|
|
13
|
+
} from './__fixtures__/synthetic'
|
|
14
|
+
import { ATR_GOLDEN, assertSeriesClose } from './__fixtures__/golden'
|
|
15
|
+
import {
|
|
16
|
+
assertNonNegative,
|
|
17
|
+
assertFiniteOrUndefined,
|
|
18
|
+
} from './_propertyAssertions'
|
|
19
|
+
|
|
20
|
+
describe('calcATRData — Wilder ATR(period)', () => {
|
|
21
|
+
describe('edge cases', () => {
|
|
22
|
+
it('empty input returns empty array', () => {
|
|
23
|
+
expect(calcATRData(empty, 14)).toEqual([])
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('single bar with period 14 returns [undefined]', () => {
|
|
27
|
+
expect(calcATRData(singleBar, 14)).toEqual([undefined])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('shorter than period returns all undefined', () => {
|
|
31
|
+
const out = calcATRData(shortSequence, 14)
|
|
32
|
+
expect(out).toHaveLength(shortSequence.length)
|
|
33
|
+
for (const v of out) expect(v).toBeUndefined()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('period = 0 or negative returns all undefined', () => {
|
|
37
|
+
expect(calcATRData(pureUptrend, 0)).toEqual(
|
|
38
|
+
Array.from({ length: pureUptrend.length }, () => undefined),
|
|
39
|
+
)
|
|
40
|
+
expect(calcATRData(pureUptrend, -3)).toEqual(
|
|
41
|
+
Array.from({ length: pureUptrend.length }, () => undefined),
|
|
42
|
+
)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('period = 1 produces TR series (no smoothing of length-1 average)', () => {
|
|
46
|
+
const out = calcATRData(pureUptrend, 1)
|
|
47
|
+
expect(out).toHaveLength(pureUptrend.length)
|
|
48
|
+
for (let i = 0; i < out.length; i++) {
|
|
49
|
+
expect(out[i]).toBeCloseTo(2, 9)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe('golden values vs known fixtures', () => {
|
|
55
|
+
it('constantPrice → ATR(14) is 0 after warm-up', () => {
|
|
56
|
+
const out = calcATRData(constantPrice, 14)
|
|
57
|
+
assertSeriesClose(out, ATR_GOLDEN.constantPrice!.series)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('pureUptrend → ATR(14) is 2 after warm-up', () => {
|
|
61
|
+
const out = calcATRData(pureUptrend, 14)
|
|
62
|
+
assertSeriesClose(out, ATR_GOLDEN.pureUptrend!.series)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('spikeAtBar19 → matches Wilder smoothing exactly', () => {
|
|
66
|
+
const out = calcATRData(spikeAtBar19, 14)
|
|
67
|
+
assertSeriesClose(out, ATR_GOLDEN.spikeAtBar19!.series)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('mathematical properties (PR 0 baseline)', () => {
|
|
72
|
+
it('ATR ≥ 0 across all fixtures', () => {
|
|
73
|
+
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19, gapUp]) {
|
|
74
|
+
assertNonNegative(calcATRData(fx, 14), 'ATR series')
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('ATR yields only finite or undefined values', () => {
|
|
79
|
+
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19, gapUp]) {
|
|
80
|
+
assertFiniteOrUndefined(calcATRData(fx, 14), 'ATR series')
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('warm-up region is exactly [0, period-1)', () => {
|
|
85
|
+
const period = 14
|
|
86
|
+
const out = calcATRData(pureUptrend, period)
|
|
87
|
+
for (let i = 0; i < period - 1; i++) expect(out[i]).toBeUndefined()
|
|
88
|
+
for (let i = period - 1; i < out.length; i++) expect(out[i]).toBeDefined()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('symmetry: uptrend and downtrend with identical TR shape yield identical ATR', () => {
|
|
92
|
+
const up = calcATRData(pureUptrend, 14)
|
|
93
|
+
const down = calcATRData(pureDowntrend, 14)
|
|
94
|
+
expect(up.length).toBe(down.length)
|
|
95
|
+
for (let i = 0; i < up.length; i++) {
|
|
96
|
+
if (up[i] === undefined) {
|
|
97
|
+
expect(down[i]).toBeUndefined()
|
|
98
|
+
} else {
|
|
99
|
+
expect(down[i]).toBeCloseTo(up[i]!, 9)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('ATR responds to volatility spike (post-spike > pre-spike)', () => {
|
|
105
|
+
const out = calcATRData(spikeAtBar19, 14)
|
|
106
|
+
const preSpike = out[18]
|
|
107
|
+
const postSpike = out[19]
|
|
108
|
+
expect(preSpike).toBeDefined()
|
|
109
|
+
expect(postSpike).toBeDefined()
|
|
110
|
+
expect(postSpike!).toBeGreaterThan(preSpike!)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('ATR decays back toward baseline after spike absorbs', () => {
|
|
114
|
+
const out = calcATRData(spikeAtBar19, 14)
|
|
115
|
+
expect(out[20]!).toBeLessThan(out[19]!)
|
|
116
|
+
expect(out[21]!).toBeLessThan(out[20]!)
|
|
117
|
+
expect(out[22]!).toBeLessThan(out[21]!)
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
describe('extensional consistency (live ≡ historical)', () => {
|
|
122
|
+
it('extending data by one bar preserves all earlier ATR values', () => {
|
|
123
|
+
for (let n = 14; n < pureUptrend.length; n++) {
|
|
124
|
+
const prefix = pureUptrend.slice(0, n)
|
|
125
|
+
const extended = pureUptrend.slice(0, n + 1)
|
|
126
|
+
const prefixATR = calcATRData(prefix, 14)
|
|
127
|
+
const extendedATR = calcATRData(extended, 14)
|
|
128
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
129
|
+
if (prefixATR[i] === undefined) {
|
|
130
|
+
expect(extendedATR[i]).toBeUndefined()
|
|
131
|
+
} else {
|
|
132
|
+
expect(extendedATR[i]).toBeCloseTo(prefixATR[i]!, 12)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('bar-by-bar feeding matches batch computation on spike fixture', () => {
|
|
139
|
+
const batch = calcATRData(spikeAtBar19, 14)
|
|
140
|
+
for (let n = 1; n <= spikeAtBar19.length; n++) {
|
|
141
|
+
const partial = calcATRData(spikeAtBar19.slice(0, n), 14)
|
|
142
|
+
expect(partial).toHaveLength(n)
|
|
143
|
+
for (let i = 0; i < n; i++) {
|
|
144
|
+
if (batch[i] === undefined) {
|
|
145
|
+
expect(partial[i]).toBeUndefined()
|
|
146
|
+
} else {
|
|
147
|
+
expect(partial[i]).toBeCloseTo(batch[i]!, 12)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
})
|