ohlcv-indicators 2.0.5 → 2.0.7
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/dist/ohlcv-indicators.min.js +1 -1
- package/index.js +52 -43
- package/package.json +1 -1
- package/src/moving-averages/bollingerBands.js +116 -63
- package/src/moving-averages/donchianChannel.js +104 -83
- package/src/moving-averages/ema.js +1 -1
- package/src/moving-averages/macd.js +41 -11
- package/src/moving-averages/relativeVolume.js +16 -12
- package/src/moving-averages/sma.js +1 -1
- package/src/oscillators/rsi.js +2 -2
- package/src/oscillators/volumeOscillator.js +7 -5
- package/src/studies/candleStudies.js +35 -26
- package/src/studies/dateTime.js +8 -9
- package/src/studies/findCrosses.js +1 -1
- package/src/studies/lag.js +14 -1
- package/src/studies/priceVariations.js +5 -4
- package/src/utilities/parsing-utilities.js +157 -93
|
@@ -112,45 +112,54 @@ export const candleStudies = (main, index, size, classify = true, classification
|
|
|
112
112
|
// If there's not enough data, means remain null
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
main.
|
|
116
|
-
|
|
117
|
-
:
|
|
115
|
+
main.pushToMain({
|
|
116
|
+
index,
|
|
117
|
+
key: 'candle_body_size',
|
|
118
|
+
value: isUp
|
|
119
|
+
? classifySize(candleBodySize, bodySizeMean, 2, changeLevel)
|
|
120
|
+
: -Math.abs(classifySize(candleBodySize, bodySizeMean, 2, changeLevel)) || 0
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
|
|
118
124
|
|
|
119
|
-
main.
|
|
125
|
+
main.pushToMain({index, key: 'candle_gap_size', value: isUp
|
|
120
126
|
? classifySize(gapSize, gapMean, 2, changeLevel)
|
|
121
|
-
: -Math.abs(classifySize(gapSize, gapMean, 2, changeLevel)) || 0
|
|
127
|
+
: -Math.abs(classifySize(gapSize, gapMean, 2, changeLevel)) || 0})
|
|
128
|
+
|
|
122
129
|
|
|
123
|
-
main.
|
|
130
|
+
main.pushToMain({index, key: 'candle_close_direction', value: isUp
|
|
124
131
|
? classifySize(closeDirection, closeDirectionMean, 2, changeLevel)
|
|
125
|
-
: -Math.abs(classifySize(closeDirection, closeDirectionMean, 2, changeLevel)) || 0
|
|
132
|
+
: -Math.abs(classifySize(closeDirection, closeDirectionMean, 2, changeLevel)) || 0})
|
|
126
133
|
|
|
127
|
-
|
|
134
|
+
|
|
135
|
+
main.pushToMain({index, key: 'candle_high_direction', value: isUp
|
|
128
136
|
? classifySize(highDirection, highDirectionMean, 2, changeLevel)
|
|
129
|
-
: -Math.abs(classifySize(highDirection, highDirectionMean, 2, changeLevel)) || 0
|
|
137
|
+
: -Math.abs(classifySize(highDirection, highDirectionMean, 2, changeLevel)) || 0})
|
|
130
138
|
|
|
131
|
-
main.
|
|
139
|
+
main.pushToMain({index, key: 'candle_low_direction', value: isUp
|
|
132
140
|
? classifySize(lowDirection, lowDirectionMean, 2, changeLevel)
|
|
133
|
-
: -Math.abs(classifySize(lowDirection, lowDirectionMean, 2, changeLevel)) || 0
|
|
141
|
+
: -Math.abs(classifySize(lowDirection, lowDirectionMean, 2, changeLevel)) || 0})
|
|
134
142
|
|
|
135
|
-
main.
|
|
143
|
+
main.pushToMain({index, key: 'candle_open_direction', value: isUp
|
|
136
144
|
? classifySize(openDirection, openDirectionMean, 2, changeLevel)
|
|
137
|
-
: -Math.abs(classifySize(openDirection, openDirectionMean, 2, changeLevel)) || 0
|
|
145
|
+
: -Math.abs(classifySize(openDirection, openDirectionMean, 2, changeLevel)) || 0})
|
|
146
|
+
|
|
147
|
+
main.pushToMain({index, key: 'candle_top_size', value: classifySize(topSize, topSizeMean, 0.5, sizeLevel)})
|
|
148
|
+
main.pushToMain({index, key: 'candle_bottom_size', value: classifySize(bottomSize, bottomSizeMean, 0.5, sizeLevel)})
|
|
149
|
+
main.pushToMain({index, key: 'candle_shadow_size', value: classifySize(shadowSize, shadowSizeMean, 0.5, sizeLevel)})
|
|
138
150
|
|
|
139
|
-
main.verticalOhlcv.candle_top_size[index] = classifySize(topSize, topSizeMean, 0.5, sizeLevel);
|
|
140
|
-
main.verticalOhlcv.candle_bottom_size[index] = classifySize(bottomSize, bottomSizeMean, 0.5, sizeLevel);
|
|
141
|
-
main.verticalOhlcv.candle_shadow_size[index] = classifySize(shadowSize, shadowSizeMean, 0.5, sizeLevel);
|
|
142
151
|
} else {
|
|
143
152
|
// Without classification, just store raw values
|
|
144
|
-
|
|
145
|
-
main.
|
|
146
|
-
main.
|
|
147
|
-
main.
|
|
148
|
-
main.
|
|
149
|
-
main.
|
|
150
|
-
|
|
151
|
-
main.
|
|
152
|
-
main.
|
|
153
|
-
main.
|
|
153
|
+
|
|
154
|
+
main.pushToMain({index, key: 'candle_body_size', value: isUp ? candleBodySize : -candleBodySize})
|
|
155
|
+
main.pushToMain({index, key: 'candle_gap_size', value: isUp ? gapSize : -gapSize})
|
|
156
|
+
main.pushToMain({index, key: 'candle_close_direction', value: isUp ? closeDirection : -closeDirection})
|
|
157
|
+
main.pushToMain({index, key: 'candle_high_direction', value: isUp ? highDirection : -highDirection})
|
|
158
|
+
main.pushToMain({index, key: 'candle_low_direction', value: isUp ? lowDirection : -lowDirection})
|
|
159
|
+
main.pushToMain({index, key: 'candle_open_direction', value: isUp ? openDirection : -openDirection})
|
|
160
|
+
main.pushToMain({index, key: 'candle_top_size', value: topSize})
|
|
161
|
+
main.pushToMain({index, key: 'candle_bottom_size', value: bottomSize})
|
|
162
|
+
main.pushToMain({index, key: 'candle_shadow_size', value: shadowSize})
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
return true;
|
package/src/studies/dateTime.js
CHANGED
|
@@ -48,15 +48,14 @@ export const dateTime = (main, index) => {
|
|
|
48
48
|
main.instances.dateTime.sessionIntradayIndexCount = 0
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
main.
|
|
53
|
-
main.
|
|
54
|
-
main.
|
|
55
|
-
main.
|
|
56
|
-
main.
|
|
57
|
-
main.
|
|
58
|
-
main.
|
|
59
|
-
main.verticalOhlcv.hour[index] = hour
|
|
51
|
+
main.pushToMain({index, key: 'session_daily_index', value: main.instances.dateTime.sessionDailyIndexCount})
|
|
52
|
+
main.pushToMain({index, key: 'session_intraday_index', value: main.instances.dateTime.sessionIntradayIndexCount})
|
|
53
|
+
main.pushToMain({index, key: 'day_of_the_week', value: day_of_the_week})
|
|
54
|
+
main.pushToMain({index, key: 'day_of_the_month', value: day_of_the_month})
|
|
55
|
+
main.pushToMain({index, key: 'week_of_the_month', value: week_of_the_month})
|
|
56
|
+
main.pushToMain({index, key: 'month', value: month})
|
|
57
|
+
main.pushToMain({index, key: 'year', value: year})
|
|
58
|
+
main.pushToMain({index, key: 'hour', value: hour})
|
|
60
59
|
|
|
61
60
|
}
|
|
62
61
|
|
|
@@ -188,7 +188,7 @@ export const crossPairs = (main, index) => {
|
|
|
188
188
|
main.instances[crossName].update({fast: fastValue, slow: slowValue})
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
main.
|
|
191
|
+
main.pushToMain({index, key: crossName, value: main.instances[crossName].getResult()})
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
}
|
package/src/studies/lag.js
CHANGED
|
@@ -8,6 +8,19 @@ export const lag = (main, index) => {
|
|
|
8
8
|
|
|
9
9
|
const params = findParams.map(o => o.params)
|
|
10
10
|
|
|
11
|
+
for (const [colKeys, lags] of params)
|
|
12
|
+
{
|
|
13
|
+
for (const colKey of colKeys)
|
|
14
|
+
{
|
|
15
|
+
if(main.priceBased.includes(colKey))
|
|
16
|
+
{
|
|
17
|
+
for (let lag = 1; lag <= lags; lag++) {
|
|
18
|
+
main.priceBased.push(`${colKey}_lag_${lag}`)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
main.instances.lag = {
|
|
12
25
|
lagParams: params
|
|
13
26
|
}
|
|
@@ -33,7 +46,7 @@ export const lag = (main, index) => {
|
|
|
33
46
|
|
|
34
47
|
if(laggedIndex <= 0) break
|
|
35
48
|
|
|
36
|
-
main.
|
|
49
|
+
main.pushToMain({index, key, value: currentColumn[laggedIndex]})
|
|
37
50
|
}
|
|
38
51
|
}
|
|
39
52
|
}
|
|
@@ -9,14 +9,15 @@ export const priceVariations = (main, index) => {
|
|
|
9
9
|
mid_price_open_close: [...main.nullArray],
|
|
10
10
|
mid_price_high_low: [...main.nullArray]
|
|
11
11
|
})
|
|
12
|
+
|
|
13
|
+
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
const open = main.verticalOhlcv.open[index]
|
|
15
17
|
const high = main.verticalOhlcv.high[index]
|
|
16
18
|
const low = main.verticalOhlcv.low[index]
|
|
17
19
|
const close = main.verticalOhlcv.close[index]
|
|
18
|
-
|
|
19
|
-
main.
|
|
20
|
-
main.
|
|
21
|
-
|
|
20
|
+
|
|
21
|
+
main.pushToMain({index, key: 'mid_price_open_close', value: (open + close) / 2})
|
|
22
|
+
main.pushToMain({index, key: 'mid_price_high_low', value: (high + low) / 2})
|
|
22
23
|
}
|
|
@@ -1,110 +1,174 @@
|
|
|
1
|
-
import { validateInputParams } from "./validators.js"
|
|
2
|
-
import { rsi } from "../oscillators/rsi.js"
|
|
3
|
-
import { sma } from "../moving-averages/sma.js"
|
|
4
|
-
import { ema } from "../moving-averages/ema.js"
|
|
5
|
-
import { macd } from "../moving-averages/macd.js"
|
|
6
|
-
import { relativeVolume } from "../moving-averages/relativeVolume.js"
|
|
7
|
-
import { donchianChannels } from "../moving-averages/donchianChannel.js"
|
|
8
|
-
import { bollingerBands } from "../moving-averages/bollingerBands.js"
|
|
9
|
-
import { volumeOscillator } from "../oscillators/volumeOscillator.js"
|
|
10
|
-
import { candleStudies } from "../studies/candleStudies.js"
|
|
11
|
-
import { lag } from "../studies/lag.js"
|
|
12
|
-
import {crossPairs} from "../studies/findCrosses.js"
|
|
13
|
-
import { dateTime } from "../studies/dateTime.js"
|
|
14
|
-
import { priceVariations } from "../studies/priceVariations.js"
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import { validateInputParams } from "./validators.js";
|
|
2
|
+
import { rsi } from "../oscillators/rsi.js";
|
|
3
|
+
import { sma } from "../moving-averages/sma.js";
|
|
4
|
+
import { ema } from "../moving-averages/ema.js";
|
|
5
|
+
import { macd } from "../moving-averages/macd.js";
|
|
6
|
+
import { relativeVolume } from "../moving-averages/relativeVolume.js";
|
|
7
|
+
import { donchianChannels } from "../moving-averages/donchianChannel.js";
|
|
8
|
+
import { bollingerBands } from "../moving-averages/bollingerBands.js";
|
|
9
|
+
import { volumeOscillator } from "../oscillators/volumeOscillator.js";
|
|
10
|
+
import { candleStudies } from "../studies/candleStudies.js";
|
|
11
|
+
import { lag } from "../studies/lag.js";
|
|
12
|
+
import { crossPairs } from "../studies/findCrosses.js";
|
|
13
|
+
import { dateTime } from "../studies/dateTime.js";
|
|
14
|
+
import { priceVariations } from "../studies/priceVariations.js";
|
|
15
|
+
|
|
16
|
+
// Helper to clean non-numeric characters (except "-" at the start and decimal point)
|
|
17
|
+
const cleanNumStr = str => str.replace(/(?!^-)[^0-9.]/g, '');
|
|
18
|
+
|
|
19
|
+
// Map indicator keys to their respective functions
|
|
17
20
|
const indicatorFunctions = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export const parseOhlcvToVertical = (input, main, startIndex = 0) => {
|
|
33
|
-
const { len, inputParams } = main
|
|
34
|
-
|
|
35
|
-
main.nullArray = new Array(len).fill(null)
|
|
36
|
-
|
|
37
|
-
if (startIndex === 0) {
|
|
38
|
-
|
|
39
|
-
validateInputParams(main)
|
|
40
|
-
|
|
41
|
-
const baseKeys = ['open', 'high', 'low', 'close', 'volume'];
|
|
42
|
-
const baseKeysSet = new Set(baseKeys);
|
|
21
|
+
dateTime,
|
|
22
|
+
priceVariations,
|
|
23
|
+
rsi,
|
|
24
|
+
sma,
|
|
25
|
+
ema,
|
|
26
|
+
macd,
|
|
27
|
+
relativeVolume,
|
|
28
|
+
donchianChannels,
|
|
29
|
+
bollingerBands,
|
|
30
|
+
volumeOscillator,
|
|
31
|
+
candleStudies
|
|
32
|
+
};
|
|
43
33
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
// Processes the inputParams into a list of indicator calls
|
|
35
|
+
const processIndicatorCalls = inputParams =>
|
|
36
|
+
Array.isArray(inputParams)
|
|
37
|
+
? inputParams.map(({ key, params }) => ({
|
|
38
|
+
key,
|
|
39
|
+
fn: indicatorFunctions[key],
|
|
40
|
+
args: params
|
|
41
|
+
}))
|
|
42
|
+
: [];
|
|
43
|
+
|
|
44
|
+
// Sets the input types for each key based on the first row of data
|
|
45
|
+
const setInputTypes = (row0, main) => {
|
|
46
|
+
const isValidNumberString = str => /^(\d+(\.\d*)?|\.\d+)$/.test(str);
|
|
47
|
+
|
|
48
|
+
for (const key of Object.keys(main.inputTypes)) {
|
|
49
|
+
if (!(key in row0)) {
|
|
50
|
+
throw new Error(`Property "${key}" not found in ohlcv array. ${main.ticker}`);
|
|
51
|
+
}
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
const otherKeys = [];
|
|
51
|
-
for (let i = 0; i < inputKeys.length; i++) {
|
|
52
|
-
const key = inputKeys[i]
|
|
53
|
-
if (!baseKeysSet.has(key)) {
|
|
54
|
-
main.verticalOhlcv[key] = [...main.nullArray]
|
|
55
|
-
otherKeys.push(key)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
main.otherKeys = otherKeys;
|
|
53
|
+
let value = row0[key];
|
|
59
54
|
|
|
55
|
+
|
|
60
56
|
|
|
57
|
+
if (typeof value === 'number' && value > 0) {
|
|
58
|
+
main.inputTypes[key] = 'number';
|
|
59
|
+
} else if (typeof value === 'string' && isValidNumberString(value)) {
|
|
60
|
+
main.inputTypes[key] = 'string';
|
|
61
|
+
value = cleanNumStr(value);
|
|
61
62
|
} else {
|
|
62
|
-
|
|
63
|
-
throw new Error("otherKeys not found in main. Ensure you run with startIndex=0 first to initialize.");
|
|
64
|
-
}
|
|
63
|
+
throw new Error(`Invalid property "${key}" in ohlcv array. ${main.ticker}`);
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
main.verticalOhlcv.open[x] = current.open;
|
|
74
|
-
main.verticalOhlcv.high[x] = current.high;
|
|
75
|
-
main.verticalOhlcv.low[x] = current.low;
|
|
76
|
-
main.verticalOhlcv.close[x] = current.close;
|
|
77
|
-
main.verticalOhlcv.volume[x] = current.volume;
|
|
78
|
-
|
|
79
|
-
for (let i = 0; i < main.otherKeys.length; i++) {
|
|
80
|
-
const k = main.otherKeys[i];
|
|
81
|
-
main.verticalOhlcv[k][x] = current[k];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
for (let i = 0; i < indicatorCalls.length; i++) {
|
|
85
|
-
const { fn, args, key } = indicatorCalls[i];
|
|
86
|
-
if (key === 'crossPairs' || key === 'lag') continue;
|
|
87
|
-
|
|
88
|
-
fn(main, x, ...args)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
lag(main, x);
|
|
92
|
-
crossPairs(main, x);
|
|
93
|
-
main.lastComputedIndex++;
|
|
66
|
+
// Determine precisionMultiplier if not already set
|
|
67
|
+
if (main.precisionMultiplier === 0) {
|
|
68
|
+
const [, decimals = ''] = String(value).split('.');
|
|
69
|
+
const decimalPrecision = Math.max(4, decimals.length);
|
|
70
|
+
main.precisionMultiplier = decimalPrecision > 1 ? Math.pow(10, decimalPrecision - 1) : 1;
|
|
94
71
|
}
|
|
72
|
+
}
|
|
95
73
|
};
|
|
96
74
|
|
|
75
|
+
// Parses a number from either a numeric or string input, applying a multiplier to fix precision issues
|
|
76
|
+
const parseNumber = (num, type, precisionMultiplier) => {
|
|
77
|
+
const addMultiplier = cNum =>
|
|
78
|
+
precisionMultiplier > 1 ? cNum * precisionMultiplier : cNum;
|
|
79
|
+
|
|
80
|
+
if (type === 'number') {
|
|
81
|
+
return addMultiplier(num);
|
|
82
|
+
} else if (type === 'string') {
|
|
83
|
+
const cleanedStr = cleanNumStr(num);
|
|
84
|
+
return addMultiplier(Number(cleanedStr));
|
|
85
|
+
} else
|
|
86
|
+
{
|
|
87
|
+
return 0 // Optionally: throw an error if type is not recognized
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const parseVolume = (volume, type) => {
|
|
97
93
|
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
if(type === 'number') return volume
|
|
95
|
+
else if(type === 'string'){
|
|
96
|
+
return Number(cleanNumStr(volume))
|
|
97
|
+
}
|
|
98
|
+
else
|
|
99
|
+
{
|
|
100
|
+
return 0 // Optionally: throw an error if type is not recognized
|
|
101
|
+
}
|
|
102
|
+
}
|
|
100
103
|
|
|
101
|
-
|
|
104
|
+
export const parseOhlcvToVertical = (input, main, startIndex = 0) => {
|
|
105
|
+
const { len, inputParams } = main;
|
|
106
|
+
main.nullArray = new Array(len).fill(null);
|
|
107
|
+
|
|
108
|
+
const BASE_KEYS = ['open', 'high', 'low', 'close', 'volume'];
|
|
109
|
+
const baseKeysSet = new Set(BASE_KEYS);
|
|
110
|
+
|
|
111
|
+
if (startIndex === 0) {
|
|
112
|
+
// Validate parameters and initialize base keys in verticalOhlcv
|
|
113
|
+
validateInputParams(main);
|
|
114
|
+
BASE_KEYS.forEach(key => {
|
|
115
|
+
main.verticalOhlcv[key] = [...main.nullArray];
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Identify any additional keys from the first input row and initialize them
|
|
119
|
+
const inputKeys = Object.keys(input[0]);
|
|
120
|
+
const otherKeys = inputKeys.filter(key => !baseKeysSet.has(key));
|
|
121
|
+
otherKeys.forEach(key => {
|
|
122
|
+
main.verticalOhlcv[key] = [...main.nullArray];
|
|
123
|
+
});
|
|
124
|
+
main.otherKeys = otherKeys;
|
|
125
|
+
|
|
126
|
+
// Set the input types once using the first row
|
|
127
|
+
setInputTypes(input[0], main);
|
|
128
|
+
} else if (!main.otherKeys) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
"otherKeys not found in main. Ensure you run with startIndex=0 first to initialize."
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Prepare the list of indicator function calls
|
|
135
|
+
const indicatorCalls = processIndicatorCalls(inputParams);
|
|
136
|
+
|
|
137
|
+
// Process each row in the input
|
|
138
|
+
for (let x = startIndex; x < len; x++) {
|
|
139
|
+
const current = input[x];
|
|
140
|
+
// Destructure the base keys and use the rest for other properties
|
|
141
|
+
const { open, high, low, close, volume, ...rest } = current
|
|
142
|
+
const {precisionMultiplier} = main
|
|
143
|
+
|
|
144
|
+
const parsedOpen = parseNumber(open, main.inputTypes.open, precisionMultiplier)
|
|
145
|
+
const parsedHigh = parseNumber(open, main.inputTypes.high, precisionMultiplier)
|
|
146
|
+
const parsedLow = parseNumber(open, main.inputTypes.low, precisionMultiplier)
|
|
147
|
+
const parsedClose = parseNumber(open, main.inputTypes.close, precisionMultiplier)
|
|
148
|
+
const parsedVolume = parseVolume (volume, main.inputTypes.volume)
|
|
149
|
+
|
|
150
|
+
main.pushToMain({index: x, key: 'open', value: parsedOpen})
|
|
151
|
+
main.pushToMain({index: x, key: 'high', value: parsedHigh})
|
|
152
|
+
main.pushToMain({index: x, key: 'low', value: parsedLow})
|
|
153
|
+
main.pushToMain({index: x, key: 'close', value: parsedClose})
|
|
154
|
+
main.pushToMain({index: x, key: 'volume', value: parsedVolume})
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
// Populate any extra keys identified during initialization
|
|
158
|
+
for (const key of main.otherKeys) {
|
|
159
|
+
main.pushToMain({index: x, key, value: rest[key]})
|
|
160
|
+
}
|
|
102
161
|
|
|
103
|
-
|
|
104
|
-
{
|
|
105
|
-
|
|
106
|
-
|
|
162
|
+
// Run all indicator functions except for the ones processed later
|
|
163
|
+
for (const { key, fn, args } of indicatorCalls) {
|
|
164
|
+
if (['crossPairs', 'lag'].includes(key)) continue;
|
|
165
|
+
fn(main, x, ...args);
|
|
107
166
|
}
|
|
108
167
|
|
|
109
|
-
|
|
168
|
+
// Process these indicators separately (ensuring their execution order)
|
|
169
|
+
lag(main, x);
|
|
170
|
+
crossPairs(main, x);
|
|
171
|
+
|
|
172
|
+
main.lastComputedIndex++;
|
|
173
|
+
}
|
|
110
174
|
};
|