Types

PyneCore uses Python’s native type system extended with a set of special type annotations. These annotations control how variables behave across bar executions. Everything else is standard Python.

Primitive Types

int

Integer values.

length: int = 14

int is a static type, as in Pine: TradingView runs every number as a double, and so does PyneCore. A value typed int travels as a Python float carrying an integral value — bar_index, math.floor(), array.size(), str.length() and the int() cast all hand back 2.0, not 2. This keeps every arithmetic path on one representation, and it is what makes na(int) a plain nan: an int can be na without a wrapper object, and mixed int/float expressions never switch representation mid-way.

Everything Pine-facing already accounts for it: str.tostring, log.*, the plot CSV export and str.format print an integral value without a fractional part, a Series truncates a fractional or float offset in its own [], and range() bounds and the indexes of a Python list, str or tuple are truncated at the call site by the loader. Where plain Python meets the value directly, the float shows: an f-string writes bar=0.0, so format with str.tostring or a format spec (f"{bar_index:.0f}"), and use round() when a genuine Python int is needed for something the loader cannot see, such as a dict key it should share with an int literal.

int() is Pine’s cast, not Python’s: it truncates toward zero and returns a Pine int (a float), and na stays na. nan and inf cannot be truncated, so int(na) is na.

float

Floating-point values. Python’s float.

price: float = 100.50

bool

Boolean values. Python’s bool.

is_bull: bool = close > open

A bool has two states, as in Pine v6: a bool history before warm-up, na(bool), a fresh array<bool> element and an unset UDT bool field are all False. Pine v4 and v5 scripts keep a third state, and a script converted from them asks for it with na_bool=True on its decorator:

@script.indicator("Three-state bool", na_bool=True)

With it a bool can be na: na(flag) is true before the first assignment, == and != on a na bool give na, while not, and, or, a branch condition and nz() treat it as False, the way TradingView does. Hand-written Pyne code can use it too.

string

Text values. Python’s str.

title: str = "My Script"

color

Color values with RGBA components. Implemented as pynecore.types.color.Color.

from pynecore.lib import script, close, color, plot

@script.indicator("Color Example", overlay=True)
def main():
    my_color = color.rgb(255, 0, 0, 50)  # Red with 50% transparency
    bull_color = color.green if close > close[1] else color.red
    plot(close, "Close", color=bull_color)

See Color for details.

na

Represents “not available” — the absence of a value. Implemented as pynecore.types.na.NA.

from pynecore.types import Series
from pynecore.lib import script, close, ta, na, nz

@script.indicator("NA Example")
def main():
    sma10: Series[float] = ta.sma(close, 10)

    # sma10 is na for the first 9 bars
    if na(x=sma10):
        safe_value = 0.0
    else:
        safe_value = sma10

    # or use nz() for the same thing
    safe_value = nz(sma10, 0.0)

Operations involving na propagate — na + 1 results in na. In boolean contexts, na evaluates to False. The predicate parameter is named x, so both na(sma10) and na(x=sma10) are valid.

Type Annotations

These annotations are recognized by the AST transformer at import time and control how variables behave during bar-by-bar execution.

Series[T]

A time series variable that stores a history of values, one per bar. Supports indexing to access past values.

from pynecore.types import Series
from pynecore.lib import close

current_close: Series[float] = close
previous_close = close[1]    # one bar ago
older_close = close[5]       # five bars ago

The assignment runs every bar, pushing the new value onto a circular buffer. Historical values are accessible via [n] indexing where n is the number of bars back. The buffer size defaults to 500 and is configurable via max_bars_back.

PyneCore also supports slicing (close[1:5]), which returns a read-only view of historical values. This is a PyneCore extension — not available in Pine Script.

When used without indexing, a Series value behaves like a plain value — arithmetic, comparisons, and function calls all work on the current bar’s value transparently.

Corresponds to TradingView’s series type.

Persistent[T]

A variable that persists its value across bars. Without this annotation, variables are re-initialized on every bar execution.

from pynecore.types import Persistent

bar_count: Persistent[int] = 0
bar_count += 1  # increments every bar, value carries over

The initialization (= 0) runs only on the first bar. On subsequent bars, the variable retains its value from the end of the previous bar.

Corresponds to TradingView’s var keyword.

When a script executes multiple times on the same bar (via calc_on_order_fills or on live ticks), Persistent variables are rolled back to the previous bar’s committed state before each re-execution.

IBPersistent[T]

Like Persistent, persists across bars — but eliminates the rollback that normally occurs before each successive execution on the same bar.

from pynecore.types import Persistent, IBPersistent

var_count: Persistent[int] = 0       # rolled back on re-execution
varip_count: IBPersistent[int] = 0   # NOT rolled back

var_count += 1    # always increments by 1 per bar
varip_count += 1  # increments by 1 per execution (including re-executions)

Corresponds to TradingView’s varip keyword.

On historical bars, a script typically executes only once per bar, so there is no rollback and IBPersistent behaves identically to Persistent. The distinction appears when a script runs multiple times on the same bar:

  • Strategies with calc_on_order_fills=True: the script re-executes after each order fill. Persistent variables are rolled back; IBPersistent variables retain their accumulated value.
  • Real-time / live bars: the script runs on every new tick. Persistent variables are rolled back to the bar’s opening state; IBPersistent variables keep their value across ticks.

See Bar Magnifier — calc_on_order_fills for the execution model details.

Collection Types

array

Dynamic arrays. Python lists with Pine Script-compatible wrapper methods.

from pynecore.types import Persistent
from pynecore.lib import script, close, array

@script.indicator("Array Example")
def main():
    prices: Persistent[list] = array.new_float(0)
    array.push(prices, close)

    if array.size(prices) > 20:
        array.shift(prices)  # keep last 20

    avg = array.avg(prices)

matrix

Two-dimensional matrices. Implemented as pynecore.types.matrix.Matrix.

from pynecore.types import Persistent
from pynecore.lib import script, matrix

@script.indicator("Matrix Example")
def main():
    m: Persistent[matrix] = matrix.new(float, 3, 3, 0.0)
    matrix.set(m, 0, 0, 1.0)
    val = matrix.get(m, 0, 0)

map

Key-value dictionaries with Pine Script-compatible methods.

from pynecore.types import Persistent
from pynecore.lib import script, close, map

@script.indicator("Map Example")
def main():
    prices: Persistent[map] = map.new(str, float)
    map.put(prices, "last_close", close)
    val = map.get(prices, "last_close")

Drawing Types

Drawing types create visual elements on the chart. Each type has a corresponding namespace with constructor and manipulation functions.

TypeConstructorDescription
labellabel.new()Text labels on the chart
lineline.new()Lines between two points
boxbox.new()Rectangular boxes
tabletable.new()Data tables
polylinepolyline.new()Multi-segment lines
linefilllinefill.new()Filled area between two lines

All drawing types support the .all property to access all active instances.

chart.point

A point on the chart defined by bar index/time and price.

from pynecore.lib import script, close, high, bar_index, time, chart, label

@script.indicator("Chart Point Example", overlay=True)
def main():
    p1 = chart.point.from_index(bar_index, close)
    p2 = chart.point.from_time(time, high)
    p3 = chart.point.now(close)

    label.new(p1, "Here")

Special Types

User-Defined Types (@udt)

Custom structured types using the @udt decorator.

from pynecore.core.pine_udt import udt

@udt
class OrderInfo:
    id: str
    price: float
    qty: int = 0

    def get_value(self) -> float:
        return self.price * self.qty

Corresponds to TradingView’s type keyword. The @udt decorator provides Pine Script-compatible semantics (NA-aware field defaults, copy behavior, etc.).

Enumerations

Enumerations use Python’s StrEnum. The value of each field is its display title.

from enum import StrEnum as Enum

class Direction(Enum):
    Up = "Up"
    Down = "Down"
    Sideways = "Sideways"

Corresponds to TradingView’s enum keyword.

Source

The source type represents plottable values selectable via input.source(). In practice it behaves as float. Implemented as pynecore.types.source.Source.