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.
- package/dist/pinets.min.browser.es.js +31 -30
- package/dist/pinets.min.browser.es.js.map +1 -1
- package/dist/pinets.min.browser.js +31 -30
- package/dist/pinets.min.browser.js.map +1 -1
- package/dist/pinets.min.cjs +31 -30
- package/dist/pinets.min.cjs.map +1 -1
- package/dist/pinets.min.es.js +31 -30
- package/dist/pinets.min.es.js.map +1 -1
- package/dist/types/Context.class.d.ts +20 -4
- package/dist/types/PineTS.class.d.ts +75 -0
- package/dist/types/namespaces/Core.d.ts +1 -5
- package/dist/types/namespaces/PineTypeObject.d.ts +13 -1
- package/dist/types/namespaces/Ticker.d.ts +104 -0
- package/dist/types/namespaces/silentInSecondary.d.ts +31 -0
- package/dist/types/transpiler/analysis/ScopeManager.d.ts +8 -0
- package/dist/types/transpiler/pineToJS/lexer.d.ts +8 -0
- package/dist/types/transpiler/settings.d.ts +1 -0
- package/dist/types/transpiler/slicing/buildLtfSlices.d.ts +1 -0
- package/package.json +1 -1
|
@@ -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 [
|
|
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<[
|
|
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
|
|
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
|
-
|
|
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.
|
|
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",
|