Porting Pine v4 / v5 → v6
You are migrating an older Pine Script to v6. Work iteratively: analyze → rewrite → compile → fix.
Power-toolkit shortcut: pine_check does a server-side compile without touching the chart — use it between rewrite passes to fail fast on syntax errors before doing a full pine_smart_compile round. pine_analyze (offline) covers v4/v5 deprecation flags.
Step 1: Pull + Identify the Version
pine_get_source — read the current source (skip if the user pasted it in chat)
pine_analyze — the analyzer flags v<6 scripts explicitly; also surfaces deprecated calls
- Look at the first non-comment line:
//@version=4 → v4
//@version=5 → v5
- missing /
//@version=6 → nothing to do
Step 2: Apply Migration Rules
Update the version header first: //@version=6. Then apply these rewrites.
v4 → v6 Cheat Sheet
| v4 |
v6 |
study("Name", overlay=true) |
indicator("Name", overlay=true) |
security(...) |
request.security(...) |
sma(x, n) |
ta.sma(x, n) |
ema(x, n) |
ta.ema(x, n) |
rsi(x, n) |
ta.rsi(x, n) |
crossover(a, b) |
ta.crossover(a, b) |
crossunder(a, b) |
ta.crossunder(a, b) |
highest(x, n) |
ta.highest(x, n) |
lowest(x, n) |
ta.lowest(x, n) |
atr(n) |
ta.atr(n) |
tostring(x) |
str.tostring(x) |
tonumber(x) |
str.tonumber(x) |
nz(x) |
nz(x) (unchanged) |
input(..., type=input.integer) |
input.int(...) |
input(..., type=input.float) |
input.float(...) |
input(..., type=input.bool) |
input.bool(...) |
iff(cond, a, b) |
ternary cond ? a : b |
valuewhen(cond, src, n) |
ta.valuewhen(cond, src, n) |
v5 → v6 (smaller gap)
Most ta.*, str.*, math.*, array.*, input.* namespaces already exist in v5. Main v6 changes:
//@version=5 → //@version=6
- Some drawing/style constants renamed — trust the compiler errors to guide you
plot.style_* constants: verify they still exist; the compiler will flag drops
- Strict type inference — add explicit types (
float, int, bool) on var declarations where the compiler now requires them
request.security lookahead param default tightened; be explicit: lookahead=barmerge.lookahead_off
Structural rules that stay
- 4-space indentation (never braces)
- Every script declares exactly one of
indicator(), strategy(), or library()
- Series vs simple typing is stricter in v6 — if compiler complains, often you need
input.int(..., defval=14) instead of raw int assignments
Step 3: Rewrite Locally
Edit the source as a single coherent pass — don't piecemeal if the script is short. For long scripts (500+ lines), port function-by-function and re-check after each.
Step 4: Compile-Check Server-Side
pine_check — server-side compile of the rewritten source. Cheaper than a full pine_set_source + pine_smart_compile cycle because it doesn't mutate the live editor.
- Read errors. Common post-migration breakage:
Could not find function 'xxx' → likely still a v4 bareword; prefix with ta. / str. / math.
Cannot call 'input' with arguments... → switch to the typed variants input.int / input.float / input.bool / input.string
Syntax error at input 'study' → rename to indicator
Series type required → explicit cast, e.g., float src = close
Step 5: Iterate Until Clean
Loop:
- Fix the next error in the local source
pine_check again
- Stop when error count is 0 and warnings are acceptable
Don't push to the editor until pine_check is clean — saves round-trips.
Step 6: Push + Verify
pine_set_source — inject the clean v6 source
pine_smart_compile — final in-editor compile (catches anything pine_check missed)
pine_get_errors — confirm zero
capture_screenshot — visual sanity check that plots still render as expected
- If it's a strategy:
data_get_strategy_results — compare metrics vs the v4/v5 baseline the user recorded previously (regressions here usually indicate a subtle porting bug like lookahead semantics)
Step 7: Report
- Version bumped: v4 → v6 (or v5 → v6)
- Rewrites applied: count of
study→indicator, security→request.security, ta.* prefix adds, input.* typed conversions, etc.
- Behavioral deltas to watch: flag any
lookahead, barmerge, or math.round_to_mintick changes that could shift backtest numbers
- Final compile status and screenshot path
Error Notes
PINE_COMPILE_ERROR — iterate; don't declare done
- If
pine_get_source returns a huge blob, work in the local file and avoid re-reading unless needed (see CLAUDE.md rule 4 on context)
1---2name: porting-pine-versions3description: Migrate Pine Script v4 / v5 source to v6 — analyze, apply migration rules, compile-check, iterate until clean. Use when the user hands over an older script or asks to "upgrade to v6" / "port this Pine code".4license: MIT5---67# Porting Pine v4 / v5 → v689You are migrating an older Pine Script to v6. Work iteratively: analyze → rewrite → compile → fix.1011> **Power-toolkit shortcut:** `pine_check` does a server-side compile without touching the chart — use it between rewrite passes to fail fast on syntax errors before doing a full `pine_smart_compile` round. `pine_analyze` (offline) covers v4/v5 deprecation flags.1213## Step 1: Pull + Identify the Version14151. `pine_get_source` — read the current source (skip if the user pasted it in chat)162. `pine_analyze` — the analyzer flags `v<6` scripts explicitly; also surfaces deprecated calls173. Look at the first non-comment line:18 - `//@version=4` → v419 - `//@version=5` → v520 - missing / `//@version=6` → nothing to do2122## Step 2: Apply Migration Rules2324Update the version header first: `//@version=6`. Then apply these rewrites.2526### v4 → v6 Cheat Sheet2728| v4 | v6 |29|----|----|30| `study("Name", overlay=true)` | `indicator("Name", overlay=true)` |31| `security(...)` | `request.security(...)` |32| `sma(x, n)` | `ta.sma(x, n)` |33| `ema(x, n)` | `ta.ema(x, n)` |34| `rsi(x, n)` | `ta.rsi(x, n)` |35| `crossover(a, b)` | `ta.crossover(a, b)` |36| `crossunder(a, b)` | `ta.crossunder(a, b)` |37| `highest(x, n)` | `ta.highest(x, n)` |38| `lowest(x, n)` | `ta.lowest(x, n)` |39| `atr(n)` | `ta.atr(n)` |40| `tostring(x)` | `str.tostring(x)` |41| `tonumber(x)` | `str.tonumber(x)` |42| `nz(x)` | `nz(x)` (unchanged) |43| `input(..., type=input.integer)` | `input.int(...)` |44| `input(..., type=input.float)` | `input.float(...)` |45| `input(..., type=input.bool)` | `input.bool(...)` |46| `iff(cond, a, b)` | ternary `cond ? a : b` |47| `valuewhen(cond, src, n)` | `ta.valuewhen(cond, src, n)` |4849### v5 → v6 (smaller gap)5051Most `ta.*`, `str.*`, `math.*`, `array.*`, `input.*` namespaces already exist in v5. Main v6 changes:5253- `//@version=5` → `//@version=6`54- Some drawing/style constants renamed — trust the compiler errors to guide you55- `plot.style_*` constants: verify they still exist; the compiler will flag drops56- Strict type inference — add explicit types (`float`, `int`, `bool`) on `var` declarations where the compiler now requires them57- `request.security` lookahead param default tightened; be explicit: `lookahead=barmerge.lookahead_off`5859### Structural rules that stay6061- 4-space indentation (never braces)62- Every script declares exactly one of `indicator()`, `strategy()`, or `library()`63- Series vs simple typing is stricter in v6 — if compiler complains, often you need `input.int(..., defval=14)` instead of raw `int` assignments6465## Step 3: Rewrite Locally6667Edit the source as a single coherent pass — don't piecemeal if the script is short. For long scripts (500+ lines), port function-by-function and re-check after each.6869## Step 4: Compile-Check Server-Side70711. `pine_check` — server-side compile of the rewritten source. Cheaper than a full `pine_set_source` + `pine_smart_compile` cycle because it doesn't mutate the live editor.722. Read errors. Common post-migration breakage:73 - `Could not find function 'xxx'` → likely still a v4 bareword; prefix with `ta.` / `str.` / `math.`74 - `Cannot call 'input' with arguments...` → switch to the typed variants `input.int` / `input.float` / `input.bool` / `input.string`75 - `Syntax error at input 'study'` → rename to `indicator`76 - `Series type required` → explicit cast, e.g., `float src = close`7778## Step 5: Iterate Until Clean7980Loop:81821. Fix the next error in the local source832. `pine_check` again843. Stop when error count is 0 and warnings are acceptable8586Don't push to the editor until `pine_check` is clean — saves round-trips.8788## Step 6: Push + Verify89901. `pine_set_source` — inject the clean v6 source912. `pine_smart_compile` — final in-editor compile (catches anything `pine_check` missed)923. `pine_get_errors` — confirm zero934. `capture_screenshot` — visual sanity check that plots still render as expected945. If it's a strategy: `data_get_strategy_results` — compare metrics vs the v4/v5 baseline the user recorded previously (regressions here usually indicate a subtle porting bug like lookahead semantics)9596## Step 7: Report9798- **Version bumped**: v4 → v6 (or v5 → v6)99- **Rewrites applied**: count of `study`→`indicator`, `security`→`request.security`, `ta.*` prefix adds, `input.*` typed conversions, etc.100- **Behavioral deltas to watch**: flag any `lookahead`, `barmerge`, or `math.round_to_mintick` changes that could shift backtest numbers101- **Final compile status** and screenshot path102103## Error Notes104105- `PINE_COMPILE_ERROR` — iterate; don't declare done106- If `pine_get_source` returns a huge blob, work in the local file and avoid re-reading unless needed (see CLAUDE.md rule 4 on context)