@vulcan-js/indicators 0.1.0 → 0.2.0
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/index.d.ts +20 -20
- package/dist/index.js +499 -396
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,81 +1,179 @@
|
|
|
1
|
-
import { assert,
|
|
2
|
-
import { abs, add, div, divide, eq, equal, from, gt, lt, mul, multiply, sub, subtract } from "dnum";
|
|
1
|
+
import { assert, createSignal, fp18 } from "@vulcan-js/core";
|
|
3
2
|
|
|
4
|
-
//#region src/
|
|
5
|
-
const defaultExponentialMovingAverageOptions = { period: 12 };
|
|
3
|
+
//#region src/primitives/ad.ts
|
|
6
4
|
/**
|
|
7
|
-
*
|
|
5
|
+
* Create an accumulation/distribution processor.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* MFM = ((C - L) - (H - C)) / (H - L)
|
|
8
|
+
* MFV = MFM * Volume
|
|
9
|
+
* AD = Previous AD + MFV
|
|
11
10
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
function ad$1() {
|
|
12
|
+
let prevAD = fp18.ZERO;
|
|
13
|
+
return (h, l, c, v) => {
|
|
14
|
+
const range = h - l;
|
|
15
|
+
const mfm = range === fp18.ZERO ? fp18.ZERO : fp18.div(c - l - (h - c), range);
|
|
16
|
+
const mfv = fp18.mul(mfm, v);
|
|
17
|
+
prevAD += mfv;
|
|
18
|
+
return prevAD;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/primitives/ewma.ts
|
|
24
|
+
/**
|
|
25
|
+
* Create an exponentially weighted moving average (EWMA) processor.
|
|
26
|
+
*
|
|
27
|
+
* new = value * k + prev * (1 - k)
|
|
28
|
+
*
|
|
29
|
+
* @param k - Smoothing factor as fp18 bigint
|
|
30
|
+
*/
|
|
31
|
+
function ewma(k) {
|
|
32
|
+
const m = fp18.ONE - k;
|
|
16
33
|
let prev;
|
|
17
34
|
return (value) => {
|
|
18
35
|
if (prev === void 0) {
|
|
19
|
-
prev =
|
|
36
|
+
prev = value;
|
|
20
37
|
return prev;
|
|
21
38
|
}
|
|
22
|
-
prev =
|
|
39
|
+
prev = (value * k + prev * m) / fp18.SCALE;
|
|
23
40
|
return prev;
|
|
24
41
|
};
|
|
25
|
-
}
|
|
42
|
+
}
|
|
43
|
+
/** Compute EMA smoothing factor: k = 2 / (period + 1) */
|
|
44
|
+
ewma.k = (period) => fp18.div(fp18.TWO, BigInt(1 + period) * fp18.SCALE);
|
|
26
45
|
|
|
27
46
|
//#endregion
|
|
28
|
-
//#region src/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
assert(Number.isInteger(
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
//#region src/primitives/mmax.ts
|
|
48
|
+
/**
|
|
49
|
+
* Create a moving maximum processor.
|
|
50
|
+
*
|
|
51
|
+
* Tracks the maximum value in a sliding window of `period` values.
|
|
52
|
+
*/
|
|
53
|
+
function mmax$1(period) {
|
|
54
|
+
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
55
|
+
const buffer = [];
|
|
56
|
+
return (value) => {
|
|
57
|
+
buffer.push(value);
|
|
58
|
+
if (buffer.length > period) buffer.shift();
|
|
59
|
+
return buffer.reduce((max, cur) => cur > max ? cur : max);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
40
62
|
|
|
41
63
|
//#endregion
|
|
42
|
-
//#region src/
|
|
43
|
-
const defaultSMAOptions = { period: 2 };
|
|
64
|
+
//#region src/primitives/mmin.ts
|
|
44
65
|
/**
|
|
45
|
-
*
|
|
66
|
+
* Create a moving minimum processor.
|
|
46
67
|
*
|
|
47
|
-
*
|
|
48
|
-
|
|
68
|
+
* Tracks the minimum value in a sliding window of `period` values.
|
|
69
|
+
*/
|
|
70
|
+
function mmin$1(period) {
|
|
71
|
+
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
72
|
+
const buffer = [];
|
|
73
|
+
return (value) => {
|
|
74
|
+
buffer.push(value);
|
|
75
|
+
if (buffer.length > period) buffer.shift();
|
|
76
|
+
return buffer.reduce((min, cur) => cur < min ? cur : min);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/primitives/msum.ts
|
|
82
|
+
/**
|
|
83
|
+
* Create a moving sum processor.
|
|
49
84
|
*
|
|
50
|
-
*
|
|
51
|
-
|
|
85
|
+
* Uses a ring buffer to maintain a sliding window of `period` values.
|
|
86
|
+
*/
|
|
87
|
+
function msum$1(period) {
|
|
88
|
+
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
89
|
+
const buffer = Array.from({ length: period });
|
|
90
|
+
let head = 0;
|
|
91
|
+
let count = 0;
|
|
92
|
+
let runningSum = fp18.ZERO;
|
|
93
|
+
return (value) => {
|
|
94
|
+
if (count < period) {
|
|
95
|
+
buffer[count] = value;
|
|
96
|
+
runningSum += value;
|
|
97
|
+
count++;
|
|
98
|
+
} else {
|
|
99
|
+
runningSum = runningSum - buffer[head] + value;
|
|
100
|
+
buffer[head] = value;
|
|
101
|
+
head = (head + 1) % period;
|
|
102
|
+
}
|
|
103
|
+
return runningSum;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/primitives/rma.ts
|
|
109
|
+
/**
|
|
110
|
+
* Create a rolling moving average (RMA) processor.
|
|
52
111
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* @param options.period - The period for calculating the moving average (default: 2)
|
|
56
|
-
* @returns Generator yielding SMA values
|
|
112
|
+
* Warm-up: running SMA for the first `period` values.
|
|
113
|
+
* After: R[i] = (R[i-1] * (period - 1) + value) / period
|
|
57
114
|
*/
|
|
58
|
-
|
|
115
|
+
function rma$1(period) {
|
|
116
|
+
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
117
|
+
const periodBig = BigInt(period);
|
|
118
|
+
let count = 0;
|
|
119
|
+
let sum = fp18.ZERO;
|
|
120
|
+
let prev = fp18.ZERO;
|
|
121
|
+
return (value) => {
|
|
122
|
+
if (count < period) {
|
|
123
|
+
sum += value;
|
|
124
|
+
count++;
|
|
125
|
+
prev = sum / BigInt(count);
|
|
126
|
+
return prev;
|
|
127
|
+
}
|
|
128
|
+
prev = (prev * (periodBig - 1n) + value) / periodBig;
|
|
129
|
+
return prev;
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/primitives/sma.ts
|
|
135
|
+
/**
|
|
136
|
+
* Create a simple moving average (SMA) processor.
|
|
137
|
+
*
|
|
138
|
+
* Uses a ring buffer to maintain a sliding window of `period` values.
|
|
139
|
+
* Returns the running average (sum / count) during warm-up.
|
|
140
|
+
*/
|
|
141
|
+
function sma$1(period) {
|
|
59
142
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
60
143
|
const buffer = Array.from({ length: period });
|
|
61
144
|
let head = 0;
|
|
62
145
|
let count = 0;
|
|
63
|
-
let runningSum =
|
|
146
|
+
let runningSum = fp18.ZERO;
|
|
64
147
|
return (value) => {
|
|
65
|
-
const v = toDnum(value);
|
|
66
148
|
if (count < period) {
|
|
67
|
-
buffer[count] =
|
|
68
|
-
runningSum
|
|
149
|
+
buffer[count] = value;
|
|
150
|
+
runningSum += value;
|
|
69
151
|
count++;
|
|
70
152
|
} else {
|
|
71
|
-
runningSum =
|
|
72
|
-
|
|
73
|
-
buffer[head] = v;
|
|
153
|
+
runningSum = runningSum - buffer[head] + value;
|
|
154
|
+
buffer[head] = value;
|
|
74
155
|
head = (head + 1) % period;
|
|
75
156
|
}
|
|
76
|
-
return
|
|
157
|
+
return runningSum / BigInt(count);
|
|
77
158
|
};
|
|
78
|
-
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/momentum/absolutePriceOscillator.ts
|
|
163
|
+
const defaultAbsolutePriceOscillatorOptions = {
|
|
164
|
+
fastPeriod: 12,
|
|
165
|
+
slowPeriod: 26
|
|
166
|
+
};
|
|
167
|
+
const apo = createSignal(({ fastPeriod, slowPeriod }) => {
|
|
168
|
+
assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`));
|
|
169
|
+
assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`));
|
|
170
|
+
const fastProc = ewma(ewma.k(fastPeriod));
|
|
171
|
+
const slowProc = ewma(ewma.k(slowPeriod));
|
|
172
|
+
return (value) => {
|
|
173
|
+
const v = fp18.toFp18(value);
|
|
174
|
+
return fp18.toDnum(fastProc(v) - slowProc(v));
|
|
175
|
+
};
|
|
176
|
+
}, defaultAbsolutePriceOscillatorOptions);
|
|
79
177
|
|
|
80
178
|
//#endregion
|
|
81
179
|
//#region src/momentum/awesomeOscillator.ts
|
|
@@ -92,38 +190,14 @@ const defaultAwesomeOscillatorOptions = {
|
|
|
92
190
|
const ao = createSignal(({ fastPeriod, slowPeriod }) => {
|
|
93
191
|
assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`));
|
|
94
192
|
assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`));
|
|
95
|
-
const fastProc = sma
|
|
96
|
-
const slowProc = sma
|
|
193
|
+
const fastProc = sma$1(fastPeriod);
|
|
194
|
+
const slowProc = sma$1(slowPeriod);
|
|
97
195
|
return (bar) => {
|
|
98
|
-
const median =
|
|
99
|
-
return
|
|
196
|
+
const median = (fp18.toFp18(bar.h) + fp18.toFp18(bar.l)) / 2n;
|
|
197
|
+
return fp18.toDnum(fastProc(median) - slowProc(median));
|
|
100
198
|
};
|
|
101
199
|
}, defaultAwesomeOscillatorOptions);
|
|
102
200
|
|
|
103
|
-
//#endregion
|
|
104
|
-
//#region src/volume/accumulationDistribution.ts
|
|
105
|
-
/**
|
|
106
|
-
* Accumulation/Distribution Indicator (A/D). Cumulative indicator
|
|
107
|
-
* that uses volume and price to assess whether a stock is
|
|
108
|
-
* being accumulated or distributed.
|
|
109
|
-
*
|
|
110
|
-
* MFM = ((Closing - Low) - (High - Closing)) / (High - Low)
|
|
111
|
-
* MFV = MFM * Period Volume
|
|
112
|
-
* AD = Previous AD + CMFV
|
|
113
|
-
*/
|
|
114
|
-
const ad = createSignal(() => {
|
|
115
|
-
let prevAD = constants.ZERO;
|
|
116
|
-
return (bar) => {
|
|
117
|
-
const h = toDnum(bar.h);
|
|
118
|
-
const l = toDnum(bar.l);
|
|
119
|
-
const c = toDnum(bar.c);
|
|
120
|
-
const v = toDnum(bar.v);
|
|
121
|
-
const range = subtract(h, l);
|
|
122
|
-
prevAD = add(multiply(equal(range, 0) ? constants.ZERO : divide(subtract(subtract(c, l), subtract(h, c)), range, constants.DECIMALS), v), prevAD);
|
|
123
|
-
return prevAD;
|
|
124
|
-
};
|
|
125
|
-
});
|
|
126
|
-
|
|
127
201
|
//#endregion
|
|
128
202
|
//#region src/momentum/chaikinOscillator.ts
|
|
129
203
|
const defaultChaikinOscillatorOptions = {
|
|
@@ -142,18 +216,19 @@ const defaultChaikinOscillatorOptions = {
|
|
|
142
216
|
const cmo = createSignal(({ fastPeriod, slowPeriod }) => {
|
|
143
217
|
assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`));
|
|
144
218
|
assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`));
|
|
145
|
-
const adProc = ad
|
|
146
|
-
const fastProc =
|
|
147
|
-
const slowProc =
|
|
219
|
+
const adProc = ad$1();
|
|
220
|
+
const fastProc = ewma(ewma.k(fastPeriod));
|
|
221
|
+
const slowProc = ewma(ewma.k(slowPeriod));
|
|
148
222
|
return (bar) => {
|
|
149
|
-
const adVal = adProc(bar);
|
|
150
|
-
return
|
|
223
|
+
const adVal = adProc(fp18.toFp18(bar.h), fp18.toFp18(bar.l), fp18.toFp18(bar.c), fp18.toFp18(bar.v));
|
|
224
|
+
return fp18.toDnum(fastProc(adVal) - slowProc(adVal));
|
|
151
225
|
};
|
|
152
226
|
}, defaultChaikinOscillatorOptions);
|
|
153
227
|
|
|
154
228
|
//#endregion
|
|
155
229
|
//#region src/momentum/commodityChannelIndex.ts
|
|
156
230
|
const defaultCCIOptions = { period: 20 };
|
|
231
|
+
const LAMBERT = 15n * fp18.SCALE / 1000n;
|
|
157
232
|
/**
|
|
158
233
|
* Commodity Channel Index (CCI)
|
|
159
234
|
*
|
|
@@ -175,24 +250,26 @@ const defaultCCIOptions = { period: 20 };
|
|
|
175
250
|
const cci = createSignal(({ period }) => {
|
|
176
251
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
177
252
|
const buffer = [];
|
|
253
|
+
const periodBig = BigInt(period);
|
|
178
254
|
return (bar) => {
|
|
179
|
-
const h =
|
|
180
|
-
const l =
|
|
181
|
-
const c =
|
|
182
|
-
const tp =
|
|
255
|
+
const h = fp18.toFp18(bar.h);
|
|
256
|
+
const l = fp18.toFp18(bar.l);
|
|
257
|
+
const c = fp18.toFp18(bar.c);
|
|
258
|
+
const tp = (h + l + c) / 3n;
|
|
183
259
|
buffer.push(tp);
|
|
184
260
|
if (buffer.length > period) buffer.shift();
|
|
185
261
|
const n = buffer.length;
|
|
186
|
-
if (n < period) return
|
|
187
|
-
let sum =
|
|
188
|
-
for (const v of buffer) sum
|
|
189
|
-
const smaVal =
|
|
190
|
-
let devSum =
|
|
191
|
-
for (const v of buffer) devSum
|
|
192
|
-
const meanDev =
|
|
193
|
-
if (
|
|
194
|
-
const
|
|
195
|
-
|
|
262
|
+
if (n < period) return fp18.toDnum(fp18.ZERO);
|
|
263
|
+
let sum = fp18.ZERO;
|
|
264
|
+
for (const v of buffer) sum += v;
|
|
265
|
+
const smaVal = sum / periodBig;
|
|
266
|
+
let devSum = fp18.ZERO;
|
|
267
|
+
for (const v of buffer) devSum += fp18.abs(v - smaVal);
|
|
268
|
+
const meanDev = devSum / periodBig;
|
|
269
|
+
if (meanDev === fp18.ZERO) return fp18.toDnum(fp18.ZERO);
|
|
270
|
+
const numerator = buffer[n - 1] - smaVal;
|
|
271
|
+
const lambertMeanDev = fp18.mul(LAMBERT, meanDev);
|
|
272
|
+
return fp18.toDnum(fp18.div(numerator, lambertMeanDev));
|
|
196
273
|
};
|
|
197
274
|
}, defaultCCIOptions);
|
|
198
275
|
|
|
@@ -228,18 +305,19 @@ const ppo = createSignal(({ fastPeriod, slowPeriod, signalPeriod }) => {
|
|
|
228
305
|
assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`));
|
|
229
306
|
assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`));
|
|
230
307
|
assert(Number.isInteger(signalPeriod) && signalPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected signalPeriod to be a positive integer, got ${signalPeriod}`));
|
|
231
|
-
const fastProc =
|
|
232
|
-
const slowProc =
|
|
233
|
-
const signalProc =
|
|
308
|
+
const fastProc = ewma(ewma.k(fastPeriod));
|
|
309
|
+
const slowProc = ewma(ewma.k(slowPeriod));
|
|
310
|
+
const signalProc = ewma(ewma.k(signalPeriod));
|
|
234
311
|
return (value) => {
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
const
|
|
312
|
+
const v = fp18.toFp18(value);
|
|
313
|
+
const fast = fastProc(v);
|
|
314
|
+
const slow = slowProc(v);
|
|
315
|
+
const ppoVal = slow === fp18.ZERO ? fp18.ZERO : fp18.div((fast - slow) * 100n, slow);
|
|
238
316
|
const sig = signalProc(ppoVal);
|
|
239
317
|
return {
|
|
240
|
-
ppo: ppoVal,
|
|
241
|
-
signal: sig,
|
|
242
|
-
histogram:
|
|
318
|
+
ppo: fp18.toDnum(ppoVal),
|
|
319
|
+
signal: fp18.toDnum(sig),
|
|
320
|
+
histogram: fp18.toDnum(ppoVal - sig)
|
|
243
321
|
};
|
|
244
322
|
};
|
|
245
323
|
}, defaultPercentagePriceOscillatorOptions);
|
|
@@ -280,18 +358,19 @@ const pvo = createSignal(({ fastPeriod, slowPeriod, signalPeriod }) => {
|
|
|
280
358
|
assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`));
|
|
281
359
|
assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`));
|
|
282
360
|
assert(Number.isInteger(signalPeriod) && signalPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected signalPeriod to be a positive integer, got ${signalPeriod}`));
|
|
283
|
-
const fastProc =
|
|
284
|
-
const slowProc =
|
|
285
|
-
const signalProc =
|
|
361
|
+
const fastProc = ewma(ewma.k(fastPeriod));
|
|
362
|
+
const slowProc = ewma(ewma.k(slowPeriod));
|
|
363
|
+
const signalProc = ewma(ewma.k(signalPeriod));
|
|
286
364
|
return (value) => {
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
const
|
|
365
|
+
const v = fp18.toFp18(value);
|
|
366
|
+
const fast = fastProc(v);
|
|
367
|
+
const slow = slowProc(v);
|
|
368
|
+
const pvoVal = slow === fp18.ZERO ? fp18.ZERO : fp18.div((fast - slow) * 100n, slow);
|
|
290
369
|
const sig = signalProc(pvoVal);
|
|
291
370
|
return {
|
|
292
|
-
pvo: pvoVal,
|
|
293
|
-
signal: sig,
|
|
294
|
-
histogram:
|
|
371
|
+
pvo: fp18.toDnum(pvoVal),
|
|
372
|
+
signal: fp18.toDnum(sig),
|
|
373
|
+
histogram: fp18.toDnum(pvoVal - sig)
|
|
295
374
|
};
|
|
296
375
|
};
|
|
297
376
|
}, defaultPercentageVolumeOscillatorOptions);
|
|
@@ -319,52 +398,21 @@ const roc = createSignal(({ period }) => {
|
|
|
319
398
|
let head = 0;
|
|
320
399
|
let count = 0;
|
|
321
400
|
return (value) => {
|
|
322
|
-
const v =
|
|
401
|
+
const v = fp18.toFp18(value);
|
|
323
402
|
if (count < period) {
|
|
324
403
|
buffer[count] = v;
|
|
325
404
|
count++;
|
|
326
|
-
return
|
|
405
|
+
return fp18.toDnum(fp18.ZERO);
|
|
327
406
|
} else {
|
|
328
407
|
const oldest = buffer[head];
|
|
329
408
|
buffer[head] = v;
|
|
330
409
|
head = (head + 1) % period;
|
|
331
|
-
|
|
410
|
+
if (oldest === fp18.ZERO) return fp18.toDnum(fp18.ZERO);
|
|
411
|
+
return fp18.toDnum(fp18.div((v - oldest) * 100n, oldest));
|
|
332
412
|
}
|
|
333
413
|
};
|
|
334
414
|
}, defaultPriceRateOfChangeOptions);
|
|
335
415
|
|
|
336
|
-
//#endregion
|
|
337
|
-
//#region src/trend/movingMax.ts
|
|
338
|
-
const defaultMovingMaxOptions = { period: 4 };
|
|
339
|
-
/**
|
|
340
|
-
* Moving Maximum (MovingMax)
|
|
341
|
-
*/
|
|
342
|
-
const mmax = createSignal(({ period }) => {
|
|
343
|
-
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
344
|
-
const buffer = [];
|
|
345
|
-
return (value) => {
|
|
346
|
-
buffer.push(toDnum(value));
|
|
347
|
-
if (buffer.length > period) buffer.shift();
|
|
348
|
-
return buffer.reduce((max, cur) => gt(max, cur) ? max : cur);
|
|
349
|
-
};
|
|
350
|
-
}, defaultMovingMaxOptions);
|
|
351
|
-
|
|
352
|
-
//#endregion
|
|
353
|
-
//#region src/trend/movingMin.ts
|
|
354
|
-
const defaultMovingMinOptions = { period: 4 };
|
|
355
|
-
/**
|
|
356
|
-
* Moving Minimum (MovingMin)
|
|
357
|
-
*/
|
|
358
|
-
const mmin = createSignal(({ period }) => {
|
|
359
|
-
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
360
|
-
const buffer = [];
|
|
361
|
-
return (value) => {
|
|
362
|
-
buffer.push(toDnum(value));
|
|
363
|
-
if (buffer.length > period) buffer.shift();
|
|
364
|
-
return buffer.reduce((min, cur) => lt(min, cur) ? min : cur);
|
|
365
|
-
};
|
|
366
|
-
}, defaultMovingMinOptions);
|
|
367
|
-
|
|
368
416
|
//#endregion
|
|
369
417
|
//#region src/momentum/randomIndex.ts
|
|
370
418
|
const defaultRandomIndexOptions = {
|
|
@@ -380,9 +428,9 @@ const defaultRandomIndexOptions = {
|
|
|
380
428
|
* initial K and D values set to 50.
|
|
381
429
|
*
|
|
382
430
|
* Formula:
|
|
383
|
-
* RSV = (Close - LowestLow(period)) / (HighestHigh(period) - LowestLow(period))
|
|
384
|
-
* K = ((kPeriod - 1) / kPeriod)
|
|
385
|
-
* D = ((dPeriod - 1) / dPeriod)
|
|
431
|
+
* RSV = (Close - LowestLow(period)) / (HighestHigh(period) - LowestLow(period)) * 100
|
|
432
|
+
* K = ((kPeriod - 1) / kPeriod) * prevK + (1 / kPeriod) * RSV
|
|
433
|
+
* D = ((dPeriod - 1) / dPeriod) * prevD + (1 / dPeriod) * K
|
|
386
434
|
* J = 3K - 2D
|
|
387
435
|
*
|
|
388
436
|
* Interpretation:
|
|
@@ -401,69 +449,43 @@ const kdj = createSignal(({ period, kPeriod, dPeriod }) => {
|
|
|
401
449
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
402
450
|
assert(Number.isInteger(kPeriod) && kPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected kPeriod to be a positive integer, got ${kPeriod}`));
|
|
403
451
|
assert(Number.isInteger(dPeriod) && dPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected dPeriod to be a positive integer, got ${dPeriod}`));
|
|
404
|
-
const mmaxProc = mmax
|
|
405
|
-
const mminProc = mmin
|
|
406
|
-
const INITIAL =
|
|
407
|
-
const
|
|
452
|
+
const mmaxProc = mmax$1(period);
|
|
453
|
+
const mminProc = mmin$1(period);
|
|
454
|
+
const INITIAL = 50n * fp18.SCALE;
|
|
455
|
+
const kPeriodBig = BigInt(kPeriod);
|
|
456
|
+
const dPeriodBig = BigInt(dPeriod);
|
|
408
457
|
let prevK = INITIAL;
|
|
409
458
|
let prevD = INITIAL;
|
|
410
459
|
let isFirst = true;
|
|
411
460
|
return (bar) => {
|
|
412
|
-
const h =
|
|
413
|
-
const l =
|
|
414
|
-
const c =
|
|
461
|
+
const h = fp18.toFp18(bar.h);
|
|
462
|
+
const l = fp18.toFp18(bar.l);
|
|
463
|
+
const c = fp18.toFp18(bar.c);
|
|
415
464
|
const highestHigh = mmaxProc(h);
|
|
416
465
|
const lowestLow = mminProc(l);
|
|
417
|
-
const range =
|
|
418
|
-
const rsv =
|
|
466
|
+
const range = highestHigh - lowestLow;
|
|
467
|
+
const rsv = range === fp18.ZERO ? fp18.ZERO : fp18.div((c - lowestLow) * 100n, range);
|
|
419
468
|
let k;
|
|
420
469
|
let d;
|
|
421
470
|
if (isFirst) {
|
|
422
|
-
k =
|
|
423
|
-
d =
|
|
471
|
+
k = INITIAL * (kPeriodBig - 1n) / kPeriodBig + rsv / kPeriodBig;
|
|
472
|
+
d = INITIAL * (dPeriodBig - 1n) / dPeriodBig + k / dPeriodBig;
|
|
424
473
|
isFirst = false;
|
|
425
474
|
} else {
|
|
426
|
-
k =
|
|
427
|
-
d =
|
|
475
|
+
k = prevK * (kPeriodBig - 1n) / kPeriodBig + rsv / kPeriodBig;
|
|
476
|
+
d = prevD * (dPeriodBig - 1n) / dPeriodBig + k / dPeriodBig;
|
|
428
477
|
}
|
|
429
|
-
const j =
|
|
478
|
+
const j = k * 3n - d * 2n;
|
|
430
479
|
prevK = k;
|
|
431
480
|
prevD = d;
|
|
432
481
|
return {
|
|
433
|
-
k,
|
|
434
|
-
d,
|
|
435
|
-
j
|
|
482
|
+
k: fp18.toDnum(k),
|
|
483
|
+
d: fp18.toDnum(d),
|
|
484
|
+
j: fp18.toDnum(j)
|
|
436
485
|
};
|
|
437
486
|
};
|
|
438
487
|
}, defaultRandomIndexOptions);
|
|
439
488
|
|
|
440
|
-
//#endregion
|
|
441
|
-
//#region src/trend/rollingMovingAverage.ts
|
|
442
|
-
const defaultRMAOptions = { period: 4 };
|
|
443
|
-
/**
|
|
444
|
-
* Rolling moving average (RMA).
|
|
445
|
-
*
|
|
446
|
-
* R[0] to R[p-1] is SMA(values)
|
|
447
|
-
*
|
|
448
|
-
* R[p] and after is R[i] = ((R[i-1]*(p-1)) + v[i]) / p
|
|
449
|
-
*/
|
|
450
|
-
const rma = createSignal(({ period }) => {
|
|
451
|
-
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
452
|
-
let count = 0;
|
|
453
|
-
let sum = constants.ZERO;
|
|
454
|
-
let prev = constants.ZERO;
|
|
455
|
-
return (value) => {
|
|
456
|
-
if (count < period) {
|
|
457
|
-
sum = add(sum, value);
|
|
458
|
-
count++;
|
|
459
|
-
prev = div(sum, count, constants.DECIMALS);
|
|
460
|
-
return prev;
|
|
461
|
-
}
|
|
462
|
-
prev = div(add(mul(prev, period - 1, constants.DECIMALS), value), period, constants.DECIMALS);
|
|
463
|
-
return prev;
|
|
464
|
-
};
|
|
465
|
-
}, defaultRMAOptions);
|
|
466
|
-
|
|
467
489
|
//#endregion
|
|
468
490
|
//#region src/momentum/relativeStrengthIndex.ts
|
|
469
491
|
const defaultRSIOptions = { period: 14 };
|
|
@@ -478,25 +500,26 @@ const defaultRSIOptions = { period: 14 };
|
|
|
478
500
|
*/
|
|
479
501
|
const rsi = createSignal(({ period }) => {
|
|
480
502
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
481
|
-
const gainProc = rma
|
|
482
|
-
const lossProc = rma
|
|
503
|
+
const gainProc = rma$1(period);
|
|
504
|
+
const lossProc = rma$1(period);
|
|
483
505
|
let prev;
|
|
484
506
|
return (value) => {
|
|
485
|
-
const price =
|
|
507
|
+
const price = fp18.toFp18(value);
|
|
486
508
|
if (prev === void 0) {
|
|
487
509
|
prev = price;
|
|
488
|
-
gainProc(
|
|
489
|
-
lossProc(
|
|
490
|
-
return
|
|
510
|
+
gainProc(fp18.ZERO);
|
|
511
|
+
lossProc(fp18.ZERO);
|
|
512
|
+
return fp18.toDnum(fp18.ZERO);
|
|
491
513
|
}
|
|
492
|
-
const change =
|
|
514
|
+
const change = price - prev;
|
|
493
515
|
prev = price;
|
|
494
|
-
const gain =
|
|
495
|
-
const loss =
|
|
516
|
+
const gain = change > fp18.ZERO ? change : fp18.ZERO;
|
|
517
|
+
const loss = change > fp18.ZERO ? fp18.ZERO : -change;
|
|
496
518
|
const avgGain = gainProc(gain);
|
|
497
519
|
const avgLoss = lossProc(loss);
|
|
498
|
-
if (
|
|
499
|
-
|
|
520
|
+
if (avgLoss === fp18.ZERO) return fp18.toDnum(fp18.HUNDRED);
|
|
521
|
+
const rs = fp18.div(avgGain, avgLoss);
|
|
522
|
+
return fp18.toDnum(fp18.HUNDRED - fp18.div(fp18.HUNDRED, fp18.ONE + rs));
|
|
500
523
|
};
|
|
501
524
|
}, defaultRSIOptions);
|
|
502
525
|
|
|
@@ -517,22 +540,23 @@ const stoch = createSignal(({ kPeriod, slowingPeriod, dPeriod }) => {
|
|
|
517
540
|
assert(Number.isInteger(kPeriod) && kPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected kPeriod to be a positive integer, got ${kPeriod}`));
|
|
518
541
|
assert(Number.isInteger(slowingPeriod) && slowingPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected slowingPeriod to be a positive integer, got ${slowingPeriod}`));
|
|
519
542
|
assert(Number.isInteger(dPeriod) && dPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected dPeriod to be a positive integer, got ${dPeriod}`));
|
|
520
|
-
const mmaxProc = mmax
|
|
521
|
-
const mminProc = mmin
|
|
522
|
-
const slowingProc = slowingPeriod > 1 ? sma
|
|
523
|
-
const dProc = sma
|
|
543
|
+
const mmaxProc = mmax$1(kPeriod);
|
|
544
|
+
const mminProc = mmin$1(kPeriod);
|
|
545
|
+
const slowingProc = slowingPeriod > 1 ? sma$1(slowingPeriod) : null;
|
|
546
|
+
const dProc = sma$1(dPeriod);
|
|
524
547
|
return (bar) => {
|
|
525
|
-
const h =
|
|
526
|
-
const l =
|
|
527
|
-
const c =
|
|
548
|
+
const h = fp18.toFp18(bar.h);
|
|
549
|
+
const l = fp18.toFp18(bar.l);
|
|
550
|
+
const c = fp18.toFp18(bar.c);
|
|
528
551
|
const highestHigh = mmaxProc(h);
|
|
529
552
|
const lowestLow = mminProc(l);
|
|
530
|
-
const range =
|
|
531
|
-
const rawK =
|
|
553
|
+
const range = highestHigh - lowestLow;
|
|
554
|
+
const rawK = range === fp18.ZERO ? fp18.ZERO : fp18.div((c - lowestLow) * 100n, range);
|
|
532
555
|
const k = slowingProc ? slowingProc(rawK) : rawK;
|
|
556
|
+
const d = dProc(k);
|
|
533
557
|
return {
|
|
534
|
-
k,
|
|
535
|
-
d:
|
|
558
|
+
k: fp18.toDnum(k),
|
|
559
|
+
d: fp18.toDnum(d)
|
|
536
560
|
};
|
|
537
561
|
};
|
|
538
562
|
}, defaultStochasticOscillatorOptions);
|
|
@@ -548,7 +572,7 @@ const defaultWilliamsROptions = { period: 14 };
|
|
|
548
572
|
* over a specified period.
|
|
549
573
|
*
|
|
550
574
|
* Formula:
|
|
551
|
-
* - %R = -100
|
|
575
|
+
* - %R = -100 * (Highest High - Close) / (Highest High - Lowest Low)
|
|
552
576
|
*
|
|
553
577
|
* Range: -100 to 0
|
|
554
578
|
* - Above -20: Overbought
|
|
@@ -561,15 +585,16 @@ const defaultWilliamsROptions = { period: 14 };
|
|
|
561
585
|
*/
|
|
562
586
|
const willr = createSignal(({ period }) => {
|
|
563
587
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
564
|
-
const mmaxProc = mmax
|
|
565
|
-
const mminProc = mmin
|
|
588
|
+
const mmaxProc = mmax$1(period);
|
|
589
|
+
const mminProc = mmin$1(period);
|
|
566
590
|
return (bar) => {
|
|
567
|
-
const h =
|
|
568
|
-
const l =
|
|
569
|
-
const c =
|
|
591
|
+
const h = fp18.toFp18(bar.h);
|
|
592
|
+
const l = fp18.toFp18(bar.l);
|
|
593
|
+
const c = fp18.toFp18(bar.c);
|
|
570
594
|
const highestHigh = mmaxProc(h);
|
|
571
|
-
const range =
|
|
572
|
-
|
|
595
|
+
const range = highestHigh - mminProc(l);
|
|
596
|
+
if (range === fp18.ZERO) return fp18.toDnum(fp18.ZERO);
|
|
597
|
+
return fp18.toDnum(fp18.div((highestHigh - c) * -100n, range));
|
|
573
598
|
};
|
|
574
599
|
}, defaultWilliamsROptions);
|
|
575
600
|
|
|
@@ -587,9 +612,10 @@ const aroon = createSignal(({ period }) => {
|
|
|
587
612
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
588
613
|
const highBuffer = [];
|
|
589
614
|
const lowBuffer = [];
|
|
615
|
+
const periodBig = BigInt(period);
|
|
590
616
|
return (bar) => {
|
|
591
|
-
const h =
|
|
592
|
-
const l =
|
|
617
|
+
const h = fp18.toFp18(bar.h);
|
|
618
|
+
const l = fp18.toFp18(bar.l);
|
|
593
619
|
highBuffer.push(h);
|
|
594
620
|
lowBuffer.push(l);
|
|
595
621
|
if (highBuffer.length > period + 1) highBuffer.shift();
|
|
@@ -597,18 +623,17 @@ const aroon = createSignal(({ period }) => {
|
|
|
597
623
|
let highestIdx = 0;
|
|
598
624
|
let lowestIdx = 0;
|
|
599
625
|
for (let j = 1; j < highBuffer.length; j++) {
|
|
600
|
-
if (
|
|
601
|
-
if (
|
|
626
|
+
if (highBuffer[j] >= highBuffer[highestIdx]) highestIdx = j;
|
|
627
|
+
if (lowBuffer[j] <= lowBuffer[lowestIdx]) lowestIdx = j;
|
|
602
628
|
}
|
|
603
629
|
const daysSinceHigh = highBuffer.length - 1 - highestIdx;
|
|
604
630
|
const daysSinceLow = lowBuffer.length - 1 - lowestIdx;
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
const down = divide(multiply(toDnum(period - daysSinceLow), 100, constants.DECIMALS), periodDnum, constants.DECIMALS);
|
|
631
|
+
const up = BigInt(period - daysSinceHigh) * fp18.HUNDRED / periodBig;
|
|
632
|
+
const down = BigInt(period - daysSinceLow) * fp18.HUNDRED / periodBig;
|
|
608
633
|
return {
|
|
609
|
-
up,
|
|
610
|
-
down,
|
|
611
|
-
oscillator:
|
|
634
|
+
up: fp18.toDnum(up),
|
|
635
|
+
down: fp18.toDnum(down),
|
|
636
|
+
oscillator: fp18.toDnum(up - down)
|
|
612
637
|
};
|
|
613
638
|
};
|
|
614
639
|
}, defaultAroonOptions);
|
|
@@ -617,13 +642,13 @@ const aroon = createSignal(({ period }) => {
|
|
|
617
642
|
//#region src/trend/balanceOfPower.ts
|
|
618
643
|
const bop = createSignal(() => {
|
|
619
644
|
return (bar) => {
|
|
620
|
-
const o =
|
|
621
|
-
const h =
|
|
622
|
-
const l =
|
|
623
|
-
const c =
|
|
624
|
-
const range =
|
|
625
|
-
if (
|
|
626
|
-
return
|
|
645
|
+
const o = fp18.toFp18(bar.o);
|
|
646
|
+
const h = fp18.toFp18(bar.h);
|
|
647
|
+
const l = fp18.toFp18(bar.l);
|
|
648
|
+
const c = fp18.toFp18(bar.c);
|
|
649
|
+
const range = h - l;
|
|
650
|
+
if (range === fp18.ZERO) return fp18.toDnum(fp18.ZERO);
|
|
651
|
+
return fp18.toDnum(fp18.div(c - o, range));
|
|
627
652
|
};
|
|
628
653
|
});
|
|
629
654
|
|
|
@@ -649,24 +674,25 @@ const cfo = createSignal(({ period }) => {
|
|
|
649
674
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
650
675
|
const buffer = [];
|
|
651
676
|
return (value) => {
|
|
652
|
-
buffer.push(
|
|
677
|
+
buffer.push(fp18.toFp18(value));
|
|
653
678
|
if (buffer.length > period) buffer.shift();
|
|
654
679
|
const n = buffer.length;
|
|
655
|
-
if (n < 2) return
|
|
656
|
-
const xSum = n * (n + 1) / 2;
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
let
|
|
680
|
+
if (n < 2) return fp18.toDnum(fp18.ZERO);
|
|
681
|
+
const xSum = BigInt(n * (n + 1) / 2);
|
|
682
|
+
const x2Sum = BigInt(n * (n + 1) * (2 * n + 1) / 6);
|
|
683
|
+
const denom = BigInt(n) * x2Sum - xSum * xSum;
|
|
684
|
+
let sumY = fp18.ZERO;
|
|
685
|
+
let sumXY = fp18.ZERO;
|
|
660
686
|
for (let i = 0; i < n; i++) {
|
|
661
|
-
sumY
|
|
662
|
-
sumXY
|
|
687
|
+
sumY += buffer[i];
|
|
688
|
+
sumXY += buffer[i] * BigInt(i + 1);
|
|
663
689
|
}
|
|
664
|
-
const slope =
|
|
665
|
-
const intercept =
|
|
666
|
-
const forecast =
|
|
690
|
+
const slope = (sumXY * BigInt(n) - sumY * xSum) / denom;
|
|
691
|
+
const intercept = (sumY - slope * xSum) / BigInt(n);
|
|
692
|
+
const forecast = slope * BigInt(n) + intercept;
|
|
667
693
|
const close = buffer[n - 1];
|
|
668
|
-
if (
|
|
669
|
-
return
|
|
694
|
+
if (close === fp18.ZERO) return fp18.toDnum(fp18.ZERO);
|
|
695
|
+
return fp18.toDnum(fp18.div((close - forecast) * 100n, close));
|
|
670
696
|
};
|
|
671
697
|
}, defaultCFOOptions);
|
|
672
698
|
|
|
@@ -686,15 +712,30 @@ const defaultDoubleExponentialMovingAverageOptions = { period: 12 };
|
|
|
686
712
|
*/
|
|
687
713
|
const dema = createSignal(({ period }) => {
|
|
688
714
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
689
|
-
const ema1 =
|
|
690
|
-
const ema2 =
|
|
715
|
+
const ema1 = ewma(ewma.k(period));
|
|
716
|
+
const ema2 = ewma(ewma.k(period));
|
|
691
717
|
return (value) => {
|
|
692
|
-
const e1 = ema1(value);
|
|
718
|
+
const e1 = ema1(fp18.toFp18(value));
|
|
693
719
|
const e2 = ema2(e1);
|
|
694
|
-
return
|
|
720
|
+
return fp18.toDnum(e1 * 2n - e2);
|
|
695
721
|
};
|
|
696
722
|
}, defaultDoubleExponentialMovingAverageOptions);
|
|
697
723
|
|
|
724
|
+
//#endregion
|
|
725
|
+
//#region src/trend/exponentialMovingAverage.ts
|
|
726
|
+
const defaultExponentialMovingAverageOptions = { period: 12 };
|
|
727
|
+
/**
|
|
728
|
+
* Exponential Moving Average (EMA)
|
|
729
|
+
*
|
|
730
|
+
* EMA = Price * k + PrevEMA * (1 - k)
|
|
731
|
+
* Where k = 2 / (period + 1)
|
|
732
|
+
*/
|
|
733
|
+
const ema = createSignal(({ period }) => {
|
|
734
|
+
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
735
|
+
const proc = ewma(ewma.k(period));
|
|
736
|
+
return (value) => fp18.toDnum(proc(fp18.toFp18(value)));
|
|
737
|
+
}, defaultExponentialMovingAverageOptions);
|
|
738
|
+
|
|
698
739
|
//#endregion
|
|
699
740
|
//#region src/trend/ichimokuCloud.ts
|
|
700
741
|
const defaultIchimokuCloudOptions = {
|
|
@@ -719,23 +760,25 @@ const ichimokuCloud = createSignal(({ conversionPeriod, basePeriod, leadingBPeri
|
|
|
719
760
|
assert(Number.isInteger(conversionPeriod) && conversionPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected conversionPeriod to be a positive integer, got ${conversionPeriod}`));
|
|
720
761
|
assert(Number.isInteger(basePeriod) && basePeriod >= 1, /* @__PURE__ */ new RangeError(`Expected basePeriod to be a positive integer, got ${basePeriod}`));
|
|
721
762
|
assert(Number.isInteger(leadingBPeriod) && leadingBPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected leadingBPeriod to be a positive integer, got ${leadingBPeriod}`));
|
|
722
|
-
const convHighProc = mmax
|
|
723
|
-
const convLowProc = mmin
|
|
724
|
-
const baseHighProc = mmax
|
|
725
|
-
const baseLowProc = mmin
|
|
726
|
-
const leadBHighProc = mmax
|
|
727
|
-
const leadBLowProc = mmin
|
|
763
|
+
const convHighProc = mmax$1(conversionPeriod);
|
|
764
|
+
const convLowProc = mmin$1(conversionPeriod);
|
|
765
|
+
const baseHighProc = mmax$1(basePeriod);
|
|
766
|
+
const baseLowProc = mmin$1(basePeriod);
|
|
767
|
+
const leadBHighProc = mmax$1(leadingBPeriod);
|
|
768
|
+
const leadBLowProc = mmin$1(leadingBPeriod);
|
|
728
769
|
return (bar) => {
|
|
729
|
-
const h =
|
|
730
|
-
const l =
|
|
731
|
-
const conversion =
|
|
732
|
-
const base =
|
|
770
|
+
const h = fp18.toFp18(bar.h);
|
|
771
|
+
const l = fp18.toFp18(bar.l);
|
|
772
|
+
const conversion = (convHighProc(h) + convLowProc(l)) / 2n;
|
|
773
|
+
const base = (baseHighProc(h) + baseLowProc(l)) / 2n;
|
|
774
|
+
const leadingA = (conversion + base) / 2n;
|
|
775
|
+
const leadingB = (leadBHighProc(h) + leadBLowProc(l)) / 2n;
|
|
733
776
|
return {
|
|
734
|
-
conversion,
|
|
735
|
-
base,
|
|
736
|
-
leadingA:
|
|
737
|
-
leadingB:
|
|
738
|
-
lagging:
|
|
777
|
+
conversion: fp18.toDnum(conversion),
|
|
778
|
+
base: fp18.toDnum(base),
|
|
779
|
+
leadingA: fp18.toDnum(leadingA),
|
|
780
|
+
leadingB: fp18.toDnum(leadingB),
|
|
781
|
+
lagging: fp18.toDnum(fp18.toFp18(bar.c))
|
|
739
782
|
};
|
|
740
783
|
};
|
|
741
784
|
}, defaultIchimokuCloudOptions);
|
|
@@ -772,20 +815,43 @@ const macd = createSignal(({ fastPeriod, slowPeriod, signalPeriod }) => {
|
|
|
772
815
|
assert(Number.isInteger(fastPeriod) && fastPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected fastPeriod to be a positive integer, got ${fastPeriod}`));
|
|
773
816
|
assert(Number.isInteger(slowPeriod) && slowPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected slowPeriod to be a positive integer, got ${slowPeriod}`));
|
|
774
817
|
assert(Number.isInteger(signalPeriod) && signalPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected signalPeriod to be a positive integer, got ${signalPeriod}`));
|
|
775
|
-
const fastProc =
|
|
776
|
-
const slowProc =
|
|
777
|
-
const signalProc =
|
|
818
|
+
const fastProc = ewma(ewma.k(fastPeriod));
|
|
819
|
+
const slowProc = ewma(ewma.k(slowPeriod));
|
|
820
|
+
const signalProc = ewma(ewma.k(signalPeriod));
|
|
778
821
|
return (value) => {
|
|
779
|
-
const
|
|
822
|
+
const v = fp18.toFp18(value);
|
|
823
|
+
const m = fastProc(v) - slowProc(v);
|
|
780
824
|
const sig = signalProc(m);
|
|
781
825
|
return {
|
|
782
|
-
macd: m,
|
|
783
|
-
signal: sig,
|
|
784
|
-
histogram:
|
|
826
|
+
macd: fp18.toDnum(m),
|
|
827
|
+
signal: fp18.toDnum(sig),
|
|
828
|
+
histogram: fp18.toDnum(m - sig)
|
|
785
829
|
};
|
|
786
830
|
};
|
|
787
831
|
}, defaultMACDOptions);
|
|
788
832
|
|
|
833
|
+
//#endregion
|
|
834
|
+
//#region src/trend/movingMax.ts
|
|
835
|
+
const defaultMovingMaxOptions = { period: 4 };
|
|
836
|
+
/**
|
|
837
|
+
* Moving Maximum (MovingMax)
|
|
838
|
+
*/
|
|
839
|
+
const mmax = createSignal(({ period }) => {
|
|
840
|
+
const proc = mmax$1(period);
|
|
841
|
+
return (value) => fp18.toDnum(proc(fp18.toFp18(value)));
|
|
842
|
+
}, defaultMovingMaxOptions);
|
|
843
|
+
|
|
844
|
+
//#endregion
|
|
845
|
+
//#region src/trend/movingMin.ts
|
|
846
|
+
const defaultMovingMinOptions = { period: 4 };
|
|
847
|
+
/**
|
|
848
|
+
* Moving Minimum (MovingMin)
|
|
849
|
+
*/
|
|
850
|
+
const mmin = createSignal(({ period }) => {
|
|
851
|
+
const proc = mmin$1(period);
|
|
852
|
+
return (value) => fp18.toDnum(proc(fp18.toFp18(value)));
|
|
853
|
+
}, defaultMovingMinOptions);
|
|
854
|
+
|
|
789
855
|
//#endregion
|
|
790
856
|
//#region src/trend/movingSum.ts
|
|
791
857
|
const defaultMovingSumOptions = { period: 4 };
|
|
@@ -795,25 +861,8 @@ const defaultMovingSumOptions = { period: 4 };
|
|
|
795
861
|
* Calculates the sum of values in a sliding window of the specified period.
|
|
796
862
|
*/
|
|
797
863
|
const msum = createSignal(({ period }) => {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
let head = 0;
|
|
801
|
-
let count = 0;
|
|
802
|
-
let runningSum = constants.ZERO;
|
|
803
|
-
return (value) => {
|
|
804
|
-
const v = toDnum(value);
|
|
805
|
-
if (count < period) {
|
|
806
|
-
buffer[count] = v;
|
|
807
|
-
runningSum = add(runningSum, v);
|
|
808
|
-
count++;
|
|
809
|
-
} else {
|
|
810
|
-
runningSum = subtract(runningSum, buffer[head]);
|
|
811
|
-
runningSum = add(runningSum, v);
|
|
812
|
-
buffer[head] = v;
|
|
813
|
-
head = (head + 1) % period;
|
|
814
|
-
}
|
|
815
|
-
return runningSum;
|
|
816
|
-
};
|
|
864
|
+
const proc = msum$1(period);
|
|
865
|
+
return (value) => fp18.toDnum(proc(fp18.toFp18(value)));
|
|
817
866
|
}, defaultMovingSumOptions);
|
|
818
867
|
|
|
819
868
|
//#endregion
|
|
@@ -862,12 +911,12 @@ const psar = createSignal(({ start, increment, max }) => {
|
|
|
862
911
|
let prevLow;
|
|
863
912
|
let prevPrevHigh;
|
|
864
913
|
let prevPrevLow;
|
|
865
|
-
const afStart =
|
|
866
|
-
const afIncrement =
|
|
867
|
-
const afMax =
|
|
914
|
+
const afStart = fp18.toFp18(start);
|
|
915
|
+
const afIncrement = fp18.toFp18(increment);
|
|
916
|
+
const afMax = fp18.toFp18(max);
|
|
868
917
|
return (bar) => {
|
|
869
|
-
const h =
|
|
870
|
-
const l =
|
|
918
|
+
const h = fp18.toFp18(bar.h);
|
|
919
|
+
const l = fp18.toFp18(bar.l);
|
|
871
920
|
count++;
|
|
872
921
|
if (count === 1) {
|
|
873
922
|
isUptrend = true;
|
|
@@ -879,12 +928,12 @@ const psar = createSignal(({ start, increment, max }) => {
|
|
|
879
928
|
prevPrevHigh = h;
|
|
880
929
|
prevPrevLow = l;
|
|
881
930
|
return {
|
|
882
|
-
psar: sar,
|
|
931
|
+
psar: fp18.toDnum(sar),
|
|
883
932
|
isUptrend
|
|
884
933
|
};
|
|
885
934
|
}
|
|
886
935
|
if (count === 2) {
|
|
887
|
-
if (
|
|
936
|
+
if (h > prevHigh) {
|
|
888
937
|
isUptrend = true;
|
|
889
938
|
sar = prevLow;
|
|
890
939
|
ep = h;
|
|
@@ -899,27 +948,27 @@ const psar = createSignal(({ start, increment, max }) => {
|
|
|
899
948
|
prevHigh = h;
|
|
900
949
|
prevLow = l;
|
|
901
950
|
return {
|
|
902
|
-
psar: sar,
|
|
951
|
+
psar: fp18.toDnum(sar),
|
|
903
952
|
isUptrend
|
|
904
953
|
};
|
|
905
954
|
}
|
|
906
|
-
let nextSar =
|
|
955
|
+
let nextSar = sar + fp18.mul(af, ep - sar);
|
|
907
956
|
if (isUptrend) {
|
|
908
|
-
if (
|
|
909
|
-
if (
|
|
957
|
+
if (nextSar > prevLow) nextSar = prevLow;
|
|
958
|
+
if (nextSar > prevPrevLow) nextSar = prevPrevLow;
|
|
910
959
|
} else {
|
|
911
|
-
if (
|
|
912
|
-
if (
|
|
960
|
+
if (nextSar < prevHigh) nextSar = prevHigh;
|
|
961
|
+
if (nextSar < prevPrevHigh) nextSar = prevPrevHigh;
|
|
913
962
|
}
|
|
914
963
|
sar = nextSar;
|
|
915
964
|
let reversed = false;
|
|
916
|
-
if (isUptrend &&
|
|
965
|
+
if (isUptrend && l < sar) {
|
|
917
966
|
isUptrend = false;
|
|
918
967
|
sar = ep;
|
|
919
968
|
ep = l;
|
|
920
969
|
af = afStart;
|
|
921
970
|
reversed = true;
|
|
922
|
-
} else if (!isUptrend &&
|
|
971
|
+
} else if (!isUptrend && h > sar) {
|
|
923
972
|
isUptrend = true;
|
|
924
973
|
sar = ep;
|
|
925
974
|
ep = h;
|
|
@@ -927,14 +976,14 @@ const psar = createSignal(({ start, increment, max }) => {
|
|
|
927
976
|
reversed = true;
|
|
928
977
|
}
|
|
929
978
|
if (!reversed) {
|
|
930
|
-
if (isUptrend &&
|
|
979
|
+
if (isUptrend && h > ep) {
|
|
931
980
|
ep = h;
|
|
932
|
-
const newAf =
|
|
933
|
-
af =
|
|
934
|
-
} else if (!isUptrend &&
|
|
981
|
+
const newAf = af + afIncrement;
|
|
982
|
+
af = newAf > afMax ? afMax : newAf;
|
|
983
|
+
} else if (!isUptrend && l < ep) {
|
|
935
984
|
ep = l;
|
|
936
|
-
const newAf =
|
|
937
|
-
af =
|
|
985
|
+
const newAf = af + afIncrement;
|
|
986
|
+
af = newAf > afMax ? afMax : newAf;
|
|
938
987
|
}
|
|
939
988
|
}
|
|
940
989
|
prevPrevHigh = prevHigh;
|
|
@@ -942,7 +991,7 @@ const psar = createSignal(({ start, increment, max }) => {
|
|
|
942
991
|
prevHigh = h;
|
|
943
992
|
prevLow = l;
|
|
944
993
|
return {
|
|
945
|
-
psar: sar,
|
|
994
|
+
psar: fp18.toDnum(sar),
|
|
946
995
|
isUptrend
|
|
947
996
|
};
|
|
948
997
|
};
|
|
@@ -976,23 +1025,59 @@ const qstick = createSignal(({ period }) => {
|
|
|
976
1025
|
const buffer = Array.from({ length: period });
|
|
977
1026
|
let head = 0;
|
|
978
1027
|
let count = 0;
|
|
979
|
-
let runningSum =
|
|
1028
|
+
let runningSum = fp18.ZERO;
|
|
980
1029
|
return (bar) => {
|
|
981
|
-
const diff =
|
|
1030
|
+
const diff = fp18.toFp18(bar.c) - fp18.toFp18(bar.o);
|
|
982
1031
|
if (count < period) {
|
|
983
1032
|
buffer[count] = diff;
|
|
984
|
-
runningSum
|
|
1033
|
+
runningSum += diff;
|
|
985
1034
|
count++;
|
|
986
1035
|
} else {
|
|
987
|
-
runningSum =
|
|
988
|
-
runningSum = add(runningSum, diff);
|
|
1036
|
+
runningSum = runningSum - buffer[head] + diff;
|
|
989
1037
|
buffer[head] = diff;
|
|
990
1038
|
head = (head + 1) % period;
|
|
991
1039
|
}
|
|
992
|
-
return
|
|
1040
|
+
return fp18.toDnum(runningSum / BigInt(count));
|
|
993
1041
|
};
|
|
994
1042
|
}, defaultQstickOptions);
|
|
995
1043
|
|
|
1044
|
+
//#endregion
|
|
1045
|
+
//#region src/trend/rollingMovingAverage.ts
|
|
1046
|
+
const defaultRMAOptions = { period: 4 };
|
|
1047
|
+
/**
|
|
1048
|
+
* Rolling moving average (RMA).
|
|
1049
|
+
*
|
|
1050
|
+
* R[0] to R[p-1] is SMA(values)
|
|
1051
|
+
*
|
|
1052
|
+
* R[p] and after is R[i] = ((R[i-1]*(p-1)) + v[i]) / p
|
|
1053
|
+
*/
|
|
1054
|
+
const rma = createSignal(({ period }) => {
|
|
1055
|
+
const proc = rma$1(period);
|
|
1056
|
+
return (value) => fp18.toDnum(proc(fp18.toFp18(value)));
|
|
1057
|
+
}, defaultRMAOptions);
|
|
1058
|
+
|
|
1059
|
+
//#endregion
|
|
1060
|
+
//#region src/trend/simpleMovingAverage.ts
|
|
1061
|
+
const defaultSMAOptions = { period: 2 };
|
|
1062
|
+
/**
|
|
1063
|
+
* Simple Moving Average (SMA)
|
|
1064
|
+
*
|
|
1065
|
+
* Calculates the arithmetic mean of a set of values over a specified period.
|
|
1066
|
+
* The SMA is calculated by summing all values in the period and dividing by the period length.
|
|
1067
|
+
*
|
|
1068
|
+
* Formula: SMA = (P1 + P2 + ... + Pn) / n
|
|
1069
|
+
* Where: P = Price values, n = Period
|
|
1070
|
+
*
|
|
1071
|
+
* @param source - Iterable of price values
|
|
1072
|
+
* @param options - Configuration options
|
|
1073
|
+
* @param options.period - The period for calculating the moving average (default: 2)
|
|
1074
|
+
* @returns Generator yielding SMA values
|
|
1075
|
+
*/
|
|
1076
|
+
const sma = createSignal(({ period }) => {
|
|
1077
|
+
const proc = sma$1(period);
|
|
1078
|
+
return (value) => fp18.toDnum(proc(fp18.toFp18(value)));
|
|
1079
|
+
}, defaultSMAOptions);
|
|
1080
|
+
|
|
996
1081
|
//#endregion
|
|
997
1082
|
//#region src/trend/sinceChange.ts
|
|
998
1083
|
/**
|
|
@@ -1011,14 +1096,14 @@ const qstick = createSignal(({ period }) => {
|
|
|
1011
1096
|
*/
|
|
1012
1097
|
const since = createSignal(() => {
|
|
1013
1098
|
let last;
|
|
1014
|
-
let count =
|
|
1099
|
+
let count = fp18.ZERO;
|
|
1015
1100
|
return (value) => {
|
|
1016
|
-
const v =
|
|
1017
|
-
if (last === void 0 ||
|
|
1101
|
+
const v = fp18.toFp18(value);
|
|
1102
|
+
if (last === void 0 || last !== v) {
|
|
1018
1103
|
last = v;
|
|
1019
|
-
count =
|
|
1020
|
-
} else count
|
|
1021
|
-
return count;
|
|
1104
|
+
count = fp18.ZERO;
|
|
1105
|
+
} else count += fp18.ONE;
|
|
1106
|
+
return fp18.toDnum(count);
|
|
1022
1107
|
};
|
|
1023
1108
|
});
|
|
1024
1109
|
|
|
@@ -1036,10 +1121,11 @@ const trima = createSignal(({ period }) => {
|
|
|
1036
1121
|
n1 = (period + 1) / 2;
|
|
1037
1122
|
n2 = n1;
|
|
1038
1123
|
}
|
|
1039
|
-
const sma1 = sma
|
|
1040
|
-
const sma2 = sma
|
|
1124
|
+
const sma1 = sma$1(n2);
|
|
1125
|
+
const sma2 = sma$1(n1);
|
|
1041
1126
|
return (value) => {
|
|
1042
|
-
|
|
1127
|
+
const s1 = sma1(fp18.toFp18(value));
|
|
1128
|
+
return fp18.toDnum(sma2(s1));
|
|
1043
1129
|
};
|
|
1044
1130
|
}, defaultTriangularMovingAverageOptions);
|
|
1045
1131
|
|
|
@@ -1064,19 +1150,19 @@ const defaultTripleExponentialAverageOptions = { period: 15 };
|
|
|
1064
1150
|
*/
|
|
1065
1151
|
const trix = createSignal(({ period }) => {
|
|
1066
1152
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
1067
|
-
const ema1 =
|
|
1068
|
-
const ema2 =
|
|
1069
|
-
const ema3 =
|
|
1153
|
+
const ema1 = ewma(ewma.k(period));
|
|
1154
|
+
const ema2 = ewma(ewma.k(period));
|
|
1155
|
+
const ema3 = ewma(ewma.k(period));
|
|
1070
1156
|
let prevEma3 = null;
|
|
1071
1157
|
return (value) => {
|
|
1072
|
-
const e3 = ema3(ema2(ema1(value)));
|
|
1158
|
+
const e3 = ema3(ema2(ema1(fp18.toFp18(value))));
|
|
1073
1159
|
if (prevEma3 === null) {
|
|
1074
1160
|
prevEma3 = e3;
|
|
1075
|
-
return
|
|
1161
|
+
return fp18.toDnum(fp18.ZERO);
|
|
1076
1162
|
}
|
|
1077
|
-
const result =
|
|
1163
|
+
const result = fp18.div((e3 - prevEma3) * 100n, prevEma3);
|
|
1078
1164
|
prevEma3 = e3;
|
|
1079
|
-
return result;
|
|
1165
|
+
return fp18.toDnum(result);
|
|
1080
1166
|
};
|
|
1081
1167
|
}, defaultTripleExponentialAverageOptions);
|
|
1082
1168
|
|
|
@@ -1096,14 +1182,14 @@ const defaultTripleExponentialMovingAverageOptions = { period: 12 };
|
|
|
1096
1182
|
*/
|
|
1097
1183
|
const tema = createSignal(({ period }) => {
|
|
1098
1184
|
assert(Number.isInteger(period) && period >= 1, /* @__PURE__ */ new RangeError(`Expected period to be a positive integer, got ${period}`));
|
|
1099
|
-
const ema1 =
|
|
1100
|
-
const ema2 =
|
|
1101
|
-
const ema3 =
|
|
1185
|
+
const ema1 = ewma(ewma.k(period));
|
|
1186
|
+
const ema2 = ewma(ewma.k(period));
|
|
1187
|
+
const ema3 = ewma(ewma.k(period));
|
|
1102
1188
|
return (value) => {
|
|
1103
|
-
const e1 = ema1(value);
|
|
1189
|
+
const e1 = ema1(fp18.toFp18(value));
|
|
1104
1190
|
const e2 = ema2(e1);
|
|
1105
1191
|
const e3 = ema3(e2);
|
|
1106
|
-
return
|
|
1192
|
+
return fp18.toDnum(e1 * 3n - e2 * 3n + e3);
|
|
1107
1193
|
};
|
|
1108
1194
|
}, defaultTripleExponentialMovingAverageOptions);
|
|
1109
1195
|
|
|
@@ -1123,10 +1209,10 @@ const tema = createSignal(({ period }) => {
|
|
|
1123
1209
|
*/
|
|
1124
1210
|
const typicalPrice = createSignal(() => {
|
|
1125
1211
|
return (bar) => {
|
|
1126
|
-
const h =
|
|
1127
|
-
const l =
|
|
1128
|
-
const c =
|
|
1129
|
-
return
|
|
1212
|
+
const h = fp18.toFp18(bar.h);
|
|
1213
|
+
const l = fp18.toFp18(bar.l);
|
|
1214
|
+
const c = fp18.toFp18(bar.c);
|
|
1215
|
+
return fp18.toDnum((h + l + c) / 3n);
|
|
1130
1216
|
};
|
|
1131
1217
|
});
|
|
1132
1218
|
|
|
@@ -1140,7 +1226,7 @@ const defaultVwmaOptions = { period: 20 };
|
|
|
1140
1226
|
* influence to prices with higher trading activity. It is useful for
|
|
1141
1227
|
* confirming trends and identifying divergences between price and volume.
|
|
1142
1228
|
*
|
|
1143
|
-
* Formula: VWMA = Sum(Close
|
|
1229
|
+
* Formula: VWMA = Sum(Close * Volume, period) / Sum(Volume, period)
|
|
1144
1230
|
*
|
|
1145
1231
|
* Interpretation:
|
|
1146
1232
|
* - When VWMA is below price, it suggests bullish sentiment (higher volume at higher prices)
|
|
@@ -1158,28 +1244,28 @@ const vwma = createSignal(({ period }) => {
|
|
|
1158
1244
|
const vBuffer = Array.from({ length: period });
|
|
1159
1245
|
let head = 0;
|
|
1160
1246
|
let count = 0;
|
|
1161
|
-
let pvSum =
|
|
1162
|
-
let vSum =
|
|
1247
|
+
let pvSum = fp18.ZERO;
|
|
1248
|
+
let vSum = fp18.ZERO;
|
|
1163
1249
|
return (bar) => {
|
|
1164
|
-
const close =
|
|
1165
|
-
const volume =
|
|
1166
|
-
const pv =
|
|
1250
|
+
const close = fp18.toFp18(bar.c);
|
|
1251
|
+
const volume = fp18.toFp18(bar.v);
|
|
1252
|
+
const pv = fp18.mul(close, volume);
|
|
1167
1253
|
if (count < period) {
|
|
1168
1254
|
pvBuffer[count] = pv;
|
|
1169
1255
|
vBuffer[count] = volume;
|
|
1170
|
-
pvSum
|
|
1171
|
-
vSum
|
|
1256
|
+
pvSum += pv;
|
|
1257
|
+
vSum += volume;
|
|
1172
1258
|
count++;
|
|
1173
1259
|
} else {
|
|
1174
|
-
pvSum
|
|
1175
|
-
vSum
|
|
1260
|
+
pvSum -= pvBuffer[head];
|
|
1261
|
+
vSum -= vBuffer[head];
|
|
1176
1262
|
pvBuffer[head] = pv;
|
|
1177
1263
|
vBuffer[head] = volume;
|
|
1178
|
-
pvSum
|
|
1179
|
-
vSum
|
|
1264
|
+
pvSum += pv;
|
|
1265
|
+
vSum += volume;
|
|
1180
1266
|
head = (head + 1) % period;
|
|
1181
1267
|
}
|
|
1182
|
-
return
|
|
1268
|
+
return fp18.toDnum(fp18.div(pvSum, vSum));
|
|
1183
1269
|
};
|
|
1184
1270
|
}, defaultVwmaOptions);
|
|
1185
1271
|
|
|
@@ -1204,59 +1290,56 @@ const vortex = createSignal(({ period }) => {
|
|
|
1204
1290
|
const trBuffer = Array.from({ length: period });
|
|
1205
1291
|
let head = 0;
|
|
1206
1292
|
let count = 0;
|
|
1207
|
-
let sumVmPlus =
|
|
1208
|
-
let sumVmMinus =
|
|
1209
|
-
let sumTr =
|
|
1293
|
+
let sumVmPlus = fp18.ZERO;
|
|
1294
|
+
let sumVmMinus = fp18.ZERO;
|
|
1295
|
+
let sumTr = fp18.ZERO;
|
|
1210
1296
|
let prevHigh = null;
|
|
1211
1297
|
let prevLow = null;
|
|
1212
1298
|
let prevClose = null;
|
|
1213
1299
|
return (bar) => {
|
|
1214
|
-
const h =
|
|
1215
|
-
const l =
|
|
1216
|
-
const c =
|
|
1300
|
+
const h = fp18.toFp18(bar.h);
|
|
1301
|
+
const l = fp18.toFp18(bar.l);
|
|
1302
|
+
const c = fp18.toFp18(bar.c);
|
|
1217
1303
|
if (prevHigh === null || prevLow === null || prevClose === null) {
|
|
1218
1304
|
prevHigh = h;
|
|
1219
1305
|
prevLow = l;
|
|
1220
1306
|
prevClose = c;
|
|
1221
1307
|
return {
|
|
1222
|
-
plus:
|
|
1223
|
-
minus:
|
|
1308
|
+
plus: fp18.toDnum(fp18.ZERO),
|
|
1309
|
+
minus: fp18.toDnum(fp18.ZERO)
|
|
1224
1310
|
};
|
|
1225
1311
|
}
|
|
1226
|
-
const vmPlus = abs(
|
|
1227
|
-
const vmMinus = abs(
|
|
1228
|
-
const hl =
|
|
1229
|
-
const hpc = abs(
|
|
1230
|
-
const lpc = abs(
|
|
1312
|
+
const vmPlus = fp18.abs(h - prevLow);
|
|
1313
|
+
const vmMinus = fp18.abs(l - prevHigh);
|
|
1314
|
+
const hl = h - l;
|
|
1315
|
+
const hpc = fp18.abs(h - prevClose);
|
|
1316
|
+
const lpc = fp18.abs(l - prevClose);
|
|
1231
1317
|
let tr = hl;
|
|
1232
|
-
if (
|
|
1233
|
-
if (
|
|
1318
|
+
if (hpc > tr) tr = hpc;
|
|
1319
|
+
if (lpc > tr) tr = lpc;
|
|
1234
1320
|
if (count < period) {
|
|
1235
1321
|
vmPlusBuffer[count] = vmPlus;
|
|
1236
1322
|
vmMinusBuffer[count] = vmMinus;
|
|
1237
1323
|
trBuffer[count] = tr;
|
|
1238
|
-
sumVmPlus
|
|
1239
|
-
sumVmMinus
|
|
1240
|
-
sumTr
|
|
1324
|
+
sumVmPlus += vmPlus;
|
|
1325
|
+
sumVmMinus += vmMinus;
|
|
1326
|
+
sumTr += tr;
|
|
1241
1327
|
count++;
|
|
1242
1328
|
} else {
|
|
1243
|
-
sumVmPlus =
|
|
1244
|
-
sumVmMinus =
|
|
1245
|
-
sumTr =
|
|
1329
|
+
sumVmPlus = sumVmPlus - vmPlusBuffer[head] + vmPlus;
|
|
1330
|
+
sumVmMinus = sumVmMinus - vmMinusBuffer[head] + vmMinus;
|
|
1331
|
+
sumTr = sumTr - trBuffer[head] + tr;
|
|
1246
1332
|
vmPlusBuffer[head] = vmPlus;
|
|
1247
1333
|
vmMinusBuffer[head] = vmMinus;
|
|
1248
1334
|
trBuffer[head] = tr;
|
|
1249
|
-
sumVmPlus = add(sumVmPlus, vmPlus);
|
|
1250
|
-
sumVmMinus = add(sumVmMinus, vmMinus);
|
|
1251
|
-
sumTr = add(sumTr, tr);
|
|
1252
1335
|
head = (head + 1) % period;
|
|
1253
1336
|
}
|
|
1254
1337
|
prevHigh = h;
|
|
1255
1338
|
prevLow = l;
|
|
1256
1339
|
prevClose = c;
|
|
1257
1340
|
return {
|
|
1258
|
-
plus:
|
|
1259
|
-
minus:
|
|
1341
|
+
plus: fp18.toDnum(fp18.div(sumVmPlus, sumTr)),
|
|
1342
|
+
minus: fp18.toDnum(fp18.div(sumVmMinus, sumTr))
|
|
1260
1343
|
};
|
|
1261
1344
|
};
|
|
1262
1345
|
}, defaultVortexOptions);
|
|
@@ -1290,15 +1373,35 @@ const defaultMassIndexOptions = {
|
|
|
1290
1373
|
const mi = createSignal(({ emaPeriod, miPeriod }) => {
|
|
1291
1374
|
assert(Number.isInteger(emaPeriod) && emaPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected emaPeriod to be a positive integer, got ${emaPeriod}`));
|
|
1292
1375
|
assert(Number.isInteger(miPeriod) && miPeriod >= 1, /* @__PURE__ */ new RangeError(`Expected miPeriod to be a positive integer, got ${miPeriod}`));
|
|
1293
|
-
const ema1Proc =
|
|
1294
|
-
const ema2Proc =
|
|
1295
|
-
const msumProc = msum
|
|
1376
|
+
const ema1Proc = ewma(ewma.k(emaPeriod));
|
|
1377
|
+
const ema2Proc = ewma(ewma.k(emaPeriod));
|
|
1378
|
+
const msumProc = msum$1(miPeriod);
|
|
1296
1379
|
return (bar) => {
|
|
1297
|
-
const e1 = ema1Proc(
|
|
1298
|
-
|
|
1380
|
+
const e1 = ema1Proc(fp18.toFp18(bar.h) - fp18.toFp18(bar.l));
|
|
1381
|
+
const e2 = ema2Proc(e1);
|
|
1382
|
+
const ratio = fp18.div(e1, e2);
|
|
1383
|
+
return fp18.toDnum(msumProc(ratio));
|
|
1299
1384
|
};
|
|
1300
1385
|
}, defaultMassIndexOptions);
|
|
1301
1386
|
|
|
1387
|
+
//#endregion
|
|
1388
|
+
//#region src/volume/accumulationDistribution.ts
|
|
1389
|
+
/**
|
|
1390
|
+
* Accumulation/Distribution Indicator (A/D). Cumulative indicator
|
|
1391
|
+
* that uses volume and price to assess whether a stock is
|
|
1392
|
+
* being accumulated or distributed.
|
|
1393
|
+
*
|
|
1394
|
+
* MFM = ((Closing - Low) - (High - Closing)) / (High - Low)
|
|
1395
|
+
* MFV = MFM * Period Volume
|
|
1396
|
+
* AD = Previous AD + CMFV
|
|
1397
|
+
*/
|
|
1398
|
+
const ad = createSignal(() => {
|
|
1399
|
+
const proc = ad$1();
|
|
1400
|
+
return (bar) => {
|
|
1401
|
+
return fp18.toDnum(proc(fp18.toFp18(bar.h), fp18.toFp18(bar.l), fp18.toFp18(bar.c), fp18.toFp18(bar.v)));
|
|
1402
|
+
};
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1302
1405
|
//#endregion
|
|
1303
1406
|
export { apo as absolutePriceOscillator, apo, ad as accumulationDistribution, ad, ao, ao as awesomeOscillator, aroon, bop as balanceOfPower, bop, cci, cci as commodityChannelIndex, cfo, cfo as chandeForecastOscillator, cmo as chaikinOscillator, cmo, defaultAbsolutePriceOscillatorOptions, defaultAroonOptions, defaultAwesomeOscillatorOptions, defaultCCIOptions, defaultCFOOptions, defaultChaikinOscillatorOptions, defaultDoubleExponentialMovingAverageOptions, defaultExponentialMovingAverageOptions, defaultIchimokuCloudOptions, defaultMACDOptions, defaultMassIndexOptions, defaultMovingMaxOptions, defaultMovingMinOptions, defaultMovingSumOptions, defaultParabolicSarOptions, defaultPercentagePriceOscillatorOptions, defaultPercentageVolumeOscillatorOptions, defaultPriceRateOfChangeOptions, defaultQstickOptions, defaultRMAOptions, defaultRSIOptions, defaultRandomIndexOptions, defaultSMAOptions, defaultStochasticOscillatorOptions, defaultTriangularMovingAverageOptions, defaultTripleExponentialAverageOptions, defaultTripleExponentialMovingAverageOptions, defaultVortexOptions, defaultVwmaOptions, defaultWilliamsROptions, dema, dema as doubleExponentialMovingAverage, ema, ema as exponentialMovingAverage, ichimokuCloud, kdj, kdj as randomIndex, macd, macd as movingAverageConvergenceDivergence, mi as massIndex, mi, mmax, mmax as movingMax, mmin, mmin as movingMin, msum, psar as parabolicSar, psar, ppo as percentagePriceOscillator, ppo, pvo as percentageVolumeOscillator, pvo, roc as priceRateOfChange, roc, qstick, qstick as qstickIndicator, rsi as relativeStrengthIndex, rsi, rma, rma as rollingMovingAverage, sma as simpleMovingAverage, sma, since, since as sinceChange, stoch, stoch as stochasticOscillator, tema, tema as tripleExponentialMovingAverage, trima as triangularMovingAverage, trima, trix as tripleExponentialAverage, trix, typicalPrice, typicalPrice as typicalPriceIndicator, vwma as volumeWeightedMovingAverage, vwma, vortex, vortex as vortexIndicator, willr as williamsR, willr };
|
|
1304
1407
|
//# sourceMappingURL=index.js.map
|