@363045841yyt/klinechart-core 0.7.3 → 0.7.5-alpha.2
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/engine/renderers/webgl/candleSurface.js +47 -47
- 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 +19 -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 +2815 -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 +427 -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,100 +1,100 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { calcCMFData, calcMFIData } from '../calculators'
|
|
3
|
-
import {
|
|
4
|
-
empty,
|
|
5
|
-
constantPrice,
|
|
6
|
-
pureUptrend,
|
|
7
|
-
pureDowntrend,
|
|
8
|
-
sideways,
|
|
9
|
-
spikeAtBar19,
|
|
10
|
-
} from './__fixtures__/synthetic'
|
|
11
|
-
|
|
12
|
-
describe('calcCMFData', () => {
|
|
13
|
-
it('empty returns empty', () => {
|
|
14
|
-
expect(calcCMFData(empty, 20)).toEqual([])
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('period <= 0 returns all undefined', () => {
|
|
18
|
-
const out = calcCMFData(pureUptrend, 0)
|
|
19
|
-
for (const v of out) expect(v).toBeUndefined()
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('CMF ∈ [-1, 1] for all defined values', () => {
|
|
23
|
-
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19]) {
|
|
24
|
-
for (const v of calcCMFData(fx, 10)) {
|
|
25
|
-
if (v !== undefined) {
|
|
26
|
-
expect(v).toBeGreaterThanOrEqual(-1)
|
|
27
|
-
expect(v).toBeLessThanOrEqual(1)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('on pureUptrend (close=high+1-1=center) CMF stays near 0', () => {
|
|
34
|
-
// close = 100+i, high = 101+i, low = 99+i → close midway → MFM ≈ 0
|
|
35
|
-
const out = calcCMFData(pureUptrend, 10)
|
|
36
|
-
for (let t = 9; t < out.length; t++) {
|
|
37
|
-
expect(out[t]).toBeCloseTo(0, 9)
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('extensional consistency', () => {
|
|
42
|
-
const full = calcCMFData(pureUptrend, 10)
|
|
43
|
-
for (let n = 11; n < pureUptrend.length; n++) {
|
|
44
|
-
const partial = calcCMFData(pureUptrend.slice(0, n), 10)
|
|
45
|
-
for (let i = 0; i < n; i++) {
|
|
46
|
-
if (full[i] !== undefined && partial[i] !== undefined) {
|
|
47
|
-
expect(partial[i]).toBeCloseTo(full[i]!, 9)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
describe('calcMFIData', () => {
|
|
55
|
-
it('empty returns empty', () => {
|
|
56
|
-
expect(calcMFIData(empty, 14)).toEqual([])
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('shorter than period+1 returns all undefined', () => {
|
|
60
|
-
const out = calcMFIData(pureUptrend.slice(0, 10), 14)
|
|
61
|
-
for (const v of out) expect(v).toBeUndefined()
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('MFI ∈ [0, 100] for all defined values', () => {
|
|
65
|
-
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19]) {
|
|
66
|
-
for (const v of calcMFIData(fx, 7)) {
|
|
67
|
-
if (v !== undefined) {
|
|
68
|
-
expect(v).toBeGreaterThanOrEqual(0)
|
|
69
|
-
expect(v).toBeLessThanOrEqual(100)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('on pureUptrend MFI = 100 (all positive money flow, no negative)', () => {
|
|
76
|
-
const out = calcMFIData(pureUptrend, 14)
|
|
77
|
-
for (let t = 14; t < out.length; t++) {
|
|
78
|
-
expect(out[t]).toBe(100)
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('on pureDowntrend MFI = 0 (all negative money flow)', () => {
|
|
83
|
-
const out = calcMFIData(pureDowntrend, 14)
|
|
84
|
-
for (let t = 14; t < out.length; t++) {
|
|
85
|
-
expect(out[t]).toBe(0)
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('extensional consistency', () => {
|
|
90
|
-
const full = calcMFIData(sideways, 7)
|
|
91
|
-
for (let n = 10; n < sideways.length; n++) {
|
|
92
|
-
const partial = calcMFIData(sideways.slice(0, n), 7)
|
|
93
|
-
for (let i = 0; i < n; i++) {
|
|
94
|
-
if (full[i] !== undefined && partial[i] !== undefined) {
|
|
95
|
-
expect(partial[i]).toBeCloseTo(full[i]!, 9)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
})
|
|
100
|
-
})
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { calcCMFData, calcMFIData } from '../calculators'
|
|
3
|
+
import {
|
|
4
|
+
empty,
|
|
5
|
+
constantPrice,
|
|
6
|
+
pureUptrend,
|
|
7
|
+
pureDowntrend,
|
|
8
|
+
sideways,
|
|
9
|
+
spikeAtBar19,
|
|
10
|
+
} from './__fixtures__/synthetic'
|
|
11
|
+
|
|
12
|
+
describe('calcCMFData', () => {
|
|
13
|
+
it('empty returns empty', () => {
|
|
14
|
+
expect(calcCMFData(empty, 20)).toEqual([])
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('period <= 0 returns all undefined', () => {
|
|
18
|
+
const out = calcCMFData(pureUptrend, 0)
|
|
19
|
+
for (const v of out) expect(v).toBeUndefined()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('CMF ∈ [-1, 1] for all defined values', () => {
|
|
23
|
+
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19]) {
|
|
24
|
+
for (const v of calcCMFData(fx, 10)) {
|
|
25
|
+
if (v !== undefined) {
|
|
26
|
+
expect(v).toBeGreaterThanOrEqual(-1)
|
|
27
|
+
expect(v).toBeLessThanOrEqual(1)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('on pureUptrend (close=high+1-1=center) CMF stays near 0', () => {
|
|
34
|
+
// close = 100+i, high = 101+i, low = 99+i → close midway → MFM ≈ 0
|
|
35
|
+
const out = calcCMFData(pureUptrend, 10)
|
|
36
|
+
for (let t = 9; t < out.length; t++) {
|
|
37
|
+
expect(out[t]).toBeCloseTo(0, 9)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('extensional consistency', () => {
|
|
42
|
+
const full = calcCMFData(pureUptrend, 10)
|
|
43
|
+
for (let n = 11; n < pureUptrend.length; n++) {
|
|
44
|
+
const partial = calcCMFData(pureUptrend.slice(0, n), 10)
|
|
45
|
+
for (let i = 0; i < n; i++) {
|
|
46
|
+
if (full[i] !== undefined && partial[i] !== undefined) {
|
|
47
|
+
expect(partial[i]).toBeCloseTo(full[i]!, 9)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe('calcMFIData', () => {
|
|
55
|
+
it('empty returns empty', () => {
|
|
56
|
+
expect(calcMFIData(empty, 14)).toEqual([])
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('shorter than period+1 returns all undefined', () => {
|
|
60
|
+
const out = calcMFIData(pureUptrend.slice(0, 10), 14)
|
|
61
|
+
for (const v of out) expect(v).toBeUndefined()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('MFI ∈ [0, 100] for all defined values', () => {
|
|
65
|
+
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19]) {
|
|
66
|
+
for (const v of calcMFIData(fx, 7)) {
|
|
67
|
+
if (v !== undefined) {
|
|
68
|
+
expect(v).toBeGreaterThanOrEqual(0)
|
|
69
|
+
expect(v).toBeLessThanOrEqual(100)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('on pureUptrend MFI = 100 (all positive money flow, no negative)', () => {
|
|
76
|
+
const out = calcMFIData(pureUptrend, 14)
|
|
77
|
+
for (let t = 14; t < out.length; t++) {
|
|
78
|
+
expect(out[t]).toBe(100)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('on pureDowntrend MFI = 0 (all negative money flow)', () => {
|
|
83
|
+
const out = calcMFIData(pureDowntrend, 14)
|
|
84
|
+
for (let t = 14; t < out.length; t++) {
|
|
85
|
+
expect(out[t]).toBe(0)
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('extensional consistency', () => {
|
|
90
|
+
const full = calcMFIData(sideways, 7)
|
|
91
|
+
for (let n = 10; n < sideways.length; n++) {
|
|
92
|
+
const partial = calcMFIData(sideways.slice(0, n), 7)
|
|
93
|
+
for (let i = 0; i < n; i++) {
|
|
94
|
+
if (full[i] !== undefined && partial[i] !== undefined) {
|
|
95
|
+
expect(partial[i]).toBeCloseTo(full[i]!, 9)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
})
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { calcDEMAData } from '../calculators'
|
|
3
|
-
import {
|
|
4
|
-
empty,
|
|
5
|
-
singleBar,
|
|
6
|
-
constantPrice,
|
|
7
|
-
pureUptrend,
|
|
8
|
-
pureDowntrend,
|
|
9
|
-
sideways,
|
|
10
|
-
spikeAtBar19,
|
|
11
|
-
} from './__fixtures__/synthetic'
|
|
12
|
-
import { DEMA_GOLDEN, assertSeriesClose } from './__fixtures__/golden'
|
|
13
|
-
import {
|
|
14
|
-
assertFiniteOrUndefined,
|
|
15
|
-
} from './_propertyAssertions'
|
|
16
|
-
|
|
17
|
-
describe('calcDEMAData — Double Exponential Moving Average', () => {
|
|
18
|
-
describe('edge cases', () => {
|
|
19
|
-
it('empty returns empty array', () => {
|
|
20
|
-
expect(calcDEMAData(empty, 20)).toEqual([])
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('single bar produces a single defined value (EMA seed = first close)', () => {
|
|
24
|
-
const out = calcDEMAData(singleBar, 20)
|
|
25
|
-
expect(out).toHaveLength(1)
|
|
26
|
-
expect(out[0]).toBeCloseTo(100, 9)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('period = 0 or negative returns all undefined', () => {
|
|
30
|
-
const zero = calcDEMAData(pureUptrend, 0)
|
|
31
|
-
for (const v of zero) expect(v).toBeUndefined()
|
|
32
|
-
const neg = calcDEMAData(pureUptrend, -1)
|
|
33
|
-
for (const v of neg) expect(v).toBeUndefined()
|
|
34
|
-
})
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
describe('golden values', () => {
|
|
38
|
-
it('constantPrice → DEMA(20) = 100 throughout (zero lag at steady state)', () => {
|
|
39
|
-
assertSeriesClose(calcDEMAData(constantPrice, 20), DEMA_GOLDEN.constantPrice!.series)
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
describe('mathematical properties', () => {
|
|
44
|
-
it('all values finite', () => {
|
|
45
|
-
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19]) {
|
|
46
|
-
assertFiniteOrUndefined(calcDEMAData(fx, 20), 'DEMA series')
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('on linear input close=100+t, DEMA converges toward close[t] (zero-lag property)', () => {
|
|
51
|
-
// 30-bar fixture isn't enough for full convergence with period=20,
|
|
52
|
-
// but DEMA should be closer to close[t] than EMA would be.
|
|
53
|
-
// Just verify monotonic increase tracking the uptrend.
|
|
54
|
-
const out = calcDEMAData(pureUptrend, 20)
|
|
55
|
-
const tail = out.slice(20).filter((v): v is number => v !== undefined)
|
|
56
|
-
for (let i = 1; i < tail.length; i++) {
|
|
57
|
-
expect(tail[i]!).toBeGreaterThan(tail[i - 1]!)
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('extensional consistency (no leak from later bars into earlier values)', () => {
|
|
62
|
-
const full = calcDEMAData(pureUptrend, 20)
|
|
63
|
-
for (let n = 5; n < pureUptrend.length; n++) {
|
|
64
|
-
const partial = calcDEMAData(pureUptrend.slice(0, n), 20)
|
|
65
|
-
for (let i = 0; i < n; i++) {
|
|
66
|
-
if (full[i] !== undefined && partial[i] !== undefined) {
|
|
67
|
-
expect(partial[i]).toBeCloseTo(full[i]!, 9)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
})
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { calcDEMAData } from '../calculators'
|
|
3
|
+
import {
|
|
4
|
+
empty,
|
|
5
|
+
singleBar,
|
|
6
|
+
constantPrice,
|
|
7
|
+
pureUptrend,
|
|
8
|
+
pureDowntrend,
|
|
9
|
+
sideways,
|
|
10
|
+
spikeAtBar19,
|
|
11
|
+
} from './__fixtures__/synthetic'
|
|
12
|
+
import { DEMA_GOLDEN, assertSeriesClose } from './__fixtures__/golden'
|
|
13
|
+
import {
|
|
14
|
+
assertFiniteOrUndefined,
|
|
15
|
+
} from './_propertyAssertions'
|
|
16
|
+
|
|
17
|
+
describe('calcDEMAData — Double Exponential Moving Average', () => {
|
|
18
|
+
describe('edge cases', () => {
|
|
19
|
+
it('empty returns empty array', () => {
|
|
20
|
+
expect(calcDEMAData(empty, 20)).toEqual([])
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('single bar produces a single defined value (EMA seed = first close)', () => {
|
|
24
|
+
const out = calcDEMAData(singleBar, 20)
|
|
25
|
+
expect(out).toHaveLength(1)
|
|
26
|
+
expect(out[0]).toBeCloseTo(100, 9)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('period = 0 or negative returns all undefined', () => {
|
|
30
|
+
const zero = calcDEMAData(pureUptrend, 0)
|
|
31
|
+
for (const v of zero) expect(v).toBeUndefined()
|
|
32
|
+
const neg = calcDEMAData(pureUptrend, -1)
|
|
33
|
+
for (const v of neg) expect(v).toBeUndefined()
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('golden values', () => {
|
|
38
|
+
it('constantPrice → DEMA(20) = 100 throughout (zero lag at steady state)', () => {
|
|
39
|
+
assertSeriesClose(calcDEMAData(constantPrice, 20), DEMA_GOLDEN.constantPrice!.series)
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('mathematical properties', () => {
|
|
44
|
+
it('all values finite', () => {
|
|
45
|
+
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19]) {
|
|
46
|
+
assertFiniteOrUndefined(calcDEMAData(fx, 20), 'DEMA series')
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('on linear input close=100+t, DEMA converges toward close[t] (zero-lag property)', () => {
|
|
51
|
+
// 30-bar fixture isn't enough for full convergence with period=20,
|
|
52
|
+
// but DEMA should be closer to close[t] than EMA would be.
|
|
53
|
+
// Just verify monotonic increase tracking the uptrend.
|
|
54
|
+
const out = calcDEMAData(pureUptrend, 20)
|
|
55
|
+
const tail = out.slice(20).filter((v): v is number => v !== undefined)
|
|
56
|
+
for (let i = 1; i < tail.length; i++) {
|
|
57
|
+
expect(tail[i]!).toBeGreaterThan(tail[i - 1]!)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('extensional consistency (no leak from later bars into earlier values)', () => {
|
|
62
|
+
const full = calcDEMAData(pureUptrend, 20)
|
|
63
|
+
for (let n = 5; n < pureUptrend.length; n++) {
|
|
64
|
+
const partial = calcDEMAData(pureUptrend.slice(0, n), 20)
|
|
65
|
+
for (let i = 0; i < n; i++) {
|
|
66
|
+
if (full[i] !== undefined && partial[i] !== undefined) {
|
|
67
|
+
expect(partial[i]).toBeCloseTo(full[i]!, 9)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { calcDonchianData } from '../calculators'
|
|
3
|
-
import {
|
|
4
|
-
empty,
|
|
5
|
-
constantPrice,
|
|
6
|
-
pureUptrend,
|
|
7
|
-
sideways,
|
|
8
|
-
spikeAtBar19,
|
|
9
|
-
} from './__fixtures__/synthetic'
|
|
10
|
-
|
|
11
|
-
describe('calcDonchianData', () => {
|
|
12
|
-
it('empty returns empty', () => {
|
|
13
|
-
expect(calcDonchianData(empty, 20)).toEqual([])
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('shorter than period returns all undefined', () => {
|
|
17
|
-
const out = calcDonchianData(constantPrice.slice(0, 5), 20)
|
|
18
|
-
for (const v of out) expect(v).toBeUndefined()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('period <= 0 returns all undefined', () => {
|
|
22
|
-
const out = calcDonchianData(pureUptrend, 0)
|
|
23
|
-
for (const v of out) expect(v).toBeUndefined()
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('on constantPrice (H=L=100) all bands collapse to 100', () => {
|
|
27
|
-
const out = calcDonchianData(constantPrice, 20)
|
|
28
|
-
const valid = out.filter((p) => p !== undefined)
|
|
29
|
-
expect(valid.length).toBeGreaterThan(0)
|
|
30
|
-
for (const p of valid) {
|
|
31
|
-
expect(p!.upper).toBe(100)
|
|
32
|
-
expect(p!.middle).toBe(100)
|
|
33
|
-
expect(p!.lower).toBe(100)
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('upper >= middle >= lower invariant', () => {
|
|
38
|
-
for (const fx of [pureUptrend, sideways, spikeAtBar19]) {
|
|
39
|
-
const out = calcDonchianData(fx, 20)
|
|
40
|
-
for (const p of out) {
|
|
41
|
-
if (!p) continue
|
|
42
|
-
expect(p.upper).toBeGreaterThanOrEqual(p.middle)
|
|
43
|
-
expect(p.middle).toBeGreaterThanOrEqual(p.lower)
|
|
44
|
-
expect(p.middle).toBeCloseTo((p.upper + p.lower) / 2, 9)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('on pureUptrend, upper at index t equals high[t] (newest is always the maximum)', () => {
|
|
50
|
-
const out = calcDonchianData(pureUptrend, 20)
|
|
51
|
-
for (let t = 19; t < out.length; t++) {
|
|
52
|
-
expect(out[t]!.upper).toBe(pureUptrend[t]!.high)
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('extensional consistency', () => {
|
|
57
|
-
const full = calcDonchianData(pureUptrend, 20)
|
|
58
|
-
for (let n = 20; n < pureUptrend.length; n++) {
|
|
59
|
-
const partial = calcDonchianData(pureUptrend.slice(0, n), 20)
|
|
60
|
-
for (let i = 0; i < n; i++) {
|
|
61
|
-
const f = full[i]
|
|
62
|
-
const p = partial[i]
|
|
63
|
-
if (f && p) {
|
|
64
|
-
expect(p.upper).toBe(f.upper)
|
|
65
|
-
expect(p.lower).toBe(f.lower)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
})
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { calcDonchianData } from '../calculators'
|
|
3
|
+
import {
|
|
4
|
+
empty,
|
|
5
|
+
constantPrice,
|
|
6
|
+
pureUptrend,
|
|
7
|
+
sideways,
|
|
8
|
+
spikeAtBar19,
|
|
9
|
+
} from './__fixtures__/synthetic'
|
|
10
|
+
|
|
11
|
+
describe('calcDonchianData', () => {
|
|
12
|
+
it('empty returns empty', () => {
|
|
13
|
+
expect(calcDonchianData(empty, 20)).toEqual([])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('shorter than period returns all undefined', () => {
|
|
17
|
+
const out = calcDonchianData(constantPrice.slice(0, 5), 20)
|
|
18
|
+
for (const v of out) expect(v).toBeUndefined()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('period <= 0 returns all undefined', () => {
|
|
22
|
+
const out = calcDonchianData(pureUptrend, 0)
|
|
23
|
+
for (const v of out) expect(v).toBeUndefined()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('on constantPrice (H=L=100) all bands collapse to 100', () => {
|
|
27
|
+
const out = calcDonchianData(constantPrice, 20)
|
|
28
|
+
const valid = out.filter((p) => p !== undefined)
|
|
29
|
+
expect(valid.length).toBeGreaterThan(0)
|
|
30
|
+
for (const p of valid) {
|
|
31
|
+
expect(p!.upper).toBe(100)
|
|
32
|
+
expect(p!.middle).toBe(100)
|
|
33
|
+
expect(p!.lower).toBe(100)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('upper >= middle >= lower invariant', () => {
|
|
38
|
+
for (const fx of [pureUptrend, sideways, spikeAtBar19]) {
|
|
39
|
+
const out = calcDonchianData(fx, 20)
|
|
40
|
+
for (const p of out) {
|
|
41
|
+
if (!p) continue
|
|
42
|
+
expect(p.upper).toBeGreaterThanOrEqual(p.middle)
|
|
43
|
+
expect(p.middle).toBeGreaterThanOrEqual(p.lower)
|
|
44
|
+
expect(p.middle).toBeCloseTo((p.upper + p.lower) / 2, 9)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('on pureUptrend, upper at index t equals high[t] (newest is always the maximum)', () => {
|
|
50
|
+
const out = calcDonchianData(pureUptrend, 20)
|
|
51
|
+
for (let t = 19; t < out.length; t++) {
|
|
52
|
+
expect(out[t]!.upper).toBe(pureUptrend[t]!.high)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('extensional consistency', () => {
|
|
57
|
+
const full = calcDonchianData(pureUptrend, 20)
|
|
58
|
+
for (let n = 20; n < pureUptrend.length; n++) {
|
|
59
|
+
const partial = calcDonchianData(pureUptrend.slice(0, n), 20)
|
|
60
|
+
for (let i = 0; i < n; i++) {
|
|
61
|
+
const f = full[i]
|
|
62
|
+
const p = partial[i]
|
|
63
|
+
if (f && p) {
|
|
64
|
+
expect(p.upper).toBe(f.upper)
|
|
65
|
+
expect(p.lower).toBe(f.lower)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
})
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { calcHMAData } from '../calculators'
|
|
3
|
-
import {
|
|
4
|
-
empty,
|
|
5
|
-
singleBar,
|
|
6
|
-
constantPrice,
|
|
7
|
-
pureUptrend,
|
|
8
|
-
pureDowntrend,
|
|
9
|
-
sideways,
|
|
10
|
-
spikeAtBar19,
|
|
11
|
-
} from './__fixtures__/synthetic'
|
|
12
|
-
import { HMA_GOLDEN, assertSeriesClose } from './__fixtures__/golden'
|
|
13
|
-
import {
|
|
14
|
-
assertFiniteOrUndefined,
|
|
15
|
-
} from './_propertyAssertions'
|
|
16
|
-
|
|
17
|
-
describe('calcHMAData — Hull Moving Average', () => {
|
|
18
|
-
describe('edge cases', () => {
|
|
19
|
-
it('empty returns empty array', () => {
|
|
20
|
-
expect(calcHMAData(empty, 9)).toEqual([])
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('single bar with period 9 returns [undefined]', () => {
|
|
24
|
-
expect(calcHMAData(singleBar, 9)).toEqual([undefined])
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('period = 0 returns all undefined', () => {
|
|
28
|
-
const zero = calcHMAData(pureUptrend, 0)
|
|
29
|
-
for (const v of zero) expect(v).toBeUndefined()
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('period = 1 returns close[t] (WMA of WMA of single value is itself)', () => {
|
|
33
|
-
const out = calcHMAData(pureUptrend, 1)
|
|
34
|
-
for (let i = 0; i < out.length; i++) {
|
|
35
|
-
expect(out[i]).toBeCloseTo(pureUptrend[i]!.close, 9)
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
describe('golden values', () => {
|
|
41
|
-
it('constantPrice → HMA(9) = 100 after warm-up', () => {
|
|
42
|
-
assertSeriesClose(calcHMAData(constantPrice, 9), HMA_GOLDEN.constantPrice!.series)
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
describe('mathematical properties', () => {
|
|
47
|
-
it('all values finite (or undefined in warm-up)', () => {
|
|
48
|
-
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19]) {
|
|
49
|
-
assertFiniteOrUndefined(calcHMAData(fx, 9), 'HMA series')
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('HMA(9) on pureUptrend tracks linear motion with very low lag', () => {
|
|
54
|
-
const out = calcHMAData(pureUptrend, 9)
|
|
55
|
-
const tail = out.slice(11).filter((v): v is number => v !== undefined)
|
|
56
|
-
for (let i = 1; i < tail.length; i++) {
|
|
57
|
-
expect(tail[i]!).toBeGreaterThan(tail[i - 1]!)
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('extensional consistency', () => {
|
|
62
|
-
const full = calcHMAData(pureUptrend, 9)
|
|
63
|
-
for (let n = 12; n < pureUptrend.length; n++) {
|
|
64
|
-
const partial = calcHMAData(pureUptrend.slice(0, n), 9)
|
|
65
|
-
for (let i = 0; i < n; i++) {
|
|
66
|
-
if (full[i] !== undefined && partial[i] !== undefined) {
|
|
67
|
-
expect(partial[i]).toBeCloseTo(full[i]!, 9)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
})
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { calcHMAData } from '../calculators'
|
|
3
|
+
import {
|
|
4
|
+
empty,
|
|
5
|
+
singleBar,
|
|
6
|
+
constantPrice,
|
|
7
|
+
pureUptrend,
|
|
8
|
+
pureDowntrend,
|
|
9
|
+
sideways,
|
|
10
|
+
spikeAtBar19,
|
|
11
|
+
} from './__fixtures__/synthetic'
|
|
12
|
+
import { HMA_GOLDEN, assertSeriesClose } from './__fixtures__/golden'
|
|
13
|
+
import {
|
|
14
|
+
assertFiniteOrUndefined,
|
|
15
|
+
} from './_propertyAssertions'
|
|
16
|
+
|
|
17
|
+
describe('calcHMAData — Hull Moving Average', () => {
|
|
18
|
+
describe('edge cases', () => {
|
|
19
|
+
it('empty returns empty array', () => {
|
|
20
|
+
expect(calcHMAData(empty, 9)).toEqual([])
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('single bar with period 9 returns [undefined]', () => {
|
|
24
|
+
expect(calcHMAData(singleBar, 9)).toEqual([undefined])
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('period = 0 returns all undefined', () => {
|
|
28
|
+
const zero = calcHMAData(pureUptrend, 0)
|
|
29
|
+
for (const v of zero) expect(v).toBeUndefined()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('period = 1 returns close[t] (WMA of WMA of single value is itself)', () => {
|
|
33
|
+
const out = calcHMAData(pureUptrend, 1)
|
|
34
|
+
for (let i = 0; i < out.length; i++) {
|
|
35
|
+
expect(out[i]).toBeCloseTo(pureUptrend[i]!.close, 9)
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('golden values', () => {
|
|
41
|
+
it('constantPrice → HMA(9) = 100 after warm-up', () => {
|
|
42
|
+
assertSeriesClose(calcHMAData(constantPrice, 9), HMA_GOLDEN.constantPrice!.series)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe('mathematical properties', () => {
|
|
47
|
+
it('all values finite (or undefined in warm-up)', () => {
|
|
48
|
+
for (const fx of [pureUptrend, pureDowntrend, sideways, spikeAtBar19]) {
|
|
49
|
+
assertFiniteOrUndefined(calcHMAData(fx, 9), 'HMA series')
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('HMA(9) on pureUptrend tracks linear motion with very low lag', () => {
|
|
54
|
+
const out = calcHMAData(pureUptrend, 9)
|
|
55
|
+
const tail = out.slice(11).filter((v): v is number => v !== undefined)
|
|
56
|
+
for (let i = 1; i < tail.length; i++) {
|
|
57
|
+
expect(tail[i]!).toBeGreaterThan(tail[i - 1]!)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('extensional consistency', () => {
|
|
62
|
+
const full = calcHMAData(pureUptrend, 9)
|
|
63
|
+
for (let n = 12; n < pureUptrend.length; n++) {
|
|
64
|
+
const partial = calcHMAData(pureUptrend.slice(0, n), 9)
|
|
65
|
+
for (let i = 0; i < n; i++) {
|
|
66
|
+
if (full[i] !== undefined && partial[i] !== undefined) {
|
|
67
|
+
expect(partial[i]).toBeCloseTo(full[i]!, 9)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
})
|