ohlcv-indicators 3.5.78 → 3.5.80
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/AGENTS.md +113 -0
- package/README.md +456 -0
- package/dist/ohlcv-indicators.min.js +2 -1
- package/dist/ohlcv-indicators.min.js.LICENSE.txt +22 -0
- package/index.js +112 -84
- package/package.json +2 -5
- package/src/core-functions/initializeColumns.js +40 -0
- package/src/core-functions/mainLoop.js +35 -30
- package/src/core-functions/pushToMain.js +8 -27
- package/src/core-indicators/README.md +60 -0
- package/src/core-indicators/composites.js +136 -0
- package/src/core-indicators/index.js +5 -0
- package/src/core-indicators/indicator.js +81 -0
- package/src/core-indicators/movingAverages.js +88 -0
- package/src/core-indicators/rsi.js +41 -0
- package/src/core-indicators/volatility.js +107 -0
- package/src/moving-averages/bollingerBands.js +39 -31
- package/src/moving-averages/donchianChannel.js +62 -36
- package/src/moving-averages/heikenAshi.js +33 -52
- package/src/moving-averages/macd.js +10 -26
- package/src/moving-averages/movingAverages.js +19 -25
- package/src/moving-averages/relativeVolume.js +30 -24
- package/src/oscillators/mfi.js +56 -0
- package/src/oscillators/rsi.js +7 -26
- package/src/oscillators/stochastic.js +6 -19
- package/src/oscillators/volumeDelta.js +36 -28
- package/src/oscillators/volumeOscillator.js +24 -32
- package/src/studies/candleFeatures.js +40 -28
- package/src/studies/dateTime.js +54 -56
- package/src/studies/findCrosses.js +18 -16
- package/src/studies/lag.js +32 -20
- package/src/utilities/assignTypes.js +1 -1
- package/src/utilities/config.js +31 -0
- package/src/utilities/numberUtilities.js +5 -3
- package/src/utilities/precisionMultiplier.js +11 -6
- package/src/utilities/validators.js +40 -1
- package/src/utilities/verticalToHorizontal.js +26 -16
- package/src/volatility/adx.js +7 -20
- package/src/volatility/atr.js +10 -23
- package/test/test.js +1230 -0
- package/webpack.config.js +0 -11
- package/src/moving-averages/vidya.js +0 -181
- package/src/studies/mapCols.js +0 -46
package/webpack.config.js
CHANGED
|
@@ -41,17 +41,6 @@ export default {
|
|
|
41
41
|
plugins: ['@babel/plugin-proposal-class-properties']
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
test: /\.js$/,
|
|
47
|
-
include: /node_modules\/trading-signals/,
|
|
48
|
-
use: {
|
|
49
|
-
loader: 'babel-loader',
|
|
50
|
-
options: {
|
|
51
|
-
presets: ['@babel/preset-env'],
|
|
52
|
-
plugins: ['@babel/plugin-proposal-class-properties']
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
44
|
}
|
|
56
45
|
]
|
|
57
46
|
}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { FasterSMA } from 'trading-signals';
|
|
2
|
-
// Variable Index Dynamic Average (VIDYA) indicator with volume stats
|
|
3
|
-
|
|
4
|
-
export const vidya = (main, index, size, momentum, options = {}) => {
|
|
5
|
-
const { verticalOhlcv, instances, len, priceBased } = main;
|
|
6
|
-
const { target, lag, atrLength = 200, bandDistance = 2, liquidityLookback = 5 } = options
|
|
7
|
-
const indicatorKey = `${size}_${momentum}`;
|
|
8
|
-
|
|
9
|
-
// Initialization on the first bar
|
|
10
|
-
if (index === 0) {
|
|
11
|
-
if (!(target in verticalOhlcv)) {
|
|
12
|
-
throw new Error(`VIDYA could not find target "${target}"`)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const prefix = `vidya_${indicatorKey}`
|
|
16
|
-
const keyBase = target === 'close' ? prefix : `${prefix}_${target}`
|
|
17
|
-
const keys = {
|
|
18
|
-
vidya: keyBase,
|
|
19
|
-
upper: `${prefix}_upper`,
|
|
20
|
-
lower: `${prefix}_lower`,
|
|
21
|
-
delta_vol: `${prefix}_delta_vol`,
|
|
22
|
-
cross: `${prefix}_cross`,
|
|
23
|
-
liquidity: `${prefix}_liquidity`,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const priceBasedVidyaKeys = [keyBase, keys.upper, keys.lower, keys.liquidity]
|
|
27
|
-
|
|
28
|
-
for(const k of priceBasedVidyaKeys) {
|
|
29
|
-
priceBased.add(k)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Prepare instances container
|
|
33
|
-
if (!instances.vidya) instances.vidya = { settings: {} };
|
|
34
|
-
instances.vidya.settings[indicatorKey] = {
|
|
35
|
-
momentumBuffer: [], prevVidya: NaN,
|
|
36
|
-
sma: new FasterSMA(15), atrBuffer: [],
|
|
37
|
-
isTrendUp: false, upVol: 0, downVol: 0,
|
|
38
|
-
liquidity: {
|
|
39
|
-
high: [],
|
|
40
|
-
low: []
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Allocate output arrays
|
|
45
|
-
Object.values(keys).forEach(key => {
|
|
46
|
-
verticalOhlcv[key] = new Float64Array(len).fill(NaN)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
if (lag > 0) main.lag(Object.values(keys), lag);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Per-bar computation
|
|
53
|
-
const inst = instances.vidya.settings[indicatorKey];
|
|
54
|
-
const prefix = `vidya_${indicatorKey}`;
|
|
55
|
-
const keyBase = target === 'close' ? prefix : `${prefix}_${target}`;
|
|
56
|
-
const keys = {
|
|
57
|
-
vidya: keyBase,
|
|
58
|
-
upper: `${prefix}_upper`,
|
|
59
|
-
lower: `${prefix}_lower`,
|
|
60
|
-
delta_vol: `${prefix}_delta_vol`,
|
|
61
|
-
cross: `${prefix}_cross`,
|
|
62
|
-
liquidity: `${prefix}_liquidity`,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// Series inputs
|
|
66
|
-
const price = verticalOhlcv[target][index]
|
|
67
|
-
const high = verticalOhlcv.high[index]
|
|
68
|
-
const low = verticalOhlcv.low[index]
|
|
69
|
-
const close = verticalOhlcv.close[index]
|
|
70
|
-
const open = verticalOhlcv.open[index]
|
|
71
|
-
const vol = verticalOhlcv.volume[index]
|
|
72
|
-
const prevCross = index > 0 ? verticalOhlcv[keys.cross][index - 1] : 0
|
|
73
|
-
|
|
74
|
-
const highestHigh = (inst.liquidity.high.length === liquidityLookback) ? Math.max(...inst.liquidity.high) : NaN
|
|
75
|
-
const lowestLow = (inst.liquidity.low.length === liquidityLookback) ? Math.min(...inst.liquidity.low) : NaN
|
|
76
|
-
|
|
77
|
-
// Compute ATR (Wilder's RMA)
|
|
78
|
-
if (index > 0) {
|
|
79
|
-
const prevClose = verticalOhlcv.close[index - 1];
|
|
80
|
-
const tr = Math.max(
|
|
81
|
-
high - low,
|
|
82
|
-
Math.abs(high - prevClose),
|
|
83
|
-
Math.abs(low - prevClose)
|
|
84
|
-
)
|
|
85
|
-
inst.atrBuffer.push(tr)
|
|
86
|
-
if (inst.atrBuffer.length > atrLength) inst.atrBuffer.shift()
|
|
87
|
-
}
|
|
88
|
-
const atr = inst.atrBuffer.length === atrLength
|
|
89
|
-
? inst.atrBuffer.reduce((a, b) => a + b, 0) / atrLength
|
|
90
|
-
: NaN
|
|
91
|
-
|
|
92
|
-
// Compute raw VIDYA
|
|
93
|
-
let rawVidya = NaN
|
|
94
|
-
let mom = NaN
|
|
95
|
-
if (index > 0) mom = price - verticalOhlcv[target][index - 1]
|
|
96
|
-
inst.momentumBuffer.push(mom)
|
|
97
|
-
if (inst.momentumBuffer.length > momentum) inst.momentumBuffer.shift()
|
|
98
|
-
if (inst.momentumBuffer.length === momentum) {
|
|
99
|
-
let sumPos = 0, sumNeg = 0
|
|
100
|
-
inst.momentumBuffer.forEach(m => m >= 0 ? sumPos += m : sumNeg += -m);
|
|
101
|
-
const denom = sumPos + sumNeg
|
|
102
|
-
const cmo = denom === 0 ? 0 : Math.abs((sumPos - sumNeg) / denom) * 100;
|
|
103
|
-
const alpha = 2 / (size + 1)
|
|
104
|
-
const a = alpha * (cmo / 100)
|
|
105
|
-
rawVidya = isNaN(inst.prevVidya) ? price : (a * price + (1 - a) * inst.prevVidya);
|
|
106
|
-
inst.prevVidya = rawVidya
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Smooth VIDYA using fixed 15-bar SMA
|
|
110
|
-
inst.sma.update(rawVidya)
|
|
111
|
-
let smoothedVidya = NaN
|
|
112
|
-
try { smoothedVidya = inst.sma.getResult() } catch {}
|
|
113
|
-
|
|
114
|
-
// Bands
|
|
115
|
-
const upper = (!isNaN(atr) && !isNaN(smoothedVidya))
|
|
116
|
-
? smoothedVidya + atr * bandDistance
|
|
117
|
-
: NaN
|
|
118
|
-
const lower = (!isNaN(atr) && !isNaN(smoothedVidya))
|
|
119
|
-
? smoothedVidya - atr * bandDistance
|
|
120
|
-
: NaN
|
|
121
|
-
|
|
122
|
-
// Detect crosses
|
|
123
|
-
const prevPrice = index > 0 ? verticalOhlcv[target][index - 1] : NaN
|
|
124
|
-
const prevUpper = index > 0 ? verticalOhlcv[keys.upper][index - 1] : NaN
|
|
125
|
-
const prevLower = index > 0 ? verticalOhlcv[keys.lower][index - 1] : NaN
|
|
126
|
-
const crossUp = index > 0 && prevPrice <= prevUpper && price > upper
|
|
127
|
-
const crossDown = index > 0 && prevPrice >= prevLower && price < lower
|
|
128
|
-
|
|
129
|
-
// Update trend state
|
|
130
|
-
if (crossUp) inst.isTrendUp = true
|
|
131
|
-
if (crossDown) inst.isTrendUp = false
|
|
132
|
-
|
|
133
|
-
// Cumulative cross counter: positive when uptrend, negative when downtrend
|
|
134
|
-
let cross = 0;
|
|
135
|
-
if (index > 0) {
|
|
136
|
-
if (inst.isTrendUp) {
|
|
137
|
-
cross = prevCross > 0 ? prevCross + 1 : 1
|
|
138
|
-
} else {
|
|
139
|
-
cross = prevCross < 0 ? prevCross - 1 : -1
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Volume accumulation
|
|
144
|
-
if (crossUp || crossDown) {
|
|
145
|
-
inst.upVol = inst.downVol = 0
|
|
146
|
-
} else {
|
|
147
|
-
inst.upVol += (close > open ? vol : 0)
|
|
148
|
-
inst.downVol += (close < open ? vol : 0)
|
|
149
|
-
}
|
|
150
|
-
const avgVol = (inst.upVol + inst.downVol) / 2 || 1
|
|
151
|
-
const deltaPct = (inst.upVol - inst.downVol) / avgVol
|
|
152
|
-
|
|
153
|
-
// Trend-based boundary (reset on crossover)
|
|
154
|
-
const trendValue = (inst.isTrendUp) ? lower : upper
|
|
155
|
-
let liquidity = NaN
|
|
156
|
-
|
|
157
|
-
if(cross > 0)
|
|
158
|
-
{
|
|
159
|
-
liquidity = lowestLow
|
|
160
|
-
}
|
|
161
|
-
else if(cross < 0)
|
|
162
|
-
{
|
|
163
|
-
liquidity = highestHigh
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Push outputs
|
|
167
|
-
main.pushToMain({ index, key: keys.vidya, value: trendValue })
|
|
168
|
-
main.pushToMain({ index, key: keys.upper, value: upper })
|
|
169
|
-
main.pushToMain({ index, key: keys.lower, value: lower })
|
|
170
|
-
main.pushToMain({ index, key: keys.delta_vol, value: deltaPct })
|
|
171
|
-
main.pushToMain({ index, key: keys.cross, value: cross })
|
|
172
|
-
main.pushToMain({ index, key: keys.liquidity, value: liquidity })
|
|
173
|
-
|
|
174
|
-
inst.liquidity.high.push(high)
|
|
175
|
-
inst.liquidity.low.push(low)
|
|
176
|
-
|
|
177
|
-
if(inst.liquidity.high.length > liquidityLookback) inst.liquidity.high.shift()
|
|
178
|
-
if(inst.liquidity.low.length > liquidityLookback) inst.liquidity.low.shift()
|
|
179
|
-
|
|
180
|
-
return true
|
|
181
|
-
}
|
package/src/studies/mapCols.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
export const mapCols = (main, index, newCols, callback, {lag, isPriceBased}) => {
|
|
2
|
-
|
|
3
|
-
const {verticalOhlcv, len, priceBased, precision} = main
|
|
4
|
-
|
|
5
|
-
if(index === 0)
|
|
6
|
-
{
|
|
7
|
-
for(const key of newCols)
|
|
8
|
-
{
|
|
9
|
-
if(verticalOhlcv.hasOwnProperty(key)) {
|
|
10
|
-
throw new Error(`New property "${key}" already exist in "verticalOhlcv" and can not be modified using mapCols.`)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
verticalOhlcv[key] = new Array(len).fill(0)
|
|
14
|
-
|
|
15
|
-
if(precision && isPriceBased)
|
|
16
|
-
{
|
|
17
|
-
priceBased.add(key)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if(lag > 0)
|
|
22
|
-
{
|
|
23
|
-
main.lag(newCols, lag)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const cols = callback({index, main})
|
|
28
|
-
|
|
29
|
-
if(cols == null) return true
|
|
30
|
-
|
|
31
|
-
for(const [key, value] of Object.entries(cols))
|
|
32
|
-
{
|
|
33
|
-
main.pushToMain({ index, key, value })
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const defaultMapColsCallback = ({main, index}) => {
|
|
38
|
-
const {verticalOhlcv} = main
|
|
39
|
-
|
|
40
|
-
const close = verticalOhlcv.close[index]
|
|
41
|
-
const open = verticalOhlcv.open[index-1]
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
change: ((close - open) / open) * 100
|
|
45
|
-
}
|
|
46
|
-
}
|