pinets 0.9.14 → 0.9.16

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.
@@ -66,6 +66,8 @@ export declare class Context {
66
66
  sDate: number;
67
67
  eDate: number;
68
68
  fullContext: Context;
69
+ viewportLeft: number | undefined;
70
+ viewportRight: number | undefined;
69
71
  pineTSCode: Function | String;
70
72
  inputs: Record<string, any>;
71
73
  constructor({ marketData, source, tickerId, timeframe, limit, sDate, eDate, fullContext, inputs, }: {
@@ -144,12 +146,25 @@ export declare class Context {
144
146
  */
145
147
  iter(source: any): any;
146
148
  /**
147
- * Resolve an iterable yielding [index, value] tuples for `for [i, x] in collection`
149
+ * Resolve an iterable yielding [key, value] tuples for `for [k, v] in collection`
148
150
  * destructuring codegen. PineArrayObject's [Symbol.iterator] yields scalar values, so
149
- * we must explicitly call `.entries()` on the underlying array.
151
+ * we must explicitly call `.entries()` on the underlying array. PineMapObject stores
152
+ * its data on `.map` (a JS Map) — without this branch the fallthrough returned an
153
+ * empty iterator and `for [k,v] in map` silently iterated 0 times.
150
154
  */
151
- entries(source: any): IterableIterator<[number, any]>;
155
+ entries(source: any): IterableIterator<[any, any]>;
152
156
  private _callStack;
157
+ /**
158
+ * Cumulative call-path stack. Each entry is the full path from the root to
159
+ * the current call, formed by joining the syntactic call-site ids with '|'.
160
+ *
161
+ * Pine semantics is per-call-PATH (not per-syntactic-call-site): a function
162
+ * with internal `var` state, called via two distinct paths through a wrapper,
163
+ * must keep state independent per path. Keying lctx by the path (rather than
164
+ * the immediate site id) makes `$$.var.*` slots and `$$.id + '_taN'` ta
165
+ * callsite ids correctly path-scoped without any transpiler changes.
166
+ */
167
+ private _pathStack;
153
168
  /**
154
169
  * Pushes a call ID onto the stack
155
170
  * @param id - The call ID
@@ -160,7 +175,8 @@ export declare class Context {
160
175
  */
161
176
  popId(): void;
162
177
  /**
163
- * Returns the current call ID from the top of the stack
178
+ * Returns the current call PATH (cumulative ids joined by '|') from the top
179
+ * of the stack. Used as the lctx key for the current function call.
164
180
  */
165
181
  peekId(): string;
166
182
  /**
@@ -57,6 +57,56 @@ export declare class PineTS {
57
57
  * @param mode Alert firing mode
58
58
  */
59
59
  setAlertMode(mode: 'realtime' | 'all'): void;
60
+ private _viewportLeft;
61
+ private _viewportRight;
62
+ private _usesVisibleRange;
63
+ private _lastRunViewport;
64
+ private _lastResult;
65
+ private _lastPineTSCode;
66
+ /**
67
+ * Set the visible range of bars from the host (e.g. chart UI viewport).
68
+ * Affects `chart.left_visible_bar_time` and `chart.right_visible_bar_time`.
69
+ * Defaults derive from `marketData[0]/[last].openTime` if never called.
70
+ *
71
+ * The setter only stores values; it does NOT trigger a re-run. Call
72
+ * `update()` afterwards to apply the change. For scripts that don't
73
+ * reference visible-range built-ins, `update()` is a no-op.
74
+ *
75
+ * @param left openTime of the leftmost visible bar
76
+ * @param right openTime of the rightmost visible bar
77
+ */
78
+ setVisibleRange(left: number, right: number): void;
79
+ /**
80
+ * Whether the loaded script references any visible-range built-in
81
+ * (e.g. `chart.left_visible_bar_time`). Detected statically during
82
+ * transpile. Consumers fanning viewport changes across many indicators
83
+ * should skip non-tagged instances to avoid unnecessary re-runs.
84
+ */
85
+ usesVisibleRange(): boolean;
86
+ /** Current viewport left (undefined if setter never called). */
87
+ get visibleRangeLeft(): number | undefined;
88
+ /** Current viewport right (undefined if setter never called). */
89
+ get visibleRangeRight(): number | undefined;
90
+ /**
91
+ * Smart re-run: executes `run()` only if a re-run is actually needed.
92
+ *
93
+ * - First call: behaves like `run()` (always executes).
94
+ * - Subsequent calls: returns the cached previous result UNLESS the script
95
+ * is viewport-dependent (`usesVisibleRange()`) AND the viewport has
96
+ * changed since the last cached run.
97
+ *
98
+ * The typical pattern for a chart consumer with multiple indicators:
99
+ *
100
+ * // user pans the chart
101
+ * for (const p of indicators) {
102
+ * p.setVisibleRange(left, right);
103
+ * await p.update(code); // no-op for non-viewport indicators
104
+ * }
105
+ *
106
+ * The pineTSCode argument is optional after the first call — the same code
107
+ * is reused. Pass it again only when the script source itself has changed.
108
+ */
109
+ update(pineTSCode?: Indicator | Function | String): Promise<Context>;
60
110
  constructor(source: IProvider | any[], tickerId?: string, timeframe?: string, limit?: number, sDate?: number, eDate?: number);
61
111
  setDebugSettings({ ln, debug }: {
62
112
  ln: boolean;
@@ -98,6 +148,16 @@ export declare class PineTS {
98
148
  * Run the script completely and return the final context (backward compatible behavior)
99
149
  * @private
100
150
  */
151
+ /**
152
+ * Run an already-transpiled PineTS function in this instance — no
153
+ * additional transpile/parse pass. Used by `request.security_lower_tf`'s
154
+ * slow path to execute the slice produced at primary-transpile time
155
+ * (a truncated body containing only the prefix up to the call). The
156
+ * caller is responsible for ensuring `transpiledFn` was produced by
157
+ * this transpiler against the same source — calling this with an
158
+ * arbitrary function is unsafe.
159
+ */
160
+ runPretranspiled(transpiledFn: Function, inputs?: Record<string, any>, periods?: number): Promise<Context>;
101
161
  private _runComplete;
102
162
  /**
103
163
  * Run the script with pagination, yielding results page by page
@@ -186,6 +246,21 @@ export declare class PineTS {
186
246
  * @private
187
247
  */
188
248
  private _transpileCode;
249
+ /**
250
+ * Static analysis on the transpiled function body to detect references to
251
+ * host-bound built-ins (currently visible-range; extensible via
252
+ * VIEWPORT_DEPENDENT_BUILTINS). Comments are stripped during pine2js, so
253
+ * scanning the post-transpile output is comment-safe.
254
+ *
255
+ * Why post-transpile (not regex on Pine source): a `chart.left_visible_bar_time`
256
+ * literal inside a // comment would be a false positive at the source level.
257
+ * After pine2js, only live code remains.
258
+ *
259
+ * Why regex (not AST visitor): `chart` is a reserved namespace in
260
+ * KNOWN_NAMESPACES — Pine scripts cannot shadow it with a local identifier,
261
+ * so a whole-word match on `chart.<prop>` is unambiguous.
262
+ */
263
+ private _detectViewportUsage;
189
264
  /**
190
265
  * Execute iterations from startIdx to endIdx, updating the context
191
266
  * @private
@@ -1,4 +1,3 @@
1
- import { PineTypeObject } from './PineTypeObject';
2
1
  import type { IndicatorOptions } from '../types/PineTypes';
3
2
  export declare function parseIndicatorOptions(args: any[]): Partial<IndicatorOptions>;
4
3
  /**
@@ -84,8 +83,5 @@ export declare class Core {
84
83
  int(series: any): number;
85
84
  float(series: any): number;
86
85
  string(series: any): any;
87
- Type(definition: Record<string, string | [string, any]>): {
88
- new: (...args: any[]) => PineTypeObject;
89
- copy: (object: PineTypeObject) => PineTypeObject;
90
- };
86
+ Type(definition: Record<string, string | [string, any]>): any;
91
87
  }
@@ -2,7 +2,19 @@ export declare class PineTypeObject {
2
2
  private _definition;
3
3
  context: any;
4
4
  get __def__(): Record<string, string>;
5
- constructor(_definition: Record<string, string>, context: any);
5
+ /**
6
+ * Back-reference to the UDT factory that produced this instance.
7
+ * Used by `request.security_lower_tf`'s pure-builtin fast path to
8
+ * detect UDTs whose field defaults are all bare price builtins
9
+ * (e.g. `type candle { float o = open; float h = high; … }`) — when
10
+ * detected, the secondary's per-LTF-bar values can be synthesised
11
+ * directly from the candle stream without running any user script.
12
+ * Optional and nullable: instances created outside `Type().new` (or
13
+ * for legacy / direct constructions) leave this undefined and the
14
+ * fast path simply doesn't engage.
15
+ */
16
+ _udt?: any;
17
+ constructor(_definition: Record<string, string>, context: any, _udt?: any);
6
18
  copy(): PineTypeObject;
7
19
  toString(): string;
8
20
  }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Pine Script `ticker.*` namespace.
3
+ *
4
+ * The methods here construct "ticker ID" strings that are passed to
5
+ * `request.security` / `request.security_lower_tf` to fetch data for a
6
+ * specific symbol — potentially with extra modifiers (session,
7
+ * adjustment, non-standard chart type). PineTS' data providers serve
8
+ * standard candles only; chart-type modifiers (Heikin-Ashi, Renko,
9
+ * Kagi, Line Break, Point & Figure) are silently dropped at the
10
+ * `request.security` boundary, since we can't render alternative
11
+ * bar-construction algorithms from the raw OHLCV feed.
12
+ *
13
+ * For the plain "no-modifier" cases — which cover virtually every
14
+ * real-world Pine script — the returned tickerid strings match
15
+ * TradingView's exact log output, so automation tests pass strictly.
16
+ * Non-default `adjustment` values trigger TV's encoded
17
+ * `={"adjustment":"…","symbol":"…"}` form; PineTS only emits the
18
+ * plain symbol there (since `request.security` doesn't honor
19
+ * adjustment either). Document as a known divergence.
20
+ */
21
+ export declare class Ticker {
22
+ private context;
23
+ constructor(context: any);
24
+ /**
25
+ * Type B param wrapper — extract scalar from series/primitive.
26
+ * Used by the transpiler to wrap ticker.* arguments.
27
+ */
28
+ param(source: any, index?: number, _name?: string): any;
29
+ /**
30
+ * ticker.inherit(from_tickerid, symbol) → simple string
31
+ *
32
+ * Returns a ticker ID that uses `symbol` and inherits modifier
33
+ * settings (session, currency, adjustment, chart type) from
34
+ * `from_tickerid`. For data-fetching purposes the result is
35
+ * effectively `symbol` — modifiers can't be honored without a TV
36
+ * datafeed, and `symbol` is what `request.security` needs.
37
+ */
38
+ inherit(_from_tickerid: any, symbol: any): string;
39
+ /**
40
+ * ticker.new(prefix, ticker, session?, adjustment?, ...) → simple string
41
+ *
42
+ * Returns "prefix:ticker". Modifier arguments are accepted but
43
+ * ignored — see class-level note. Returns an empty string if
44
+ * either prefix or ticker is empty (matches TV).
45
+ */
46
+ new(prefix: any, ticker: any, _session?: any, _adjustment?: any, _backadjustment?: any, _settlement_as_close?: any): string;
47
+ /**
48
+ * ticker.modify(tickerid, session?, adjustment?, ...) → simple string
49
+ *
50
+ * Returns the tickerid unchanged — modifier args are accepted but
51
+ * ignored.
52
+ */
53
+ modify(tickerid: any, _session?: any, _adjustment?: any, _backadjustment?: any, _settlement_as_close?: any): string;
54
+ /**
55
+ * ticker.standard(symbol?) → simple string
56
+ *
57
+ * Returns the symbol stripped of any non-standard chart-type
58
+ * modifiers. Since PineTS doesn't synthesise non-standard chart
59
+ * types in the first place, this is effectively a pass-through
60
+ * (the standard form IS what our providers serve). If `symbol` is
61
+ * undefined, falls back to `syminfo.tickerid`.
62
+ */
63
+ standard(symbol?: any): string;
64
+ /**
65
+ * ticker.heikinashi(symbol) → simple string
66
+ *
67
+ * In TV this returns an encoded tickerid that instructs the
68
+ * datafeed to deliver Heikin-Ashi bars. PineTS' providers don't
69
+ * synthesise HA candles, so we return the plain symbol — downstream
70
+ * `request.security` fetches standard candles, NOT HA-transformed
71
+ * ones. Behavior diverges from TV when the script depends on the
72
+ * HA values matching TV's HA computation. Documented limitation.
73
+ */
74
+ heikinashi(symbol: any): string;
75
+ /**
76
+ * ticker.renko(symbol, style?, param?, request_wicks?, source?) → simple string
77
+ *
78
+ * Stub: returns the plain symbol. See heikinashi() note.
79
+ */
80
+ renko(symbol: any, _style?: any, _param?: any, _request_wicks?: any, _source?: any): string;
81
+ /**
82
+ * ticker.kagi(symbol, reversal) → simple string
83
+ *
84
+ * Stub: returns the plain symbol. See heikinashi() note.
85
+ */
86
+ kagi(symbol: any, _reversal?: any): string;
87
+ /**
88
+ * ticker.linebreak(symbol, number_of_lines) → simple string
89
+ *
90
+ * Stub: returns the plain symbol. See heikinashi() note.
91
+ */
92
+ linebreak(symbol: any, _number_of_lines?: any): string;
93
+ /**
94
+ * ticker.pointfigure(symbol, source, style, param, reversal) → simple string
95
+ *
96
+ * Stub: returns the plain symbol. See heikinashi() note.
97
+ */
98
+ pointfigure(symbol: any, _source?: any, _style?: any, _param?: any, _reversal?: any): string;
99
+ /**
100
+ * Coerce a runtime value to a plain string. Handles Series wrappers
101
+ * (used by the transpiler), `na`/null/undefined, and primitives.
102
+ */
103
+ private _coerce;
104
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `@silentInSecondary` — method decorator.
3
+ *
4
+ * Marks a helper method as a no-op when invoked on a secondary context
5
+ * (i.e. the auxiliary PineTS instance that `request.security` /
6
+ * `request.security_lower_tf` spawns to compute the captured expression
7
+ * at another symbol/timeframe).
8
+ *
9
+ * Drawings, plots, alerts and similar side-effect-only operations are
10
+ * never observable from a secondary context — its sole job is to populate
11
+ * `secContext.params[expression_name]` with the value of the captured
12
+ * expression bar-by-bar. Silencing those operations on secondaries cuts
13
+ * the per-bar work substantially without changing the captured value
14
+ * (the only output that callers ever read).
15
+ *
16
+ * Constructor-style methods (e.g. `label.new`, `line.new`, `box.new`)
17
+ * return `null`; setters / mutators / deletes return `undefined`. The
18
+ * existing helper code is null-safe end-to-end:
19
+ * - `get_*` methods already return `NaN`/`""` when the receiver is null.
20
+ * - The transpiler emits method calls on UDT-typed receivers as
21
+ * `obj?.method?.(...)`, so `null?.set_x1?.(...)` short-circuits.
22
+ * - Built-in setters like `LineHelper.set_x1(id, x)` already guard
23
+ * `if (id && !id._deleted) ...` and no-op on null.
24
+ *
25
+ * Pre-condition: target classes use the conventional
26
+ * `constructor(private context: any) {}`
27
+ * shape, so `this.context.isSecondaryContext` is uniformly accessible.
28
+ * (`Core.ts` `AlertHelper`, `Plots.ts` `PlotHelper`/`HlineHelper`/
29
+ * `FillHelper`, and the drawing helpers all conform.)
30
+ */
31
+ export declare function silentInSecondary(_target: any, _propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor;
@@ -99,6 +99,14 @@ export declare class ScopeManager {
99
99
  pushScope(type: string): void;
100
100
  popScope(): void;
101
101
  getCurrentScopeType(): string;
102
+ /**
103
+ * True when any active scope on the stack is a function scope.
104
+ * Different from `getCurrentScopeType() === 'fn'`, which only matches
105
+ * the immediate scope — code inside a nested `if`/`for`/`while` inside
106
+ * a function body still needs the function-scope context (e.g. for
107
+ * call-path-keyed param/ta-callsite ids).
108
+ */
109
+ isInsideFunctionScope(): boolean;
102
110
  getCurrentScopeCount(): number;
103
111
  addLocalSeriesVar(name: string): void;
104
112
  removeLocalSeriesVar(name: string): void;
@@ -14,6 +14,14 @@ export declare class Lexer {
14
14
  tokenize(): Token[];
15
15
  handleNewline(): void;
16
16
  handleIndentation(): void;
17
+ /**
18
+ * True when the most recently emitted token (skipping NEWLINE / COMMENT
19
+ * — those are layout, not content) is a token that requires a right-
20
+ * hand-side and therefore implies the next non-blank line is a
21
+ * continuation, not a new block. Mirrors the set the parser's
22
+ * `peekOperatorEx` already crosses NEWLINE for.
23
+ */
24
+ private isContinuationFromPrevToken;
17
25
  readComment(): void;
18
26
  readString(): void;
19
27
  readColorLiteral(): void;
@@ -1,6 +1,7 @@
1
1
  export declare const KNOWN_NAMESPACES: string[];
2
2
  export declare const NAMESPACES_LIKE: string[];
3
3
  export declare const ASYNC_METHODS: string[];
4
+ export declare const VIEWPORT_DEPENDENT_BUILTINS: string[];
4
5
  export declare const FACTORY_METHODS: string[];
5
6
  export declare const NAMESPACE_COLLISION_NAMES: Set<string>;
6
7
  export declare const JS_RESERVED_WORDS: Set<string>;
@@ -0,0 +1 @@
1
+ export declare function buildLtfSlices(ast: any): Record<string, Function>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinets",
3
- "version": "0.9.14",
3
+ "version": "0.9.16",
4
4
  "description": "Run Pine Script anywhere. PineTS is an open-source transpiler and runtime that brings Pine Script logic to Node.js and the browser with 1:1 syntax compatibility. Reliably write, port, and run indicators or strategies on your own infrastructure.",
5
5
  "keywords": [
6
6
  "Pine Script",