Configure PineScript
Define a native Pine indicator and expose named Buy and Sell arrows to SJS.
This guide creates an indicator that plots signals. SJS will own order placement. Native Pine is saved in the app's pinescript field.
For native runtime support, inputs, signal timing, and strategy-intent limitations, read the Pine Script guides.
For saved configuration, launch choices, and session controls, follow Apps & Running Sessions. Check monitoring after startup and stopping and restarting when changing a running candidate.
1. Create or open an app
Open your workspace in the console and create or edit an app. Choose its app type, instrument, and interval. For your first run, use Stage Session (SSS) with historical data available for that instrument.
Open the PineScript editor. Keep the SJS, L3, and SQL/SQX editors empty until the chart behaves as expected. This gives you one signal source to inspect.
2. Define the indicator
Paste this into PineScript:
//@version=5
indicator("Tutorial: range breakout", overlay=true)
length = input.int(15, "Range bars", minval=2)
rangeHigh = ta.highest(high, length)[1]
rangeLow = ta.lowest(low, length)[1]
buySignal = barstate.isconfirmed and ta.crossover(close, rangeHigh)
sellSignal = barstate.isconfirmed and ta.crossunder(close, rangeLow)
plot(rangeHigh, title="Range high", color=color.blue)
plot(rangeLow, title="Range low", color=color.orange)
plotshape(buySignal, title="Buy",
style=shape.triangleup, location=location.belowbar,
color=color.lime, size=size.small)
plotshape(sellSignal, title="Sell",
style=shape.triangledown, location=location.abovebar,
color=color.red, size=size.small)The previous range uses [1], so the current candle does not move its own breakout threshold. Confirmation keeps this example focused on completed bars. It needs enough input bars to initialize the range and crossover calculation.
3. Save and inspect the chart
Save the app and run a historical window with more than 15 bars. Look for two range lines and arrows named Buy and Sell. A quiet window may have no breakouts; the absence of arrows alone is not an error.
An indicator's arrows are chart output. They do not place orders by themselves.
4. Read the arrows in SJS
Pine chart series are exposed through:
const series = context.pineState?.chartData?.series ?? [];
const buyPlot = series.find((item) => item.name === "Buy");
const latestPoint = buyPlot?.data?.at(-1);Points may be arrays such as [time, value] or objects with x/time and y. Match the point's timestamp to new Date(bar.date).getTime() and reject missing values. An old arrow can remain the last point in a series after its signal has passed.
Native Pine runs after SJS in the shared frame, so a strict same-timestamp reader can reject the previous frame's output. See Signals in SJS for an explicit recent-event observer.
Keep plot titles and lookup names identical for a chart-series reader. The complete Pine signals to SJS orders tutorial uses the separate EMA indicator and its numeric pine.signal plot; use that paired source rather than connecting its reader directly to the Buy/Sell arrows above.
5. Choose one order owner
This tutorial uses indicator() plus SJS orders. Native Pine also supports strategy order intents through functions such as strategy.entry and strategy.close. If you use that path, do not also have SJS submit the same trades from arrows.
Troubleshooting
| Symptom | Check |
|---|---|
| No range lines | Pine compilation, input bars, selected instrument and interval |
| Lines but no arrows | A confirmed breakout must occur after the lookback initializes |
| Arrows but no orders | Add the SJS reader; indicators only plot signals |
| Stale or repeated entries | Compare signal and bar timestamps, and deduplicate execution attempts |
| Live startup differs from the test | Check warmup and whether the current bar is confirmed |
Live Pine sessions default to one hour of warmup unless configured otherwise. Historical in-memory runs use their selected data window and do not fetch external warmup. Choose an interval and window that supply enough bars for your indicator.