oakscriptjs 0.13.2 → 0.14.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/AGENTS.md +6 -1
- package/README.md +1 -1
- package/bin/oakscript.mjs +5 -0
- package/dist/cjs/check/checks.d.ts +78 -0
- package/dist/cjs/check/checks.d.ts.map +1 -0
- package/dist/cjs/check/checks.js +109 -0
- package/dist/cjs/check/checks.js.map +7 -0
- package/dist/cjs/check/format.d.ts +26 -0
- package/dist/cjs/check/format.d.ts.map +1 -0
- package/dist/cjs/check/format.js +106 -0
- package/dist/cjs/check/format.js.map +7 -0
- package/dist/cjs/check/harness.d.ts +94 -0
- package/dist/cjs/check/harness.d.ts.map +1 -0
- package/dist/cjs/check/harness.js +302 -0
- package/dist/cjs/check/harness.js.map +7 -0
- package/dist/cjs/check/index.d.ts +253 -0
- package/dist/cjs/check/index.d.ts.map +1 -0
- package/dist/cjs/check/index.js +466 -0
- package/dist/cjs/check/index.js.map +7 -0
- package/dist/cjs/check/node.d.ts +53 -0
- package/dist/cjs/check/node.d.ts.map +1 -0
- package/dist/cjs/check/node.js +318 -0
- package/dist/cjs/check/node.js.map +7 -0
- package/dist/cjs/check/spec.d.ts +44 -0
- package/dist/cjs/check/spec.d.ts.map +1 -0
- package/dist/cjs/check/spec.js +199 -0
- package/dist/cjs/check/spec.js.map +7 -0
- 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/runtime/series.d.ts +6 -0
- package/dist/cjs/runtime/series.d.ts.map +1 -1
- package/dist/cjs/runtime/series.js +15 -0
- package/dist/cjs/runtime/series.js.map +2 -2
- package/dist/cjs/script/constants.d.ts +200 -0
- package/dist/cjs/script/constants.d.ts.map +1 -0
- package/dist/cjs/script/constants.js +217 -0
- package/dist/cjs/script/constants.js.map +7 -0
- package/dist/cjs/script/drawing-args.d.ts +25 -0
- package/dist/cjs/script/drawing-args.d.ts.map +1 -0
- package/dist/cjs/script/drawing-args.js +203 -0
- package/dist/cjs/script/drawing-args.js.map +7 -0
- package/dist/cjs/script/globals.d.ts +127 -0
- package/dist/cjs/script/globals.d.ts.map +1 -0
- package/dist/cjs/script/globals.js +17 -0
- package/dist/cjs/script/globals.js.map +7 -0
- package/dist/cjs/script/index.d.ts +504 -166
- package/dist/cjs/script/index.d.ts.map +1 -1
- package/dist/cjs/script/index.js +236 -96
- package/dist/cjs/script/index.js.map +3 -3
- package/dist/esm/check/checks.d.ts +78 -0
- package/dist/esm/check/checks.d.ts.map +1 -0
- package/dist/esm/check/checks.js +95 -0
- package/dist/esm/check/checks.js.map +1 -0
- package/dist/esm/check/format.d.ts +26 -0
- package/dist/esm/check/format.d.ts.map +1 -0
- package/dist/esm/check/format.js +105 -0
- package/dist/esm/check/format.js.map +1 -0
- package/dist/esm/check/harness.d.ts +94 -0
- package/dist/esm/check/harness.d.ts.map +1 -0
- package/dist/esm/check/harness.js +317 -0
- package/dist/esm/check/harness.js.map +1 -0
- package/dist/esm/check/index.d.ts +253 -0
- package/dist/esm/check/index.d.ts.map +1 -0
- package/dist/esm/check/index.js +470 -0
- package/dist/esm/check/index.js.map +1 -0
- package/dist/esm/check/node.d.ts +53 -0
- package/dist/esm/check/node.d.ts.map +1 -0
- package/dist/esm/check/node.js +347 -0
- package/dist/esm/check/node.js.map +1 -0
- package/dist/esm/check/spec.d.ts +44 -0
- package/dist/esm/check/spec.d.ts.map +1 -0
- package/dist/esm/check/spec.js +198 -0
- package/dist/esm/check/spec.js.map +1 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/runtime/series.d.ts +6 -0
- package/dist/esm/runtime/series.d.ts.map +1 -1
- package/dist/esm/runtime/series.js +14 -0
- package/dist/esm/runtime/series.js.map +1 -1
- package/dist/esm/script/constants.d.ts +200 -0
- package/dist/esm/script/constants.d.ts.map +1 -0
- package/dist/esm/script/constants.js +151 -0
- package/dist/esm/script/constants.js.map +1 -0
- package/dist/esm/script/drawing-args.d.ts +25 -0
- package/dist/esm/script/drawing-args.d.ts.map +1 -0
- package/dist/esm/script/drawing-args.js +171 -0
- package/dist/esm/script/drawing-args.js.map +1 -0
- package/dist/esm/script/globals.d.ts +127 -0
- package/dist/esm/script/globals.d.ts.map +1 -0
- package/dist/esm/script/globals.js +28 -0
- package/dist/esm/script/globals.js.map +1 -0
- package/dist/esm/script/index.d.ts +504 -166
- package/dist/esm/script/index.d.ts.map +1 -1
- package/dist/esm/script/index.js +256 -83
- package/dist/esm/script/index.js.map +1 -1
- package/package.json +31 -1
- package/skills/oakscript/SKILL.md +62 -26
- package/skills/oakscript/examples/ma-cross.js +2 -2
- package/skills/oakscript/examples/mtf-trend-filter.js +3 -3
- package/skills/oakscript/reference/check.md +150 -0
- package/skills/oakscript/reference/errors.md +31 -7
- package/skills/oakscript/reference/pine-differences.md +17 -16
- package/skills/oakscript/reference/plotting.md +25 -19
- package/skills/oakscript/reference/script-structure.md +34 -17
- package/skills/oakscript/reference/timeframes-symbols.md +5 -1
- package/skills/oakscript/spec.schema.json +199 -0
- package/src/check/checks.ts +175 -0
- package/src/check/format.ts +117 -0
- package/src/check/harness.ts +372 -0
- package/src/check/index.ts +708 -0
- package/src/check/node.ts +368 -0
- package/src/check/spec.ts +200 -0
- package/src/index.ts +1 -1
- package/src/runtime/series.ts +19 -0
- package/src/script/constants.ts +169 -0
- package/src/script/drawing-args.ts +179 -0
- package/src/script/globals.ts +129 -0
- package/src/script/index.ts +462 -81
package/src/script/index.ts
CHANGED
|
@@ -75,6 +75,8 @@ import type {
|
|
|
75
75
|
} from '../runtime/types.js';
|
|
76
76
|
import { BarData, Series, MAX_BARS_BACK, historyError } from '../runtime/series.js';
|
|
77
77
|
import * as taSeries from '../ta-series.js';
|
|
78
|
+
import * as pineConstants from './constants.js';
|
|
79
|
+
import { drawingNamespace, isPlainObject, namedArguments, unknownArgument } from './drawing-args.js';
|
|
78
80
|
import * as colorCore from '../color/index.js';
|
|
79
81
|
import { inputColorDefault } from '../color/parse.js';
|
|
80
82
|
import { fixnan as fixnanValues, isNA, nz } from '../utils/index.js';
|
|
@@ -109,7 +111,7 @@ import { SessionBars, sessionFlags, strayPeriods, type SessionFlags } from '../s
|
|
|
109
111
|
import { afterCompletion, heikinAshi, mapToChart, periodsOf, resample } from '../security/resample.js';
|
|
110
112
|
import { securityLowerTf, securityOther, type OtherSymbolChart } from '../security/other.js';
|
|
111
113
|
import { position } from '../types/index.js';
|
|
112
|
-
import type { ChartPoint } from '../types/index.js';
|
|
114
|
+
import type { Box, ChartPoint, Label, Line, Linefill, Polyline, Table } from '../types/index.js';
|
|
113
115
|
import {
|
|
114
116
|
STRATEGY_CONSTANTS,
|
|
115
117
|
STRATEGY_DEFAULTS,
|
|
@@ -135,7 +137,7 @@ import { sessionPeriods, type SessionPeriods } from '../session/context.js';
|
|
|
135
137
|
|
|
136
138
|
/** Everything one run of a script produced. */
|
|
137
139
|
export interface ScriptRunResult {
|
|
138
|
-
metadata: { title: string; shortTitle?: string; overlay: boolean; precision?: number; format?: string };
|
|
140
|
+
metadata: { title: string; shortTitle?: string; overlay: boolean; precision?: number; format?: string; scale?: 'right' | 'left' | 'none' };
|
|
139
141
|
inputConfig: InputConfig[];
|
|
140
142
|
plotConfig: PlotConfig[];
|
|
141
143
|
hlineConfig: HLineConfig[];
|
|
@@ -218,6 +220,8 @@ interface Collector {
|
|
|
218
220
|
overlay: boolean;
|
|
219
221
|
precision?: number;
|
|
220
222
|
format?: string;
|
|
223
|
+
/** indicator(..., { scale }): the price scale the script uses. */
|
|
224
|
+
scale?: 'right' | 'left' | 'none';
|
|
221
225
|
inputValues: Record<string, unknown>;
|
|
222
226
|
inputConfig: InputConfig[];
|
|
223
227
|
inputIds: Set<string>;
|
|
@@ -275,7 +279,7 @@ function collector(): Collector {
|
|
|
275
279
|
function freshCollector(inputValues: Record<string, unknown>): Collector {
|
|
276
280
|
return {
|
|
277
281
|
title: 'Untitled',
|
|
278
|
-
overlay:
|
|
282
|
+
overlay: false,
|
|
279
283
|
inputValues,
|
|
280
284
|
inputConfig: [],
|
|
281
285
|
inputIds: new Set(),
|
|
@@ -452,6 +456,7 @@ export function executeScript(
|
|
|
452
456
|
overlay: c.overlay,
|
|
453
457
|
precision: c.precision,
|
|
454
458
|
format: c.format,
|
|
459
|
+
...(c.scale ? { scale: c.scale } : {}),
|
|
455
460
|
},
|
|
456
461
|
inputConfig: c.inputConfig,
|
|
457
462
|
plotConfig: c.plotConfig,
|
|
@@ -595,9 +600,12 @@ export function varip<T>(initial: T): VaripVariable<T> {
|
|
|
595
600
|
|
|
596
601
|
export interface IndicatorOptions {
|
|
597
602
|
shorttitle?: string;
|
|
603
|
+
/** true: drawn on the price pane; false (default, as PineScript): in its own pane. */
|
|
598
604
|
overlay?: boolean;
|
|
599
605
|
precision?: number;
|
|
600
606
|
format?: string;
|
|
607
|
+
/** The price scale: `scale.right`, `scale.left` or `scale.none` (recorded in `metadata.scale` for the host). */
|
|
608
|
+
scale?: 'right' | 'left' | 'none';
|
|
601
609
|
/** Lines kept before the oldest are deleted (default 50; PineScript keeps up to 5 more). */
|
|
602
610
|
max_lines_count?: number;
|
|
603
611
|
/** Labels kept before the oldest are deleted (default 50). */
|
|
@@ -614,9 +622,10 @@ export function indicator(title: string, options: IndicatorOptions = {}): void {
|
|
|
614
622
|
declare(c, 'indicator');
|
|
615
623
|
c.title = title;
|
|
616
624
|
c.shortTitle = options.shorttitle;
|
|
617
|
-
c.overlay = options.overlay ??
|
|
625
|
+
c.overlay = options.overlay ?? false;
|
|
618
626
|
c.precision = options.precision;
|
|
619
627
|
c.format = options.format;
|
|
628
|
+
c.scale = options.scale;
|
|
620
629
|
setDrawingLimits({
|
|
621
630
|
line: options.max_lines_count,
|
|
622
631
|
label: options.max_labels_count,
|
|
@@ -634,10 +643,15 @@ function declare(c: Collector, kind: 'indicator' | 'strategy'): void {
|
|
|
634
643
|
|
|
635
644
|
/** Options of every `input.*` kind (PineScript `group`, `inline`, `tooltip`, `confirm`, `display`, `active`). */
|
|
636
645
|
export interface InputOptions {
|
|
646
|
+
/** Group title: every input with the same `group` is listed under it, at the place of the group's first input. */
|
|
637
647
|
group?: string;
|
|
648
|
+
/** Inputs with the same `inline` id share one row of their group (ungrouped inputs share rows among themselves). */
|
|
638
649
|
inline?: string;
|
|
650
|
+
/** Help text shown next to the input. */
|
|
639
651
|
tooltip?: string;
|
|
652
|
+
/** The host asks for the value when the script is added. */
|
|
640
653
|
confirm?: boolean;
|
|
654
|
+
/** Where the value is shown besides the settings: 'all', 'none', 'status_line', 'data_window'. */
|
|
641
655
|
display?: InputConfig['display'];
|
|
642
656
|
/**
|
|
643
657
|
* PineScript `active`: false greys the input out in the settings (it keeps its value). A boolean as in
|
|
@@ -688,61 +702,132 @@ function registerNumeric(type: 'int' | 'float', defval: number, title?: string,
|
|
|
688
702
|
return typeof v === 'number' ? v : defval;
|
|
689
703
|
}
|
|
690
704
|
|
|
691
|
-
/**
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
705
|
+
/** Named arguments of an input: its options, with `title` (and `defval` when every argument is named). */
|
|
706
|
+
type InputArgs<O, D> = O & { title?: string; defval?: D };
|
|
707
|
+
|
|
708
|
+
const COMMON_INPUT_KEYS = ['tooltip', 'inline', 'group', 'confirm', 'display', 'active'] as const;
|
|
709
|
+
/** The PineScript arguments of each input kind (named arguments are checked against them). */
|
|
710
|
+
const INPUT_KEYS: Record<string, readonly string[]> = {
|
|
711
|
+
int: ['defval', 'title', 'minval', 'maxval', 'step', ...COMMON_INPUT_KEYS],
|
|
712
|
+
float: ['defval', 'title', 'minval', 'maxval', 'step', ...COMMON_INPUT_KEYS],
|
|
713
|
+
price: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
714
|
+
bool: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
715
|
+
string: ['defval', 'title', 'options', ...COMMON_INPUT_KEYS],
|
|
716
|
+
text_area: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
717
|
+
color: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
718
|
+
source: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
719
|
+
timeframe: ['defval', 'title', 'options', ...COMMON_INPUT_KEYS],
|
|
720
|
+
session: ['defval', 'title', 'options', ...COMMON_INPUT_KEYS],
|
|
721
|
+
symbol: ['defval', 'title', 'options', ...COMMON_INPUT_KEYS],
|
|
722
|
+
time: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* The default, title and options of an `input.*` call, as PineScript writes it: `(defval, title, options)`,
|
|
727
|
+
* `(defval, { title, ...options })` or every argument by name (`{ defval, title, ...options }`). An unknown named
|
|
728
|
+
* argument throws with the closest names (`min` for `minval`).
|
|
729
|
+
*/
|
|
730
|
+
function inputArgs<D, O extends object>(
|
|
731
|
+
kind: string,
|
|
732
|
+
defvalOrArgs: unknown,
|
|
733
|
+
titleOrArgs: unknown,
|
|
734
|
+
opts: O | undefined
|
|
735
|
+
): { defval: D; title: string | undefined; opts: O } {
|
|
736
|
+
const name = `input.${kind}`;
|
|
737
|
+
const keys = INPUT_KEYS[kind]!;
|
|
738
|
+
let named: Record<string, unknown> = {};
|
|
739
|
+
let defval = defvalOrArgs;
|
|
740
|
+
let title: unknown = titleOrArgs;
|
|
741
|
+
if (isPlainObject(defvalOrArgs)) {
|
|
742
|
+
named = { ...defvalOrArgs };
|
|
743
|
+
defval = named.defval;
|
|
744
|
+
title = undefined;
|
|
745
|
+
} else if (isPlainObject(titleOrArgs)) {
|
|
746
|
+
named = { ...titleOrArgs };
|
|
747
|
+
title = undefined;
|
|
748
|
+
}
|
|
749
|
+
if (opts !== undefined) {
|
|
750
|
+
if (!isPlainObject(opts)) throw new TypeError(`${name}(): the options are an object, e.g. { tooltip: "..." }.`);
|
|
751
|
+
named = { ...named, ...opts };
|
|
752
|
+
}
|
|
753
|
+
for (const key of Object.keys(named)) if (!keys.includes(key)) throw unknownArgument(name, key, keys);
|
|
754
|
+
if (title !== undefined && typeof title !== 'string') {
|
|
755
|
+
throw new TypeError(`${name}(): the title is a string, got ${typeof title}.`);
|
|
756
|
+
}
|
|
757
|
+
const { defval: namedDefval, title: namedTitle, ...rest } = named;
|
|
758
|
+
return {
|
|
759
|
+
defval: (defval ?? namedDefval) as D,
|
|
760
|
+
title: (title as string | undefined) ?? (namedTitle as string | undefined),
|
|
761
|
+
opts: rest as O,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/** PineScript `input.*` — declares an input AND returns its current value. The arguments after the default can be
|
|
766
|
+
* named, as in PineScript: `input.int(14, "Length", { minval: 1 })`, `input.int(14, { title: "Length", minval: 1 })`,
|
|
767
|
+
* `input.int({ defval: 14, title: "Length" })`. */
|
|
768
|
+
export const input = strictNamespace('input', {
|
|
769
|
+
int(defval: number | InputArgs<NumericInputOptions, number>, title?: string | InputArgs<NumericInputOptions, number>, opts?: NumericInputOptions): number {
|
|
770
|
+
const a = inputArgs<number, NumericInputOptions>('int', defval, title, opts);
|
|
771
|
+
return Math.round(registerNumeric('int', a.defval, a.title, a.opts));
|
|
695
772
|
},
|
|
696
|
-
float(defval: number, title?: string, opts
|
|
697
|
-
|
|
773
|
+
float(defval: number | InputArgs<NumericInputOptions, number>, title?: string | InputArgs<NumericInputOptions, number>, opts?: NumericInputOptions): number {
|
|
774
|
+
const a = inputArgs<number, NumericInputOptions>('float', defval, title, opts);
|
|
775
|
+
return registerNumeric('float', a.defval, a.title, a.opts);
|
|
698
776
|
},
|
|
699
|
-
bool(defval: boolean, title?: string, opts
|
|
700
|
-
|
|
777
|
+
bool(defval: boolean | InputArgs<InputOptions, boolean>, title?: string | InputArgs<InputOptions, boolean>, opts?: InputOptions): boolean {
|
|
778
|
+
const a = inputArgs<boolean, InputOptions>('bool', defval, title, opts);
|
|
779
|
+
return registerInput<boolean>({ type: 'bool', defval: a.defval, title: a.title, ...common(a.opts) }, 'input_bool');
|
|
701
780
|
},
|
|
702
|
-
string(defval: string, title?: string, opts
|
|
703
|
-
|
|
781
|
+
string(defval: string | InputArgs<StringInputOptions, string>, title?: string | InputArgs<StringInputOptions, string>, opts?: StringInputOptions): string {
|
|
782
|
+
const a = inputArgs<string, StringInputOptions>('string', defval, title, opts);
|
|
783
|
+
return registerInput<string>({ type: 'string', defval: a.defval, title: a.title, options: a.opts.options, ...common(a.opts) }, 'input_string');
|
|
704
784
|
},
|
|
705
|
-
/** Multi-line text (
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
return registerInput<string>({ type: 'text_area', defval, title, ...common(opts) }, 'input_text_area');
|
|
785
|
+
/** Multi-line text (lines separated by a newline), e.g. a list a script parses (#184). */
|
|
786
|
+
text_area(defval: string | InputArgs<InputOptions, string>, title?: string | InputArgs<InputOptions, string>, opts?: InputOptions): string {
|
|
787
|
+
const a = inputArgs<string, InputOptions>('text_area', defval, title, opts);
|
|
788
|
+
return registerInput<string>({ type: 'text_area', defval: a.defval, title: a.title, ...common(a.opts) }, 'input_text_area');
|
|
709
789
|
},
|
|
710
790
|
/**
|
|
711
791
|
* Declares a colour input. As in PineScript, the default keeps an alpha of 2 decimals: an input default
|
|
712
792
|
* `color.new(c, 90)` has the alpha byte 26 (25 in the script). A value given by the host is not changed.
|
|
713
793
|
*/
|
|
714
|
-
color(defval: string, title?: string, opts
|
|
715
|
-
|
|
794
|
+
color(defval: string | InputArgs<InputOptions, string>, title?: string | InputArgs<InputOptions, string>, opts?: InputOptions): string {
|
|
795
|
+
const a = inputArgs<string, InputOptions>('color', defval, title, opts);
|
|
796
|
+
return registerInput<string>({ type: 'color', defval: inputColorDefault(a.defval), title: a.title, ...common(a.opts) }, 'input_color');
|
|
716
797
|
},
|
|
717
798
|
/** Declares a source input and returns it as a Series. */
|
|
718
|
-
source(defval: string = 'close', title?: string, opts
|
|
719
|
-
const
|
|
799
|
+
source(defval: string | Series = 'close', title?: string | InputArgs<InputOptions, string | Series>, opts?: InputOptions): Series {
|
|
800
|
+
const a = inputArgs<string | Series, InputOptions>('source', defval, title, opts);
|
|
801
|
+
const name = registerInput<string>({ type: 'source', defval: sourceName(a.defval ?? 'close'), title: a.title, ...common(a.opts) }, 'input_source');
|
|
720
802
|
return sourceSeries(name);
|
|
721
803
|
},
|
|
722
804
|
/** Timeframe string ("60", "1D", "W"). As in PineScript, "" means the chart timeframe. */
|
|
723
|
-
timeframe(defval: string, title?: string, opts
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
'input_timeframe'
|
|
727
|
-
);
|
|
805
|
+
timeframe(defval: string | InputArgs<StringInputOptions, string>, title?: string | InputArgs<StringInputOptions, string>, opts?: StringInputOptions): string {
|
|
806
|
+
const a = inputArgs<string, StringInputOptions>('timeframe', defval, title, opts);
|
|
807
|
+
return registerInput<string>({ type: 'timeframe', defval: a.defval, title: a.title, options: a.opts.options, ...common(a.opts) }, 'input_timeframe');
|
|
728
808
|
},
|
|
729
809
|
/** Session string ("0930-1600", "0930-1600:23456"). */
|
|
730
|
-
session(defval: string, title?: string, opts
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
'input_session'
|
|
734
|
-
);
|
|
810
|
+
session(defval: string | InputArgs<StringInputOptions, string>, title?: string | InputArgs<StringInputOptions, string>, opts?: StringInputOptions): string {
|
|
811
|
+
const a = inputArgs<string, StringInputOptions>('session', defval, title, opts);
|
|
812
|
+
return registerInput<string>({ type: 'session', defval: a.defval, title: a.title, options: a.opts.options, ...common(a.opts) }, 'input_session');
|
|
735
813
|
},
|
|
736
814
|
/** Ticker id ("NASDAQ:MSFT"), for `request.security` of another symbol (#180). */
|
|
737
|
-
symbol(defval: string, title?: string, opts
|
|
738
|
-
|
|
815
|
+
symbol(defval: string | InputArgs<StringInputOptions, string>, title?: string | InputArgs<StringInputOptions, string>, opts?: StringInputOptions): string {
|
|
816
|
+
const a = inputArgs<string, StringInputOptions>('symbol', defval, title, opts);
|
|
817
|
+
return registerInput<string>({ type: 'symbol', defval: a.defval, title: a.title, options: a.opts.options, ...common(a.opts) }, 'input_symbol');
|
|
818
|
+
},
|
|
819
|
+
/** PineScript `input.price`: a price level, a float input (the host shows it as a number). */
|
|
820
|
+
price(defval: number | InputArgs<InputOptions, number>, title?: string | InputArgs<InputOptions, number>, opts?: InputOptions): number {
|
|
821
|
+
const a = inputArgs<number, InputOptions>('price', defval, title, opts);
|
|
822
|
+
return registerNumeric('float', a.defval, a.title, a.opts);
|
|
739
823
|
},
|
|
740
824
|
/** Date and time as a UNIX time in milliseconds, e.g. `input.time(timestamp('2024-01-01'), 'Start')`. */
|
|
741
|
-
time(defval: number, title?: string, opts
|
|
742
|
-
const
|
|
743
|
-
|
|
825
|
+
time(defval: number | InputArgs<InputOptions, number>, title?: string | InputArgs<InputOptions, number>, opts?: InputOptions): number {
|
|
826
|
+
const a = inputArgs<number, InputOptions>('time', defval, title, opts);
|
|
827
|
+
const v = registerInput<unknown>({ type: 'time', defval: a.defval, title: a.title, ...common(a.opts) }, 'input_time');
|
|
828
|
+
return typeof v === 'number' ? v : a.defval;
|
|
744
829
|
},
|
|
745
|
-
};
|
|
830
|
+
});
|
|
746
831
|
|
|
747
832
|
// ── OHLCV builtins ───────────────────────────────────────────────────────────
|
|
748
833
|
|
|
@@ -755,11 +840,27 @@ export const hl2 = new Series(ctxBars, (b) => (b.high + b.low) / 2);
|
|
|
755
840
|
export const hlc3 = new Series(ctxBars, (b) => (b.high + b.low + b.close) / 3);
|
|
756
841
|
export const ohlc4 = new Series(ctxBars, (b) => (b.open + b.high + b.low + b.close) / 4);
|
|
757
842
|
export const hlcc4 = new Series(ctxBars, (b) => (b.high + b.low + b.close + b.close) / 4);
|
|
843
|
+
/** PineScript `bar_index`: the index of each bar (0 on the first). In a bar loop, `c.i` is the same number. */
|
|
844
|
+
export const bar_index = new Series(ctxBars, (_b, i) => i);
|
|
845
|
+
bar_index.fullHistory = true;
|
|
846
|
+
/** PineScript `last_bar_index`: the index of the last bar, on every bar. In a bar loop, `c.n - 1`. */
|
|
847
|
+
export const last_bar_index = new Series(ctxBars, (_b, _i, data) => data.length - 1);
|
|
848
|
+
last_bar_index.fullHistory = true;
|
|
758
849
|
// built-in price variables keep the whole history (no 5000-bar limit)
|
|
759
850
|
for (const s of [hl2, hlc3, ohlc4, hlcc4]) s.fullHistory = true;
|
|
760
851
|
|
|
761
852
|
const SOURCES: Record<string, Series> = { open, high, low, close, volume, hl2, hlc3, ohlc4, hlcc4 };
|
|
762
853
|
|
|
854
|
+
/** The name of a source default: `input.source(close)` as `input.source("close")` (PineScript passes the series). */
|
|
855
|
+
function sourceName(defval: string | Series): string {
|
|
856
|
+
if (typeof defval === 'string') return defval;
|
|
857
|
+
const name = Object.keys(SOURCES).find((k) => SOURCES[k] === defval);
|
|
858
|
+
if (!name) {
|
|
859
|
+
throw new Error('input.source(): the default must be a built-in source (open, high, low, close, volume, hl2, hlc3, ohlc4, hlcc4).');
|
|
860
|
+
}
|
|
861
|
+
return name;
|
|
862
|
+
}
|
|
863
|
+
|
|
763
864
|
function sourceSeries(name: string): Series {
|
|
764
865
|
const builtin = SOURCES[name];
|
|
765
866
|
if (builtin) return builtin;
|
|
@@ -788,9 +889,86 @@ function barsSeries(compute: (b: Bar[]) => Series): Series {
|
|
|
788
889
|
});
|
|
789
890
|
}
|
|
790
891
|
|
|
892
|
+
// ── Guards for script mistakes ───────────────────────────────────────────────
|
|
893
|
+
|
|
894
|
+
/** Edit distance of two names (for the "did you mean" of a namespace). */
|
|
895
|
+
function editDistance(a: string, b: string): number {
|
|
896
|
+
const row = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
897
|
+
for (let i = 1; i <= a.length; i++) {
|
|
898
|
+
let diag = row[0]!;
|
|
899
|
+
row[0] = i;
|
|
900
|
+
for (let j = 1; j <= b.length; j++) {
|
|
901
|
+
const up = row[j]!;
|
|
902
|
+
row[j] = Math.min(up + 1, row[j - 1]! + 1, diag + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
903
|
+
diag = up;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return row[b.length]!;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/** Names a tool reads to probe an object (promise, JSON, test matchers, module interop): never an error. */
|
|
910
|
+
const PROBED_NAMES = new Set(['then', 'toJSON', 'asymmetricMatch', 'nodeType', 'tagName', 'inspect', 'default']);
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* A namespace whose unknown members throw instead of being `undefined`: `ta.smaa(close, 5)` fails with
|
|
914
|
+
* "ta.smaa does not exist. Did you mean ta.sma?" rather than "ta.smaa is not a function", and `color.purpel`
|
|
915
|
+
* fails where it is read rather than giving a plot with no colour.
|
|
916
|
+
*/
|
|
917
|
+
function strictNamespace<T extends object>(name: string, members: T): T {
|
|
918
|
+
return new Proxy(members, {
|
|
919
|
+
get(target, prop, receiver) {
|
|
920
|
+
if (typeof prop === 'symbol' || prop in target || PROBED_NAMES.has(prop) || /^[_$@]/.test(prop)) {
|
|
921
|
+
return Reflect.get(target, prop, receiver);
|
|
922
|
+
}
|
|
923
|
+
const keys = Object.keys(target);
|
|
924
|
+
const lower = prop.toLowerCase();
|
|
925
|
+
const ranked = keys
|
|
926
|
+
.map((k) => ({ k, d: editDistance(lower, k.toLowerCase()) }))
|
|
927
|
+
.filter(({ k, d }) => {
|
|
928
|
+
const key = k.toLowerCase();
|
|
929
|
+
return d <= Math.max(1, Math.floor(prop.length / 3)) || key.includes(lower) || (key.length >= 3 && lower.includes(key));
|
|
930
|
+
})
|
|
931
|
+
.sort((x, y) => x.d - y.d)
|
|
932
|
+
.slice(0, 3)
|
|
933
|
+
.map(({ k }) => `${name}.${k}`);
|
|
934
|
+
throw new TypeError(
|
|
935
|
+
`${name}.${prop} does not exist.` + (ranked.length ? ` Did you mean ${ranked.join(', ')}?` : '') +
|
|
936
|
+
' See docs/inventory.md for the supported functions.'
|
|
937
|
+
);
|
|
938
|
+
},
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* The ta.* functions compute a whole Series in one call. Called inside an eachBar() callback, a call runs again on
|
|
944
|
+
* every bar (slow) and returns a Series, not the value of the bar, and it has no per-bar history: the call throws
|
|
945
|
+
* with the fix.
|
|
946
|
+
*/
|
|
947
|
+
function outsideBarLoops<T extends Record<string, unknown>>(members: T): T {
|
|
948
|
+
const out: Record<string, unknown> = {};
|
|
949
|
+
for (const [key, value] of Object.entries(members)) {
|
|
950
|
+
if (typeof value !== 'function') {
|
|
951
|
+
out[key] = value;
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
const fn = value as (...args: unknown[]) => unknown;
|
|
955
|
+
out[key] = function (this: unknown, ...args: unknown[]) {
|
|
956
|
+
if (currentBar >= 0) {
|
|
957
|
+
throw new Error(
|
|
958
|
+
`ta.${key}() was called inside eachBar(): a ta.* call computes the whole Series at once. ` +
|
|
959
|
+
`Compute it before the loop (const x = ta.${key}(...)) and read the bar's value with c.get(x). ` +
|
|
960
|
+
'For a Pine ta.* call inside an if block or a for loop, use the callsite.* call sites.'
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
return fn.apply(this, args);
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
return out as T;
|
|
967
|
+
}
|
|
968
|
+
|
|
791
969
|
/** ta.* with the chart-implicit (bars-first) functions bound to the context,
|
|
792
970
|
* so scripts call them like PineScript: `ta.tr(true)`, `ta.atr(14)`. */
|
|
793
|
-
export const ta = {
|
|
971
|
+
export const ta = strictNamespace('ta', outsideBarLoops({
|
|
794
972
|
...taSeries,
|
|
795
973
|
tr: (handleNa: boolean = false): Series => taSeries.tr(bars(), handleNa),
|
|
796
974
|
atr: (length: number): Series => taSeries.atr(bars(), length),
|
|
@@ -838,6 +1016,8 @@ export const ta = {
|
|
|
838
1016
|
(source: Series, anchor?: Series): Series;
|
|
839
1017
|
(source: Series, anchor: Series | undefined, stdev_mult: number): [Series, Series, Series];
|
|
840
1018
|
},
|
|
1019
|
+
/** `ta.vwma(source, length)` with the chart volume, as PineScript; a third argument gives another volume. */
|
|
1020
|
+
vwma: (source: Series, length: number, vol?: Series): Series => taSeries.vwma(source, length, vol ?? volume),
|
|
841
1021
|
/** `ta.mfi(source, length)` with the chart volume (PineScript takes 2 arguments). */
|
|
842
1022
|
mfi: (source: Series, length: number): Series => taSeries.mfi(source, length, volume),
|
|
843
1023
|
/** `ta.pivothigh(source, leftbars, rightbars)` or `ta.pivothigh(leftbars, rightbars)` (source = high).
|
|
@@ -860,7 +1040,7 @@ export const ta = {
|
|
|
860
1040
|
iii: barsSeries(taSeries.iii),
|
|
861
1041
|
wad: barsSeries(taSeries.wad),
|
|
862
1042
|
wvad: barsSeries(taSeries.wvad),
|
|
863
|
-
};
|
|
1043
|
+
}));
|
|
864
1044
|
|
|
865
1045
|
/** PineScript `fixnan(source)`: each `na` value becomes the previous non-`na` value.
|
|
866
1046
|
* Accepts a Series or a per-bar color array (`undefined` = `na`, as returned by `color.when`). */
|
|
@@ -903,15 +1083,17 @@ function colorWhen(cond: Series, colorTrue: string, colorFalse?: string): Array<
|
|
|
903
1083
|
}
|
|
904
1084
|
|
|
905
1085
|
/** color.* plus `when` — per-bar conditional colors for plot()/fill()/bgcolor()/barcolor(). */
|
|
906
|
-
export const color = {
|
|
1086
|
+
export const color = strictNamespace('color', {
|
|
907
1087
|
...colorCore,
|
|
908
1088
|
new: colorCore.new_color,
|
|
909
1089
|
when: colorWhen,
|
|
910
|
-
};
|
|
1090
|
+
});
|
|
911
1091
|
|
|
912
1092
|
// ── Drawing declarations ─────────────────────────────────────────────────────
|
|
913
1093
|
|
|
914
1094
|
export interface ScriptPlotOptions {
|
|
1095
|
+
/** Shift in bars (PineScript `offset`): the value of bar i is drawn on bar i + offset (negative: to the left). */
|
|
1096
|
+
offset?: number;
|
|
915
1097
|
/** Static color, or a per-bar color array from color.when(). */
|
|
916
1098
|
color?: string | string[];
|
|
917
1099
|
linewidth?: number;
|
|
@@ -968,10 +1150,14 @@ function plotImpl(series: Series, title: string = DEFAULT_OUTPUT_TITLES.plot, op
|
|
|
968
1150
|
const values = series.toArray();
|
|
969
1151
|
const b = bars();
|
|
970
1152
|
const data: TimeValue[] = [];
|
|
971
|
-
|
|
1153
|
+
// offset (PineScript plot(offset = k)): the value and colour of bar i are drawn on bar i + k; bars past the
|
|
1154
|
+
// chart's first or last bar are dropped
|
|
1155
|
+
const off = Math.trunc(options.offset ?? 0);
|
|
1156
|
+
for (let j = Math.max(0, off); j < b.length && j - off < b.length; j++) {
|
|
1157
|
+
const i = j - off;
|
|
972
1158
|
const value = values[i];
|
|
973
1159
|
if (value === undefined || !Number.isFinite(value)) continue; // PineScript plots +/-Infinity as na
|
|
974
|
-
data.push(perBar ? { time: b[
|
|
1160
|
+
data.push(perBar ? { time: b[j]!.time, value, color: perBar[i] } : { time: b[j]!.time, value });
|
|
975
1161
|
}
|
|
976
1162
|
c.plots[id] = data;
|
|
977
1163
|
return { id, kind: 'plot' };
|
|
@@ -2316,7 +2502,8 @@ export const weekofyear = calendar(timeCore.weekofyear);
|
|
|
2316
2502
|
/** PineScript `dayofmonth(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
2317
2503
|
export const dayofmonth = calendar(timeCore.dayofmonth);
|
|
2318
2504
|
/** PineScript `dayofweek(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
2319
|
-
|
|
2505
|
+
/** `dayofweek(time, timezone?)`, with the PineScript constants `dayofweek.sunday` (1) ... `dayofweek.saturday` (7). */
|
|
2506
|
+
export const dayofweek = Object.assign(calendar(timeCore.dayofweek), pineConstants.daysOfWeek);
|
|
2320
2507
|
/** PineScript `hour(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
2321
2508
|
export const hour = calendar(timeCore.hour);
|
|
2322
2509
|
/** PineScript `minute(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
@@ -2355,7 +2542,7 @@ export function inSession(time: number, session: string, timezone?: string): boo
|
|
|
2355
2542
|
}
|
|
2356
2543
|
|
|
2357
2544
|
/** PineScript `str.*`; `format_time` and the dates of `format` use the exchange time zone by default. */
|
|
2358
|
-
export const str = {
|
|
2545
|
+
export const str = strictNamespace('str', {
|
|
2359
2546
|
...strCore,
|
|
2360
2547
|
// format.mintick uses the chart symbol's tick and price scale (syminfo.mintick, syminfo.pricescale)
|
|
2361
2548
|
tostring: ((value: unknown, format?: string, mintick?: number, pricescale?: number) =>
|
|
@@ -2368,7 +2555,7 @@ export const str = {
|
|
|
2368
2555
|
format(pattern: string, ...args: unknown[]): string {
|
|
2369
2556
|
return formatMessage(pattern, args, exchangeTimezone);
|
|
2370
2557
|
},
|
|
2371
|
-
};
|
|
2558
|
+
});
|
|
2372
2559
|
|
|
2373
2560
|
// ── Session values (issue #100) ──────────────────────────────────────────────
|
|
2374
2561
|
|
|
@@ -2811,60 +2998,249 @@ function security_lower_tf<T extends SecurityValue>(
|
|
|
2811
2998
|
}
|
|
2812
2999
|
|
|
2813
3000
|
/** PineScript `request.*`: `security` (issue #101, other symbols #179, lower timeframes #181) and `security_lower_tf`. */
|
|
2814
|
-
|
|
3001
|
+
/** PineScript parameters of `request.security` / `request.security_lower_tf`, in their positional order. */
|
|
3002
|
+
const SECURITY_PARAMETERS = ['symbol', 'timeframe', 'expression', 'gaps', 'lookahead', 'ignore_invalid_symbol', 'currency', 'calc_bars_count'] as const;
|
|
3003
|
+
const SECURITY_LOWER_TF_PARAMETERS = ['symbol', 'timeframe', 'expression', 'ignore_invalid_symbol', 'currency', 'ignore_invalid_timeframe', 'calc_bars_count'] as const;
|
|
3004
|
+
|
|
3005
|
+
/** Named arguments of `request.security` (PineScript names). */
|
|
3006
|
+
export interface SecurityArgs<T extends SecurityValue = SecurityValue> {
|
|
3007
|
+
symbol?: string;
|
|
3008
|
+
timeframe?: string;
|
|
3009
|
+
expression?: () => T;
|
|
3010
|
+
gaps?: string;
|
|
3011
|
+
lookahead?: string;
|
|
3012
|
+
ignore_invalid_symbol?: boolean;
|
|
3013
|
+
currency?: string;
|
|
3014
|
+
calc_bars_count?: number;
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
/** Named arguments of `request.security_lower_tf` (PineScript names). */
|
|
3018
|
+
export interface SecurityLowerTfArgs<T extends SecurityValue = SecurityValue> {
|
|
3019
|
+
symbol?: string;
|
|
3020
|
+
timeframe?: string;
|
|
3021
|
+
expression?: () => T;
|
|
3022
|
+
ignore_invalid_symbol?: boolean;
|
|
3023
|
+
currency?: string;
|
|
3024
|
+
ignore_invalid_timeframe?: boolean;
|
|
3025
|
+
calc_bars_count?: number;
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
/** `currency` and `calc_bars_count` change the values in PineScript: they throw instead of being ignored. */
|
|
3029
|
+
function unsupportedSecurityArgs(name: string, currency: unknown, calcBarsCount: unknown): void {
|
|
3030
|
+
if (currency !== undefined && currency !== '' && currency !== 'NONE') {
|
|
3031
|
+
throw new Error(`${name}(): the currency argument (conversion of the values) is not supported.`);
|
|
3032
|
+
}
|
|
3033
|
+
if (calcBarsCount !== undefined) throw new Error(`${name}(): the calc_bars_count argument is not supported.`);
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
/** `request.security` with PineScript's named arguments: `request.security(sym, "D", () => close, { lookahead: barmerge.lookahead_on })`. */
|
|
3037
|
+
function securityCall<T extends SecurityValue>(
|
|
3038
|
+
...args:
|
|
3039
|
+
| [symbol: string, timeframe: string, expression: () => T, gaps?: string, lookahead?: string, ignore_invalid_symbol?: boolean]
|
|
3040
|
+
| [symbol: string, timeframe: string, expression: () => T, args: SecurityArgs<T>]
|
|
3041
|
+
| [args: SecurityArgs<T> & { symbol: string; timeframe: string; expression: () => T }]
|
|
3042
|
+
): T extends unknown[] ? Series[] : Series {
|
|
3043
|
+
const a = namedArguments('request.security', SECURITY_PARAMETERS, args);
|
|
3044
|
+
unsupportedSecurityArgs('request.security', a[6], a[7]);
|
|
3045
|
+
return security(a[0] as string, a[1] as string, a[2] as () => T, a[3] as string | undefined, a[4] as string | undefined, a[5] as boolean | undefined);
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
/** `request.security_lower_tf` with PineScript's named arguments. */
|
|
3049
|
+
function securityLowerTfCall<T extends SecurityValue>(
|
|
3050
|
+
...args:
|
|
3051
|
+
| [symbol: string, timeframe: string, expression: () => T, ignore_invalid_symbol?: boolean]
|
|
3052
|
+
| [symbol: string, timeframe: string, expression: () => T, args: SecurityLowerTfArgs<T>]
|
|
3053
|
+
| [args: SecurityLowerTfArgs<T> & { symbol: string; timeframe: string; expression: () => T }]
|
|
3054
|
+
): T extends unknown[] ? number[][][] : number[][] {
|
|
3055
|
+
const a = namedArguments('request.security_lower_tf', SECURITY_LOWER_TF_PARAMETERS, args);
|
|
3056
|
+
unsupportedSecurityArgs('request.security_lower_tf', a[4], a[6]);
|
|
3057
|
+
if (a[5] !== undefined && a[5] !== false) throw new Error('request.security_lower_tf(): the ignore_invalid_timeframe argument is not supported.');
|
|
3058
|
+
return security_lower_tf(a[0] as string, a[1] as string, a[2] as () => T, a[3] as boolean | undefined);
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
export const request = strictNamespace('request', { security: securityCall, security_lower_tf: securityLowerTfCall });
|
|
2815
3062
|
|
|
2816
3063
|
// ── Drawing objects ──────────────────────────────────────────────────────────
|
|
2817
3064
|
// The drawing namespaces come from this bundle so that they share the registry that
|
|
2818
3065
|
// executeScript() resets. `line.all` etc. are read like PineScript variables.
|
|
2819
3066
|
|
|
2820
3067
|
/** PineScript `line.*`; `line.all` lists the live lines in creation order. */
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
3068
|
+
// ── Drawing namespaces (named arguments, Series arguments in bar loops) ──────
|
|
3069
|
+
|
|
3070
|
+
type Numeric = number | Series;
|
|
3071
|
+
/** A function whose number parameters also take a Series (read on the current bar inside a bar loop). */
|
|
3072
|
+
type WithSeries<F> = F extends (...args: infer A) => infer R
|
|
3073
|
+
? (...args: { [K in keyof A]: number extends A[K] ? A[K] | Series : A[K] }) => R
|
|
3074
|
+
: never;
|
|
3075
|
+
type ScriptDrawing<T> = { [K in keyof T]: T[K] extends (...args: never[]) => unknown ? T[K] & WithSeries<T[K]> : T[K] };
|
|
3076
|
+
type Colour = string | undefined;
|
|
3077
|
+
|
|
3078
|
+
/** Named arguments of `label.new` (PineScript parameter names). */
|
|
3079
|
+
export interface LabelNewArgs {
|
|
3080
|
+
x?: Numeric;
|
|
3081
|
+
y?: Numeric;
|
|
3082
|
+
point?: ChartPoint;
|
|
3083
|
+
text?: string;
|
|
3084
|
+
xloc?: 'bar_index' | 'bar_time';
|
|
3085
|
+
yloc?: 'price' | 'abovebar' | 'belowbar';
|
|
3086
|
+
color?: Colour;
|
|
3087
|
+
style?: NonNullable<Label['style']>;
|
|
3088
|
+
textcolor?: Colour;
|
|
3089
|
+
size?: string | number;
|
|
3090
|
+
textalign?: 'left' | 'center' | 'right';
|
|
3091
|
+
tooltip?: string;
|
|
3092
|
+
text_font_family?: 'default' | 'monospace';
|
|
3093
|
+
force_overlay?: boolean;
|
|
3094
|
+
text_formatting?: number;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
/** Named arguments of `line.new`. */
|
|
3098
|
+
export interface LineNewArgs {
|
|
3099
|
+
x1?: Numeric;
|
|
3100
|
+
y1?: Numeric;
|
|
3101
|
+
x2?: Numeric;
|
|
3102
|
+
y2?: Numeric;
|
|
3103
|
+
first_point?: ChartPoint;
|
|
3104
|
+
second_point?: ChartPoint;
|
|
3105
|
+
xloc?: 'bar_index' | 'bar_time';
|
|
3106
|
+
extend?: 'none' | 'left' | 'right' | 'both';
|
|
3107
|
+
color?: Colour;
|
|
3108
|
+
style?: 'solid' | 'dotted' | 'dashed' | 'arrow_left' | 'arrow_right' | 'arrow_both';
|
|
3109
|
+
width?: Numeric;
|
|
3110
|
+
force_overlay?: boolean;
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
/** Named arguments of `box.new`. */
|
|
3114
|
+
export interface BoxNewArgs {
|
|
3115
|
+
left?: Numeric;
|
|
3116
|
+
top?: Numeric;
|
|
3117
|
+
right?: Numeric;
|
|
3118
|
+
bottom?: Numeric;
|
|
3119
|
+
top_left?: ChartPoint;
|
|
3120
|
+
bottom_right?: ChartPoint;
|
|
3121
|
+
border_color?: Colour;
|
|
3122
|
+
border_width?: Numeric;
|
|
3123
|
+
border_style?: 'solid' | 'dotted' | 'dashed';
|
|
3124
|
+
extend?: 'none' | 'left' | 'right' | 'both';
|
|
3125
|
+
xloc?: 'bar_index' | 'bar_time';
|
|
3126
|
+
bgcolor?: Colour;
|
|
3127
|
+
text?: string;
|
|
3128
|
+
text_size?: string | number;
|
|
3129
|
+
text_color?: Colour;
|
|
3130
|
+
text_halign?: 'left' | 'center' | 'right';
|
|
3131
|
+
text_valign?: 'top' | 'center' | 'bottom';
|
|
3132
|
+
text_wrap?: 'none' | 'auto';
|
|
3133
|
+
text_font_family?: 'default' | 'monospace';
|
|
3134
|
+
force_overlay?: boolean;
|
|
3135
|
+
text_formatting?: number;
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
/** Named arguments of `table.new`. */
|
|
3139
|
+
export interface TableNewArgs {
|
|
3140
|
+
position?: string;
|
|
3141
|
+
columns?: number;
|
|
3142
|
+
rows?: number;
|
|
3143
|
+
bgcolor?: Colour;
|
|
3144
|
+
frame_color?: Colour;
|
|
3145
|
+
frame_width?: number;
|
|
3146
|
+
border_color?: Colour;
|
|
3147
|
+
border_width?: number;
|
|
3148
|
+
force_overlay?: boolean;
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
/** Named arguments of `table.cell`. */
|
|
3152
|
+
export interface TableCellArgs {
|
|
3153
|
+
table_id?: Table;
|
|
3154
|
+
column?: number;
|
|
3155
|
+
row?: number;
|
|
3156
|
+
text?: string;
|
|
3157
|
+
width?: number;
|
|
3158
|
+
height?: number;
|
|
3159
|
+
text_color?: Colour;
|
|
3160
|
+
text_halign?: 'left' | 'center' | 'right';
|
|
3161
|
+
text_valign?: 'top' | 'center' | 'bottom';
|
|
3162
|
+
text_size?: string | number;
|
|
3163
|
+
bgcolor?: Colour;
|
|
3164
|
+
tooltip?: string;
|
|
3165
|
+
text_font_family?: 'default' | 'monospace';
|
|
3166
|
+
text_formatting?: number;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
/** Named arguments of `polyline.new`. */
|
|
3170
|
+
export interface PolylineNewArgs {
|
|
3171
|
+
points?: ChartPoint[];
|
|
3172
|
+
curved?: boolean;
|
|
3173
|
+
closed?: boolean;
|
|
3174
|
+
xloc?: 'bar_index' | 'bar_time';
|
|
3175
|
+
line_color?: Colour;
|
|
3176
|
+
fill_color?: Colour | null;
|
|
3177
|
+
line_style?: 'solid' | 'dotted' | 'dashed';
|
|
3178
|
+
line_width?: number;
|
|
3179
|
+
force_overlay?: boolean;
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
/** Named arguments of `linefill.new`. */
|
|
3183
|
+
export interface LinefillNewArgs {
|
|
3184
|
+
line1?: Line;
|
|
3185
|
+
line2?: Line;
|
|
3186
|
+
color?: Colour;
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
const loopBar = (): number => currentBar;
|
|
3190
|
+
|
|
3191
|
+
/** Wraps a drawing namespace and keeps its `all` getter (the live drawings in creation order). */
|
|
3192
|
+
function drawings<T extends Record<string, unknown>, D>(name: string, members: T, all: () => D[]) {
|
|
3193
|
+
const out = drawingNamespace(name, members, loopBar) as ScriptDrawing<T>;
|
|
3194
|
+
return Object.defineProperty(out, 'all', { get: all, enumerable: true }) as Omit<ScriptDrawing<T>, 'all'> & { readonly all: D[] };
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
const lineNs = drawings('line', { ...lineCore, ...pineConstants.lineStyles }, () => lineCore.all());
|
|
3198
|
+
/** PineScript `line.*` (with `line.style_*`); `line.all` lists the live lines in creation order. */
|
|
3199
|
+
export const line = lineNs as typeof lineNs & {
|
|
3200
|
+
new: ((args: LineNewArgs) => Line) &
|
|
3201
|
+
((x1: Numeric, y1: Numeric, x2: Numeric, y2: Numeric, args: LineNewArgs) => Line) &
|
|
3202
|
+
((first_point: ChartPoint, second_point: ChartPoint, args: LineNewArgs) => Line);
|
|
2826
3203
|
};
|
|
2827
3204
|
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
3205
|
+
const labelNs = drawings('label', { ...labelCore, ...pineConstants.labelStyles }, () => labelCore.all());
|
|
3206
|
+
/** PineScript `label.*` (with `label.style_*`); `label.all` lists the live labels in creation order. */
|
|
3207
|
+
export const label = labelNs as typeof labelNs & {
|
|
3208
|
+
new: ((args: LabelNewArgs) => Label) &
|
|
3209
|
+
((x: Numeric, y: Numeric, args: LabelNewArgs) => Label) &
|
|
3210
|
+
((x: Numeric, y: Numeric, text: string, args: LabelNewArgs) => Label) &
|
|
3211
|
+
((point: ChartPoint, args: LabelNewArgs) => Label) &
|
|
3212
|
+
((point: ChartPoint, text: string, args: LabelNewArgs) => Label);
|
|
2834
3213
|
};
|
|
2835
3214
|
|
|
3215
|
+
const boxNs = drawings('box', { ...boxCore }, () => boxCore.all());
|
|
2836
3216
|
/** PineScript `box.*`; `box.all` lists the live boxes in creation order. */
|
|
2837
|
-
export const box = {
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
},
|
|
3217
|
+
export const box = boxNs as typeof boxNs & {
|
|
3218
|
+
new: ((args: BoxNewArgs) => Box) &
|
|
3219
|
+
((left: Numeric, top: Numeric, right: Numeric, bottom: Numeric, args: BoxNewArgs) => Box) &
|
|
3220
|
+
((top_left: ChartPoint, bottom_right: ChartPoint, args: BoxNewArgs) => Box);
|
|
2842
3221
|
};
|
|
2843
3222
|
|
|
3223
|
+
const linefillNs = drawings('linefill', { ...linefillCore }, () => linefillCore.all());
|
|
2844
3224
|
/** PineScript `linefill.*`; `linefill.all` lists the live linefills in creation order. */
|
|
2845
|
-
export const linefill = {
|
|
2846
|
-
|
|
2847
|
-
get all() {
|
|
2848
|
-
return linefillCore.all();
|
|
2849
|
-
},
|
|
3225
|
+
export const linefill = linefillNs as typeof linefillNs & {
|
|
3226
|
+
new: ((args: LinefillNewArgs) => Linefill) & ((line1: Line, line2: Line, args: LinefillNewArgs) => Linefill);
|
|
2850
3227
|
};
|
|
2851
3228
|
|
|
3229
|
+
const polylineNs = drawings('polyline', { ...polylineCore }, () => polylineCore.all());
|
|
2852
3230
|
/** PineScript `polyline.*`; `polyline.all` lists the live polylines in creation order. */
|
|
2853
|
-
export const polyline = {
|
|
2854
|
-
|
|
2855
|
-
get all() {
|
|
2856
|
-
return polylineCore.all();
|
|
2857
|
-
},
|
|
3231
|
+
export const polyline = polylineNs as typeof polylineNs & {
|
|
3232
|
+
new: ((args: PolylineNewArgs) => Polyline) & ((points: ChartPoint[], args: PolylineNewArgs) => Polyline);
|
|
2858
3233
|
};
|
|
2859
3234
|
|
|
3235
|
+
const tableNs = drawings('table', { ...tableCore }, () => tableCore.all());
|
|
2860
3236
|
/**
|
|
2861
3237
|
* PineScript `table.*`; `table.all` lists the live tables in creation order. The live tables go to `result.tables`.
|
|
2862
3238
|
*/
|
|
2863
|
-
export const table = {
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
3239
|
+
export const table = tableNs as typeof tableNs & {
|
|
3240
|
+
new: ((args: TableNewArgs) => Table) & ((position: string, columns: number, rows: number, args: TableNewArgs) => Table);
|
|
3241
|
+
cell: ((args: TableCellArgs) => void) &
|
|
3242
|
+
((table_id: Table, column: number, row: number, args: TableCellArgs) => void) &
|
|
3243
|
+
((table_id: Table, column: number, row: number, text: string, args: TableCellArgs) => void);
|
|
2868
3244
|
};
|
|
2869
3245
|
|
|
2870
3246
|
/** PineScript `position.*` constants (table positions). */
|
|
@@ -2873,6 +3249,11 @@ export { position };
|
|
|
2873
3249
|
/** PineScript `text.format_*`, `text.align_*` and `text.wrap_*` constants. */
|
|
2874
3250
|
export const text = { ...textCore };
|
|
2875
3251
|
|
|
3252
|
+
/** PineScript constant namespaces (`shape.triangleup`, `size.small`, `xloc.bar_time`, `extend.right`...): the strings
|
|
3253
|
+
* the functions take. */
|
|
3254
|
+
export const { shape, location, size, xloc, yloc, extend, display, format, font, scale, order, currency, adjustment } =
|
|
3255
|
+
pineConstants;
|
|
3256
|
+
|
|
2876
3257
|
/**
|
|
2877
3258
|
* PineScript `chart.point.*`. `chart.point.now(price?)` is the point of the current bar
|
|
2878
3259
|
* (index, time, and `price`, default the close), so it runs inside eachBar().
|
|
@@ -2984,13 +3365,13 @@ export type {
|
|
|
2984
3365
|
const { saveRandom: _saveRandom, restoreRandom: _restoreRandom, ...mathScript } = mathCore;
|
|
2985
3366
|
void _saveRandom;
|
|
2986
3367
|
void _restoreRandom;
|
|
2987
|
-
export const math = {
|
|
3368
|
+
export const math = strictNamespace('math', {
|
|
2988
3369
|
...mathScript,
|
|
2989
3370
|
round_to_mintick: ((number: number | Series, mintick?: number) => {
|
|
2990
3371
|
const tick = mintick ?? syminfo.mintick;
|
|
2991
3372
|
return number instanceof Series ? mathCore.round_to_mintick(number, tick) : mathCore.round_to_mintick(number, tick);
|
|
2992
3373
|
}) as typeof mathCore.round_to_mintick,
|
|
2993
|
-
};
|
|
3374
|
+
});
|
|
2994
3375
|
export * as compare from '../compare/index.js';
|
|
2995
3376
|
export { isInputActive } from '../runtime/input-active.js';
|
|
2996
3377
|
export type {
|