oakscriptjs 0.13.2 → 0.14.1

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