ohlcv-indicators 3.5.47 → 3.5.49
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/index.js +23 -3
- package/package.json +1 -1
- package/src/core-functions/mainLoop.js +2 -0
- package/src/moving-averages/heikenAshi.js +113 -0
package/index.js
CHANGED
|
@@ -355,14 +355,34 @@ export default class OHLCV_INDICATORS {
|
|
|
355
355
|
return this
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
-
|
|
358
|
+
heikenAshi(smoothLength = 10, afterSmoothLength = 10, options = {}) {
|
|
359
|
+
const methodName = 'heikenAshi'
|
|
360
|
+
|
|
361
|
+
isAlreadyComputed(this)
|
|
362
|
+
|
|
363
|
+
validateNumber(smoothLength, {min: 1, max: this.len, allowDecimals: false}, 'smoothLength', methodName)
|
|
364
|
+
validateNumber(afterSmoothLength, {min: 1, max: this.len, allowDecimals: false}, 'afterSmoothLength', methodName)
|
|
365
|
+
validateObject(options, 'options', methodName)
|
|
366
|
+
|
|
367
|
+
const {lag = 0} = options
|
|
368
|
+
|
|
369
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
370
|
+
|
|
371
|
+
const order = 0
|
|
372
|
+
|
|
373
|
+
this.inputParams.push({key: methodName, order, params: [smoothLength, afterSmoothLength, {lag}]})
|
|
374
|
+
|
|
375
|
+
return this
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
vidya(size = 14, momentum = 20, options = {}) {
|
|
359
379
|
|
|
360
380
|
const methodName = 'vidya'
|
|
361
381
|
|
|
362
382
|
isAlreadyComputed(this)
|
|
363
383
|
|
|
364
384
|
validateNumber(size, {min: 2, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
365
|
-
validateNumber(
|
|
385
|
+
validateNumber(momentum, {min: 1, max: this.len, allowDecimals: false}, 'momentum', methodName)
|
|
366
386
|
validateObject(options, 'options', methodName)
|
|
367
387
|
|
|
368
388
|
const {target = 'close', lag = 0, atrLength = 200, bandDistance = 2, liquidityLookback = 20} = options
|
|
@@ -375,7 +395,7 @@ export default class OHLCV_INDICATORS {
|
|
|
375
395
|
|
|
376
396
|
const order = getOrderFromArray([target], methodName)
|
|
377
397
|
|
|
378
|
-
this.inputParams.push({key: methodName, order, params: [size,
|
|
398
|
+
this.inputParams.push({key: methodName, order, params: [size, momentum, {target, lag}]})
|
|
379
399
|
|
|
380
400
|
return this
|
|
381
401
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ohlcv-indicators",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.49",
|
|
4
4
|
"description": "A JavaScript library featuring optimized trading indicators for OHLCV arrays [{open, high, low, close, value}]. This library is optimized for backtesting and trading bot development.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -3,6 +3,7 @@ import { validateInputParams } from "../utilities/validators.js";
|
|
|
3
3
|
import { rsi } from "../oscillators/rsi.js";
|
|
4
4
|
import { stochastic } from "../oscillators/stochastic.js";
|
|
5
5
|
import { movingAverages } from "../moving-averages/movingAverages.js";
|
|
6
|
+
import { heikenAshi } from "../moving-averages/heikenAshi.js";
|
|
6
7
|
import { vidya } from "../moving-averages/vidya.js";
|
|
7
8
|
import { macd } from "../moving-averages/macd.js";
|
|
8
9
|
import { relativeVolume } from "../moving-averages/relativeVolume.js";
|
|
@@ -27,6 +28,7 @@ import { classifier } from "../machine-learning/classifier.js";
|
|
|
27
28
|
// Map indicator keys to their respective functions
|
|
28
29
|
const mainFunctions = {
|
|
29
30
|
dateTime,
|
|
31
|
+
heikenAshi,
|
|
30
32
|
rsi,
|
|
31
33
|
stochastic,
|
|
32
34
|
atr,
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { FasterEMA } from 'trading-signals';
|
|
2
|
+
|
|
3
|
+
export const heikenAshi = (main, index, smoothLength, afterSmoothLength, {lag}) => {
|
|
4
|
+
const { verticalOhlcv, instances, len } = main
|
|
5
|
+
const prefix = `heiken_ashi_${smoothLength}_${afterSmoothLength}`
|
|
6
|
+
const keys = ['open', 'high', 'low', 'close']
|
|
7
|
+
|
|
8
|
+
// ---- INIT ----
|
|
9
|
+
if (index === 0) {
|
|
10
|
+
|
|
11
|
+
// Initialize pre-smoothing EMA instances and buffers
|
|
12
|
+
instances[prefix] = {
|
|
13
|
+
emaPre: Object.fromEntries(keys.map(k => [k, new FasterEMA(smoothLength)])),
|
|
14
|
+
emaPost: Object.fromEntries(keys.map(k => [k, new FasterEMA(afterSmoothLength)])),
|
|
15
|
+
prevHaOpen: NaN,
|
|
16
|
+
prevHaClose: NaN,
|
|
17
|
+
isTrendUp: false,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const keyNames = keys.map(k => `${prefix}_${k}`)
|
|
21
|
+
|
|
22
|
+
const verticalOhlcvSetup = Object.fromEntries([...keyNames, `${prefix}_cross`].map(v => [v, new Float64Array(len).fill(NaN)]))
|
|
23
|
+
|
|
24
|
+
Object.assign(verticalOhlcv, {...verticalOhlcvSetup})
|
|
25
|
+
|
|
26
|
+
if(lag > 0)
|
|
27
|
+
{
|
|
28
|
+
main.lag(keyNames, lag)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ---- FETCH RAW INPUTS ----
|
|
33
|
+
const open = verticalOhlcv.open[index]
|
|
34
|
+
const high = verticalOhlcv.high[index]
|
|
35
|
+
const low = verticalOhlcv.low[index]
|
|
36
|
+
const close = verticalOhlcv.close[index]
|
|
37
|
+
const inst = instances[prefix]
|
|
38
|
+
|
|
39
|
+
// ---- PRE-SMOOTHING (EMA) ----
|
|
40
|
+
inst.emaPre.open.update(open)
|
|
41
|
+
inst.emaPre.high.update(high)
|
|
42
|
+
inst.emaPre.low.update(low)
|
|
43
|
+
inst.emaPre.close.update(close)
|
|
44
|
+
|
|
45
|
+
let sOpen, sHigh, sLow, sClose
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
sOpen = inst.emaPre.open.getResult()
|
|
49
|
+
sHigh = inst.emaPre.high.getResult()
|
|
50
|
+
sLow = inst.emaPre.low.getResult()
|
|
51
|
+
sClose = inst.emaPre.close.getResult()
|
|
52
|
+
} catch {
|
|
53
|
+
return true; // skip if not enough data
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ---- HEIKEN ASHI CORE ----
|
|
57
|
+
const haClose = (sOpen + sHigh + sLow + sClose) / 4
|
|
58
|
+
const haOpen = (Number.isNaN(inst.prevHaOpen) || Number.isNaN(inst.prevHaClose)) ? (sOpen + sClose) / 2 : (inst.prevHaOpen + inst.prevHaClose) / 2
|
|
59
|
+
const haHigh = Math.max(sHigh, haOpen, haClose)
|
|
60
|
+
const haLow = Math.min(sLow, haOpen, haClose)
|
|
61
|
+
|
|
62
|
+
// Store for next round
|
|
63
|
+
inst.prevHaOpen = haOpen
|
|
64
|
+
inst.prevHaClose = haClose
|
|
65
|
+
|
|
66
|
+
// ---- POST-SMOOTHING (EMA) ----
|
|
67
|
+
inst.emaPost.open.update(haOpen)
|
|
68
|
+
inst.emaPost.high.update(haHigh)
|
|
69
|
+
inst.emaPost.low.update(haLow)
|
|
70
|
+
inst.emaPost.close.update(haClose)
|
|
71
|
+
|
|
72
|
+
let smOpen, smHigh, smLow, smClose
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
smOpen = inst.emaPost.open.getResult()
|
|
76
|
+
smHigh = inst.emaPost.high.getResult()
|
|
77
|
+
smLow = inst.emaPost.low.getResult()
|
|
78
|
+
smClose = inst.emaPost.close.getResult()
|
|
79
|
+
} catch {
|
|
80
|
+
return true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Cumulative cross counter: positive when uptrend, negative when downtrend
|
|
84
|
+
const prevCross = index > 0 ? verticalOhlcv[`${prefix}_cross`][index - 1] : 0
|
|
85
|
+
const prevHaOpen = index > 0 ? verticalOhlcv[`${prefix}_open`][index - 1] : NaN
|
|
86
|
+
const prevHaClose = index > 0 ? verticalOhlcv[`${prefix}_close`][index - 1] : NaN
|
|
87
|
+
const crossUp = !Number.isNaN(prevHaClose) && prevHaClose <= prevHaOpen && smClose > smOpen
|
|
88
|
+
const crossDown = !Number.isNaN(prevHaClose) && prevHaClose >= prevHaOpen && smClose < smOpen
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if (crossUp) inst.isTrendUp = true;
|
|
92
|
+
if (crossDown) inst.isTrendUp = false;
|
|
93
|
+
|
|
94
|
+
let cross = 0
|
|
95
|
+
|
|
96
|
+
if (index > 0) {
|
|
97
|
+
if (inst.isTrendUp) {
|
|
98
|
+
cross = prevCross > 0 ? prevCross + 1 : 1
|
|
99
|
+
} else {
|
|
100
|
+
cross = prevCross < 0 ? prevCross - 1 : -1
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---- PUSH OUTPUTS ----
|
|
105
|
+
main.pushToMain({ index, key: `${prefix}_open`, value: smOpen })
|
|
106
|
+
main.pushToMain({ index, key: `${prefix}_high`, value: smHigh })
|
|
107
|
+
main.pushToMain({ index, key: `${prefix}_low`, value: smLow })
|
|
108
|
+
main.pushToMain({ index, key: `${prefix}_close`, value: smClose })
|
|
109
|
+
main.pushToMain({ index, key: `${prefix}_cross`, value: cross })
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
return true
|
|
113
|
+
}
|