Supported Pine Features
TestMax supports Pine Script v5 and v6 across common indicator and strategy workflows, and converts v4 and v3 scripts when they are applied. This page describes the implemented features and the limits to check when bringing a script from TradingView. Identical source can produce different results with different data, sessions, history, or simulation assumptions.
Language and inputs
| Feature | Support |
|---|---|
indicator() and strategy() | Indicators and strategies can be applied to replay charts |
library() and import | Available library source can be saved in your account and imported by exact path |
| Inputs | Integer, float, boolean, string, text area, color, source, timeframe, session, time, price, symbol, and enum inputs; options, group, inline, tooltip, active, and confirm |
| Control flow | if / else, for, for ... in (array elements, map pairs, and matrix rows), while, switch, and once; if, switch, and loops can return values, and a function returns the value of its last statement |
| Functions and types | User-defined functions, methods, user-defined types, enum types, and the const, input, simple, and series qualifiers |
| Strings | Single-line strings and triple-quoted strings ("""...""") that span lines and keep their leading spaces |
| Collections | Arrays, matrices, and maps in function form (array.sort(a)) and method form (a.sort()), with size limits; array.slice() returns a view that shares its elements with the original array |
| Series history | x[n] and history-dependent calculations |
| Version rules | v5 evaluates both sides of and / or and divides integer constants as integers (5 / 2 is 2); v6 stops early and divides as decimals (2.5). v5 also divides int(5) / int(2) as 2. v3 to v5 compare a bool with a number as two bools (0 and na are false); v6 refuses that comparison, a number used as a condition, and na(), nz(), or fixnan() on a bool. In v6 a bool is never na: its history before the first bar is false. timeframe.period is "D", "W", or "M" in v5 and "1D", "1W", or "1M" in v6. A plot or level linewidth below 1 is drawn as 1 in v5 and is an error in v6 |
| Persistent state | var state rolls back between open-candle updates, including referenced objects; varip persists between executions |
| Logs and errors | log.info, log.warning, log.error, and runtime.error |
The compiler refuses the common mistakes that TradingView refuses, with their line: a wrong value type in a typed declaration, argument, or array, missing or extra function arguments, a variable declared twice in one scope, x = na without a type (version 4 and later), a field name that the type does not have in Type.new(), a name that does not exist in syminfo, barstate, timeframe, session, strategy, math, or ta (for example syminfo.tickr), plotshape() or plotchar() text that is not a constant string, and a continuation line that is not indented or is indented by a multiple of four spaces. It does not implement every Pine type rule or collection method. A successful compilation is not a promise of full language equivalence.
Older versions. A //@version=4 or //@version=3 script is converted to current Pine before it compiles: study(), names without a namespace (sma, security, tostring, …), iff(), input(type = ...), transp, version 3 color, style, and weekday names, and the long argument of strategy orders. A name the converter does not know stops the script with an error that names it. A script without a //@version line is version 1 on TradingView and is not supported; add a version line and migrate it. Version 2 is not supported. A ta.* or math.sum call inside a branch keeps its own history, as on TradingView: it sees only the bars on which the branch ran.
Common TA calculations
| Category | Functions and built-ins |
|---|---|
| Moving averages | sma, ema, rma, wma, vwma, swma, alma |
| Oscillators | rsi, macd, stoch, cci, mfi, tsi, cog, rci |
| Bands and dispersion | bb, stdev, variance, dev, atr, tr |
| Trend | supertrend, sar, dmi — dmi returns +DI, −DI, and ADX |
| Extremes and statistics | highest, lowest, highestbars, lowestbars, all-time max and min, median, mode, range, percentrank, percentile_linear_interpolation, percentile_nearest_rank, linreg, correlation |
| Changes and totals | change, mom, roc, cum |
| Crosses and conditions | cross, crossover, crossunder, rising, falling |
| Pivots and events | pivothigh, pivotlow, pivot_point_levels, barssince, valuewhen |
| Volume | obv, accdist, iii, nvi, pvi, pvt, wad, wvad, and vwap with an optional anchor condition and standard-deviation bands |
Common math.* helpers and constants (math.pi, math.e, math.phi, math.rphi), math.sum, na, nz, and fixnan are available. Numeric str.tostring() formatting supports common decimal/grouping patterns with literal text around the number ("$#.##"), format.mintick, format.percent, and format.volume; it also prints arrays and matrices. str.format() supports plain, number, date, and time placeholders. str.match() accepts simple patterns; patterns that could stall the calculation are refused.
Bar-state values update with replay execution, including barstate.isconfirmed.
Time, sessions, and timeframes
Each instrument reports its exchange clock: CME futures use Chicago time with a 17:00 to 16:00 session, forex and spot metals use New York time with a 17:00 to 17:00 session, and crypto uses UTC around the clock. hour, minute, dayofweek, session strings without a timezone, and str.format_time() use that clock.
| Feature | Support |
|---|---|
time() and time_close() | Chart timeframe, another timeframe (time("D")), a session string, and a timezone |
| Session strings | One or several ranges ("0930-1200,1300-1600"), day lists, overnight and full-day sessions |
session.* | ismarket, isfirstbar, islastbar, their _regular forms, session.regular, session.extended |
timeframe.* | period, multiplier, isseconds to ismonthly, change(), in_seconds(), from_seconds(), main_period |
timestamp() | Numeric parts with an optional timezone, and date strings |
dayofweek.* | dayofweek.sunday to dayofweek.saturday |
Higher-timeframe periods follow TestMax’s own candles, which are cut on the UTC clock. A Pine "D" period therefore matches the TestMax daily chart, and can start at a different hour than the exchange day on TradingView. ta.vwap(source) resets by calendar day in the exchange timezone. ta.vwap(source, anchor) resets on each bar where anchor is true.
Plots and drawing objects
| Visual | Support |
|---|---|
| Numeric plots | Lines, step lines, circles, crosses, histograms, columns, and areas, including the linebr, areabr, steplinebr, and stepline_diamond styles; per-bar colors and transparency; offset, show_last, join, trackprice, linewidth, linestyle, histbase, format, and precision |
hline() | Constant or input-controlled levels with solid, dashed, or dotted lines; levels are part of the pane’s scale |
plotshape() / plotchar() | Shape and character glyphs, including absolute, above/below-bar, and pane-edge locations; offset, show_last, and force_overlay |
plotarrow() | Signed arrows with configurable minimum and maximum heights |
plotbar() / plotcandle() | OHLC drawings with colors, na visibility, show_last, and force_overlay |
bgcolor() | Colored background bands for individual bars; offset, show_last, and display |
barcolor() | Colors the candle body, wick, and border; offset, show_last, and display |
fill() | Fills between compatible pairs of plot IDs or horizontal-line IDs, including dynamic colors, gradient fills (top_value, bottom_value, top_color, bottom_color), and fillgaps |
| Lines, boxes, and labels | Creation from coordinates or chart.point values, setters, copies, deletion, line extensions/arrowheads, multiline text, box text with wrapping, label tooltips, and force_overlay |
linefill | Fills that follow the referenced lines |
| Polylines | Straight or curved paths, closed shapes, and fill colors |
| Tables | All nine positions, merged cells, regional clearing, text/alignment/size/color setters, tooltips, and supported font formatting |
Open-candle redraws replace temporary output and restore prior drawing state. Script visuals are drawn behind the candles unless the script sets behind_chart = false. Lines, boxes, labels, linefills, and polylines stay above the script’s own plots, fills, and levels. The grid stays below all script visuals, and the crosshair stays on top. Box text with the default size.auto shrinks to fit inside the box.
The scale argument of indicator() and strategy() puts a separate-pane script on the right or left axis. With overlay = true, scale.none gives the script no axis of its own (without overlay = true it is a compile error, as on TradingView), and scale.left gives it its own axis on the left side of the price pane. With explicit_plot_zorder = true, plots, levels, and fills stack in the order the script calls them; lines, boxes, labels, and tables always stay on top of them. Exact font metrics, curve interpolation, and marker sizing can differ slightly from TradingView.
The following options remain limited:
- A plot
offsetmust keep the same value during a run. A positive offset draws at most 500 bars beyond the last candle. - Supported display settings control pane visibility. TradingView’s status line values, Data Window, and price-scale labels are not reproduced.
indicator(timeframe = ...)with a timeframe other than the chart’s is not supported. The script stops with an error.alert()andalertcondition()are accepted and do nothing.chart.left_visible_bar_timeandchart.right_visible_bar_timeare not supported, because TestMax does not re-run a script when you scroll or zoom. The script stops with an error.
Libraries
Save each library’s source as a Library with the exact name used by the import, such as local/MathTools/1. Then use import local/MathTools/1 as tools in an indicator or strategy.
Imports resolve from your saved sources. TestMax does not download a TradingView library automatically. Missing versions, duplicate saved names, circular imports, and ambiguous overloads report errors. Libraries are not applied directly to a chart. See the library walkthrough.
Other symbols and timeframes
request.security() and request.security_lower_tf() evaluate supported expressions using chart datasets available in your replay. Add a chart pane for a required symbol or timeframe before applying a script that needs it.
- The same symbol/timeframe can reuse its existing bars.
- Higher-timeframe calculations need an available matching dataset or a supported exact aggregation of finer bars. Automatic aggregation currently requires a UTC, continuous-session bar grid; exchange-session rebucketing is not available.
- Lower-timeframe requests require an available finer dataset whose interval divides the chart interval.
request.security_lower_tf()returns the revealed intrabars as arrays. - A
lookahead_onrequest needs a history offset of at least 1 that the compiler can prove from a literal, an input, a default value, or a call argument, such asclose[1]orf(1). Otherwise the script stops with an error at the request’s line. - Gaps and lookahead settings operate within the replay’s revealed-data boundary. A request cannot provide future replay candles, so scripts relying on future historical values can differ from a complete TradingView history run.
- A request can sit inside your own function, such as the common
noRepaintSecurity(sym, tf, src)wrapper. When an earlier line of that function changes an array, map, matrix, or object through a path the compiler cannot follow (for example a conditional or tuple alias, a value returned by a call, or afor ... inelement), the script stops with an error at that line. Pass the value as a parameter, or call the request outside the function. - Requested results can be primitive values or flat tuples. Nested requests, chart drawing/order calls inside requested expressions, currency conversion, and
calc_bars_countare not supported. request.financial(),request.economic(),request.dividends(),request.splits(),request.earnings(),request.seed(),request.currency_rate(),request.footprint(), and Heikin Ashi, Renko, or other non-standardticker.*data are not available. The script stops with an error.
If an error says data is unavailable, add the matching pane and apply again. If a requested expression captures a mutable outer value, move its calculation into a function evaluated by the request.
Strategy support
Entries, raw orders, stop/limit brackets, trailing stops, partial exits, exit reservations, FIFO/ANY allocation, entry/order OCA groups, immediate closes, commissions, margin checks, and margin liquidation are implemented. The Strategies guide explains their defaults and fill rules.
The strategy.risk.* rules are implemented. Bar Magnifier, alternate calculation modes, custom OCA groups on strategy.exit(), and automatic currency conversion remain unsupported. Alerts do not send notifications or webhooks; use chart markers, tables, or log.* output for feedback during replay.
Limits
| Limit | Value |
|---|---|
| Editor, paste, import, save, and export | 50 MiB of valid UTF-8 per script |
| Script plus imported libraries | 50 MiB combined; up to 16 libraries |
| Saved scripts | 1,000 per account |
| Account script storage | 100 MiB by default; administrator configurable |
| Applied scripts | 10 per symbol |
| Compile and history calculation | 30 seconds each |
| Loops | 500 ms for one loop on one bar, as on TradingView. Time spent loading request.security() data for another symbol or timeframe does not count |
| Executable source complexity | 100,000 code tokens; deeply nested expressions may also be rejected |
| TA lookback | 10,000 bars |
| Collections | 100,000 elements per collection; maps up to 50,000 key/value pairs |
| Data requests | 40 request contexts, using available replay data |
| Drawing counts | 50 by default; declared line/box/label limits up to 500 and polyline limits up to 100; 500 linefills. A new linefill between the same two lines replaces the old one |
| Drawing positions | With xloc.bar_index, at most 500 bars after and 10,000 bars before the current bar |
| Table cells | 10,000 per script |
| Chart output history | 8 MiB of drawing changes and 32 MiB of per-bar visuals (fills, backgrounds, bar colors, shapes, and candle plots); deleted drawings stop counting once their bar is confirmed; a run of identical fill bars is stored once; 500,000 fill values in total (one per fill() call per bar) |
| Strategy orders | 1,000 calls per execution and 1,000 pending orders |
| Strategy history | 10,000 trade records |
| Plot colors | 65,535 distinct colors per plot |
A 30 MiB comment-heavy script can compile and run when its executable body fits the workload limits. Large document size alone does not determine calculation cost. Dense generated code, extensive loops, large collections, and excessive chart output can exceed separate execution limits.
Large documents automatically use plain-text editor mode. Wait for a large paste or import to finish checking before saving or applying. If you hit a limit, reduce the calculation’s lookback, collection size, drawing count, or loaded history as appropriate. Additional memory and output limits can stop a run with a specific error.
Errors and recovery
Compile errors appear in the editor’s error console; use the reported location to find the relevant code. Runtime errors include the bar where execution stopped. Collection errors use TradingView’s text, such as Index 5 is out of bounds. Array size is 3., and array.pop() or array.shift() on an empty array is an error; runtime.error("text") stops the script with your own text. log.info(), log.warning(), and log.error() write to the Logs list in the editor console, which keeps the newest 1,000 lines. Re-apply after correcting the script or providing its missing data. Other applied scripts can keep running.