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/src/ta/limits.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History limits of the window functions.
|
|
3
|
+
*
|
|
4
|
+
* PineScript stops a script when `ta.highest` / `ta.lowest` / `ta.highestbars` / `ta.lowestbars` ask for more than
|
|
5
|
+
* 15,000 bars (runtime error RE10004): with a constant length on the first bar, with a length that changes on the
|
|
6
|
+
* bar where it passes the limit. `math.sum`, `ta.sma`, `ta.variance`, `ta.stdev` and `ta.bb` have the same error
|
|
7
|
+
* above 5,000 bars; the message names the function that keeps the window ('stdev'->'variance', 'bb'->'sma').
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Largest length of `ta.highest` / `ta.lowest` / `ta.highestbars` / `ta.lowestbars`. */
|
|
11
|
+
export const MAX_WINDOW_LENGTH = 15000;
|
|
12
|
+
|
|
13
|
+
/** Largest length of `math.sum` / `ta.sma` / `ta.variance` / `ta.stdev` / `ta.bb`. */
|
|
14
|
+
export const MAX_SUM_LENGTH = 5000;
|
|
15
|
+
|
|
16
|
+
/** Names of the PineScript message for the functions with the 5,000 limit ("a->b": a calls b). */
|
|
17
|
+
export const SUM_FUNCTIONS: Record<string, string> = {
|
|
18
|
+
'math.sum': 'sum',
|
|
19
|
+
'ta.sma': 'sma',
|
|
20
|
+
'ta.variance': 'variance',
|
|
21
|
+
'ta.stdev': 'stdev->variance',
|
|
22
|
+
'ta.bb': 'bb->sma',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* PineScript's runtime error RE10004 text; `bar` is the bar index of the call when it is known. `name` is the
|
|
27
|
+
* function, or a call path "stdev->variance" (printed 'stdev'->'variance').
|
|
28
|
+
*/
|
|
29
|
+
export function windowError(name: string, length: number, bar?: number, max = MAX_WINDOW_LENGTH): string {
|
|
30
|
+
const fn = name.split('->').map((part) => `'${part}'`).join('->');
|
|
31
|
+
return (
|
|
32
|
+
(bar === undefined ? '' : `Error on bar ${bar}: `) +
|
|
33
|
+
`The ${fn} function references too many historical candles (${length}), the limit is ${max}.`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Throws RE10004 when the (truncated) length of a window function is above the limit. */
|
|
38
|
+
export function checkWindowLength(name: string, length: number, bar?: number, max = MAX_WINDOW_LENGTH): void {
|
|
39
|
+
const len = Math.trunc(length);
|
|
40
|
+
if (len <= 0) throw new Error(lengthError(name, len, bar));
|
|
41
|
+
if (len > max) throw new Error(windowError(name, len, bar, max));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Throws RE10004 for a function of {@link SUM_FUNCTIONS} ("ta.sma", ...) with a length above 5,000. */
|
|
45
|
+
export function checkSumLength(name: string, length: number, bar?: number): void {
|
|
46
|
+
const fn = SUM_FUNCTIONS[name];
|
|
47
|
+
if (fn === undefined) return;
|
|
48
|
+
// RE10001 names 'bb' itself, RE10004 its inner 'sma'
|
|
49
|
+
checkPositiveLength(name === 'ta.bb' ? 'bb' : fn, length, bar);
|
|
50
|
+
checkWindowLength(fn, length, bar, MAX_SUM_LENGTH);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* PineScript's runtime error RE10001 text for a length that is 0 or negative. `name` is the function, or a call path
|
|
55
|
+
* "atr->rma" (printed 'atr'->'rma'); the value prints as a float ("0.0", "-1.0").
|
|
56
|
+
*/
|
|
57
|
+
export function lengthError(name: string, value: number, bar?: number, arg = 'length'): string {
|
|
58
|
+
const fn = name.split('->').map((part) => `'${part}'`).join('->');
|
|
59
|
+
const text = Number.isInteger(value) ? value.toFixed(1) : String(value);
|
|
60
|
+
return (
|
|
61
|
+
(bar === undefined ? '' : `Error on bar ${bar}: `) +
|
|
62
|
+
`Invalid value of the '${arg}' argument (${text}) in the ${fn} function. It must be > 0.`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Throws RE10001 when the (truncated) length is 0 or negative, as PineScript does for every window function on the
|
|
68
|
+
* bar of the call (#175): `ta.sma(close, 0)` stops on bar 0, a series length that becomes 0 on the bar where it
|
|
69
|
+
* does. `ta.hma(src, 1)` stops in its inner `wma(src, 0)`. An na length is not checked here.
|
|
70
|
+
*/
|
|
71
|
+
export function checkPositiveLength(name: string, length: number, bar?: number, arg = 'length'): void {
|
|
72
|
+
const len = Math.trunc(length);
|
|
73
|
+
if (len <= 0) throw new Error(lengthError(name, len, bar, arg));
|
|
74
|
+
}
|
package/src/ta/running-sum.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* @internal
|
|
16
16
|
*/
|
|
17
|
+
import { checkSumLength } from './limits.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Whether the compensation `c` is (partly) lost when it is applied to `x`: `x + |c|` in floating point moves `x` by
|
|
@@ -176,6 +177,7 @@ function seriesLengths(lengths: ArrayLike<number>, count: number, name: string):
|
|
|
176
177
|
}
|
|
177
178
|
return Array.from({ length: count }, (_, i) => {
|
|
178
179
|
const len = Math.floor(lengths[i]!);
|
|
180
|
+
checkSumLength(name, len, i);
|
|
179
181
|
if (!(len >= 1)) throw new Error(`${name}: length must be at least 1 (bar ${i}: ${lengths[i]})`);
|
|
180
182
|
return len;
|
|
181
183
|
});
|
package/src/ta-series.ts
CHANGED
|
@@ -40,10 +40,10 @@ export function ema(source: Series, length: number): Series {
|
|
|
40
40
|
* @param length - Period length
|
|
41
41
|
* @returns Series with WMA values
|
|
42
42
|
*/
|
|
43
|
-
export function wma(source: Series, length: number): Series {
|
|
43
|
+
export function wma(source: Series, length: number | Series): Series {
|
|
44
44
|
const bars = source.bars as Bar[];
|
|
45
45
|
const sourceValues = source.toArray();
|
|
46
|
-
const result = taCore.wma(sourceValues, length);
|
|
46
|
+
const result = taCore.wma(sourceValues, length instanceof Series ? length.toArray() : length);
|
|
47
47
|
return Series.fromArray(bars, result);
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -66,10 +66,11 @@ export function rma(source: Series, length: number): Series {
|
|
|
66
66
|
* @param length - Period length
|
|
67
67
|
* @returns Series with RSI values
|
|
68
68
|
*/
|
|
69
|
-
export function rsi(source: Series, length: number): Series {
|
|
69
|
+
export function rsi(source: Series, length: number | Series): Series {
|
|
70
70
|
const bars = source.bars as Bar[];
|
|
71
71
|
const sourceValues = source.toArray();
|
|
72
|
-
|
|
72
|
+
// a Series as second argument: PineScript v4 rsi(x, y) of two averages (#180)
|
|
73
|
+
const result = length instanceof Series ? taCore.rsi(sourceValues, length.toArray()) : taCore.rsi(sourceValues, length);
|
|
73
74
|
return Series.fromArray(bars, result);
|
|
74
75
|
}
|
|
75
76
|
|
|
@@ -333,10 +334,10 @@ export function roc(source: Series, length: number): Series {
|
|
|
333
334
|
* @param length - Period length
|
|
334
335
|
* @returns Series with highest values
|
|
335
336
|
*/
|
|
336
|
-
export function highest(source: Series, length: number): Series {
|
|
337
|
+
export function highest(source: Series, length: number | Series): Series {
|
|
337
338
|
const bars = source.bars as Bar[];
|
|
338
339
|
const sourceValues = source.toArray();
|
|
339
|
-
const result = taCore.highest(sourceValues, length);
|
|
340
|
+
const result = taCore.highest(sourceValues, length instanceof Series ? length.toArray() : length);
|
|
340
341
|
return Series.fromArray(bars, result);
|
|
341
342
|
}
|
|
342
343
|
|
|
@@ -346,10 +347,10 @@ export function highest(source: Series, length: number): Series {
|
|
|
346
347
|
* @param length - Period length
|
|
347
348
|
* @returns Series with lowest values
|
|
348
349
|
*/
|
|
349
|
-
export function lowest(source: Series, length: number): Series {
|
|
350
|
+
export function lowest(source: Series, length: number | Series): Series {
|
|
350
351
|
const bars = source.bars as Bar[];
|
|
351
352
|
const sourceValues = source.toArray();
|
|
352
|
-
const result = taCore.lowest(sourceValues, length);
|
|
353
|
+
const result = taCore.lowest(sourceValues, length instanceof Series ? length.toArray() : length);
|
|
353
354
|
return Series.fromArray(bars, result);
|
|
354
355
|
}
|
|
355
356
|
|
|
@@ -688,9 +689,10 @@ export function hma(source: Series, length: number): Series {
|
|
|
688
689
|
* @returns Series with the pivot high value on the bar where the pivot is confirmed,
|
|
689
690
|
* `rightbars` bars after the pivot bar (as in PineScript), NaN elsewhere
|
|
690
691
|
*/
|
|
691
|
-
export function pivothigh(source: Series, leftbars: number, rightbars: number): Series {
|
|
692
|
+
export function pivothigh(source: Series, leftbars: number | Series, rightbars: number | Series): Series {
|
|
692
693
|
const bars = source.bars as Bar[];
|
|
693
|
-
const
|
|
694
|
+
const arr = (v: number | Series) => (v instanceof Series ? v.toArray() : v);
|
|
695
|
+
const result = taCore.pivothigh(source.toArray(), arr(leftbars), arr(rightbars));
|
|
694
696
|
return Series.fromArray(bars, result);
|
|
695
697
|
}
|
|
696
698
|
|
|
@@ -702,9 +704,10 @@ export function pivothigh(source: Series, leftbars: number, rightbars: number):
|
|
|
702
704
|
* @returns Series with the pivot low value on the bar where the pivot is confirmed,
|
|
703
705
|
* `rightbars` bars after the pivot bar (as in PineScript), NaN elsewhere
|
|
704
706
|
*/
|
|
705
|
-
export function pivotlow(source: Series, leftbars: number, rightbars: number): Series {
|
|
707
|
+
export function pivotlow(source: Series, leftbars: number | Series, rightbars: number | Series): Series {
|
|
706
708
|
const bars = source.bars as Bar[];
|
|
707
|
-
const
|
|
709
|
+
const arr = (v: number | Series) => (v instanceof Series ? v.toArray() : v);
|
|
710
|
+
const result = taCore.pivotlow(source.toArray(), arr(leftbars), arr(rightbars));
|
|
708
711
|
return Series.fromArray(bars, result);
|
|
709
712
|
}
|
|
710
713
|
|
|
@@ -861,9 +864,9 @@ export function kcw(bars: Bar[], source: Series, length: number = 20, mult: numb
|
|
|
861
864
|
* @param length - Period length
|
|
862
865
|
* @returns Series with bar offsets to highest value
|
|
863
866
|
*/
|
|
864
|
-
export function highestbars(source: Series, length: number): Series {
|
|
867
|
+
export function highestbars(source: Series, length: number | Series): Series {
|
|
865
868
|
const bars = source.bars as Bar[];
|
|
866
|
-
const result = taCore.highestbars(source.toArray(), length);
|
|
869
|
+
const result = taCore.highestbars(source.toArray(), length instanceof Series ? length.toArray() : length);
|
|
867
870
|
return Series.fromArray(bars, result);
|
|
868
871
|
}
|
|
869
872
|
|
|
@@ -873,9 +876,9 @@ export function highestbars(source: Series, length: number): Series {
|
|
|
873
876
|
* @param length - Period length
|
|
874
877
|
* @returns Series with bar offsets to lowest value
|
|
875
878
|
*/
|
|
876
|
-
export function lowestbars(source: Series, length: number): Series {
|
|
879
|
+
export function lowestbars(source: Series, length: number | Series): Series {
|
|
877
880
|
const bars = source.bars as Bar[];
|
|
878
|
-
const result = taCore.lowestbars(source.toArray(), length);
|
|
881
|
+
const result = taCore.lowestbars(source.toArray(), length instanceof Series ? length.toArray() : length);
|
|
879
882
|
return Series.fromArray(bars, result);
|
|
880
883
|
}
|
|
881
884
|
|
package/src/table/index.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* - `table.cell_set_*` changes one property of a cell (a cell that was not defined is created)
|
|
13
13
|
* - a column or row outside the table is a runtime error
|
|
14
14
|
* - a deleted table is `na`: its functions do nothing
|
|
15
|
+
* - one live table per position: `table.new` at the position of a live table deletes that table (#176)
|
|
15
16
|
*
|
|
16
17
|
* @version 6
|
|
17
18
|
*/
|
|
@@ -25,7 +26,8 @@ type VAlign = 'top' | 'center' | 'bottom';
|
|
|
25
26
|
type FontFamily = 'default' | 'monospace';
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
|
-
* Creates a new table (PineScript `table.new`).
|
|
29
|
+
* Creates a new table (PineScript `table.new`). A live table at the same position is deleted, as PineScript keeps
|
|
30
|
+
* one table per position (#176).
|
|
29
31
|
*
|
|
30
32
|
* @example
|
|
31
33
|
* ```typescript
|
|
@@ -44,6 +46,7 @@ export function new_table(
|
|
|
44
46
|
border_width?: number,
|
|
45
47
|
force_overlay?: boolean
|
|
46
48
|
): Table {
|
|
49
|
+
for (const old of allDrawings('table')) if (old.position === position) remove('table', old);
|
|
47
50
|
return register('table', {
|
|
48
51
|
position,
|
|
49
52
|
columns,
|
package/src/time/index.ts
CHANGED
|
@@ -156,8 +156,11 @@ export function second(time: int, timezone: string): int {
|
|
|
156
156
|
* ":" (1 = Sunday ... 7 = Saturday), e.g. "0930-1600:23456" or "1000-1100,1400-1500".
|
|
157
157
|
* Without days, one period covers every day and several periods cover Monday to Friday.
|
|
158
158
|
* A period includes its start and excludes its end. An overnight period ("1800-0930")
|
|
159
|
-
* belongs to the day it ends. "0000-0000" and "24x7" cover the whole day.
|
|
159
|
+
* belongs to the day it ends. "0000-0000" and "24x7" cover the whole day. A period that ends at 0000 ends at 24:00 of
|
|
160
|
+
* the day it starts. On the days around a clock change the periods are placed as PineScript places them (see
|
|
161
|
+
* time/session.ts); `barMs`, the duration of the bars in ms, is needed for one of these rules (the day before the
|
|
162
|
+
* clocks go back).
|
|
160
163
|
*/
|
|
161
|
-
export function inSession(time: int, session: string, timezone: string): boolean {
|
|
162
|
-
return sessionContains(time, session, timezone);
|
|
164
|
+
export function inSession(time: int, session: string, timezone: string, barMs?: number): boolean {
|
|
165
|
+
return sessionContains(time, session, timezone, barMs);
|
|
163
166
|
}
|
package/src/time/session.ts
CHANGED
|
@@ -12,11 +12,15 @@
|
|
|
12
12
|
* - a trailing ",", ":" or "|" ends its list ("1100-1400," is "1100-1400" Monday to Friday)
|
|
13
13
|
* - an end before the start is an overnight run that belongs to the day it ends; equal ends are 24 hours;
|
|
14
14
|
* "0000-0000" and "24x7" are the whole day, every day
|
|
15
|
+
* - a run that ends at 0000 ("2000-0000") ends at 24:00 of the day it starts and belongs to that day
|
|
16
|
+
* ("2000-0000:6" is Friday 20:00 to midnight)
|
|
17
|
+
* - "F" after the start moves it one day before the day the run ends on ("1700F-1900:2": Sunday 17:00 to Monday
|
|
18
|
+
* 19:00; "1900-1900:3456|1700F-1900:2" is the TVC:DXY symbol session) (#173)
|
|
15
19
|
* - a string that is empty or does not start with a digit ("regular", "extended") is the symbol's own session
|
|
16
20
|
* - a malformed string ("1100 -1400", "0930-16", ":8") is an error, as the PineScript runtime error
|
|
17
21
|
*/
|
|
18
22
|
|
|
19
|
-
import { zonedFields } from './timezone.js';
|
|
23
|
+
import { zoneOffset, zonedFields, zonedToUnix } from './timezone.js';
|
|
20
24
|
|
|
21
25
|
const MINUTES_PER_DAY = 1440;
|
|
22
26
|
const WEEKDAYS = [2, 3, 4, 5, 6];
|
|
@@ -27,17 +31,27 @@ export interface SessionRange {
|
|
|
27
31
|
start: number;
|
|
28
32
|
end: number;
|
|
29
33
|
days: Set<number>;
|
|
34
|
+
/** Days between the start and the day the run ends on, given by "F" after the start (absent otherwise) */
|
|
35
|
+
startDays?: number;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
function invalid(session: string): SyntaxError {
|
|
33
39
|
return new SyntaxError(`Invalid session "${session}": expected "HHMM-HHMM[,HHMM-HHMM...][:days][|...]"`);
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
function parseRanges(text: string, session: string): Array<[number, number]> {
|
|
42
|
+
function parseRanges(text: string, session: string): Array<[number, number, number?]> {
|
|
37
43
|
if (!text) return [];
|
|
38
44
|
const entries = text.split(',');
|
|
39
45
|
if (entries.length > 1 && entries[entries.length - 1] === '') entries.pop();
|
|
40
|
-
return entries.map((entry) => {
|
|
46
|
+
return entries.map((entry): [number, number, number?] => {
|
|
47
|
+
// "HHMMF-HHMM": the start is one day before the day the run ends on
|
|
48
|
+
const f = /^(\d{4})F-(\d{4})$/.exec(entry);
|
|
49
|
+
if (f) {
|
|
50
|
+
const minutes = (hhmm: string): number => Number(hhmm.slice(0, 2)) * 60 + Number(hhmm.slice(2, 4));
|
|
51
|
+
const [start, end] = [minutes(f[1]!), minutes(f[2]!)];
|
|
52
|
+
if (start >= MINUTES_PER_DAY || end >= MINUTES_PER_DAY) throw invalid(session);
|
|
53
|
+
return [start, end, 1];
|
|
54
|
+
}
|
|
41
55
|
if (!/^\d{4}-\d{4}$/.test(entry)) throw invalid(session);
|
|
42
56
|
let start = Number(entry.slice(0, 2)) * 60 + Number(entry.slice(2, 4));
|
|
43
57
|
let end = Number(entry.slice(5, 7)) * 60 + Number(entry.slice(7, 9));
|
|
@@ -58,6 +72,9 @@ function parseDays(text: string, session: string): Set<number> {
|
|
|
58
72
|
|
|
59
73
|
const parsed = new Map<string, SessionRange[] | null>();
|
|
60
74
|
|
|
75
|
+
const range = (start: number, end: number, days: Set<number>, startDays?: number): SessionRange =>
|
|
76
|
+
startDays === undefined ? { start, end, days } : { start, end, days, startDays };
|
|
77
|
+
|
|
61
78
|
/**
|
|
62
79
|
* Runs of a PineScript session string, or null for the symbol's own session (empty or not starting with a digit).
|
|
63
80
|
* @throws SyntaxError when the string is malformed (a PineScript runtime error)
|
|
@@ -72,7 +89,7 @@ export function parsePineSession(session: string): SessionRange[] | null {
|
|
|
72
89
|
} else if (spec === '24x7') {
|
|
73
90
|
out = [{ start: 0, end: 0, days: new Set(ALL_DAYS) }];
|
|
74
91
|
} else if (!/[:,|]/.test(spec)) {
|
|
75
|
-
out = parseRanges(spec, session).map(([start, end]) => (
|
|
92
|
+
out = parseRanges(spec, session).map(([start, end, startDays]) => range(start, end, new Set(ALL_DAYS), startDays));
|
|
76
93
|
} else {
|
|
77
94
|
const sections = spec.split('|');
|
|
78
95
|
if (sections.length > 1 && sections[sections.length - 1] === '') sections.pop();
|
|
@@ -97,33 +114,138 @@ export function parsePineSession(session: string): SessionRange[] | null {
|
|
|
97
114
|
items.forEach((it, i) => {
|
|
98
115
|
if (it.days === null) for (const d of WEEKDAYS) if (!claimed.has(d)) own[i]!.add(d);
|
|
99
116
|
});
|
|
100
|
-
out = items.flatMap((it, i) => (own[i]!.size ? it.ranges.map(([start, end]) => (
|
|
117
|
+
out = items.flatMap((it, i) => (own[i]!.size ? it.ranges.map(([start, end, startDays]) => range(start, end, own[i]!, startDays)) : []));
|
|
101
118
|
}
|
|
102
119
|
parsed.set(session, out);
|
|
103
120
|
return out;
|
|
104
121
|
}
|
|
105
122
|
|
|
123
|
+
const MS_PER_MINUTE = 60_000;
|
|
124
|
+
const MS_PER_HOUR = 3_600_000;
|
|
125
|
+
const MS_PER_DAY = 86_400_000;
|
|
126
|
+
/** A clock change is looked for this far on each side of the tested time. */
|
|
127
|
+
const CHANGE_WINDOW = 36 * MS_PER_HOUR;
|
|
128
|
+
|
|
129
|
+
/** The clock change of `timezone` within 36 hours of `time`: its UNIX time and the offsets (ms) around it. */
|
|
130
|
+
function clockChangeNear(time: number, timezone: string): { at: number; before: number; after: number } | null {
|
|
131
|
+
let lo = time - CHANGE_WINDOW;
|
|
132
|
+
let hi = time + CHANGE_WINDOW;
|
|
133
|
+
const before = zoneOffset(lo, timezone);
|
|
134
|
+
const after = zoneOffset(hi, timezone);
|
|
135
|
+
if (before === after) return null;
|
|
136
|
+
while (hi - lo > MS_PER_MINUTE) {
|
|
137
|
+
const mid = Math.floor((lo + hi) / 2 / MS_PER_MINUTE) * MS_PER_MINUTE;
|
|
138
|
+
if (zoneOffset(mid, timezone) === before) lo = mid;
|
|
139
|
+
else hi = mid;
|
|
140
|
+
}
|
|
141
|
+
return { at: hi, before, after };
|
|
142
|
+
}
|
|
143
|
+
|
|
106
144
|
/**
|
|
107
145
|
* True when `time` falls inside `session`, with the session hours read in `timezone`.
|
|
108
146
|
* A run includes its start minute and excludes its end minute.
|
|
147
|
+
*
|
|
148
|
+
* On the days around a clock change PineScript does not read the wall clock of each bar; it places each run of
|
|
149
|
+
* each local day (measured on 15- and 5-minute bars, clocks going forward and back, four time zones):
|
|
150
|
+
* - a wall time of the day is placed with the offset before the change when it is before the wall time of the
|
|
151
|
+
* change (02:00 for 02:00 -> 03:00, 03:00 for 03:00 -> 02:00), with the offset after it otherwise. Forward
|
|
152
|
+
* change: "0100-0300" lasts one hour and "0130-0230" is empty; back change: "0230-0330" lasts two hours
|
|
153
|
+
* - a run that ends exactly on the wall time of the change is placed with the offset after the change at both ends
|
|
154
|
+
* (forward change: "0100-0200" is one hour earlier than the wall clock says)
|
|
155
|
+
* - clocks going back: a run that ends inside the repeated hour also covers its second pass
|
|
156
|
+
* - clocks going back, the day BEFORE the change: a run with a start or an end on the wall time of the change is
|
|
157
|
+
* also true one hour (the amount of the change) later, from one bar after its usual end. This needs the bar
|
|
158
|
+
* duration `barMs`; without it the rule is not applied
|
|
159
|
+
*
|
|
160
|
+
* @param barMs - Duration of the bars in ms (the chart timeframe); only used by the last rule
|
|
109
161
|
* @throws Error for the symbol's own session ("regular", "extended", ""), which this function does not know: use
|
|
110
162
|
* `time(timeframe, session)` in a script with the chart session
|
|
111
163
|
*/
|
|
112
|
-
export function inSession(time: number, session: string, timezone: string): boolean {
|
|
113
|
-
|
|
164
|
+
export function inSession(time: number, session: string, timezone: string, barMs?: number): boolean {
|
|
165
|
+
return sessionRun(time, session, timezone, barMs) !== null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The run of `session` that contains `time`, as [start, end) UNIX times (ms), or null outside the session; rules in
|
|
170
|
+
* {@link inSession}. PineScript aligns the intraday periods of `time(tf, session)` on the start of this run (#173).
|
|
171
|
+
*/
|
|
172
|
+
export function sessionRun(time: number, session: string, timezone: string, barMs?: number): [number, number] | null {
|
|
173
|
+
if (Number.isNaN(time)) return null;
|
|
114
174
|
const ranges = parsePineSession(session);
|
|
115
175
|
if (ranges === null) throw new Error(`Session "${session}" is the symbol session: it needs the chart session (time(timeframe, session) in a script)`);
|
|
176
|
+
const change = clockChangeNear(time, timezone);
|
|
116
177
|
const f = zonedFields(time, timezone);
|
|
117
178
|
const minute = f.hour * 60 + f.minute;
|
|
118
179
|
const nextDay = (f.dayOfWeek % 7) + 1;
|
|
119
|
-
for (const { start, end, days } of ranges) {
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
180
|
+
for (const { start, end: rawEnd, days, startDays } of ranges) {
|
|
181
|
+
if (startDays !== undefined) {
|
|
182
|
+
// "F": from `start` of `startDays` days before the end day to `end` of the end day, wall times read in the zone
|
|
183
|
+
for (let k = 0; k <= startDays + 1; k++) {
|
|
184
|
+
const endDay = Date.UTC(f.year, f.month - 1, f.day + k);
|
|
185
|
+
if (!days.has(new Date(endDay).getUTCDay() + 1)) continue;
|
|
186
|
+
const d = new Date(endDay);
|
|
187
|
+
const at = (dayShift: number, minutes: number): number =>
|
|
188
|
+
zonedToUnix(timezone, d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate() - dayShift, Math.floor(minutes / 60), minutes % 60);
|
|
189
|
+
const from = at(startDays, start);
|
|
190
|
+
const to = at(0, rawEnd);
|
|
191
|
+
if (time >= from && time < to) return [from, to];
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (start === rawEnd) {
|
|
196
|
+
// 24 hours: the run belongs to the day it ends ("0000-0000": the day itself)
|
|
197
|
+
if (days.has(start === 0 || minute < start ? f.dayOfWeek : nextDay)) {
|
|
198
|
+
const from = time - ((minute - start + MINUTES_PER_DAY) % MINUTES_PER_DAY) * MS_PER_MINUTE - f.second * 1000 - (time % 1000);
|
|
199
|
+
return [from, from + MS_PER_DAY];
|
|
200
|
+
}
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
// a run that ends at 0000 ends at 24:00 of the day it starts
|
|
204
|
+
const end = rawEnd === 0 ? MINUTES_PER_DAY : rawEnd;
|
|
205
|
+
const overnight = start > end;
|
|
206
|
+
if (!change) {
|
|
207
|
+
// the wall-clock minute of the bar: the run is `start` to `end` minutes of the same offset
|
|
208
|
+
const midnight = time - minute * MS_PER_MINUTE - f.second * 1000 - (time % 1000);
|
|
209
|
+
if (!overnight) {
|
|
210
|
+
if (minute >= start && minute < end && days.has(f.dayOfWeek)) return [midnight + start * MS_PER_MINUTE, midnight + end * MS_PER_MINUTE];
|
|
211
|
+
} else {
|
|
212
|
+
// the part from `start` belongs to the next day
|
|
213
|
+
if (minute >= start && days.has(nextDay)) return [midnight + start * MS_PER_MINUTE, midnight + (end + MINUTES_PER_DAY) * MS_PER_MINUTE];
|
|
214
|
+
if (minute < end && days.has(f.dayOfWeek)) return [midnight + (start - MINUTES_PER_DAY) * MS_PER_MINUTE, midnight + end * MS_PER_MINUTE];
|
|
215
|
+
}
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
// local days as naive instants (wall clock read as UTC)
|
|
219
|
+
const changeWall = change.at + change.before;
|
|
220
|
+
const place = (naive: number): number => (naive < changeWall ? naive - change.before : naive - change.after);
|
|
221
|
+
const today = Math.floor((time + change.before) / MS_PER_DAY);
|
|
222
|
+
for (let day = today - 1; day <= today + 1; day++) {
|
|
223
|
+
if (!days.has(((day + 4) % 7) + 1)) continue; // 01/01/1970 is a Thursday (5)
|
|
224
|
+
const from = day * MS_PER_DAY + (overnight ? start - MINUTES_PER_DAY : start) * MS_PER_MINUTE;
|
|
225
|
+
const to = day * MS_PER_DAY + end * MS_PER_MINUTE;
|
|
226
|
+
if (to === changeWall) {
|
|
227
|
+
if (time >= from - change.after && time < to - change.after) return [from - change.after, to - change.after];
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (time >= place(from) && time < place(to)) return [place(from), place(to)];
|
|
231
|
+
// clocks going back: the second pass of the repeated hour
|
|
232
|
+
const repeated = change.at + change.after;
|
|
233
|
+
if (change.after < change.before && to < changeWall && to > repeated) {
|
|
234
|
+
if (time >= Math.max(from, repeated) - change.after && time < to - change.after) return [place(from), to - change.after];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// clocks going back, the day before: a run with an end on the wall time of the change is also true one change
|
|
238
|
+
// later, from one bar after its usual end
|
|
239
|
+
if (barMs !== undefined && change.after < change.before) {
|
|
240
|
+
const day = Math.floor(changeWall / MS_PER_DAY) - 1;
|
|
241
|
+
const wallMinute = (changeWall - (day + 1) * MS_PER_DAY) / MS_PER_MINUTE;
|
|
242
|
+
if ((start === wallMinute || end === wallMinute) && days.has(((day + 4) % 7) + 1)) {
|
|
243
|
+
const from = day * MS_PER_DAY + (overnight ? start - MINUTES_PER_DAY : start) * MS_PER_MINUTE - change.before;
|
|
244
|
+
const to = day * MS_PER_DAY + end * MS_PER_MINUTE - change.before;
|
|
245
|
+
const shift = change.before - change.after;
|
|
246
|
+
if (time >= from + shift && time < to + shift && time >= to + barMs) return [from + shift, to + shift];
|
|
247
|
+
}
|
|
126
248
|
}
|
|
127
249
|
}
|
|
128
|
-
return
|
|
250
|
+
return null;
|
|
129
251
|
}
|
package/src/time/timezone.ts
CHANGED
|
@@ -177,6 +177,11 @@ export function civilToUnix(year: number, month: number, day: number, hour = 0,
|
|
|
177
177
|
return days * MS_PER_DAY + Date.UTC(1970, 0, 1, hour, minute, second);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* UNIX time (ms) of a wall-clock date in `timezone`. A wall time skipped by a clock change is read with the offset
|
|
182
|
+
* before the change (02:30 on a 02:00 -> 03:00 night is 03:30 after it); a wall time that occurs twice gives the
|
|
183
|
+
* later instant, or the earlier one with `earlier`.
|
|
184
|
+
*/
|
|
180
185
|
export function zonedToUnix(
|
|
181
186
|
timezone: string,
|
|
182
187
|
year: number,
|
|
@@ -184,7 +189,8 @@ export function zonedToUnix(
|
|
|
184
189
|
day: number,
|
|
185
190
|
hour: number = 0,
|
|
186
191
|
minute: number = 0,
|
|
187
|
-
second: number = 0
|
|
192
|
+
second: number = 0,
|
|
193
|
+
earlier: boolean = false
|
|
188
194
|
): number {
|
|
189
195
|
const zone = resolveZone(timezone);
|
|
190
196
|
const local = civilToUnix(year, month, day, hour, minute, second);
|
|
@@ -195,6 +201,7 @@ export function zonedToUnix(
|
|
|
195
201
|
const before = offsetAt(zone, local - MS_PER_DAY);
|
|
196
202
|
const after = offsetAt(zone, local + MS_PER_DAY);
|
|
197
203
|
const candidates = [local - before, local - after].filter((t) => local - offsetAt(zone, t) === t);
|
|
198
|
-
|
|
204
|
+
// a wall time that occurs twice: the later instant, or the earlier one with `earlier`
|
|
205
|
+
if (candidates.length) return earlier ? Math.min(...candidates) : Math.max(...candidates);
|
|
199
206
|
return local - before;
|
|
200
207
|
}
|
package/src/types/metadata.ts
CHANGED
|
@@ -86,7 +86,7 @@ export interface FillOptions {
|
|
|
86
86
|
/**
|
|
87
87
|
* Input parameter type
|
|
88
88
|
*/
|
|
89
|
-
export type InputType = 'int' | 'float' | 'bool' | 'string' | 'source' | 'color' | 'timeframe' | 'session' | 'time';
|
|
89
|
+
export type InputType = 'int' | 'float' | 'bool' | 'string' | 'source' | 'color' | 'timeframe' | 'session' | 'time' | 'symbol';
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* Input parameter metadata
|
|
@@ -431,17 +431,20 @@ export interface LabelData {
|
|
|
431
431
|
/** Price; not used with yloc 'abovebar' / 'belowbar' */
|
|
432
432
|
price: number;
|
|
433
433
|
text: string;
|
|
434
|
-
/** Background, border and arrow colour */
|
|
434
|
+
/** Background, border and arrow colour. Omitted: #2962FF (color.blue; #2196F3 in a v4 script) */
|
|
435
435
|
color?: string;
|
|
436
|
+
/** Omitted: #FFFFFF in a v6 script, #363A45 in v4 and v5 (the result does not carry the version) */
|
|
436
437
|
textColor?: string;
|
|
437
|
-
/** 'none': text only, no background (PineScript label.style_none) */
|
|
438
|
+
/** 'none': text only, no background (PineScript label.style_none). Omitted: 'label_down' */
|
|
438
439
|
style?: LabelStyle;
|
|
440
|
+
/** Omitted: 'normal' */
|
|
439
441
|
size?: PineSize | number;
|
|
440
442
|
/**
|
|
441
443
|
* PineScript yloc: 'price' (default) places the label at `price`; 'abovebar' / 'belowbar' place it above the bar
|
|
442
444
|
* high / below the bar low and ignore `price` (the renderer reads the bar at `time`).
|
|
443
445
|
*/
|
|
444
446
|
yloc?: 'price' | 'abovebar' | 'belowbar';
|
|
447
|
+
/** Omitted: 'center' */
|
|
445
448
|
textAlign?: 'left' | 'center' | 'right';
|
|
446
449
|
tooltip?: string;
|
|
447
450
|
fontFamily?: 'default' | 'monospace';
|
|
@@ -459,8 +462,11 @@ export interface LineDrawingData {
|
|
|
459
462
|
price1: number;
|
|
460
463
|
time2: number;
|
|
461
464
|
price2: number;
|
|
465
|
+
/** Omitted: #2962FF (color.blue; #2196F3 in a v4 script) */
|
|
462
466
|
color?: string;
|
|
467
|
+
/** Omitted: 1. A width of 0 is kept (PineScript `width = 0`) */
|
|
463
468
|
width?: number;
|
|
469
|
+
/** Omitted: 'solid' */
|
|
464
470
|
style?: 'solid' | 'dashed' | 'dotted' | 'arrow_left' | 'arrow_right' | 'arrow_both';
|
|
465
471
|
extend?: 'none' | 'left' | 'right' | 'both';
|
|
466
472
|
/** PineScript force_overlay = true: the line of a non-overlay indicator is drawn on the price pane */
|
|
@@ -475,14 +481,22 @@ export interface BoxData {
|
|
|
475
481
|
price1: number;
|
|
476
482
|
time2: number;
|
|
477
483
|
price2: number;
|
|
484
|
+
/** Omitted: #2962FF (color.blue; #2196F3 in a v4 script) */
|
|
478
485
|
bgColor?: string;
|
|
486
|
+
/** Omitted: #2962FF (color.blue; #2196F3 in a v4 script) */
|
|
479
487
|
borderColor?: string;
|
|
488
|
+
/** Omitted: 1. A width of 0 is no border (PineScript `border_width = 0`) */
|
|
480
489
|
borderWidth?: number;
|
|
490
|
+
/** Omitted: 'solid' */
|
|
481
491
|
borderStyle?: 'solid' | 'dashed' | 'dotted';
|
|
482
492
|
text?: string;
|
|
493
|
+
/** Omitted: #FFFFFF in a v6 script, #363A45 in v5 (the result does not carry the version) */
|
|
483
494
|
textColor?: string;
|
|
495
|
+
/** Omitted: 'auto' */
|
|
484
496
|
textSize?: PineSize | number;
|
|
497
|
+
/** Omitted: 'center' */
|
|
485
498
|
textHAlign?: 'left' | 'center' | 'right';
|
|
499
|
+
/** Omitted: 'center' */
|
|
486
500
|
textVAlign?: 'top' | 'center' | 'bottom';
|
|
487
501
|
textWrap?: 'none' | 'auto';
|
|
488
502
|
fontFamily?: 'default' | 'monospace';
|
|
@@ -512,10 +526,12 @@ export interface PolylineData {
|
|
|
512
526
|
curved?: boolean;
|
|
513
527
|
/** The last point is connected to the first */
|
|
514
528
|
closed?: boolean;
|
|
529
|
+
/** Omitted: #2962FF (color.blue) */
|
|
515
530
|
lineColor?: string;
|
|
516
|
-
/** Fill of the area inside the polyline */
|
|
531
|
+
/** Fill of the area inside the polyline. Omitted: no fill */
|
|
517
532
|
fillColor?: string;
|
|
518
533
|
lineStyle?: 'solid' | 'dotted' | 'dashed' | 'arrow_left' | 'arrow_right' | 'arrow_both';
|
|
534
|
+
/** Omitted: 1 */
|
|
519
535
|
lineWidth?: number;
|
|
520
536
|
/** PineScript force_overlay = true: the polyline of a non-overlay indicator is drawn on the price pane */
|
|
521
537
|
forceOverlay?: boolean;
|
|
@@ -540,8 +556,11 @@ export interface TableCellData {
|
|
|
540
556
|
row: number;
|
|
541
557
|
column: number;
|
|
542
558
|
text: string;
|
|
559
|
+
/** Omitted: no background */
|
|
543
560
|
bgColor?: string;
|
|
561
|
+
/** Omitted: #363A45 (in v4, v5 and v6 scripts) */
|
|
544
562
|
textColor?: string;
|
|
563
|
+
/** Omitted: 'auto' */
|
|
545
564
|
textSize?: PineSize | number;
|
|
546
565
|
/** Width in % of the chart width; 0 fits the text */
|
|
547
566
|
width?: number;
|
|
@@ -575,10 +594,15 @@ export interface TableData {
|
|
|
575
594
|
rows: number;
|
|
576
595
|
cells: TableCellData[];
|
|
577
596
|
merges?: TableMergeData[];
|
|
597
|
+
/** Omitted: no background */
|
|
578
598
|
bgColor?: string;
|
|
599
|
+
/** Omitted: no frame */
|
|
579
600
|
frameColor?: string;
|
|
601
|
+
/** Omitted: 0 */
|
|
580
602
|
frameWidth?: number;
|
|
603
|
+
/** Omitted: no cell borders */
|
|
581
604
|
borderColor?: string;
|
|
605
|
+
/** Omitted: 0 */
|
|
582
606
|
borderWidth?: number;
|
|
583
607
|
/** PineScript force_overlay = true: the table of a non-overlay indicator is drawn on the price pane */
|
|
584
608
|
forceOverlay?: boolean;
|