Script Format
Structure and anatomy of a PyneCore script file
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.
The edge Variant
You may also encounter scripts whose docstring starts with @pyne edge:
"""@pyne edge"""The extra token marks code that stays within a strict, Pine-equivalent subset of Python. The
PyneComp compiler emits it automatically, since compiled Pine Script always falls within this
subset. PyneCore runs @pyne edge scripts exactly like any other Pyne code — the token changes
nothing at runtime. It exists to enable future tooling that relies on the subset guarantee (for
example, a possible Pyne-to-Pine conversion). If you edit such a script and want the full power
of Python, simply remove the edge word from the docstring.
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, colorpynecore.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:
| Parameter | Default | Description |
|---|---|---|
title | '' | Display name |
overlay | False | Show on price chart (True) or separate pane |
format | inherit | Number formatting |
precision | None | Decimal digits |
max_bars_back | 0 | History buffer length (0 = auto) |
dynamic_requests | False | Allow dynamic request.*() calls |
na_bool | False | Keep Pine v4/v5’s three-state bool (see bool) |
@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):
| Parameter | Default | Description |
|---|---|---|
initial_capital | 100000 | Starting capital |
currency | currency.NONE | Account currency |
pyramiding | 0 | Max entries in same direction |
default_qty_type | percent_of_equity | Position sizing method |
default_qty_value | 100 | Default quantity |
commission_type | commission.percent | Commission calculation method |
commission_value | 0.0 | Commission amount |
slippage | 0 | Slippage in ticks |
margin_long | 100.0 | Long margin percentage |
margin_short | 100.0 | Short margin percentage |
calc_on_order_fills | False | Re-execute on fills |
calc_on_every_tick | False | Re-execute on every tick (live only) |
calc_on_every_history_tick | False | Re-execute on every historical tick |
use_bar_magnifier | True | Use LTF data for fill accuracy |
process_orders_on_close | False | Extra order processing after bar close |
close_entries_rule | 'FIFO' | Trade closing order |
risk_free_rate | 2.0 | For Sharpe ratio calculation |
close_by_id | False | One close() order per id (v4/v5) |
@script.library
For reusable library modules.
@script.library("My Utils", overlay=True)
def main():
...Exported functions may reference library constants, but must not capture non-constant
variables from the module or library main() scope. The AST validator also checks helpers
and default arguments reached by an export. Create per-call state and drawings inside the
exported function, or pass them as parameters. This check applies to handwritten Pyne and
to libraries run directly as the main script.
When a library is imported, drawing constructors and copies called by its main() (including
its helper calls) return typed na handles without allocating or registering drawings.
Setters and deletion on those handles have no effect. The same library draws normally when
run as the main script, and an exported function called by the importing script can create
normal drawings. This suppression is separate from the request.security execution context.
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.
5. Reserved Names
The transform adds names of its own to the script, so two kinds of identifier are reserved and rejected with a SyntaxError at load time:
- any identifier containing a middle dot (
·); - the plain double-underscore names the transform emits, such as
__state__,__bool1__,__cmp3__or any__pyne_<name>__.
Other double-underscore names, including the ones PyneComp writes into compiled scripts (__block_result__, __input_1__, …), are allowed. Strings and comments are not checked. See AST Transformations for the complete list.
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,
}