Pine Script v6 Data Types Explained: bool, float, and int — When and How to Use Each

Understanding the three fundamental scalar data types in Pine Script v6 — bool, float, and int — is essential for writing correct, predictable indicators and strategies. Each type has distinct memory representation, arithmetic rules, and v6-specific constraints that differ meaningfully from prior versions.

1. Overview of the Three Types

Pine Script v6 is a statically-typed language with type inference. Every variable has a resolved type at compile time. The three primitive numeric/logical types are:

Type Value Domain Can Hold na? Typical Use Case
bool true or false only ❌ No (v6 breaking change) Conditions, flags, signal gates
int 64-bit signed integer or na ✅ Yes Bar counts, indices, loop counters
float 64-bit IEEE 754 double or na ✅ Yes Prices, ratios, indicator values

2. The bool Type — v6 Breaking Changes

In Pine Script v6, bool is strictly binary. It can only hold true or false. This is a critical breaking change from v5, where bool could hold na as a third state.

2-A. What Was Removed in v6

  • bool b = na — illegal in v6. bool cannot hold na.
  • na(myBool) — compile error. na() does not accept bool arguments.
  • nz(myBool, false) — compile error. nz() does not accept bool arguments.
  • Implicit numeric → bool cast: if myInt is illegal. You must write if myInt != 0 or if bool(myInt).

2-B. History Reference on Bar 0

When you reference a bool variable's history on bar 0 (e.g., myBool[1] on the first bar), v5 returned na. In v6, it returns false. This can silently change logic if you migrated code from v5.

2-C. Three-State Logic Pattern

If you need a variable that can be true, false, or missing (uninitialized), use int or float as a proxy:

//@version=6
indicator("Three-State Demo", overlay = false)

// Use int as a 3-state proxy: 1 = true, 0 = false, na = missing
var int state = na  // starts as na (missing), not false

if bar_index == 10
    state := 1  // set to "true" on bar 10

if bar_index == 20
    state := 0  // set to "false" on bar 20

// Check state safely
bool isTrue  = state == 1
bool isFalse = state == 0
bool isMissing = na(state)

plot(state, title = "State", color = color.blue)

3. The int Type

The int type represents a 64-bit signed integer. It is used for discrete, countable quantities: bar indices, loop counters, array sizes, and period lengths.

3-A. Integer Division — Critical Rule

The / operator in Pine Script always returns float, regardless of operand types. There is no C-style integer truncation.

$$\frac{1}{2} = 0.5 \quad (\text{not } 0)$$

To perform integer division (truncating toward zero), use explicit casting:

//@version=6
indicator("Integer Division Demo", overlay = false)

float divResult  = 7 / 2          // → 3.5  (float, always)
int   truncated  = int(7 / 2)     // → 3    (truncate toward zero)
int   floored    = math.floor(7 / 2)  // → 3  (floor toward negative infinity)

plot(divResult, title = "Float Div",  color = color.orange)
plot(truncated, title = "Truncated",  color = color.blue)
plot(floored,   title = "Floored",    color = color.green)

3-B. int and na

Unlike bool, int can hold na. This makes it suitable for representing "not yet computed" states:

//@version=6
indicator("Int na Demo", overlay = false)

var int lastCrossBar = na  // na until a cross occurs

bool crossed = ta.crossover(close, ta.sma(close, 20))
if crossed
    lastCrossBar := bar_index

// Safe check: na(lastCrossBar) is valid for int
int barsSinceCross = na(lastCrossBar) ? na : bar_index - lastCrossBar

plot(barsSinceCross, title = "Bars Since Cross", color = color.purple)

4. The float Type

The float type is a 64-bit IEEE 754 double-precision floating-point number. It is the default numeric type for price data, indicator outputs, and most mathematical computations in Pine Script.

4-A. Precision and Representation

IEEE 754 double precision provides approximately 15–17 significant decimal digits. For financial calculations, this is sufficient for price arithmetic, but be aware of floating-point representation limits:

$$0.1 + 0.2 \neq 0.3 \quad (\text{IEEE 754 artifact})$$

When comparing floats for equality, prefer a tolerance-based check:

//@version=6
indicator("Float Equality Demo", overlay = false)

float a = 0.1 + 0.2
float b = 0.3
float epsilon = 1e-10  // tolerance threshold

// Direct equality may fail due to IEEE 754 representation
bool directEqual    = a == b                        // may be false
bool toleranceEqual = math.abs(a - b) < epsilon     // robust check

plot(directEqual    ? 1 : 0, title = "Direct ==",    color = color.red)
plot(toleranceEqual ? 1 : 0, title = "Tolerance ==", color = color.green)

4-B. float and na

float variables can hold na, which represents a missing or undefined value. Built-in series like close are of type series float and may contain na on extended sessions or data gaps.


5. Type Conversion Rules

Pine Script v6 enforces explicit type conversion in several cases that were implicit in v5. The table below summarizes all legal and illegal conversion patterns:

Conversion Syntax Legal in v6? Notes
int → float float(myInt) ✅ Yes Widening conversion, no data loss
float → int int(myFloat) ✅ Yes Truncates toward zero
bool → int myBool ? 1 : 0 ✅ Yes (ternary only) int(myBool) causes CE10123
bool → float myBool ? 1.0 : 0.0 ✅ Yes (ternary only) float(myBool) causes CE10123
int → bool myInt != 0 or bool(myInt) ✅ Yes (explicit only) Implicit cast (if myInt) removed in v6
float → bool myFloat != 0.0 or bool(myFloat) ✅ Yes (explicit only) Implicit cast removed in v6
bool → na bool b = na ❌ No Compile error in v6

6. Comprehensive Type Demonstration Script

The following script demonstrates all three types in a single indicator, including correct conversion patterns, na handling, and the three-state proxy pattern.

graph TD A[Variable Declaration] --> B{Which type?} B --> C[bool] B --> D[int] B --> E[float] C --> C1[true or false only] C1 --> C2{Need na state?} C2 -->|Yes| C3["Use int proxy (1=true, 0=false, na=missing)"] C2 -->|No| C4[Use bool directly] D --> D1[Can hold na] D1 --> D2[Bar counts, indices, loop counters] D2 --> D3{Division needed?} D3 -->|int result| D4["int result = int(a / b)"] D3 -->|float result| D5["float result = a / b"] E --> E1[Can hold na] E1 --> E2[Prices, ratios, indicator values] E2 --> E3{Equality check?} E3 -->|Robust| E4["math.abs(a - b) < epsilon"] E3 -->|Direct| E5["a == b (May fail IEEE 754)"]
🔽 [Click to expand] View Full Pine Script Code
//@version=6
indicator("bool / float / int Type Demo", overlay = false)

// ─────────────────────────────────────────────
// SECTION 1: int type
// ─────────────────────────────────────────────

// Declare a persistent int counter using var
var int barCount = 0
barCount := barCount + 1  // increments every bar

// Integer division always returns float in Pine Script
// Use int() to truncate toward zero
int halfBars = int(barCount / 2)  // truncated integer division

// int can hold na — useful for "not yet set" states
var int lastBullBar = na
bool isBullBar = close > open
if isBullBar
    lastBullBar := bar_index  // record the last bullish bar index

// Safe distance calculation: guard against na
int barsSinceBull = na(lastBullBar) ? na : bar_index - lastBullBar

// ─────────────────────────────────────────────
// SECTION 2: float type
// ─────────────────────────────────────────────

// float is the default type for price-based calculations
float sma20 = ta.sma(close, 20)  // series float

// Ratio calculation — result is always float
float priceToSma = na(sma20) ? na : close / sma20

// Tolerance-based float comparison (robust against IEEE 754 artifacts)
float epsilon = 1e-9
bool nearSma = math.abs(close - sma20) < epsilon  // true if close ≈ sma20

// ─────────────────────────────────────────────
// SECTION 3: bool type — v6 strict binary
// ─────────────────────────────────────────────

// bool is strictly true/false in v6 — cannot hold na
bool aboveSma = close > sma20  // series bool

// CORRECT: bool → int conversion via ternary (NOT int(myBool) → CE10123)
int aboveSmaInt = aboveSma ? 1 : 0

// CORRECT: int → bool conversion via explicit comparison (NOT implicit cast)
// if aboveSmaInt  ← ILLEGAL in v6
// if aboveSmaInt != 0  ← LEGAL
bool aboveSmaBack = aboveSmaInt != 0

// Three-state proxy using int: 1=bullish, 0=bearish, na=insufficient data
var int regime = na
if bar_index >= 20  // only after enough bars for SMA
    regime := aboveSma ? 1 : 0

// ─────────────────────────────────────────────
// SECTION 4: Plots
// ─────────────────────────────────────────────

plot(priceToSma,    title = "Price/SMA Ratio",    color = color.blue,   linewidth = 2)
plot(aboveSmaInt,   title = "Above SMA (0/1)",    color = color.green,  linewidth = 1)
plot(barsSinceBull, title = "Bars Since Bull Bar", color = color.orange, linewidth = 1)
plot(regime,        title = "Regime (3-state)",   color = color.purple, linewidth = 1)

// Reference line at 1.0 for the ratio
hline(1.0, "SMA Parity", color = color.gray, linestyle = hline.style_dashed)

7. Variable Declaration Keywords and Type Interaction

The choice of declaration keyword (=, var, varip) interacts with type behavior, especially for bool in v6.

Keyword Initialization Persistence bool bar-0 history
= Every bar None (resets each bar) Returns false (v6)
var Once on bar 0 Persists across all bars Returns false (v6)
varip Once on bar 0 Persists + updates on every real-time tick Returns false (v6)

Key implication: If you declare var bool flag = false and reference flag[1] on bar 0, you get false — not na. In v5, this returned na, which could cause different branching behavior. Always audit v5 → v6 migrations for this pattern.

8. Arithmetic Operator Behavior by Type

The following table documents how arithmetic operators behave when operand types are mixed:

Expression Result Type Example Value
int + int int 3 + 2 = 5
int / int float 7 / 2 = 3.5
int * float float 3 * 1.5 = 4.5
float + float float 1.1 + 2.2 = 3.3000...04
int % int int 7 % 3 = 1

9. Conclusion

  • bool in v6 is strictly binary (true/false). It cannot hold na, and functions like na(), nz(), and fixnan() do not accept bool arguments. Use int or float as a three-state proxy when a missing state is required.
  • The / operator always returns float in Pine Script, regardless of operand types. Use int() or math.floor() explicitly when integer truncation is needed.
  • Type conversion between bool and numeric types requires explicit patterns: use the ternary operator (myBool ? 1 : 0) for bool→int/float, and explicit comparison (myInt != 0) or bool() cast for int/float→bool. int(myBool) and float(myBool) cause compile error CE10123 in v6.

Ideas for Script Advancement

  1. Type-safe signal encoding: Build a signal encoding system using int as a multi-state variable (e.g., 2=strong buy, 1=buy, 0=neutral, -1=sell, -2=strong sell, na=no data) to replace multiple bool flags with a single compact representation.
  2. Precision-aware comparison library: Create a reusable function library that wraps all float equality checks with configurable epsilon tolerance, preventing silent logic errors from IEEE 754 floating-point representation artifacts in price-level comparisons.

Related Posts

Comments

Popular posts from this blog

Pine Script v6: indicator() vs strategy() — Core Functional Differences Explained

Pine Script Variable Declaration: Mastering var, varip, and Proper Initialization Across Historical Bars

Pine Script v6 plot() Function: Complete Guide to Lines, Histograms, and Circles