PineTS Request (request) Namespace
This directory contains the implementation of Pine Script’s request.* functions, primarily request.security for multi-timeframe analysis.
Architecture
Functions are factory functions accessing the context.
The param() Method
The request.param() method is highly specialized. It performs two critical tasks:
- Tuple Detection: It distinguishes between a tuple of expressions (e.g.,
[open, close]) and a time-series array.- If inputs are
Seriesobjects, it extracts their current values. - If inputs are scalars, it preserves them.
- Heuristic:
hasOnlySeriesorhasOnlyScalarscheck.
- If inputs are
- ID Return: Unlike other param methods, it returns a tuple
[value, name].value: The extracted value(s).name: The unique ID (p0,p1…) assigned by the transpiler.
return [val, name];
Why Return the Name?
The request.security function relies on caching secondary contexts (HTF contexts). To do this efficiently, it constructs a cache key using the parameter ID (name). Without this ID, it wouldn’t know which expression corresponds to which cached context.
Implementation Specifics
1. Secondary Contexts
request.security creates a new PineTS instance (a secondary context) to evaluate the expression in the requested timeframe.
- It prevents recursion: Secondary contexts have a flag
isSecondaryContext = true. - If
request.securityis called within a secondary context, it returns the expression directly (no new context).
2. Tuple Handling
When request.security returns a tuple (e.g., from [open, close]), it wraps the result in a 2D array [[val1, val2]]. This signals to Context.init() that the result is a tuple to be destructured, not a history array.
3. request.footprint — Order-Flow Data From the Provider
request.footprint(ticks_per_row, va_percent = 70, imbalance_percent = 300) does not spawn a secondary context: it asks the context’s own data source for order-flow data through the optional getFootprintData(tickerId, timeframe, limit?, sDate?, eDate?) surface (IFootprintProvider, src/marketData/IProvider.ts). The provider returns one FootprintBar per bar — { openTime, tick?, levels: [{ price, buyVolume, sellVolume }] } — at whatever price granularity it has. The method is in ASYNC_METHODS, so the transpiler awaits it like request.security.
- Store (
context.cache.__footprint): bars keyed byopenTime, builtfootprintobjects keyed by bar, the argument set of the script’s request (a second, different one throws TradingView’sThe script executes too many `request.footprint()` function calls.), and thedataVersionthe store reflects. The first call loads the whole history (sDate= first bar,eDate= last bar’scloseTime); whencontext.dataVersionmoves (streaming: forming bar ticked, new bars appended) the store re-requests from the current bar’sopenTimeand replaces those bars — the forming bar’s footprint grows between polls. - Semantics live in
src/namespaces/footprint/, not in the provider, so every source shares one behavior:FootprintObject.build()bins levels into rows ofticks_per_row × syminfo.mintickanchored at price 0 (contiguous over the candle’slow..highand every level, empty rows included; the top row is the one whose upper edge reacheshigh), then derives the POC (largest total, ties → the row closest to the footprint’s middle, lower when equidistant), the value area (grow from the POC by the larger adjacent row — ties to the row closer to the POC, then upward — refusing the row that would carry the area more than 0.01 volume units pastva_percent) and the diagonal imbalance flags (buy vs. the sell one row below, sell vs. the buy one row above, thresholdmax(imbalance_percent / 100, 1)). More than 2000 rows →na. Rows areVolumeRowObjects. napaths: no provider surface or nomintick→ a singlecontext.warn(…, 'request.footprint')andnaon every bar; a bar the provider omitted, orticks_per_rowof 0 /na→na; a negative argument →PineRuntimeError. Everyfootprint.*/volume_row.*accessor throws aPineRuntimeErrorfor annaid (TradingView’sThe `footprint` ID used in the `delta()` call cannot be `na`.). Insiderequest.security()the call runs in the secondary context, on that context’s symbol, timeframe andmintick.- Transpiler wiring:
footprintandvolume_roware listed inCONTEXT_PINE_VARS(injection),NAMESPACES_LIKE(sofootprint(x)/volume_row(x)reachfootprint.any/volume_row.any, which reject the call with TradingView’s “Could not find function or function reference” — Pine has no cast function for these types) andNAMESPACE_COLLISION_NAMES(a user variable namedfootprintis renamed). Typed declarations (footprint fp = …,array<volume_row>) need no special casing — the Pine parser treats them like any object type. For UNTYPED declarations,AnalysisPass’sBUILTIN_PRODUCER_TYPESinfersfp = request.footprint(…)asfootprintandrow = footprint.poc/vah/val/get_row_by_price(…)asvolume_row, so usermethods declared on those types dispatch statically (fp.myMethod()→$.call($M_myMethod, …, fp)) exactly as they do forl = line.new(…). A BUILT-IN method on a receiver statically typedfootprint/volume_row(includingfp.poc()/fp.get_row_by_price(p)results, typedvolume_row) is emitted as the namespace call$.pine.footprint.delta(fp)rather than the optional-chainedobj?.delta?.()used for drawings, so annareceiver reaches the helper’s runtime error (ORDERFLOW_METHODSinsettings.ts).
Generating the Barrel File
To regenerate the request.index.ts file:
npm run generate:request-index