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.
Files changed (119) hide show
  1. package/AGENTS.md +6 -1
  2. package/README.md +1 -1
  3. package/bin/oakscript.mjs +5 -0
  4. package/dist/cjs/check/checks.d.ts +78 -0
  5. package/dist/cjs/check/checks.d.ts.map +1 -0
  6. package/dist/cjs/check/checks.js +109 -0
  7. package/dist/cjs/check/checks.js.map +7 -0
  8. package/dist/cjs/check/format.d.ts +26 -0
  9. package/dist/cjs/check/format.d.ts.map +1 -0
  10. package/dist/cjs/check/format.js +106 -0
  11. package/dist/cjs/check/format.js.map +7 -0
  12. package/dist/cjs/check/harness.d.ts +94 -0
  13. package/dist/cjs/check/harness.d.ts.map +1 -0
  14. package/dist/cjs/check/harness.js +302 -0
  15. package/dist/cjs/check/harness.js.map +7 -0
  16. package/dist/cjs/check/index.d.ts +253 -0
  17. package/dist/cjs/check/index.d.ts.map +1 -0
  18. package/dist/cjs/check/index.js +466 -0
  19. package/dist/cjs/check/index.js.map +7 -0
  20. package/dist/cjs/check/node.d.ts +53 -0
  21. package/dist/cjs/check/node.d.ts.map +1 -0
  22. package/dist/cjs/check/node.js +318 -0
  23. package/dist/cjs/check/node.js.map +7 -0
  24. package/dist/cjs/check/spec.d.ts +44 -0
  25. package/dist/cjs/check/spec.d.ts.map +1 -0
  26. package/dist/cjs/check/spec.js +199 -0
  27. package/dist/cjs/check/spec.js.map +7 -0
  28. package/dist/cjs/index.d.ts +1 -1
  29. package/dist/cjs/index.js +1 -1
  30. package/dist/cjs/index.js.map +1 -1
  31. package/dist/cjs/runtime/series.d.ts +6 -0
  32. package/dist/cjs/runtime/series.d.ts.map +1 -1
  33. package/dist/cjs/runtime/series.js +15 -0
  34. package/dist/cjs/runtime/series.js.map +2 -2
  35. package/dist/cjs/script/constants.d.ts +200 -0
  36. package/dist/cjs/script/constants.d.ts.map +1 -0
  37. package/dist/cjs/script/constants.js +217 -0
  38. package/dist/cjs/script/constants.js.map +7 -0
  39. package/dist/cjs/script/drawing-args.d.ts +25 -0
  40. package/dist/cjs/script/drawing-args.d.ts.map +1 -0
  41. package/dist/cjs/script/drawing-args.js +203 -0
  42. package/dist/cjs/script/drawing-args.js.map +7 -0
  43. package/dist/cjs/script/globals.d.ts +127 -0
  44. package/dist/cjs/script/globals.d.ts.map +1 -0
  45. package/dist/cjs/script/globals.js +17 -0
  46. package/dist/cjs/script/globals.js.map +7 -0
  47. package/dist/cjs/script/index.d.ts +499 -166
  48. package/dist/cjs/script/index.d.ts.map +1 -1
  49. package/dist/cjs/script/index.js +236 -96
  50. package/dist/cjs/script/index.js.map +3 -3
  51. package/dist/esm/check/checks.d.ts +78 -0
  52. package/dist/esm/check/checks.d.ts.map +1 -0
  53. package/dist/esm/check/checks.js +95 -0
  54. package/dist/esm/check/checks.js.map +1 -0
  55. package/dist/esm/check/format.d.ts +26 -0
  56. package/dist/esm/check/format.d.ts.map +1 -0
  57. package/dist/esm/check/format.js +105 -0
  58. package/dist/esm/check/format.js.map +1 -0
  59. package/dist/esm/check/harness.d.ts +94 -0
  60. package/dist/esm/check/harness.d.ts.map +1 -0
  61. package/dist/esm/check/harness.js +317 -0
  62. package/dist/esm/check/harness.js.map +1 -0
  63. package/dist/esm/check/index.d.ts +253 -0
  64. package/dist/esm/check/index.d.ts.map +1 -0
  65. package/dist/esm/check/index.js +470 -0
  66. package/dist/esm/check/index.js.map +1 -0
  67. package/dist/esm/check/node.d.ts +53 -0
  68. package/dist/esm/check/node.d.ts.map +1 -0
  69. package/dist/esm/check/node.js +347 -0
  70. package/dist/esm/check/node.js.map +1 -0
  71. package/dist/esm/check/spec.d.ts +44 -0
  72. package/dist/esm/check/spec.d.ts.map +1 -0
  73. package/dist/esm/check/spec.js +198 -0
  74. package/dist/esm/check/spec.js.map +1 -0
  75. package/dist/esm/index.d.ts +1 -1
  76. package/dist/esm/index.js +1 -1
  77. package/dist/esm/runtime/series.d.ts +6 -0
  78. package/dist/esm/runtime/series.d.ts.map +1 -1
  79. package/dist/esm/runtime/series.js +14 -0
  80. package/dist/esm/runtime/series.js.map +1 -1
  81. package/dist/esm/script/constants.d.ts +200 -0
  82. package/dist/esm/script/constants.d.ts.map +1 -0
  83. package/dist/esm/script/constants.js +151 -0
  84. package/dist/esm/script/constants.js.map +1 -0
  85. package/dist/esm/script/drawing-args.d.ts +25 -0
  86. package/dist/esm/script/drawing-args.d.ts.map +1 -0
  87. package/dist/esm/script/drawing-args.js +171 -0
  88. package/dist/esm/script/drawing-args.js.map +1 -0
  89. package/dist/esm/script/globals.d.ts +127 -0
  90. package/dist/esm/script/globals.d.ts.map +1 -0
  91. package/dist/esm/script/globals.js +28 -0
  92. package/dist/esm/script/globals.js.map +1 -0
  93. package/dist/esm/script/index.d.ts +499 -166
  94. package/dist/esm/script/index.d.ts.map +1 -1
  95. package/dist/esm/script/index.js +256 -83
  96. package/dist/esm/script/index.js.map +1 -1
  97. package/package.json +31 -1
  98. package/skills/oakscript/SKILL.md +62 -26
  99. package/skills/oakscript/examples/ma-cross.js +2 -2
  100. package/skills/oakscript/examples/mtf-trend-filter.js +3 -3
  101. package/skills/oakscript/reference/check.md +150 -0
  102. package/skills/oakscript/reference/errors.md +31 -7
  103. package/skills/oakscript/reference/pine-differences.md +17 -16
  104. package/skills/oakscript/reference/plotting.md +25 -19
  105. package/skills/oakscript/reference/script-structure.md +8 -5
  106. package/skills/oakscript/reference/timeframes-symbols.md +5 -1
  107. package/skills/oakscript/spec.schema.json +199 -0
  108. package/src/check/checks.ts +175 -0
  109. package/src/check/format.ts +117 -0
  110. package/src/check/harness.ts +372 -0
  111. package/src/check/index.ts +708 -0
  112. package/src/check/node.ts +368 -0
  113. package/src/check/spec.ts +200 -0
  114. package/src/index.ts +1 -1
  115. package/src/runtime/series.ts +19 -0
  116. package/src/script/constants.ts +169 -0
  117. package/src/script/drawing-args.ts +179 -0
  118. package/src/script/globals.ts +129 -0
  119. package/src/script/index.ts +457 -81
@@ -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: true,
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 ?? true;
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
- /** PineScript `input.*` — declares an input AND returns its current value. */
692
- export const input = {
693
- int(defval: number, title?: string, opts: NumericInputOptions = {}): number {
694
- return Math.round(registerNumeric('int', defval, title, opts));
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: NumericInputOptions = {}): number {
697
- return registerNumeric('float', defval, title, opts);
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: InputOptions = {}): boolean {
700
- return registerInput<boolean>({ type: 'bool', defval, title, ...common(opts) }, 'input_bool');
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: StringInputOptions = {}): string {
703
- return registerInput<string>({ type: 'string', defval, title, options: opts.options, ...common(opts) }, 'input_string');
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
- " between the lines), e.g. a list a script parses (#184). */
707
- text_area(defval: string, title?: string, opts: InputOptions = {}): string {
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: InputOptions = {}): string {
715
- return registerInput<string>({ type: 'color', defval: inputColorDefault(defval), title, ...common(opts) }, 'input_color');
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: InputOptions = {}): Series {
719
- const name = registerInput<string>({ type: 'source', defval, title, ...common(opts) }, 'input_source');
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: StringInputOptions = {}): string {
724
- return registerInput<string>(
725
- { type: 'timeframe', defval, title, options: opts.options, ...common(opts) },
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: StringInputOptions = {}): string {
731
- return registerInput<string>(
732
- { type: 'session', defval, title, options: opts.options, ...common(opts) },
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: StringInputOptions = {}): string {
738
- return registerInput<string>({ type: 'symbol', defval, title, options: opts.options, ...common(opts) }, 'input_symbol');
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: InputOptions = {}): number {
742
- const v = registerInput<unknown>({ type: 'time', defval, title, ...common(opts) }, 'input_time');
743
- return typeof v === 'number' ? v : defval;
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
- for (let i = 0; i < b.length; i++) {
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[i]!.time, value, color: perBar[i] } : { time: b[i]!.time, value });
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
- export const dayofweek = calendar(timeCore.dayofweek);
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
- export const request = { security, security_lower_tf };
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
- export const line = {
2822
- ...lineCore,
2823
- get all() {
2824
- return lineCore.all();
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
- /** PineScript `label.*`; `label.all` lists the live labels in creation order. */
2829
- export const label = {
2830
- ...labelCore,
2831
- get all() {
2832
- return labelCore.all();
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
- ...boxCore,
2839
- get all() {
2840
- return boxCore.all();
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
- ...linefillCore,
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
- ...polylineCore,
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
- ...tableCore,
2865
- get all() {
2866
- return tableCore.all();
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 {