oakscriptjs 0.13.2 → 0.14.0
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 +499 -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 +499 -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 +8 -5
- 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 +457 -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,
|
|
@@ -688,61 +697,132 @@ function registerNumeric(type: 'int' | 'float', defval: number, title?: string,
|
|
|
688
697
|
return typeof v === 'number' ? v : defval;
|
|
689
698
|
}
|
|
690
699
|
|
|
691
|
-
/**
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
700
|
+
/** Named arguments of an input: its options, with `title` (and `defval` when every argument is named). */
|
|
701
|
+
type InputArgs<O, D> = O & { title?: string; defval?: D };
|
|
702
|
+
|
|
703
|
+
const COMMON_INPUT_KEYS = ['tooltip', 'inline', 'group', 'confirm', 'display', 'active'] as const;
|
|
704
|
+
/** The PineScript arguments of each input kind (named arguments are checked against them). */
|
|
705
|
+
const INPUT_KEYS: Record<string, readonly string[]> = {
|
|
706
|
+
int: ['defval', 'title', 'minval', 'maxval', 'step', ...COMMON_INPUT_KEYS],
|
|
707
|
+
float: ['defval', 'title', 'minval', 'maxval', 'step', ...COMMON_INPUT_KEYS],
|
|
708
|
+
price: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
709
|
+
bool: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
710
|
+
string: ['defval', 'title', 'options', ...COMMON_INPUT_KEYS],
|
|
711
|
+
text_area: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
712
|
+
color: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
713
|
+
source: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
714
|
+
timeframe: ['defval', 'title', 'options', ...COMMON_INPUT_KEYS],
|
|
715
|
+
session: ['defval', 'title', 'options', ...COMMON_INPUT_KEYS],
|
|
716
|
+
symbol: ['defval', 'title', 'options', ...COMMON_INPUT_KEYS],
|
|
717
|
+
time: ['defval', 'title', ...COMMON_INPUT_KEYS],
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* The default, title and options of an `input.*` call, as PineScript writes it: `(defval, title, options)`,
|
|
722
|
+
* `(defval, { title, ...options })` or every argument by name (`{ defval, title, ...options }`). An unknown named
|
|
723
|
+
* argument throws with the closest names (`min` for `minval`).
|
|
724
|
+
*/
|
|
725
|
+
function inputArgs<D, O extends object>(
|
|
726
|
+
kind: string,
|
|
727
|
+
defvalOrArgs: unknown,
|
|
728
|
+
titleOrArgs: unknown,
|
|
729
|
+
opts: O | undefined
|
|
730
|
+
): { defval: D; title: string | undefined; opts: O } {
|
|
731
|
+
const name = `input.${kind}`;
|
|
732
|
+
const keys = INPUT_KEYS[kind]!;
|
|
733
|
+
let named: Record<string, unknown> = {};
|
|
734
|
+
let defval = defvalOrArgs;
|
|
735
|
+
let title: unknown = titleOrArgs;
|
|
736
|
+
if (isPlainObject(defvalOrArgs)) {
|
|
737
|
+
named = { ...defvalOrArgs };
|
|
738
|
+
defval = named.defval;
|
|
739
|
+
title = undefined;
|
|
740
|
+
} else if (isPlainObject(titleOrArgs)) {
|
|
741
|
+
named = { ...titleOrArgs };
|
|
742
|
+
title = undefined;
|
|
743
|
+
}
|
|
744
|
+
if (opts !== undefined) {
|
|
745
|
+
if (!isPlainObject(opts)) throw new TypeError(`${name}(): the options are an object, e.g. { tooltip: "..." }.`);
|
|
746
|
+
named = { ...named, ...opts };
|
|
747
|
+
}
|
|
748
|
+
for (const key of Object.keys(named)) if (!keys.includes(key)) throw unknownArgument(name, key, keys);
|
|
749
|
+
if (title !== undefined && typeof title !== 'string') {
|
|
750
|
+
throw new TypeError(`${name}(): the title is a string, got ${typeof title}.`);
|
|
751
|
+
}
|
|
752
|
+
const { defval: namedDefval, title: namedTitle, ...rest } = named;
|
|
753
|
+
return {
|
|
754
|
+
defval: (defval ?? namedDefval) as D,
|
|
755
|
+
title: (title as string | undefined) ?? (namedTitle as string | undefined),
|
|
756
|
+
opts: rest as O,
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/** PineScript `input.*` — declares an input AND returns its current value. The arguments after the default can be
|
|
761
|
+
* named, as in PineScript: `input.int(14, "Length", { minval: 1 })`, `input.int(14, { title: "Length", minval: 1 })`,
|
|
762
|
+
* `input.int({ defval: 14, title: "Length" })`. */
|
|
763
|
+
export const input = strictNamespace('input', {
|
|
764
|
+
int(defval: number | InputArgs<NumericInputOptions, number>, title?: string | InputArgs<NumericInputOptions, number>, opts?: NumericInputOptions): number {
|
|
765
|
+
const a = inputArgs<number, NumericInputOptions>('int', defval, title, opts);
|
|
766
|
+
return Math.round(registerNumeric('int', a.defval, a.title, a.opts));
|
|
695
767
|
},
|
|
696
|
-
float(defval: number, title?: string, opts
|
|
697
|
-
|
|
768
|
+
float(defval: number | InputArgs<NumericInputOptions, number>, title?: string | InputArgs<NumericInputOptions, number>, opts?: NumericInputOptions): number {
|
|
769
|
+
const a = inputArgs<number, NumericInputOptions>('float', defval, title, opts);
|
|
770
|
+
return registerNumeric('float', a.defval, a.title, a.opts);
|
|
698
771
|
},
|
|
699
|
-
bool(defval: boolean, title?: string, opts
|
|
700
|
-
|
|
772
|
+
bool(defval: boolean | InputArgs<InputOptions, boolean>, title?: string | InputArgs<InputOptions, boolean>, opts?: InputOptions): boolean {
|
|
773
|
+
const a = inputArgs<boolean, InputOptions>('bool', defval, title, opts);
|
|
774
|
+
return registerInput<boolean>({ type: 'bool', defval: a.defval, title: a.title, ...common(a.opts) }, 'input_bool');
|
|
701
775
|
},
|
|
702
|
-
string(defval: string, title?: string, opts
|
|
703
|
-
|
|
776
|
+
string(defval: string | InputArgs<StringInputOptions, string>, title?: string | InputArgs<StringInputOptions, string>, opts?: StringInputOptions): string {
|
|
777
|
+
const a = inputArgs<string, StringInputOptions>('string', defval, title, opts);
|
|
778
|
+
return registerInput<string>({ type: 'string', defval: a.defval, title: a.title, options: a.opts.options, ...common(a.opts) }, 'input_string');
|
|
704
779
|
},
|
|
705
|
-
/** Multi-line text (
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
return registerInput<string>({ type: 'text_area', defval, title, ...common(opts) }, 'input_text_area');
|
|
780
|
+
/** Multi-line text (lines separated by a newline), e.g. a list a script parses (#184). */
|
|
781
|
+
text_area(defval: string | InputArgs<InputOptions, string>, title?: string | InputArgs<InputOptions, string>, opts?: InputOptions): string {
|
|
782
|
+
const a = inputArgs<string, InputOptions>('text_area', defval, title, opts);
|
|
783
|
+
return registerInput<string>({ type: 'text_area', defval: a.defval, title: a.title, ...common(a.opts) }, 'input_text_area');
|
|
709
784
|
},
|
|
710
785
|
/**
|
|
711
786
|
* Declares a colour input. As in PineScript, the default keeps an alpha of 2 decimals: an input default
|
|
712
787
|
* `color.new(c, 90)` has the alpha byte 26 (25 in the script). A value given by the host is not changed.
|
|
713
788
|
*/
|
|
714
|
-
color(defval: string, title?: string, opts
|
|
715
|
-
|
|
789
|
+
color(defval: string | InputArgs<InputOptions, string>, title?: string | InputArgs<InputOptions, string>, opts?: InputOptions): string {
|
|
790
|
+
const a = inputArgs<string, InputOptions>('color', defval, title, opts);
|
|
791
|
+
return registerInput<string>({ type: 'color', defval: inputColorDefault(a.defval), title: a.title, ...common(a.opts) }, 'input_color');
|
|
716
792
|
},
|
|
717
793
|
/** Declares a source input and returns it as a Series. */
|
|
718
|
-
source(defval: string = 'close', title?: string, opts
|
|
719
|
-
const
|
|
794
|
+
source(defval: string | Series = 'close', title?: string | InputArgs<InputOptions, string | Series>, opts?: InputOptions): Series {
|
|
795
|
+
const a = inputArgs<string | Series, InputOptions>('source', defval, title, opts);
|
|
796
|
+
const name = registerInput<string>({ type: 'source', defval: sourceName(a.defval ?? 'close'), title: a.title, ...common(a.opts) }, 'input_source');
|
|
720
797
|
return sourceSeries(name);
|
|
721
798
|
},
|
|
722
799
|
/** 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
|
-
);
|
|
800
|
+
timeframe(defval: string | InputArgs<StringInputOptions, string>, title?: string | InputArgs<StringInputOptions, string>, opts?: StringInputOptions): string {
|
|
801
|
+
const a = inputArgs<string, StringInputOptions>('timeframe', defval, title, opts);
|
|
802
|
+
return registerInput<string>({ type: 'timeframe', defval: a.defval, title: a.title, options: a.opts.options, ...common(a.opts) }, 'input_timeframe');
|
|
728
803
|
},
|
|
729
804
|
/** Session string ("0930-1600", "0930-1600:23456"). */
|
|
730
|
-
session(defval: string, title?: string, opts
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
'input_session'
|
|
734
|
-
);
|
|
805
|
+
session(defval: string | InputArgs<StringInputOptions, string>, title?: string | InputArgs<StringInputOptions, string>, opts?: StringInputOptions): string {
|
|
806
|
+
const a = inputArgs<string, StringInputOptions>('session', defval, title, opts);
|
|
807
|
+
return registerInput<string>({ type: 'session', defval: a.defval, title: a.title, options: a.opts.options, ...common(a.opts) }, 'input_session');
|
|
735
808
|
},
|
|
736
809
|
/** Ticker id ("NASDAQ:MSFT"), for `request.security` of another symbol (#180). */
|
|
737
|
-
symbol(defval: string, title?: string, opts
|
|
738
|
-
|
|
810
|
+
symbol(defval: string | InputArgs<StringInputOptions, string>, title?: string | InputArgs<StringInputOptions, string>, opts?: StringInputOptions): string {
|
|
811
|
+
const a = inputArgs<string, StringInputOptions>('symbol', defval, title, opts);
|
|
812
|
+
return registerInput<string>({ type: 'symbol', defval: a.defval, title: a.title, options: a.opts.options, ...common(a.opts) }, 'input_symbol');
|
|
813
|
+
},
|
|
814
|
+
/** PineScript `input.price`: a price level, a float input (the host shows it as a number). */
|
|
815
|
+
price(defval: number | InputArgs<InputOptions, number>, title?: string | InputArgs<InputOptions, number>, opts?: InputOptions): number {
|
|
816
|
+
const a = inputArgs<number, InputOptions>('price', defval, title, opts);
|
|
817
|
+
return registerNumeric('float', a.defval, a.title, a.opts);
|
|
739
818
|
},
|
|
740
819
|
/** 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
|
-
|
|
820
|
+
time(defval: number | InputArgs<InputOptions, number>, title?: string | InputArgs<InputOptions, number>, opts?: InputOptions): number {
|
|
821
|
+
const a = inputArgs<number, InputOptions>('time', defval, title, opts);
|
|
822
|
+
const v = registerInput<unknown>({ type: 'time', defval: a.defval, title: a.title, ...common(a.opts) }, 'input_time');
|
|
823
|
+
return typeof v === 'number' ? v : a.defval;
|
|
744
824
|
},
|
|
745
|
-
};
|
|
825
|
+
});
|
|
746
826
|
|
|
747
827
|
// ── OHLCV builtins ───────────────────────────────────────────────────────────
|
|
748
828
|
|
|
@@ -755,11 +835,27 @@ export const hl2 = new Series(ctxBars, (b) => (b.high + b.low) / 2);
|
|
|
755
835
|
export const hlc3 = new Series(ctxBars, (b) => (b.high + b.low + b.close) / 3);
|
|
756
836
|
export const ohlc4 = new Series(ctxBars, (b) => (b.open + b.high + b.low + b.close) / 4);
|
|
757
837
|
export const hlcc4 = new Series(ctxBars, (b) => (b.high + b.low + b.close + b.close) / 4);
|
|
838
|
+
/** PineScript `bar_index`: the index of each bar (0 on the first). In a bar loop, `c.i` is the same number. */
|
|
839
|
+
export const bar_index = new Series(ctxBars, (_b, i) => i);
|
|
840
|
+
bar_index.fullHistory = true;
|
|
841
|
+
/** PineScript `last_bar_index`: the index of the last bar, on every bar. In a bar loop, `c.n - 1`. */
|
|
842
|
+
export const last_bar_index = new Series(ctxBars, (_b, _i, data) => data.length - 1);
|
|
843
|
+
last_bar_index.fullHistory = true;
|
|
758
844
|
// built-in price variables keep the whole history (no 5000-bar limit)
|
|
759
845
|
for (const s of [hl2, hlc3, ohlc4, hlcc4]) s.fullHistory = true;
|
|
760
846
|
|
|
761
847
|
const SOURCES: Record<string, Series> = { open, high, low, close, volume, hl2, hlc3, ohlc4, hlcc4 };
|
|
762
848
|
|
|
849
|
+
/** The name of a source default: `input.source(close)` as `input.source("close")` (PineScript passes the series). */
|
|
850
|
+
function sourceName(defval: string | Series): string {
|
|
851
|
+
if (typeof defval === 'string') return defval;
|
|
852
|
+
const name = Object.keys(SOURCES).find((k) => SOURCES[k] === defval);
|
|
853
|
+
if (!name) {
|
|
854
|
+
throw new Error('input.source(): the default must be a built-in source (open, high, low, close, volume, hl2, hlc3, ohlc4, hlcc4).');
|
|
855
|
+
}
|
|
856
|
+
return name;
|
|
857
|
+
}
|
|
858
|
+
|
|
763
859
|
function sourceSeries(name: string): Series {
|
|
764
860
|
const builtin = SOURCES[name];
|
|
765
861
|
if (builtin) return builtin;
|
|
@@ -788,9 +884,86 @@ function barsSeries(compute: (b: Bar[]) => Series): Series {
|
|
|
788
884
|
});
|
|
789
885
|
}
|
|
790
886
|
|
|
887
|
+
// ── Guards for script mistakes ───────────────────────────────────────────────
|
|
888
|
+
|
|
889
|
+
/** Edit distance of two names (for the "did you mean" of a namespace). */
|
|
890
|
+
function editDistance(a: string, b: string): number {
|
|
891
|
+
const row = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
892
|
+
for (let i = 1; i <= a.length; i++) {
|
|
893
|
+
let diag = row[0]!;
|
|
894
|
+
row[0] = i;
|
|
895
|
+
for (let j = 1; j <= b.length; j++) {
|
|
896
|
+
const up = row[j]!;
|
|
897
|
+
row[j] = Math.min(up + 1, row[j - 1]! + 1, diag + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
898
|
+
diag = up;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
return row[b.length]!;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/** Names a tool reads to probe an object (promise, JSON, test matchers, module interop): never an error. */
|
|
905
|
+
const PROBED_NAMES = new Set(['then', 'toJSON', 'asymmetricMatch', 'nodeType', 'tagName', 'inspect', 'default']);
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* A namespace whose unknown members throw instead of being `undefined`: `ta.smaa(close, 5)` fails with
|
|
909
|
+
* "ta.smaa does not exist. Did you mean ta.sma?" rather than "ta.smaa is not a function", and `color.purpel`
|
|
910
|
+
* fails where it is read rather than giving a plot with no colour.
|
|
911
|
+
*/
|
|
912
|
+
function strictNamespace<T extends object>(name: string, members: T): T {
|
|
913
|
+
return new Proxy(members, {
|
|
914
|
+
get(target, prop, receiver) {
|
|
915
|
+
if (typeof prop === 'symbol' || prop in target || PROBED_NAMES.has(prop) || /^[_$@]/.test(prop)) {
|
|
916
|
+
return Reflect.get(target, prop, receiver);
|
|
917
|
+
}
|
|
918
|
+
const keys = Object.keys(target);
|
|
919
|
+
const lower = prop.toLowerCase();
|
|
920
|
+
const ranked = keys
|
|
921
|
+
.map((k) => ({ k, d: editDistance(lower, k.toLowerCase()) }))
|
|
922
|
+
.filter(({ k, d }) => {
|
|
923
|
+
const key = k.toLowerCase();
|
|
924
|
+
return d <= Math.max(1, Math.floor(prop.length / 3)) || key.includes(lower) || (key.length >= 3 && lower.includes(key));
|
|
925
|
+
})
|
|
926
|
+
.sort((x, y) => x.d - y.d)
|
|
927
|
+
.slice(0, 3)
|
|
928
|
+
.map(({ k }) => `${name}.${k}`);
|
|
929
|
+
throw new TypeError(
|
|
930
|
+
`${name}.${prop} does not exist.` + (ranked.length ? ` Did you mean ${ranked.join(', ')}?` : '') +
|
|
931
|
+
' See docs/inventory.md for the supported functions.'
|
|
932
|
+
);
|
|
933
|
+
},
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* The ta.* functions compute a whole Series in one call. Called inside an eachBar() callback, a call runs again on
|
|
939
|
+
* every bar (slow) and returns a Series, not the value of the bar, and it has no per-bar history: the call throws
|
|
940
|
+
* with the fix.
|
|
941
|
+
*/
|
|
942
|
+
function outsideBarLoops<T extends Record<string, unknown>>(members: T): T {
|
|
943
|
+
const out: Record<string, unknown> = {};
|
|
944
|
+
for (const [key, value] of Object.entries(members)) {
|
|
945
|
+
if (typeof value !== 'function') {
|
|
946
|
+
out[key] = value;
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
const fn = value as (...args: unknown[]) => unknown;
|
|
950
|
+
out[key] = function (this: unknown, ...args: unknown[]) {
|
|
951
|
+
if (currentBar >= 0) {
|
|
952
|
+
throw new Error(
|
|
953
|
+
`ta.${key}() was called inside eachBar(): a ta.* call computes the whole Series at once. ` +
|
|
954
|
+
`Compute it before the loop (const x = ta.${key}(...)) and read the bar's value with c.get(x). ` +
|
|
955
|
+
'For a Pine ta.* call inside an if block or a for loop, use the callsite.* call sites.'
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
return fn.apply(this, args);
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
return out as T;
|
|
962
|
+
}
|
|
963
|
+
|
|
791
964
|
/** ta.* with the chart-implicit (bars-first) functions bound to the context,
|
|
792
965
|
* so scripts call them like PineScript: `ta.tr(true)`, `ta.atr(14)`. */
|
|
793
|
-
export const ta = {
|
|
966
|
+
export const ta = strictNamespace('ta', outsideBarLoops({
|
|
794
967
|
...taSeries,
|
|
795
968
|
tr: (handleNa: boolean = false): Series => taSeries.tr(bars(), handleNa),
|
|
796
969
|
atr: (length: number): Series => taSeries.atr(bars(), length),
|
|
@@ -838,6 +1011,8 @@ export const ta = {
|
|
|
838
1011
|
(source: Series, anchor?: Series): Series;
|
|
839
1012
|
(source: Series, anchor: Series | undefined, stdev_mult: number): [Series, Series, Series];
|
|
840
1013
|
},
|
|
1014
|
+
/** `ta.vwma(source, length)` with the chart volume, as PineScript; a third argument gives another volume. */
|
|
1015
|
+
vwma: (source: Series, length: number, vol?: Series): Series => taSeries.vwma(source, length, vol ?? volume),
|
|
841
1016
|
/** `ta.mfi(source, length)` with the chart volume (PineScript takes 2 arguments). */
|
|
842
1017
|
mfi: (source: Series, length: number): Series => taSeries.mfi(source, length, volume),
|
|
843
1018
|
/** `ta.pivothigh(source, leftbars, rightbars)` or `ta.pivothigh(leftbars, rightbars)` (source = high).
|
|
@@ -860,7 +1035,7 @@ export const ta = {
|
|
|
860
1035
|
iii: barsSeries(taSeries.iii),
|
|
861
1036
|
wad: barsSeries(taSeries.wad),
|
|
862
1037
|
wvad: barsSeries(taSeries.wvad),
|
|
863
|
-
};
|
|
1038
|
+
}));
|
|
864
1039
|
|
|
865
1040
|
/** PineScript `fixnan(source)`: each `na` value becomes the previous non-`na` value.
|
|
866
1041
|
* Accepts a Series or a per-bar color array (`undefined` = `na`, as returned by `color.when`). */
|
|
@@ -903,15 +1078,17 @@ function colorWhen(cond: Series, colorTrue: string, colorFalse?: string): Array<
|
|
|
903
1078
|
}
|
|
904
1079
|
|
|
905
1080
|
/** color.* plus `when` — per-bar conditional colors for plot()/fill()/bgcolor()/barcolor(). */
|
|
906
|
-
export const color = {
|
|
1081
|
+
export const color = strictNamespace('color', {
|
|
907
1082
|
...colorCore,
|
|
908
1083
|
new: colorCore.new_color,
|
|
909
1084
|
when: colorWhen,
|
|
910
|
-
};
|
|
1085
|
+
});
|
|
911
1086
|
|
|
912
1087
|
// ── Drawing declarations ─────────────────────────────────────────────────────
|
|
913
1088
|
|
|
914
1089
|
export interface ScriptPlotOptions {
|
|
1090
|
+
/** Shift in bars (PineScript `offset`): the value of bar i is drawn on bar i + offset (negative: to the left). */
|
|
1091
|
+
offset?: number;
|
|
915
1092
|
/** Static color, or a per-bar color array from color.when(). */
|
|
916
1093
|
color?: string | string[];
|
|
917
1094
|
linewidth?: number;
|
|
@@ -968,10 +1145,14 @@ function plotImpl(series: Series, title: string = DEFAULT_OUTPUT_TITLES.plot, op
|
|
|
968
1145
|
const values = series.toArray();
|
|
969
1146
|
const b = bars();
|
|
970
1147
|
const data: TimeValue[] = [];
|
|
971
|
-
|
|
1148
|
+
// offset (PineScript plot(offset = k)): the value and colour of bar i are drawn on bar i + k; bars past the
|
|
1149
|
+
// chart's first or last bar are dropped
|
|
1150
|
+
const off = Math.trunc(options.offset ?? 0);
|
|
1151
|
+
for (let j = Math.max(0, off); j < b.length && j - off < b.length; j++) {
|
|
1152
|
+
const i = j - off;
|
|
972
1153
|
const value = values[i];
|
|
973
1154
|
if (value === undefined || !Number.isFinite(value)) continue; // PineScript plots +/-Infinity as na
|
|
974
|
-
data.push(perBar ? { time: b[
|
|
1155
|
+
data.push(perBar ? { time: b[j]!.time, value, color: perBar[i] } : { time: b[j]!.time, value });
|
|
975
1156
|
}
|
|
976
1157
|
c.plots[id] = data;
|
|
977
1158
|
return { id, kind: 'plot' };
|
|
@@ -2316,7 +2497,8 @@ export const weekofyear = calendar(timeCore.weekofyear);
|
|
|
2316
2497
|
/** PineScript `dayofmonth(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
2317
2498
|
export const dayofmonth = calendar(timeCore.dayofmonth);
|
|
2318
2499
|
/** PineScript `dayofweek(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
2319
|
-
|
|
2500
|
+
/** `dayofweek(time, timezone?)`, with the PineScript constants `dayofweek.sunday` (1) ... `dayofweek.saturday` (7). */
|
|
2501
|
+
export const dayofweek = Object.assign(calendar(timeCore.dayofweek), pineConstants.daysOfWeek);
|
|
2320
2502
|
/** PineScript `hour(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
2321
2503
|
export const hour = calendar(timeCore.hour);
|
|
2322
2504
|
/** PineScript `minute(time, timezone?)`; the default time zone is the exchange time zone. */
|
|
@@ -2355,7 +2537,7 @@ export function inSession(time: number, session: string, timezone?: string): boo
|
|
|
2355
2537
|
}
|
|
2356
2538
|
|
|
2357
2539
|
/** PineScript `str.*`; `format_time` and the dates of `format` use the exchange time zone by default. */
|
|
2358
|
-
export const str = {
|
|
2540
|
+
export const str = strictNamespace('str', {
|
|
2359
2541
|
...strCore,
|
|
2360
2542
|
// format.mintick uses the chart symbol's tick and price scale (syminfo.mintick, syminfo.pricescale)
|
|
2361
2543
|
tostring: ((value: unknown, format?: string, mintick?: number, pricescale?: number) =>
|
|
@@ -2368,7 +2550,7 @@ export const str = {
|
|
|
2368
2550
|
format(pattern: string, ...args: unknown[]): string {
|
|
2369
2551
|
return formatMessage(pattern, args, exchangeTimezone);
|
|
2370
2552
|
},
|
|
2371
|
-
};
|
|
2553
|
+
});
|
|
2372
2554
|
|
|
2373
2555
|
// ── Session values (issue #100) ──────────────────────────────────────────────
|
|
2374
2556
|
|
|
@@ -2811,60 +2993,249 @@ function security_lower_tf<T extends SecurityValue>(
|
|
|
2811
2993
|
}
|
|
2812
2994
|
|
|
2813
2995
|
/** PineScript `request.*`: `security` (issue #101, other symbols #179, lower timeframes #181) and `security_lower_tf`. */
|
|
2814
|
-
|
|
2996
|
+
/** PineScript parameters of `request.security` / `request.security_lower_tf`, in their positional order. */
|
|
2997
|
+
const SECURITY_PARAMETERS = ['symbol', 'timeframe', 'expression', 'gaps', 'lookahead', 'ignore_invalid_symbol', 'currency', 'calc_bars_count'] as const;
|
|
2998
|
+
const SECURITY_LOWER_TF_PARAMETERS = ['symbol', 'timeframe', 'expression', 'ignore_invalid_symbol', 'currency', 'ignore_invalid_timeframe', 'calc_bars_count'] as const;
|
|
2999
|
+
|
|
3000
|
+
/** Named arguments of `request.security` (PineScript names). */
|
|
3001
|
+
export interface SecurityArgs<T extends SecurityValue = SecurityValue> {
|
|
3002
|
+
symbol?: string;
|
|
3003
|
+
timeframe?: string;
|
|
3004
|
+
expression?: () => T;
|
|
3005
|
+
gaps?: string;
|
|
3006
|
+
lookahead?: string;
|
|
3007
|
+
ignore_invalid_symbol?: boolean;
|
|
3008
|
+
currency?: string;
|
|
3009
|
+
calc_bars_count?: number;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
/** Named arguments of `request.security_lower_tf` (PineScript names). */
|
|
3013
|
+
export interface SecurityLowerTfArgs<T extends SecurityValue = SecurityValue> {
|
|
3014
|
+
symbol?: string;
|
|
3015
|
+
timeframe?: string;
|
|
3016
|
+
expression?: () => T;
|
|
3017
|
+
ignore_invalid_symbol?: boolean;
|
|
3018
|
+
currency?: string;
|
|
3019
|
+
ignore_invalid_timeframe?: boolean;
|
|
3020
|
+
calc_bars_count?: number;
|
|
3021
|
+
}
|
|
3022
|
+
|
|
3023
|
+
/** `currency` and `calc_bars_count` change the values in PineScript: they throw instead of being ignored. */
|
|
3024
|
+
function unsupportedSecurityArgs(name: string, currency: unknown, calcBarsCount: unknown): void {
|
|
3025
|
+
if (currency !== undefined && currency !== '' && currency !== 'NONE') {
|
|
3026
|
+
throw new Error(`${name}(): the currency argument (conversion of the values) is not supported.`);
|
|
3027
|
+
}
|
|
3028
|
+
if (calcBarsCount !== undefined) throw new Error(`${name}(): the calc_bars_count argument is not supported.`);
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
/** `request.security` with PineScript's named arguments: `request.security(sym, "D", () => close, { lookahead: barmerge.lookahead_on })`. */
|
|
3032
|
+
function securityCall<T extends SecurityValue>(
|
|
3033
|
+
...args:
|
|
3034
|
+
| [symbol: string, timeframe: string, expression: () => T, gaps?: string, lookahead?: string, ignore_invalid_symbol?: boolean]
|
|
3035
|
+
| [symbol: string, timeframe: string, expression: () => T, args: SecurityArgs<T>]
|
|
3036
|
+
| [args: SecurityArgs<T> & { symbol: string; timeframe: string; expression: () => T }]
|
|
3037
|
+
): T extends unknown[] ? Series[] : Series {
|
|
3038
|
+
const a = namedArguments('request.security', SECURITY_PARAMETERS, args);
|
|
3039
|
+
unsupportedSecurityArgs('request.security', a[6], a[7]);
|
|
3040
|
+
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);
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
/** `request.security_lower_tf` with PineScript's named arguments. */
|
|
3044
|
+
function securityLowerTfCall<T extends SecurityValue>(
|
|
3045
|
+
...args:
|
|
3046
|
+
| [symbol: string, timeframe: string, expression: () => T, ignore_invalid_symbol?: boolean]
|
|
3047
|
+
| [symbol: string, timeframe: string, expression: () => T, args: SecurityLowerTfArgs<T>]
|
|
3048
|
+
| [args: SecurityLowerTfArgs<T> & { symbol: string; timeframe: string; expression: () => T }]
|
|
3049
|
+
): T extends unknown[] ? number[][][] : number[][] {
|
|
3050
|
+
const a = namedArguments('request.security_lower_tf', SECURITY_LOWER_TF_PARAMETERS, args);
|
|
3051
|
+
unsupportedSecurityArgs('request.security_lower_tf', a[4], a[6]);
|
|
3052
|
+
if (a[5] !== undefined && a[5] !== false) throw new Error('request.security_lower_tf(): the ignore_invalid_timeframe argument is not supported.');
|
|
3053
|
+
return security_lower_tf(a[0] as string, a[1] as string, a[2] as () => T, a[3] as boolean | undefined);
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
export const request = strictNamespace('request', { security: securityCall, security_lower_tf: securityLowerTfCall });
|
|
2815
3057
|
|
|
2816
3058
|
// ── Drawing objects ──────────────────────────────────────────────────────────
|
|
2817
3059
|
// The drawing namespaces come from this bundle so that they share the registry that
|
|
2818
3060
|
// executeScript() resets. `line.all` etc. are read like PineScript variables.
|
|
2819
3061
|
|
|
2820
3062
|
/** PineScript `line.*`; `line.all` lists the live lines in creation order. */
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
3063
|
+
// ── Drawing namespaces (named arguments, Series arguments in bar loops) ──────
|
|
3064
|
+
|
|
3065
|
+
type Numeric = number | Series;
|
|
3066
|
+
/** A function whose number parameters also take a Series (read on the current bar inside a bar loop). */
|
|
3067
|
+
type WithSeries<F> = F extends (...args: infer A) => infer R
|
|
3068
|
+
? (...args: { [K in keyof A]: number extends A[K] ? A[K] | Series : A[K] }) => R
|
|
3069
|
+
: never;
|
|
3070
|
+
type ScriptDrawing<T> = { [K in keyof T]: T[K] extends (...args: never[]) => unknown ? T[K] & WithSeries<T[K]> : T[K] };
|
|
3071
|
+
type Colour = string | undefined;
|
|
3072
|
+
|
|
3073
|
+
/** Named arguments of `label.new` (PineScript parameter names). */
|
|
3074
|
+
export interface LabelNewArgs {
|
|
3075
|
+
x?: Numeric;
|
|
3076
|
+
y?: Numeric;
|
|
3077
|
+
point?: ChartPoint;
|
|
3078
|
+
text?: string;
|
|
3079
|
+
xloc?: 'bar_index' | 'bar_time';
|
|
3080
|
+
yloc?: 'price' | 'abovebar' | 'belowbar';
|
|
3081
|
+
color?: Colour;
|
|
3082
|
+
style?: NonNullable<Label['style']>;
|
|
3083
|
+
textcolor?: Colour;
|
|
3084
|
+
size?: string | number;
|
|
3085
|
+
textalign?: 'left' | 'center' | 'right';
|
|
3086
|
+
tooltip?: string;
|
|
3087
|
+
text_font_family?: 'default' | 'monospace';
|
|
3088
|
+
force_overlay?: boolean;
|
|
3089
|
+
text_formatting?: number;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
/** Named arguments of `line.new`. */
|
|
3093
|
+
export interface LineNewArgs {
|
|
3094
|
+
x1?: Numeric;
|
|
3095
|
+
y1?: Numeric;
|
|
3096
|
+
x2?: Numeric;
|
|
3097
|
+
y2?: Numeric;
|
|
3098
|
+
first_point?: ChartPoint;
|
|
3099
|
+
second_point?: ChartPoint;
|
|
3100
|
+
xloc?: 'bar_index' | 'bar_time';
|
|
3101
|
+
extend?: 'none' | 'left' | 'right' | 'both';
|
|
3102
|
+
color?: Colour;
|
|
3103
|
+
style?: 'solid' | 'dotted' | 'dashed' | 'arrow_left' | 'arrow_right' | 'arrow_both';
|
|
3104
|
+
width?: Numeric;
|
|
3105
|
+
force_overlay?: boolean;
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
/** Named arguments of `box.new`. */
|
|
3109
|
+
export interface BoxNewArgs {
|
|
3110
|
+
left?: Numeric;
|
|
3111
|
+
top?: Numeric;
|
|
3112
|
+
right?: Numeric;
|
|
3113
|
+
bottom?: Numeric;
|
|
3114
|
+
top_left?: ChartPoint;
|
|
3115
|
+
bottom_right?: ChartPoint;
|
|
3116
|
+
border_color?: Colour;
|
|
3117
|
+
border_width?: Numeric;
|
|
3118
|
+
border_style?: 'solid' | 'dotted' | 'dashed';
|
|
3119
|
+
extend?: 'none' | 'left' | 'right' | 'both';
|
|
3120
|
+
xloc?: 'bar_index' | 'bar_time';
|
|
3121
|
+
bgcolor?: Colour;
|
|
3122
|
+
text?: string;
|
|
3123
|
+
text_size?: string | number;
|
|
3124
|
+
text_color?: Colour;
|
|
3125
|
+
text_halign?: 'left' | 'center' | 'right';
|
|
3126
|
+
text_valign?: 'top' | 'center' | 'bottom';
|
|
3127
|
+
text_wrap?: 'none' | 'auto';
|
|
3128
|
+
text_font_family?: 'default' | 'monospace';
|
|
3129
|
+
force_overlay?: boolean;
|
|
3130
|
+
text_formatting?: number;
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
/** Named arguments of `table.new`. */
|
|
3134
|
+
export interface TableNewArgs {
|
|
3135
|
+
position?: string;
|
|
3136
|
+
columns?: number;
|
|
3137
|
+
rows?: number;
|
|
3138
|
+
bgcolor?: Colour;
|
|
3139
|
+
frame_color?: Colour;
|
|
3140
|
+
frame_width?: number;
|
|
3141
|
+
border_color?: Colour;
|
|
3142
|
+
border_width?: number;
|
|
3143
|
+
force_overlay?: boolean;
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
/** Named arguments of `table.cell`. */
|
|
3147
|
+
export interface TableCellArgs {
|
|
3148
|
+
table_id?: Table;
|
|
3149
|
+
column?: number;
|
|
3150
|
+
row?: number;
|
|
3151
|
+
text?: string;
|
|
3152
|
+
width?: number;
|
|
3153
|
+
height?: number;
|
|
3154
|
+
text_color?: Colour;
|
|
3155
|
+
text_halign?: 'left' | 'center' | 'right';
|
|
3156
|
+
text_valign?: 'top' | 'center' | 'bottom';
|
|
3157
|
+
text_size?: string | number;
|
|
3158
|
+
bgcolor?: Colour;
|
|
3159
|
+
tooltip?: string;
|
|
3160
|
+
text_font_family?: 'default' | 'monospace';
|
|
3161
|
+
text_formatting?: number;
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
/** Named arguments of `polyline.new`. */
|
|
3165
|
+
export interface PolylineNewArgs {
|
|
3166
|
+
points?: ChartPoint[];
|
|
3167
|
+
curved?: boolean;
|
|
3168
|
+
closed?: boolean;
|
|
3169
|
+
xloc?: 'bar_index' | 'bar_time';
|
|
3170
|
+
line_color?: Colour;
|
|
3171
|
+
fill_color?: Colour | null;
|
|
3172
|
+
line_style?: 'solid' | 'dotted' | 'dashed';
|
|
3173
|
+
line_width?: number;
|
|
3174
|
+
force_overlay?: boolean;
|
|
3175
|
+
}
|
|
3176
|
+
|
|
3177
|
+
/** Named arguments of `linefill.new`. */
|
|
3178
|
+
export interface LinefillNewArgs {
|
|
3179
|
+
line1?: Line;
|
|
3180
|
+
line2?: Line;
|
|
3181
|
+
color?: Colour;
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3184
|
+
const loopBar = (): number => currentBar;
|
|
3185
|
+
|
|
3186
|
+
/** Wraps a drawing namespace and keeps its `all` getter (the live drawings in creation order). */
|
|
3187
|
+
function drawings<T extends Record<string, unknown>, D>(name: string, members: T, all: () => D[]) {
|
|
3188
|
+
const out = drawingNamespace(name, members, loopBar) as ScriptDrawing<T>;
|
|
3189
|
+
return Object.defineProperty(out, 'all', { get: all, enumerable: true }) as Omit<ScriptDrawing<T>, 'all'> & { readonly all: D[] };
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
const lineNs = drawings('line', { ...lineCore, ...pineConstants.lineStyles }, () => lineCore.all());
|
|
3193
|
+
/** PineScript `line.*` (with `line.style_*`); `line.all` lists the live lines in creation order. */
|
|
3194
|
+
export const line = lineNs as typeof lineNs & {
|
|
3195
|
+
new: ((args: LineNewArgs) => Line) &
|
|
3196
|
+
((x1: Numeric, y1: Numeric, x2: Numeric, y2: Numeric, args: LineNewArgs) => Line) &
|
|
3197
|
+
((first_point: ChartPoint, second_point: ChartPoint, args: LineNewArgs) => Line);
|
|
2826
3198
|
};
|
|
2827
3199
|
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
3200
|
+
const labelNs = drawings('label', { ...labelCore, ...pineConstants.labelStyles }, () => labelCore.all());
|
|
3201
|
+
/** PineScript `label.*` (with `label.style_*`); `label.all` lists the live labels in creation order. */
|
|
3202
|
+
export const label = labelNs as typeof labelNs & {
|
|
3203
|
+
new: ((args: LabelNewArgs) => Label) &
|
|
3204
|
+
((x: Numeric, y: Numeric, args: LabelNewArgs) => Label) &
|
|
3205
|
+
((x: Numeric, y: Numeric, text: string, args: LabelNewArgs) => Label) &
|
|
3206
|
+
((point: ChartPoint, args: LabelNewArgs) => Label) &
|
|
3207
|
+
((point: ChartPoint, text: string, args: LabelNewArgs) => Label);
|
|
2834
3208
|
};
|
|
2835
3209
|
|
|
3210
|
+
const boxNs = drawings('box', { ...boxCore }, () => boxCore.all());
|
|
2836
3211
|
/** PineScript `box.*`; `box.all` lists the live boxes in creation order. */
|
|
2837
|
-
export const box = {
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
},
|
|
3212
|
+
export const box = boxNs as typeof boxNs & {
|
|
3213
|
+
new: ((args: BoxNewArgs) => Box) &
|
|
3214
|
+
((left: Numeric, top: Numeric, right: Numeric, bottom: Numeric, args: BoxNewArgs) => Box) &
|
|
3215
|
+
((top_left: ChartPoint, bottom_right: ChartPoint, args: BoxNewArgs) => Box);
|
|
2842
3216
|
};
|
|
2843
3217
|
|
|
3218
|
+
const linefillNs = drawings('linefill', { ...linefillCore }, () => linefillCore.all());
|
|
2844
3219
|
/** PineScript `linefill.*`; `linefill.all` lists the live linefills in creation order. */
|
|
2845
|
-
export const linefill = {
|
|
2846
|
-
|
|
2847
|
-
get all() {
|
|
2848
|
-
return linefillCore.all();
|
|
2849
|
-
},
|
|
3220
|
+
export const linefill = linefillNs as typeof linefillNs & {
|
|
3221
|
+
new: ((args: LinefillNewArgs) => Linefill) & ((line1: Line, line2: Line, args: LinefillNewArgs) => Linefill);
|
|
2850
3222
|
};
|
|
2851
3223
|
|
|
3224
|
+
const polylineNs = drawings('polyline', { ...polylineCore }, () => polylineCore.all());
|
|
2852
3225
|
/** PineScript `polyline.*`; `polyline.all` lists the live polylines in creation order. */
|
|
2853
|
-
export const polyline = {
|
|
2854
|
-
|
|
2855
|
-
get all() {
|
|
2856
|
-
return polylineCore.all();
|
|
2857
|
-
},
|
|
3226
|
+
export const polyline = polylineNs as typeof polylineNs & {
|
|
3227
|
+
new: ((args: PolylineNewArgs) => Polyline) & ((points: ChartPoint[], args: PolylineNewArgs) => Polyline);
|
|
2858
3228
|
};
|
|
2859
3229
|
|
|
3230
|
+
const tableNs = drawings('table', { ...tableCore }, () => tableCore.all());
|
|
2860
3231
|
/**
|
|
2861
3232
|
* PineScript `table.*`; `table.all` lists the live tables in creation order. The live tables go to `result.tables`.
|
|
2862
3233
|
*/
|
|
2863
|
-
export const table = {
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
3234
|
+
export const table = tableNs as typeof tableNs & {
|
|
3235
|
+
new: ((args: TableNewArgs) => Table) & ((position: string, columns: number, rows: number, args: TableNewArgs) => Table);
|
|
3236
|
+
cell: ((args: TableCellArgs) => void) &
|
|
3237
|
+
((table_id: Table, column: number, row: number, args: TableCellArgs) => void) &
|
|
3238
|
+
((table_id: Table, column: number, row: number, text: string, args: TableCellArgs) => void);
|
|
2868
3239
|
};
|
|
2869
3240
|
|
|
2870
3241
|
/** PineScript `position.*` constants (table positions). */
|
|
@@ -2873,6 +3244,11 @@ export { position };
|
|
|
2873
3244
|
/** PineScript `text.format_*`, `text.align_*` and `text.wrap_*` constants. */
|
|
2874
3245
|
export const text = { ...textCore };
|
|
2875
3246
|
|
|
3247
|
+
/** PineScript constant namespaces (`shape.triangleup`, `size.small`, `xloc.bar_time`, `extend.right`...): the strings
|
|
3248
|
+
* the functions take. */
|
|
3249
|
+
export const { shape, location, size, xloc, yloc, extend, display, format, font, scale, order, currency, adjustment } =
|
|
3250
|
+
pineConstants;
|
|
3251
|
+
|
|
2876
3252
|
/**
|
|
2877
3253
|
* PineScript `chart.point.*`. `chart.point.now(price?)` is the point of the current bar
|
|
2878
3254
|
* (index, time, and `price`, default the close), so it runs inside eachBar().
|
|
@@ -2984,13 +3360,13 @@ export type {
|
|
|
2984
3360
|
const { saveRandom: _saveRandom, restoreRandom: _restoreRandom, ...mathScript } = mathCore;
|
|
2985
3361
|
void _saveRandom;
|
|
2986
3362
|
void _restoreRandom;
|
|
2987
|
-
export const math = {
|
|
3363
|
+
export const math = strictNamespace('math', {
|
|
2988
3364
|
...mathScript,
|
|
2989
3365
|
round_to_mintick: ((number: number | Series, mintick?: number) => {
|
|
2990
3366
|
const tick = mintick ?? syminfo.mintick;
|
|
2991
3367
|
return number instanceof Series ? mathCore.round_to_mintick(number, tick) : mathCore.round_to_mintick(number, tick);
|
|
2992
3368
|
}) as typeof mathCore.round_to_mintick,
|
|
2993
|
-
};
|
|
3369
|
+
});
|
|
2994
3370
|
export * as compare from '../compare/index.js';
|
|
2995
3371
|
export { isInputActive } from '../runtime/input-active.js';
|
|
2996
3372
|
export type {
|