Converting from Pine Script
Learn how to convert your TradingView Pine Script code to PyneCore
Converting from Pine Script
If you have existing Pine Script that you want to use with PyneCore, you have two options:
- Manual conversion: Convert your Pine Script code to Pyne code by hand
- Automatic conversion: Use the PyneComp compiler (closed source SaaS service) to automatically convert your code. This will be integrated in to the pynecore CLI through API keys.
This guide explains both approaches and highlights the key differences you need to be aware of.
Basic Structure Conversion
Let’s start with the basic structure of a script:
Pine Script:
//@version=6 indicator("My Indicator", overlay=true) // Calculations ma = ta.sma(close, 20) // Plotting plot(ma, "SMA", color=color.blue) PyneCore:
""" @pyne """ from pynecore.lib import script, ta, close, plot, color @script.indicator("My Indicator", overlay=True) def main(): # Calculations ma = ta.sma(close, 20) # Plotting plot(ma, "SMA", color=color.blue) Key differences:
- PyneCore uses a magic comment
@pyneinstead of//@version=6(though you could put any metadata after the@pynecomment) - You need to import the required components from the
pynecore.libmodule - The script declaration is a decorator on the
main()function - The main code goes inside the
main()function
Variable Declaration Differences
PyneCore uses Python’s type hints for variable declarations:
Pine Script:
//@version=6 indicator("Variable Example") // Variable declarations var float myFloat = 0.0 var int myInt = 0 var bool myBool = true var string myString = "text" float[] myArray = array.new_float(0) PyneCore:
""" @pyne """ from pynecore import Series, Persistent from pynecore.lib import script, array @script.indicator("Variable Example") def main(): # Variable declarations myFloat: Persistent[float] = 0.0 # Note: you don't need to create a Persistent object, just annotate the type myInt: Persistent[int] = 0 myBool: Persistent[bool] = True myString: Persistent[str] = "text" myArray = array.new_float(0) Key differences:
varin Pine Script becomesPersistentin PyneCore- PyneCore uses Python’s type hints system (
variable: Type = value) - Arrays are handled similarly, but with Python’s syntax
Series Variables
In Pine Script, all variables are series by default. In PyneCore, you need to be explicit:
Pine Script:
//@version=6 indicator("Series Example") // All these are series price = close avgPrice = ta.sma(price, 20) PyneCore:
""" @pyne """ from pynecore import Series from pynecore.lib import script, close, ta @script.indicator("Series Example") def main(): # Need to be explicit about Series types price: Series[float] = close avgPrice: Series[float] = ta.sma(price, 20) Function Definitions
Function definitions use Python syntax but behave like Pine Script functions:
Pine Script:
//@version=6 indicator("Function Example") // Function definition myMAFunc(src, len) => result = ta.sma(src, len) resultSquared = math.pow(result, 2) resultSquared // Function usage value = myMAFunc(close, 20) plot(value) PyneCore:
""" @pyne """ from pynecore import Series from pynecore.lib import script, close, ta, plot, math def myMAFunc(src, len): result = ta.sma(src, len) resultSquared = math.pow(result, 2) return resultSquared # Explicit return statement @script.indicator("Function Example") def main(): # Function usage value = myMAFunc(close, 20) plot(value) Conditional Statements
Conditional statements use Python syntax:
Pine Script:
//@version=6 indicator("Conditional Example") // Condition condition = close > open // Conditional (ternary) operator barColor = condition ? color.green : color.red // If statement if (condition) strategy.entry("Long", strategy.long) else strategy.close("Long") PyneCore:
""" @pyne """ from pynecore.lib import script, close, open, color, plot, strategy @script.strategy("Conditional Example") def main(): # Condition condition = close > open # Conditional logic (ternary operator becomes if-else) barColor = color.green if condition else color.red # If statement if condition: strategy.entry("Long", strategy.long) else: strategy.close("Long") Loops
Loops use Python syntax:
Pine Script:
//@version=6 indicator("Loop Example") // For loop sum = 0.0 for i = 0 to 9 sum := sum + close[i] plot(sum / 10) PyneCore:
""" @pyne """ from pynecore import Series from pynecore.lib import script, close, plot @script.indicator("Loop Example") def main(): # For loop sum = 0.0 for i in range(10): sum += close[i] plot(sum / 10) NA Value Handling
In Pine Script, operations with na values propagate na. In PyneCore, you can use the na() function to check for NA values:
Pine Script:
//@version=6 indicator("NA Example") // NA handling if na(close) close := open value = nz(close, 0) PyneCore:
""" @pyne """ from pynecore.lib import script, close, open, plot, na, nz @script.indicator("NA Example") def main(): # NA handling if na(close): close_value = open else: close_value = close value = nz(close, 0) plot(value) Automatic Conversion with PyneComp
For complex scripts, you can use the PyneComp compiler (a closed source SaaS service). This service automatically converts Pine Script code to PyneCore Python code.
Benefits of using PyneComp:
- Saves time on manual conversion
- Handles complex syntax and edge cases
- Generates clean, readable Python code
- Offers a “strict mode” for better variable scoping
Example: Complete Strategy Conversion
Here’s a more complex strategy conversion example:
Pine Script (RSI Strategy):
//@version=6 strategy("RSI Strategy", overlay=true) // Input parameters length = input.int(14, "RSI Length") overbought = input.int(70, "Overbought") oversold = input.int(30, "Oversold") // Calculate RSI rsiValue = ta.rsi(close, length) // Entry/exit conditions enterLong = ta.crossover(rsiValue, oversold) exitLong = ta.crossover(rsiValue, overbought) // Strategy execution if (enterLong) strategy.entry("Long", strategy.long) else if (exitLong) strategy.close("Long") // Display RSI hline(overbought, "Overbought", color.red) hline(oversold, "Oversold", color.green) PyneCore (RSI Strategy):
""" @pyne """ from pynecore import Series from pynecore.lib import script, close, ta, strategy, input, hline, color @script.strategy("RSI Strategy", overlay=True) def main(): # Input parameters length = input.int("RSI Length", 14) overbought = input.int("Overbought", 70) oversold = input.int("Oversold", 30) # Calculate RSI rsiValue: Series[float] = ta.rsi(close, length) # Entry/exit conditions enterLong = ta.crossover(rsiValue, oversold) exitLong = ta.crossover(rsiValue, overbought) # Strategy execution if enterLong: strategy.entry("Long", strategy.long) elif exitLong: strategy.close("Long") # Display RSI hline(overbought, "Overbought", color=color.red) hline(oversold, "Oversold", color=color.green) Common Conversion Pitfalls
When converting from Pine Script to PyneCore, be aware of these common issues:
1. Variable Scope and Lifecycle Differences
Both Pine Script and Python use lexical scoping, but with important differences:
Scope differences:
- Pine Script: Block-level scoping - every code block (if, for, functions) has its own scope
- Python: Function-level scoping - only functions create new scopes, blocks (if, for) do not
Variable lifecycle differences:
- Pine Script: By default, variables reinitialize on each bar; use the
varkeyword to make them persist between bars - PyneCore: By default, variables follow normal Python behavior; use
Persistent[T]type annotation to indicate persistence between bars
Series behavior:
- Pine Script: Every variable is a Series by default
- PyneCore: Series variables must be explicitly marked with
Series[T]type annotation
Pine Script example:
//@version=6 indicator("Scope Example") // Reinitializes on each bar counter = 0 // Persists between bars var persistentCounter = 0 myFunction() => // Only accessible within the function localCounter = 0 if (true) // Only accessible within the if block blockCounter = 42 localCounter := blockCounter // blockCounter is NOT accessible outside the if block! // blockCounter := 10 // This would cause an error localCounter // Return value Equivalent PyneCore code:
""" @pyne """ from pynecore import Series, Persistent from pynecore.lib import script @script.indicator("Scope Example") def main(): # Reinitializes on each bar, explicitly marked as Series counter: Series[int] = 0 # Persists between bars persistentCounter: Persistent[int] = 0 def my_function(): # Only accessible within the function local_counter = 0 if True: # DIFFERENCE: block_counter is accessible OUTSIDE the if block in Python! block_counter = 42 local_counter = block_counter # In Python, block_counter would be usable here # block_counter = 10 # This would work in Python (but not in Pine) return local_counter The most important difference is that compared to Pine Script’s block-level scoping, Python is less strict, with only functions creating new scopes. Therefore, block-level variables that would be safely encapsulated in Pine Script might have broader scope in Python/PyneCore implementations.
2. Type Handling
PyneCore requires explicit type annotations for Series and Persistent variables.
3. Return Values
In Pine Script, the last expression is automatically the return value. In PyneCore (Python), you need an explicit return statement.
4. Ternary Operators
Pine Script’s ternary operator (condition ? value1 : value2) must be converted to Python’s if-else statements.
5. Reassignment Operator
Pine Script’s := reassignment operator becomes = in Python.
Best Practices for Conversion
- Start small: Begin with simple scripts and work your way up
- Test thoroughly: Compare results with the original Pine Script
- Use type annotations: Be explicit about Series and Persistent variables
- Leverage Python features: Take advantage of Python’s richer features during conversion
- Keep organized: Break complex scripts into functions or modules