openpinescript 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +346 -2
- package/dist/cli.mjs +108686 -0
- package/dist/index.cjs +108670 -0
- package/dist/index.mjs +108624 -0
- package/dist/types/index.d.ts +31 -0
- package/dist/types/parser/v1/generated/PineV1Lexer.d.ts +66 -0
- package/dist/types/parser/v1/generated/PineV1Parser.d.ts +555 -0
- package/dist/types/parser/v1/generated/PineV1ParserVisitor.d.ts +318 -0
- package/dist/types/parser/v1/index.d.ts +39 -0
- package/dist/types/parser/v2/generated/PineV2Lexer.d.ts +67 -0
- package/dist/types/parser/v2/generated/PineV2Parser.d.ts +568 -0
- package/dist/types/parser/v2/generated/PineV2ParserVisitor.d.ts +325 -0
- package/dist/types/parser/v2/index.d.ts +2 -0
- package/dist/types/parser/v3/generated/PineV3Lexer.d.ts +67 -0
- package/dist/types/parser/v3/generated/PineV3Parser.d.ts +568 -0
- package/dist/types/parser/v3/generated/PineV3ParserVisitor.d.ts +325 -0
- package/dist/types/parser/v3/index.d.ts +2 -0
- package/dist/types/parser/v4/generated/PineV4Lexer.d.ts +86 -0
- package/dist/types/parser/v4/generated/PineV4Parser.d.ts +673 -0
- package/dist/types/parser/v4/generated/PineV4ParserVisitor.d.ts +367 -0
- package/dist/types/parser/v4/index.d.ts +2 -0
- package/dist/types/parser/v5/generated/PineV5Lexer.d.ts +95 -0
- package/dist/types/parser/v5/generated/PineV5Parser.d.ts +964 -0
- package/dist/types/parser/v5/generated/PineV5ParserVisitor.d.ts +521 -0
- package/dist/types/parser/v5/index.d.ts +2 -0
- package/dist/types/parser/v6/generated/PineV6Lexer.d.ts +98 -0
- package/dist/types/parser/v6/generated/PineV6Parser.d.ts +984 -0
- package/dist/types/parser/v6/generated/PineV6ParserVisitor.d.ts +528 -0
- package/dist/types/parser/v6/index.d.ts +2 -0
- package/dist/types/runtime/v1/Series.d.ts +61 -0
- package/dist/types/runtime/v1/broker/Broker.d.ts +325 -0
- package/dist/types/runtime/v1/broker/index.d.ts +158 -0
- package/dist/types/runtime/v1/broker/val.d.ts +28 -0
- package/dist/types/runtime/v1/constDefault.d.ts +11 -0
- package/dist/types/runtime/v1/context.d.ts +1705 -0
- package/dist/types/runtime/v1/footprint.d.ts +32 -0
- package/dist/types/runtime/v1/index.d.ts +7 -0
- package/dist/types/runtime/v1/language-spec.d.ts +12 -0
- package/dist/types/runtime/v1/openTrades.d.ts +12 -0
- package/dist/types/runtime/v1/palette.d.ts +18 -0
- package/dist/types/runtime/v1/session.d.ts +482 -0
- package/dist/types/runtime/v1/sessions.d.ts +31 -0
- package/dist/types/runtime/v1/stateClone.d.ts +19 -0
- package/dist/types/runtime/v1/stdlib/alerts.d.ts +31 -0
- package/dist/types/runtime/v1/stdlib/arrays.d.ts +133 -0
- package/dist/types/runtime/v1/stdlib/barmerge.d.ts +27 -0
- package/dist/types/runtime/v1/stdlib/barstate.d.ts +79 -0
- package/dist/types/runtime/v1/stdlib/cast.d.ts +81 -0
- package/dist/types/runtime/v1/stdlib/chart.d.ts +283 -0
- package/dist/types/runtime/v1/stdlib/color.d.ts +51 -0
- package/dist/types/runtime/v1/stdlib/core.d.ts +162 -0
- package/dist/types/runtime/v1/stdlib/dataset.d.ts +36 -0
- package/dist/types/runtime/v1/stdlib/display.d.ts +50 -0
- package/dist/types/runtime/v1/stdlib/drawings.d.ts +407 -0
- package/dist/types/runtime/v1/stdlib/errors.d.ts +21 -0
- package/dist/types/runtime/v1/stdlib/font.d.ts +20 -0
- package/dist/types/runtime/v1/stdlib/footprint.d.ts +76 -0
- package/dist/types/runtime/v1/stdlib/format.d.ts +57 -0
- package/dist/types/runtime/v1/stdlib/index.d.ts +71 -0
- package/dist/types/runtime/v1/stdlib/input.d.ts +14 -0
- package/dist/types/runtime/v1/stdlib/inputs.d.ts +60 -0
- package/dist/types/runtime/v1/stdlib/late_members.d.ts +97 -0
- package/dist/types/runtime/v1/stdlib/logging.d.ts +31 -0
- package/dist/types/runtime/v1/stdlib/maps.d.ts +55 -0
- package/dist/types/runtime/v1/stdlib/math.d.ts +71 -0
- package/dist/types/runtime/v1/stdlib/matrix.d.ts +201 -0
- package/dist/types/runtime/v1/stdlib/metadata.d.ts +14 -0
- package/dist/types/runtime/v1/stdlib/mtf.d.ts +15 -0
- package/dist/types/runtime/v1/stdlib/renames.d.ts +117 -0
- package/dist/types/runtime/v1/stdlib/renames5.d.ts +113 -0
- package/dist/types/runtime/v1/stdlib/renames6.d.ts +81 -0
- package/dist/types/runtime/v1/stdlib/request.d.ts +207 -0
- package/dist/types/runtime/v1/stdlib/scale.d.ts +19 -0
- package/dist/types/runtime/v1/stdlib/session.d.ts +113 -0
- package/dist/types/runtime/v1/stdlib/sort_field.d.ts +12 -0
- package/dist/types/runtime/v1/stdlib/sources.d.ts +24 -0
- package/dist/types/runtime/v1/stdlib/str.d.ts +117 -0
- package/dist/types/runtime/v1/stdlib/strategy.d.ts +314 -0
- package/dist/types/runtime/v1/stdlib/syminfo.d.ts +209 -0
- package/dist/types/runtime/v1/stdlib/ta.d.ts +409 -0
- package/dist/types/runtime/v1/stdlib/time.d.ts +158 -0
- package/dist/types/runtime/v1/stdlib/trades.d.ts +108 -0
- package/dist/types/runtime/v1/stdlib/ui.d.ts +196 -0
- package/dist/types/runtime/v1/stdlib/v6_members.d.ts +68 -0
- package/dist/types/runtime/v1/timeframes.d.ts +139 -0
- package/dist/types/runtime/v1/zones.d.ts +30 -0
- package/dist/types/transpiler/common/index.d.ts +51 -0
- package/dist/types/transpiler/common/libraries.d.ts +29 -0
- package/dist/types/transpiler/common/parseErrors.d.ts +38 -0
- package/dist/types/transpiler/common/passes/ScopeAnalysis.d.ts +158 -0
- package/dist/types/transpiler/common/passes/TypeRules.d.ts +26 -0
- package/dist/types/transpiler/common/profiles/index.d.ts +96 -0
- package/dist/types/transpiler/common/version.d.ts +40 -0
- package/dist/types/transpiler/v1/ToJsVisitor.d.ts +720 -0
- package/dist/types/transpiler/v2/ToJsVisitor.d.ts +72 -0
- package/dist/types/transpiler/v3/ToJsVisitor.d.ts +87 -0
- package/dist/types/transpiler/v4/ToJsVisitor.d.ts +263 -0
- package/dist/types/transpiler/v5/ToJsVisitor.d.ts +1101 -0
- package/dist/types/transpiler/v6/ToJsVisitor.d.ts +237 -0
- package/dist/types/transpiler/v6/rules.d.ts +94 -0
- package/dist/types/utils/v2/common.d.ts +4 -0
- package/package.json +92 -4
|
@@ -0,0 +1,1705 @@
|
|
|
1
|
+
import { Series } from "./Series.js";
|
|
2
|
+
import { MarketOrder } from "./stdlib/strategy.js";
|
|
3
|
+
import type { FootprintLevel } from "./footprint.js";
|
|
4
|
+
import { LanguageProfile } from "../../transpiler/common/profiles/index.js";
|
|
5
|
+
export interface Trade {
|
|
6
|
+
id: string;
|
|
7
|
+
entryTime: number;
|
|
8
|
+
entryPrice: number;
|
|
9
|
+
exitTime: number;
|
|
10
|
+
exitPrice: number;
|
|
11
|
+
qty: number;
|
|
12
|
+
pnl: number;
|
|
13
|
+
direction: "long" | "short";
|
|
14
|
+
/**
|
|
15
|
+
* The bars the trade opened and closed on.
|
|
16
|
+
*
|
|
17
|
+
* A time alone cannot be aligned to a chart without searching the bar
|
|
18
|
+
* list for it, and "Avg # Bars in Trades" — a row of TradingView's
|
|
19
|
+
* performance summary — is `exitBarIndex - entryBarIndex` and nothing
|
|
20
|
+
* else. Both are recorded at the fill, where they are known for free.
|
|
21
|
+
*/
|
|
22
|
+
entryBarIndex?: number;
|
|
23
|
+
exitBarIndex?: number;
|
|
24
|
+
/**
|
|
25
|
+
* The order id that OPENED the position, which TradingView's list of
|
|
26
|
+
* trades calls the trade's signal. `id` names the closing order, so
|
|
27
|
+
* without this the entry row of every trade reads "Close".
|
|
28
|
+
*/
|
|
29
|
+
entryId?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Favourable and adverse excursion, in currency, over the life of the
|
|
32
|
+
* trade — how far the position ran in the script's favour before it
|
|
33
|
+
* closed, and how far against.
|
|
34
|
+
*
|
|
35
|
+
* run-up = (highest price while open - entry price) * qty [long]
|
|
36
|
+
* drawdown = (entry price - lowest price while open) * qty [long]
|
|
37
|
+
*
|
|
38
|
+
* mirrored for a short. TradingView states the price form of both and
|
|
39
|
+
* reports them per trade; the exit price does not enter either one.
|
|
40
|
+
* Both are >= 0, and both are measured against the position's average
|
|
41
|
+
* price so a pyramided add moves them the way it moves the P&L.
|
|
42
|
+
*/
|
|
43
|
+
runup?: number;
|
|
44
|
+
drawdown?: number;
|
|
45
|
+
/** Commission charged on the round turn, already deducted from `pnl`. */
|
|
46
|
+
commission?: number;
|
|
47
|
+
/**
|
|
48
|
+
* `strategy.closedtrades.exit_id` and `.exit_comment`, as TradingView
|
|
49
|
+
* names them: the exit's own id, "Close entry(s) order <entry>" for
|
|
50
|
+
* `strategy.close`, "Close position order" for `close_all` and a margin
|
|
51
|
+
* call. Absent where no version records them yet, and the accessors fall
|
|
52
|
+
* back to `id`.
|
|
53
|
+
*/
|
|
54
|
+
exitId?: string;
|
|
55
|
+
exitComment?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* One row of the equity curve: what the account was worth at a bar's close.
|
|
59
|
+
*
|
|
60
|
+
* Sampled every bar rather than every fill because that is what a chart
|
|
61
|
+
* plots — TradingView's overview draws equity against time, and a curve
|
|
62
|
+
* that only moves when a trade closes is a staircase that hides the open
|
|
63
|
+
* position's swing. `openPL` is the part of `equity` that is not yet
|
|
64
|
+
* realized, which is what makes the difference visible.
|
|
65
|
+
*/
|
|
66
|
+
export interface EquityPoint {
|
|
67
|
+
time: number;
|
|
68
|
+
/** Realized cash plus the open position's mark-to-market. */
|
|
69
|
+
equity: number;
|
|
70
|
+
/** Unrealized P&L of the position held at this bar's close; 0 while flat. */
|
|
71
|
+
openPL: number;
|
|
72
|
+
}
|
|
73
|
+
/** One entry still open inside a position. See Position.entries. */
|
|
74
|
+
export interface OpenEntry {
|
|
75
|
+
id: string;
|
|
76
|
+
/** Still open, and as filled. `qty_percent` exits are a share of `opened`. */
|
|
77
|
+
qty: number;
|
|
78
|
+
opened: number;
|
|
79
|
+
price: number;
|
|
80
|
+
bar: number;
|
|
81
|
+
time: number;
|
|
82
|
+
/** Entry commission not yet claimed by a closing trade. */
|
|
83
|
+
commission: number;
|
|
84
|
+
/** Identifies the trade for as long as it lives: the bar and the fill's order count. */
|
|
85
|
+
uid?: string;
|
|
86
|
+
}
|
|
87
|
+
export interface Position {
|
|
88
|
+
size: number;
|
|
89
|
+
avgPrice: number;
|
|
90
|
+
/**
|
|
91
|
+
* The bar the position was OPENED on, the time of that bar, and the order
|
|
92
|
+
* id that opened it.
|
|
93
|
+
*
|
|
94
|
+
* Recorded so `strategy.opentrades.entry_bar_index` and `.entry_id` have
|
|
95
|
+
* something to answer with. Undefined while flat, which is the same fact as
|
|
96
|
+
* `size === 0` and reads as `na` either way.
|
|
97
|
+
*
|
|
98
|
+
* `entryTime` is here because a host has to PLACE this position on a
|
|
99
|
+
* chart, and a bar index cannot be placed: the chart's bar list grows as
|
|
100
|
+
* older bars page in, so index 40 is a different bar after a scroll.
|
|
101
|
+
* `Trade` carries both for the same reason; leaving it off `Position`
|
|
102
|
+
* meant the still-open position could only be drawn on the last bar.
|
|
103
|
+
*/
|
|
104
|
+
entryBarIndex?: number;
|
|
105
|
+
entryTime?: number;
|
|
106
|
+
entryId?: string;
|
|
107
|
+
/**
|
|
108
|
+
* Entry-side commission already paid on the position still open, so an
|
|
109
|
+
* exit can charge its own side and claim a share of this rather than
|
|
110
|
+
* recomputing a whole round turn.
|
|
111
|
+
*
|
|
112
|
+
* `commission.cash_per_order` is a flat fee PER ORDER, and it used to be
|
|
113
|
+
* charged twice per closed-trade record. Two partial exits of one entry
|
|
114
|
+
* are three orders and were billed for four; three pyramided entries
|
|
115
|
+
* closed in one go are four orders and were billed for two. The other two
|
|
116
|
+
* commission types scale with quantity, so they came out right by
|
|
117
|
+
* accident and this changes nothing for them.
|
|
118
|
+
*/
|
|
119
|
+
entryCommission?: number;
|
|
120
|
+
/**
|
|
121
|
+
* The largest size this position has held since it opened: what a v6
|
|
122
|
+
* `qty_percent` exit is a percentage OF. Half of a 0.731 entry is 0.3655
|
|
123
|
+
* whether or not another exit has already sold part of it.
|
|
124
|
+
*/
|
|
125
|
+
openedQty?: number;
|
|
126
|
+
/**
|
|
127
|
+
* The entries that make up the position, oldest first — what TradingView
|
|
128
|
+
* calls its open trades. Kept by every broker and READ only where the
|
|
129
|
+
* broker tracks trades (V6Broker): `strategy.close(id)` and an exit's
|
|
130
|
+
* `from_entry` act on the entries with that id, and a close records one
|
|
131
|
+
* trade per entry it consumes, at that entry's own price.
|
|
132
|
+
*/
|
|
133
|
+
entries?: OpenEntry[];
|
|
134
|
+
/**
|
|
135
|
+
* The id ledger, apart from `entries` (the trades, first in first out):
|
|
136
|
+
* what each entry id still owns, and what it opened with. An exit with
|
|
137
|
+
* `from_entry` and `close(id)` draw on an id's quantity, but what they sell
|
|
138
|
+
* comes off the OLDEST trades whatever their id (TradingView's log,
|
|
139
|
+
* harness_strategy_exit_mix and harness_strategy_pyr3 phase d).
|
|
140
|
+
*/
|
|
141
|
+
ledger?: Record<string, {
|
|
142
|
+
qty: number;
|
|
143
|
+
opened: number;
|
|
144
|
+
}>;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* One line emitted by v5's `log.*` functions.
|
|
148
|
+
*
|
|
149
|
+
* Declared here rather than beside the functions in stdlib/logging.ts so the
|
|
150
|
+
* dependency runs one way: every stdlib module already imports the Context, and
|
|
151
|
+
* having the Context import a stdlib module back would close the cycle.
|
|
152
|
+
*/
|
|
153
|
+
export interface LogEntry {
|
|
154
|
+
readonly level: "info" | "warning" | "error";
|
|
155
|
+
readonly message: string;
|
|
156
|
+
/** The bar the line was emitted on, so a log can be aligned to a series. */
|
|
157
|
+
readonly bar: number;
|
|
158
|
+
/** The bar's timestamp, which is what the generated CSV rows are keyed by. */
|
|
159
|
+
readonly time: number;
|
|
160
|
+
}
|
|
161
|
+
interface PlotBase {
|
|
162
|
+
title: string;
|
|
163
|
+
color?: string;
|
|
164
|
+
}
|
|
165
|
+
export interface LinePlot extends PlotBase {
|
|
166
|
+
type: 'line';
|
|
167
|
+
value: number;
|
|
168
|
+
linewidth?: number;
|
|
169
|
+
style?: number;
|
|
170
|
+
}
|
|
171
|
+
export interface OHLCPlot extends PlotBase {
|
|
172
|
+
type: 'candle';
|
|
173
|
+
open: number;
|
|
174
|
+
high: number;
|
|
175
|
+
low: number;
|
|
176
|
+
close: number;
|
|
177
|
+
}
|
|
178
|
+
export interface MarkerPlot extends PlotBase {
|
|
179
|
+
type: 'shape' | 'char' | 'arrow';
|
|
180
|
+
value: number;
|
|
181
|
+
style?: string;
|
|
182
|
+
}
|
|
183
|
+
export interface ColorDirective extends PlotBase {
|
|
184
|
+
type: 'bgcolor' | 'barcolor';
|
|
185
|
+
}
|
|
186
|
+
export type PlotData = LinePlot | OHLCPlot | MarkerPlot | ColorDirective;
|
|
187
|
+
/** Where in the Pine source a runtime error was raised; see `Context.locateError`. */
|
|
188
|
+
export interface PineLocation {
|
|
189
|
+
line: number;
|
|
190
|
+
col: number;
|
|
191
|
+
/** The alias of the imported library the error was raised in, if it was in one. */
|
|
192
|
+
library?: string;
|
|
193
|
+
/** For an error inside a library: the call in the script that led there. */
|
|
194
|
+
caller?: {
|
|
195
|
+
line: number;
|
|
196
|
+
col: number;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export interface FillData {
|
|
200
|
+
plotId1: string;
|
|
201
|
+
plotId2: string;
|
|
202
|
+
color: string;
|
|
203
|
+
title: string;
|
|
204
|
+
/** `fill(show_last)`: draw only the last N bars. Absent when the script gave none. */
|
|
205
|
+
showLast?: number;
|
|
206
|
+
/** The gradient overload, `fill(p1, p2, top_value, bottom_value, top_color, bottom_color)`. */
|
|
207
|
+
gradient?: true;
|
|
208
|
+
/** `top_color` is drawn at the price `top_value`, `bottom_color` at `bottom_value`; this bar's. */
|
|
209
|
+
top_value?: number;
|
|
210
|
+
bottom_value?: number;
|
|
211
|
+
top_color?: string;
|
|
212
|
+
bottom_color?: string;
|
|
213
|
+
}
|
|
214
|
+
export interface InputDef {
|
|
215
|
+
id: string;
|
|
216
|
+
defval: any;
|
|
217
|
+
title: string;
|
|
218
|
+
type: string;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Script-level metadata declared by the `study()` / `strategy()` directive.
|
|
222
|
+
* `kind` drives mode-specific behavior (e.g. study mode disables the broker).
|
|
223
|
+
*/
|
|
224
|
+
/**
|
|
225
|
+
* The directive kinds a script can declare.
|
|
226
|
+
*
|
|
227
|
+
* `indicator` is v5's rename of `study` and nothing else — same parameters,
|
|
228
|
+
* same meaning. Both spellings are kept rather than normalised so a host
|
|
229
|
+
* reading `scriptMeta.kind` sees the keyword the author actually wrote, which
|
|
230
|
+
* is also what every diagnostic quotes back at them.
|
|
231
|
+
*
|
|
232
|
+
* `library` is v5's third kind. A library declares functions for other scripts
|
|
233
|
+
* to import and plots nothing itself, so a host must be able to tell one from
|
|
234
|
+
* an indicator before it tries to render anything.
|
|
235
|
+
*/
|
|
236
|
+
export type ScriptKind = 'study' | 'indicator' | 'strategy' | 'library';
|
|
237
|
+
/**
|
|
238
|
+
* What a strategy starts with when `strategy()` does not say.
|
|
239
|
+
*
|
|
240
|
+
* TradingView's own figure, read off its own output: the golden strategy
|
|
241
|
+
* export passes no `initial_capital` and its `strategy.equity` reads exactly
|
|
242
|
+
* 1000000 on every bar before the first fill
|
|
243
|
+
* (conformance/v5/parity/harness_strategy.BTCUSDT.1D.log). The engine used to
|
|
244
|
+
* start at 100000, which put every equity-derived figure — `netprofit_percent`,
|
|
245
|
+
* `grossprofit_percent` — out by a factor of ten.
|
|
246
|
+
*/
|
|
247
|
+
export declare const DEFAULT_INITIAL_CAPITAL = 1000000;
|
|
248
|
+
/**
|
|
249
|
+
* ...but only for Pine v5. Read off TradingView with a strategy that passes no
|
|
250
|
+
* `initial_capital`: v2, v3, v4 and v6 start at 100,000, v5 at 1,000,000.
|
|
251
|
+
*/
|
|
252
|
+
export declare function defaultInitialCapital(version: number): number;
|
|
253
|
+
export interface ScriptMeta {
|
|
254
|
+
kind: ScriptKind;
|
|
255
|
+
title: string;
|
|
256
|
+
shorttitle: string;
|
|
257
|
+
overlay: boolean;
|
|
258
|
+
precision?: number;
|
|
259
|
+
pyramiding?: number;
|
|
260
|
+
calc_on_every_tick?: boolean;
|
|
261
|
+
calc_on_order_fills?: boolean;
|
|
262
|
+
currency?: string;
|
|
263
|
+
/**
|
|
264
|
+
* The rest of the `strategy()` declaration — the backtest settings a
|
|
265
|
+
* TradingView user reads off the Properties tab and changes without
|
|
266
|
+
* touching the script.
|
|
267
|
+
*
|
|
268
|
+
* Recorded whether or not the broker acts on each one, because a host
|
|
269
|
+
* cannot show a properties panel for arguments it was never told about,
|
|
270
|
+
* and a panel that invents its own defaults will disagree with the
|
|
271
|
+
* script the moment the script sets one.
|
|
272
|
+
*/
|
|
273
|
+
initial_capital?: number;
|
|
274
|
+
default_qty_type?: string;
|
|
275
|
+
default_qty_value?: number;
|
|
276
|
+
commission_type?: string;
|
|
277
|
+
commission_value?: number;
|
|
278
|
+
slippage?: number;
|
|
279
|
+
margin_long?: number;
|
|
280
|
+
margin_short?: number;
|
|
281
|
+
process_orders_on_close?: boolean;
|
|
282
|
+
risk_free_rate?: number;
|
|
283
|
+
/**
|
|
284
|
+
* v6, July 2026: run once per tick of every HISTORICAL bar. Recorded for a
|
|
285
|
+
* host to display; this engine is given bars, not their ticks, so it has
|
|
286
|
+
* nothing to run per tick and computes exactly as if this were false.
|
|
287
|
+
* Present only when a v6 script declares it.
|
|
288
|
+
*/
|
|
289
|
+
calc_on_every_history_tick?: boolean;
|
|
290
|
+
}
|
|
291
|
+
export interface Drawing {
|
|
292
|
+
id: string;
|
|
293
|
+
kind: 'line' | 'label' | 'box' | 'table' | 'polyline' | 'linefill';
|
|
294
|
+
/** The bar the drawing was created on. */
|
|
295
|
+
bar: number;
|
|
296
|
+
props: Record<string, any>;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* One observation of a non-price series — a dividend, a quarterly figure.
|
|
300
|
+
*
|
|
301
|
+
* These are EVENTS, not bars: a value is published on one date and stands until
|
|
302
|
+
* the next, so a request reads as a step function over its samples rather than
|
|
303
|
+
* as one value per bar.
|
|
304
|
+
*/
|
|
305
|
+
export interface RequestSample {
|
|
306
|
+
/** UNIX ms. The value applies to this bar and every later one until the next sample. */
|
|
307
|
+
time: number;
|
|
308
|
+
value: number;
|
|
309
|
+
}
|
|
310
|
+
/** A higher-timeframe candle supplied for `security()` evaluation (P1). */
|
|
311
|
+
export interface SecurityCandle {
|
|
312
|
+
time: number;
|
|
313
|
+
open: number;
|
|
314
|
+
high: number;
|
|
315
|
+
low: number;
|
|
316
|
+
close: number;
|
|
317
|
+
volume: number;
|
|
318
|
+
/**
|
|
319
|
+
* When the bar closes, if the host knows it. A stock's hourly and daily bars end
|
|
320
|
+
* where the SESSION ends, not on a clock boundary (the last hourly bar of a
|
|
321
|
+
* 09:30-16:00 session is 15:30-16:00); without it the end is the calendar's.
|
|
322
|
+
* `request.security` closes a bar with the chart bar that ends it, so a host
|
|
323
|
+
* with a session calendar should give this (TradingView's `time_close`).
|
|
324
|
+
*/
|
|
325
|
+
closeTime?: number;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* The stack of call ids being executed, with the joined key of every depth
|
|
329
|
+
* kept as it moves.
|
|
330
|
+
*
|
|
331
|
+
* A call site's key was rebuilt by joining the whole stack on every call,
|
|
332
|
+
* which was a tenth of a run's time. The keys are interned, so each is one
|
|
333
|
+
* string whose hash is computed once. A built-in that names a frame of its own
|
|
334
|
+
* (`ta.ts` pushes "rsi_gain" and the like) pushes through the same methods.
|
|
335
|
+
*/
|
|
336
|
+
declare class CallStack {
|
|
337
|
+
private ids;
|
|
338
|
+
/** `keys[i]` is the first `i + 1` ids joined with "/". */
|
|
339
|
+
readonly keys: string[];
|
|
340
|
+
private intern;
|
|
341
|
+
get length(): number;
|
|
342
|
+
/** The innermost id. */
|
|
343
|
+
get top(): string | undefined;
|
|
344
|
+
/** The whole stack joined with "/", "" when empty. */
|
|
345
|
+
get path(): string;
|
|
346
|
+
/** Returns the new path. */
|
|
347
|
+
push(id: string): string;
|
|
348
|
+
pop(): void;
|
|
349
|
+
join(sep: string): string;
|
|
350
|
+
[Symbol.iterator](): Iterator<string>;
|
|
351
|
+
}
|
|
352
|
+
export declare class Context {
|
|
353
|
+
protected callStack: CallStack;
|
|
354
|
+
/** What a call id says about itself, which never changes: its name and whether it is a user frame. */
|
|
355
|
+
private callIds;
|
|
356
|
+
/**
|
|
357
|
+
* Keyword arguments passed to a built-in whose signature does not name them.
|
|
358
|
+
*
|
|
359
|
+
* `<function>(<argument>)`, deduplicated. Empty is the only acceptable
|
|
360
|
+
* value: a name Pine accepts and this engine does not model is a gap, and a
|
|
361
|
+
* silently dropped argument is the worst shape one can take — the script
|
|
362
|
+
* runs, and does something other than what it says.
|
|
363
|
+
*/
|
|
364
|
+
droppedArguments: Set<string>;
|
|
365
|
+
/**
|
|
366
|
+
* The series NAMESPACE of the user-function frame currently executing.
|
|
367
|
+
*
|
|
368
|
+
* Empty at script scope, and `<call id>|` inside a user function — nested
|
|
369
|
+
* one level per frame, so a function called from two places has two
|
|
370
|
+
* namespaces and a function called from inside another has one per outer
|
|
371
|
+
* frame.
|
|
372
|
+
*
|
|
373
|
+
* ── Why this exists ─────────────────────────────────────────────────────
|
|
374
|
+
*
|
|
375
|
+
* "each function call has its own instance of the series variables" is how
|
|
376
|
+
* Pine works, and this engine gave every call site ONE instance:
|
|
377
|
+
*
|
|
378
|
+
* f() =>
|
|
379
|
+
* var c = 0
|
|
380
|
+
* c := c + 1
|
|
381
|
+
* c
|
|
382
|
+
* a = f()
|
|
383
|
+
* b = f()
|
|
384
|
+
*
|
|
385
|
+
* yielded a = 1,3,5 and b = 2,4,6, where TradingView gives 1,2,3 for both.
|
|
386
|
+
* Both calls advanced the same counter. The same held for every function
|
|
387
|
+
* local, not just `var` ones: `t = close * 2` inside a function was one
|
|
388
|
+
* series shared by every caller, so a function's HISTORY (`t[1]`) mixed the
|
|
389
|
+
* call sites together.
|
|
390
|
+
*
|
|
391
|
+
* The emitter already qualifies a local's name by its FUNCTION, which stops
|
|
392
|
+
* two different functions from colliding. This is the other half: two calls
|
|
393
|
+
* of the SAME function are two variables too.
|
|
394
|
+
*/
|
|
395
|
+
private seriesScope;
|
|
396
|
+
private seriesScopes;
|
|
397
|
+
/** Which user-function CALL is running: a fresh number per call, 0 at script scope. */
|
|
398
|
+
private frameSeq;
|
|
399
|
+
private frameSeqs;
|
|
400
|
+
private frameSeqCounter;
|
|
401
|
+
/** The series the running user call was handed as arguments: a parameter's history stays bar-indexed. */
|
|
402
|
+
private argSeries;
|
|
403
|
+
private frameArgs;
|
|
404
|
+
/** A series key in the frame that is running — the plain name at script scope. */
|
|
405
|
+
private scoped;
|
|
406
|
+
private states;
|
|
407
|
+
private committedSnapshot;
|
|
408
|
+
vars: Map<string, Series>;
|
|
409
|
+
time: number;
|
|
410
|
+
open: number;
|
|
411
|
+
high: number;
|
|
412
|
+
low: number;
|
|
413
|
+
close: number;
|
|
414
|
+
volume: number;
|
|
415
|
+
currentBarIndex: number;
|
|
416
|
+
/**
|
|
417
|
+
* The chart's timeframe, as Pine spells it: "1", "5", "60", "D", "W", "M".
|
|
418
|
+
*
|
|
419
|
+
* Read by `period` / `interval` in stdlib/chart.ts. A script that branches
|
|
420
|
+
* on the chart timeframe (a very common pattern in session and pivot
|
|
421
|
+
* indicators) computes different numbers depending on this, so a host that
|
|
422
|
+
* runs intraday data MUST set it — the default is the daily bars this
|
|
423
|
+
* repo's sample dataset uses, not a claim about the data supplied.
|
|
424
|
+
*/
|
|
425
|
+
resolution: string;
|
|
426
|
+
/** The chart's symbol, as `tickerid` / `ticker` report it. */
|
|
427
|
+
symbol: string;
|
|
428
|
+
/**
|
|
429
|
+
* The instrument's minimum price increment, read by `math.round_to_mintick`.
|
|
430
|
+
*
|
|
431
|
+
* A property of the SYMBOL, so a host that charts anything other than the
|
|
432
|
+
* sample dataset must set it. The default is the two-decimal tick of a
|
|
433
|
+
* typical equity, which is a starting point rather than a claim about the
|
|
434
|
+
* data supplied — the same stance `resolution` takes above.
|
|
435
|
+
*/
|
|
436
|
+
mintick: number;
|
|
437
|
+
/**
|
|
438
|
+
* The rest of what a script can ask about its instrument — the `syminfo.*`
|
|
439
|
+
* family added at v4.
|
|
440
|
+
*
|
|
441
|
+
* ── Why these are host-declared, with stated defaults ───────────────────
|
|
442
|
+
*
|
|
443
|
+
* None of it is derivable from OHLCV bars. A CSV of prices does not say
|
|
444
|
+
* which exchange listed them, what currency they are quoted in, or which
|
|
445
|
+
* time zone the session runs in. So each is a field the host sets, and each
|
|
446
|
+
* default below is a CHOICE that a script can act on:
|
|
447
|
+
*
|
|
448
|
+
* `syminfo.type == "crypto"` selects a different pivot window in a large
|
|
449
|
+
* share of published indicators, and `syminfo.currency` appears directly
|
|
450
|
+
* in position-sizing arithmetic.
|
|
451
|
+
*
|
|
452
|
+
* Left unset, a script gets the defaults — a US equity quoted in USD on a
|
|
453
|
+
* one-cent tick — which is exactly what this repo's sample dataset is. That
|
|
454
|
+
* is a starting point, not a claim about whatever data a host supplies.
|
|
455
|
+
*
|
|
456
|
+
* Two are DERIVED rather than stored, because Pine defines them in terms of
|
|
457
|
+
* the ticker id: `syminfo.prefix` and `syminfo.ticker` are the two halves
|
|
458
|
+
* of `EXCHANGE:SYMBOL`. Storing them separately would let them drift out of
|
|
459
|
+
* step with the symbol they describe.
|
|
460
|
+
*/
|
|
461
|
+
currency: string;
|
|
462
|
+
basecurrency: string;
|
|
463
|
+
description: string;
|
|
464
|
+
symbolType: string;
|
|
465
|
+
symbolRoot: string;
|
|
466
|
+
timezone: string;
|
|
467
|
+
pointvalue: number;
|
|
468
|
+
sessionSpec: string;
|
|
469
|
+
mincontract: number;
|
|
470
|
+
/** True once the host supplied `mincontract`; only then are order quantities floored to it. */
|
|
471
|
+
mincontractSupplied: boolean;
|
|
472
|
+
isin: string;
|
|
473
|
+
currentContract: string | null;
|
|
474
|
+
/** The live quote, for `bid` / `ask` (v6). Not in an OHLCV bar. */
|
|
475
|
+
bid: number;
|
|
476
|
+
ask: number;
|
|
477
|
+
/**
|
|
478
|
+
* Volume at price, per bar, for `request.footprint()` (v6). Keyed by bar
|
|
479
|
+
* open time. `null` until a host supplies any — see getFootprintLevels.
|
|
480
|
+
*/
|
|
481
|
+
private footprints;
|
|
482
|
+
/** Supplies footprint data: for each bar, its volume per price level. */
|
|
483
|
+
provideFootprints(bars: ReadonlyArray<{
|
|
484
|
+
time: number;
|
|
485
|
+
levels: FootprintLevel[];
|
|
486
|
+
}>): void;
|
|
487
|
+
/**
|
|
488
|
+
* This bar's levels, `null` when the host has none for it, and a refusal
|
|
489
|
+
* when the host has supplied no footprints at all — the `request.*` rule:
|
|
490
|
+
* a series this engine cannot fetch is asked for, not silently `na`.
|
|
491
|
+
*/
|
|
492
|
+
getFootprintLevels(): FootprintLevel[] | null;
|
|
493
|
+
/** The source location of the one `request.footprint()` call, once seen. */
|
|
494
|
+
private footprintSite;
|
|
495
|
+
/**
|
|
496
|
+
* "Scripts can make only one request.footprint() call. Multiple calls
|
|
497
|
+
* cause a runtime error." A CALL is a place in the source: the innermost
|
|
498
|
+
* call id, which carries its location. Counting executions instead
|
|
499
|
+
* rejected one call inside a `for` loop on the first bar.
|
|
500
|
+
*/
|
|
501
|
+
countFootprintRequest(): void;
|
|
502
|
+
/** Declares the current bid and ask. See `bid` in stdlib/v6_members.ts. */
|
|
503
|
+
provideQuote(quote: {
|
|
504
|
+
bid?: number;
|
|
505
|
+
ask?: number;
|
|
506
|
+
}): void;
|
|
507
|
+
/** Declares the instrument's metadata. Every field is optional. */
|
|
508
|
+
provideSymbolInfo(info: {
|
|
509
|
+
symbol?: string;
|
|
510
|
+
mintick?: number;
|
|
511
|
+
currency?: string;
|
|
512
|
+
basecurrency?: string;
|
|
513
|
+
description?: string;
|
|
514
|
+
type?: string;
|
|
515
|
+
root?: string;
|
|
516
|
+
timezone?: string;
|
|
517
|
+
pointvalue?: number;
|
|
518
|
+
session?: string;
|
|
519
|
+
mincontract?: number;
|
|
520
|
+
isin?: string;
|
|
521
|
+
current_contract?: string;
|
|
522
|
+
}): void;
|
|
523
|
+
/**
|
|
524
|
+
* How many bars the host is going to feed, and when the last one opens.
|
|
525
|
+
*
|
|
526
|
+
* ── Why the host has to say ─────────────────────────────────────────────
|
|
527
|
+
*
|
|
528
|
+
* `last_bar_index` and `last_bar_time` (v5, December 2021) are documented
|
|
529
|
+
* as "known at the beginning of the script's calculation" — on bar 1 a
|
|
530
|
+
* script can already ask how far the dataset runs, which is how the common
|
|
531
|
+
* `bar_index == last_bar_index - 1` idiom works at all.
|
|
532
|
+
*
|
|
533
|
+
* This engine streams bars: `setBar` is called one at a time and the
|
|
534
|
+
* Context has no way to know how many more are coming. Only the caller
|
|
535
|
+
* knows, so the caller declares it. `mock_run` reads it off the CSV; the
|
|
536
|
+
* session API takes it from the candle array it was handed.
|
|
537
|
+
*
|
|
538
|
+
* ── When it is not declared ─────────────────────────────────────────────
|
|
539
|
+
*
|
|
540
|
+
* The fallback is the highest bar seen SO FAR, which is correct on the last
|
|
541
|
+
* bar and an underestimate before it. That is a real divergence from
|
|
542
|
+
* TradingView rather than a hidden approximation, and it is why
|
|
543
|
+
* `provideDatasetExtent` exists: a host that can be exact should be.
|
|
544
|
+
*/
|
|
545
|
+
datasetSize: number | null;
|
|
546
|
+
datasetLastTime: number | null;
|
|
547
|
+
/** Declare the extent of the run before the first bar. */
|
|
548
|
+
provideDatasetExtent(barCount: number, lastBarTime?: number): void;
|
|
549
|
+
/** Bar index of the last bar in the dataset — `last_bar_index`. */
|
|
550
|
+
get lastBarIndex(): number;
|
|
551
|
+
/**
|
|
552
|
+
* Is the script executing on the dataset's final bar — `barstate.islast`?
|
|
553
|
+
*
|
|
554
|
+
* Derived, not stored. `is_last` is written only by `applyTick`, which is
|
|
555
|
+
* the LIVE path, so on a purely historical run nothing ever set it and
|
|
556
|
+
* `barstate.islast` was false on every bar including the last one. That
|
|
557
|
+
* silently broke the commonest idiom in Pine — `if barstate.islast` is how
|
|
558
|
+
* every dashboard table, summary label and end-of-chart drawing is guarded,
|
|
559
|
+
* and all of them simply never ran.
|
|
560
|
+
*
|
|
561
|
+
* The extent is known: `runHistory` declares it before the first bar. So
|
|
562
|
+
* the answer is the comparison, with the live flag kept as an override for
|
|
563
|
+
* the realtime bar, which is last by definition however long the dataset is.
|
|
564
|
+
*/
|
|
565
|
+
get onLastBar(): boolean;
|
|
566
|
+
/**
|
|
567
|
+
* How many bars at the END of the run the host marks REALTIME.
|
|
568
|
+
*
|
|
569
|
+
* ── Why the host has to say ─────────────────────────────────────────────
|
|
570
|
+
*
|
|
571
|
+
* `barstate.islastconfirmedhistory` is "true if the script is executing on
|
|
572
|
+
* the dataset's last bar when the market is closed, or on the bar
|
|
573
|
+
* immediately preceding the realtime bar if the market is open". The second
|
|
574
|
+
* reading needs to be answered ON that preceding bar — one bar before the
|
|
575
|
+
* realtime flag arrives — so it cannot be inferred from `is_realtime`, which
|
|
576
|
+
* the host sets bar by bar.
|
|
577
|
+
*
|
|
578
|
+
* The same shape as `provideDatasetExtent` above, and for the same reason:
|
|
579
|
+
* only the caller knows how its run ends. Zero — no realtime bar, the whole
|
|
580
|
+
* dataset is settled history — is the default, because a host that says
|
|
581
|
+
* nothing is replaying closed bars. Every harness in this repository that
|
|
582
|
+
* models an open market (`is_realtime = is_last`) sets it to 1.
|
|
583
|
+
*
|
|
584
|
+
* Getting this wrong is silent: with a realtime bar unaccounted for, the
|
|
585
|
+
* guard fires on NO bar at all, and the twenty-nine documentation examples
|
|
586
|
+
* that draw their labels and tables under it drew nothing.
|
|
587
|
+
*/
|
|
588
|
+
realtimeBars: number;
|
|
589
|
+
/** Bar index of the last bar the script may treat as settled history. */
|
|
590
|
+
get lastConfirmedHistoryIndex(): number;
|
|
591
|
+
/** Opening time of the last bar in the dataset — `last_bar_time`. */
|
|
592
|
+
get lastBarTime(): number;
|
|
593
|
+
/**
|
|
594
|
+
* The chart's background colour — what `chart.bg_color` returns.
|
|
595
|
+
*
|
|
596
|
+
* "chart.bg_color returns the color of the chart's background from the
|
|
597
|
+
* 'Chart settings/Appearance/Background' field."
|
|
598
|
+
*
|
|
599
|
+
* There is no chart here, so the host declares it. The default is
|
|
600
|
+
* TradingView's own light-theme background, `#ffffff`, which is a CHOICE
|
|
601
|
+
* and not a fact about the run — a script that draws its own dark
|
|
602
|
+
* dashboard will get black text out of `chart.fg_color` unless the host
|
|
603
|
+
* says otherwise. That is why the setter exists.
|
|
604
|
+
*/
|
|
605
|
+
chartBgColor: string;
|
|
606
|
+
/** Declares the chart's background colour, for `chart.bg_color`. */
|
|
607
|
+
provideChartBackground(color: string): void;
|
|
608
|
+
/**
|
|
609
|
+
* The first and last VISIBLE bar times — `chart.left_visible_bar_time` and
|
|
610
|
+
* `chart.right_visible_bar_time`.
|
|
611
|
+
*
|
|
612
|
+
* "chart.left_visible_bar_time returns the time of the leftmost bar
|
|
613
|
+
* currently visible on the chart."
|
|
614
|
+
*
|
|
615
|
+
* Visibility is a property of a viewport, and this engine has none: it runs
|
|
616
|
+
* every bar it is given. So the visible range defaults to the WHOLE
|
|
617
|
+
* dataset, which is what a script scanning "the bars on screen" would see
|
|
618
|
+
* if the user had zoomed all the way out.
|
|
619
|
+
*
|
|
620
|
+
* Stated rather than hidden, because it changes results: a script that
|
|
621
|
+
* counts bars between the two, or anchors a VWAP to the left edge, gets the
|
|
622
|
+
* full history here and a window on TradingView. A host that knows the
|
|
623
|
+
* viewport should declare it.
|
|
624
|
+
*/
|
|
625
|
+
visibleRange: {
|
|
626
|
+
left: number;
|
|
627
|
+
right: number;
|
|
628
|
+
} | null;
|
|
629
|
+
/** Declares the visible bar range, in milliseconds. */
|
|
630
|
+
provideVisibleRange(leftTime: number, rightTime: number): void;
|
|
631
|
+
/** Opening time of the leftmost visible bar. */
|
|
632
|
+
get leftVisibleBarTime(): number;
|
|
633
|
+
/** Opening time of the rightmost visible bar. */
|
|
634
|
+
get rightVisibleBarTime(): number;
|
|
635
|
+
/**
|
|
636
|
+
* Opening time of the FIRST bar seen, or 0 before any bar.
|
|
637
|
+
*
|
|
638
|
+
* Recorded on the first `setBar` rather than derived, because the Context
|
|
639
|
+
* is fed one bar at a time and nothing else remembers where the run began.
|
|
640
|
+
*/
|
|
641
|
+
firstBarTime: number;
|
|
642
|
+
/**
|
|
643
|
+
* Lines emitted by `log.info` / `log.warning` / `log.error` (v5).
|
|
644
|
+
*
|
|
645
|
+
* Kept on the Context rather than written to the console because they are
|
|
646
|
+
* OUTPUT: scripts/common/make-log-harness.ts generates harnesses whose entire
|
|
647
|
+
* result is a log line per bar, and a host has to be able to read them back
|
|
648
|
+
* to reproduce locally what TradingView's Pine Logs pane shows.
|
|
649
|
+
*/
|
|
650
|
+
logs: LogEntry[];
|
|
651
|
+
/**
|
|
652
|
+
* Wall-clock time reported by `timenow`, in ms.
|
|
653
|
+
*
|
|
654
|
+
* Settable so a backtest or replay is reproducible — reading Date.now()
|
|
655
|
+
* inside the script would make two runs of the same data disagree.
|
|
656
|
+
*/
|
|
657
|
+
now: number;
|
|
658
|
+
plots: Map<string, (PlotData | null)[]>;
|
|
659
|
+
fills: Map<string, (FillData | null)[]>;
|
|
660
|
+
/**
|
|
661
|
+
* v4 drawing objects — line, label, box, table — keyed by their generated id.
|
|
662
|
+
*
|
|
663
|
+
* A Map rather than an array so `line.delete(id)` is O(1) and ids stay
|
|
664
|
+
* stable, which is what a host needs to diff one bar's drawings against the
|
|
665
|
+
* next instead of re-rendering everything.
|
|
666
|
+
*
|
|
667
|
+
* These do NOT feed `plots`: a drawing is not a per-bar value, it is an
|
|
668
|
+
* object with a lifetime. Nothing in the golden-parity suite reads them —
|
|
669
|
+
* TradingView's exported chart data does not include drawings either.
|
|
670
|
+
*/
|
|
671
|
+
drawings: Map<string, Drawing>;
|
|
672
|
+
private drawingCounter;
|
|
673
|
+
/** Live ids per kind, oldest first, so the cap below can evict in O(1). */
|
|
674
|
+
private drawingOrder;
|
|
675
|
+
/**
|
|
676
|
+
* How many drawings of each kind survive, and when the rest are deleted.
|
|
677
|
+
*
|
|
678
|
+
* The limit is the script's own: `max_lines_count`, `max_labels_count`,
|
|
679
|
+
* `max_boxes_count` and `max_polylines_count` on `indicator()` /
|
|
680
|
+
* `study()` / `strategy()`, each defaulting to 50 (`declareScript` applies
|
|
681
|
+
* them). Linefills are fixed at 50. Tables are absent deliberately: Pine
|
|
682
|
+
* limits them by POSITION (one per slot), not by count, so an eviction rule
|
|
683
|
+
* would be the wrong shape.
|
|
684
|
+
*
|
|
685
|
+
* ── The rule, as TradingView applies it ─────────────────────────────────
|
|
686
|
+
*
|
|
687
|
+
* It does NOT hold the count at `limit`. Drawings pile up past the limit
|
|
688
|
+
* and are then cut back in one go: when creating a drawing makes the live
|
|
689
|
+
* count of that kind exceed `limit + 5`, the oldest are deleted until the
|
|
690
|
+
* count equals `limit`. It happens at the creation itself, so it also
|
|
691
|
+
* happens in the middle of one bar's execution.
|
|
692
|
+
*
|
|
693
|
+
* Measured with conformance/v5/parity/probe_drawing_limits.pine:
|
|
694
|
+
*
|
|
695
|
+
* limit 10, one line per bar the count after each bar goes
|
|
696
|
+
* 1, 2, ... 15, then 10, 11 ... 15, 10, ...
|
|
697
|
+
* limit 10, 30 labels in one the same sawtooth, per label.new
|
|
698
|
+
* execution
|
|
699
|
+
* limit 500, 1003 lines 505 remain, the newest 505
|
|
700
|
+
* (harness_trader_club)
|
|
701
|
+
*
|
|
702
|
+
* Log: conformance/v5/parity/probe_drawing_limits.BTCUSDT.5.csv. Only
|
|
703
|
+
* limits 10 and 500 were measured; the +5 slack is assumed to be constant
|
|
704
|
+
* in between and at the default of 50.
|
|
705
|
+
*
|
|
706
|
+
* Without any cap, `label.new` on every bar of a 6,000-bar chart retains
|
|
707
|
+
* 6,000 records here while TradingView shows about 50, so a script that
|
|
708
|
+
* reads its own drawings back diverges, and memory grows without bound on
|
|
709
|
+
* exactly the scripts most likely to be long-running.
|
|
710
|
+
*/
|
|
711
|
+
private drawingLimits;
|
|
712
|
+
/** Registers a drawing and returns its id, evicting the oldest past the cap. */
|
|
713
|
+
newDrawing(kind: Drawing["kind"], props: Record<string, any>): string;
|
|
714
|
+
/**
|
|
715
|
+
* v5 and v6: who goes when a kind passes `limit + 5`.
|
|
716
|
+
*
|
|
717
|
+
* Measured on TradingView (dev-docs/tv-parity/_probes/version-scope/
|
|
718
|
+
* v6-gc-order-1, -2, -3 and v5-gc-order, limit 10; golden test
|
|
719
|
+
* tests/v6/runtime/drawing_eviction_tv.test.ts):
|
|
720
|
+
*
|
|
721
|
+
* - A drawing a `var` / `varip` variable holds RIGHT NOW is never
|
|
722
|
+
* evicted. One held only inside an array is not protected. UDT fields,
|
|
723
|
+
* maps and plain variables were not probed and are treated as not
|
|
724
|
+
* protected.
|
|
725
|
+
* - On a historical bar the oldest unprotected drawings go, whatever bar
|
|
726
|
+
* they were drawn on, until the count is `limit`.
|
|
727
|
+
* - On the realtime bar a pass first removes unprotected drawings from
|
|
728
|
+
* EARLIER bars, oldest first, and stops there, even with the count
|
|
729
|
+
* still above `limit`. Only a pass that finds no such drawing removes
|
|
730
|
+
* the current bar's own, oldest first, down to `limit`.
|
|
731
|
+
*
|
|
732
|
+
* v4 and earlier were not probed and keep the plain oldest-first cut.
|
|
733
|
+
*/
|
|
734
|
+
private evictDrawings;
|
|
735
|
+
/** Ids of drawings that a `var` / `varip` variable holds at this moment, in any frame. */
|
|
736
|
+
private varHeldDrawings;
|
|
737
|
+
/**
|
|
738
|
+
* Removes a drawing, keeping the eviction queue in step.
|
|
739
|
+
*
|
|
740
|
+
* Deleting straight out of `drawings` would leave the id in `drawingOrder`,
|
|
741
|
+
* so the next eviction would shift off a dead id, delete nothing, and let
|
|
742
|
+
* the live count sit above the cap indefinitely.
|
|
743
|
+
*/
|
|
744
|
+
deleteDrawing(id: any): void;
|
|
745
|
+
/**
|
|
746
|
+
* Reads a field off a user-defined-type instance.
|
|
747
|
+
*
|
|
748
|
+
* Unwraps a Series first: a UDT instance held in a variable is wrapped by
|
|
749
|
+
* `ctx.new_var` exactly as a number is, so `p.x` reaches this with the
|
|
750
|
+
* wrapper rather than the object.
|
|
751
|
+
*
|
|
752
|
+
* A missing field, or a read through `na`, gives `na` rather than throwing.
|
|
753
|
+
* Pine's type checker catches a misspelt field at COMPILE time and this
|
|
754
|
+
* engine does not model field types, so throwing here would turn a
|
|
755
|
+
* legitimate `na` object — the state every UDT variable starts in — into a
|
|
756
|
+
* crash.
|
|
757
|
+
*/
|
|
758
|
+
field(target: any, name: string): any;
|
|
759
|
+
/**
|
|
760
|
+
* Writes a field on a user-defined-type instance, and returns the value.
|
|
761
|
+
*
|
|
762
|
+
* Writing through `na` is an ERROR, unlike reading. `p.x := 1` when `p` has
|
|
763
|
+
* never been constructed cannot mean anything, and silently discarding the
|
|
764
|
+
* write would leave a script that looks like it is accumulating state and
|
|
765
|
+
* is not — the failure this engine exists to prevent.
|
|
766
|
+
*/
|
|
767
|
+
/**
|
|
768
|
+
* The value a UDT field stores: a series argument's value on this bar.
|
|
769
|
+
*
|
|
770
|
+
* `Item.new(bx, bar_index, t)` with `box bx = box.new(…)` and `float t =
|
|
771
|
+
* high` hands the constructor SERIES -- the variables' wrappers -- and an
|
|
772
|
+
* object that kept the wrapper would read whatever the variable holds on
|
|
773
|
+
* the bar it is READ, not the one it was built on. A `var` array of such
|
|
774
|
+
* objects then saw every element change together: all reported the latest
|
|
775
|
+
* bar's values, and `box.delete(oldest.bx)` deleted the NEWEST box.
|
|
776
|
+
* TradingView stores the value (checked with Pine Logs:
|
|
777
|
+
* dev-docs/tv-parity/_probes/na-drawings/parity-udt-fields.pine), as `setField` already did for writes.
|
|
778
|
+
*
|
|
779
|
+
* Arrays, maps, matrices, drawings and nested objects are values that are
|
|
780
|
+
* references, so unwrapping keeps sharing them exactly as Pine does.
|
|
781
|
+
*/
|
|
782
|
+
fieldValue(value: any): any;
|
|
783
|
+
setField(target: any, name: string, value: any): any;
|
|
784
|
+
/**
|
|
785
|
+
* `obj.copy()` — a SHALLOW copy, which is what Pine specifies.
|
|
786
|
+
*
|
|
787
|
+
* Shallow matters: a UDT holding an array shares that array with its copy,
|
|
788
|
+
* and a script relying on the opposite would diverge from TradingView.
|
|
789
|
+
*/
|
|
790
|
+
copyObject(target: any): any;
|
|
791
|
+
/**
|
|
792
|
+
* `receiver.name(args)` where `name` is a BUILT-IN method — v5's method-call
|
|
793
|
+
* syntax for the collection and drawing namespaces.
|
|
794
|
+
*
|
|
795
|
+
* `a.push(1)` and `array.push(a, 1)` are the same call in Pine, and the
|
|
796
|
+
* emitter cannot tell them apart statically: it does not track types, so it
|
|
797
|
+
* does not know whether `a` is an array, a matrix or a map. The receiver
|
|
798
|
+
* does know, at run time, so the dispatch happens here.
|
|
799
|
+
*
|
|
800
|
+
* A matrix is the one case a value cannot answer for itself — it is an
|
|
801
|
+
* array of arrays, which is also a valid array — so `matrix.new` marks its
|
|
802
|
+
* result. See `MATRIX_TAG` in runtime/v1/stdlib/matrix.ts.
|
|
803
|
+
*/
|
|
804
|
+
/**
|
|
805
|
+
* Which declaration of an overloaded user function or method a call
|
|
806
|
+
* reaches, chosen from the VALUES: the emitted dispatcher's job when the
|
|
807
|
+
* types are not written down where the call is (see
|
|
808
|
+
* V5ToJsVisitor.emitOverloadDispatcher).
|
|
809
|
+
*
|
|
810
|
+
* Candidates are `[fn, min, max, declared parameter types]`. Those whose
|
|
811
|
+
* arity fits are scored by how well each argument's own type matches the
|
|
812
|
+
* declared one — a user-defined object by its `__type`, a drawing by its
|
|
813
|
+
* `__builtin`, an array by its first element, a number as int or float —
|
|
814
|
+
* and the best wins; a tie goes to the first declared.
|
|
815
|
+
*/
|
|
816
|
+
pickOverload(name: string, args: any[], candidates: Array<[Function, number, number, Array<string | null>]>): Function | null;
|
|
817
|
+
/** Calls the overload a dispatcher picked, binding keyword arguments by its own parameter names. */
|
|
818
|
+
callPicked(fn: Function, args: any[], name: string): any;
|
|
819
|
+
/** Loop signals thrown across a `switch` arm's function boundary. */
|
|
820
|
+
readonly LOOP_BREAK: Readonly<{
|
|
821
|
+
loopSignal: "break";
|
|
822
|
+
}>;
|
|
823
|
+
readonly LOOP_CONTINUE: Readonly<{
|
|
824
|
+
loopSignal: "continue";
|
|
825
|
+
}>;
|
|
826
|
+
builtinMethod(id: string, receiver: any, name: string, ...args: any[]): any;
|
|
827
|
+
/**
|
|
828
|
+
* Method names only the DRAWING namespaces answer.
|
|
829
|
+
*
|
|
830
|
+
* Derived from the registry rather than listed, so a drawing member added
|
|
831
|
+
* later is covered and a name a collection also spells — `copy`, `get`,
|
|
832
|
+
* `set` — is not. Which of `line` and `box` a name belongs to does not
|
|
833
|
+
* matter here: the call is a no-op either way.
|
|
834
|
+
*/
|
|
835
|
+
private static drawingOnly;
|
|
836
|
+
private static drawingOnlyMethods;
|
|
837
|
+
/** Which built-in namespace owns this value's methods, or null. */
|
|
838
|
+
private static receiverNamespace;
|
|
839
|
+
/** Looks a drawing up, or throws — a stale id is a bug, not a no-op. */
|
|
840
|
+
/**
|
|
841
|
+
* Titles of every `enum` member this script declares, keyed by the member's
|
|
842
|
+
* own value — `"OscType.rsi"` → `"Relative Strength Index"`.
|
|
843
|
+
*
|
|
844
|
+
* ── Why a member is a STRING and not an object ──────────────────────────
|
|
845
|
+
*
|
|
846
|
+
* Because `==` has to work, and the emitter emits JavaScript `==`. A script
|
|
847
|
+
* variable holds a Series, so `x == OscType.rsi` compares a Series to the
|
|
848
|
+
* member; JavaScript coerces an object against a PRIMITIVE via `valueOf`
|
|
849
|
+
* and compares two OBJECTS by reference, so a member object would have made
|
|
850
|
+
* every such comparison false — silently, on every bar.
|
|
851
|
+
*
|
|
852
|
+
* A string carries the enum name as a prefix, which keeps the rule the
|
|
853
|
+
* manual states: "Scripts cannot compare members of different enums ... even
|
|
854
|
+
* if the fields have identical names and titles." `A.up` and `B.up` are
|
|
855
|
+
* different strings.
|
|
856
|
+
*
|
|
857
|
+
* The TITLE cannot live in the member for the same reason, so it lives here
|
|
858
|
+
* — read back by `str.tostring`, which the manual calls "the only way to
|
|
859
|
+
* retrieve its title".
|
|
860
|
+
*/
|
|
861
|
+
enumTitles: Map<string, string>;
|
|
862
|
+
/** Members of each enum, in declaration order — what a dropdown lists. */
|
|
863
|
+
enumMembers: Map<string, string[]>;
|
|
864
|
+
/**
|
|
865
|
+
* Records an enum declaration and returns its member table.
|
|
866
|
+
*
|
|
867
|
+
* Called once per bar from the emitted script, and idempotent: re-recording
|
|
868
|
+
* the same titles is a no-op, and the table it returns holds the same
|
|
869
|
+
* STRINGS every time, which is what makes `==` work across bars.
|
|
870
|
+
*/
|
|
871
|
+
declareEnum(name: string, titles: Record<string, string>): Record<string, string>;
|
|
872
|
+
/** The title of an enum member, or undefined when the value is not one. */
|
|
873
|
+
enumTitle(value: any): string | undefined;
|
|
874
|
+
/**
|
|
875
|
+
* Every live drawing of one kind, oldest first — `polyline.all`,
|
|
876
|
+
* `linefill.all`, `label.all`.
|
|
877
|
+
*
|
|
878
|
+
* Reads the EVICTION QUEUE rather than filtering `drawings`, so the order
|
|
879
|
+
* is the one Pine documents (creation order) and deleted ids are already
|
|
880
|
+
* gone from it.
|
|
881
|
+
*/
|
|
882
|
+
drawingIds(kind: Drawing["kind"]): string[];
|
|
883
|
+
/** `na`, in the sense every drawing accessor has to answer to. */
|
|
884
|
+
private static isNa;
|
|
885
|
+
/**
|
|
886
|
+
* A stand-in drawing for an `na` id: writes are discarded, reads are `na`.
|
|
887
|
+
*
|
|
888
|
+
* ── Why an na id is not an error ────────────────────────────────────────
|
|
889
|
+
*
|
|
890
|
+
* Because TradingView's own examples depend on it, on three separate pages.
|
|
891
|
+
*
|
|
892
|
+
* `v5-concepts-lines-and-boxes-02` declares `var line periodLine = na`
|
|
893
|
+
* and calls `line.set_color(periodLine, finalColor)` on the first period
|
|
894
|
+
* change — before anything has been assigned to it. Its OTHER branch is
|
|
895
|
+
* guarded with `else if not na(periodLine)`, so the author knew the value
|
|
896
|
+
* could be absent and did not guard the branch where it always is.
|
|
897
|
+
*
|
|
898
|
+
* `v5-language-loops-17` fills an array with `array.new<line>(20)` — 20
|
|
899
|
+
* `na` lines — and immediately loops `for pivotLine in pivotLines`
|
|
900
|
+
* reading `pivotLine.get_x2()`.
|
|
901
|
+
*
|
|
902
|
+
* `v4-essential-drawings-12` and `-14` are the published Pivot Points
|
|
903
|
+
* indicators: three `var line ... = na` declarations and a block that
|
|
904
|
+
* moves them before the first assignment.
|
|
905
|
+
*
|
|
906
|
+
* All four ran on TradingView, so a setter on an absent drawing does
|
|
907
|
+
* nothing and a getter on one returns `na`. Modelling that here rather than
|
|
908
|
+
* at each of the forty call sites keeps `getDrawing`'s contract — "hand me
|
|
909
|
+
* the record" — and makes a STALE id still an error, which it is.
|
|
910
|
+
*
|
|
911
|
+
* Fresh each time, so a discarded write can never be read back.
|
|
912
|
+
*/
|
|
913
|
+
private static naDrawing;
|
|
914
|
+
getDrawing(id: any, fn: string): Drawing;
|
|
915
|
+
is_history: boolean;
|
|
916
|
+
is_realtime: boolean;
|
|
917
|
+
is_new: boolean;
|
|
918
|
+
/** True only during a realtime bar's closing execution (`Session.commit`); see `barstate.isconfirmed`. */
|
|
919
|
+
is_confirmed: boolean;
|
|
920
|
+
is_last: boolean;
|
|
921
|
+
position: Position;
|
|
922
|
+
/** Bar timestamp by bar index — see the note where it is written. */
|
|
923
|
+
barTimes: number[];
|
|
924
|
+
/** The bar a timestamp falls on, or NaN when no bar has carried it. */
|
|
925
|
+
barIndexOfTime(time: number): number;
|
|
926
|
+
cash: number;
|
|
927
|
+
/**
|
|
928
|
+
* Host overrides for the `strategy()` / `indicator()` declaration.
|
|
929
|
+
*
|
|
930
|
+
* TradingView's settings dialog lets a user change a strategy's starting
|
|
931
|
+
* capital or pyramiding without editing the script, and a host needs the
|
|
932
|
+
* same. They are consulted by `declareScript`, which is the only moment
|
|
933
|
+
* they can take effect: `cash` moves with the first fill, so seeding it
|
|
934
|
+
* afterwards would backdate money the broker has already spent.
|
|
935
|
+
*
|
|
936
|
+
* A key absent here means "use whatever the script declared".
|
|
937
|
+
*/
|
|
938
|
+
propertyOverrides: Record<string, any>;
|
|
939
|
+
/**
|
|
940
|
+
* What the SCRIPT declared, before any host override.
|
|
941
|
+
*
|
|
942
|
+
* Kept apart so a settings panel can offer "reset to the script's value"
|
|
943
|
+
* — reading it back off the Context afterwards returns the override, which
|
|
944
|
+
* would make the reset button restore the thing it was resetting from.
|
|
945
|
+
*/
|
|
946
|
+
declaredProperties: Record<string, any>;
|
|
947
|
+
/**
|
|
948
|
+
* The equity the strategy started with, for `strategy.initial_capital`.
|
|
949
|
+
*
|
|
950
|
+
* Held separately from `cash`, which moves with every fill — the two are
|
|
951
|
+
* equal only until the first trade closes, and the difference between them
|
|
952
|
+
* IS the realised profit.
|
|
953
|
+
*
|
|
954
|
+
* Set from `strategy(initial_capital = ...)` by declareScript().
|
|
955
|
+
*/
|
|
956
|
+
initialCapital: number;
|
|
957
|
+
trades: Trade[];
|
|
958
|
+
orders: any[];
|
|
959
|
+
/**
|
|
960
|
+
* The equity curve, one point per CLOSED bar, for strategies only.
|
|
961
|
+
*
|
|
962
|
+
* A study never opens a position, so sampling one would append a flat
|
|
963
|
+
* line of `initial_capital` to every indicator's render model.
|
|
964
|
+
*/
|
|
965
|
+
equityCurve: EquityPoint[];
|
|
966
|
+
/**
|
|
967
|
+
* Excursion of the position currently open, in currency, reset when a
|
|
968
|
+
* position is opened from flat and read when it closes. Both are >= 0.
|
|
969
|
+
*
|
|
970
|
+
* Tracked on the Context rather than on the Position because they are
|
|
971
|
+
* measured across bars and the broker only ever sees one: by the time
|
|
972
|
+
* `closeAt` runs, the high the trade reached three bars ago is gone.
|
|
973
|
+
*/
|
|
974
|
+
positionRunup: number;
|
|
975
|
+
positionDrawdown: number;
|
|
976
|
+
/** The largest absolute position size held at any point in the run. */
|
|
977
|
+
maxContractsHeld: number;
|
|
978
|
+
/** Commission charged across the whole run, for the performance summary. */
|
|
979
|
+
commissionPaid: number;
|
|
980
|
+
/**
|
|
981
|
+
* The first and last close the strategy saw, for the buy & hold baseline
|
|
982
|
+
* TradingView draws against the equity curve. Recorded here because the
|
|
983
|
+
* render model is built from the Context alone and the candles are the
|
|
984
|
+
* caller's.
|
|
985
|
+
*/
|
|
986
|
+
firstClose: number;
|
|
987
|
+
lastClose: number;
|
|
988
|
+
/**
|
|
989
|
+
* Market orders placed on this bar, waiting for the next bar's open.
|
|
990
|
+
*
|
|
991
|
+
* `setBar` drains it; see MarketOrder in ./stdlib/strategy.ts for why a
|
|
992
|
+
* market order does not fill on the bar that places it.
|
|
993
|
+
*/
|
|
994
|
+
marketOrders: MarketOrder[];
|
|
995
|
+
/** Orders filled this run: entries and exits. See `Broker.countOrder`. */
|
|
996
|
+
filledOrderCount: number;
|
|
997
|
+
/** The closed trades' profit, summed as they close, in order: `netProfit()` reads it instead of re-adding every trade. */
|
|
998
|
+
netProfitSum: number;
|
|
999
|
+
inputDefs: InputDef[];
|
|
1000
|
+
userInputs: Record<string, any>;
|
|
1001
|
+
private inputCounter;
|
|
1002
|
+
opsv2_na: number;
|
|
1003
|
+
scriptMeta: ScriptMeta | null;
|
|
1004
|
+
__sandbox: any;
|
|
1005
|
+
private securityData;
|
|
1006
|
+
private htfStates;
|
|
1007
|
+
requestedSecurities: Set<string>;
|
|
1008
|
+
/**
|
|
1009
|
+
* The language profile this script runs under. Drives version-dependent
|
|
1010
|
+
* runtime behaviour: banned identifiers (`bar_index` vs `n`) and the
|
|
1011
|
+
* security() lookahead default.
|
|
1012
|
+
*
|
|
1013
|
+
* profiles/ depends only on version.ts and nothing in runtime/, so this
|
|
1014
|
+
* import direction introduces no cycle.
|
|
1015
|
+
*/
|
|
1016
|
+
readonly profile: LanguageProfile;
|
|
1017
|
+
constructor(profile?: LanguageProfile);
|
|
1018
|
+
private initBaseSeries;
|
|
1019
|
+
/**
|
|
1020
|
+
* Resets the entire execution environment for a new pass.
|
|
1021
|
+
* Crucially, preserves `inputDefs` and `userInputs`.
|
|
1022
|
+
*/
|
|
1023
|
+
reset(): void;
|
|
1024
|
+
/**
|
|
1025
|
+
* Records the script's `study()` / `strategy()` directive metadata.
|
|
1026
|
+
* Called from transpiled code; positional args are mapped to named params
|
|
1027
|
+
* in directive-declaration order, with keyword args (prefix-stripped) taking
|
|
1028
|
+
* precedence. Idempotent across bars.
|
|
1029
|
+
*/
|
|
1030
|
+
declareScript(kind: ScriptKind, positional?: any[], kwargs?: Record<string, any>): void;
|
|
1031
|
+
/**
|
|
1032
|
+
* @param meta the input's CONSTRAINTS and grouping — `minval`, `maxval`,
|
|
1033
|
+
* `step`, `options`, `tooltip`, `inline`, `group`, `confirm`,
|
|
1034
|
+
* `display`. Recorded on the definition so a host can render the
|
|
1035
|
+
* control, and the numeric three are ENFORCED on the value below.
|
|
1036
|
+
*
|
|
1037
|
+
* They used to be accepted and thrown away, on the reasoning that a
|
|
1038
|
+
* constraint the engine does not enforce is worse than no constraint.
|
|
1039
|
+
* The reasoning was right and the conclusion was not: the answer is
|
|
1040
|
+
* to enforce it. `minval` appears on seventeen of TradingView's own
|
|
1041
|
+
* examples and `options` restricts an input to a fixed set, and a
|
|
1042
|
+
* user override outside either was being taken at face value.
|
|
1043
|
+
*/
|
|
1044
|
+
registerInput(defval: any, title?: string, type?: string, meta?: Record<string, any>): any;
|
|
1045
|
+
/** `registerInput`'s last answer for each input, by its position in the run. */
|
|
1046
|
+
private inputMemo;
|
|
1047
|
+
/** Clamp to `minval`/`maxval` and fall back to the default outside `options`. */
|
|
1048
|
+
private static constrainInput;
|
|
1049
|
+
/**
|
|
1050
|
+
* SMART CALL EXECUTION
|
|
1051
|
+
* Handles:
|
|
1052
|
+
* 1. Registry Lookup (Values vs Functions)
|
|
1053
|
+
* 2. Context Injection
|
|
1054
|
+
* 3. Keyword Argument Resolution (kwargs)
|
|
1055
|
+
*/
|
|
1056
|
+
/**
|
|
1057
|
+
* How the CURRENT call was written: which parameters the caller named, and
|
|
1058
|
+
* how many arguments it passed positionally.
|
|
1059
|
+
*
|
|
1060
|
+
* `call` flattens keyword arguments into the positional array below, which
|
|
1061
|
+
* is what a JavaScript function can receive — and that flattening throws
|
|
1062
|
+
* away the one thing an OVERLOADED built-in needs. `line.new` has two
|
|
1063
|
+
* published signatures, the registry holds one, and which slot a value
|
|
1064
|
+
* belongs to depends on whether the caller wrote it as `width = 3` or as
|
|
1065
|
+
* the fifth thing in the list. See `pointTail` in stdlib/drawings.ts.
|
|
1066
|
+
*
|
|
1067
|
+
* Set for the duration of one call and restored afterwards, so a built-in
|
|
1068
|
+
* that calls another does not read its caller's shape.
|
|
1069
|
+
*/
|
|
1070
|
+
keywordArguments: ReadonlySet<string>;
|
|
1071
|
+
/** How many arguments the caller passed before any keyword. */
|
|
1072
|
+
positionalArguments: number;
|
|
1073
|
+
/**
|
|
1074
|
+
* Resolve a trailing keyword bundle against a parameter list.
|
|
1075
|
+
*
|
|
1076
|
+
* The same rule the built-in path applies, over names the emitter supplies
|
|
1077
|
+
* rather than names the registry does. A gap is left `undefined` so the
|
|
1078
|
+
* emitted function's own JavaScript default takes over, which is how a
|
|
1079
|
+
* Pine parameter default is expressed.
|
|
1080
|
+
*/
|
|
1081
|
+
private bindKeywordArguments;
|
|
1082
|
+
/**
|
|
1083
|
+
* Does `fname` have a parameter called `name` at THIS version?
|
|
1084
|
+
*
|
|
1085
|
+
* The stdlib is one implementation across versions, so a parameter added
|
|
1086
|
+
* for v6 — `active` on every input, `text_formatting` on labels — is on
|
|
1087
|
+
* the shared function. V6_ONLY_PARAMS says which those are, and below v6
|
|
1088
|
+
* they are treated as absent.
|
|
1089
|
+
*/
|
|
1090
|
+
private parameterExists;
|
|
1091
|
+
/**
|
|
1092
|
+
* Positional arguments, re-slotted for the signature THIS version has.
|
|
1093
|
+
*
|
|
1094
|
+
* The stdlib keeps one signature per function, the union of every
|
|
1095
|
+
* version's, and two kinds of version difference sit in the middle of one:
|
|
1096
|
+
*
|
|
1097
|
+
* below v6 a parameter v6 ADDED (V6_ONLY_PARAMS). Filled by position
|
|
1098
|
+
* it is cleared, so it does what it did before it existed:
|
|
1099
|
+
* nothing. Keyword use is refused separately, in the binding
|
|
1100
|
+
* below.
|
|
1101
|
+
* at v6 a function whose v6 order differs from the shared one —
|
|
1102
|
+
* v6 removed `transp` and `when` from the middle of several.
|
|
1103
|
+
* Positional arguments are placed BY NAME from V6_POSITIONAL:
|
|
1104
|
+
* v6 `plot(close, "p", c, 2, style, false)` means trackprice =
|
|
1105
|
+
* false, not transp. A name the shared signature lacks is
|
|
1106
|
+
* dropped and recorded, like an unknown keyword.
|
|
1107
|
+
*/
|
|
1108
|
+
private positionalForVersion;
|
|
1109
|
+
call(id: string, fn: Function, ...args: any[]): any;
|
|
1110
|
+
/**
|
|
1111
|
+
* Pins a runtime error to the Pine source position of the call it passed
|
|
1112
|
+
* through, so the host can say WHERE the script failed.
|
|
1113
|
+
*
|
|
1114
|
+
* Every call id carries its position -- `array.get@L45:C12`, and
|
|
1115
|
+
* `array.get@L4:C14@L` for a call inside the library imported as `L` -- so
|
|
1116
|
+
* the position costs nothing until something throws: this runs only on
|
|
1117
|
+
* the way out, once per frame, and the first frame an error passes through
|
|
1118
|
+
* is the innermost call, which is the one that raised it. It is a property
|
|
1119
|
+
* of the error rather than text in its message, which other code matches.
|
|
1120
|
+
*
|
|
1121
|
+
* An error inside a library is located at the library's own line, and
|
|
1122
|
+
* the first frame outside it -- the call in the user's own script, the
|
|
1123
|
+
* line an editor can show -- is kept as `caller`.
|
|
1124
|
+
*
|
|
1125
|
+
* Not covered: an error raised in script code that is not inside any call
|
|
1126
|
+
* (nothing the stdlib throws is), which has no position to report.
|
|
1127
|
+
*/
|
|
1128
|
+
private locateError;
|
|
1129
|
+
/**
|
|
1130
|
+
* Resolves a built-in by name straight from the registry.
|
|
1131
|
+
*
|
|
1132
|
+
* Pine keeps functions and variables in separate namespaces, so
|
|
1133
|
+
* `sma = sma(close, 10)` is legal and common. The emitted JavaScript has no
|
|
1134
|
+
* such separation: `let opsv2_sma = ...` shadows the sandbox binding, and
|
|
1135
|
+
* the call inside its own initialiser saw the variable (undefined) instead
|
|
1136
|
+
* of the function. The transpiler routes shadowed built-in callees here.
|
|
1137
|
+
*/
|
|
1138
|
+
/**
|
|
1139
|
+
* The stdlib entry for `name` AT THIS SCRIPT'S VERSION.
|
|
1140
|
+
*
|
|
1141
|
+
* `REGISTRY` is deliberately the union of every version's view, so a few
|
|
1142
|
+
* names carry two meanings in it and the union's last writer decides which
|
|
1143
|
+
* one survives. `float`, `bool` and `string` are the v1–v3 `input()` type
|
|
1144
|
+
* CONSTANTS and v4's casting FUNCTIONS; `session` is a v1–v3 constant and a
|
|
1145
|
+
* v5 namespace. Reading the union directly handed a v4 script the string
|
|
1146
|
+
* "float" where it had called `float(na)` — "[Context.call] ... The
|
|
1147
|
+
* provided reference is not a function".
|
|
1148
|
+
*
|
|
1149
|
+
* This is the preference `createStdlib` already applies to the sandbox,
|
|
1150
|
+
* applied to the CALL path as well. Falling back to the union keeps a name
|
|
1151
|
+
* the view does not carry resolvable: the transpiler has already decided
|
|
1152
|
+
* what a script may spell, and the runtime is not the place to refuse a
|
|
1153
|
+
* second time.
|
|
1154
|
+
*/
|
|
1155
|
+
private entryFor;
|
|
1156
|
+
/** `entryFor`'s answers: the registries are large and the same few names are asked for on every bar. */
|
|
1157
|
+
private entries;
|
|
1158
|
+
builtin(name: string): any;
|
|
1159
|
+
/**
|
|
1160
|
+
* `and` over operands that have ALREADY been evaluated.
|
|
1161
|
+
*
|
|
1162
|
+
* Pine does not short-circuit, so the emitter passes every operand as an
|
|
1163
|
+
* argument and lets JavaScript's own evaluation order do the work. The
|
|
1164
|
+
* body is trivial on purpose: all the meaning is in the call having
|
|
1165
|
+
* happened at all. See visitAnd_expr in transpiler/v1/ToJsVisitor.ts.
|
|
1166
|
+
*/
|
|
1167
|
+
allOf(...operands: boolean[]): boolean;
|
|
1168
|
+
/** `or` over operands that have already been evaluated. See `allOf`. */
|
|
1169
|
+
anyOf(...operands: boolean[]): boolean;
|
|
1170
|
+
/**
|
|
1171
|
+
* Per call site: the bar and pass it was last reached on, and whether it
|
|
1172
|
+
* has run at all. One lookup per call where there were three.
|
|
1173
|
+
*/
|
|
1174
|
+
private siteReach;
|
|
1175
|
+
/**
|
|
1176
|
+
* The call sites a script is known to reach more than once on a bar. Null
|
|
1177
|
+
* (the default, and always for a forming bar) takes a rollback point at
|
|
1178
|
+
* every stateful site on every bar; a set takes one only at the sites in it,
|
|
1179
|
+
* and `missedRepeat` reports the first site that needed one and had none.
|
|
1180
|
+
*
|
|
1181
|
+
* Performance: only `Session.runHistory` sets it, for the history bars,
|
|
1182
|
+
* which are never re-ticked. A live or forming bar is re-ticked and rolled
|
|
1183
|
+
* back at every site, so it must keep this null: `runHistoryOnce` resets it
|
|
1184
|
+
* before returning, and anything else that runs bars must not set it.
|
|
1185
|
+
* Guarded by tests/v1/runtime/rollback_snapshot_work.test.ts. See
|
|
1186
|
+
* docs/common/ARCHITECTURE.md ("Performance invariants").
|
|
1187
|
+
*/
|
|
1188
|
+
repeatingSites: Set<string> | null;
|
|
1189
|
+
/** The site that was reached again on a bar with no rollback point; see `repeatingSites`. */
|
|
1190
|
+
missedRepeat: string | null;
|
|
1191
|
+
/** Counts passes over a bar: `forgetBarProgress` starts a new one. */
|
|
1192
|
+
private barPass;
|
|
1193
|
+
/**
|
|
1194
|
+
* Call sites that own series state, learned the first time one asks for it.
|
|
1195
|
+
*
|
|
1196
|
+
* A site that has run and never asked — `hl2`, `math.abs`, `array.get` —
|
|
1197
|
+
* has nothing to roll back, so it is skipped entirely. Without this every
|
|
1198
|
+
* stateless call took the snapshot path on every bar.
|
|
1199
|
+
*/
|
|
1200
|
+
private statefulSites;
|
|
1201
|
+
/**
|
|
1202
|
+
* State keys at or beneath each call site, so a rollback does not have to
|
|
1203
|
+
* scan every state in the run to find the handful it owns.
|
|
1204
|
+
*
|
|
1205
|
+
* A built-in may call another through `ctx.call` — `ta.stoch` delegates to
|
|
1206
|
+
* `ta.highest` — and the nested state hangs below the outer site's key, so
|
|
1207
|
+
* rolling the outer one back has to take the inner ones with it.
|
|
1208
|
+
*/
|
|
1209
|
+
private stateKeysBySite;
|
|
1210
|
+
/** State keys already indexed in `stateKeysBySite`. */
|
|
1211
|
+
private indexedStateKeys;
|
|
1212
|
+
/**
|
|
1213
|
+
* Rollback points for the sites in `repeatableCalls`, taken at their first
|
|
1214
|
+
* reach of the bar, and dropped by `forgetBarProgress` when the bar moves
|
|
1215
|
+
* on or is re-run. The per-entry `bar` is still checked, because a host
|
|
1216
|
+
* may drive the stdlib directly and never call `setBar` at all.
|
|
1217
|
+
*
|
|
1218
|
+
* ── One slot per BAR, not one per call ─────────────────────────────────
|
|
1219
|
+
*
|
|
1220
|
+
* A series function's history has one slot per bar. Reaching its call site
|
|
1221
|
+
* again on the same bar OVERWRITES that bar's slot rather than appending a
|
|
1222
|
+
* second one, and the value returned is the one including whatever was
|
|
1223
|
+
* just written. Earlier bars keep whatever their LAST reach wrote.
|
|
1224
|
+
*
|
|
1225
|
+
* That one rule accounts for every loop probe TradingView has answered:
|
|
1226
|
+
*
|
|
1227
|
+
* - Same arguments every reach (the loops export): each reach writes the
|
|
1228
|
+
* same value into the same slot, so all three reads agree and
|
|
1229
|
+
* `loopEma / 3` equals a plain `ta.ema(close, 10)` on all 2,475 bars.
|
|
1230
|
+
* - Different arguments (the loopargs export): each reach overwrites,
|
|
1231
|
+
* and the three reads differ. Replaying this rule against that export
|
|
1232
|
+
* reproduces TradingView on all 2,464 bars it reports, na-parity
|
|
1233
|
+
* included — where "the first reach wins", "the last reach wins" and
|
|
1234
|
+
* "each reach keeps its own history" each miss every single bar.
|
|
1235
|
+
* - Never reached at all: nothing is written, so the history does not
|
|
1236
|
+
* advance and the site goes STALE. `probe_call_inside_if` and
|
|
1237
|
+
* `probe_ternary_both_arms` both confirm that, and it is why this is
|
|
1238
|
+
* not symmetric with the two cases above.
|
|
1239
|
+
*
|
|
1240
|
+
* Overwriting is implemented as rollback-and-rerun, which is the same
|
|
1241
|
+
* thing from the outside and does not require every stateful built-in to
|
|
1242
|
+
* grow a re-entry path of its own.
|
|
1243
|
+
*
|
|
1244
|
+
* Which sites get one, and why not all of them, is at the call site in
|
|
1245
|
+
* `call()`.
|
|
1246
|
+
*/
|
|
1247
|
+
private barStateSnapshot;
|
|
1248
|
+
/**
|
|
1249
|
+
* Forgets everything scoped to ONE PASS over the current bar.
|
|
1250
|
+
*
|
|
1251
|
+
* Called at the top of `setBar`, which covers every bar and every re-tick
|
|
1252
|
+
* of a forming one, and again from `restoreSnapshot` so that rolling back
|
|
1253
|
+
* to a baseline restores this too. "One pass" includes the script's own
|
|
1254
|
+
* execution-order counters, not just the broker's bookkeeping.
|
|
1255
|
+
*
|
|
1256
|
+
* The two order guards are the subtle half. They are NOT part of
|
|
1257
|
+
* `captureSnapshot`, so a rollback restored the broker's position and its
|
|
1258
|
+
* pending orders while leaving the guards saying "already done for this
|
|
1259
|
+
* bar". Tick 2 then skipped both the market fills and the pending-order
|
|
1260
|
+
* matching, and the script saw a position that had already been closed.
|
|
1261
|
+
*
|
|
1262
|
+
* `repeatableCalls` deliberately survives: which sites repeat is learned
|
|
1263
|
+
* about the SCRIPT, not about the bar.
|
|
1264
|
+
*/
|
|
1265
|
+
private forgetBarProgress;
|
|
1266
|
+
/** Every state entry belonging to `key` or nested beneath it. */
|
|
1267
|
+
private collectCallState;
|
|
1268
|
+
/** Remembers `key`'s state so a later reach on this bar can undo its own. */
|
|
1269
|
+
private snapshotCallState;
|
|
1270
|
+
/** Puts `key` back as it was at the start of the bar. */
|
|
1271
|
+
private restoreCallState;
|
|
1272
|
+
getPersistentState<T>(initFn: () => T): T;
|
|
1273
|
+
/** Declaration sites whose initialiser has already run, keyed by @L:C. */
|
|
1274
|
+
private varInitialised;
|
|
1275
|
+
/** Series declared `varip`, exempt from intra-bar rollback. */
|
|
1276
|
+
private varipSeries;
|
|
1277
|
+
/** Every series declared `var` or `varip`, by scoped name: what drawing eviction treats as a holder. */
|
|
1278
|
+
private varSeries;
|
|
1279
|
+
/**
|
|
1280
|
+
* `var x = expr` — initialise ONCE, then persist across bars.
|
|
1281
|
+
*
|
|
1282
|
+
* The initialiser is passed as a thunk, not a value: `var x = expr` must not
|
|
1283
|
+
* evaluate `expr` after the first bar. Passing the value would evaluate it
|
|
1284
|
+
* every bar and throw the result away, which is invisible for `0.0` and
|
|
1285
|
+
* quite visible for `var count = count + 1` or anything with a side effect.
|
|
1286
|
+
*
|
|
1287
|
+
* Persistence re-stamps the LAST value onto the current bar, read from
|
|
1288
|
+
* `valueOf()` rather than `get(1)`. The two differ exactly when the variable
|
|
1289
|
+
* was last written on an earlier bar than the previous one — a `var` inside
|
|
1290
|
+
* a conditional block — where `get(1)` finds a hole and returns NaN while
|
|
1291
|
+
* `valueOf()` returns the value that is actually still held. Pine persists
|
|
1292
|
+
* the value, so `valueOf()` is the right question.
|
|
1293
|
+
*
|
|
1294
|
+
* Re-stamping every bar is also what makes `:=` work unchanged: it writes
|
|
1295
|
+
* the current bar's slot through `new_var`, and the next bar carries that
|
|
1296
|
+
* forward. No write-back path and no coupling to the assignment emitter.
|
|
1297
|
+
*
|
|
1298
|
+
* @param key the declaration's @L<line>:C<col> site, so two DIFFERENT
|
|
1299
|
+
* `var` declarations do not share an initialised flag.
|
|
1300
|
+
*
|
|
1301
|
+
* ── `var` inside a user function ────────────────────────────────────────
|
|
1302
|
+
*
|
|
1303
|
+
* Every call site gets its OWN `var`, which is what Pine does — "each
|
|
1304
|
+
* function call has its own instance of the series variables". Given
|
|
1305
|
+
*
|
|
1306
|
+
* f() =>
|
|
1307
|
+
* var c = 0
|
|
1308
|
+
* c := c + 1
|
|
1309
|
+
* c
|
|
1310
|
+
* a = f()
|
|
1311
|
+
* b = f()
|
|
1312
|
+
*
|
|
1313
|
+
* both `a` and `b` count 1,2,3.
|
|
1314
|
+
*
|
|
1315
|
+
* This was wrong for a long time, and the note that stood here explained
|
|
1316
|
+
* why keying the initialised flag on the call stack was not enough: the
|
|
1317
|
+
* SERIES NAME was shared too, so per-site flags only redistributed the
|
|
1318
|
+
* error. Both halves are now in place — `seriesScope` gives each frame its
|
|
1319
|
+
* own namespace in `vars`, and this method scopes the flag into the same
|
|
1320
|
+
* namespace — so `key` alone is the declaration SITE and the frame supplies
|
|
1321
|
+
* the instance.
|
|
1322
|
+
*
|
|
1323
|
+
* `varipSeries` is scoped with them, so a `varip x` in one frame and a
|
|
1324
|
+
* `var x` in another are no longer one series and no longer share an
|
|
1325
|
+
* exemption from rollback.
|
|
1326
|
+
*/
|
|
1327
|
+
var_def(name: string, key: string, initFn: () => any, ip?: boolean): Series;
|
|
1328
|
+
/**
|
|
1329
|
+
* Pine truthiness, for anything used as a CONDITION.
|
|
1330
|
+
*
|
|
1331
|
+
* ── Why this exists ─────────────────────────────────────────────────────
|
|
1332
|
+
*
|
|
1333
|
+
* `new_var` returns a Series, and in JavaScript EVERY object is truthy. So
|
|
1334
|
+
* a condition stored in a variable was permanently true:
|
|
1335
|
+
*
|
|
1336
|
+
* up = close > open // a Series wrapping true/false
|
|
1337
|
+
* plot(up ? 1 : 0) // 1 on every bar, forever
|
|
1338
|
+
*
|
|
1339
|
+
* Relational operators hid it — `close > open` coerces through valueOf() and
|
|
1340
|
+
* works — but `?:`, `if`, `and`, `or` and `not` use ToBoolean, which does not
|
|
1341
|
+
* consult valueOf(). Naming a condition and reusing it is how nearly every
|
|
1342
|
+
* published script is written, so every branch in them was taken.
|
|
1343
|
+
*
|
|
1344
|
+
* The corpus suite could not see this: a script whose conditions are all
|
|
1345
|
+
* true still runs and still plots finite numbers.
|
|
1346
|
+
*
|
|
1347
|
+
* ── The rule ────────────────────────────────────────────────────────────
|
|
1348
|
+
*
|
|
1349
|
+
* Unwrap first, then apply Pine's own conversion: `na` is false, 0 is false,
|
|
1350
|
+
* any other number is true. v1–v3 coerce numbers to bool freely (`isSunday()
|
|
1351
|
+
* and openPrice ? ...` is published v1), so this must accept numbers rather
|
|
1352
|
+
* than demand a bool.
|
|
1353
|
+
*/
|
|
1354
|
+
truthy(v: any): boolean;
|
|
1355
|
+
/**
|
|
1356
|
+
* FACTORY: Creates or Updates a Series Object.
|
|
1357
|
+
*/
|
|
1358
|
+
new_var(name: string, val: any): Series;
|
|
1359
|
+
/**
|
|
1360
|
+
* `new_var` for the series behind a history reference on an expression,
|
|
1361
|
+
* `(close + 0)[3]`: bar-indexed, carrying its last value over the bars the
|
|
1362
|
+
* expression was not evaluated on. See `Series.updateCarry`.
|
|
1363
|
+
*/
|
|
1364
|
+
new_anon(name: string, val: any): Series;
|
|
1365
|
+
/**
|
|
1366
|
+
* FACTORY (Tuple): Handles assignments like [a, b] = func()
|
|
1367
|
+
*/
|
|
1368
|
+
new_vars(names: string[], values: any): Series[];
|
|
1369
|
+
/**
|
|
1370
|
+
* `for … in` — one iteration handle over an array, matrix or map.
|
|
1371
|
+
*
|
|
1372
|
+
* Yields `[key, value]` pairs. What those mean is the collection's business,
|
|
1373
|
+
* and the manual states each one:
|
|
1374
|
+
*
|
|
1375
|
+
* array "item ... holds sequential values or references from the
|
|
1376
|
+
* specified collection_id" and index is "the index ... of the
|
|
1377
|
+
* retrieved item"
|
|
1378
|
+
* matrix the item is a ROW array, and the index is "the row's position
|
|
1379
|
+
* within the matrix"
|
|
1380
|
+
* map "the index contains the key, and item contains the associated
|
|
1381
|
+
* value from each key-value pair"
|
|
1382
|
+
*
|
|
1383
|
+
* A matrix is represented as an array of row arrays, so it needs no branch
|
|
1384
|
+
* of its own here: iterating an array of rows already yields rows.
|
|
1385
|
+
*
|
|
1386
|
+
* ── The size rule is different for maps, so the code is too ──────────────
|
|
1387
|
+
*
|
|
1388
|
+
* "Scripts can modify the sizes of arrays and matrices directly within a
|
|
1389
|
+
* for…in loop's local scope. When a for…in loop changes the size of a
|
|
1390
|
+
* collection during an iteration, the loop's header uses the UPDATED size
|
|
1391
|
+
* to control subsequent iterations."
|
|
1392
|
+
*
|
|
1393
|
+
* That is why the array branch re-reads `.length` every step instead of
|
|
1394
|
+
* capturing it once. A snapshot would run past the end of a shrunk array
|
|
1395
|
+
* and stop short of a grown one.
|
|
1396
|
+
*
|
|
1397
|
+
* "Attempting to add or remove a map's key-value pairs while looping
|
|
1398
|
+
* through it with this structure typically causes a runtime error."
|
|
1399
|
+
*
|
|
1400
|
+
* So the map branch does the opposite: it takes the keys once and then
|
|
1401
|
+
* REFUSES a size change, rather than silently iterating a stale key list.
|
|
1402
|
+
* `arity` is how many loop variables the script wrote, which is what makes
|
|
1403
|
+
* the single-variable form on a map reportable — the manual's second form
|
|
1404
|
+
* is the only one maps accept.
|
|
1405
|
+
*/
|
|
1406
|
+
forIn(collection: any, arity: number, at: string): Generator<[any, any]>;
|
|
1407
|
+
/**
|
|
1408
|
+
* Universal History Accessor (Called by transpiled code: [])
|
|
1409
|
+
*/
|
|
1410
|
+
get(target: any, offset: number, id: string): number;
|
|
1411
|
+
/**
|
|
1412
|
+
* A user variable declared OUTSIDE the running function: not one of its own
|
|
1413
|
+
* locals (their ids carry the frame's scope) and not a built-in series.
|
|
1414
|
+
*/
|
|
1415
|
+
private isOuterUserSeries;
|
|
1416
|
+
/**
|
|
1417
|
+
* `v[n]` on a variable from outside the running function. TradingView keeps
|
|
1418
|
+
* a history of it per CALL SITE, recorded only when that call runs
|
|
1419
|
+
* (Pine Logs, dev-docs/tv-parity/_probes/fn-history): `[n]` is its value at
|
|
1420
|
+
* the n-th previous call of this site, not on the n-th previous bar. Before
|
|
1421
|
+
* that many calls it is na. The probe's first calls (bars 0, 0) log na, and
|
|
1422
|
+
* the next ones read the value the variable had at those first calls.
|
|
1423
|
+
*/
|
|
1424
|
+
private callSiteHistory;
|
|
1425
|
+
/** The series registered under `name`, this frame's instance first. */
|
|
1426
|
+
private findSeries;
|
|
1427
|
+
/**
|
|
1428
|
+
* Internal helper for fallback lookups
|
|
1429
|
+
*/
|
|
1430
|
+
getSeries(name: string, offset: number): number;
|
|
1431
|
+
setBar(time: number, open: number, high: number, low: number, close: number, volume: number, deferFills?: boolean): void;
|
|
1432
|
+
/** Whether the script asked to be re-run after each order fill. */
|
|
1433
|
+
get calcOnOrderFills(): boolean;
|
|
1434
|
+
/**
|
|
1435
|
+
* Set while a bar is being replayed for `calc_on_order_fills`: how far along
|
|
1436
|
+
* the bar's price path (segment index plus the share of it covered) the
|
|
1437
|
+
* last fill was, and the price there. Null otherwise.
|
|
1438
|
+
*/
|
|
1439
|
+
intrabar: {
|
|
1440
|
+
cursor: number;
|
|
1441
|
+
price: number;
|
|
1442
|
+
} | null;
|
|
1443
|
+
/** The most times one bar is re-run after fills; TradingView's own limit is unknown. */
|
|
1444
|
+
private static readonly MAX_FILL_RERUNS;
|
|
1445
|
+
/**
|
|
1446
|
+
* Runs one historical bar of a `calc_on_order_fills` strategy.
|
|
1447
|
+
*
|
|
1448
|
+
* Each fill, in the order it happens along the bar's path, re-runs the
|
|
1449
|
+
* script on the whole bar with `var` and series state put back to the bar's
|
|
1450
|
+
* start (as a realtime tick does; `varip` keeps its value), while the broker
|
|
1451
|
+
* keeps what filled. A market order the re-run places fills at once at the
|
|
1452
|
+
* price of the fill that triggered it, and a limit or stop order it places
|
|
1453
|
+
* can fill later on the same bar. The ordinary run comes last, and its
|
|
1454
|
+
* effects are what the next bar sees. Verified on TradingView's probe
|
|
1455
|
+
* calc-on-fills (dev-docs/tv-parity/_probes/calc-on-fills) and the 12/26-IT
|
|
1456
|
+
* strategy (ENGINE-BUGS.md section 1).
|
|
1457
|
+
*/
|
|
1458
|
+
runBarWithFills(time: number, open: number, high: number, low: number, close: number, volume: number, exec: () => void): void;
|
|
1459
|
+
/** The bar `fillMarketOrders` last ran on, so a re-tick does not refill. */
|
|
1460
|
+
private _marketOrdersFilledBar;
|
|
1461
|
+
/**
|
|
1462
|
+
* Registers a pre-built PlotData object for the current bar.
|
|
1463
|
+
* Each ui.ts function constructs the correctly-typed variant.
|
|
1464
|
+
*/
|
|
1465
|
+
/**
|
|
1466
|
+
* Strip the runtime wrapper off every field of a plot or fill record.
|
|
1467
|
+
*
|
|
1468
|
+
* `f = plot(...)` compiles to `ctx.new_var(...)`, so the plot id a script
|
|
1469
|
+
* hands to `fill()` arrives as a `Series`, not a string — and a `Series`
|
|
1470
|
+
* holds a back-reference to this Context, which holds the sandbox, which
|
|
1471
|
+
* holds every stdlib function. Storing one put the entire runtime inside
|
|
1472
|
+
* the render model: `structuredClone` threw, so `postMessage` threw, so
|
|
1473
|
+
* every script using `fill()` failed in any host that runs the engine in a
|
|
1474
|
+
* Worker or a backend — which is the architecture the integration guide
|
|
1475
|
+
* documents. The values were all correct; the model just could not leave
|
|
1476
|
+
* the process it was built in.
|
|
1477
|
+
*
|
|
1478
|
+
* The same applies to `plot(close, color = myColorVar)`, where the colour
|
|
1479
|
+
* is a declared variable rather than a literal.
|
|
1480
|
+
*/
|
|
1481
|
+
private plainPlotData;
|
|
1482
|
+
registerPlot(data: PlotData): void;
|
|
1483
|
+
registerFill(id1: any, id2: any, options?: {
|
|
1484
|
+
color?: string;
|
|
1485
|
+
title?: string;
|
|
1486
|
+
[k: string]: any;
|
|
1487
|
+
}): void;
|
|
1488
|
+
finalizeBar(): void;
|
|
1489
|
+
/**
|
|
1490
|
+
* Records one equity point and advances the open position's excursion.
|
|
1491
|
+
*
|
|
1492
|
+
* Run at the END of a bar, after the script and the broker have both had
|
|
1493
|
+
* it: an entry placed on this bar is already in `position`, and the high
|
|
1494
|
+
* and low of the bar it filled on count towards its run-up, which is what
|
|
1495
|
+
* TradingView reports.
|
|
1496
|
+
*/
|
|
1497
|
+
/**
|
|
1498
|
+
* Mark-to-market on the open position, at a price the caller names.
|
|
1499
|
+
*
|
|
1500
|
+
* One formula, three callers: the equity curve samples it at each bar's
|
|
1501
|
+
* close, `strategy.equity()` answers with it, and the performance report
|
|
1502
|
+
* takes it at the last close. All three had their own copy, so a change
|
|
1503
|
+
* to any of them — deducting the entry commission, say — would have
|
|
1504
|
+
* landed in one place and quietly disagreed with the other two.
|
|
1505
|
+
*
|
|
1506
|
+
* The price is a parameter rather than read from the Context because the
|
|
1507
|
+
* report deliberately uses `lastClose` and the other two use `close`.
|
|
1508
|
+
*/
|
|
1509
|
+
openProfitAt(price: number): number;
|
|
1510
|
+
/**
|
|
1511
|
+
* What one unit of the symbol's currency is worth in the strategy's
|
|
1512
|
+
* currency (`strategy(currency = ...)`) on this bar: the latest sample of
|
|
1513
|
+
* the host's `currency_rate` series at or before the bar's time. 1 when the
|
|
1514
|
+
* script asked for no other currency, or the host supplied no series, so
|
|
1515
|
+
* nothing converts and nothing throws.
|
|
1516
|
+
*
|
|
1517
|
+
* TradingView converts each closed trade at the exit bar's rate
|
|
1518
|
+
* (_probes/currency-usd-1btc). Open profit converted at the current bar's
|
|
1519
|
+
* rate, and sizing (percent_of_equity, cash qty) left unconverted, are
|
|
1520
|
+
* unverified against TradingView.
|
|
1521
|
+
*/
|
|
1522
|
+
accountRate(): number;
|
|
1523
|
+
private sampleEquity;
|
|
1524
|
+
/**
|
|
1525
|
+
* Applies a real-time tick to the forming (last) bar, per the control
|
|
1526
|
+
* protocol §7. The candle is an ABSOLUTE snapshot of the forming bar, not an
|
|
1527
|
+
* increment. On the first tick of a bar we capture the committed baseline; on
|
|
1528
|
+
* every later tick we roll back to it first, so re-evaluation never
|
|
1529
|
+
* double-counts accumulating state (e.g. `cum`, EMA `prev`, broker orders).
|
|
1530
|
+
*
|
|
1531
|
+
* The caller runs the compiled script after this, then reads provisional
|
|
1532
|
+
* output. Finalize with `commitBar()` when the bar closes.
|
|
1533
|
+
*/
|
|
1534
|
+
/**
|
|
1535
|
+
* `barstate.isnew` / `isconfirmed` follow TradingView's realtime bar states, and `Session.commit`
|
|
1536
|
+
* runs the closing execution. Probed on v5 (rt-confirmed-v5) and v6 (rt-semantics-v6,
|
|
1537
|
+
* rt-confirmed-v6); v1-v4 keep the older reading (isnew always true, isconfirmed = not realtime).
|
|
1538
|
+
*/
|
|
1539
|
+
get realtimeBarStates(): boolean;
|
|
1540
|
+
applyTick(time: number, open: number, high: number, low: number, close: number, volume: number): void;
|
|
1541
|
+
/**
|
|
1542
|
+
* Commits the forming bar permanently and advances. After this the next
|
|
1543
|
+
* `applyTick` re-captures a fresh baseline for the new forming bar.
|
|
1544
|
+
*/
|
|
1545
|
+
commitBar(): void;
|
|
1546
|
+
/** A rollback point for a sub-context (see `request.security_lower_tf`): opaque to the caller. */
|
|
1547
|
+
checkpoint(): unknown;
|
|
1548
|
+
/** Rolls this context back to a `checkpoint()`. */
|
|
1549
|
+
rollbackTo(cp: unknown): void;
|
|
1550
|
+
private captureSnapshot;
|
|
1551
|
+
private restoreSnapshot;
|
|
1552
|
+
private secKey;
|
|
1553
|
+
/** Supplies HTF candles for a (symbol, resolution); called by the session layer. */
|
|
1554
|
+
provideSecurityData(symbol: string, resolution: string, candles: SecurityCandle[]): void;
|
|
1555
|
+
/**
|
|
1556
|
+
* Seeded pseudo-random state, one stream per seed.
|
|
1557
|
+
*
|
|
1558
|
+
* Held on the Context rather than in the stdlib module so that two Contexts
|
|
1559
|
+
* — a backtest and a re-run of it — do not share a stream, and so that
|
|
1560
|
+
* `reset()` restarts the sequence. A seeded run that changed its numbers
|
|
1561
|
+
* because something else had drawn from the generator first would not be
|
|
1562
|
+
* reproducible, which is the only thing a seed is for.
|
|
1563
|
+
*/
|
|
1564
|
+
private randomStreams;
|
|
1565
|
+
/**
|
|
1566
|
+
* The next number in the stream for `seed`, in [0, 1).
|
|
1567
|
+
*
|
|
1568
|
+
* mulberry32 — small, fast, and with a period far beyond any chart. NOT
|
|
1569
|
+
* TradingView's generator, which is unpublished; see `math.random`.
|
|
1570
|
+
*/
|
|
1571
|
+
seededRandom(seed: number): number;
|
|
1572
|
+
/**
|
|
1573
|
+
* Timestamped samples for `request.financial` / `.dividends` / `.splits` /
|
|
1574
|
+
* `.earnings` / `.quandl`, keyed by kind and by the arguments that select a
|
|
1575
|
+
* series.
|
|
1576
|
+
*
|
|
1577
|
+
* Supplied by the host, like `securityData` above and for the same reason:
|
|
1578
|
+
* this engine has no connection to TradingView, and a request it cannot
|
|
1579
|
+
* answer must be refused rather than resolved to `na`.
|
|
1580
|
+
*/
|
|
1581
|
+
private requestData;
|
|
1582
|
+
/**
|
|
1583
|
+
* Supplies one non-price series.
|
|
1584
|
+
*
|
|
1585
|
+
* `key` is what runtime/v1/stdlib/request.ts builds from the call's
|
|
1586
|
+
* arguments — `"NASDAQ:AAPL|TOTAL_REVENUE|FQ"`. Samples are sorted here, so
|
|
1587
|
+
* a host may provide them in any order.
|
|
1588
|
+
*/
|
|
1589
|
+
provideRequestData(kind: string, key: string, samples: RequestSample[]): void;
|
|
1590
|
+
getRequestData(kind: string, key: string): RequestSample[] | undefined;
|
|
1591
|
+
/** The chart's own bars, as they arrive. The basis of a derived request. */
|
|
1592
|
+
private chartBars;
|
|
1593
|
+
/**
|
|
1594
|
+
* The history run's whole dataset, set by `Session.runHistory`: only the TIMES
|
|
1595
|
+
* of the bars after the current one are read, to tell where a session or a
|
|
1596
|
+
* higher-timeframe bar ends. No price is ever taken from it.
|
|
1597
|
+
*/
|
|
1598
|
+
chartAhead: readonly {
|
|
1599
|
+
time: number;
|
|
1600
|
+
}[] | null;
|
|
1601
|
+
/** Derived higher-timeframe series, keyed by resolution, extended as bars arrive. */
|
|
1602
|
+
private derivedSeries;
|
|
1603
|
+
/** Heikin-Ashi transforms of a request's candles, keyed like `securityData`. */
|
|
1604
|
+
private heikinAshi;
|
|
1605
|
+
/**
|
|
1606
|
+
* Does `symbol` name the chart's OWN instrument?
|
|
1607
|
+
*
|
|
1608
|
+
* `request.security(syminfo.tickerid, …)` is how nearly every published
|
|
1609
|
+
* script asks for a higher timeframe of what it is already looking at, and
|
|
1610
|
+
* `syminfo.tickerid` is the exchange-qualified spelling of the same thing
|
|
1611
|
+
* `syminfo.ticker` gives bare. An empty symbol means the chart's too.
|
|
1612
|
+
*/
|
|
1613
|
+
private isChartSymbol;
|
|
1614
|
+
/**
|
|
1615
|
+
* `request.security()` on the chart's own symbol, at the chart's timeframe
|
|
1616
|
+
* or a higher one, answered from the chart's own bars.
|
|
1617
|
+
*
|
|
1618
|
+
* ── Why this is exact and not an approximation ──────────────────────────
|
|
1619
|
+
*
|
|
1620
|
+
* A higher-timeframe bar IS the aggregate of the chart bars inside it: it
|
|
1621
|
+
* opens where the first one opened, closes where the last one closed, spans
|
|
1622
|
+
* their extremes and carries their total volume. That is the relationship
|
|
1623
|
+
* between the two series, not a model of it. TradingView builds its own
|
|
1624
|
+
* weekly candles from the same daily feed.
|
|
1625
|
+
*
|
|
1626
|
+
* Before this, `request.security(syminfo.tickerid, "D", close)` on a daily
|
|
1627
|
+
* chart returned `na` — the most basic call there is, silently empty on
|
|
1628
|
+
* every bar, in twenty-five of TradingView's own examples.
|
|
1629
|
+
*
|
|
1630
|
+
* ── What is still refused ───────────────────────────────────────────────
|
|
1631
|
+
*
|
|
1632
|
+
* A LOWER timeframe, and any other symbol. The manual is explicit about the
|
|
1633
|
+
* first: request.security() on a lower timeframe "will evaluate the
|
|
1634
|
+
* expression from the LTF context", and there is no LTF context to evaluate
|
|
1635
|
+
* it in — the intrabar prices are not in the data. Both keep returning `na`
|
|
1636
|
+
* and recording the request, so a host that HAS the data can supply it.
|
|
1637
|
+
*
|
|
1638
|
+
* ── Causality ───────────────────────────────────────────────────────────
|
|
1639
|
+
*
|
|
1640
|
+
* Only bars the script has already seen are folded. Aggregating the whole
|
|
1641
|
+
* dataset would hand the script the future: the group containing the
|
|
1642
|
+
* current bar would close before its last bar had arrived. `security()`
|
|
1643
|
+
* then applies its own closed-bar rule on top of a series that is already
|
|
1644
|
+
* causal.
|
|
1645
|
+
*/
|
|
1646
|
+
private deriveSecurityData;
|
|
1647
|
+
/**
|
|
1648
|
+
* Is this request just the chart's own series, under another name?
|
|
1649
|
+
*
|
|
1650
|
+
* True only for a DERIVED series at the chart's own timeframe — the same
|
|
1651
|
+
* symbol, no host data, and a resolution that folds one chart bar into one
|
|
1652
|
+
* requested bar. Then the requested bar and the chart bar ARE the same bar,
|
|
1653
|
+
* and there is no closed-bar rule to apply: `request.security(
|
|
1654
|
+
* syminfo.tickerid, timeframe.period, close)` is the current close.
|
|
1655
|
+
*
|
|
1656
|
+
* Host-supplied data is never this, even at a matching label. A host may
|
|
1657
|
+
* hand over any series it likes under the name "D", and the conservative
|
|
1658
|
+
* closed-bar rule is the right default for something this engine cannot see
|
|
1659
|
+
* inside.
|
|
1660
|
+
*/
|
|
1661
|
+
isOwnChartSeries(symbol: string, resolution: string): boolean;
|
|
1662
|
+
/**
|
|
1663
|
+
* Where a request's history starts: always the first bar. `calc_bars_count`
|
|
1664
|
+
* ("the maximum number of recent historical bars the function can request")
|
|
1665
|
+
* is accepted and has NO observable effect, which TradingView's own log
|
|
1666
|
+
* shows (conformance/v5/parity/harness_security_cbc, BTCUSDT 5m, 5,464 bars):
|
|
1667
|
+
* `request.security("60", bar_index, calc_bars_count = 50)` equals the same
|
|
1668
|
+
* call without it on every row, and `ta.cum(1)` inside it is 6984 at chart
|
|
1669
|
+
* bar 0; `security_lower_tf("1", bar_index, calc_bars_count = 200)` likewise
|
|
1670
|
+
* equals the call without it. 50 and 200 limited nothing.
|
|
1671
|
+
*
|
|
1672
|
+
* (A data-supply fact for hosts, not a broker rule: the hourly context's
|
|
1673
|
+
* history starts long before the chart's first bar, with 6,983 hourly bars
|
|
1674
|
+
* ahead of it there, while the 1-minute lower-timeframe context starts at the
|
|
1675
|
+
* chart's first bar.)
|
|
1676
|
+
*/
|
|
1677
|
+
securityFirstBar(_symbol: string, _resolution: string, _calcBarsCount: any, _fname: string): number;
|
|
1678
|
+
getSecurityData(symbol: string, resolution: string, lookaheadOn?: boolean): SecurityCandle[] | undefined;
|
|
1679
|
+
/** Records a (symbol, resolution) the script needs but hasn't been given. */
|
|
1680
|
+
requestSecurity(symbol: string, resolution: string): void;
|
|
1681
|
+
/**
|
|
1682
|
+
* The chart's context, for a sub-context that evaluates a requested
|
|
1683
|
+
* expression; null on the chart's own. `syminfo.main_tickerid` and
|
|
1684
|
+
* `timeframe.main_period` read it.
|
|
1685
|
+
*/
|
|
1686
|
+
main: Context | null;
|
|
1687
|
+
/**
|
|
1688
|
+
* A context to evaluate a `request.security` expression in: the requested
|
|
1689
|
+
* timeframe and symbol, and the chart's symbol facts when it is the
|
|
1690
|
+
* chart's symbol — `timeframe.period` inside the request is "5" for a
|
|
1691
|
+
* 5-minute request, as TradingView logs it, where a bare context said "1D".
|
|
1692
|
+
*/
|
|
1693
|
+
securitySubContext(symbol: string, resolution: string): Context;
|
|
1694
|
+
/** Stable key for the current call site (used for per-`security()` HTF state). */
|
|
1695
|
+
currentCallKey(): string;
|
|
1696
|
+
/** Per-call-site HTF state, exempt from live-tick snapshot/rollback. */
|
|
1697
|
+
getHtfState<T>(key: string, initFn: () => T): T;
|
|
1698
|
+
/**
|
|
1699
|
+
* Evaluates a deferred `security()` expression for one HTF bar inside a
|
|
1700
|
+
* sub-context, by rebinding the VM's global series (open/high/.../close) and
|
|
1701
|
+
* `ctx` to the sub-context for the duration of the thunk. Restores on exit.
|
|
1702
|
+
*/
|
|
1703
|
+
evalSecurityBar(sub: Context, bar: SecurityCandle, thunk: () => any, provisional?: boolean): any;
|
|
1704
|
+
}
|
|
1705
|
+
export {};
|