Differences from Pine Script
Key differences between PyneCore and TradingView Pine Script
Differences from Pine Script
1st and most important, it is Python ;)
Structure differences
Pyne magic comment
Pyne codes must start with a magic doc-comment @pyne
to be recognized as Pyne code.
"""
@pyne
"""
Under the hood, this tells the AST transformers that this is a Pyne code and should be transformed. Also you, the programmer can clearly see, this code will have extra Pine like features.
Main function
In Pyne all runnable scripts must have a main()
function. This function is the entry point of the script.
def main():
...
# Your code here
Imported modules don’t need to have a main()
function, but they can e.g. for testing purposes.
Indicator, strategy or library
In Pine Script, you should have an indicator()
, strategy()
or library()
function to define your script type.
In contrast, Pyne uses decorators to define the script type.
from pynecore.lib import script
@script.indicator("My Indicator", shorttitle="MI")
def main():
...
Looks much more cleaner and pythonic, right?
Inputs
In Pine Script, you define your inputs with the input()
and input.int()
, input.X()
like functions.
In Pyne, you define your inputs as the main function arguments.
from pynecore import Series
from pynecore.lib import script, input
@script.indicator("My Indicator", shorttitle="MI")
def main(
src: Series[float] = input.source('close', title="Source"),
length: int = input.int(5, title="Length"),
):
...
Still better, huh?
Plotting
In Pine Script, you plot your data with the plot()
function. You can use this in Pyne too, but Pyne
has a more pythonic way to “plot” your data:
from pynecore import Series
from pynecore.lib import script, input, plot, ta
@script.indicator("My Indicator", shorttitle="MI")
def main(
src: Series[float] = input.source('close', title="Source"),
length: int = input.int(5, title="Length"),
):
sma = ta.sma(src, length)
ema = ta.ema(src, length)
plot(sma, title="SMA") # This works the same way as in Pine
# This is the Pyne way:
return {
"EMA": ema, # Title: vale
}
You can use both way according to your taste.
Variable Scope and Lifecycle
Both Pine Script and Python (Pyne) use lexical scoping, but with important differences:
Scope Model Differences
Pine Script has stricter scoping rules than Python:
Pine Script:
- Uses block-level scoping
- Every code block (if statements, for loops, functions) has its own scope
- Variables defined within blocks are not accessible outside that block
if (condition)
blockVar = 10 // Only accessible within this if block
Python/PyneCore:
- Uses function-level scoping
- Only functions create new scopes
- Variables from blocks (if statements, for loops) remain accessible outside those blocks
if condition:
block_var = 10 # Still accessible outside the if block!
print(block_var) # Works in Python, would fail in Pine Script
Variable Lifecycle Differences
Pine Script:
- Variables reinitialize on each bar by default
- Use the
var
keyword to make a variable persist between bars
counter = 0 // Resets to 0 on each bar
var persistentCounter = 0 // Keeps its value across bars
PyneCore:
- Regular variables follow normal Python behavior
- Use
Persistent[T]
type annotation to indicate persistence between bars
counter = 0 # Normal Python variable
persistentCounter: Persistent[int] = 0 # Persists across bars
Series Behavior
Pine Script:
- Every variable is a Series by default
- Can access historical values with index operator:
close[1]
PyneCore:
- Series variables must be explicitly marked with
Series[T]
type annotation - Historical values accessed the same way:
close[1]
regular_var = 5 # Not a Series
series_var: Series[float] = close # Explicitly marked as Series
previous_value = series_var[1] # Access previous bar's value
These differences require special attention when converting code from Pine Script to PyneCore, particularly regarding block-level variables and explicit type annotations.
Inline functions
In Pyne code you can use inline functions. That is very similar to Pine Script’s functions, where you can access variables from outer scopes. Though in Pyne you can also modify outer scope variables by using the nonlocal
keyword.
// Pine Script
x = 1
inner() =>
x += 1 // This raises an error in Pine Script
x
def main():
x = 1
def inner():
nonlocal x
x += 1 # This works in Pyne (Python)
return x
y = inner()
return {
"x": x,
"y": y,
}
Both x
and y
will be 2
in this case.
Library differences from Pine
Different modul names
Pine | PyneCore | Why? |
---|---|---|
str | string | str is a frequently used builtin keyword in python |
Different function names
Pine | PyneCore | Why? |
---|---|---|
array.from() | array.from_items() | Because from is a keyword in Python |
Type differences
na
In Pine Script every basic types can be na
(not available). In Pyne there is an NA
class that works
the same way. Though in Pine if you write the following code:
int i = na // Note that you must specify the type here
float f = na
i
will be an int(na)
and f
will be a float(na)
. So na
will have the type of the variable.
In Python it is not possible, so in Pyne you can add a type to na
like this:
from pynecore.types.na import NA
i = NA(int)
f = NA(float)
or
from pynecore.lib import na
i = na(int)
f = na(float)
Most of the time it is not necessary to specify the type, it is needed only when you want to use method
overloading. If you do not specify the type, it will be NA[int]
by default.
Array
Although we implemented all the array functions in array module, we decided to not implement Pine’s array
class and
its methods. Instead, we use Python’s builtin list
, because it is almost the same
and Pyne is just a Python framework after all.
This means if you see something like this in Pine:
a = array.new_float(5)
a.set(0, 1.0)
a.push(2.0)
You can do the same in Pyne like this:
from pynecore.types.na import NA
from pynecore.lib import array, na
# (Note, type hints are optional here)
a: list[float | NA[float]] = array.new_float(5) # Functional Pine Script way, `a` is just a python list
b: list[float | NA[float]] = [na(float)] * 5 # Pythonic way, for the same result
array.set(a, 0, 1.0) # Functional Pine Script way
b[0] = 1.0 # Pythonic way for the same
array.push(a, 2.0) # Functional Pine Script way
b.append(2.0) # Pythonic way for the same
Map
The same goes for the map
class. We use the builtin dict
class which is almost the same. Though
you can use the map
module too, like in Pine, but it is just a wrapper around the builtin dict
class.
Series
Pine Script uses dedicated series objects. Which is created on declaration. In Pyne we designed the system to use the “primitive” type everywhere. If you mark a variable as a series, it will create a series object, but every osage of them will be that basic type. This way the implementation could be much simpler. We don’t need to handle everything differently because it is a series or not.