Starter Examples
The safest first examples to copy — indicator starter, strategy starter, and visual starter for ATK PyneScript V6.
Starter Examples#
If you are starting from zero, these three scripts are the safest to copy because they map directly to the current runtime contract.
| File | What it demonstrates | Best use | Download |
|---|---|---|---|
pynescript_indicator_starter.py | indicator, input.int, input.source, ta.rsi, hline. | First runnable indicator example. | Download |
pynescript_strategy_starter.py | strategy, EMA crossover, mapped execution fields, build_mapped_trade_frame. | Canonical strategy starter. | Download |
pynescript_visual_indicator_starter.py | plot, plotshape, hline, static frame.attrs payload for box, label, line objects. | Best introduction to render-only build_visuals. | Download |
Indicator Starter#
Use this when you want the smallest correct indicator scaffold with inputs, TA, and a single computed output series. All series computation belongs in build_indicator_frame. Static visuals like hline can be declared at module scope.
# @name: pynescript_indicator_starter
import pandas as pd
from source import indicator, input, ta, hline
indicator("Pyne Indicator Starter", overlay=False)
length = input.int(14, title="Length", key="length")
source_type = input.source("close", title="Source", key="source_type")
hline(70, title="Overbought", color="#ff6b6b")
hline(30, title="Oversold", color="#51cf66")
def build_indicator_frame(df: pd.DataFrame, params: dict | None = None) -> pd.DataFrame:
frame = df.copy().reset_index(drop=True)
p = {"length": int(length), "source_type": str(source_type)} | dict(params or {})
src_name = str(p.get("source_type", "close"))
src = frame[src_name] if src_name in frame.columns else frame["close"]
frame["value"] = ta.rsi(src, int(p.get("length", 14)))
return frameStrategy Starter#
Use this as the canonical starting point for trade-frame strategies. build_signal_frame computes signals and canonical execution fields. build_trade_frame stays mapping-only by delegating to build_mapped_trade_frame.
# @name: pynescript_strategy_starter
import numpy as np
import pandas as pd
from source import strategy, input, ta, build_mapped_trade_frame
strategy("Pyne Strategy Starter", overlay=True, process_orders_on_close=True)
fast_period = input.int(10, title="Fast", key="fast_period")
slow_period = input.int(21, title="Slow", key="slow_period")
trade_qty = input.float(1.0, title="Trade Qty", key="trade_qty")
size_pct = input.float(0.0, title="Size %", key="size_pct")
def build_signal_frame(df: pd.DataFrame, params: dict | None = None) -> pd.DataFrame:
frame = df.copy().reset_index(drop=True)
p = {
"fast_period": int(fast_period),
"slow_period": int(slow_period),
"trade_qty": float(trade_qty),
"size_pct": float(size_pct),
} | dict(params or {})
ema_fast = ta.ema(frame["close"], int(p["fast_period"]))
ema_slow = ta.ema(frame["close"], int(p["slow_period"]))
frame["ema_fast"] = ema_fast
frame["ema_slow"] = ema_slow
frame["buy_signal"] = ta.crossover(ema_fast, ema_slow).fillna(False)
frame["sell_signal"] = ta.crossunder(ema_fast, ema_slow).fillna(False)
frame["entry_side"] = np.where(frame["buy_signal"], "BUY", np.where(frame["sell_signal"], "SELL", ""))
frame["entry_price"] = frame["open"]
frame["quantity"] = float(p.get("trade_qty", 0.0) or 0.0)
frame["size_pct"] = float(p.get("size_pct", 0.0) or 0.0)
return frame
def build_trade_frame(signal_df: pd.DataFrame, params: dict | None = None, styles: dict | None = None) -> pd.DataFrame:
return build_mapped_trade_frame(signal_df)Visual Indicator Starter#
Use this when row-aligned plots are not enough and you need object visuals such as boxes, labels, or lines derived from the computed frame. Keep dynamic calculation in build_indicator_frame, store static object config in frame.attrs, then create visuals in a render-only build_visuals.
# @name: pynescript_visual_indicator_starter
import pandas as pd
from source import indicator, input, ta, plot, plotshape, hline
indicator("Pyne Visual Indicator Starter", overlay=True)
length = input.int(20, title="Length", key="length")
plot("ema_fast", key="ema_fast", title="EMA Fast", color="#00c853", width=2)
plot("ema_slow", key="ema_slow", title="EMA Slow", color="#ff6d00", width=2)
hline(100, key="price_ref", title="Reference", color="#9e9e9e")
plotshape("buy_signal", key="buy_markers", location="belowbar", style="arrow_up", color="#00c853", text="BUY")
def build_indicator_frame(df: pd.DataFrame, params: dict | None = None) -> pd.DataFrame:
frame = df.copy().reset_index(drop=True)
p = {"length": int(length)} | dict(params or {})
fast_length = int(p.get("length", 20))
slow_length = max(fast_length + 5, 2)
frame["ema_fast"] = ta.ema(frame["close"], fast_length)
frame["ema_slow"] = ta.ema(frame["close"], slow_length)
frame["buy_signal"] = ta.crossover(frame["ema_fast"], frame["ema_slow"]).fillna(False)
if not frame.empty:
last = frame.iloc[-1]
anchor = frame.iloc[max(len(frame) - 10, 0)]
frame.attrs["visual_indicator_payload"] = {
"box": {
"left": int(anchor["index"]), "top": float(last["high"] + 0.5),
"right": int(last["index"]), "bottom": float(last["low"] - 0.5),
"text": "ACTIVE RANGE", "bgcolor": "rgba(41,98,255,0.12)", "border_color": "#2962ff",
},
"label": {"x": int(last["index"]), "y": float(last["close"]),
"text": "LAST", "color": "#2962ff", "style": "label_down"},
"line": {"x1": int(anchor["index"]), "y1": float(anchor["close"]),
"x2": int(last["index"]), "y2": float(last["close"]),
"color": "#ffd600", "text": "TREND"},
}
else:
frame.attrs["visual_indicator_payload"] = {}
return frame
def build_visuals(frame: pd.DataFrame, params: dict | None = None, ctx=None):
if frame is None or frame.empty or ctx is None:
return None
payload = dict(frame.attrs.get("visual_indicator_payload") or {})
if not payload:
return None
ctx.box.new(key="last_range_box", **dict(payload.get("box") or {}))
ctx.label.new(key="last_close_label", **dict(payload.get("label") or {}))
return ctx.line.new(key="trend_hint", **dict(payload.get("line") or {}))Download visual indicator starter
Core API Recipes#
Annotated Indicator Declaration#
import pandas as pd
from source import indicator, input, plot, ta
indicator("Annotated EMA Overlay", overlay=True, precision=4, max_bars_back=200)
length = input.int(34, title="EMA Length", key="length", minval=1)
source_type = input.source("close", title="Source", key="source_type")
plot("ema_value", key="ema_value", title="EMA", color="#00c853", width=2)
def build_indicator_frame(df: pd.DataFrame, params: dict | None = None) -> pd.DataFrame:
frame = df.copy().reset_index(drop=True)
merged = {"length": int(length), "source_type": str(source_type)} | dict(params or {})
source_name = str(merged.get("source_type", "close") or "close")
source_series = frame[source_name] if source_name in frame.columns else frame["close"]
frame["ema_value"] = ta.ema(source_series, int(merged.get("length", 34) or 34))
return frameAnnotated Library Declaration#
from source import library, import_library
library("Annotated Utility Library", version=1, overlay=False, description="Reusable helper functions")
def clamp_length(value, minimum=1):
return max(int(value), int(minimum))
# In another script, import only the exports you want.
utils = import_library("Annotated Utility Library@1", exports=["clamp_length"])
safe_length = utils.clamp_length(0, 2)See also: