VEEMANDocs
DocumentationSupportOpen Dashboard

Getting started

  • Introduction
  • Quick start
  • Core concepts

Importing data

  • Supported formats
  • How import works
  • Splitting a file
  • Troubleshooting

Building your portfolio

  • Workspace & equity
  • Weights & capital
  • Weekday filter
  • Keyframes
  • Currencies & FX
  • Library
  • Strike guard
  • Variants & comparison
  • History (undo/redo)
  • Shared spaces

Analytics

  • Metrics reference
  • Charts (deep-dive)
  • Monthly P&L
  • Correlations
  • Comparison
  • Monte Carlo
  • Equity Control
  • Optimization
  • IBKR reconciliation
  • Veeman AI

Research

  • Research overview
  • Define
  • Composing a backtest
  • How the engine trades
  • Results & archive
  • Parameter study
  • Rewind
  • Tactics
  • Indicators

Reference

  • Keyboard & command palette
  • Exporting data
  • Settings

Technical

  • Architecture
  • API reference
  • Security
  • Your data & privacy
  • Risk disclosure
← PreviousTacticsNext →Keyboard & command palette

Indicators

Indicators is where your own indicators live. Paste one written in Pine Script v5 or v6 (the language of TradingView) and Veeman tells you, before you save anything, exactly what runs: every input, every output, which outputs can reach a backtest and why the others can't.

The module compiles, previews, inspects, verifies and saves indicators, and a saved indicator can decide a backtest's entries and exits (Use in backtest). Showing it in Rewind comes in a next release; EasyLanguage arrives in a later phase.

Note

Veeman never rewrites your script. It keeps it as you wrote it, license and copyright lines included (only Windows line endings become plain line breaks, as in any editor), and runs it on its own servers: no code of the script runs in your browser.

Opening a script

There are three ways in:

  • Paste the script (the Paste button, or ⌘V anywhere on the empty page);
  • drop a .pine or .txt file, or choose one;
  • start from a template written by Veeman.

The script compiles a moment after you stop typing. When it isn't all exact, the import report opens first: the constructs that work differently, with their line and what happens to them in Veeman, and a summary of inputs and outputs. Open anyway opens the script; Cancel drops it. Closing the report (✕, Esc or a click outside) keeps the script open. A script that is all exact opens directly, with a one-line summary.

What Veeman tells you about each construct

Every function and variable of the language has a class. The editor shows it when you hover a name, and the import report and the Checks tab count them.

Exact
Computed exactly as on the source platform: same algorithm, same handling of na, same warm-up.
Mapped
Same meaning, carried into Veeman's world. An alertcondition becomes an event you can use; nothing is sent.
Veeman semantics
It works, but its meaning depends on where it runs. barstate.islast and last_bar_index know where the data ends: in the preview that is the last bar shown. timenow is the close of the bar being computed, never the real clock.
Display only
It runs and draws, but its values could not exist at that time (see Looking ahead).
Not yet
Arrives in a later phase. Tables and drawings are skipped meanwhile (see Tables and drawings); anything else doesn't compile yet, with an error that names the construct.

The script as a whole gets a level: Exact; Runs with differences (some Veeman semantics); Some outputs display-only; Runs · tables skipped (or drawings, or both).

Writing and fixing

Each problem has a code (like IND-P-0301), a message that says what happened and what to do, and its place in the text: errors are underlined in red, warnings in amber, and the message sits under the line. Some come with a quick fix, one click away:

  • an undeclared function that exists in a namespace: max → Use math.max;
  • a number used as a condition in v6 (it is no longer true or false): Compare: x != 0;
  • a daily value read before the day closes: Use yesterday's high (high[1]), see below.

Pine v5 and v6 run with their own rules (type conversions, na in conditions, division of integers, timeframe.period…): the version comes from //@version and the chip next to the name shows it.

Outputs

Each output of the script becomes a typed output. The Outputs tab lists them with their pane (on the price or in their own pane) and where they will be usable.

Line
plot(): a value per bar. Its style (line, step line, histogram, columns, circles, area) is kept for the preview.
Level
hline(): a constant, usually a threshold.
Event
plotshape() and plotchar(): 1 on the bars where the shape is drawn, 0 elsewhere. With location.absolute the value is the price where it's drawn.
Arrows
plotarrow(): the value itself, up when positive, down when negative.
Band
bgcolor(): 1 on the bars with a background color, 0 elsewhere.
Alert
alertcondition(): 1 on the bars where the condition holds.

An output is display only when its values could not have been known at that bar. It still shows in the preview (line, step and area plots dashed), and the Outputs tab gives the reason and its line:

  • it looks ahead (next section);
  • it knows where the data ends (barstate.islast, last_bar_index, last_bar_time);
  • it reads a skipped drawing (a value taken from a label, a line or a box, which is na until drawings arrive).

Veeman checks this without running the script, by following every value back to where it comes from, through variables, conditions, state, loops (a break decided by barstate.islast counts) and your own functions. Arrays are followed as a whole: until arrays get their own tracking, a value that looks ahead or knows where the data ends, stored in any array, makes every output that reads an array display only.

Looking ahead

request.security() with lookahead = barmerge.lookahead_on on a higher timeframe shows the requested bar while it is still forming: on a 5-minute chart, high from the daily bar is the day's final high from the first bar of the day. That never happens in a backtest, so the outputs built on it stay display only. The quick fix offsets the expression by one bar (high[1]: yesterday's high, known all day long). The same request with lookahead_off waits for the bar to close.

Reading the open or the time of the requested bar with lookahead_on is fine: they are known when the bar starts.

Tables and drawings

Tables, labels, lines, boxes and polylines arrive in a later phase. Until then a script that uses them still runs: the lines that build them are skipped and shown struck through, nothing else changes. A value read back from a drawing (label.get_y, a point's price…) is na, so an output that uses it is display only. When a construct like barstate.islast only drives a skipped table, it doesn't count as a difference.

Preview

The preview runs the script on Veeman's data, on the symbol and timeframe of the Timing tab: the last 1, 5 or 20 sessions on intraday charts, the last 3 months, year or 3 years on the daily chart. The warm-up the indicator needs is computed before the window, so the first bar shown is already settled; when it isn't, the bars are shaded warming up.

Values are at bar close: a bar's value uses data up to its close and never changes afterwards. A runtime error (an index out of range, a loop that never ends…) is shown with the bar where it happened.

Veeman has no chart-data export: the preview draws the window on screen, and backtests read the indicator's full history on Veeman's servers.

Inspector

Click a bar in the preview: next to every line of the script that declares a global variable (one per name, not the ones inside your functions), the editor shows its value at that bar's close. Long texts are cut at 200 characters. ← and → step bar by bar (in the editor header or with the arrow keys on the chart).

Console

log.info(), log.warning() and log.error() write to the console under the preview, with the bar time of each message, for the bars of the window. A run keeps up to 2,000 messages, counting the ones written during the warm-up before the window, so the console can show fewer; a message longer than 1,000 characters is cut.

Timing

Chart
The symbol and timeframe of the preview: SPX, NDX, RUT, SPY, QQQ, IWM, VIX, VIX9D, VIX1D, VIX3M, VVIX; 1, 5, 15, 30, 60 minutes or daily.
Reads
When a backtest will read the indicator: on a 5-minute chart, at 10:31 it reads the 10:25 bar, which closed at 10:30.
Warm-up
How many bars the outputs need before they settle, including the bars of requested timeframes. Values that build on their own past («depends on where the data starts») are flagged instead of being counted.
Ready from
The first settled bar of the data the preview ran on (the warm-up before the window included), so it can be earlier than the first bar shown.
Data
Veeman's data has no volume for these symbols, so volume-based functions return na. On SPX, NDX and RUT, the 16:00 bar carries the official close (13:00 on half-days).

Verify against your chart

You can check, on your own platform, that Veeman computes your script exactly as it does there. The check uses no market data of the platform.

1

Make a verification copy

In Checks (or the ⋯ menu), Veeman makes a copy of the script: the same logic, with the chart's prices replaced by synthetic series computed from each bar's time (whole numbers and multiples of 0.25, exact everywhere), one full-precision plot per output, one for bar_index and one that marks the bars your platform computes live. The license and copyright lines stay at the top.

2

Run it on your platform

Open a chart with the same symbol as the Timing tab, at any timeframe: Veeman recognizes the timeframe from the export's bar times and compares on that one. Paste the copy into a new indicator, add it to the chart and export the chart data as CSV.

3

Drop the export here

The file is read in your browser: the price columns (open, high, low, close, volume) are removed there, and only the bar times and the values computed by the script are sent.

Veeman runs the same copy on the bars of your export and compares each output: identical to the last bit, within 64 units in the last place when a transcendental function is involved, or within a relative 10⁻⁹. The report gives, per output, the share of bars that match within those limits, the largest difference and the first bar where they differ. A copy can be made for up to 31 outputs (each one takes a second plot, and the platform allows 64), and an export can have up to 50,000 rows.

The copy's prices depend only on the bar time, so what matters is that both sides run on the same bars. Bars your platform has and Veeman's data doesn't (on SPX, the minutes after the 16:00 close, or a session more recent than Veeman's data) are added for the comparison; bars Veeman has and the export doesn't (on the VIX, minutes without a price change) are left out. The report says how many, and why.

Bars your platform computed live, because the market was open when you exported, run through the calculation but aren't compared. On a live bar a request to another timeframe shows the period still forming (the hour, the day, the week in progress), and the values change until the bar closes. Veeman computes every bar as history, as backtests read it. The report says how many bars were live.

Tip

Functions that read the chart's prices by themselves, like ta.atr, can't be pointed at the synthetic prices: the verify dialog says so, and small differences there can come from the two data feeds. ta.vwap needs volume, which Veeman's data doesn't have, so on Veeman's side it is na on every bar.

On a saved indicator the result stays on it (the export isn't kept). A change to the script takes it off, because it verified a different script; edits that don't change what runs (comments, or the default values of numbers, switches and options) keep it.

Saving and versions

Save (⌘S) keeps the indicator in your library, or in the space you are working in. A new version is recorded each time the script or its default inputs change; Versions lists them with the script of each one, and Restore brings an old one back as a new version, so nothing is lost. Besides the current one, up to 50 earlier versions are kept, and at most 1,000,000 characters of them (scripts, inputs and notes); the oldest go first. Restoring from a space's history works the same way: a new version, with the history kept.

In a shared space, if someone saved a newer version while you were editing, saving asks you what to do instead of overwriting their work: Reload their version, or Save as copy. Leaving the page with unsaved changes asks first.

Use in backtest

Use in backtest opens the backtest composer with the indicator already in Only if, with a first condition to complete: on its first line output that can reach a backtest, or else on its first usable output (an event starts as an already complete «is on»). If the composer has no entry time yet, the floating window is set to the whole session, so the position opens at the first minute the indicator says so; an entry time or window you already set stays (Custom indicators has the details). It needs a saved indicator with no unsaved changes (a backtest reads the indicator from your library), a script that compiles, and at least one output a backtest can read. From the composer the same indicator can also close positions, or open them at each of its signals (an event, or a condition that starts to hold): see Custom indicators and Indicator signal.

A backtest reads each output at the close of its bar, the rule of the Reads line under Timing: at 10:31 a 5-minute indicator gives the bar that closed at 10:30. Display-only outputs never reach it. Every run keeps the script it used, so changing or deleting the indicator later doesn't change a saved backtest.

Templates

Six templates written by Veeman: RSI(2) pullback, Opening range, VIX/VIX3M term structure, Trend filter · SMA 200 daily, ATR channel, Bollinger squeeze. Each compiles exact, with every output able to reach a backtest.

Limits today

  • Not yet supported: user-defined types, methods and enums; libraries (import); maps and matrices; request.security() inside a function, inside a loop when it reads the loop's variables, or with a symbol or timeframe that changes from bar to bar; strategies. A script that uses them doesn't compile, with an error that names the construct.
  • Size: a history reference reaches at most 5,000 bars back (close[5000]), as on TradingView; a chain of more than 180 ?: or unary operators, a sum of more than about 200 terms, or 99 nested if is refused (split it into variables); a script whose functions call each other so much that its checks would take too long is refused with Script too complex to analyze.
  • Strings hold at most 40,960 characters, as on TradingView: a script that builds a longer one (doubling it in a loop, str.repeat with a large count) stops with String is too long.
  • Loops: all the loops of one bar together run at most 1,000,000 iterations — the budget is shared, so nesting two of them doesn't multiply it. Beyond that the script stops with Loops take too long to execute, naming the bar.
  • Inputs: each value must match its input's type and range (a number, on/off, one of the options, a source like high or hl2); a source input you pick in the Inputs tab is the series the preview reads.
  • Tables and drawings are skipped (above).
  • Symbols are the ones of Veeman's minute data listed under Timing; request.security() can read any of them.