← Back to Paction

PactionScript Documentation

Build custom indicators, layout themes, and interactive widgets for Paction trading terminal.

Overview

PactionScript is a safe scripting language for creating custom tools in the Paction Creator. It runs in a sandboxed environment with access only to chart data and a curated API — no access to DOM, network, or browser APIs.

Three script types
  • Indicator — draws on the chart: lines, shapes, fills, oscillator panels
  • Layout — customizes the interface with CSS: colors, fonts, spacing, theming
  • Layout + Indicator — combines both: custom UI + chart overlays

Quick Start

Open Create in the top bar, select Indicator type, paste this and press Ctrl+Enter:

var len = input.number("Length", 20, 5, 200);
var ma = sma(close, len);
plot(ma, "Moving Average", "#2962ff", 2);

This creates a simple moving average indicator with a configurable length parameter.

Script Types

Indicator

Pure chart overlay. Has access to all price data, indicator functions, and drawing API. Cannot modify page layout.

Layout

Pure CSS. Customizes the interface appearance. Write standard CSS targeting Paction's class names. No JavaScript.

Layout + Indicator (Hybrid)

Combines both. Use /* CSS */ and /* END CSS */ markers or // @css prefix for CSS lines:

/* CSS */
.top-bar { background: #1a1e2e; }
.logo { color: #ff9800; }
/* END CSS */

var ma = sma(close, 20);
plot(ma, "MA", "#ff9800", 2);

Security Sandbox

PactionScript runs in an isolated sandbox. The following are blocked:

Use the Widget API instead of DOM manipulation. Widgets are safe declarative objects that the engine renders for you.

Price Data DATA

These arrays contain OHLCV data for every candle on the chart:

VariableTypeDescription
opennumber[]Opening price of each candle
highnumber[]Highest price of each candle
lownumber[]Lowest price of each candle
closenumber[]Closing price of each candle
volumenumber[]Volume of each candle
timenumber[]Unix timestamp (seconds) of each candle
barIndexnumber[]Index of each bar [0, 1, 2, ...]
barCountnumberTotal number of candles

Derived Sources DATA

VariableFormulaDescription
hlc3(H+L+C)/3Typical price
hl2(H+L)/2Median price
ohlc4(O+H+L+C)/4Average price

Math Operations FUNCTION

Array-aware math operations. All accept arrays or numbers:

FunctionExampleDescription
add(a, b)add(close, val(10))Element-wise addition
sub(a, b)sub(high, low)Element-wise subtraction
mult(a, b)mult(close, 1.01)Element-wise multiplication
div(a, b)div(close, open)Element-wise division (safe, returns null on /0)
abs(a)abs(sub(close, open))Absolute value
max(a, b)max(open, close)Element-wise maximum
min(a, b)min(open, close)Element-wise minimum
neg(a)neg(close)Negate each element
val(n)val(100)Create constant array of value n
offset(a, n)offset(close, 1)Shift array by n bars (previous values)

The math object provides standard functions: math.abs, math.sqrt, math.pow, math.log, math.round, math.floor, math.ceil, math.PI, math.sin, math.cos.

Built-in Indicators FUNCTION

FunctionParametersReturns
sma(src, len)src: number[], len: intSimple Moving Average
ema(src, len)src: number[], len: intExponential Moving Average
rsi(src, len)src: number[], len: intRelative Strength Index (0-100)
stoch(src, h, l, len)src, high, low arrays + periodStochastic oscillator (0-100)
atr(len)len: intAverage True Range
stdev(src, len)src: number[], len: intStandard deviation
highest(src, len)src: number[], len: intHighest value over period
lowest(src, len)src: number[], len: intLowest value over period
// Bollinger Bands example
var len = 20;
var ma = sma(close, len);
var dev = stdev(close, len);
var upper = add(ma, mult(dev, 2));
var lower = sub(ma, mult(dev, 2));
plot(ma, "MA", "#2962ff", 2);
plot(upper, "Upper", "#26a69a", 1);
plot(lower, "Lower", "#ef5350", 1);
fill(upper, lower, "rgba(41,98,255,0.08)");

Signal Detection FUNCTION

FunctionReturnsDescription
crossover(a, b)boolean[]True when a crosses above b
crossunder(a, b)boolean[]True when a crosses below b
var fast = ema(close, 9);
var slow = ema(close, 21);
var buySignal = crossover(fast, slow);
var sellSignal = crossunder(fast, slow);
arrow(buySignal, "up", "#26a69a");
arrow(sellSignal, "down", "#ef5350");

plot() DRAWING

Draw a line series on the chart.

plot(data, name, color, width, style)
ParamTypeDefaultDescription
datanumber[]requiredArray of values to plot
namestring"Plot"Legend label
colorstring"#2962ff"Line color (hex or rgba)
widthnumber1.5Line width in pixels
stylestring"line""line", "dashed", or "dotted"

plotShape() DRAWING

Draw shapes at specific bars based on a condition.

plotShape(condition, name, shape, location, color, size)
ParamTypeOptions
conditionboolean[]Array of true/false per bar
shapestring"circle", "triangleup", "triangledown", "cross"
locationstring"abovebar" or "belowbar"

hline() DRAWING

Draw a horizontal line at a fixed price level.

hline(70, "Overbought", "#ef5350", 1, "dashed");
hline(30, "Oversold", "#26a69a", 1, "dashed");

fill() DRAWING

Fill the area between two data series.

fill(upperBand, lowerBand, "rgba(41,98,255,0.08)");

bgcolor() DRAWING

Color the background of specific bars based on a condition.

var bullish = crossover(ema(close,9), ema(close,21));
bgcolor(bullish, "rgba(38,166,154,0.15)");

arrow() DRAWING

Shortcut for directional arrows (buy/sell signals).

arrow(buyCondition, "up", "#26a69a");   // green up arrow below bar
arrow(sellCondition, "down", "#ef5350"); // red down arrow above bar

label() DRAWING

Place a text label at a specific bar index.

label(barCount - 1, "Latest", "#ff9800", "above");

User Inputs FUNCTION

Inputs create configurable parameters. Values are returned immediately and can be saved per-script.

FunctionReturnsExample
input.number(name, default, min, max)numberinput.number("Length", 14, 1, 200)
input.color(name, default)stringinput.color("Color", "#2962ff")
input.bool(name, default)booleaninput.bool("Show Labels", true)
input.select(name, options, default)stringinput.select("Source", ["close","open","hlc3"], "close")
input.string(name, default)stringinput.string("Label", "My Label")
var len = input.number("Length", 14, 1, 200);
var col = input.color("Line Color", "#2962ff");
var show = input.bool("Show Fill", true);
var src = input.select("Source", ["close","hlc3","hl2"], "close");

// Use the source
var srcData = src === "hlc3" ? hlc3 : src === "hl2" ? hl2 : close;
var ma = sma(srcData, len);
plot(ma, "MA", col, 2);

widget.button() WIDGET

Create a fixed-position button anywhere on screen.

var btn = widget.button("Click Me", {
    top: 12, right: 200,
    bg: "#2962ff", color: "#fff",
    fontSize: 13, radius: 8,
    padding: "8px 16px",
    shadow: "0 4px 12px rgba(41,98,255,0.3)"
});
btn.onClick(function() {
    myPanel.toggle();
});
OptionTypeDescription
top, right, left, bottomnumber (px)Fixed position on screen
bgstringBackground color
colorstringText color
fontSizenumberFont size in px
radiusnumberBorder radius in px
paddingstringCSS padding value
shadowstringCSS box-shadow value

widget.toolButton() WIDGET NEW

Add a button to the left toolbox — looks like a native tool button.

var btn = widget.toolButton("\uD83D\uDCC5", "My Tool", {
    position: "bottom"  // "top" or "bottom"
});
btn.onClick(function() {
    myPanel.toggle();
});

The first argument is an emoji or text icon. The second is the tooltip text.

widget.topBarButton() WIDGET NEW

Add a button to the top navigation bar.

var btn = widget.topBarButton("My Tool", {
    color: "#ff9800",
    icon: "\u26A1"
});
btn.onClick(function() { /* ... */ });

widget.panel() WIDGET

Create an information panel with rows of data.

var panel = widget.panel("Market Data", {
    top: 60, right: 20, width: 300,
    bg: "#11171f", border: "#2962ff", color: "#d1d4dc"
});
panel.addSection("Prices");
panel.addRow("BTC", "$87,424", "#26a69a");
panel.addRow("ETH", "$1,987", "#ef5350");
panel.addText("Updated every 5 seconds", "#787b86");
panel.show();
MethodDescription
.addRow(label, value, color)Add a key-value row
.addSection(text)Add a section header
.addText(text, color)Add a text paragraph
.show()Make panel visible
.hide()Hide panel
.toggle()Toggle visibility

widget.table() WIDGET

Create a data table with headers and rows.

var tbl = widget.table("Watchlist", ["Symbol","Price","Change"], {
    top: 60, right: 20, width: 400
});
tbl.addRow(["BTC", {text:"$87,424",color:"#26a69a",bold:true}, "+2.3%"]);
tbl.addRow(["ETH", {text:"$1,987",color:"#ef5350",bold:true}, "-1.1%"]);

Layout CSS

Layout scripts accept standard CSS. Select Layout type in the Creator.

.top-bar {
    background: linear-gradient(90deg, #0a0e17, #1a1e2e);
    border-bottom: 2px solid #2962ff;
}
.logo {
    color: #ff9800;
    text-shadow: 0 0 10px rgba(255,152,0,0.3);
}
.toolbox {
    background: #0d1117;
}
.tool-btn:hover {
    background: rgba(41,98,255,0.2);
}

Hybrid Scripts

Use /* CSS */ blocks or // @css prefix to mix CSS with indicator code:

// @css .top-bar { background: #1a1e2e; }
// @css .logo { color: #26a69a; }

var ma = sma(close, 20);
plot(ma, "Trend", "#26a69a", 2);

Or use block markers:

/* CSS */
.panel-btn.active { background: #ff9800; color: #000; }
/* END CSS */

var r = rsi(close, 14);
plot(r, "RSI", "#ab47bc");

CSS Classes Reference

ClassElement
.top-barTop navigation bar
.logoPACTION logo text
.btnTop bar buttons (Search, Alerts, etc.)
.toolboxLeft vertical toolbar
.tool-btnIndividual tool buttons
.tool-sepToolbar separator line
.chart-panelChart container panel
.panel-headerChart panel header (symbol, TFs)
.panel-btnTimeframe buttons
.panel-btn.activeCurrently selected timeframe
.sym-btnSymbol selector button
.chart-gridGrid containing all chart panels
.ind-itemIndicator list items
.ind-dotIndicator color dot
bodyPage body

Blocked CSS: @import, expression(), javascript: URLs, -moz-binding, external url() are stripped for security. data:image URLs are allowed.

Example: EMA Crossover Strategy INDICATOR

var fast = input.number("Fast", 9, 2, 50);
var slow = input.number("Slow", 21, 5, 200);
var bullCol = input.color("Bull", "#26a69a");
var bearCol = input.color("Bear", "#ef5350");

var emaF = ema(close, fast);
var emaS = ema(close, slow);

plot(emaF, "Fast EMA", bullCol, 2);
plot(emaS, "Slow EMA", bearCol, 2);
fill(emaF, emaS, "rgba(41,98,255,0.06)");

var buy = crossover(emaF, emaS);
var sell = crossunder(emaF, emaS);
arrow(buy, "up", bullCol);
arrow(sell, "down", bearCol);
bgcolor(buy, "rgba(38,166,154,0.1)");
bgcolor(sell, "rgba(239,83,80,0.1)");

Example: Bollinger Bands INDICATOR

var len = input.number("Length", 20, 5, 100);
var m = input.number("Multiplier", 2, 0.5, 5);

var basis = sma(close, len);
var dev = stdev(close, len);
var upper = add(basis, mult(dev, m));
var lower = sub(basis, mult(dev, m));

plot(basis, "Basis", "#ff9800", 1);
plot(upper, "Upper", "#2962ff", 1, "dashed");
plot(lower, "Lower", "#2962ff", 1, "dashed");
fill(upper, lower, "rgba(41,98,255,0.06)");

Example: RSI Oscillator Panel INDICATOR

oscPanel(120);

var len = input.number("RSI Length", 14, 2, 50);
var r = rsi(close, len);
plot(r, "RSI", "#ab47bc", 2);
hline(70, "Overbought", "#ef5350", 1, "dashed");
hline(30, "Oversold", "#26a69a", 1, "dashed");
hline(50, "Mid", "#787b86", 0.5, "dotted");

Note: oscPanel(120) reserves a 120px panel below the chart, similar to the built-in Cyborg indicator.

Example: Candle Coloring INDICATOR

// Highlight high-volume candles
var avgVol = sma(volume, 20);
var highVol = [];
for (var i = 0; i < barCount; i++) {
    highVol.push(volume[i] > avgVol[i] * 2);
}
bgcolor(highVol, "rgba(255,152,0,0.12)");

// Mark hammer candles
var hammers = [];
for (var i = 0; i < barCount; i++) {
    var body = math.abs(close[i] - open[i]);
    var range = high[i] - low[i];
    var lowerWick = math.min(open[i], close[i]) - low[i];
    hammers.push(range > 0 && body / range < 0.3 && lowerWick / range > 0.6);
}
plotShape(hammers, "Hammer", "triangleup", "belowbar", "#26a69a", 6);

Example: Info Dashboard WIDGET

var panel = widget.panel("\uD83D\uDCCA Market Info", {
    top: 60, right: 20, width: 280,
    bg: "#11171f", border: "#2962ff"
});

var lastClose = close[barCount - 1];
var prevClose = close[barCount - 2];
var change = ((lastClose - prevClose) / prevClose * 100);
var changeStr = (change >= 0 ? "+" : "") + change.toFixed(2) + "%";
var changeCol = change >= 0 ? "#26a69a" : "#ef5350";

panel.addRow("Price", lastClose.toFixed(2), "#d1d4dc");
panel.addRow("Change", changeStr, changeCol);
panel.addRow("High", math.max(high[barCount-1], high[barCount-2]).toFixed(2), "#26a69a");
panel.addRow("Low", math.min(low[barCount-1], low[barCount-2]).toFixed(2), "#ef5350");
panel.addRow("Volume", volume[barCount-1].toFixed(0), "#787b86");
panel.show();

Example: Economic Calendar WIDGET

var ecoPanel = widget.panel("\uD83D\uDCCA Economic Calendar", {
    top: 200, left: 72, width: 340,
    bg: "#11171f", border: "#00ff9d", color: "#e0e7ff"
});

ecoPanel.addSection("Today's Events");
ecoPanel.addRow("NFP", "245K", "#ff4d4d");
ecoPanel.addRow("CPI YoY", "2.8%", "#00ff9d");
ecoPanel.addRow("Unemployment", "4.1%", "#ffffff");
ecoPanel.addRow("Fed Rate", "4.25%", "#ff4d4d");
ecoPanel.addRow("GDP QoQ", "2.3%", "#00ff9d");
ecoPanel.addSection("Info");
ecoPanel.addText("Click button again to close");

var btn = widget.toolButton("\uD83D\uDCC5", "Economic Calendar");
btn.onClick(function() {
    ecoPanel.toggle();
});

Example: Matrix Theme LAYOUT

body {
    background: #000;
    color: #00ff41;
}
.top-bar {
    background: #000;
    border-bottom: 1px solid #00ff41;
    box-shadow: 0 0 10px #00ff41;
}
.logo {
    color: #00ff41;
    text-shadow: 0 0 5px #00ff41, 0 0 15px #00ff41;
}
.toolbox {
    background: #000;
    border: 1px solid #00ff41;
}
.tool-btn:hover {
    background: #00ff41;
    color: #000;
}
.panel-btn.active {
    background: #00ff41;
    color: #000;
}
input, select {
    background: #000;
    color: #00ff41;
    border: 1px solid #00ff41;
}

PactionScript v1.0 — Back to Paction

Last updated: March 2026