oakscriptjs 0.11.0 → 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.js +6 -6
- package/dist/cjs/box/index.js.map +2 -2
- package/dist/cjs/callsite/index.js +1 -1
- 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 +3 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +6 -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 +65 -12
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +168 -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 +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 +11 -7
- package/dist/cjs/session/calendar.js.map +2 -2
- package/dist/cjs/session/context.d.ts +2 -1
- package/dist/cjs/session/context.d.ts.map +1 -1
- package/dist/cjs/session/context.js +2 -1
- 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 +34 -17
- package/dist/cjs/ta/index.d.ts.map +1 -1
- package/dist/cjs/ta/index.js +132 -116
- package/dist/cjs/ta/index.js.map +2 -2
- 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 +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/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.js +1 -1
- 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 +3 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -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.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 +65 -12
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +217 -29
- 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 +19 -7
- package/dist/esm/session/calendar.js.map +1 -1
- package/dist/esm/session/context.d.ts +2 -1
- package/dist/esm/session/context.d.ts.map +1 -1
- package/dist/esm/session/context.js +5 -3
- 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 +34 -17
- package/dist/esm/ta/index.d.ts.map +1 -1
- package/dist/esm/ta/index.js +155 -185
- 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 +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/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 +1 -1
- package/src/drawing/registry.ts +30 -7
- package/src/index.ts +11 -1
- 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 +292 -30
- package/src/security/other.ts +269 -0
- package/src/session/bars.ts +24 -1
- package/src/session/calendar.ts +23 -7
- package/src/session/context.ts +5 -3
- 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 +157 -183
- 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 +4 -1
- package/src/time/session.ts +58 -16
- package/src/time/timezone.ts +9 -2
- package/src/types/metadata.ts +1 -1
package/src/array/index.ts
CHANGED
|
@@ -41,7 +41,7 @@ function position(name: string, id: PineArray<unknown>, index: number, extra = 0
|
|
|
41
41
|
const i = Math.trunc(index);
|
|
42
42
|
const n = id.length;
|
|
43
43
|
if (i < -n || i >= n + extra) {
|
|
44
|
-
throw new Error(`array.${name}
|
|
44
|
+
throw new Error(`In 'array.${name}()' function. Index ${i} is out of bounds, array size is ${n}.`);
|
|
45
45
|
}
|
|
46
46
|
return i < 0 ? i + n : i;
|
|
47
47
|
}
|
|
@@ -50,7 +50,12 @@ function position(name: string, id: PineArray<unknown>, index: number, extra = 0
|
|
|
50
50
|
* Returns the element at the specified index.
|
|
51
51
|
*
|
|
52
52
|
* PineScript rules: a negative index counts from the end (`get(a, -1)` is the last element); an index outside
|
|
53
|
-
* `-size .. size - 1` is a runtime error (throws)
|
|
53
|
+
* `-size .. size - 1` is a runtime error (throws "In 'array.get()' function. Index 5 is out of bounds, array size
|
|
54
|
+
* is 3."); an `na` index gives `na`.
|
|
55
|
+
*
|
|
56
|
+
* Negative indexes are a PineScript v6 rule, which oakscriptjs follows: a version 5 script stops on them with the
|
|
57
|
+
* same out of bounds error (`array.get(a, -1)`, #176). A port of a v5 script that the original stops must check the
|
|
58
|
+
* index itself.
|
|
54
59
|
*/
|
|
55
60
|
export function get<T>(id: PineArray<T>, index: simple_int): T {
|
|
56
61
|
if (Number.isNaN(index)) return naOf(id);
|
|
@@ -973,7 +978,8 @@ export function binary_search_rightmost(id: PineArray<float>, val: float): int {
|
|
|
973
978
|
* - Formula: Cov(X,Y) = E[(X - μX)(Y - μY)]
|
|
974
979
|
*
|
|
975
980
|
* PineScript rules: a pair where either value is `na` is skipped and the count is the number of pairs kept; both
|
|
976
|
-
* means first, then the sum of the products from the first to the last pair
|
|
981
|
+
* means first, then the sum of the products from the first to the last pair, set to 0 whenever it is within 1e-10 of
|
|
982
|
+
* zero (#177: five equal closes give 0, not -1.7e-18). One pair gives 0 biased, NaN unbiased.
|
|
977
983
|
*/
|
|
978
984
|
export function covariance(id1: PineArray<float>, id2: PineArray<float>, biased: bool = true): float {
|
|
979
985
|
if (id1.length !== id2.length) {
|
|
@@ -996,9 +1002,11 @@ export function covariance(id1: PineArray<float>, id2: PineArray<float>, biased:
|
|
|
996
1002
|
}
|
|
997
1003
|
mean1 /= n;
|
|
998
1004
|
mean2 /= n;
|
|
1005
|
+
// the co-moment is a running sum set to 0 when it is within 1e-10 of zero, as array.sum (#177)
|
|
999
1006
|
let sum = 0;
|
|
1000
1007
|
for (let i = 0; i < n; i++) {
|
|
1001
1008
|
sum += (xs[i]! - mean1) * (ys[i]! - mean2);
|
|
1009
|
+
if (Math.abs(sum) <= 1e-10) sum = 0;
|
|
1002
1010
|
}
|
|
1003
1011
|
return sum / (biased ? n : n - 1);
|
|
1004
1012
|
}
|
package/src/box/index.ts
CHANGED
|
@@ -91,8 +91,8 @@ export function new_box(...args: unknown[]): Box {
|
|
|
91
91
|
(string | number)?, color?, ('left' | 'center' | 'right')?, ('top' | 'center' | 'bottom')?,
|
|
92
92
|
('none' | 'auto')?, ('default' | 'monospace')?, boolean?, number?,
|
|
93
93
|
];
|
|
94
|
-
checkBarIndex('box.new', 'left', left, xloc);
|
|
95
|
-
checkBarIndex('box.new', 'right', right, xloc);
|
|
94
|
+
left = checkBarIndex('box.new', 'left', left, xloc);
|
|
95
|
+
right = checkBarIndex('box.new', 'right', right, xloc);
|
|
96
96
|
return register('box', {
|
|
97
97
|
left,
|
|
98
98
|
top,
|
|
@@ -248,7 +248,7 @@ export function copy(id: Box): Box {
|
|
|
248
248
|
*/
|
|
249
249
|
export function set_left(id: Box, left: number): Box {
|
|
250
250
|
if (isDeleted(id)) return id;
|
|
251
|
-
checkBarIndex('box.set_(left*/xloc)', 'left', left, id.xloc);
|
|
251
|
+
left = checkBarIndex('box.set_(left*/xloc)', 'left', left, id.xloc);
|
|
252
252
|
id.left = left;
|
|
253
253
|
return id;
|
|
254
254
|
}
|
|
@@ -268,7 +268,7 @@ export function set_left(id: Box, left: number): Box {
|
|
|
268
268
|
*/
|
|
269
269
|
export function set_right(id: Box, right: number): Box {
|
|
270
270
|
if (isDeleted(id)) return id;
|
|
271
|
-
checkBarIndex('box.set_(right*/xlock)', 'right', right, id.xloc);
|
|
271
|
+
right = checkBarIndex('box.set_(right*/xlock)', 'right', right, id.xloc);
|
|
272
272
|
id.right = right;
|
|
273
273
|
return id;
|
|
274
274
|
}
|
|
@@ -327,7 +327,7 @@ export function set_bottom(id: Box, bottom: number): Box {
|
|
|
327
327
|
*/
|
|
328
328
|
export function set_lefttop(id: Box, left: number, top: number): Box {
|
|
329
329
|
if (isDeleted(id)) return id;
|
|
330
|
-
checkBarIndex('box.set_(left*/xloc)', 'left', left, id.xloc);
|
|
330
|
+
left = checkBarIndex('box.set_(left*/xloc)', 'left', left, id.xloc);
|
|
331
331
|
id.left = left;
|
|
332
332
|
id.top = top;
|
|
333
333
|
return id;
|
|
@@ -349,7 +349,7 @@ export function set_lefttop(id: Box, left: number, top: number): Box {
|
|
|
349
349
|
*/
|
|
350
350
|
export function set_rightbottom(id: Box, right: number, bottom: number): Box {
|
|
351
351
|
if (isDeleted(id)) return id;
|
|
352
|
-
checkBarIndex('box.set_(right*/xlock)', 'right', right, id.xloc);
|
|
352
|
+
right = checkBarIndex('box.set_(right*/xlock)', 'right', right, id.xloc);
|
|
353
353
|
id.right = right;
|
|
354
354
|
id.bottom = bottom;
|
|
355
355
|
return id;
|
package/src/callsite/index.ts
CHANGED
|
@@ -364,8 +364,8 @@ function loopSumSite(
|
|
|
364
364
|
let pending = false;
|
|
365
365
|
return (barIndex, value, length) => {
|
|
366
366
|
const len = Math.floor(length);
|
|
367
|
-
if (!(len >= 1)) throw new Error(`callsite.${name}: length must be at least 1 (got ${length})`);
|
|
368
367
|
checkSumLength(name === 'sum' ? 'math.sum' : `ta.${name}`, len, barIndex);
|
|
368
|
+
if (!(len >= 1)) throw new Error(`callsite.${name}: length must be at least 1 (got ${length})`);
|
|
369
369
|
if (barIndex !== lastBar) {
|
|
370
370
|
if (pending) {
|
|
371
371
|
running.commit();
|
package/src/drawing/registry.ts
CHANGED
|
@@ -36,8 +36,9 @@
|
|
|
36
36
|
* - polylines on the last bar: the `P` carried from the bar before is counted twice (the first creation adds it once
|
|
37
37
|
* more, unless it sets `P` to 0), later creations do not set `P` to 0, and the polylines of the bars before that a
|
|
38
38
|
* cut deletes are added to `P`. Once none is
|
|
39
|
-
* left,
|
|
40
|
-
*
|
|
39
|
+
* left, `P` is at least the number of polylines of the bars before deleted on the last bar (by the count or by
|
|
40
|
+
* the script) and a cut deletes polylines of the last bar: a script that deletes its 100 polylines and creates 200
|
|
41
|
+
* on the last bar keeps them all (#176). Equal to PineScript for the polylines alive at the end on 271 of
|
|
41
42
|
* 281 probe runs; the count inside the bar can differ when the script deletes polylines of the last bar
|
|
42
43
|
*
|
|
43
44
|
* - a bar index (`xloc.bar_index`) more than 10,000 bars before the current bar stops the script (runtime error
|
|
@@ -171,7 +172,10 @@ export function varDrawing<T extends object>(initial?: T): DrawingVariable<T> {
|
|
|
171
172
|
|
|
172
173
|
/** Largest distance, in bars, of a bar index before the current bar (PineScript runtime error RE10026 above it). */
|
|
173
174
|
export const MAX_DRAWING_BARS_BACK = 10000;
|
|
175
|
+
/** Largest distance, in bars, of a bar index after the last bar of the data (PineScript runtime error RE10020 above it). */
|
|
176
|
+
export const MAX_DRAWING_BARS_FORWARD = 500;
|
|
174
177
|
let currentBar: number | null = null;
|
|
178
|
+
let lastBarIndex: number | null = null;
|
|
175
179
|
|
|
176
180
|
/** Kinds whose allowance on the last bar is the `P` they had when the bar started. */
|
|
177
181
|
type AllowanceKind = 'line' | 'label' | 'box';
|
|
@@ -198,11 +202,14 @@ let lastBar: LastBar | null = null;
|
|
|
198
202
|
* bars back throws as in PineScript; `null` turns the check off. `eachBar` of the script API calls it.
|
|
199
203
|
*
|
|
200
204
|
* The bar also dates the objects held by a `var` variable (protected from the next bar on). `last` says that it is
|
|
201
|
-
* the last bar of the data, which has rules of its own for the maximum count.
|
|
205
|
+
* the last bar of the data, which has rules of its own for the maximum count. `lastIndex` is the bar index of the
|
|
206
|
+
* last bar of the data (PineScript `last_bar_index`): with it, a bar index more than 500 bars after it throws, also
|
|
207
|
+
* on the bars before the last one (RE10020).
|
|
202
208
|
*/
|
|
203
|
-
export function setDrawingBar(barIndex: number | null, last: boolean = false): void {
|
|
209
|
+
export function setDrawingBar(barIndex: number | null, last: boolean = false, lastIndex?: number): void {
|
|
204
210
|
if (lastBar && !(last && barIndex === lastBar.bar)) lastBar = null;
|
|
205
211
|
currentBar = barIndex;
|
|
212
|
+
lastBarIndex = barIndex === null ? null : (lastIndex ?? (last ? barIndex : null));
|
|
206
213
|
if (barIndex !== null && last && !lastBar) {
|
|
207
214
|
const start = (kind: AllowanceKind): number => (pending[kind] >= PENDING_RESET[kind] ? 0 : pending[kind]);
|
|
208
215
|
lastBar = {
|
|
@@ -217,17 +224,30 @@ export function setDrawingBar(barIndex: number | null, last: boolean = false): v
|
|
|
217
224
|
}
|
|
218
225
|
|
|
219
226
|
/**
|
|
220
|
-
* Throws the PineScript runtime error RE10026 when `x` is a bar index more than 10,000 bars before the current bar
|
|
227
|
+
* Throws the PineScript runtime error RE10026 when `x` is a bar index more than 10,000 bars before the current bar,
|
|
228
|
+
* and RE10020 when it is more than 500 bars after the last bar of the data (when {@link setDrawingBar} knows it).
|
|
221
229
|
* `method` and `variable` are the names of the PineScript message ("line.new", "x1").
|
|
230
|
+
*
|
|
231
|
+
* Returns `x`, truncated when it is a bar index: PineScript's x is an int (a box from 5537.94 starts on bar 5537,
|
|
232
|
+
* #176).
|
|
222
233
|
*/
|
|
223
|
-
export function checkBarIndex(method: string, variable: string, x: number, xloc: string | undefined):
|
|
224
|
-
if (
|
|
234
|
+
export function checkBarIndex(method: string, variable: string, x: number, xloc: string | undefined): number {
|
|
235
|
+
if ((xloc ?? 'bar_index') !== 'bar_index') return x;
|
|
236
|
+
x = Math.trunc(x);
|
|
237
|
+
if (currentBar === null) return x;
|
|
238
|
+
if (lastBarIndex !== null && x - lastBarIndex > MAX_DRAWING_BARS_FORWARD) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`Error on bar ${currentBar}: Objects positioned using xloc.bar_index cannot be drawn further than ` +
|
|
241
|
+
`${MAX_DRAWING_BARS_FORWARD} bars into the future.`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
225
244
|
if (currentBar - x > MAX_DRAWING_BARS_BACK) {
|
|
226
245
|
throw new Error(
|
|
227
246
|
`Error on bar ${currentBar}: Bar index value of the \`${variable}\` argument (${x}) in \`${method}()\` is too far ` +
|
|
228
247
|
'from the current bar index. Try using `time` instead.'
|
|
229
248
|
);
|
|
230
249
|
}
|
|
250
|
+
return x;
|
|
231
251
|
}
|
|
232
252
|
|
|
233
253
|
/** Numeric fields that become NaN when an object is deleted, so that its getters return `na`. */
|
|
@@ -314,6 +334,8 @@ function registerLast<K extends DrawingKind>(bar: LastBar, kind: K, obj: Drawing
|
|
|
314
334
|
}
|
|
315
335
|
for (const old of cut) markDeleted(kind, old, true);
|
|
316
336
|
} else {
|
|
337
|
+
// the polylines of the bars before deleted on this bar count before the cut (#176)
|
|
338
|
+
if (kind === 'polyline' && pending.polyline < bar.oldPolylinesGone) pending.polyline = bar.oldPolylinesGone;
|
|
317
339
|
const cut = cutOldest(kind, list, obj);
|
|
318
340
|
if (kind === 'polyline' && cut.length > 0) pending.polyline = bar.oldPolylinesGone;
|
|
319
341
|
for (const old of cut) markDeleted(kind, old, false);
|
|
@@ -351,6 +373,7 @@ export function resetDrawings(newLimits: DrawingLimits = {}): void {
|
|
|
351
373
|
heldAt = new WeakMap();
|
|
352
374
|
pending = { line: 0, label: 0, box: 0, polyline: 0 };
|
|
353
375
|
currentBar = null;
|
|
376
|
+
lastBarIndex = null;
|
|
354
377
|
lastBar = null;
|
|
355
378
|
limits = {
|
|
356
379
|
line: newLimits.line ?? DEFAULT_MAX_COUNT,
|
package/src/index.ts
CHANGED
|
@@ -75,6 +75,15 @@ export { SessionBars, sessionFlags, type SessionFlags } from './session/bars.js'
|
|
|
75
75
|
|
|
76
76
|
// Same-symbol request.security on a higher timeframe (completion and lookahead rules) and ticker.heikinashi
|
|
77
77
|
export { resample, periodsOf, afterCompletion, mapToChart, heikinAshi, type Resampled } from './security/resample.js';
|
|
78
|
+
// request.security of another symbol: which bar of the other symbol each chart bar shows (#179)
|
|
79
|
+
export {
|
|
80
|
+
securityOther,
|
|
81
|
+
securityOtherValues,
|
|
82
|
+
securityLowerTf,
|
|
83
|
+
type OtherSymbolChart,
|
|
84
|
+
type OtherSymbolOptions,
|
|
85
|
+
type SymbolCalendar,
|
|
86
|
+
} from './security/other.js';
|
|
78
87
|
|
|
79
88
|
// Export Series class (self-contained, no context)
|
|
80
89
|
export { Series, BarData } from './runtime/series.js';
|
|
@@ -170,6 +179,7 @@ export {
|
|
|
170
179
|
input_source,
|
|
171
180
|
input_timeframe,
|
|
172
181
|
input_session,
|
|
182
|
+
input_symbol,
|
|
173
183
|
input_time,
|
|
174
184
|
} from './runtime/inputs.js';
|
|
175
185
|
export { isInputActive } from './runtime/input-active.js';
|
|
@@ -205,7 +215,7 @@ export type { LightweightSeriesDefinitions } from './runtime/adapters/Lightweigh
|
|
|
205
215
|
export { SimpleInputAdapter } from './runtime/adapters/SimpleInputAdapter.js';
|
|
206
216
|
|
|
207
217
|
// Version
|
|
208
|
-
export const VERSION = '0.11.
|
|
218
|
+
export const VERSION = '0.11.1';
|
|
209
219
|
|
|
210
220
|
/**
|
|
211
221
|
* Library information
|
package/src/input.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export interface InputValue<T> {
|
|
11
11
|
/** Input type identifier */
|
|
12
|
-
type: 'int' | 'float' | 'source' | 'bool' | 'string' | 'timeframe' | 'session' | 'time';
|
|
12
|
+
type: 'int' | 'float' | 'source' | 'bool' | 'string' | 'timeframe' | 'session' | 'time' | 'symbol';
|
|
13
13
|
/** Default value */
|
|
14
14
|
defaultValue: T;
|
|
15
15
|
/** Current value (initially equals defaultValue) */
|
|
@@ -254,6 +254,23 @@ export const input = {
|
|
|
254
254
|
};
|
|
255
255
|
},
|
|
256
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Create a symbol input parameter (PineScript `input.symbol`): a ticker id ("NASDAQ:MSFT") that an application can
|
|
259
|
+
* edit with a symbol picker (#180)
|
|
260
|
+
* @param defaultValue - Default ticker id
|
|
261
|
+
* @param options - Additional options (title, options for dropdown)
|
|
262
|
+
* @returns Input value object
|
|
263
|
+
*/
|
|
264
|
+
symbol(defaultValue: string, options: StringInputOptions = {}): InputValue<string> {
|
|
265
|
+
return {
|
|
266
|
+
type: 'symbol' as const,
|
|
267
|
+
defaultValue,
|
|
268
|
+
value: defaultValue,
|
|
269
|
+
title: options.title,
|
|
270
|
+
options: options.options,
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
|
|
257
274
|
/**
|
|
258
275
|
* Create a date/time input parameter (PineScript `input.time`)
|
|
259
276
|
* @param defaultValue - Default UNIX time in milliseconds, e.g. `time.timestamp('2024-01-01')`
|
package/src/label/index.ts
CHANGED
|
@@ -80,7 +80,7 @@ export function new_label(...args: unknown[]): Label {
|
|
|
80
80
|
string?, LabelXloc?, LabelYloc?, color?, LabelStyle?, color?, (string | number)?,
|
|
81
81
|
('left' | 'center' | 'right')?, string?, ('default' | 'monospace')?, boolean?, number?,
|
|
82
82
|
];
|
|
83
|
-
checkBarIndex('label.new', 'x', x, xloc);
|
|
83
|
+
x = checkBarIndex('label.new', 'x', x, xloc);
|
|
84
84
|
return register('label', {
|
|
85
85
|
x,
|
|
86
86
|
y,
|
|
@@ -189,7 +189,7 @@ export function copy(id: Label): Label {
|
|
|
189
189
|
*/
|
|
190
190
|
export function set_x(id: Label, x: number): Label {
|
|
191
191
|
if (isDeleted(id)) return id;
|
|
192
|
-
checkBarIndex('label.set_x', 'x', x, id.xloc);
|
|
192
|
+
x = checkBarIndex('label.set_x', 'x', x, id.xloc);
|
|
193
193
|
id.x = x;
|
|
194
194
|
return id;
|
|
195
195
|
}
|
|
@@ -229,7 +229,7 @@ export function set_y(id: Label, y: number): Label {
|
|
|
229
229
|
*/
|
|
230
230
|
export function set_xy(id: Label, x: number, y: number): Label {
|
|
231
231
|
if (isDeleted(id)) return id;
|
|
232
|
-
checkBarIndex('label.set_x', 'x', x, id.xloc);
|
|
232
|
+
x = checkBarIndex('label.set_x', 'x', x, id.xloc);
|
|
233
233
|
id.x = x;
|
|
234
234
|
id.y = y;
|
|
235
235
|
return id;
|
package/src/line/index.ts
CHANGED
|
@@ -70,8 +70,8 @@ export function new_line(...args: unknown[]): Line {
|
|
|
70
70
|
const [xloc, extend, lineColor, style, width, force_overlay] = rest as [
|
|
71
71
|
LineXloc?, LineExtend?, color?, LineStyle?, number?, boolean?,
|
|
72
72
|
];
|
|
73
|
-
checkBarIndex('line.new', 'x1', x1, xloc);
|
|
74
|
-
checkBarIndex('line.new', 'x2', x2, xloc);
|
|
73
|
+
x1 = checkBarIndex('line.new', 'x1', x1, xloc);
|
|
74
|
+
x2 = checkBarIndex('line.new', 'x2', x2, xloc);
|
|
75
75
|
return register('line', {
|
|
76
76
|
x1,
|
|
77
77
|
y1,
|
|
@@ -87,83 +87,35 @@ export function new_line(...args: unknown[]): Line {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
|
-
* Returns the price level of a line at a given bar index.
|
|
90
|
+
* Returns the price level of a line at a given bar index (PineScript `line.get_price`).
|
|
91
91
|
*
|
|
92
92
|
* @param id - Line object
|
|
93
93
|
* @param x - Bar index to calculate price at
|
|
94
|
-
* @returns Price level at the given bar index
|
|
94
|
+
* @returns Price level at the given bar index; NaN for a vertical line (x1 == x2) or a deleted line
|
|
95
95
|
*
|
|
96
96
|
* @remarks
|
|
97
|
-
*
|
|
98
|
-
* -
|
|
99
|
-
* -
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* - 'right': Extends infinitely to the right (x > x2)
|
|
103
|
-
* - 'both': Extends infinitely in both directions
|
|
104
|
-
* - Only works with xloc='bar_index' lines (not xloc='bar_time')
|
|
105
|
-
* - In PineScript, this function treats all lines as if extend='both' regardless of actual setting
|
|
106
|
-
* For compatibility, we follow the actual extend setting
|
|
97
|
+
* As in PineScript (#174):
|
|
98
|
+
* - the line is read as extended on both sides whatever its `extend`: a point before x1 or after x2 has a price
|
|
99
|
+
* - the price is `y1 + (y2 - y1) * (x - x1) / (x2 - x1)`, in that order (bit for bit)
|
|
100
|
+
* - a line placed with `xloc.bar_time` throws "'line.get_price' must be used with lines created using
|
|
101
|
+
* 'xloc=xloc.bar_index'." (runtime error RE10119)
|
|
107
102
|
*
|
|
108
103
|
* @example
|
|
109
104
|
* ```typescript
|
|
110
105
|
* const trendLine = line.new(0, 100, 50, 150, 'bar_index', 'right');
|
|
111
106
|
*
|
|
112
|
-
* //
|
|
113
|
-
* line.get_price(trendLine,
|
|
114
|
-
*
|
|
115
|
-
* // Extended right
|
|
116
|
-
* line.get_price(trendLine, 100); // Returns 200
|
|
117
|
-
*
|
|
118
|
-
* // Not extended left (with extend='right')
|
|
119
|
-
* line.get_price(trendLine, -10); // Returns NaN
|
|
120
|
-
*
|
|
121
|
-
* // Use for breakout detection
|
|
122
|
-
* const currentPrice = close[close.length - 1];
|
|
123
|
-
* const linePrice = line.get_price(trendLine, close.length - 1);
|
|
124
|
-
* if (currentPrice > linePrice) {
|
|
125
|
-
* console.log('Breakout above trend line!');
|
|
126
|
-
* }
|
|
107
|
+
* line.get_price(trendLine, 25); // 125
|
|
108
|
+
* line.get_price(trendLine, 100); // 200
|
|
109
|
+
* line.get_price(trendLine, -10); // 90: extended to the left too
|
|
127
110
|
* ```
|
|
128
111
|
*/
|
|
129
112
|
export function get_price(id: Line, x: number): number {
|
|
130
|
-
const { x1, y1, x2, y2,
|
|
131
|
-
|
|
132
|
-
// Only works with bar_index xloc
|
|
113
|
+
const { x1, y1, x2, y2, xloc } = id;
|
|
133
114
|
if (xloc === 'bar_time') {
|
|
134
|
-
throw new Error('line.get_price
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Avoid division by zero
|
|
138
|
-
if (x2 === x1) {
|
|
139
|
-
// Vertical line - return NaN or could return y1/y2
|
|
140
|
-
return NaN;
|
|
115
|
+
throw new Error("'line.get_price' must be used with lines created using 'xloc=xloc.bar_index'.");
|
|
141
116
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const slope = (y2 - y1) / (x2 - x1);
|
|
145
|
-
const price = y1 + slope * (x - x1);
|
|
146
|
-
|
|
147
|
-
// Check bounds based on extend mode
|
|
148
|
-
if (extend === 'none') {
|
|
149
|
-
// Only valid within the segment
|
|
150
|
-
if (x < Math.min(x1, x2) || x > Math.max(x1, x2)) {
|
|
151
|
-
return NaN;
|
|
152
|
-
}
|
|
153
|
-
} else if (extend === 'left') {
|
|
154
|
-
// Valid to the left and within, but not to the right
|
|
155
|
-
if (x > Math.max(x1, x2)) {
|
|
156
|
-
return NaN;
|
|
157
|
-
}
|
|
158
|
-
} else if (extend === 'right') {
|
|
159
|
-
// Valid to the right and within, but not to the left
|
|
160
|
-
if (x < Math.min(x1, x2)) {
|
|
161
|
-
return NaN;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
// extend === 'both': always valid
|
|
165
|
-
|
|
166
|
-
return price;
|
|
117
|
+
if (x2 === x1) return NaN;
|
|
118
|
+
return y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
|
|
167
119
|
}
|
|
168
120
|
|
|
169
121
|
/**
|
|
@@ -267,7 +219,7 @@ export function copy(id: Line): Line {
|
|
|
267
219
|
*/
|
|
268
220
|
export function set_x1(id: Line, x: number): Line {
|
|
269
221
|
if (isDeleted(id)) return id;
|
|
270
|
-
checkBarIndex('line.set_x***', 'x1', x, id.xloc);
|
|
222
|
+
x = checkBarIndex('line.set_x***', 'x1', x, id.xloc);
|
|
271
223
|
id.x1 = x;
|
|
272
224
|
return id;
|
|
273
225
|
}
|
|
@@ -287,7 +239,7 @@ export function set_x1(id: Line, x: number): Line {
|
|
|
287
239
|
*/
|
|
288
240
|
export function set_x2(id: Line, x: number): Line {
|
|
289
241
|
if (isDeleted(id)) return id;
|
|
290
|
-
checkBarIndex('line.set_x***', 'x2', x, id.xloc);
|
|
242
|
+
x = checkBarIndex('line.set_x***', 'x2', x, id.xloc);
|
|
291
243
|
id.x2 = x;
|
|
292
244
|
return id;
|
|
293
245
|
}
|
|
@@ -346,7 +298,7 @@ export function set_y2(id: Line, y: number): Line {
|
|
|
346
298
|
*/
|
|
347
299
|
export function set_xy1(id: Line, x: number, y: number): Line {
|
|
348
300
|
if (isDeleted(id)) return id;
|
|
349
|
-
checkBarIndex('line.set_x***', 'x1', x, id.xloc);
|
|
301
|
+
x = checkBarIndex('line.set_x***', 'x1', x, id.xloc);
|
|
350
302
|
id.x1 = x;
|
|
351
303
|
id.y1 = y;
|
|
352
304
|
return id;
|
|
@@ -368,7 +320,7 @@ export function set_xy1(id: Line, x: number, y: number): Line {
|
|
|
368
320
|
*/
|
|
369
321
|
export function set_xy2(id: Line, x: number, y: number): Line {
|
|
370
322
|
if (isDeleted(id)) return id;
|
|
371
|
-
checkBarIndex('line.set_x***', 'x2', x, id.xloc);
|
|
323
|
+
x = checkBarIndex('line.set_x***', 'x2', x, id.xloc);
|
|
372
324
|
id.x2 = x;
|
|
373
325
|
id.y2 = y;
|
|
374
326
|
return id;
|
package/src/linefill/index.ts
CHANGED
|
@@ -24,6 +24,9 @@ import type { Linefill, Line, color } from '../types/index.js';
|
|
|
24
24
|
* @remarks
|
|
25
25
|
* - Automatically follows line movements (if lines are updated, fill updates too)
|
|
26
26
|
* - Automatically deleted if either line is deleted
|
|
27
|
+
* - One linefill per pair of lines, as in PineScript (#174): when both lines already belong to a live linefill
|
|
28
|
+
* (`linefill.new(b, a)` after `linefill.new(a, b)`, or `linefill.new(a, a)` after either), that linefill is
|
|
29
|
+
* returned with the new colour instead of a new one; with several, the one created or returned last
|
|
27
30
|
* - If both lines extend in same direction, fill extends too
|
|
28
31
|
* - Purely visual in PineScript, but getters allow retrieving line references
|
|
29
32
|
*
|
|
@@ -42,13 +45,29 @@ import type { Linefill, Line, color } from '../types/index.js';
|
|
|
42
45
|
export function new_linefill(line1: Line, line2: Line, fillColor?: color): Linefill {
|
|
43
46
|
// PineScript: an na line gives an na linefill
|
|
44
47
|
if (line1 === null || line1 === undefined || line2 === null || line2 === undefined) return null as unknown as Linefill;
|
|
45
|
-
|
|
48
|
+
let found: Linefill | undefined;
|
|
49
|
+
for (const fill of allDrawings('linefill')) {
|
|
50
|
+
const has = (l: Line): boolean => fill.line1 === l || fill.line2 === l;
|
|
51
|
+
if (has(line1) && has(line2) && (!found || (touched.get(fill) ?? 0) > (touched.get(found) ?? 0))) found = fill;
|
|
52
|
+
}
|
|
53
|
+
if (found) {
|
|
54
|
+
found.color = fillColor;
|
|
55
|
+
touched.set(found, ++touchCount);
|
|
56
|
+
return found;
|
|
57
|
+
}
|
|
58
|
+
const fill = register('linefill', {
|
|
46
59
|
line1,
|
|
47
60
|
line2,
|
|
48
61
|
color: fillColor
|
|
49
62
|
});
|
|
63
|
+
touched.set(fill, ++touchCount);
|
|
64
|
+
return fill;
|
|
50
65
|
}
|
|
51
66
|
|
|
67
|
+
/** Order in which the linefills were created or returned by `linefill.new` (the last one wins a lookup). */
|
|
68
|
+
const touched = new WeakMap<Linefill, number>();
|
|
69
|
+
let touchCount = 0;
|
|
70
|
+
|
|
52
71
|
/**
|
|
53
72
|
* Returns the first line reference.
|
|
54
73
|
*
|
|
@@ -70,7 +70,7 @@ export class SimpleInputAdapter implements InputAdapter {
|
|
|
70
70
|
|
|
71
71
|
// Validate string, timeframe and session inputs with options
|
|
72
72
|
if (
|
|
73
|
-
(config.type === 'string' || config.type === 'timeframe' || config.type === 'session') &&
|
|
73
|
+
(config.type === 'string' || config.type === 'timeframe' || config.type === 'session' || config.type === 'symbol') &&
|
|
74
74
|
config.options &&
|
|
75
75
|
typeof value === 'string'
|
|
76
76
|
) {
|
package/src/runtime/index.ts
CHANGED
package/src/runtime/inputs.ts
CHANGED
|
@@ -277,6 +277,22 @@ export function input_session(defval: string, title?: string, options?: string[]
|
|
|
277
277
|
return typeof value === 'string' ? value : defval;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Register a symbol input (PineScript `input.symbol`): a ticker id such as "NASDAQ:MSFT" (#180)
|
|
282
|
+
* First call registers with defval, subsequent calls return current value
|
|
283
|
+
* @param defval - Default ticker id
|
|
284
|
+
* @param title - Display title (optional)
|
|
285
|
+
* @param options - Array of symbols for a dropdown (optional)
|
|
286
|
+
* @returns Current ticker id
|
|
287
|
+
*/
|
|
288
|
+
export function input_symbol(defval: string, title?: string, options?: string[]): string {
|
|
289
|
+
if (!getContext()) {
|
|
290
|
+
return defval;
|
|
291
|
+
}
|
|
292
|
+
const value = registerOnce({ id: generateInputId(title, defval, 'symbol'), type: 'symbol', defval, title, options });
|
|
293
|
+
return typeof value === 'string' ? value : defval;
|
|
294
|
+
}
|
|
295
|
+
|
|
280
296
|
/**
|
|
281
297
|
* Register a date/time input (PineScript `input.time`)
|
|
282
298
|
* First call registers with defval, subsequent calls return current value
|
package/src/runtime/types.ts
CHANGED
|
@@ -72,7 +72,7 @@ export interface InputConfig {
|
|
|
72
72
|
* Type of input. `timeframe` and `session` values are strings ("60", "1D", "0930-1600:23456";
|
|
73
73
|
* an empty timeframe means the chart timeframe), `time` values are UNIX times in milliseconds.
|
|
74
74
|
*/
|
|
75
|
-
type: 'int' | 'float' | 'bool' | 'string' | 'source' | 'color' | 'timeframe' | 'session' | 'time';
|
|
75
|
+
type: 'int' | 'float' | 'bool' | 'string' | 'source' | 'color' | 'timeframe' | 'session' | 'time' | 'symbol';
|
|
76
76
|
/** Default value */
|
|
77
77
|
defval: unknown;
|
|
78
78
|
/** Display title */
|