oakscriptjs 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/array/index.d.ts +8 -2
- package/dist/cjs/array/index.d.ts.map +1 -1
- package/dist/cjs/array/index.js +2 -1
- package/dist/cjs/array/index.js.map +2 -2
- package/dist/cjs/box/index.js +6 -6
- package/dist/cjs/box/index.js.map +2 -2
- package/dist/cjs/callsite/index.d.ts.map +1 -1
- package/dist/cjs/callsite/index.js +6 -3
- package/dist/cjs/callsite/index.js.map +2 -2
- package/dist/cjs/drawing/registry.d.ts +15 -6
- package/dist/cjs/drawing/registry.d.ts.map +1 -1
- package/dist/cjs/drawing/registry.js +16 -2
- package/dist/cjs/drawing/registry.js.map +2 -2
- package/dist/cjs/index.d.ts +4 -3
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +7 -1
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/input.d.ts +9 -1
- package/dist/cjs/input.d.ts.map +1 -1
- package/dist/cjs/input.js +16 -0
- package/dist/cjs/input.js.map +2 -2
- package/dist/cjs/label/index.js +3 -3
- package/dist/cjs/label/index.js.map +2 -2
- package/dist/cjs/line/index.d.ts +10 -27
- package/dist/cjs/line/index.d.ts.map +1 -1
- package/dist/cjs/line/index.js +10 -27
- package/dist/cjs/line/index.js.map +2 -2
- package/dist/cjs/linefill/index.d.ts +3 -0
- package/dist/cjs/linefill/index.d.ts.map +1 -1
- package/dist/cjs/linefill/index.js +15 -1
- package/dist/cjs/linefill/index.js.map +3 -3
- package/dist/cjs/runtime/adapters/SimpleInputAdapter.js +1 -1
- package/dist/cjs/runtime/adapters/SimpleInputAdapter.js.map +2 -2
- package/dist/cjs/runtime/index.d.ts +1 -1
- package/dist/cjs/runtime/index.d.ts.map +1 -1
- package/dist/cjs/runtime/index.js +1 -0
- package/dist/cjs/runtime/index.js.map +2 -2
- package/dist/cjs/runtime/inputs.d.ts +9 -0
- package/dist/cjs/runtime/inputs.d.ts.map +1 -1
- package/dist/cjs/runtime/inputs.js +8 -0
- package/dist/cjs/runtime/inputs.js.map +2 -2
- package/dist/cjs/runtime/types.d.ts +1 -1
- package/dist/cjs/runtime/types.d.ts.map +1 -1
- package/dist/cjs/runtime/types.js.map +1 -1
- package/dist/cjs/script/index.d.ts +66 -13
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +139 -35
- package/dist/cjs/script/index.js.map +3 -3
- package/dist/cjs/security/other.d.ts +92 -0
- package/dist/cjs/security/other.d.ts.map +1 -0
- package/dist/cjs/security/other.js +164 -0
- package/dist/cjs/security/other.js.map +7 -0
- package/dist/cjs/session/bars.d.ts +10 -1
- package/dist/cjs/session/bars.d.ts.map +1 -1
- package/dist/cjs/session/bars.js +16 -2
- package/dist/cjs/session/bars.js.map +2 -2
- package/dist/cjs/session/calendar.d.ts +4 -1
- package/dist/cjs/session/calendar.d.ts.map +1 -1
- package/dist/cjs/session/calendar.js +12 -7
- package/dist/cjs/session/calendar.js.map +2 -2
- package/dist/cjs/session/context.d.ts +22 -5
- package/dist/cjs/session/context.d.ts.map +1 -1
- package/dist/cjs/session/context.js +67 -6
- package/dist/cjs/session/context.js.map +2 -2
- package/dist/cjs/str/floatingdecimal.d.ts +31 -0
- package/dist/cjs/str/floatingdecimal.d.ts.map +1 -0
- package/dist/cjs/str/floatingdecimal.js +298 -0
- package/dist/cjs/str/floatingdecimal.js.map +7 -0
- package/dist/cjs/str/index.d.ts +4 -3
- package/dist/cjs/str/index.d.ts.map +1 -1
- package/dist/cjs/str/index.js +1 -1
- package/dist/cjs/str/index.js.map +2 -2
- package/dist/cjs/str/numberformat.d.ts +6 -4
- package/dist/cjs/str/numberformat.d.ts.map +1 -1
- package/dist/cjs/str/numberformat.js +3 -2
- package/dist/cjs/str/numberformat.js.map +2 -2
- package/dist/cjs/ta/index.d.ts +64 -45
- package/dist/cjs/ta/index.d.ts.map +1 -1
- package/dist/cjs/ta/index.js +140 -126
- package/dist/cjs/ta/index.js.map +3 -3
- package/dist/cjs/ta/limits.d.ts +11 -0
- package/dist/cjs/ta/limits.d.ts.map +1 -1
- package/dist/cjs/ta/limits.js +15 -1
- package/dist/cjs/ta/limits.js.map +2 -2
- package/dist/cjs/ta/running-sum.js +1 -1
- package/dist/cjs/ta/running-sum.js.map +2 -2
- package/dist/cjs/ta-series.d.ts +1 -1
- package/dist/cjs/ta-series.d.ts.map +1 -1
- package/dist/cjs/ta-series.js +1 -1
- package/dist/cjs/ta-series.js.map +2 -2
- package/dist/cjs/table/index.d.ts +10 -3
- package/dist/cjs/table/index.d.ts.map +1 -1
- package/dist/cjs/table/index.js +11 -1
- package/dist/cjs/table/index.js.map +3 -3
- package/dist/cjs/time/session.d.ts +9 -0
- package/dist/cjs/time/session.d.ts.map +1 -1
- package/dist/cjs/time/session.js +42 -14
- package/dist/cjs/time/session.js.map +3 -3
- package/dist/cjs/time/timezone.d.ts +6 -1
- package/dist/cjs/time/timezone.d.ts.map +1 -1
- package/dist/cjs/time/timezone.js +2 -2
- package/dist/cjs/time/timezone.js.map +2 -2
- package/dist/cjs/types/metadata.d.ts +1 -1
- package/dist/cjs/types/metadata.d.ts.map +1 -1
- package/dist/cjs/types/metadata.js.map +1 -1
- package/dist/esm/array/index.d.ts +8 -2
- package/dist/esm/array/index.d.ts.map +1 -1
- package/dist/esm/array/index.js +12 -3
- package/dist/esm/array/index.js.map +1 -1
- package/dist/esm/box/index.js +6 -6
- package/dist/esm/box/index.js.map +1 -1
- package/dist/esm/callsite/index.d.ts.map +1 -1
- package/dist/esm/callsite/index.js +6 -3
- package/dist/esm/callsite/index.js.map +1 -1
- package/dist/esm/drawing/registry.d.ts +15 -6
- package/dist/esm/drawing/registry.d.ts.map +1 -1
- package/dist/esm/drawing/registry.js +30 -7
- package/dist/esm/drawing/registry.js.map +1 -1
- package/dist/esm/index.d.ts +4 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +8 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/input.d.ts +9 -1
- package/dist/esm/input.d.ts.map +1 -1
- package/dist/esm/input.js +16 -0
- package/dist/esm/input.js.map +1 -1
- package/dist/esm/label/index.js +3 -3
- package/dist/esm/label/index.js.map +1 -1
- package/dist/esm/line/index.d.ts +10 -27
- package/dist/esm/line/index.d.ts.map +1 -1
- package/dist/esm/line/index.js +20 -64
- package/dist/esm/line/index.js.map +1 -1
- package/dist/esm/linefill/index.d.ts +3 -0
- package/dist/esm/linefill/index.d.ts.map +1 -1
- package/dist/esm/linefill/index.js +20 -1
- package/dist/esm/linefill/index.js.map +1 -1
- package/dist/esm/runtime/adapters/SimpleInputAdapter.js +1 -1
- package/dist/esm/runtime/adapters/SimpleInputAdapter.js.map +1 -1
- package/dist/esm/runtime/index.d.ts +1 -1
- package/dist/esm/runtime/index.d.ts.map +1 -1
- package/dist/esm/runtime/index.js +1 -1
- package/dist/esm/runtime/index.js.map +1 -1
- package/dist/esm/runtime/inputs.d.ts +9 -0
- package/dist/esm/runtime/inputs.d.ts.map +1 -1
- package/dist/esm/runtime/inputs.js +15 -0
- package/dist/esm/runtime/inputs.js.map +1 -1
- package/dist/esm/runtime/types.d.ts +1 -1
- package/dist/esm/runtime/types.d.ts.map +1 -1
- package/dist/esm/script/index.d.ts +66 -13
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +187 -54
- package/dist/esm/script/index.js.map +1 -1
- package/dist/esm/security/other.d.ts +92 -0
- package/dist/esm/security/other.d.ts.map +1 -0
- package/dist/esm/security/other.js +211 -0
- package/dist/esm/security/other.js.map +1 -0
- package/dist/esm/session/bars.d.ts +10 -1
- package/dist/esm/session/bars.d.ts.map +1 -1
- package/dist/esm/session/bars.js +26 -1
- package/dist/esm/session/bars.js.map +1 -1
- package/dist/esm/session/calendar.d.ts +4 -1
- package/dist/esm/session/calendar.d.ts.map +1 -1
- package/dist/esm/session/calendar.js +23 -7
- package/dist/esm/session/calendar.js.map +1 -1
- package/dist/esm/session/context.d.ts +22 -5
- package/dist/esm/session/context.d.ts.map +1 -1
- package/dist/esm/session/context.js +86 -10
- package/dist/esm/session/context.js.map +1 -1
- package/dist/esm/str/floatingdecimal.d.ts +31 -0
- package/dist/esm/str/floatingdecimal.d.ts.map +1 -0
- package/dist/esm/str/floatingdecimal.js +272 -0
- package/dist/esm/str/floatingdecimal.js.map +1 -0
- package/dist/esm/str/index.d.ts +4 -3
- package/dist/esm/str/index.d.ts.map +1 -1
- package/dist/esm/str/index.js +6 -5
- package/dist/esm/str/index.js.map +1 -1
- package/dist/esm/str/numberformat.d.ts +6 -4
- package/dist/esm/str/numberformat.d.ts.map +1 -1
- package/dist/esm/str/numberformat.js +9 -5
- package/dist/esm/str/numberformat.js.map +1 -1
- package/dist/esm/ta/index.d.ts +64 -45
- package/dist/esm/ta/index.d.ts.map +1 -1
- package/dist/esm/ta/index.js +198 -228
- package/dist/esm/ta/index.js.map +1 -1
- package/dist/esm/ta/limits.d.ts +11 -0
- package/dist/esm/ta/limits.d.ts.map +1 -1
- package/dist/esm/ta/limits.js +27 -2
- package/dist/esm/ta/limits.js.map +1 -1
- package/dist/esm/ta/running-sum.js +1 -1
- package/dist/esm/ta/running-sum.js.map +1 -1
- package/dist/esm/ta-series.d.ts +1 -1
- package/dist/esm/ta-series.d.ts.map +1 -1
- package/dist/esm/ta-series.js +2 -1
- package/dist/esm/ta-series.js.map +1 -1
- package/dist/esm/table/index.d.ts +10 -3
- package/dist/esm/table/index.d.ts.map +1 -1
- package/dist/esm/table/index.js +23 -3
- package/dist/esm/table/index.js.map +1 -1
- package/dist/esm/time/session.d.ts +9 -0
- package/dist/esm/time/session.d.ts.map +1 -1
- package/dist/esm/time/session.js +53 -15
- package/dist/esm/time/session.js.map +1 -1
- package/dist/esm/time/timezone.d.ts +6 -1
- package/dist/esm/time/timezone.d.ts.map +1 -1
- package/dist/esm/time/timezone.js +8 -2
- package/dist/esm/time/timezone.js.map +1 -1
- package/dist/esm/types/metadata.d.ts +1 -1
- package/dist/esm/types/metadata.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/array/index.ts +11 -3
- package/src/box/index.ts +6 -6
- package/src/callsite/index.ts +6 -3
- package/src/drawing/registry.ts +30 -7
- package/src/index.ts +16 -3
- package/src/input.ts +18 -1
- package/src/label/index.ts +3 -3
- package/src/line/index.ts +20 -68
- package/src/linefill/index.ts +20 -1
- package/src/runtime/adapters/SimpleInputAdapter.ts +1 -1
- package/src/runtime/index.ts +1 -0
- package/src/runtime/inputs.ts +16 -0
- package/src/runtime/types.ts +1 -1
- package/src/script/index.ts +262 -58
- package/src/security/other.ts +269 -0
- package/src/session/bars.ts +24 -1
- package/src/session/calendar.ts +26 -7
- package/src/session/context.ts +104 -10
- package/src/str/floatingdecimal.ts +268 -0
- package/src/str/index.ts +6 -5
- package/src/str/numberformat.ts +11 -6
- package/src/ta/index.ts +200 -225
- package/src/ta/limits.ts +28 -1
- package/src/ta/running-sum.ts +1 -1
- package/src/ta-series.ts +3 -2
- package/src/table/index.ts +22 -3
- package/src/time/session.ts +58 -16
- package/src/time/timezone.ts +9 -2
- package/src/types/metadata.ts +1 -1
package/dist/esm/ta/index.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { eq, ge, gt, le, lt } from '../compare/index.js';
|
|
11
11
|
import { runningSum, runningSumSeries, runningVariance, runningVarianceSeries } from './running-sum.js';
|
|
12
|
-
import { checkSumLength, checkWindowLength } from './limits.js';
|
|
12
|
+
import { checkPositiveLength, checkSumLength, checkWindowLength } from './limits.js';
|
|
13
13
|
/**
|
|
14
14
|
* Simple Moving Average - returns the moving average (sum of last y values divided by y).
|
|
15
15
|
*
|
|
@@ -64,6 +64,7 @@ export function sma(source, length) {
|
|
|
64
64
|
* ```
|
|
65
65
|
*/
|
|
66
66
|
export function ema(source, length) {
|
|
67
|
+
checkPositiveLength('ema', length, 0);
|
|
67
68
|
const result = [];
|
|
68
69
|
const len = Math.floor(length);
|
|
69
70
|
const multiplier = 2 / (len + 1);
|
|
@@ -101,32 +102,19 @@ export function ema(source, length) {
|
|
|
101
102
|
}
|
|
102
103
|
return result;
|
|
103
104
|
}
|
|
104
|
-
/**
|
|
105
|
-
* Relative Strength Index - momentum oscillator measuring speed and magnitude of price changes.
|
|
106
|
-
*
|
|
107
|
-
* @param source - Series of values to process
|
|
108
|
-
* @param length - Number of bars (length)
|
|
109
|
-
* @returns RSI series (values range from 0 to 100)
|
|
110
|
-
*
|
|
111
|
-
* @remarks
|
|
112
|
-
* - RSI values above 70 typically indicate overbought conditions
|
|
113
|
-
* - RSI values below 30 typically indicate oversold conditions
|
|
114
|
-
* - Uses `ta.rma()` (Relative Moving Average) for smoothing, matching PineScript v6
|
|
115
|
-
* - Formula: RSI = 100 - (100 / (1 + RS)), where RS = Average Gain / Average Loss
|
|
116
|
-
* - As in PineScript (`down == 0 ? 100 : up == 0 ? 0 : ...`): 100 when the average loss is 0, else 0 when the
|
|
117
|
-
* average gain is 0, both with the 1e-10 tolerance of the comparison operators
|
|
118
|
-
* - Average Gain and Average Loss are calculated using RMA (alpha = 1 / length)
|
|
119
|
-
* - `na` values in the source series are ignored
|
|
120
|
-
*
|
|
121
|
-
* @example
|
|
122
|
-
* ```typescript
|
|
123
|
-
* const rsi14 = ta.rsi(closePrices, 14);
|
|
124
|
-
* // Identify overbought/oversold conditions
|
|
125
|
-
* const overbought = rsi14.map(v => v > 70);
|
|
126
|
-
* const oversold = rsi14.map(v => v < 30);
|
|
127
|
-
* ```
|
|
128
|
-
*/
|
|
129
105
|
export function rsi(source, length) {
|
|
106
|
+
if (typeof length !== 'number') {
|
|
107
|
+
return Array.from(source, (xv, i) => {
|
|
108
|
+
const x = xv ?? NaN;
|
|
109
|
+
const y = length[i] ?? NaN;
|
|
110
|
+
if (eq(y, 0))
|
|
111
|
+
return 100;
|
|
112
|
+
if (eq(x, 0))
|
|
113
|
+
return 0;
|
|
114
|
+
return 100 - 100 / (1 + x / y);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
checkPositiveLength('rsi', length, 0, 'y');
|
|
130
118
|
const result = [];
|
|
131
119
|
// Calculate price changes
|
|
132
120
|
const changes = [];
|
|
@@ -239,14 +227,14 @@ export function stdev(source, length, biased = true) {
|
|
|
239
227
|
return varianceOf(source, length, biased, 'ta.stdev').map((v) => (Number.isNaN(v) ? NaN : v > 0 ? Math.sqrt(v) : 0));
|
|
240
228
|
}
|
|
241
229
|
/**
|
|
242
|
-
* The last `length` non-na values up to bar `i` (the bar itself may be na), or null when fewer exist.
|
|
243
|
-
*
|
|
230
|
+
* The last `length` non-na values up to bar `i` (the bar itself may be na), or null when fewer exist. +/-Infinity
|
|
231
|
+
* counts as na (#183).
|
|
244
232
|
*/
|
|
245
233
|
function lastValues(source, i, length) {
|
|
246
234
|
const values = [];
|
|
247
235
|
for (let j = i; j >= 0 && values.length < length; j--) {
|
|
248
236
|
const x = source[j];
|
|
249
|
-
if (
|
|
237
|
+
if (Number.isFinite(x))
|
|
250
238
|
values.push(x);
|
|
251
239
|
}
|
|
252
240
|
return values.length === length ? values : null;
|
|
@@ -305,7 +293,7 @@ function windowExtremes(source, length, lowest, newest = false) {
|
|
|
305
293
|
/**
|
|
306
294
|
* The non-na values of `source` with their bars, and for each bar the position of its last non-na value (-1 when
|
|
307
295
|
* there is none yet). PineScript's ta.highest / ta.lowest / ta.highestbars / ta.lowestbars / ta.pivothigh /
|
|
308
|
-
* ta.pivotlow keep the non-na values they receive and nothing else.
|
|
296
|
+
* ta.pivotlow keep the non-na values they receive and nothing else; +/-Infinity is skipped like na (#183).
|
|
309
297
|
*/
|
|
310
298
|
function keptValues(source) {
|
|
311
299
|
const values = [];
|
|
@@ -313,7 +301,7 @@ function keptValues(source) {
|
|
|
313
301
|
const last = new Int32Array(source.length);
|
|
314
302
|
for (let i = 0; i < source.length; i++) {
|
|
315
303
|
const x = source[i];
|
|
316
|
-
if (
|
|
304
|
+
if (Number.isFinite(x)) {
|
|
317
305
|
values.push(x);
|
|
318
306
|
bars.push(i);
|
|
319
307
|
}
|
|
@@ -463,6 +451,7 @@ export function crossunder(series1, series2) {
|
|
|
463
451
|
* ```
|
|
464
452
|
*/
|
|
465
453
|
export function change(source, length = 1) {
|
|
454
|
+
checkPositiveLength('change', length, 0);
|
|
466
455
|
const result = [];
|
|
467
456
|
for (let i = 0; i < source.length; i++) {
|
|
468
457
|
if (i < length) {
|
|
@@ -560,6 +549,7 @@ export function tr(handle_na = false, high, low, close) {
|
|
|
560
549
|
* ```
|
|
561
550
|
*/
|
|
562
551
|
export function atr(length, high, low, close) {
|
|
552
|
+
checkPositiveLength('atr->rma', length, 0);
|
|
563
553
|
if (!high || !low || !close) {
|
|
564
554
|
throw new Error('ta.atr() requires high, low, and close series. ' +
|
|
565
555
|
'Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data.');
|
|
@@ -684,6 +674,7 @@ export function supertrend(factor, atrPeriod, high, low, close, wicks = false) {
|
|
|
684
674
|
* ```
|
|
685
675
|
*/
|
|
686
676
|
export function rma(source, length) {
|
|
677
|
+
checkPositiveLength('rma', length, 0);
|
|
687
678
|
const result = [];
|
|
688
679
|
// Floor the length to match PineScript's auto-truncation of float to int
|
|
689
680
|
const len = Math.floor(length);
|
|
@@ -763,6 +754,8 @@ export function wma(source, length) {
|
|
|
763
754
|
const result = [];
|
|
764
755
|
// Floor the length to match PineScript's auto-truncation of float to int
|
|
765
756
|
const fixed = typeof length === 'number' ? Math.floor(length) : NaN;
|
|
757
|
+
if (typeof length === 'number')
|
|
758
|
+
checkPositiveLength('wma', fixed, 0);
|
|
766
759
|
// PineScript: na on a bar with an na source (also +/-Infinity) and until `len` non-na values were received;
|
|
767
760
|
// otherwise the last `len` bars, each na replaced by the previous non-na value
|
|
768
761
|
const filled = [];
|
|
@@ -778,6 +771,8 @@ export function wma(source, length) {
|
|
|
778
771
|
filled.push(last);
|
|
779
772
|
// a series length: the window of this bar's length (the result of ta.wma with that fixed length)
|
|
780
773
|
const len = typeof length === 'number' ? fixed : Math.floor(length[i]);
|
|
774
|
+
if (typeof length !== 'number')
|
|
775
|
+
checkPositiveLength('wma', len, i);
|
|
781
776
|
if (isNa || !(len >= 1) || count < len) {
|
|
782
777
|
result.push(NaN);
|
|
783
778
|
continue;
|
|
@@ -802,9 +797,9 @@ export function wma(source, length) {
|
|
|
802
797
|
* @returns Highest value series (na on the first `length - 1` bars)
|
|
803
798
|
*
|
|
804
799
|
* @remarks
|
|
805
|
-
* As in PineScript: `na` values are skipped. The result is the highest of the last `length`
|
|
806
|
-
* of them when there are fewer), also on a bar whose own value is `na`; `na` before bar
|
|
807
|
-
* value was received. A fractional length is truncated (7.5 is 7 bars).
|
|
800
|
+
* As in PineScript: `na` values (also +/-Infinity, #183) are skipped. The result is the highest of the last `length`
|
|
801
|
+
* non-na values (of all of them when there are fewer), also on a bar whose own value is `na`; `na` before bar
|
|
802
|
+
* `length - 1` and while no value was received. A fractional length is truncated (7.5 is 7 bars).
|
|
808
803
|
* `length` can be an array (a length per bar, PineScript series length), also for `ta.lowest`, `ta.highestbars`
|
|
809
804
|
* and `ta.lowestbars`: each bar uses its own length.
|
|
810
805
|
* A length above 15,000 throws the PineScript runtime error (also `ta.lowest`, `ta.highestbars`, `ta.lowestbars`).
|
|
@@ -821,9 +816,9 @@ export function highest(source, length) {
|
|
|
821
816
|
* @returns Lowest value series (na on the first `length - 1` bars)
|
|
822
817
|
*
|
|
823
818
|
* @remarks
|
|
824
|
-
* As in PineScript: `na` values are skipped. The result is the lowest of the last `length`
|
|
825
|
-
* of them when there are fewer), also on a bar whose own value is `na`; `na` before bar
|
|
826
|
-
* value was received.
|
|
819
|
+
* As in PineScript: `na` values (also +/-Infinity, #183) are skipped. The result is the lowest of the last `length`
|
|
820
|
+
* non-na values (of all of them when there are fewer), also on a bar whose own value is `na`; `na` before bar
|
|
821
|
+
* `length - 1` and while no value was received.
|
|
827
822
|
*/
|
|
828
823
|
export function lowest(source, length) {
|
|
829
824
|
return extremeValue(source, length, true);
|
|
@@ -909,7 +904,7 @@ function monotonic(source, length, step) {
|
|
|
909
904
|
for (let i = 0; i < source.length; i++) {
|
|
910
905
|
const v = source[i];
|
|
911
906
|
const prev = i > 0 ? source[i - 1] : NaN;
|
|
912
|
-
if (
|
|
907
|
+
if (Number.isFinite(v) && Number.isFinite(prev))
|
|
913
908
|
count = step(v, prev) ? count + 1 : 0;
|
|
914
909
|
result.push(count >= length);
|
|
915
910
|
}
|
|
@@ -926,8 +921,8 @@ function monotonic(source, length, step) {
|
|
|
926
921
|
* PineScript rules:
|
|
927
922
|
* - a counter of rises from the previous bar (a rise is larger than the 1e-10 tolerance); a step that is not a rise
|
|
928
923
|
* resets it to 0
|
|
929
|
-
* - a step from or to an `na` bar leaves the counter unchanged (so an `na` bar, and the bar after
|
|
930
|
-
* result of the bar before the `na`)
|
|
924
|
+
* - a step from or to an `na` (or +/-Infinity) bar leaves the counter unchanged (so an `na` bar, and the bar after
|
|
925
|
+
* it, keep the result of the bar before the `na`)
|
|
931
926
|
* - true when the counter is at least `length` (false at the start)
|
|
932
927
|
*
|
|
933
928
|
* @example
|
|
@@ -937,6 +932,7 @@ function monotonic(source, length, step) {
|
|
|
937
932
|
* ```
|
|
938
933
|
*/
|
|
939
934
|
export function rising(source, length) {
|
|
935
|
+
checkPositiveLength('rising', length, 0);
|
|
940
936
|
return monotonic(source, length, gt);
|
|
941
937
|
}
|
|
942
938
|
/**
|
|
@@ -950,8 +946,8 @@ export function rising(source, length) {
|
|
|
950
946
|
* PineScript rules:
|
|
951
947
|
* - a counter of falls from the previous bar (a fall is larger than the 1e-10 tolerance); a step that is not a fall
|
|
952
948
|
* resets it to 0
|
|
953
|
-
* - a step from or to an `na` bar leaves the counter unchanged (so an `na` bar, and the bar after
|
|
954
|
-
* result of the bar before the `na`)
|
|
949
|
+
* - a step from or to an `na` (or +/-Infinity) bar leaves the counter unchanged (so an `na` bar, and the bar after
|
|
950
|
+
* it, keep the result of the bar before the `na`)
|
|
955
951
|
* - true when the counter is at least `length` (false at the start)
|
|
956
952
|
*
|
|
957
953
|
* @example
|
|
@@ -961,6 +957,7 @@ export function rising(source, length) {
|
|
|
961
957
|
* ```
|
|
962
958
|
*/
|
|
963
959
|
export function falling(source, length) {
|
|
960
|
+
checkPositiveLength('falling', length, 0);
|
|
964
961
|
return monotonic(source, length, lt);
|
|
965
962
|
}
|
|
966
963
|
/**
|
|
@@ -985,6 +982,7 @@ export function falling(source, length) {
|
|
|
985
982
|
* ```
|
|
986
983
|
*/
|
|
987
984
|
export function roc(source, length) {
|
|
985
|
+
checkPositiveLength('roc', length, 0);
|
|
988
986
|
const result = [];
|
|
989
987
|
for (let i = 0; i < source.length; i++) {
|
|
990
988
|
if (i < length) {
|
|
@@ -1024,6 +1022,7 @@ export function roc(source, length) {
|
|
|
1024
1022
|
* ```
|
|
1025
1023
|
*/
|
|
1026
1024
|
export function mom(source, length) {
|
|
1025
|
+
checkPositiveLength('mom', length, 0);
|
|
1027
1026
|
const result = [];
|
|
1028
1027
|
for (let i = 0; i < source.length; i++) {
|
|
1029
1028
|
if (i < length) {
|
|
@@ -1062,6 +1061,7 @@ export function mom(source, length) {
|
|
|
1062
1061
|
* ```
|
|
1063
1062
|
*/
|
|
1064
1063
|
export function dev(source, length) {
|
|
1064
|
+
checkPositiveLength('dev', length, 0);
|
|
1065
1065
|
const len = Math.floor(length);
|
|
1066
1066
|
return sma(source, len).map((mean, i) => {
|
|
1067
1067
|
if (i < len - 1 || Number.isNaN(mean))
|
|
@@ -1124,7 +1124,7 @@ function varianceOf(source, length, biased, name) {
|
|
|
1124
1124
|
* @remarks
|
|
1125
1125
|
* - Returns the middle value when values are sorted
|
|
1126
1126
|
* - For an even length, returns the average of the two middle values
|
|
1127
|
-
* - As in PineScript: `na` values are skipped, the window holds the last `length` non-na values
|
|
1127
|
+
* - As in PineScript: `na` values (also +/-Infinity) are skipped, the window holds the last `length` non-na values
|
|
1128
1128
|
* (going back as many bars as needed), and a bar whose own value is `na` still gets a result
|
|
1129
1129
|
* - Returns NaN until `length` non-na values exist
|
|
1130
1130
|
* - More robust to outliers than mean (SMA)
|
|
@@ -1136,6 +1136,7 @@ function varianceOf(source, length, biased, name) {
|
|
|
1136
1136
|
* ```
|
|
1137
1137
|
*/
|
|
1138
1138
|
export function median(source, length) {
|
|
1139
|
+
checkPositiveLength('median', length, 0);
|
|
1139
1140
|
const len = Math.floor(length);
|
|
1140
1141
|
const n = source.length;
|
|
1141
1142
|
const out = new Array(n).fill(NaN);
|
|
@@ -1162,7 +1163,7 @@ export function median(source, length) {
|
|
|
1162
1163
|
const mid = Math.floor(len / 2);
|
|
1163
1164
|
for (let i = 0; i < n; i++) {
|
|
1164
1165
|
const x = source[i];
|
|
1165
|
-
if (
|
|
1166
|
+
if (Number.isFinite(x)) {
|
|
1166
1167
|
kept[count++] = x;
|
|
1167
1168
|
sorted.splice(bound(x, false), 0, x); // before the equal values: the newest first
|
|
1168
1169
|
// the value leaving the window is the oldest, so the last of its equal values
|
|
@@ -1224,6 +1225,7 @@ export function swma(source) {
|
|
|
1224
1225
|
* ```
|
|
1225
1226
|
*/
|
|
1226
1227
|
export function vwma(source, length, volume) {
|
|
1228
|
+
checkPositiveLength('vwma->sma', length, 0);
|
|
1227
1229
|
if (!volume) {
|
|
1228
1230
|
throw new Error('ta.vwma() requires volume series. ' +
|
|
1229
1231
|
'Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data.');
|
|
@@ -1271,6 +1273,7 @@ export function vwma(source, length, volume) {
|
|
|
1271
1273
|
* ```
|
|
1272
1274
|
*/
|
|
1273
1275
|
export function linreg(source, length, offset = 0) {
|
|
1276
|
+
checkPositiveLength('linreg', length, 0);
|
|
1274
1277
|
const result = [];
|
|
1275
1278
|
// Floor the length to match PineScript's auto-truncation of float to int
|
|
1276
1279
|
const len = Math.floor(length);
|
|
@@ -1338,6 +1341,7 @@ export function linreg(source, length, offset = 0) {
|
|
|
1338
1341
|
* ```
|
|
1339
1342
|
*/
|
|
1340
1343
|
export function correlation(source1, source2, length) {
|
|
1344
|
+
checkPositiveLength('correlation', length, 0);
|
|
1341
1345
|
// PineScript: from compensated running sums, cov = sma(x * y) - sma(x) * sma(y), r = cov / sqrt(var(x) * var(y))
|
|
1342
1346
|
// (biased variances); each running sum skips its own na values
|
|
1343
1347
|
const product = Array.from({ length: source1.length }, (_, i) => {
|
|
@@ -1377,8 +1381,8 @@ export function correlation(source1, source2, length) {
|
|
|
1377
1381
|
*
|
|
1378
1382
|
* PineScript rules:
|
|
1379
1383
|
* - percentrank = count(source[k] <= source, k = 1..length) * 100 / length (in this order: the PineScript bits)
|
|
1380
|
-
* - a value from bar `length` on
|
|
1381
|
-
*
|
|
1384
|
+
* - a value from bar `length` on; an `na` value in the window counts as not `<=` the current value, and an `na`
|
|
1385
|
+
* current value gives 0 (#177)
|
|
1382
1386
|
* - the comparison uses the 1e-10 tolerance
|
|
1383
1387
|
*
|
|
1384
1388
|
* @example
|
|
@@ -1389,14 +1393,15 @@ export function correlation(source1, source2, length) {
|
|
|
1389
1393
|
* ```
|
|
1390
1394
|
*/
|
|
1391
1395
|
export function percentrank(source, length) {
|
|
1396
|
+
checkPositiveLength('percentrank', length, 0);
|
|
1392
1397
|
const result = [];
|
|
1393
1398
|
for (let i = 0; i < source.length; i++) {
|
|
1394
1399
|
const currentValue = source[i];
|
|
1395
|
-
if (i < length
|
|
1400
|
+
if (i < length) {
|
|
1396
1401
|
result.push(NaN);
|
|
1397
1402
|
continue;
|
|
1398
1403
|
}
|
|
1399
|
-
// le() is false for an na value in the window
|
|
1404
|
+
// le() is false for an na value in the window, and for all of them when the current value is na (0)
|
|
1400
1405
|
let countLessOrEqual = 0;
|
|
1401
1406
|
for (let j = 1; j <= length; j++) {
|
|
1402
1407
|
if (le(source[i - j], currentValue))
|
|
@@ -1435,6 +1440,7 @@ export function percentrank(source, length) {
|
|
|
1435
1440
|
* \`\`\`
|
|
1436
1441
|
*/
|
|
1437
1442
|
export function cci(source, length) {
|
|
1443
|
+
checkPositiveLength('cci', length, 0);
|
|
1438
1444
|
const smaValues = sma(source, length);
|
|
1439
1445
|
const devValues = dev(source, length);
|
|
1440
1446
|
let last = 0;
|
|
@@ -1461,8 +1467,9 @@ export function cci(source, length) {
|
|
|
1461
1467
|
* - Measures where close is relative to the high-low range
|
|
1462
1468
|
* - Values above 80 typically indicate overbought
|
|
1463
1469
|
* - Values below 20 typically indicate oversold
|
|
1464
|
-
* - As PineScript: when the source, \`highest(high, length)\` or \`lowest(low, length)\` is \`na\`, or the range
|
|
1465
|
-
* result is the previous result of the call (\`na\` until a first value)
|
|
1470
|
+
* - As PineScript: when the source, \`highest(high, length)\` or \`lowest(low, length)\` is \`na\`, or the range and
|
|
1471
|
+
* \`source - lowest\` are both 0, the result is the previous result of the call (\`na\` until a first value); a zero
|
|
1472
|
+
* range with another source value gives +/-Infinity (plotted as \`na\`)
|
|
1466
1473
|
*
|
|
1467
1474
|
* @example
|
|
1468
1475
|
* \`\`\`typescript
|
|
@@ -1472,17 +1479,18 @@ export function cci(source, length) {
|
|
|
1472
1479
|
* \`\`\`
|
|
1473
1480
|
*/
|
|
1474
1481
|
export function stoch(source, high, low, length) {
|
|
1482
|
+
checkPositiveLength('stoch->highest', length, 0);
|
|
1475
1483
|
const result = [];
|
|
1476
1484
|
const lowestValues = lowest(low, length);
|
|
1477
1485
|
const highestValues = highest(high, length);
|
|
1478
|
-
// PineScript: an na input or a zero range
|
|
1486
|
+
// PineScript: an na (NaN) result keeps the previous one: an na input, or 0 / 0 on a zero range; a non-zero value
|
|
1487
|
+
// over a zero range gives +/-Infinity (#183)
|
|
1479
1488
|
let previous = NaN;
|
|
1480
1489
|
for (let i = 0; i < source.length; i++) {
|
|
1481
|
-
const value = source[i];
|
|
1482
1490
|
const lo = lowestValues[i];
|
|
1483
|
-
const
|
|
1484
|
-
if (!Number.isNaN(
|
|
1485
|
-
previous =
|
|
1491
|
+
const k = (100 * (source[i] - lo)) / (highestValues[i] - lo);
|
|
1492
|
+
if (!Number.isNaN(k))
|
|
1493
|
+
previous = k;
|
|
1486
1494
|
result.push(previous);
|
|
1487
1495
|
}
|
|
1488
1496
|
return result;
|
|
@@ -1521,6 +1529,7 @@ export function stoch(source, high, low, length) {
|
|
|
1521
1529
|
* ```
|
|
1522
1530
|
*/
|
|
1523
1531
|
export function mfi(source, length, volume) {
|
|
1532
|
+
checkPositiveLength('mfi', length, 0);
|
|
1524
1533
|
if (!volume) {
|
|
1525
1534
|
throw new Error('ta.mfi() requires volume series. ' +
|
|
1526
1535
|
'Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data.');
|
|
@@ -1576,6 +1585,7 @@ export function mfi(source, length, volume) {
|
|
|
1576
1585
|
* ```
|
|
1577
1586
|
*/
|
|
1578
1587
|
export function hma(source, length) {
|
|
1588
|
+
checkPositiveLength('hma', length, 0);
|
|
1579
1589
|
const halfLength = Math.floor(length / 2);
|
|
1580
1590
|
const sqrtLength = Math.floor(Math.sqrt(length));
|
|
1581
1591
|
const wmaHalf = wma(source, halfLength);
|
|
@@ -1728,8 +1738,8 @@ function pivot(source, leftbars, rightbars, isHigh) {
|
|
|
1728
1738
|
* - As in PineScript, the value appears `rightbars` bars after the pivot bar: the pivot bar
|
|
1729
1739
|
* index is `i - rightbars`
|
|
1730
1740
|
* - A pivot high may equal values on its left but must be higher than all values on its right
|
|
1731
|
-
* - `na` values are skipped: the window is the last `leftbars + rightbars + 1` non-na values,
|
|
1732
|
-
* them must be on the bar `rightbars` bars back
|
|
1741
|
+
* - `na` values (also +/-Infinity) are skipped: the window is the last `leftbars + rightbars + 1` non-na values,
|
|
1742
|
+
* and the highest of them must be on the bar `rightbars` bars back
|
|
1733
1743
|
* - `leftbars` / `rightbars` can be arrays (a length per bar, PineScript series length), also for `ta.pivotlow`:
|
|
1734
1744
|
* each bar uses its own lengths
|
|
1735
1745
|
*
|
|
@@ -1762,8 +1772,8 @@ export function pivothigh(sourceOrLeftbars, leftbarsOrRightbars, rightbars, high
|
|
|
1762
1772
|
* - As in PineScript, the value appears `rightbars` bars after the pivot bar: the pivot bar
|
|
1763
1773
|
* index is `i - rightbars`
|
|
1764
1774
|
* - A pivot low may equal values on its left but must be lower than all values on its right
|
|
1765
|
-
* - `na` values are skipped: the window is the last `leftbars + rightbars + 1` non-na values,
|
|
1766
|
-
* them must be on the bar `rightbars` bars back
|
|
1775
|
+
* - `na` values (also +/-Infinity) are skipped: the window is the last `leftbars + rightbars + 1` non-na values,
|
|
1776
|
+
* and the lowest of them must be on the bar `rightbars` bars back
|
|
1767
1777
|
*
|
|
1768
1778
|
* @example
|
|
1769
1779
|
* ```typescript
|
|
@@ -1933,6 +1943,7 @@ export function dmi(diLength, adxSmoothing, high, low, close) {
|
|
|
1933
1943
|
* ```
|
|
1934
1944
|
*/
|
|
1935
1945
|
export function tsi(source, shortLength, longLength) {
|
|
1946
|
+
checkPositiveLength('tsi->ema', shortLength, 0);
|
|
1936
1947
|
const momentum = [];
|
|
1937
1948
|
// Calculate momentum (price change)
|
|
1938
1949
|
for (let i = 0; i < source.length; i++) {
|
|
@@ -1978,6 +1989,7 @@ export function tsi(source, shortLength, longLength) {
|
|
|
1978
1989
|
* ```
|
|
1979
1990
|
*/
|
|
1980
1991
|
export function cmo(source, length) {
|
|
1992
|
+
checkPositiveLength('cmo', length, 0);
|
|
1981
1993
|
// PineScript definition: mom = ta.change(src), sm1 = math.sum(mom >= 0 ? mom : 0, length),
|
|
1982
1994
|
// sm2 = math.sum(mom >= 0 ? 0 : -mom, length), cmo = 100 * (sm1 - sm2) / (sm1 + sm2), with the running sums of
|
|
1983
1995
|
// math.sum (bit for bit). A comparison with na is false: an na change adds 0 to sm1 and na (skipped) to sm2.
|
|
@@ -2018,6 +2030,7 @@ export function cmo(source, length) {
|
|
|
2018
2030
|
* ```
|
|
2019
2031
|
*/
|
|
2020
2032
|
export function kc(source, length, mult, useTrueRange = true, high, low, close) {
|
|
2033
|
+
checkPositiveLength('kc', length, 0);
|
|
2021
2034
|
// Calculate middle band (EMA of source)
|
|
2022
2035
|
const middle = ema(source, length);
|
|
2023
2036
|
// Calculate range
|
|
@@ -2072,6 +2085,7 @@ export function kc(source, length, mult, useTrueRange = true, high, low, close)
|
|
|
2072
2085
|
* ```
|
|
2073
2086
|
*/
|
|
2074
2087
|
export function bbw(source, length, mult) {
|
|
2088
|
+
checkPositiveLength('bbw', length, 0);
|
|
2075
2089
|
const [basis, upper, lower] = bb(source, length, mult);
|
|
2076
2090
|
const result = [];
|
|
2077
2091
|
for (let i = 0; i < source.length; i++) {
|
|
@@ -2113,6 +2127,7 @@ export function bbw(source, length, mult) {
|
|
|
2113
2127
|
* ```
|
|
2114
2128
|
*/
|
|
2115
2129
|
export function wpr(high, low, close, length = 14) {
|
|
2130
|
+
checkPositiveLength('wpr', length, 0);
|
|
2116
2131
|
const highs = highest(high, length);
|
|
2117
2132
|
const lows = lowest(low, length);
|
|
2118
2133
|
return Array.from({ length: close.length }, (_, i) => {
|
|
@@ -2177,6 +2192,7 @@ export function vwap(source, volume, anchor, stdev_mult) {
|
|
|
2177
2192
|
* ```
|
|
2178
2193
|
*/
|
|
2179
2194
|
export function alma(source, length = 9, offset = 0.85, sigma = 6, floor = false) {
|
|
2195
|
+
checkPositiveLength('alma', length, 0);
|
|
2180
2196
|
const result = [];
|
|
2181
2197
|
const m = floor ? Math.floor(offset * (length - 1)) : offset * (length - 1);
|
|
2182
2198
|
const s = length / sigma;
|
|
@@ -2251,10 +2267,11 @@ export function kcw(source, length = 20, mult = 2, useTrueRange = true, high, lo
|
|
|
2251
2267
|
* (PineScript `ta.range(source, length)`).
|
|
2252
2268
|
*
|
|
2253
2269
|
* @remarks
|
|
2254
|
-
* As in PineScript: `na` values are skipped (the window holds the last `length` non-na
|
|
2270
|
+
* As in PineScript: `na` values (also +/-Infinity) are skipped (the window holds the last `length` non-na
|
|
2255
2271
|
* values), and a bar whose own value is `na` still gets a result.
|
|
2256
2272
|
*/
|
|
2257
2273
|
export function range(source, length) {
|
|
2274
|
+
checkPositiveLength('range', length, 0);
|
|
2258
2275
|
return source.map((_, i) => {
|
|
2259
2276
|
const values = lastValues(source, i, length);
|
|
2260
2277
|
return values ? Math.max(...values) - Math.min(...values) : NaN;
|
|
@@ -2265,9 +2282,9 @@ export function range(source, length) {
|
|
|
2265
2282
|
* -1 for the previous bar, and so on (as in PineScript, the offset is negative).
|
|
2266
2283
|
*
|
|
2267
2284
|
* @remarks
|
|
2268
|
-
* As in PineScript: the best of the last `length` non-na values, the newest one on equal
|
|
2269
|
-
* distance in bars to its bar, so it is below `-(length - 1)` when `na` values were
|
|
2270
|
-
* `length - 1` and while no value was received; a fractional length is truncated.
|
|
2285
|
+
* As in PineScript: the best of the last `length` non-na values (+/-Infinity skipped), the newest one on equal
|
|
2286
|
+
* values; the offset is the distance in bars to its bar, so it is below `-(length - 1)` when `na` values were
|
|
2287
|
+
* skipped. `na` before bar `length - 1` and while no value was received; a fractional length is truncated.
|
|
2271
2288
|
*/
|
|
2272
2289
|
export function highestbars(source, length) {
|
|
2273
2290
|
return extremeOffset(source, length, false);
|
|
@@ -2277,9 +2294,9 @@ export function highestbars(source, length) {
|
|
|
2277
2294
|
* -1 for the previous bar, and so on (as in PineScript, the offset is negative).
|
|
2278
2295
|
*
|
|
2279
2296
|
* @remarks
|
|
2280
|
-
* As in PineScript: the best of the last `length` non-na values, the newest one on equal
|
|
2281
|
-
* distance in bars to its bar, so it is below `-(length - 1)` when `na` values were
|
|
2282
|
-
* `length - 1` and while no value was received; a fractional length is truncated.
|
|
2297
|
+
* As in PineScript: the best of the last `length` non-na values (+/-Infinity skipped), the newest one on equal
|
|
2298
|
+
* values; the offset is the distance in bars to its bar, so it is below `-(length - 1)` when `na` values were
|
|
2299
|
+
* skipped. `na` before bar `length - 1` and while no value was received; a fractional length is truncated.
|
|
2283
2300
|
*/
|
|
2284
2301
|
export function lowestbars(source, length) {
|
|
2285
2302
|
return extremeOffset(source, length, true);
|
|
@@ -2288,13 +2305,13 @@ export function lowestbars(source, length) {
|
|
|
2288
2305
|
* All-time highest value of `source` up to each bar (PineScript `ta.max(source)`).
|
|
2289
2306
|
*
|
|
2290
2307
|
* @remarks
|
|
2291
|
-
* As in PineScript: `na` values are skipped, the value carries over `na` bars, and the
|
|
2308
|
+
* As in PineScript: `na` values (also +/-Infinity) are skipped, the value carries over `na` bars, and the
|
|
2292
2309
|
* result is `na` until the first non-na value.
|
|
2293
2310
|
*/
|
|
2294
2311
|
export function max(source) {
|
|
2295
2312
|
let best = NaN;
|
|
2296
2313
|
return source.map((x) => {
|
|
2297
|
-
if (
|
|
2314
|
+
if (Number.isFinite(x) && !(x <= best))
|
|
2298
2315
|
best = x;
|
|
2299
2316
|
return best;
|
|
2300
2317
|
});
|
|
@@ -2303,13 +2320,13 @@ export function max(source) {
|
|
|
2303
2320
|
* All-time lowest value of `source` up to each bar (PineScript `ta.min(source)`).
|
|
2304
2321
|
*
|
|
2305
2322
|
* @remarks
|
|
2306
|
-
* As in PineScript: `na` values are skipped, the value carries over `na` bars, and the
|
|
2323
|
+
* As in PineScript: `na` values (also +/-Infinity) are skipped, the value carries over `na` bars, and the
|
|
2307
2324
|
* result is `na` until the first non-na value.
|
|
2308
2325
|
*/
|
|
2309
2326
|
export function min(source) {
|
|
2310
2327
|
let best = NaN;
|
|
2311
2328
|
return source.map((x) => {
|
|
2312
|
-
if (
|
|
2329
|
+
if (Number.isFinite(x) && !(x >= best))
|
|
2313
2330
|
best = x;
|
|
2314
2331
|
return best;
|
|
2315
2332
|
});
|
|
@@ -2337,6 +2354,7 @@ export function min(source) {
|
|
|
2337
2354
|
* ```
|
|
2338
2355
|
*/
|
|
2339
2356
|
export function cog(source, length = 10) {
|
|
2357
|
+
checkPositiveLength('cog', length, 0);
|
|
2340
2358
|
const len = Math.floor(length);
|
|
2341
2359
|
return runningSum(source, len).map((sum, i) => {
|
|
2342
2360
|
if (i < len - 1 || Number.isNaN(sum) || sum === 0)
|
|
@@ -2365,18 +2383,19 @@ export function cog(source, length = 10) {
|
|
|
2365
2383
|
*
|
|
2366
2384
|
* @remarks
|
|
2367
2385
|
* PineScript rules:
|
|
2368
|
-
* - the window is the last `length` non-`na` values (an `na` bar does not enter it, and gets the mode
|
|
2369
|
-
* window it keeps)
|
|
2386
|
+
* - the window is the last `length` non-`na` values (an `na` or +/-Infinity bar does not enter it, and gets the mode
|
|
2387
|
+
* of the window it keeps)
|
|
2370
2388
|
* - NaN until `length` non-`na` values exist
|
|
2371
2389
|
* - If no mode exists, returns the smallest value
|
|
2372
2390
|
*/
|
|
2373
2391
|
export function mode(source, length) {
|
|
2392
|
+
checkPositiveLength('mode', length, 0);
|
|
2374
2393
|
const result = [];
|
|
2375
2394
|
const len = Math.floor(length);
|
|
2376
2395
|
const seen = []; // non-na values so far
|
|
2377
2396
|
for (let i = 0; i < source.length; i++) {
|
|
2378
2397
|
const v = source[i];
|
|
2379
|
-
if (
|
|
2398
|
+
if (Number.isFinite(v))
|
|
2380
2399
|
seen.push(v);
|
|
2381
2400
|
if (seen.length < len) {
|
|
2382
2401
|
result.push(NaN);
|
|
@@ -2438,6 +2457,7 @@ export function mode(source, length) {
|
|
|
2438
2457
|
* `na` values are before all numbers
|
|
2439
2458
|
*/
|
|
2440
2459
|
export function percentile_linear_interpolation(source, length, percentage) {
|
|
2460
|
+
checkPositiveLength('percentile_linear_interpolation', length, 0);
|
|
2441
2461
|
return sortedWindow(source, length, (s) => {
|
|
2442
2462
|
const pos = (percentage / 100) * length - 0.5;
|
|
2443
2463
|
if (pos <= 0)
|
|
@@ -2504,6 +2524,7 @@ function sortedWindow(source, length, fn) {
|
|
|
2504
2524
|
* - `na` values stay in the window, sorted from bar to bar as in `ta.percentile_linear_interpolation`
|
|
2505
2525
|
*/
|
|
2506
2526
|
export function percentile_nearest_rank(source, length, percentage) {
|
|
2527
|
+
checkPositiveLength('percentile_nearest_rank', length, 0);
|
|
2507
2528
|
const rank = Math.min(length, Math.max(1, Math.ceil((percentage / 100) * length)));
|
|
2508
2529
|
return sortedWindow(source, length, (s) => s[rank - 1]);
|
|
2509
2530
|
}
|
|
@@ -2539,6 +2560,7 @@ export function percentile_nearest_rank(source, length, percentage) {
|
|
|
2539
2560
|
* where the `na` was
|
|
2540
2561
|
*/
|
|
2541
2562
|
export function rci(source, length) {
|
|
2563
|
+
checkPositiveLength('rci', length, 0);
|
|
2542
2564
|
const len = Math.floor(length);
|
|
2543
2565
|
const result = [];
|
|
2544
2566
|
// PineScript keeps the values in a ring of `length + 1` slots indexed by the bar index, filled with 0 at the start
|
|
@@ -2604,187 +2626,135 @@ export function rci(source, length) {
|
|
|
2604
2626
|
return result;
|
|
2605
2627
|
}
|
|
2606
2628
|
/**
|
|
2607
|
-
* Pivot Point Levels
|
|
2629
|
+
* Pivot Point Levels (PineScript `ta.pivot_point_levels`).
|
|
2608
2630
|
*
|
|
2609
|
-
*
|
|
2610
|
-
* Returns an array containing: [P, R1, S1, R2, S2, R3, S3, R4, S4, R5, S5]
|
|
2631
|
+
* Returns 11 series: [P, R1, S1, R2, S2, R3, S3, R4, S4, R5, S5] (one value per bar in each).
|
|
2611
2632
|
*
|
|
2612
2633
|
* @param type - Calculation type: "Traditional", "Fibonacci", "Woodie", "Classic", "DM", "Camarilla"
|
|
2613
|
-
* @param anchor - Condition that
|
|
2614
|
-
* @param developing -
|
|
2615
|
-
*
|
|
2616
|
-
* @param
|
|
2617
|
-
* @param
|
|
2618
|
-
* @param
|
|
2619
|
-
* @
|
|
2634
|
+
* @param anchor - Condition that starts a new period (`timeframe.change("W")`...)
|
|
2635
|
+
* @param developing - false: the levels of the previous period, constant until the next anchor; true: the levels of
|
|
2636
|
+
* the current period so far, recalculated on each bar
|
|
2637
|
+
* @param high - High price series
|
|
2638
|
+
* @param low - Low price series
|
|
2639
|
+
* @param close - Close price series
|
|
2640
|
+
* @param open - Open price series (Woodie and DM)
|
|
2641
|
+
* @returns 11 series of levels
|
|
2620
2642
|
*
|
|
2621
2643
|
* @example
|
|
2622
2644
|
* ```typescript
|
|
2623
2645
|
* const weekChange = [false, false, false, false, true, false, ...]; // Weekly anchor
|
|
2624
|
-
* const
|
|
2625
|
-
* //
|
|
2646
|
+
* const levels = ta.pivot_point_levels("Traditional", weekChange, false, high, low, close, open);
|
|
2647
|
+
* // levels[0]![i] is P on bar i, levels[5]![i] is R3
|
|
2626
2648
|
* ```
|
|
2627
2649
|
*
|
|
2628
2650
|
* @remarks
|
|
2629
|
-
*
|
|
2630
|
-
* -
|
|
2631
|
-
*
|
|
2651
|
+
* As in PineScript (#177):
|
|
2652
|
+
* - a period runs from an anchor bar to the bar before the next anchor; the bars before the first anchor are a
|
|
2653
|
+
* period too (from bar 0). The fixed levels use the high and low of the previous period and the close of the bar
|
|
2654
|
+
* before the anchor (na with an anchor on bar 0), and are na until the first anchor; the developing levels use
|
|
2655
|
+
* the current period's high and low and the bar's close, from bar 0
|
|
2656
|
+
* - Traditional: R3 = P * 2 + (H - 2 * L), S3 = P * 2 - (2 * H - L), R4 = P * 3 + (H - 3 * L), S4 = P * 3 - (3 * H - L),
|
|
2657
|
+
* R5 = P * 4 + (H - 4 * L), S5 = P * 4 - (4 * H - L)
|
|
2658
|
+
* - Fibonacci: P +- 0.382 / 0.618 / 1 * (H - L); R4 to S5 na
|
|
2659
|
+
* - Classic: R3 / S3 = P +- 2 * (H - L), R4 / S4 = P +- 3 * (H - L); R5 / S5 na
|
|
2660
|
+
* - Woodie: P = (H + L + 2 * open of the current period) / 4, R3 = H + 2 * (P - L), S3 = L - 2 * (H - P), R4 / S4 =
|
|
2661
|
+
* R3 + (H - L) / S3 - (H - L); R5 / S5 na. `developing` true throws (RE10128)
|
|
2662
|
+
* - DM: x = H + 2 * L + C (C < open of the period), 2 * H + L + C (C > O) or H + L + 2 * C; P = x / 4,
|
|
2663
|
+
* R1 = x / 2 - L, S1 = x / 2 - H; the other levels na
|
|
2664
|
+
* - Camarilla: C +- (H - L) * 1.1 / 12, / 6, / 4, / 2 for R1 to S4, R5 = (H / L) * C, S5 = 2 * C - R5
|
|
2632
2665
|
*/
|
|
2633
2666
|
export function pivot_point_levels(type, anchor, developing = false, high, low, close, open) {
|
|
2634
|
-
// For simplicity, we need to handle the case where type is a string array
|
|
2635
|
-
// But according to the docs, type is "series string", meaning it can change per bar
|
|
2636
|
-
// For this implementation, we'll support constant type strings
|
|
2637
2667
|
const typeStr = typeof type === 'string' ? type : String(type[0]);
|
|
2638
|
-
const isDeveloping = typeof developing === 'boolean' ? developing : developing[0];
|
|
2639
|
-
// Woodie cannot be developing
|
|
2668
|
+
const isDeveloping = typeof developing === 'boolean' ? developing : Boolean(developing[0]);
|
|
2640
2669
|
if (typeStr === 'Woodie' && isDeveloping) {
|
|
2641
|
-
throw new Error('ta.pivot_point_levels
|
|
2642
|
-
}
|
|
2643
|
-
|
|
2644
|
-
const
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
lastAnchorIndex = i;
|
|
2662
|
-
// Store OHLC at anchor point (these will be used for calculations)
|
|
2663
|
-
lastH = high ? high[i] : NaN;
|
|
2664
|
-
lastL = low ? low[i] : NaN;
|
|
2665
|
-
lastC = close ? close[i] : NaN;
|
|
2666
|
-
lastO = open ? open[i] : NaN;
|
|
2667
|
-
}
|
|
2668
|
-
// Calculate data to use
|
|
2669
|
-
let h, l, c, o;
|
|
2670
|
-
if (isDeveloping && lastAnchorIndex >= 0) {
|
|
2671
|
-
// Developing: use max/min/last since anchor
|
|
2672
|
-
h = high ? Math.max(...high.slice(lastAnchorIndex, i + 1).filter(v => !isNaN(v))) : NaN;
|
|
2673
|
-
l = low ? Math.min(...low.slice(lastAnchorIndex, i + 1).filter(v => !isNaN(v))) : NaN;
|
|
2674
|
-
c = close ? close[i] : NaN;
|
|
2675
|
-
o = open && lastAnchorIndex >= 0 ? open[lastAnchorIndex] : NaN;
|
|
2676
|
-
}
|
|
2677
|
-
else {
|
|
2678
|
-
// Not developing: use last anchor values
|
|
2679
|
-
h = lastH;
|
|
2680
|
-
l = lastL;
|
|
2681
|
-
c = lastC;
|
|
2682
|
-
o = lastO;
|
|
2683
|
-
}
|
|
2684
|
-
// Calculate pivot levels based on type
|
|
2685
|
-
const levels = calculatePivotLevels(typeStr, h, l, c, o);
|
|
2686
|
-
// Push to results
|
|
2687
|
-
for (let j = 0; j < 11; j++) {
|
|
2688
|
-
results[j].push(levels[j]);
|
|
2670
|
+
throw new Error('Error on bar 0: The `developing` parameter of the `ta.pivot_point_levels()` cannot be `true` when `type` is "Woodie".');
|
|
2671
|
+
}
|
|
2672
|
+
const n = anchor.length;
|
|
2673
|
+
for (const [s, name] of [[high, 'High'], [low, 'Low'], [close, 'Close'], [open, 'Open']]) {
|
|
2674
|
+
if (s && s.length !== n)
|
|
2675
|
+
throw new Error(`${name} series length mismatch`);
|
|
2676
|
+
}
|
|
2677
|
+
const at = (s, i) => (s ? (s[i] ?? NaN) : NaN);
|
|
2678
|
+
const results = Array.from({ length: 11 }, () => new Array(n));
|
|
2679
|
+
// the current period (from bar 0 until the first anchor) and the previous one
|
|
2680
|
+
let curH = NaN, curL = NaN, curO = NaN;
|
|
2681
|
+
let prevH = NaN, prevL = NaN, prevC = NaN, prevO = NaN;
|
|
2682
|
+
let anchored = false;
|
|
2683
|
+
for (let i = 0; i < n; i++) {
|
|
2684
|
+
const h = at(high, i), l = at(low, i);
|
|
2685
|
+
if (i === 0) {
|
|
2686
|
+
// the first period starts on bar 0: an anchor there makes bar 0 its own previous period
|
|
2687
|
+
curH = h;
|
|
2688
|
+
curL = l;
|
|
2689
|
+
curO = at(open, i);
|
|
2689
2690
|
}
|
|
2691
|
+
if (anchor[i]) {
|
|
2692
|
+
prevH = curH;
|
|
2693
|
+
prevL = curL;
|
|
2694
|
+
prevC = i > 0 ? at(close, i - 1) : NaN;
|
|
2695
|
+
prevO = curO;
|
|
2696
|
+
curH = h;
|
|
2697
|
+
curL = l;
|
|
2698
|
+
curO = at(open, i);
|
|
2699
|
+
anchored = true;
|
|
2700
|
+
}
|
|
2701
|
+
else if (i > 0) {
|
|
2702
|
+
if (h > curH || Number.isNaN(curH))
|
|
2703
|
+
curH = h;
|
|
2704
|
+
if (l < curL || Number.isNaN(curL))
|
|
2705
|
+
curL = l;
|
|
2706
|
+
}
|
|
2707
|
+
const levels = isDeveloping
|
|
2708
|
+
? pivotLevels(typeStr, curH, curL, at(close, i), curO, at(close, i))
|
|
2709
|
+
: anchored
|
|
2710
|
+
? pivotLevels(typeStr, prevH, prevL, prevC, prevO, curO)
|
|
2711
|
+
: NA_LEVELS;
|
|
2712
|
+
for (let k = 0; k < 11; k++)
|
|
2713
|
+
results[k][i] = levels[k];
|
|
2690
2714
|
}
|
|
2691
2715
|
return results;
|
|
2692
2716
|
}
|
|
2717
|
+
const NA_LEVELS = new Array(11).fill(NaN);
|
|
2693
2718
|
/**
|
|
2694
|
-
*
|
|
2719
|
+
* The 11 levels of one bar, in PineScript's operation order (bit for bit). `o` is the open of the period (DM),
|
|
2720
|
+
* `wo` the close term of Woodie (the open of the current period, or the bar's close while developing).
|
|
2695
2721
|
*/
|
|
2696
|
-
function
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
return Array(11).fill(NaN);
|
|
2700
|
-
}
|
|
2701
|
-
const levels = Array(11).fill(NaN);
|
|
2702
|
-
// Calculate pivot point (P)
|
|
2703
|
-
let P;
|
|
2722
|
+
function pivotLevels(type, h, l, c, o, wo) {
|
|
2723
|
+
const N = NaN;
|
|
2724
|
+
const r = h - l;
|
|
2704
2725
|
switch (type) {
|
|
2705
|
-
case 'Traditional':
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
case '
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
switch (type) {
|
|
2725
|
-
case 'Traditional':
|
|
2726
|
-
case 'Classic':
|
|
2727
|
-
levels[1] = 2 * P - l; // R1
|
|
2728
|
-
levels[2] = 2 * P - h; // S1
|
|
2729
|
-
levels[3] = P + (h - l); // R2
|
|
2730
|
-
levels[4] = P - (h - l); // S2
|
|
2731
|
-
levels[5] = h + 2 * (P - l); // R3
|
|
2732
|
-
levels[6] = l - 2 * (h - P); // S3
|
|
2733
|
-
levels[7] = levels[5] + (h - l); // R4
|
|
2734
|
-
levels[8] = levels[6] - (h - l); // S4
|
|
2735
|
-
levels[9] = levels[7] + (h - l); // R5
|
|
2736
|
-
levels[10] = levels[8] - (h - l); // S5
|
|
2737
|
-
break;
|
|
2738
|
-
case 'Fibonacci':
|
|
2739
|
-
levels[1] = P + 0.382 * (h - l); // R1
|
|
2740
|
-
levels[2] = P - 0.382 * (h - l); // S1
|
|
2741
|
-
levels[3] = P + 0.618 * (h - l); // R2
|
|
2742
|
-
levels[4] = P - 0.618 * (h - l); // S2
|
|
2743
|
-
levels[5] = P + (h - l); // R3
|
|
2744
|
-
levels[6] = P - (h - l); // S3
|
|
2745
|
-
levels[7] = levels[5] + 0.618 * (h - l); // R4
|
|
2746
|
-
levels[8] = levels[6] - 0.618 * (h - l); // S4
|
|
2747
|
-
levels[9] = levels[7] + 0.382 * (h - l); // R5
|
|
2748
|
-
levels[10] = levels[8] - 0.382 * (h - l); // S5
|
|
2749
|
-
break;
|
|
2750
|
-
case 'Woodie':
|
|
2751
|
-
levels[1] = 2 * P - l; // R1
|
|
2752
|
-
levels[2] = 2 * P - h; // S1
|
|
2753
|
-
levels[3] = P + (h - l); // R2
|
|
2754
|
-
levels[4] = P - (h - l); // S2
|
|
2755
|
-
levels[5] = h + 2 * (P - l); // R3
|
|
2756
|
-
levels[6] = l - 2 * (h - P); // S3
|
|
2757
|
-
levels[7] = levels[5] + (h - l); // R4
|
|
2758
|
-
levels[8] = levels[6] - (h - l); // S4
|
|
2759
|
-
levels[9] = levels[7] + (h - l); // R5
|
|
2760
|
-
levels[10] = levels[8] - (h - l); // S5
|
|
2761
|
-
break;
|
|
2726
|
+
case 'Traditional': {
|
|
2727
|
+
const p = (h + l + c) / 3;
|
|
2728
|
+
return [p, 2 * p - l, 2 * p - h, p + r, p - r, p * 2 + (h - 2 * l), p * 2 - (2 * h - l), p * 3 + (h - 3 * l),
|
|
2729
|
+
p * 3 - (3 * h - l), p * 4 + (h - 4 * l), p * 4 - (4 * h - l)];
|
|
2730
|
+
}
|
|
2731
|
+
case 'Fibonacci': {
|
|
2732
|
+
const p = (h + l + c) / 3;
|
|
2733
|
+
return [p, p + 0.382 * r, p - 0.382 * r, p + 0.618 * r, p - 0.618 * r, p + 1 * r, p - 1 * r, N, N, N, N];
|
|
2734
|
+
}
|
|
2735
|
+
case 'Classic': {
|
|
2736
|
+
const p = (h + l + c) / 3;
|
|
2737
|
+
return [p, 2 * p - l, 2 * p - h, p + r, p - r, p + 2 * r, p - 2 * r, p + 3 * r, p - 3 * r, N, N];
|
|
2738
|
+
}
|
|
2739
|
+
case 'Woodie': {
|
|
2740
|
+
const p = (h + l + 2 * wo) / 4;
|
|
2741
|
+
const r3 = h + 2 * (p - l);
|
|
2742
|
+
const s3 = l - 2 * (h - p);
|
|
2743
|
+
return [p, 2 * p - l, 2 * p - h, p + r, p - r, r3, s3, r3 + r, s3 - r, N, N];
|
|
2744
|
+
}
|
|
2762
2745
|
case 'DM': {
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
const newP = x / (isNaN(o) ? 4 : 5);
|
|
2766
|
-
levels[0] = newP;
|
|
2767
|
-
levels[1] = x / 2 - l; // R1
|
|
2768
|
-
levels[2] = x / 2 - h; // S1
|
|
2769
|
-
// R2-S5 remain NaN
|
|
2770
|
-
break;
|
|
2746
|
+
const x = c < o ? h + 2 * l + c : c > o ? 2 * h + l + c : h + l + 2 * c;
|
|
2747
|
+
return [x / 4, x / 2 - l, x / 2 - h, N, N, N, N, N, N, N, N];
|
|
2771
2748
|
}
|
|
2772
2749
|
case 'Camarilla': {
|
|
2773
|
-
const
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
levels[4] = c - range * 1.1 / 6; // S2
|
|
2778
|
-
levels[5] = c + range * 1.1 / 4; // R3
|
|
2779
|
-
levels[6] = c - range * 1.1 / 4; // S3
|
|
2780
|
-
levels[7] = c + range * 1.1 / 2; // R4
|
|
2781
|
-
levels[8] = c - range * 1.1 / 2; // S4
|
|
2782
|
-
levels[9] = h; // R5 (high)
|
|
2783
|
-
levels[10] = l; // S5 (low)
|
|
2784
|
-
break;
|
|
2750
|
+
const p = (h + l + c) / 3;
|
|
2751
|
+
const r5 = (h / l) * c;
|
|
2752
|
+
return [p, c + (r * 1.1) / 12, c - (r * 1.1) / 12, c + (r * 1.1) / 6, c - (r * 1.1) / 6, c + (r * 1.1) / 4,
|
|
2753
|
+
c - (r * 1.1) / 4, c + (r * 1.1) / 2, c - (r * 1.1) / 2, r5, 2 * c - r5];
|
|
2785
2754
|
}
|
|
2755
|
+
default:
|
|
2756
|
+
return NA_LEVELS;
|
|
2786
2757
|
}
|
|
2787
|
-
return levels;
|
|
2788
2758
|
}
|
|
2789
2759
|
/**
|
|
2790
2760
|
* Ichimoku Kinko Hyo (Ichimoku Cloud) - Japanese charting technique for trend identification.
|