Script Format

A PyneCore script is a standard .py file with a specific structure. This page documents the required elements and conventions.

Minimal Example

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


@script.indicator("My Indicator")
def main():
    return {"close": close}

1. Magic Comment

Every PyneCore script must begin with a docstring that starts with @pyne:

"""@pyne"""

The docstring can also serve as the script’s documentation — any text after @pyne is treated as a description:

"""
@pyne
RSI Mean Reversion Strategy

This strategy enters long positions when RSI crosses above the oversold level
and exits when RSI crosses below the overbought level.
"""

This marker tells the import hook to apply AST transformations (Series, Persistent, function isolation, etc.) before executing the module. Without it, the file is treated as regular Python.

2. Imports

Import type annotations from pynecore and library functions from pynecore.lib:

from pynecore.types import Series, Persistent, IBPersistent
from pynecore.lib import script, input, close, open, high, low, volume, ta, strategy, plot, color

pynecore.types provides the type annotations. pynecore.lib provides all runtime functions, built-in variables, and namespaces (ta, math, strategy, color, etc.).

3. Script Decorator

The main() function must be decorated with one of:

@script.indicator

For scripts that calculate and display values.

@script.indicator("SMA Indicator", overlay=True)
def main():
    ...

Key parameters:

ParameterDefaultDescription
title''Display name
overlayFalseShow on price chart (True) or separate pane
formatinheritNumber formatting
precisionNoneDecimal digits
max_bars_back0History buffer length (0 = auto)
dynamic_requestsFalseAllow dynamic request.*() calls

@script.strategy

For scripts that simulate trading.

@script.strategy(
    "MA Crossover",
    overlay=True,
    initial_capital=10000,
    commission_type=strategy.commission.percent,
    commission_value=0.1,
)
def main():
    ...

Key parameters (in addition to indicator parameters):

ParameterDefaultDescription
initial_capital1000000Starting capital
currencycurrency.NONEAccount currency
pyramiding0Max entries in same direction
default_qty_typestrategy.fixedPosition sizing method
default_qty_value1Default quantity
commission_typecommission.percentCommission calculation method
commission_value0.0Commission amount
slippage0Slippage in ticks
margin_long100.0Long margin percentage
margin_short100.0Short margin percentage
calc_on_order_fillsFalseRe-execute on fills
calc_on_every_tickFalseRe-execute on every tick (live only)
use_bar_magnifierTrueUse LTF data for fill accuracy
process_orders_on_closeFalseExtra order processing after bar close
close_entries_rule'FIFO'Trade closing order
risk_free_rate2.0For Sharpe ratio calculation

@script.library

For reusable library modules.

@script.library("My Utils", overlay=True)
def main():
    ...

4. The main() Function

The main() function is called once per bar during execution. It receives input parameters and contains all script logic.

Input Parameters

Configurable parameters are declared as function arguments with input.*() defaults:

@script.indicator("RSI", overlay=False)
def main(
        length: int = input.int(14, title="RSI Length", minval=1),
        overbought: int = input.int(70, title="Overbought"),
        oversold: int = input.int(30, title="Oversold"),
        src: Series[float] = input.source(close, title="Source"),
):
    rsi_value = ta.rsi(src, length)
    ...

See Input Functions for all available input types.

Return Value

main() can optionally return a dict of named values to plot:

def main():
    fast = ta.sma(close, 9)
    slow = ta.sma(close, 21)
    return {"Fast MA": fast, "Slow MA": slow}

Alternatively, use plot() calls within the function body. Both approaches can be combined.

Complete Example

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


@script.strategy(
    "RSI Mean Reversion",
    overlay=True,
    initial_capital=10000,
    commission_type=strategy.commission.percent,
    commission_value=0.1,
)
def main(
        rsi_length: int = input.int(14, title="RSI Length", minval=1),
        oversold: int = input.int(30, title="Oversold Level"),
        overbought: int = input.int(70, title="Overbought Level"),
):
    rsi: Series[float] = ta.rsi(close, rsi_length)

    trade_count: Persistent[int] = 0

    if ta.crossover(rsi, oversold):
        strategy.entry("Long", strategy.long)
        trade_count += 1

    if ta.crossunder(rsi, overbought):
        strategy.close("Long")

    plot(rsi, "RSI", color=color.purple)

    return {
        "Overbought": overbought,
        "Oversold": oversold,
    }