Your First PyneCore Script

This guide will walk you through creating and running your first PyneCore script. You’ll learn the basic structure of a Pyne script and see how it differs from regular Python.

The Magic Comment

Every PyneCore script must start with a special magic comment to identify it as a Pyne script:

  """
@pyne
"""
  

This comment must be placed at the beginning of your file, before any import statements. It tells the PyneCore system to apply the necessary AST transformations to make your Python code behave like Pine Script.

Basic Script Structure

A minimal PyneCore script has the following structure:

  """
@pyne
"""
from pynecore.lib import script, close

@script.indicator("My First Indicator")
def main():
    # Your code goes here
    return {"close": close}
  

Let’s break this down:

  1. Magic Comment: Signals that this is a Pyne script
  2. Imports: Import necessary modules from PyneCore
  3. Script Declaration: Use a decorator to define the script type (indicator, strategy, etc.)
  4. Main Function: The entry point of your script that will be executed for each bar

Creating a Simple Moving Average Indicator

Let’s create a simple indicator that calculates and plots a 20-period Simple Moving Average (SMA):

  """
@pyne
"""
from pynecore import Series
from pynecore.lib import script, close, ta, plot, color

@script.indicator("Simple Moving Average", overlay=True)
def main():
    # Calculate 20-period SMA
    sma = ta.sma(close, 20)

    # Plot the SMA on the chart (PyneCore won't plot anything, it just saves the data)
    plot(sma, "SMA")
  

Save this code to a file named simple_ma.py in your working directorys scripts/ folder.

Download some data

This will download the OHLCV data for the BTC/USDT pair from Bybit and save it as ccxt_BYBIT_BTC_USDT_USDT_1D.ohlcv in your working directorys data/ folder:

  pyne data download ccxt --symbol "BYBIT:BTC/USDT:USDT"
  

Running Your Script

To run your PyneCore script, use the command-line interface:

  pyne run simple_ma ccxt_BYBIT_BTC_USDT_USDT_1D.ohlcv
  

This will run your script on every bar of the OHLCV data, and save the plots or returned values to the output folder of your working directory. This mechanism is the heart of PyneCore. You actually write a script that will run on every candle.

Adding Parameters

Let’s enhance our script by adding user-configurable parameters:

  """
@pyne
"""
from pynecore import Series
from pynecore.lib import script, close, ta, plot, color, input

@script.indicator("Customizable Moving Average", overlay=True)
def main():
    # Input parameters
    length = input.int("Period", 20, minval=1)
    ma_type = input.string("Type", "SMA", options=["SMA", "EMA", "WMA"])
    line_color = input.color("Line Color", color.blue)

    # Calculate the selected moving average
    ma: Series[float] = None
    if ma_type == "SMA":
        ma = ta.sma(close, length)
    elif ma_type == "EMA":
        ma = ta.ema(close, length)
    else:  # WMA
        ma = ta.wma(close, length)

    # Plot the result
    plot(ma, f"{ma_type} ({length})", color=line_color, linewidth=2)
  

PyneCore will save a toml file in the output folder which contains the parameters of your script. If you would like to change a parameter, you can do so by removing the comment from the line and changing the default value.

Understanding Series and Persistence

Two key concepts in PyneCore are Series and Persistent variables:

Series Variables

A Series variable holds a time series of values, with one value per bar:

  """
@pyne
"""
from pynecore import Series
from pynecore.lib import script, close, high, low, ta, plot, color

@script.indicator("Series Example", overlay=True)
def main():
    # Series variables
    price: Series[float] = close
    highest: Series[float] = ta.highest(high, 10)
    lowest: Series[float] = ta.lowest(low, 10)

    # Series operations
    middle = (highest + lowest) / 2

    # Accessing historical values
    prev_price = price[1]  # Previous bar's price

    # Plot results
    plot(middle, "Middle", color=color.purple)
  

In Pyne code you can declare a Series by adding a Series type annotation, but you don’t need to actually create a Series object. More about Series here.

Persistent Variables

Persistent variables retain their values between bars:

  """
@pyne
"""
from pynecore import Series, Persistent
from pynecore.lib import script, close, plot, color

@script.indicator("Persistent Example", overlay=True)
def main():
    # Persistent variable - retains its value from bar to bar
    counter: Persistent[int] = 0
    counter += 1

    # Persistent with series operations
    sum_price: Persistent[float] = 0
    sum_price += close

    avg_price = sum_price / counter

    # Plot
    plot(avg_price, "Average Price", color=color.orange)
  

More about Persistent variables here.

Creating a Complete Strategy

Let’s create a simple trading strategy using PyneCore:

  """
@pyne
"""
from pynecore import Series
from pynecore.lib import script, close, ta, strategy, plot, color

@script.strategy("Simple Crossover Strategy", overlay=True)
def main():
    # Calculate fast and slow moving averages
    fast_ma: Series[float] = ta.ema(close, 9)
    slow_ma: Series[float] = ta.ema(close, 21)

    # Define entry conditions
    buy_signal = ta.crossover(fast_ma, slow_ma)
    sell_signal = ta.crossunder(fast_ma, slow_ma)

    # Execute the strategy
    if buy_signal:
        strategy.entry("Long", strategy.long)
    elif sell_signal:
        strategy.entry("Short", strategy.short)

    # Plot indicators
    plot(fast_ma, "Fast EMA", color=color.blue)
    plot(slow_ma, "Slow EMA", color=color.red)
  
Note: The `Series[float]` type annotation in the above code is not necessary, as it is not used as a series because it is not indexed. It is included for clarity. The `ta.ema` function creates its own Series internally, so the basic float type could have been used instead. This differs from Pine Script behavior.

Working with Functions

You can define your own functions in PyneCore:

  """
@pyne
"""
from pynecore import Series
from pynecore.lib import script, close, plot, color, math

def average(a, b):
    return (a + b) / 2

def custom_ma(src, length):
    """A simple moving average implementation"""
    sum = 0.0
    for i in range(length):
        sum += src[i]
    return sum / length

@script.indicator("Custom Functions", overlay=True)
def main():
    # Use custom functions
    avg = average(close, close[1])
    custom_average: Series[float] = custom_ma(close, 20)

    # Plot results
    plot(avg, "Bar Average", color=color.green)
    plot(custom_average, "Custom MA", color=color.purple)
  

You could also use inline functions, if you need variables from outer scopes. Learn more about how functions work in PyneCore in the Function Isolation and Advanced Function Isolation documentation.

Next Steps

Now that you’ve created your first PyneCore script, you can:

  1. Learn how to convert existing Pine Script code to PyneCore
  2. Explore the core concepts of PyneCore in depth
  3. Check out the library documentation for available functions and indicators
  4. Dive into advanced topics for more technical details