oakscriptjs 0.10.1 → 0.11.1
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.d.ts.map +1 -1
- package/dist/cjs/box/index.js +7 -1
- package/dist/cjs/box/index.js.map +2 -2
- package/dist/cjs/callsite/index.d.ts +73 -32
- package/dist/cjs/callsite/index.d.ts.map +1 -1
- package/dist/cjs/callsite/index.js +69 -65
- package/dist/cjs/callsite/index.js.map +2 -2
- package/dist/cjs/drawing/registry.d.ts +84 -0
- package/dist/cjs/drawing/registry.d.ts.map +1 -1
- package/dist/cjs/drawing/registry.js +153 -6
- package/dist/cjs/drawing/registry.js.map +2 -2
- package/dist/cjs/index.d.ts +8 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +33 -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.d.ts.map +1 -1
- package/dist/cjs/label/index.js +3 -0
- 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 +11 -22
- 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/math/index.d.ts.map +1 -1
- package/dist/cjs/math/index.js +2 -0
- package/dist/cjs/math/index.js.map +2 -2
- package/dist/cjs/polyline/index.d.ts.map +1 -1
- package/dist/cjs/polyline/index.js +3 -0
- package/dist/cjs/polyline/index.js.map +2 -2
- 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 +91 -21
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +207 -19
- 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 +24 -1
- package/dist/cjs/session/bars.d.ts.map +1 -1
- package/dist/cjs/session/bars.js +56 -7
- package/dist/cjs/session/bars.js.map +3 -3
- package/dist/cjs/session/calendar.d.ts +13 -2
- package/dist/cjs/session/calendar.d.ts.map +1 -1
- package/dist/cjs/session/calendar.js +39 -9
- package/dist/cjs/session/calendar.js.map +2 -2
- package/dist/cjs/session/context.d.ts +76 -0
- package/dist/cjs/session/context.d.ts.map +1 -0
- package/dist/cjs/session/context.js +114 -0
- package/dist/cjs/session/context.js.map +7 -0
- 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 -1
- package/dist/cjs/str/index.d.ts.map +1 -1
- package/dist/cjs/str/index.js +2 -1
- package/dist/cjs/str/index.js.map +2 -2
- package/dist/cjs/str/numberformat.d.ts +7 -3
- package/dist/cjs/str/numberformat.d.ts.map +1 -1
- package/dist/cjs/str/numberformat.js +4 -3
- package/dist/cjs/str/numberformat.js.map +2 -2
- package/dist/cjs/ta/index.d.ts +67 -34
- package/dist/cjs/ta/index.d.ts.map +1 -1
- package/dist/cjs/ta/index.js +202 -145
- package/dist/cjs/ta/index.js.map +2 -2
- package/dist/cjs/ta/limits.d.ts +35 -0
- package/dist/cjs/ta/limits.d.ts.map +1 -0
- package/dist/cjs/ta/limits.js +64 -0
- package/dist/cjs/ta/limits.js.map +7 -0
- package/dist/cjs/ta/running-sum.d.ts +0 -16
- package/dist/cjs/ta/running-sum.d.ts.map +1 -1
- package/dist/cjs/ta/running-sum.js +2 -0
- package/dist/cjs/ta/running-sum.js.map +2 -2
- package/dist/cjs/ta-series.d.ts +8 -8
- package/dist/cjs/ta-series.d.ts.map +1 -1
- package/dist/cjs/ta-series.js +10 -8
- package/dist/cjs/ta-series.js.map +2 -2
- package/dist/cjs/table/index.d.ts +3 -1
- package/dist/cjs/table/index.d.ts.map +1 -1
- package/dist/cjs/table/index.js +1 -0
- package/dist/cjs/table/index.js.map +3 -3
- package/dist/cjs/time/index.d.ts +5 -2
- package/dist/cjs/time/index.d.ts.map +1 -1
- package/dist/cjs/time/index.js +2 -2
- package/dist/cjs/time/index.js.map +2 -2
- package/dist/cjs/time/session.d.ts +26 -1
- package/dist/cjs/time/session.d.ts.map +1 -1
- package/dist/cjs/time/session.js +94 -12
- 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 +28 -4
- 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.d.ts.map +1 -1
- package/dist/esm/box/index.js +8 -2
- package/dist/esm/box/index.js.map +1 -1
- package/dist/esm/callsite/index.d.ts +73 -32
- package/dist/esm/callsite/index.d.ts.map +1 -1
- package/dist/esm/callsite/index.js +143 -106
- package/dist/esm/callsite/index.js.map +1 -1
- package/dist/esm/drawing/registry.d.ts +84 -0
- package/dist/esm/drawing/registry.d.ts.map +1 -1
- package/dist/esm/drawing/registry.js +260 -6
- package/dist/esm/drawing/registry.js.map +1 -1
- package/dist/esm/index.d.ts +8 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +13 -2
- 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.d.ts.map +1 -1
- package/dist/esm/label/index.js +4 -1
- 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 +22 -60
- 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/math/index.d.ts.map +1 -1
- package/dist/esm/math/index.js +3 -0
- package/dist/esm/math/index.js.map +1 -1
- package/dist/esm/polyline/index.d.ts.map +1 -1
- package/dist/esm/polyline/index.js +5 -1
- package/dist/esm/polyline/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 +91 -21
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +259 -28
- 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 +24 -1
- package/dist/esm/session/bars.d.ts.map +1 -1
- package/dist/esm/session/bars.js +75 -6
- package/dist/esm/session/bars.js.map +1 -1
- package/dist/esm/session/calendar.d.ts +13 -2
- package/dist/esm/session/calendar.d.ts.map +1 -1
- package/dist/esm/session/calendar.js +54 -10
- package/dist/esm/session/calendar.js.map +1 -1
- package/dist/esm/session/context.d.ts +76 -0
- package/dist/esm/session/context.d.ts.map +1 -0
- package/dist/esm/session/context.js +124 -0
- package/dist/esm/session/context.js.map +1 -0
- 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 -1
- package/dist/esm/str/index.d.ts.map +1 -1
- package/dist/esm/str/index.js +9 -2
- package/dist/esm/str/index.js.map +1 -1
- package/dist/esm/str/numberformat.d.ts +7 -3
- package/dist/esm/str/numberformat.d.ts.map +1 -1
- package/dist/esm/str/numberformat.js +11 -5
- package/dist/esm/str/numberformat.js.map +1 -1
- package/dist/esm/ta/index.d.ts +67 -34
- package/dist/esm/ta/index.d.ts.map +1 -1
- package/dist/esm/ta/index.js +284 -240
- package/dist/esm/ta/index.js.map +1 -1
- package/dist/esm/ta/limits.d.ts +35 -0
- package/dist/esm/ta/limits.d.ts.map +1 -0
- package/dist/esm/ta/limits.js +67 -0
- package/dist/esm/ta/limits.js.map +1 -0
- package/dist/esm/ta/running-sum.d.ts +0 -16
- package/dist/esm/ta/running-sum.d.ts.map +1 -1
- package/dist/esm/ta/running-sum.js +2 -0
- package/dist/esm/ta/running-sum.js.map +1 -1
- package/dist/esm/ta-series.d.ts +8 -8
- package/dist/esm/ta-series.d.ts.map +1 -1
- package/dist/esm/ta-series.js +11 -8
- package/dist/esm/ta-series.js.map +1 -1
- package/dist/esm/table/index.d.ts +3 -1
- package/dist/esm/table/index.d.ts.map +1 -1
- package/dist/esm/table/index.js +6 -1
- package/dist/esm/table/index.js.map +1 -1
- package/dist/esm/time/index.d.ts +5 -2
- package/dist/esm/time/index.d.ts.map +1 -1
- package/dist/esm/time/index.js +6 -3
- package/dist/esm/time/index.js.map +1 -1
- package/dist/esm/time/session.d.ts +26 -1
- package/dist/esm/time/session.d.ts.map +1 -1
- package/dist/esm/time/session.js +141 -16
- 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 +28 -4
- 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 +8 -2
- package/src/callsite/index.ts +150 -100
- package/src/drawing/registry.ts +278 -5
- package/src/index.ts +44 -1
- package/src/input.ts +18 -1
- package/src/label/index.ts +4 -1
- package/src/line/index.ts +22 -64
- package/src/linefill/index.ts +20 -1
- package/src/math/index.ts +2 -0
- package/src/polyline/index.ts +3 -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 +355 -34
- package/src/security/other.ts +269 -0
- package/src/session/bars.ts +70 -6
- package/src/session/calendar.ts +60 -10
- package/src/session/context.ts +162 -0
- package/src/str/floatingdecimal.ts +268 -0
- package/src/str/index.ts +8 -2
- package/src/str/numberformat.ts +13 -6
- package/src/ta/index.ts +304 -248
- package/src/ta/limits.ts +74 -0
- package/src/ta/running-sum.ts +2 -0
- package/src/ta-series.ts +19 -16
- package/src/table/index.ts +4 -1
- package/src/time/index.ts +6 -3
- package/src/time/session.ts +137 -15
- package/src/time/timezone.ts +9 -2
- package/src/types/metadata.ts +28 -4
package/dist/esm/ta/index.js
CHANGED
|
@@ -9,6 +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 { checkPositiveLength, checkSumLength, checkWindowLength } from './limits.js';
|
|
12
13
|
/**
|
|
13
14
|
* Simple Moving Average - returns the moving average (sum of last y values divided by y).
|
|
14
15
|
*
|
|
@@ -39,6 +40,7 @@ export function sma(source, length) {
|
|
|
39
40
|
}
|
|
40
41
|
// Floor the length to match PineScript's auto-truncation of float to int
|
|
41
42
|
const len = Math.floor(length);
|
|
43
|
+
checkSumLength('ta.sma', len, 0);
|
|
42
44
|
return runningSum(source, len).map((sum) => sum / len);
|
|
43
45
|
}
|
|
44
46
|
/**
|
|
@@ -62,6 +64,7 @@ export function sma(source, length) {
|
|
|
62
64
|
* ```
|
|
63
65
|
*/
|
|
64
66
|
export function ema(source, length) {
|
|
67
|
+
checkPositiveLength('ema', length, 0);
|
|
65
68
|
const result = [];
|
|
66
69
|
const len = Math.floor(length);
|
|
67
70
|
const multiplier = 2 / (len + 1);
|
|
@@ -99,32 +102,20 @@ export function ema(source, length) {
|
|
|
99
102
|
}
|
|
100
103
|
return result;
|
|
101
104
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Relative Strength Index - momentum oscillator measuring speed and magnitude of price changes.
|
|
104
|
-
*
|
|
105
|
-
* @param source - Series of values to process
|
|
106
|
-
* @param length - Number of bars (length)
|
|
107
|
-
* @returns RSI series (values range from 0 to 100)
|
|
108
|
-
*
|
|
109
|
-
* @remarks
|
|
110
|
-
* - RSI values above 70 typically indicate overbought conditions
|
|
111
|
-
* - RSI values below 30 typically indicate oversold conditions
|
|
112
|
-
* - Uses `ta.rma()` (Relative Moving Average) for smoothing, matching PineScript v6
|
|
113
|
-
* - Formula: RSI = 100 - (100 / (1 + RS)), where RS = Average Gain / Average Loss
|
|
114
|
-
* - As in PineScript (`down == 0 ? 100 : up == 0 ? 0 : ...`): 100 when the average loss is 0, else 0 when the
|
|
115
|
-
* average gain is 0, both with the 1e-10 tolerance of the comparison operators
|
|
116
|
-
* - Average Gain and Average Loss are calculated using RMA (alpha = 1 / length)
|
|
117
|
-
* - `na` values in the source series are ignored
|
|
118
|
-
*
|
|
119
|
-
* @example
|
|
120
|
-
* ```typescript
|
|
121
|
-
* const rsi14 = ta.rsi(closePrices, 14);
|
|
122
|
-
* // Identify overbought/oversold conditions
|
|
123
|
-
* const overbought = rsi14.map(v => v > 70);
|
|
124
|
-
* const oversold = rsi14.map(v => v < 30);
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
127
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
|
+
const r = 100 - 100 / (1 + x / y);
|
|
115
|
+
return Number.isFinite(r) ? r : NaN;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
checkPositiveLength('rsi', length, 0, 'y');
|
|
128
119
|
const result = [];
|
|
129
120
|
// Calculate price changes
|
|
130
121
|
const changes = [];
|
|
@@ -208,6 +199,7 @@ export function macd(source, fastLength, slowLength, signalLength) {
|
|
|
208
199
|
* ```
|
|
209
200
|
*/
|
|
210
201
|
export function bb(series, length, mult) {
|
|
202
|
+
checkSumLength('ta.bb', length, 0);
|
|
211
203
|
const basis = sma(series, length);
|
|
212
204
|
const dev = stdev(series, length);
|
|
213
205
|
const upper = [];
|
|
@@ -250,14 +242,15 @@ function lastValues(source, i, length) {
|
|
|
250
242
|
}
|
|
251
243
|
/**
|
|
252
244
|
* The extreme of the window of `length` bars ending at each bar, in one pass (monotonic deque): the bar index of
|
|
253
|
-
* the oldest highest (or lowest) value, or -1 when the window is empty.
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* (lowest), the zero that `Math.max` / `Math.min`
|
|
245
|
+
* the oldest highest (or lowest) value (the newest one with `newest`), or -1 when the window is empty. The window
|
|
246
|
+
* stops at the first `na` value going back (the callers pass the non-na values only, see {@link keptValues}), and a
|
|
247
|
+
* fractional length is truncated (7.5 is 7 bars), as PineScript does.
|
|
248
|
+
* `zeros[i]` tells whether the window holds a +0 (highest) or a -0 (lowest), the zero that `Math.max` / `Math.min`
|
|
249
|
+
* return when the extreme is 0 (not kept with `newest`).
|
|
257
250
|
*/
|
|
258
|
-
function windowExtremes(source, length, lowest) {
|
|
251
|
+
function windowExtremes(source, length, lowest, newest = false) {
|
|
259
252
|
const n = source.length;
|
|
260
|
-
const span = length > 0 ? Math.
|
|
253
|
+
const span = length > 0 ? Math.trunc(length) : 0; // PineScript casts the length to int
|
|
261
254
|
const at = new Int32Array(n).fill(-1);
|
|
262
255
|
const zeros = new Uint8Array(n);
|
|
263
256
|
const queue = new Int32Array(n); // bar indices; values from best to worst, the oldest first among equal values
|
|
@@ -275,8 +268,11 @@ function windowExtremes(source, length, lowest) {
|
|
|
275
268
|
zeroCount = 0;
|
|
276
269
|
continue;
|
|
277
270
|
}
|
|
278
|
-
// drop the values that the new one beats; equal values stay (the oldest wins ties)
|
|
279
|
-
while (tail > head
|
|
271
|
+
// drop the values that the new one beats; equal values stay (the oldest wins ties), or go with `newest`
|
|
272
|
+
while (tail > head) {
|
|
273
|
+
const last = source[queue[tail - 1]];
|
|
274
|
+
if (!(lowest ? last > x : last < x) && !(newest && last === x))
|
|
275
|
+
break;
|
|
280
276
|
if (isPreferredZero(queue[--tail]))
|
|
281
277
|
zeroCount--;
|
|
282
278
|
}
|
|
@@ -295,20 +291,81 @@ function windowExtremes(source, length, lowest) {
|
|
|
295
291
|
}
|
|
296
292
|
return { at, zeros };
|
|
297
293
|
}
|
|
298
|
-
/**
|
|
294
|
+
/**
|
|
295
|
+
* The non-na values of `source` with their bars, and for each bar the position of its last non-na value (-1 when
|
|
296
|
+
* there is none yet). PineScript's ta.highest / ta.lowest / ta.highestbars / ta.lowestbars / ta.pivothigh /
|
|
297
|
+
* ta.pivotlow keep the non-na values they receive and nothing else.
|
|
298
|
+
*/
|
|
299
|
+
function keptValues(source) {
|
|
300
|
+
const values = [];
|
|
301
|
+
const bars = [];
|
|
302
|
+
const last = new Int32Array(source.length);
|
|
303
|
+
for (let i = 0; i < source.length; i++) {
|
|
304
|
+
const x = source[i];
|
|
305
|
+
if (!Number.isNaN(x)) {
|
|
306
|
+
values.push(x);
|
|
307
|
+
bars.push(i);
|
|
308
|
+
}
|
|
309
|
+
last[i] = values.length - 1;
|
|
310
|
+
}
|
|
311
|
+
return { values, bars, last };
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* ta.highest / ta.lowest / ta.highestbars / ta.lowestbars with a series length (a length per bar): on each bar the
|
|
315
|
+
* best of the last `length[i]` non-na values (the newest one on ties), na before bar `length[i] - 1`, as PineScript
|
|
316
|
+
* (measured with a length that changes on each bar, with and without na in the source).
|
|
317
|
+
*/
|
|
318
|
+
function extremeSeriesLength(source, lengths, lowest, offsets) {
|
|
319
|
+
const name = (lowest ? 'lowest' : 'highest') + (offsets ? 'bars' : '');
|
|
320
|
+
const { values, bars, last } = keptValues(source);
|
|
321
|
+
return Array.from(source, (_, i) => {
|
|
322
|
+
const len = Math.trunc(lengths[i]);
|
|
323
|
+
checkWindowLength(name, len, i);
|
|
324
|
+
const k = last[i];
|
|
325
|
+
if (!(len >= 1) || i < len - 1 || k < 0)
|
|
326
|
+
return NaN;
|
|
327
|
+
let best = k;
|
|
328
|
+
for (let j = k - 1; j >= 0 && j > k - len; j--) {
|
|
329
|
+
if (lowest ? values[j] < values[best] : values[j] > values[best])
|
|
330
|
+
best = j;
|
|
331
|
+
}
|
|
332
|
+
return offsets ? -(i - bars[best]) : values[best];
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Offset to the best of the last `length` non-na values (the newest one on ties): the distance in bars, so below
|
|
337
|
+
* `-(length - 1)` after an na value. na before bar `length - 1` and while there is no value.
|
|
338
|
+
*/
|
|
299
339
|
function extremeOffset(source, length, lowest) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
340
|
+
if (typeof length !== 'number')
|
|
341
|
+
return extremeSeriesLength(source, length, lowest, true);
|
|
342
|
+
checkWindowLength(lowest ? 'lowestbars' : 'highestbars', length, 0);
|
|
343
|
+
const { values, bars, last } = keptValues(source);
|
|
344
|
+
const { at } = windowExtremes(values, length, lowest, true);
|
|
345
|
+
const len = Math.trunc(length);
|
|
346
|
+
// -(i - bar): -0 when the extreme is the current bar, as the offset -j of the window scan
|
|
347
|
+
return source.map((_, i) => {
|
|
348
|
+
const k = last[i];
|
|
349
|
+
return i < len - 1 || k < 0 || at[k] < 0 ? NaN : -(i - bars[at[k]]);
|
|
350
|
+
});
|
|
303
351
|
}
|
|
304
|
-
/**
|
|
352
|
+
/**
|
|
353
|
+
* Best of the last `length` non-na values (of all of them when there are fewer), with the zero sign of `Math.max` /
|
|
354
|
+
* `Math.min`. na before bar `length - 1` and while there is no value.
|
|
355
|
+
*/
|
|
305
356
|
function extremeValue(source, length, lowest) {
|
|
306
|
-
|
|
357
|
+
if (typeof length !== 'number')
|
|
358
|
+
return extremeSeriesLength(source, length, lowest, false);
|
|
359
|
+
checkWindowLength(lowest ? 'lowest' : 'highest', length, 0);
|
|
360
|
+
const { values, last } = keptValues(source);
|
|
361
|
+
const { at, zeros } = windowExtremes(values, length, lowest);
|
|
362
|
+
const len = Math.trunc(length);
|
|
307
363
|
return source.map((_, i) => {
|
|
308
|
-
|
|
364
|
+
const k = last[i];
|
|
365
|
+
if (i < len - 1 || k < 0 || at[k] < 0)
|
|
309
366
|
return NaN;
|
|
310
|
-
const v =
|
|
311
|
-
return v === 0 && zeros[
|
|
367
|
+
const v = values[at[k]];
|
|
368
|
+
return v === 0 && zeros[k] ? (lowest ? -0 : 0) : v;
|
|
312
369
|
});
|
|
313
370
|
}
|
|
314
371
|
/**
|
|
@@ -395,6 +452,7 @@ export function crossunder(series1, series2) {
|
|
|
395
452
|
* ```
|
|
396
453
|
*/
|
|
397
454
|
export function change(source, length = 1) {
|
|
455
|
+
checkPositiveLength('change', length, 0);
|
|
398
456
|
const result = [];
|
|
399
457
|
for (let i = 0; i < source.length; i++) {
|
|
400
458
|
if (i < length) {
|
|
@@ -492,6 +550,7 @@ export function tr(handle_na = false, high, low, close) {
|
|
|
492
550
|
* ```
|
|
493
551
|
*/
|
|
494
552
|
export function atr(length, high, low, close) {
|
|
553
|
+
checkPositiveLength('atr->rma', length, 0);
|
|
495
554
|
if (!high || !low || !close) {
|
|
496
555
|
throw new Error('ta.atr() requires high, low, and close series. ' +
|
|
497
556
|
'Either pass them explicitly or use createContext({ chart: { high, low, close } }) for implicit data.');
|
|
@@ -616,6 +675,7 @@ export function supertrend(factor, atrPeriod, high, low, close, wicks = false) {
|
|
|
616
675
|
* ```
|
|
617
676
|
*/
|
|
618
677
|
export function rma(source, length) {
|
|
678
|
+
checkPositiveLength('rma', length, 0);
|
|
619
679
|
const result = [];
|
|
620
680
|
// Floor the length to match PineScript's auto-truncation of float to int
|
|
621
681
|
const len = Math.floor(length);
|
|
@@ -682,6 +742,8 @@ function wmaAdd(a, b) {
|
|
|
682
742
|
* - Summed from the oldest value to the newest; as PineScript, a partial sum `s + t` with
|
|
683
743
|
* `|s + t| <= 1e-10 * max(1, |s| + |t|)` is 0 (a cancellation residue, or values below 1e-10)
|
|
684
744
|
* - More responsive to recent price changes than SMA
|
|
745
|
+
* - `length` can be an array (a length per bar, PineScript series length): each bar gives the result of its own
|
|
746
|
+
* length (`na` for a length below 1)
|
|
685
747
|
*
|
|
686
748
|
* @example
|
|
687
749
|
* ```typescript
|
|
@@ -692,7 +754,9 @@ function wmaAdd(a, b) {
|
|
|
692
754
|
export function wma(source, length) {
|
|
693
755
|
const result = [];
|
|
694
756
|
// Floor the length to match PineScript's auto-truncation of float to int
|
|
695
|
-
const
|
|
757
|
+
const fixed = typeof length === 'number' ? Math.floor(length) : NaN;
|
|
758
|
+
if (typeof length === 'number')
|
|
759
|
+
checkPositiveLength('wma', fixed, 0);
|
|
696
760
|
// PineScript: na on a bar with an na source (also +/-Infinity) and until `len` non-na values were received;
|
|
697
761
|
// otherwise the last `len` bars, each na replaced by the previous non-na value
|
|
698
762
|
const filled = [];
|
|
@@ -706,7 +770,11 @@ export function wma(source, length) {
|
|
|
706
770
|
count++;
|
|
707
771
|
}
|
|
708
772
|
filled.push(last);
|
|
709
|
-
|
|
773
|
+
// a series length: the window of this bar's length (the result of ta.wma with that fixed length)
|
|
774
|
+
const len = typeof length === 'number' ? fixed : Math.floor(length[i]);
|
|
775
|
+
if (typeof length !== 'number')
|
|
776
|
+
checkPositiveLength('wma', len, i);
|
|
777
|
+
if (isNa || !(len >= 1) || count < len) {
|
|
710
778
|
result.push(NaN);
|
|
711
779
|
continue;
|
|
712
780
|
}
|
|
@@ -730,8 +798,12 @@ export function wma(source, length) {
|
|
|
730
798
|
* @returns Highest value series (na on the first `length - 1` bars)
|
|
731
799
|
*
|
|
732
800
|
* @remarks
|
|
733
|
-
* As in PineScript:
|
|
734
|
-
*
|
|
801
|
+
* As in PineScript: `na` values are skipped. The result is the highest of the last `length` non-na values (of all
|
|
802
|
+
* of them when there are fewer), also on a bar whose own value is `na`; `na` before bar `length - 1` and while no
|
|
803
|
+
* value was received. A fractional length is truncated (7.5 is 7 bars).
|
|
804
|
+
* `length` can be an array (a length per bar, PineScript series length), also for `ta.lowest`, `ta.highestbars`
|
|
805
|
+
* and `ta.lowestbars`: each bar uses its own length.
|
|
806
|
+
* A length above 15,000 throws the PineScript runtime error (also `ta.lowest`, `ta.highestbars`, `ta.lowestbars`).
|
|
735
807
|
* PineScript `ta.highest(length)` (source = high) is `ta.highest(length)` in the script API.
|
|
736
808
|
*/
|
|
737
809
|
export function highest(source, length) {
|
|
@@ -745,8 +817,9 @@ export function highest(source, length) {
|
|
|
745
817
|
* @returns Lowest value series (na on the first `length - 1` bars)
|
|
746
818
|
*
|
|
747
819
|
* @remarks
|
|
748
|
-
* As in PineScript:
|
|
749
|
-
*
|
|
820
|
+
* As in PineScript: `na` values are skipped. The result is the lowest of the last `length` non-na values (of all
|
|
821
|
+
* of them when there are fewer), also on a bar whose own value is `na`; `na` before bar `length - 1` and while no
|
|
822
|
+
* value was received.
|
|
750
823
|
*/
|
|
751
824
|
export function lowest(source, length) {
|
|
752
825
|
return extremeValue(source, length, true);
|
|
@@ -860,6 +933,7 @@ function monotonic(source, length, step) {
|
|
|
860
933
|
* ```
|
|
861
934
|
*/
|
|
862
935
|
export function rising(source, length) {
|
|
936
|
+
checkPositiveLength('rising', length, 0);
|
|
863
937
|
return monotonic(source, length, gt);
|
|
864
938
|
}
|
|
865
939
|
/**
|
|
@@ -884,6 +958,7 @@ export function rising(source, length) {
|
|
|
884
958
|
* ```
|
|
885
959
|
*/
|
|
886
960
|
export function falling(source, length) {
|
|
961
|
+
checkPositiveLength('falling', length, 0);
|
|
887
962
|
return monotonic(source, length, lt);
|
|
888
963
|
}
|
|
889
964
|
/**
|
|
@@ -908,6 +983,7 @@ export function falling(source, length) {
|
|
|
908
983
|
* ```
|
|
909
984
|
*/
|
|
910
985
|
export function roc(source, length) {
|
|
986
|
+
checkPositiveLength('roc', length, 0);
|
|
911
987
|
const result = [];
|
|
912
988
|
for (let i = 0; i < source.length; i++) {
|
|
913
989
|
if (i < length) {
|
|
@@ -947,6 +1023,7 @@ export function roc(source, length) {
|
|
|
947
1023
|
* ```
|
|
948
1024
|
*/
|
|
949
1025
|
export function mom(source, length) {
|
|
1026
|
+
checkPositiveLength('mom', length, 0);
|
|
950
1027
|
const result = [];
|
|
951
1028
|
for (let i = 0; i < source.length; i++) {
|
|
952
1029
|
if (i < length) {
|
|
@@ -985,6 +1062,7 @@ export function mom(source, length) {
|
|
|
985
1062
|
* ```
|
|
986
1063
|
*/
|
|
987
1064
|
export function dev(source, length) {
|
|
1065
|
+
checkPositiveLength('dev', length, 0);
|
|
988
1066
|
const len = Math.floor(length);
|
|
989
1067
|
return sma(source, len).map((mean, i) => {
|
|
990
1068
|
if (i < len - 1 || Number.isNaN(mean))
|
|
@@ -1031,6 +1109,7 @@ function varianceOf(source, length, biased, name) {
|
|
|
1031
1109
|
return biased ? result : result.map((v, i) => (Math.floor(length[i]) > 1 ? v : NaN));
|
|
1032
1110
|
}
|
|
1033
1111
|
const len = Math.floor(length);
|
|
1112
|
+
checkSumLength(name, len, 0);
|
|
1034
1113
|
// PineScript: the last `len` non-na values (also +/-Infinity skipped), as ta.sma
|
|
1035
1114
|
if ((biased ? len : len - 1) <= 0)
|
|
1036
1115
|
return source.map(() => NaN);
|
|
@@ -1058,6 +1137,7 @@ function varianceOf(source, length, biased, name) {
|
|
|
1058
1137
|
* ```
|
|
1059
1138
|
*/
|
|
1060
1139
|
export function median(source, length) {
|
|
1140
|
+
checkPositiveLength('median', length, 0);
|
|
1061
1141
|
const len = Math.floor(length);
|
|
1062
1142
|
const n = source.length;
|
|
1063
1143
|
const out = new Array(n).fill(NaN);
|
|
@@ -1146,6 +1226,7 @@ export function swma(source) {
|
|
|
1146
1226
|
* ```
|
|
1147
1227
|
*/
|
|
1148
1228
|
export function vwma(source, length, volume) {
|
|
1229
|
+
checkPositiveLength('vwma->sma', length, 0);
|
|
1149
1230
|
if (!volume) {
|
|
1150
1231
|
throw new Error('ta.vwma() requires volume series. ' +
|
|
1151
1232
|
'Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data.');
|
|
@@ -1193,6 +1274,7 @@ export function vwma(source, length, volume) {
|
|
|
1193
1274
|
* ```
|
|
1194
1275
|
*/
|
|
1195
1276
|
export function linreg(source, length, offset = 0) {
|
|
1277
|
+
checkPositiveLength('linreg', length, 0);
|
|
1196
1278
|
const result = [];
|
|
1197
1279
|
// Floor the length to match PineScript's auto-truncation of float to int
|
|
1198
1280
|
const len = Math.floor(length);
|
|
@@ -1260,6 +1342,7 @@ export function linreg(source, length, offset = 0) {
|
|
|
1260
1342
|
* ```
|
|
1261
1343
|
*/
|
|
1262
1344
|
export function correlation(source1, source2, length) {
|
|
1345
|
+
checkPositiveLength('correlation', length, 0);
|
|
1263
1346
|
// PineScript: from compensated running sums, cov = sma(x * y) - sma(x) * sma(y), r = cov / sqrt(var(x) * var(y))
|
|
1264
1347
|
// (biased variances); each running sum skips its own na values
|
|
1265
1348
|
const product = Array.from({ length: source1.length }, (_, i) => {
|
|
@@ -1299,8 +1382,8 @@ export function correlation(source1, source2, length) {
|
|
|
1299
1382
|
*
|
|
1300
1383
|
* PineScript rules:
|
|
1301
1384
|
* - percentrank = count(source[k] <= source, k = 1..length) * 100 / length (in this order: the PineScript bits)
|
|
1302
|
-
* - a value from bar `length` on
|
|
1303
|
-
*
|
|
1385
|
+
* - a value from bar `length` on; an `na` value in the window counts as not `<=` the current value, and an `na`
|
|
1386
|
+
* current value gives 0 (#177)
|
|
1304
1387
|
* - the comparison uses the 1e-10 tolerance
|
|
1305
1388
|
*
|
|
1306
1389
|
* @example
|
|
@@ -1311,14 +1394,15 @@ export function correlation(source1, source2, length) {
|
|
|
1311
1394
|
* ```
|
|
1312
1395
|
*/
|
|
1313
1396
|
export function percentrank(source, length) {
|
|
1397
|
+
checkPositiveLength('percentrank', length, 0);
|
|
1314
1398
|
const result = [];
|
|
1315
1399
|
for (let i = 0; i < source.length; i++) {
|
|
1316
1400
|
const currentValue = source[i];
|
|
1317
|
-
if (i < length
|
|
1401
|
+
if (i < length) {
|
|
1318
1402
|
result.push(NaN);
|
|
1319
1403
|
continue;
|
|
1320
1404
|
}
|
|
1321
|
-
// le() is false for an na value in the window
|
|
1405
|
+
// le() is false for an na value in the window, and for all of them when the current value is na (0)
|
|
1322
1406
|
let countLessOrEqual = 0;
|
|
1323
1407
|
for (let j = 1; j <= length; j++) {
|
|
1324
1408
|
if (le(source[i - j], currentValue))
|
|
@@ -1357,6 +1441,7 @@ export function percentrank(source, length) {
|
|
|
1357
1441
|
* \`\`\`
|
|
1358
1442
|
*/
|
|
1359
1443
|
export function cci(source, length) {
|
|
1444
|
+
checkPositiveLength('cci', length, 0);
|
|
1360
1445
|
const smaValues = sma(source, length);
|
|
1361
1446
|
const devValues = dev(source, length);
|
|
1362
1447
|
let last = 0;
|
|
@@ -1394,6 +1479,7 @@ export function cci(source, length) {
|
|
|
1394
1479
|
* \`\`\`
|
|
1395
1480
|
*/
|
|
1396
1481
|
export function stoch(source, high, low, length) {
|
|
1482
|
+
checkPositiveLength('stoch->highest', length, 0);
|
|
1397
1483
|
const result = [];
|
|
1398
1484
|
const lowestValues = lowest(low, length);
|
|
1399
1485
|
const highestValues = highest(high, length);
|
|
@@ -1443,6 +1529,7 @@ export function stoch(source, high, low, length) {
|
|
|
1443
1529
|
* ```
|
|
1444
1530
|
*/
|
|
1445
1531
|
export function mfi(source, length, volume) {
|
|
1532
|
+
checkPositiveLength('mfi', length, 0);
|
|
1446
1533
|
if (!volume) {
|
|
1447
1534
|
throw new Error('ta.mfi() requires volume series. ' +
|
|
1448
1535
|
'Either pass it explicitly or use createContext({ chart: { ..., volume } }) for implicit data.');
|
|
@@ -1498,6 +1585,7 @@ export function mfi(source, length, volume) {
|
|
|
1498
1585
|
* ```
|
|
1499
1586
|
*/
|
|
1500
1587
|
export function hma(source, length) {
|
|
1588
|
+
checkPositiveLength('hma', length, 0);
|
|
1501
1589
|
const halfLength = Math.floor(length / 2);
|
|
1502
1590
|
const sqrtLength = Math.floor(Math.sqrt(length));
|
|
1503
1591
|
const wmaHalf = wma(source, halfLength);
|
|
@@ -1613,37 +1701,27 @@ export function sar(start, inc, max, high, low, close) {
|
|
|
1613
1701
|
/**
|
|
1614
1702
|
* Pivot detection as PineScript computes it.
|
|
1615
1703
|
*
|
|
1616
|
-
* The
|
|
1617
|
-
*
|
|
1618
|
-
*
|
|
1619
|
-
*
|
|
1704
|
+
* The function keeps the non-na values it receives. On each bar it takes the last `leftbars + rightbars + 1` of them
|
|
1705
|
+
* (all of them when there are fewer) and finds the highest (the newest one on equal values; the lowest for a pivot
|
|
1706
|
+
* low): there is a pivot when that value is on the bar `rightbars` bars back, and its value is returned on this bar,
|
|
1707
|
+
* so no future bar is used. A pivot high may so equal values on its left but must be higher than every value on its
|
|
1708
|
+
* right. `na` before bar `leftbars + rightbars`.
|
|
1620
1709
|
*/
|
|
1621
1710
|
function pivot(source, leftbars, rightbars, isHigh) {
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
const v = source[center];
|
|
1630
|
-
if (Number.isNaN(v))
|
|
1711
|
+
const { values, bars, last } = keptValues(source);
|
|
1712
|
+
return Array.from(source, (_, i) => {
|
|
1713
|
+
// a series length: the lengths of the current bar
|
|
1714
|
+
const left = Math.trunc(typeof leftbars === 'number' ? leftbars : leftbars[i]);
|
|
1715
|
+
const right = Math.trunc(typeof rightbars === 'number' ? rightbars : rightbars[i]);
|
|
1716
|
+
const k = last[i];
|
|
1717
|
+
if (!(i >= left + right) || k < 0)
|
|
1631
1718
|
return NaN;
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
if (
|
|
1635
|
-
|
|
1636
|
-
if (beatsLeft(x, v))
|
|
1637
|
-
return NaN;
|
|
1638
|
-
}
|
|
1639
|
-
for (let j = 1; j <= rightbars; j++) {
|
|
1640
|
-
const x = source[center + j];
|
|
1641
|
-
if (Number.isNaN(x))
|
|
1642
|
-
break;
|
|
1643
|
-
if (beatsRight(x, v))
|
|
1644
|
-
return NaN;
|
|
1719
|
+
let best = k;
|
|
1720
|
+
for (let j = k - 1; j >= 0 && j >= k - left - right; j--) {
|
|
1721
|
+
if (isHigh ? values[j] > values[best] : values[j] < values[best])
|
|
1722
|
+
best = j;
|
|
1645
1723
|
}
|
|
1646
|
-
return
|
|
1724
|
+
return bars[best] === i - right ? values[best] : NaN;
|
|
1647
1725
|
});
|
|
1648
1726
|
}
|
|
1649
1727
|
/**
|
|
@@ -1660,7 +1738,10 @@ function pivot(source, leftbars, rightbars, isHigh) {
|
|
|
1660
1738
|
* - As in PineScript, the value appears `rightbars` bars after the pivot bar: the pivot bar
|
|
1661
1739
|
* index is `i - rightbars`
|
|
1662
1740
|
* - A pivot high may equal values on its left but must be higher than all values on its right
|
|
1663
|
-
* -
|
|
1741
|
+
* - `na` values are skipped: the window is the last `leftbars + rightbars + 1` non-na values, and the highest of
|
|
1742
|
+
* them must be on the bar `rightbars` bars back
|
|
1743
|
+
* - `leftbars` / `rightbars` can be arrays (a length per bar, PineScript series length), also for `ta.pivotlow`:
|
|
1744
|
+
* each bar uses its own lengths
|
|
1664
1745
|
*
|
|
1665
1746
|
* @example
|
|
1666
1747
|
* ```typescript
|
|
@@ -1691,7 +1772,8 @@ export function pivothigh(sourceOrLeftbars, leftbarsOrRightbars, rightbars, high
|
|
|
1691
1772
|
* - As in PineScript, the value appears `rightbars` bars after the pivot bar: the pivot bar
|
|
1692
1773
|
* index is `i - rightbars`
|
|
1693
1774
|
* - A pivot low may equal values on its left but must be lower than all values on its right
|
|
1694
|
-
* -
|
|
1775
|
+
* - `na` values are skipped: the window is the last `leftbars + rightbars + 1` non-na values, and the lowest of
|
|
1776
|
+
* them must be on the bar `rightbars` bars back
|
|
1695
1777
|
*
|
|
1696
1778
|
* @example
|
|
1697
1779
|
* ```typescript
|
|
@@ -1861,6 +1943,7 @@ export function dmi(diLength, adxSmoothing, high, low, close) {
|
|
|
1861
1943
|
* ```
|
|
1862
1944
|
*/
|
|
1863
1945
|
export function tsi(source, shortLength, longLength) {
|
|
1946
|
+
checkPositiveLength('tsi->ema', shortLength, 0);
|
|
1864
1947
|
const momentum = [];
|
|
1865
1948
|
// Calculate momentum (price change)
|
|
1866
1949
|
for (let i = 0; i < source.length; i++) {
|
|
@@ -1906,6 +1989,7 @@ export function tsi(source, shortLength, longLength) {
|
|
|
1906
1989
|
* ```
|
|
1907
1990
|
*/
|
|
1908
1991
|
export function cmo(source, length) {
|
|
1992
|
+
checkPositiveLength('cmo', length, 0);
|
|
1909
1993
|
// PineScript definition: mom = ta.change(src), sm1 = math.sum(mom >= 0 ? mom : 0, length),
|
|
1910
1994
|
// sm2 = math.sum(mom >= 0 ? 0 : -mom, length), cmo = 100 * (sm1 - sm2) / (sm1 + sm2), with the running sums of
|
|
1911
1995
|
// math.sum (bit for bit). A comparison with na is false: an na change adds 0 to sm1 and na (skipped) to sm2.
|
|
@@ -1946,6 +2030,7 @@ export function cmo(source, length) {
|
|
|
1946
2030
|
* ```
|
|
1947
2031
|
*/
|
|
1948
2032
|
export function kc(source, length, mult, useTrueRange = true, high, low, close) {
|
|
2033
|
+
checkPositiveLength('kc', length, 0);
|
|
1949
2034
|
// Calculate middle band (EMA of source)
|
|
1950
2035
|
const middle = ema(source, length);
|
|
1951
2036
|
// Calculate range
|
|
@@ -2000,6 +2085,7 @@ export function kc(source, length, mult, useTrueRange = true, high, low, close)
|
|
|
2000
2085
|
* ```
|
|
2001
2086
|
*/
|
|
2002
2087
|
export function bbw(source, length, mult) {
|
|
2088
|
+
checkPositiveLength('bbw', length, 0);
|
|
2003
2089
|
const [basis, upper, lower] = bb(source, length, mult);
|
|
2004
2090
|
const result = [];
|
|
2005
2091
|
for (let i = 0; i < source.length; i++) {
|
|
@@ -2041,6 +2127,7 @@ export function bbw(source, length, mult) {
|
|
|
2041
2127
|
* ```
|
|
2042
2128
|
*/
|
|
2043
2129
|
export function wpr(high, low, close, length = 14) {
|
|
2130
|
+
checkPositiveLength('wpr', length, 0);
|
|
2044
2131
|
const highs = highest(high, length);
|
|
2045
2132
|
const lows = lowest(low, length);
|
|
2046
2133
|
return Array.from({ length: close.length }, (_, i) => {
|
|
@@ -2105,6 +2192,7 @@ export function vwap(source, volume, anchor, stdev_mult) {
|
|
|
2105
2192
|
* ```
|
|
2106
2193
|
*/
|
|
2107
2194
|
export function alma(source, length = 9, offset = 0.85, sigma = 6, floor = false) {
|
|
2195
|
+
checkPositiveLength('alma', length, 0);
|
|
2108
2196
|
const result = [];
|
|
2109
2197
|
const m = floor ? Math.floor(offset * (length - 1)) : offset * (length - 1);
|
|
2110
2198
|
const s = length / sigma;
|
|
@@ -2183,6 +2271,7 @@ export function kcw(source, length = 20, mult = 2, useTrueRange = true, high, lo
|
|
|
2183
2271
|
* values), and a bar whose own value is `na` still gets a result.
|
|
2184
2272
|
*/
|
|
2185
2273
|
export function range(source, length) {
|
|
2274
|
+
checkPositiveLength('range', length, 0);
|
|
2186
2275
|
return source.map((_, i) => {
|
|
2187
2276
|
const values = lastValues(source, i, length);
|
|
2188
2277
|
return values ? Math.max(...values) - Math.min(...values) : NaN;
|
|
@@ -2193,8 +2282,9 @@ export function range(source, length) {
|
|
|
2193
2282
|
* -1 for the previous bar, and so on (as in PineScript, the offset is negative).
|
|
2194
2283
|
*
|
|
2195
2284
|
* @remarks
|
|
2196
|
-
* As in PineScript:
|
|
2197
|
-
*
|
|
2285
|
+
* As in PineScript: the best of the last `length` non-na values, the newest one on equal values; the offset is the
|
|
2286
|
+
* distance in bars to its bar, so it is below `-(length - 1)` when `na` values were skipped. `na` before bar
|
|
2287
|
+
* `length - 1` and while no value was received; a fractional length is truncated.
|
|
2198
2288
|
*/
|
|
2199
2289
|
export function highestbars(source, length) {
|
|
2200
2290
|
return extremeOffset(source, length, false);
|
|
@@ -2204,8 +2294,9 @@ export function highestbars(source, length) {
|
|
|
2204
2294
|
* -1 for the previous bar, and so on (as in PineScript, the offset is negative).
|
|
2205
2295
|
*
|
|
2206
2296
|
* @remarks
|
|
2207
|
-
* As in PineScript:
|
|
2208
|
-
*
|
|
2297
|
+
* As in PineScript: the best of the last `length` non-na values, the newest one on equal values; the offset is the
|
|
2298
|
+
* distance in bars to its bar, so it is below `-(length - 1)` when `na` values were skipped. `na` before bar
|
|
2299
|
+
* `length - 1` and while no value was received; a fractional length is truncated.
|
|
2209
2300
|
*/
|
|
2210
2301
|
export function lowestbars(source, length) {
|
|
2211
2302
|
return extremeOffset(source, length, true);
|
|
@@ -2263,6 +2354,7 @@ export function min(source) {
|
|
|
2263
2354
|
* ```
|
|
2264
2355
|
*/
|
|
2265
2356
|
export function cog(source, length = 10) {
|
|
2357
|
+
checkPositiveLength('cog', length, 0);
|
|
2266
2358
|
const len = Math.floor(length);
|
|
2267
2359
|
return runningSum(source, len).map((sum, i) => {
|
|
2268
2360
|
if (i < len - 1 || Number.isNaN(sum) || sum === 0)
|
|
@@ -2297,6 +2389,7 @@ export function cog(source, length = 10) {
|
|
|
2297
2389
|
* - If no mode exists, returns the smallest value
|
|
2298
2390
|
*/
|
|
2299
2391
|
export function mode(source, length) {
|
|
2392
|
+
checkPositiveLength('mode', length, 0);
|
|
2300
2393
|
const result = [];
|
|
2301
2394
|
const len = Math.floor(length);
|
|
2302
2395
|
const seen = []; // non-na values so far
|
|
@@ -2364,6 +2457,7 @@ export function mode(source, length) {
|
|
|
2364
2457
|
* `na` values are before all numbers
|
|
2365
2458
|
*/
|
|
2366
2459
|
export function percentile_linear_interpolation(source, length, percentage) {
|
|
2460
|
+
checkPositiveLength('percentile_linear_interpolation', length, 0);
|
|
2367
2461
|
return sortedWindow(source, length, (s) => {
|
|
2368
2462
|
const pos = (percentage / 100) * length - 0.5;
|
|
2369
2463
|
if (pos <= 0)
|
|
@@ -2430,6 +2524,7 @@ function sortedWindow(source, length, fn) {
|
|
|
2430
2524
|
* - `na` values stay in the window, sorted from bar to bar as in `ta.percentile_linear_interpolation`
|
|
2431
2525
|
*/
|
|
2432
2526
|
export function percentile_nearest_rank(source, length, percentage) {
|
|
2527
|
+
checkPositiveLength('percentile_nearest_rank', length, 0);
|
|
2433
2528
|
const rank = Math.min(length, Math.max(1, Math.ceil((percentage / 100) * length)));
|
|
2434
2529
|
return sortedWindow(source, length, (s) => s[rank - 1]);
|
|
2435
2530
|
}
|
|
@@ -2465,6 +2560,7 @@ export function percentile_nearest_rank(source, length, percentage) {
|
|
|
2465
2560
|
* where the `na` was
|
|
2466
2561
|
*/
|
|
2467
2562
|
export function rci(source, length) {
|
|
2563
|
+
checkPositiveLength('rci', length, 0);
|
|
2468
2564
|
const len = Math.floor(length);
|
|
2469
2565
|
const result = [];
|
|
2470
2566
|
// PineScript keeps the values in a ring of `length + 1` slots indexed by the bar index, filled with 0 at the start
|
|
@@ -2530,187 +2626,135 @@ export function rci(source, length) {
|
|
|
2530
2626
|
return result;
|
|
2531
2627
|
}
|
|
2532
2628
|
/**
|
|
2533
|
-
* Pivot Point Levels
|
|
2629
|
+
* Pivot Point Levels (PineScript `ta.pivot_point_levels`).
|
|
2534
2630
|
*
|
|
2535
|
-
*
|
|
2536
|
-
* 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).
|
|
2537
2632
|
*
|
|
2538
2633
|
* @param type - Calculation type: "Traditional", "Fibonacci", "Woodie", "Classic", "DM", "Camarilla"
|
|
2539
|
-
* @param anchor - Condition that
|
|
2540
|
-
* @param developing -
|
|
2541
|
-
*
|
|
2542
|
-
* @param
|
|
2543
|
-
* @param
|
|
2544
|
-
* @param
|
|
2545
|
-
* @
|
|
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
|
|
2546
2642
|
*
|
|
2547
2643
|
* @example
|
|
2548
2644
|
* ```typescript
|
|
2549
2645
|
* const weekChange = [false, false, false, false, true, false, ...]; // Weekly anchor
|
|
2550
|
-
* const
|
|
2551
|
-
* //
|
|
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
|
|
2552
2648
|
* ```
|
|
2553
2649
|
*
|
|
2554
2650
|
* @remarks
|
|
2555
|
-
*
|
|
2556
|
-
* -
|
|
2557
|
-
*
|
|
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
|
|
2558
2665
|
*/
|
|
2559
2666
|
export function pivot_point_levels(type, anchor, developing = false, high, low, close, open) {
|
|
2560
|
-
// For simplicity, we need to handle the case where type is a string array
|
|
2561
|
-
// But according to the docs, type is "series string", meaning it can change per bar
|
|
2562
|
-
// For this implementation, we'll support constant type strings
|
|
2563
2667
|
const typeStr = typeof type === 'string' ? type : String(type[0]);
|
|
2564
|
-
const isDeveloping = typeof developing === 'boolean' ? developing : developing[0];
|
|
2565
|
-
// Woodie cannot be developing
|
|
2668
|
+
const isDeveloping = typeof developing === 'boolean' ? developing : Boolean(developing[0]);
|
|
2566
2669
|
if (typeStr === 'Woodie' && isDeveloping) {
|
|
2567
|
-
throw new Error('ta.pivot_point_levels
|
|
2568
|
-
}
|
|
2569
|
-
|
|
2570
|
-
const
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
lastAnchorIndex = i;
|
|
2588
|
-
// Store OHLC at anchor point (these will be used for calculations)
|
|
2589
|
-
lastH = high ? high[i] : NaN;
|
|
2590
|
-
lastL = low ? low[i] : NaN;
|
|
2591
|
-
lastC = close ? close[i] : NaN;
|
|
2592
|
-
lastO = open ? open[i] : NaN;
|
|
2593
|
-
}
|
|
2594
|
-
// Calculate data to use
|
|
2595
|
-
let h, l, c, o;
|
|
2596
|
-
if (isDeveloping && lastAnchorIndex >= 0) {
|
|
2597
|
-
// Developing: use max/min/last since anchor
|
|
2598
|
-
h = high ? Math.max(...high.slice(lastAnchorIndex, i + 1).filter(v => !isNaN(v))) : NaN;
|
|
2599
|
-
l = low ? Math.min(...low.slice(lastAnchorIndex, i + 1).filter(v => !isNaN(v))) : NaN;
|
|
2600
|
-
c = close ? close[i] : NaN;
|
|
2601
|
-
o = open && lastAnchorIndex >= 0 ? open[lastAnchorIndex] : NaN;
|
|
2602
|
-
}
|
|
2603
|
-
else {
|
|
2604
|
-
// Not developing: use last anchor values
|
|
2605
|
-
h = lastH;
|
|
2606
|
-
l = lastL;
|
|
2607
|
-
c = lastC;
|
|
2608
|
-
o = lastO;
|
|
2609
|
-
}
|
|
2610
|
-
// Calculate pivot levels based on type
|
|
2611
|
-
const levels = calculatePivotLevels(typeStr, h, l, c, o);
|
|
2612
|
-
// Push to results
|
|
2613
|
-
for (let j = 0; j < 11; j++) {
|
|
2614
|
-
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);
|
|
2615
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];
|
|
2616
2714
|
}
|
|
2617
2715
|
return results;
|
|
2618
2716
|
}
|
|
2717
|
+
const NA_LEVELS = new Array(11).fill(NaN);
|
|
2619
2718
|
/**
|
|
2620
|
-
*
|
|
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).
|
|
2621
2721
|
*/
|
|
2622
|
-
function
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
return Array(11).fill(NaN);
|
|
2626
|
-
}
|
|
2627
|
-
const levels = Array(11).fill(NaN);
|
|
2628
|
-
// Calculate pivot point (P)
|
|
2629
|
-
let P;
|
|
2722
|
+
function pivotLevels(type, h, l, c, o, wo) {
|
|
2723
|
+
const N = NaN;
|
|
2724
|
+
const r = h - l;
|
|
2630
2725
|
switch (type) {
|
|
2631
|
-
case 'Traditional':
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
case '
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
switch (type) {
|
|
2651
|
-
case 'Traditional':
|
|
2652
|
-
case 'Classic':
|
|
2653
|
-
levels[1] = 2 * P - l; // R1
|
|
2654
|
-
levels[2] = 2 * P - h; // S1
|
|
2655
|
-
levels[3] = P + (h - l); // R2
|
|
2656
|
-
levels[4] = P - (h - l); // S2
|
|
2657
|
-
levels[5] = h + 2 * (P - l); // R3
|
|
2658
|
-
levels[6] = l - 2 * (h - P); // S3
|
|
2659
|
-
levels[7] = levels[5] + (h - l); // R4
|
|
2660
|
-
levels[8] = levels[6] - (h - l); // S4
|
|
2661
|
-
levels[9] = levels[7] + (h - l); // R5
|
|
2662
|
-
levels[10] = levels[8] - (h - l); // S5
|
|
2663
|
-
break;
|
|
2664
|
-
case 'Fibonacci':
|
|
2665
|
-
levels[1] = P + 0.382 * (h - l); // R1
|
|
2666
|
-
levels[2] = P - 0.382 * (h - l); // S1
|
|
2667
|
-
levels[3] = P + 0.618 * (h - l); // R2
|
|
2668
|
-
levels[4] = P - 0.618 * (h - l); // S2
|
|
2669
|
-
levels[5] = P + (h - l); // R3
|
|
2670
|
-
levels[6] = P - (h - l); // S3
|
|
2671
|
-
levels[7] = levels[5] + 0.618 * (h - l); // R4
|
|
2672
|
-
levels[8] = levels[6] - 0.618 * (h - l); // S4
|
|
2673
|
-
levels[9] = levels[7] + 0.382 * (h - l); // R5
|
|
2674
|
-
levels[10] = levels[8] - 0.382 * (h - l); // S5
|
|
2675
|
-
break;
|
|
2676
|
-
case 'Woodie':
|
|
2677
|
-
levels[1] = 2 * P - l; // R1
|
|
2678
|
-
levels[2] = 2 * P - h; // S1
|
|
2679
|
-
levels[3] = P + (h - l); // R2
|
|
2680
|
-
levels[4] = P - (h - l); // S2
|
|
2681
|
-
levels[5] = h + 2 * (P - l); // R3
|
|
2682
|
-
levels[6] = l - 2 * (h - P); // S3
|
|
2683
|
-
levels[7] = levels[5] + (h - l); // R4
|
|
2684
|
-
levels[8] = levels[6] - (h - l); // S4
|
|
2685
|
-
levels[9] = levels[7] + (h - l); // R5
|
|
2686
|
-
levels[10] = levels[8] - (h - l); // S5
|
|
2687
|
-
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
|
+
}
|
|
2688
2745
|
case 'DM': {
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
const newP = x / (isNaN(o) ? 4 : 5);
|
|
2692
|
-
levels[0] = newP;
|
|
2693
|
-
levels[1] = x / 2 - l; // R1
|
|
2694
|
-
levels[2] = x / 2 - h; // S1
|
|
2695
|
-
// R2-S5 remain NaN
|
|
2696
|
-
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];
|
|
2697
2748
|
}
|
|
2698
2749
|
case 'Camarilla': {
|
|
2699
|
-
const
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
levels[4] = c - range * 1.1 / 6; // S2
|
|
2704
|
-
levels[5] = c + range * 1.1 / 4; // R3
|
|
2705
|
-
levels[6] = c - range * 1.1 / 4; // S3
|
|
2706
|
-
levels[7] = c + range * 1.1 / 2; // R4
|
|
2707
|
-
levels[8] = c - range * 1.1 / 2; // S4
|
|
2708
|
-
levels[9] = h; // R5 (high)
|
|
2709
|
-
levels[10] = l; // S5 (low)
|
|
2710
|
-
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];
|
|
2711
2754
|
}
|
|
2755
|
+
default:
|
|
2756
|
+
return NA_LEVELS;
|
|
2712
2757
|
}
|
|
2713
|
-
return levels;
|
|
2714
2758
|
}
|
|
2715
2759
|
/**
|
|
2716
2760
|
* Ichimoku Kinko Hyo (Ichimoku Cloud) - Japanese charting technique for trend identification.
|