oakscriptjs 0.9.4 → 0.9.6
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 +88 -27
- package/dist/cjs/array/index.d.ts.map +1 -1
- package/dist/cjs/array/index.js +257 -117
- package/dist/cjs/array/index.js.map +3 -3
- package/dist/cjs/callsite/index.d.ts +122 -4
- package/dist/cjs/callsite/index.d.ts.map +1 -1
- package/dist/cjs/callsite/index.js +102 -3
- package/dist/cjs/callsite/index.js.map +2 -2
- package/dist/cjs/color/index.d.ts +7 -3
- package/dist/cjs/color/index.d.ts.map +1 -1
- package/dist/cjs/color/index.js +6 -1
- package/dist/cjs/color/index.js.map +2 -2
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/label/index.d.ts.map +1 -1
- package/dist/cjs/label/index.js +2 -1
- package/dist/cjs/label/index.js.map +2 -2
- package/dist/cjs/linefill/index.d.ts.map +1 -1
- package/dist/cjs/linefill/index.js +1 -0
- package/dist/cjs/linefill/index.js.map +2 -2
- package/dist/cjs/math/index.d.ts +79 -22
- package/dist/cjs/math/index.d.ts.map +1 -1
- package/dist/cjs/math/index.js +157 -43
- package/dist/cjs/math/index.js.map +3 -3
- package/dist/cjs/matrix/index.d.ts.map +1 -1
- package/dist/cjs/matrix/index.js +42 -36
- package/dist/cjs/matrix/index.js.map +2 -2
- package/dist/cjs/runtime/series.d.ts +9 -0
- package/dist/cjs/runtime/series.d.ts.map +1 -1
- package/dist/cjs/runtime/series.js +16 -2
- package/dist/cjs/runtime/series.js.map +2 -2
- package/dist/cjs/script/index.d.ts +15 -31
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +32 -0
- package/dist/cjs/script/index.js.map +2 -2
- package/dist/cjs/session/calendar.d.ts.map +1 -1
- package/dist/cjs/session/calendar.js +15 -2
- package/dist/cjs/session/calendar.js.map +2 -2
- package/dist/cjs/str/index.d.ts +50 -38
- package/dist/cjs/str/index.d.ts.map +1 -1
- package/dist/cjs/str/index.js +43 -15
- package/dist/cjs/str/index.js.map +2 -2
- package/dist/cjs/str/messageformat.d.ts +2 -1
- package/dist/cjs/str/messageformat.d.ts.map +1 -1
- package/dist/cjs/str/messageformat.js +4 -1
- package/dist/cjs/str/messageformat.js.map +2 -2
- package/dist/cjs/str/numberformat.d.ts +4 -0
- package/dist/cjs/str/numberformat.d.ts.map +1 -1
- package/dist/cjs/str/numberformat.js +26 -4
- package/dist/cjs/str/numberformat.js.map +3 -3
- package/dist/cjs/ta/index.d.ts +95 -46
- package/dist/cjs/ta/index.d.ts.map +1 -1
- package/dist/cjs/ta/index.js +193 -318
- package/dist/cjs/ta/index.js.map +3 -3
- package/dist/cjs/ta/running-sum.d.ts +54 -1
- package/dist/cjs/ta/running-sum.d.ts.map +1 -1
- package/dist/cjs/ta/running-sum.js +132 -47
- package/dist/cjs/ta/running-sum.js.map +2 -2
- package/dist/cjs/ta-series.d.ts +4 -4
- package/dist/cjs/ta-series.d.ts.map +1 -1
- package/dist/cjs/ta-series.js +4 -3
- package/dist/cjs/ta-series.js.map +2 -2
- package/dist/cjs/time/datestring.d.ts +1 -0
- package/dist/cjs/time/datestring.d.ts.map +1 -1
- package/dist/cjs/time/datestring.js +7 -0
- package/dist/cjs/time/datestring.js.map +2 -2
- package/dist/cjs/time/index.d.ts.map +1 -1
- package/dist/cjs/time/index.js +2 -1
- package/dist/cjs/time/index.js.map +2 -2
- package/dist/cjs/time/session.d.ts +28 -9
- package/dist/cjs/time/session.d.ts.map +1 -1
- package/dist/cjs/time/session.js +74 -28
- package/dist/cjs/time/session.js.map +2 -2
- package/dist/cjs/time/timezone.d.ts +5 -0
- package/dist/cjs/time/timezone.d.ts.map +1 -1
- package/dist/cjs/time/timezone.js +15 -1
- package/dist/cjs/time/timezone.js.map +2 -2
- package/dist/esm/array/index.d.ts +88 -27
- package/dist/esm/array/index.d.ts.map +1 -1
- package/dist/esm/array/index.js +408 -157
- package/dist/esm/array/index.js.map +1 -1
- package/dist/esm/callsite/index.d.ts +122 -4
- package/dist/esm/callsite/index.d.ts.map +1 -1
- package/dist/esm/callsite/index.js +228 -8
- package/dist/esm/callsite/index.js.map +1 -1
- package/dist/esm/color/index.d.ts +7 -3
- package/dist/esm/color/index.d.ts.map +1 -1
- package/dist/esm/color/index.js +15 -3
- package/dist/esm/color/index.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/label/index.d.ts.map +1 -1
- package/dist/esm/label/index.js +2 -1
- package/dist/esm/label/index.js.map +1 -1
- package/dist/esm/linefill/index.d.ts.map +1 -1
- package/dist/esm/linefill/index.js +3 -0
- package/dist/esm/linefill/index.js.map +1 -1
- package/dist/esm/math/index.d.ts +79 -22
- package/dist/esm/math/index.d.ts.map +1 -1
- package/dist/esm/math/index.js +229 -63
- package/dist/esm/math/index.js.map +1 -1
- package/dist/esm/matrix/index.d.ts.map +1 -1
- package/dist/esm/matrix/index.js +57 -43
- package/dist/esm/matrix/index.js.map +1 -1
- package/dist/esm/runtime/series.d.ts +9 -0
- package/dist/esm/runtime/series.d.ts.map +1 -1
- package/dist/esm/runtime/series.js +18 -1
- package/dist/esm/runtime/series.js.map +1 -1
- package/dist/esm/script/index.d.ts +15 -31
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +44 -1
- package/dist/esm/script/index.js.map +1 -1
- package/dist/esm/session/calendar.d.ts.map +1 -1
- package/dist/esm/session/calendar.js +22 -2
- package/dist/esm/session/calendar.js.map +1 -1
- package/dist/esm/str/index.d.ts +50 -38
- package/dist/esm/str/index.d.ts.map +1 -1
- package/dist/esm/str/index.js +103 -52
- package/dist/esm/str/index.js.map +1 -1
- package/dist/esm/str/messageformat.d.ts +2 -1
- package/dist/esm/str/messageformat.d.ts.map +1 -1
- package/dist/esm/str/messageformat.js +7 -2
- package/dist/esm/str/messageformat.js.map +1 -1
- package/dist/esm/str/numberformat.d.ts +4 -0
- package/dist/esm/str/numberformat.d.ts.map +1 -1
- package/dist/esm/str/numberformat.js +38 -4
- package/dist/esm/str/numberformat.js.map +1 -1
- package/dist/esm/ta/index.d.ts +95 -46
- package/dist/esm/ta/index.d.ts.map +1 -1
- package/dist/esm/ta/index.js +341 -414
- package/dist/esm/ta/index.js.map +1 -1
- package/dist/esm/ta/running-sum.d.ts +54 -1
- package/dist/esm/ta/running-sum.d.ts.map +1 -1
- package/dist/esm/ta/running-sum.js +166 -60
- package/dist/esm/ta/running-sum.js.map +1 -1
- package/dist/esm/ta-series.d.ts +4 -4
- package/dist/esm/ta-series.d.ts.map +1 -1
- package/dist/esm/ta-series.js +5 -4
- package/dist/esm/ta-series.js.map +1 -1
- package/dist/esm/time/datestring.d.ts +1 -0
- package/dist/esm/time/datestring.d.ts.map +1 -1
- package/dist/esm/time/datestring.js +12 -0
- package/dist/esm/time/datestring.js.map +1 -1
- package/dist/esm/time/index.d.ts.map +1 -1
- package/dist/esm/time/index.js +3 -1
- package/dist/esm/time/index.js.map +1 -1
- package/dist/esm/time/session.d.ts +28 -9
- package/dist/esm/time/session.d.ts.map +1 -1
- package/dist/esm/time/session.js +113 -42
- package/dist/esm/time/session.js.map +1 -1
- package/dist/esm/time/timezone.d.ts +5 -0
- package/dist/esm/time/timezone.d.ts.map +1 -1
- package/dist/esm/time/timezone.js +21 -1
- package/dist/esm/time/timezone.js.map +1 -1
- package/package.json +2 -2
- package/src/array/index.ts +382 -174
- package/src/callsite/index.ts +232 -8
- package/src/color/index.ts +15 -5
- package/src/index.ts +1 -1
- package/src/label/index.ts +2 -1
- package/src/linefill/index.ts +2 -0
- package/src/math/index.ts +261 -77
- package/src/matrix/index.ts +52 -52
- package/src/runtime/series.ts +22 -1
- package/src/script/index.ts +53 -1
- package/src/session/calendar.ts +22 -2
- package/src/str/index.ts +95 -51
- package/src/str/messageformat.ts +7 -2
- package/src/str/numberformat.ts +39 -4
- package/src/ta/index.ts +356 -458
- package/src/ta/running-sum.ts +187 -62
- package/src/ta-series.ts +8 -7
- package/src/time/datestring.ts +10 -0
- package/src/time/index.ts +3 -1
- package/src/time/session.ts +97 -47
- package/src/time/timezone.ts +21 -1
package/src/callsite/index.ts
CHANGED
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
* loops, called only on the bars where the Pine call runs.
|
|
12
12
|
* - {@link lowest} / {@link highest}: `ta.lowest` / `ta.highest` with a series length (a different length on each
|
|
13
13
|
* call), which PineScript computes with a state kept between calls.
|
|
14
|
-
* - {@link linreg}: `ta.
|
|
14
|
+
* - {@link lowestByBar} / {@link highestByBar} / {@link linreg}: `ta.lowest` / `ta.highest` / `ta.linreg` in a
|
|
15
|
+
* conditional block, whose history is kept by bar (not by call).
|
|
16
|
+
* - {@link sum} / {@link sma}: `math.sum` / `ta.sma` in a `for` / `while` loop, called several times on one bar.
|
|
15
17
|
*
|
|
16
18
|
* Since PineScript v6, `and` / `or` are lazy: the right operand runs only when the left operand does not decide
|
|
17
19
|
* the result. A ta.* call in the right operand of `a or b` runs on the bars where `a` is false, in `a and b` on the
|
|
@@ -42,6 +44,8 @@
|
|
|
42
44
|
|
|
43
45
|
import type { Source } from '../types/index.js';
|
|
44
46
|
import { linreg as linregSeries } from '../ta/index.js';
|
|
47
|
+
import { RunningSum, isNa } from '../ta/running-sum.js';
|
|
48
|
+
import { seededRandom } from '../math/index.js';
|
|
45
49
|
|
|
46
50
|
/**
|
|
47
51
|
* Runs `fn` on the bars where `called` is true only, like a ta.* call inside an `if` block.
|
|
@@ -67,24 +71,35 @@ export function whenCalled<T extends number | boolean>(
|
|
|
67
71
|
return out;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* A call site that keeps the arguments of the last call where both were not na (na before such a call), as
|
|
76
|
+
* ta.crossover / ta.crossunder / ta.cross.
|
|
77
|
+
*/
|
|
71
78
|
function twoSeriesSite(test: (a: number, b: number, prevA: number, prevB: number) => boolean) {
|
|
72
79
|
let prevA = NaN;
|
|
73
80
|
let prevB = NaN;
|
|
74
81
|
return (a: number, b: number): boolean => {
|
|
75
82
|
const result = test(a, b, prevA, prevB);
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
if (!Number.isNaN(a) && !Number.isNaN(b)) {
|
|
84
|
+
prevA = a;
|
|
85
|
+
prevB = b;
|
|
86
|
+
}
|
|
78
87
|
return result;
|
|
79
88
|
};
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* One `ta.crossover(a, b)` call site: compared with the last call where both arguments were not na; false before
|
|
93
|
+
* such a call.
|
|
94
|
+
*/
|
|
83
95
|
export function crossover(): (a: number, b: number) => boolean {
|
|
84
96
|
return twoSeriesSite((a, b, pa, pb) => a > b && pa <= pb);
|
|
85
97
|
}
|
|
86
98
|
|
|
87
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* One `ta.crossunder(a, b)` call site: compared with the last call where both arguments were not na; false before
|
|
101
|
+
* such a call.
|
|
102
|
+
*/
|
|
88
103
|
export function crossunder(): (a: number, b: number) => boolean {
|
|
89
104
|
return twoSeriesSite((a, b, pa, pb) => a < b && pa >= pb);
|
|
90
105
|
}
|
|
@@ -102,9 +117,20 @@ export function barssince(): (condition: boolean | number) => number {
|
|
|
102
117
|
};
|
|
103
118
|
}
|
|
104
119
|
|
|
105
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* One `ta.cross(a, b)` call site, rules of `ta.cross`: the site keeps the side of the last call where `a` was
|
|
122
|
+
* strictly above or strictly below `b` (none before such a call; equal values and na arguments do not change it),
|
|
123
|
+
* and is true when the arguments are strictly on the other side. False with an na argument.
|
|
124
|
+
*/
|
|
106
125
|
export function cross(): (a: number, b: number) => boolean {
|
|
107
|
-
|
|
126
|
+
let side = 0; // -1: last strictly below, 1: last strictly above, 0: none yet
|
|
127
|
+
return (a, b) => {
|
|
128
|
+
if (Number.isNaN(a) || Number.isNaN(b)) return false;
|
|
129
|
+
const result = (side < 0 && a > b) || (side > 0 && a < b);
|
|
130
|
+
if (a < b) side = -1;
|
|
131
|
+
else if (a > b) side = 1;
|
|
132
|
+
return result;
|
|
133
|
+
};
|
|
108
134
|
}
|
|
109
135
|
|
|
110
136
|
/** One `ta.lowest` / `ta.highest` call site with a series length; rules in {@link lowest}. */
|
|
@@ -179,6 +205,71 @@ export function highest(): (value: number, length: number) => number {
|
|
|
179
205
|
return extremeSite(false);
|
|
180
206
|
}
|
|
181
207
|
|
|
208
|
+
/** One `ta.lowest` / `ta.highest` call site whose history is kept by bar; rules in {@link lowestByBar}. */
|
|
209
|
+
function extremeByBarSite(isLowest: boolean): (barIndex: number, value: number, length: number) => number {
|
|
210
|
+
const better = isLowest ? (a: number, b: number) => a < b : (a: number, b: number) => a > b;
|
|
211
|
+
let ring: number[] = [];
|
|
212
|
+
let extreme = NaN;
|
|
213
|
+
let extremeAt = -1;
|
|
214
|
+
return (barIndex, value, length) => {
|
|
215
|
+
const len = Math.floor(length);
|
|
216
|
+
if (ring.length !== len + 1) {
|
|
217
|
+
ring = new Array<number>(len + 1).fill(0);
|
|
218
|
+
extreme = NaN;
|
|
219
|
+
}
|
|
220
|
+
ring[barIndex % (len + 1)] = value;
|
|
221
|
+
if (Number.isNaN(extreme) || better(value, extreme)) {
|
|
222
|
+
extreme = value;
|
|
223
|
+
extremeAt = barIndex;
|
|
224
|
+
} else if (barIndex - extremeAt >= len) {
|
|
225
|
+
extreme = NaN;
|
|
226
|
+
for (let b = barIndex - len + 1; b <= barIndex; b++) {
|
|
227
|
+
const v = ring[b % (len + 1)]!;
|
|
228
|
+
if (!Number.isNaN(v) && (Number.isNaN(extreme) || better(v, extreme))) {
|
|
229
|
+
extreme = v;
|
|
230
|
+
extremeAt = b;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return barIndex < len - 1 ? NaN : extreme;
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* One `ta.lowest(source, length)` call site in a conditional block whose history is kept by bar (not by call), called
|
|
240
|
+
* with `(barIndex, value, length)` on the bars where the Pine call runs.
|
|
241
|
+
*
|
|
242
|
+
* PineScript rules (measured on a call in a ternary branch, length 6):
|
|
243
|
+
* - the values are kept in a ring of `length + 1` slots indexed by the bar index, filled with 0 at the start and
|
|
244
|
+
* written only on the bars where the call runs: a bar before the first call counts as 0, and a bar without a call
|
|
245
|
+
* holds the value of the call `length + 1` bars earlier (0 if there was none)
|
|
246
|
+
* - on a call, the value replaces the kept extreme when there is none or when it is strictly lower; only otherwise,
|
|
247
|
+
* when the kept extreme is `length` bars old, the last `length` bars are read again from the ring (the oldest bar
|
|
248
|
+
* wins a tie)
|
|
249
|
+
* - na before bar `length - 1` (by bar, not by call)
|
|
250
|
+
*
|
|
251
|
+
* {@link lowest} keeps the history by call instead; both rules were measured, on different scripts.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* // Pine: lo = cond ? ta.lowest(low, 6) : na
|
|
256
|
+
* const site = callsite.lowestByBar();
|
|
257
|
+
* const lo = lows.map((v, i) => (cond[i] ? site(i, v, 6) : NaN));
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
export function lowestByBar(): (barIndex: number, value: number, length: number) => number {
|
|
261
|
+
return extremeByBarSite(true);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* One `ta.highest(source, length)` call site in a conditional block whose history is kept by bar, called with
|
|
266
|
+
* `(barIndex, value, length)`. Same rules as {@link lowestByBar} (a value replaces the kept extreme when it is
|
|
267
|
+
* strictly higher).
|
|
268
|
+
*/
|
|
269
|
+
export function highestByBar(): (barIndex: number, value: number, length: number) => number {
|
|
270
|
+
return extremeByBarSite(false);
|
|
271
|
+
}
|
|
272
|
+
|
|
182
273
|
/**
|
|
183
274
|
* One `ta.linreg(source, length, offset)` call site in a conditional block, called with
|
|
184
275
|
* `(barIndex, value, length, offset)` on the bars where the Pine call runs.
|
|
@@ -209,3 +300,136 @@ export function linreg(): (barIndex: number, value: number, length: number, offs
|
|
|
209
300
|
};
|
|
210
301
|
}
|
|
211
302
|
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* One `math.sum` call site in a loop; rules in {@link sum}. With `squares`, it also keeps the running sum of the
|
|
306
|
+
* squared values (for {@link stdev}). Returns `[sum, sumOfSquares]` (NaN when not kept).
|
|
307
|
+
*/
|
|
308
|
+
function loopSumSite(
|
|
309
|
+
name: string,
|
|
310
|
+
squares: boolean
|
|
311
|
+
): (barIndex: number, value: number, length: number) => [number, number] {
|
|
312
|
+
const running = new RunningSum();
|
|
313
|
+
const runningSq = new RunningSum();
|
|
314
|
+
let lastBar = NaN;
|
|
315
|
+
let pending = false;
|
|
316
|
+
return (barIndex, value, length) => {
|
|
317
|
+
const len = Math.floor(length);
|
|
318
|
+
if (!(len >= 1)) throw new Error(`callsite.${name}: length must be at least 1 (got ${length})`);
|
|
319
|
+
if (barIndex !== lastBar) {
|
|
320
|
+
if (pending) {
|
|
321
|
+
running.commit();
|
|
322
|
+
if (squares) runningSq.commit();
|
|
323
|
+
}
|
|
324
|
+
pending = false;
|
|
325
|
+
lastBar = barIndex;
|
|
326
|
+
}
|
|
327
|
+
if (isNa(value)) {
|
|
328
|
+
pending = false;
|
|
329
|
+
return [running.stepNa(len), squares ? runningSq.stepNa(len) : NaN];
|
|
330
|
+
}
|
|
331
|
+
const x = value;
|
|
332
|
+
const out: [number, number] = [running.step(x, len), squares ? runningSq.step(x * x, len) : NaN];
|
|
333
|
+
pending = true;
|
|
334
|
+
return out;
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* One `math.sum(source, length)` call site inside a `for` / `while` loop, called with `(barIndex, value, length)` on
|
|
340
|
+
* each call.
|
|
341
|
+
*
|
|
342
|
+
* PineScript rules (measured on calls in loops, 1 to 3 calls per bar, and bars without a call):
|
|
343
|
+
* - the call site keeps one history value per bar: the value of its last call on that bar
|
|
344
|
+
* - every call on a bar starts from the state of the earlier bars, so it gives the sum of its value and the last
|
|
345
|
+
* `length - 1` kept values; the last call of the bar is the one kept for the later bars
|
|
346
|
+
* - the length can change from one call to the next (also on the same bar): each call first moves the kept sum to
|
|
347
|
+
* its own length as `math.sum` with a series length does (Kahan steps of the raw values)
|
|
348
|
+
* - a bar without a call is not in the history (as a call in an `if` block)
|
|
349
|
+
* - the sum is the same compensated running sum as `math.sum` (bit for bit)
|
|
350
|
+
*
|
|
351
|
+
* An na value is skipped as in `math.sum` (it returns the sum of the last `length` earlier values); when it is the
|
|
352
|
+
* last call of a bar, the bar is not in the history (not measured).
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```typescript
|
|
356
|
+
* // Pine: for i = 0 to 2
|
|
357
|
+
* // s = math.sum(close * (1 + 0.01 * i), 7)
|
|
358
|
+
* const site = callsite.sum();
|
|
359
|
+
* for (let b = 0; b < closes.length; b++) {
|
|
360
|
+
* for (let i = 0; i <= 2; i++) {
|
|
361
|
+
* const s = site(b, closes[b]! * (1 + 0.01 * i), 7);
|
|
362
|
+
* }
|
|
363
|
+
* }
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
export function sum(): (barIndex: number, value: number, length: number) => number {
|
|
367
|
+
const site = loopSumSite('sum', false);
|
|
368
|
+
return (barIndex, value, length) => site(barIndex, value, length)[0];
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* One `ta.sma(source, length)` call site inside a `for` / `while` loop, called with `(barIndex, value, length)` on
|
|
373
|
+
* each call: {@link sum} divided by the length (same rules).
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* // Pine: evaluate(ratio) => ta.sma(gann(ratio), n) // called once per ratio in a while loop
|
|
378
|
+
* const site = callsite.sma();
|
|
379
|
+
* for (let b = 0; b < closes.length; b++) {
|
|
380
|
+
* for (const ratio of ratios) {
|
|
381
|
+
* const avg = site(b, gann(b, ratio), n);
|
|
382
|
+
* }
|
|
383
|
+
* }
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
export function sma(): (barIndex: number, value: number, length: number) => number {
|
|
387
|
+
const site = loopSumSite('sma', false);
|
|
388
|
+
return (barIndex, value, length) => site(barIndex, value, length)[0] / Math.floor(length);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* One `ta.stdev(source, length, biased)` call site inside a `for` / `while` loop, called with
|
|
393
|
+
* `(barIndex, value, length, biased?)` on each call: the running sums of the values and of their squares follow the
|
|
394
|
+
* rules of {@link sum}; the result is computed from them as `ta.stdev` (bit for bit).
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```typescript
|
|
398
|
+
* // Pine: for len in lengths
|
|
399
|
+
* // [mid, up, lo] = ta.bb(close, len, 2)
|
|
400
|
+
* const avgSite = callsite.sma();
|
|
401
|
+
* const devSite = callsite.stdev();
|
|
402
|
+
* for (let b = 0; b < closes.length; b++) {
|
|
403
|
+
* for (const len of lengths) {
|
|
404
|
+
* const mid = avgSite(b, closes[b]!, len);
|
|
405
|
+
* const dev = devSite(b, closes[b]!, len);
|
|
406
|
+
* }
|
|
407
|
+
* }
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
410
|
+
export function stdev(): (barIndex: number, value: number, length: number, biased?: boolean) => number {
|
|
411
|
+
const site = loopSumSite('stdev', true);
|
|
412
|
+
return (barIndex, value, length, biased = true) => {
|
|
413
|
+
const len = Math.floor(length);
|
|
414
|
+
const [s, sq] = site(barIndex, value, length);
|
|
415
|
+
const mean = s / len;
|
|
416
|
+
const variance = biased ? sq / len - mean * mean : sq / (len - 1) - (mean * s) / (len - 1);
|
|
417
|
+
if (!biased && len <= 1) return NaN;
|
|
418
|
+
return Number.isNaN(variance) ? NaN : variance > 0 ? Math.sqrt(variance) : 0;
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* One `math.random(min, max, seed)` call site: PineScript keeps one generator per call site, so two call sites
|
|
424
|
+
* with the same seed give the same sequence. Called with `(min?, max?)` once per bar.
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```typescript
|
|
428
|
+
* // Pine: a = math.random(0, 1, 42), b = math.random(0, 1, 42) // a == b on every bar
|
|
429
|
+
* const a = callsite.random(42);
|
|
430
|
+
* const b = callsite.random(42);
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
433
|
+
export function random(seed: number): (min?: number, max?: number) => number {
|
|
434
|
+
return seededRandom(seed);
|
|
435
|
+
}
|
package/src/color/index.ts
CHANGED
|
@@ -107,7 +107,10 @@ export function from_hex(hex: string, transp?: simple_float): string {
|
|
|
107
107
|
* - Replaces the transparency with the new value
|
|
108
108
|
* - Useful for creating semi-transparent versions of existing colors
|
|
109
109
|
* - PineScript clamps the transparency to 0..100 (`color.new(c, 140)` is fully transparent, `color.new(c, -5)` is
|
|
110
|
-
* opaque); an `na` transparency is fully transparent
|
|
110
|
+
* opaque); an `na` transparency is fully transparent (measured: the colour is not na, its RGB is kept)
|
|
111
|
+
* - an `na` colour (null / undefined) gives black with the new transparency: `color.new(na, 30)` is
|
|
112
|
+
* `rgba(0, 0, 0, 0.7)`, as PineScript does for an na colour variable (measured, #150). In PineScript, the constant
|
|
113
|
+
* `color.new(color(na), 30)` is computed at compile time and stays na; a JS function cannot tell the two apart.
|
|
111
114
|
*
|
|
112
115
|
* @example
|
|
113
116
|
* ```typescript
|
|
@@ -117,7 +120,8 @@ export function from_hex(hex: string, transp?: simple_float): string {
|
|
|
117
120
|
* color.new_color(red, 100) // Returns: "rgba(255, 0, 0, 0)" - Fully transparent
|
|
118
121
|
* ```
|
|
119
122
|
*/
|
|
120
|
-
export function new_color(baseColor: color, transp: simple_float): string {
|
|
123
|
+
export function new_color(baseColor: color | null | undefined, transp: simple_float): string {
|
|
124
|
+
if (baseColor === null || baseColor === undefined) return rgb(0, 0, 0, transp); // na colour: black
|
|
121
125
|
// Parse the color and apply transparency
|
|
122
126
|
const rgba = parseColor(baseColor);
|
|
123
127
|
return rgb(rgba.r, rgba.g, rgba.b, transp);
|
|
@@ -185,6 +189,7 @@ export function b(clr: color): int {
|
|
|
185
189
|
* - Converts alpha channel (0-1) to transparency (0-100)
|
|
186
190
|
* - PineScript stores the alpha as a byte (0-255) and returns an integer transparency:
|
|
187
191
|
* `color.t(color.new(c, 33.3))` is 33
|
|
192
|
+
* - an `na` colour (null / undefined) is fully transparent: 100
|
|
188
193
|
*
|
|
189
194
|
* @example
|
|
190
195
|
* ```typescript
|
|
@@ -193,7 +198,8 @@ export function b(clr: color): int {
|
|
|
193
198
|
* color.t(color.rgb(255, 0, 0, 100)) // Returns: 100 (fully transparent)
|
|
194
199
|
* ```
|
|
195
200
|
*/
|
|
196
|
-
export function t(clr: color): float {
|
|
201
|
+
export function t(clr: color | null | undefined): float {
|
|
202
|
+
if (clr === null || clr === undefined) return 100;
|
|
197
203
|
return Math.round((1 - alphaByte(parseColor(clr).a) / 255) * 100);
|
|
198
204
|
}
|
|
199
205
|
|
|
@@ -362,8 +368,12 @@ export function from_gradient(
|
|
|
362
368
|
const c2 = rgba(top_color);
|
|
363
369
|
const a = c1.a * (1 - k) + c2.a * k;
|
|
364
370
|
if (a === 0) return transparent;
|
|
365
|
-
//
|
|
366
|
-
|
|
371
|
+
// PineScript order (bit for bit, #146): premultiplied channels ((alpha / 255) * channel) * weight, divided by the
|
|
372
|
+
// byte-alpha mix / 255, truncated. At an end (k = 0 / 1) this is floor(x * a / a): the end colour or one unit less.
|
|
373
|
+
const scale = a / 255;
|
|
374
|
+
const w1 = c1.a / 255;
|
|
375
|
+
const w2 = c2.a / 255;
|
|
376
|
+
const mix = (x1: number, x2: number) => Math.floor(((w1 * x1) * (1 - k) + (w2 * x2) * k) / scale);
|
|
367
377
|
const [r, g, b] = [mix(c1.r, c2.r), mix(c1.g, c2.g), mix(c1.b, c2.b)];
|
|
368
378
|
const alpha = Math.floor(a);
|
|
369
379
|
return alpha === 255 ? `rgb(${r}, ${g}, ${b})` : `rgba(${r}, ${g}, ${b}, ${alpha / 255})`;
|
package/src/index.ts
CHANGED
|
@@ -164,7 +164,7 @@ export type { LightweightSeriesDefinitions } from './runtime/adapters/Lightweigh
|
|
|
164
164
|
export { SimpleInputAdapter } from './runtime/adapters/SimpleInputAdapter.js';
|
|
165
165
|
|
|
166
166
|
// Version
|
|
167
|
-
export const VERSION = '0.9.
|
|
167
|
+
export const VERSION = '0.9.6';
|
|
168
168
|
|
|
169
169
|
/**
|
|
170
170
|
* Library information
|
package/src/label/index.ts
CHANGED
|
@@ -85,7 +85,8 @@ export function new_label(...args: unknown[]): Label {
|
|
|
85
85
|
y,
|
|
86
86
|
xloc: xloc ?? 'bar_index',
|
|
87
87
|
yloc: yloc ?? 'price',
|
|
88
|
-
text
|
|
88
|
+
// PineScript: a label without text has the text "" (label.get_text gives "")
|
|
89
|
+
text: text ?? '',
|
|
89
90
|
tooltip,
|
|
90
91
|
color: labelColor,
|
|
91
92
|
style,
|
package/src/linefill/index.ts
CHANGED
|
@@ -40,6 +40,8 @@ import type { Linefill, Line, color } from '../types/index.js';
|
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
42
|
export function new_linefill(line1: Line, line2: Line, fillColor?: color): Linefill {
|
|
43
|
+
// PineScript: an na line gives an na linefill
|
|
44
|
+
if (line1 === null || line1 === undefined || line2 === null || line2 === undefined) return null as unknown as Linefill;
|
|
43
45
|
return register('linefill', {
|
|
44
46
|
line1,
|
|
45
47
|
line2,
|