AQR Docs
AQR (Arkenwell Quant Runtime) is the TypeScript scripting language of Quant Studio, the editor built into the Arkenwell terminal's chart window. Write an indicator, press Ctrl+Enter, and it is computed from the bars of the symbol and timeframe on your chart and drawn there. The same script can be armed as a server-side alert that keeps evaluating with your browser closed.
Coming from…
AQR uses TypeScript — a typed superset of JavaScript. If you already know Pine Script, Python, or any C-style language, you can start writing indicators in minutes. Below is what you need to know based on your background.
Coming from Pine Script (TradingView)
The concepts are the same; the syntax is TypeScript. Here is the direct mapping:
//@version=5
indicator("My Indicator")var period = input.int(14, title="Period")myLine = ta.sma(close, period)plot(myLine, color=color.blue)close, open, high, low, volumeta.ema(src, len)
ta.rsi(src, len)
ta.macd(src, f, s, sig)
ta.bb(src, len, mult)// Runs on every bar — implicit loopdefineIndicator({ name: "My Indicator" })period: input.number({ default: 14, label: "Period" })const myLine = ak.technical.sma(ctx.candles, period)return { series: { line: { type:"line", color:"#60A5FA", data:myLine } } }ctx.candles[i].close / open / high / low / volumeak.technical.ema(candles, len)
ak.technical.rsi(candles, len)
ak.technical.macd(candles, f, s, sig)
ak.technical.bollingerBands(candles, len, mult)// Receives full OHLCV array — you map/filter itPine Script → AQR: worked example
//@version=5
indicator("EMA Cross", overlay=true)
fast_len = input.int(9, title="Fast")
slow_len = input.int(21, title="Slow")
fast = ta.ema(close, fast_len)
slow = ta.ema(close, slow_len)
plot(fast, color=color.green, title="Fast EMA")
plot(slow, color=color.orange, title="Slow EMA")import { defineIndicator, input, ak } from "@arkenwell/quant";
export default defineIndicator({
name: "EMA Cross",
inputs: {
fast: input.number({ default: 9, label: "Fast" }),
slow: input.number({ default: 21, label: "Slow" }),
},
calculate(ctx) {
const fast = ak.technical.ema(ctx.candles, ctx.inputs.fast as number);
const slow = ak.technical.ema(ctx.candles, ctx.inputs.slow as number);
return { series: {
fast: { type:"line", color:"#34D399", title:"Fast EMA", data:fast },
slow: { type:"line", color:"#F59E0B", title:"Slow EMA", data:slow },
} };
},
});Coming from Python / pandas-ta / TA-Lib
If you write backtests or indicators in Python, the model is very similar — you receive a DataFrame (here an array), call indicator functions, and return the output series.
df["sma20"] = ta.sma(df["close"], length=20)df["rsi"] = ta.rsi(df["close"], length=14)macd = ta.macd(df["close"])
# returns DataFrame with MACD_12_26_9, ...# close prices
df["close"].valuesak.technical.sma(ctx.candles, 20)ak.technical.rsi(ctx.candles, 14)const m = ak.technical.macd(ctx.candles)
// m.macd, m.signal, m.histogram// close prices
ctx.candles.map(c => c.close)TimeSeries — an array of {time, value} objects, not a raw number array.
This is because each value must be timestamped to align with the chart. Use .map(v => v.value) if you need raw numbers for intermediate calculations.
New to quant indicators? (general programmer)
If you know JavaScript, TypeScript, Java, C#, Go, or any C-style language, you already know the syntax. The only new concept is TimeSeries: every indicator function returns an array of timestamped values that the chart engine knows how to draw.
| Concept | What it means | In AQR |
|---|---|---|
| Candle / Bar | One time period of price data | { time, open, high, low, close, volume } |
| TimeSeries | Array of (timestamp, value) pairs — what the chart plots | Array<{ time, value }> |
| Indicator | A function that transforms candle data into a TimeSeries | ak.technical.sma(candles, 20) |
| Overlay | Indicator drawn on top of the price chart | All AQR series are currently overlays |
| Lookback | How many bars back an indicator needs to produce its first value | Handled automatically — output array starts where data is sufficient |
| Script input | A user-adjustable parameter (like a period length) | input.number({ default: 20, label: "Period" }) |
The mental model in one diagram
TypeScript syntax cheatsheet
If you're new to TypeScript, here are the three patterns you need for AQR:
// 1. Variables — use const (never var)
const period = 20;
const prices = ctx.candles.map(c => c.close); // arrow function
// 2. Type assertion — "trust me, this is a number"
ctx.inputs.period as number // needed because inputs are typed as unknown
// 3. Array methods — filter, map, reduce (same as JavaScript)
const closes = ctx.candles.map(c => ({ time: c.time, value: c.close }));
const above = closes.filter(v => v.value > 24000);
Quick Start
From zero to a running indicator in 4 steps:
Open the AQR editor
On the main chart page, click AQR in the bottom toolbar to expand the editor panel. You'll see a Monaco editor pre-loaded with a default SMA script.
Press ▶ Run
Click the ▶ Run button (or Ctrl+Enter). The default SMA indicator plots a blue line over the price chart immediately.
Edit the script
Change period: 20 to period: 50 and run again — the line updates. Modify the color, add more series, or swap in a different function like ak.technical.ema.
Save your script
Click Save in the toolbar, give it a name, and it persists across sessions. Open it anytime from SCRIPTS (your library).
Script Structure
Every AQR script follows this exact pattern:
import { defineIndicator, input, ak } from "@arkenwell/quant";
export default defineIndicator({
name: "My Indicator", // shown in the toolbar
inputs: { // optional — creates toolbar controls
period: input.number({ default: 20, min: 1, max: 500, label: "Period" }),
},
calculate(ctx) { // called once on every Run
const data = ak.technical.sma(ctx.candles, ctx.inputs.period as number);
return {
series: {
myLine: {
type: "line",
color: "#60A5FA",
title: "SMA",
data: data,
},
},
};
},
});
Three rules
- The file must have a
default exportusingdefineIndicator(...). calculate(ctx)must return an object with aserieskey.- Each series
dataarray must be aTimeSeries:Array<{time, value}>.
Workflow
| Action | How |
|---|---|
| Run script | ▶ Run or Ctrl+Enter. AUTO-RUN re-runs 1 s after you stop typing. |
| See problems | Mistakes are underlined as you type (before running). The status bar shows ✕ errors ! warnings — click it for the Problems list. |
| Autocorrect | Put the cursor on an underline and press Ctrl+., click Fix on a Problems row, or AUTOCORRECT ALL. Every fix is one Ctrl+Z away. |
| Tune values | PARAMS lists your declared inputs and the numbers/colours detected in your code. See Inputs & detected values. |
| Templates | TEMPLATES — SMA, EMA cross, RSI, MACD, Bollinger, VWAP, ATR channel and more; every template passes the editor's checks. |
| Save / load | Ctrl+S or SAVE (saved to your account, server-side); SCRIPTS opens your library. |
| Put it on the chart | ADD TO CHART after a clean run. It then appears in the chart legend like any indicator. |
| Resize / focus | Drag the handle above the editor; the maximise button opens a fullscreen studio (Esc exits). |
Where scripts run
| Surface | How to open | What it runs on |
|---|---|---|
| Terminal — chart window | Open any chart → QUANT STUDIO in the chart header | The chart's own bars (symbol + timeframe), in a Web Worker in your browser |
| Terminal — script alerts | Alerts → condition "Custom script", pick a saved script | The same script on the Arkenwell server (quant-runtime sidecar), with the inputs frozen at arm time |
Both runtimes load the same ak.technical implementation, so an alert computes exactly the value you see on
the chart. That is also why scripts must be deterministic: Math.random() or Date.now() would
make the chart and the alert disagree (the editor flags both, AQR-051).
Context Object — ctx
The calculate(ctx) function receives one argument: the execution context.
ctx.candles
An array of OHLCV bars for the current symbol and timeframe, newest bar last.
type CandleData = {
time: number | string; // Unix timestamp or YYYY-MM-DD
open: number;
high: number;
low: number;
close: number;
volume: number;
};
Example — read the last close price:
const last = ctx.candles[ctx.candles.length - 1].close;
ctx.inputs
A key-value map of your declared inputs at runtime — keys match what you defined in inputs: {...}.
// If you declared: period: input.number({ default: 20 })
const period = ctx.inputs.period as number; // → 20 (or whatever the user set)
ak.technical — Built-in Functions
Every function takes candles: CandleData[] first (usually ctx.candles). TimeSeries means
{ time, value }[], which can be passed straight to a series' data. Functions marked with an object return
(macd, bollingerBands, vwapBands, stochastic) must be unpacked —
data: ak.technical.macd(c).macd, not data: ak.technical.macd(c) (AQR-014 autocorrects this).
In the editor, hover any function name for this reference, and type ak.technical. for completion.
ak.technical.sma(candles, period)
Simple moving average of close over period bars. First value appears at bar period.
const r = ak.technical.sma(ctx.candles, 20);
ak.technical.ema(candles, period)
Exponential moving average of close, α = 2 / (period + 1), seeded with the SMA of the first period bars.
const r = ak.technical.ema(ctx.candles, 20);
ak.technical.rsi(candles, period = 14)
Wilder's Relative Strength Index. >70 is conventionally overbought, <30 oversold.
const r = ak.technical.rsi(ctx.candles, 14);
ak.technical.macd(candles, fast = 12, slow = 26, signal = 9)
MACD line = EMA(fast) − EMA(slow); signal = EMA(signal) of the MACD line; histogram = MACD − signal. Returns an OBJECT — pass .macd, .signal or .histogram as series data.
const r = ak.technical.macd(ctx.candles, 12, 26, 9);
ak.technical.bollingerBands(candles, period = 20, stdDev = 2)
SMA(period) ± stdDev × population standard deviation of close. basis is an alias of mid.
const r = ak.technical.bollingerBands(ctx.candles, 20, 2);
ak.technical.vwap(candles)
Cumulative volume-weighted average of typical price (H+L+C)/3 from the FIRST candle passed. Bars without volume add no weight. For bands use vwapBands().
const r = ak.technical.vwap(ctx.candles);
ak.technical.vwapBands(candles)
VWAP with ±1 volume-weighted standard deviation bands.
const r = ak.technical.vwapBands(ctx.candles);
ak.technical.atr(candles, period = 14)
Average True Range (Wilder smoothing) — volatility in price units.
const r = ak.technical.atr(ctx.candles, 14);
ak.technical.stddev(candles, period = 20)
Rolling population standard deviation of close.
const r = ak.technical.stddev(ctx.candles, 20);
ak.technical.superTrend(candles, period = 10, multiplier = 3)
ATR-based trailing stop. Each point carries trend ("UP"/"DOWN") and a matching color; map to { time, value } for a line, or keep color for per-point colouring.
const r = ak.technical.superTrend(ctx.candles, 10, 3);
ak.technical.stochastic(candles, kPeriod = 14, dPeriod = 3)
%K = position of close within the kPeriod high–low range (0–100); %D = SMA(dPeriod) of %K.
const r = ak.technical.stochastic(ctx.candles, 14, 3);
ak.technical.williamsR(candles, period = 14)
Williams %R: where close sits in the period range, scaled −100 (low) to 0 (high).
const r = ak.technical.williamsR(ctx.candles, 14);
ak.technical.cci(candles, period = 20)
Commodity Channel Index: (typical − SMA) / (0.015 × mean deviation).
const r = ak.technical.cci(ctx.candles, 20);
ak.technical.roc(candles, period = 10)
Rate of change: percent change of close versus period bars ago.
const r = ak.technical.roc(ctx.candles, 10);
ak.technical.pivotPoints(candles)
Classic floor pivots from the PREVIOUS bar (second-to-last). Returns plain numbers, not series; null with fewer than 2 bars.
const r = ak.technical.pivotPoints(ctx.candles);
ak.technical.heikinAshi(candles)
Heikin-Ashi transformed bars — feed them to any other function, e.g. ema(heikinAshi(ctx.candles), 20).
const r = ak.technical.heikinAshi(ctx.candles);
vwap() now returns the series itself (as documented); scripts that used vwap(c).vwap keep working and the editor offers
to simplify them (AQR-015). bollingerBands() now provides mid (the documented name) alongside basis.
vwapBands, superTrend, stochastic, williamsR, cci, roc,
pivotPoints and heikinAshi are available in scripts on the chart and in server alerts.
input — Toolbar Controls
Declare inputs inside defineIndicator({ inputs: {...} }) and they automatically appear as interactive controls in the toolbar. Users can change them and re-run without editing code.
input.number(config)
Creates a numeric input with an optional min/max/step.
period: input.number({
default: 20,
min: 1,
max: 500,
step: 1, // optional
label: "Period", // shown in toolbar
})
Read it: ctx.inputs.period as number
input.boolean(config)
Creates a checkbox toggle.
showSignal: input.boolean({
default: true,
label: "Show Signal",
})
Read it: ctx.inputs.showSignal as boolean
input.select(config)
Creates a dropdown with fixed options.
source: input.select({
options: ["close", "open", "high", "low"],
default: "close",
label: "Source",
})
Read it: ctx.inputs.source as string
Output — what calculate(ctx) returns
calculate(ctx) returns an object with any of these keys. Everything you return is drawn on the chart, and every part
except series styling and tables can drive an alert.
return {
series: { // lines / histograms / areas
fast: { type: "line", data: ema9, color: "#38BDF8", title: "EMA 9" },
osc: { type: "histogram", data: hist, pane: "new" }, // own pane below
},
levels: [{ price: 23500, color: "#EF4444", label: "Resistance" }],
zones: [{ priceTop: 23420, priceBottom: 23380, color: "#10B981", label: "Demand" }],
markers: [{ time: bar.time, position: "above", shape: "arrowDown", color: "#D48A1F", text: "Sell" }],
signals: [{ type: "bullish", strength: 0.8, reason: "EMA 9 above EMA 21" }],
};
series — one entry per plotted line
| Field | Values | Notes |
|---|---|---|
| type | "line" · "histogram" · "area" | Required. Anything else is AQR-040 (e.g. "lines" → "line"). |
| data | { time, value, color? }[] | Ascending by time. A per-point color colours that bar/segment. |
| pane | "overlay" (default) · "new" | "new" gives the series its own pane below price — use it for oscillators (RSI, MACD, %R). |
| paneGroup | any string | Series with the same group share one new pane (e.g. MACD line + signal). |
| color | #RRGGBB, #RRGGBBAA, rgba() | Invalid colours fall back to the default (AQR-042). |
| lineWidth | 1–4 | Line/area only. |
| lineStyle | 0 solid · 1 dotted · 2 dashed · 3 large dashed · 4 sparse dotted | Line/area only. |
| title | string | Axis label and legend name. |
levels · zones · markers
| Key | Drawn as | Alertable |
|---|---|---|
| levels | Dashed price line with an axis label | Yes — by label (keep labels stable between runs) |
| zones | Shaded band with dotted edges and a label chip | Yes — enter / exit, by label |
| markers | Shape above/below a bar: "circle", "arrowUp", "arrowDown" | Yes — a marker printed on the latest bar |
signals — the script's verdict
{ type: "bullish" | "bearish" | "neutral", strength: 0–1, reason }. Alerts read the last entry and fire when
the verdict flips (optionally above a minimum strength). "buy" / "sell" are not signal types — AQR-043 converts them.
Example — Simple Moving Average
The simplest possible overlay. One line, one input, overlays on price.
import { defineIndicator, input, ak } from "@arkenwell/quant";
export default defineIndicator({
name: "SMA",
inputs: {
period: input.number({ default: 20, min: 1, max: 500, label: "Period" }),
},
calculate(ctx) {
const sma = ak.technical.sma(ctx.candles, ctx.inputs.period as number);
return {
series: {
sma: { type: "line", color: "#60A5FA", title: "SMA", data: sma },
},
};
},
});
Example — EMA Crossover (9 / 21)
Two EMAs. Fast above slow = bullish. Fast below slow = bearish. Both inputs are adjustable from the toolbar.
import { defineIndicator, input, ak } from "@arkenwell/quant";
export default defineIndicator({
name: "EMA Cross",
inputs: {
fast: input.number({ default: 9, min: 2, max: 100, label: "Fast" }),
slow: input.number({ default: 21, min: 5, max: 200, label: "Slow" }),
},
calculate(ctx) {
const fast = ak.technical.ema(ctx.candles, ctx.inputs.fast as number);
const slow = ak.technical.ema(ctx.candles, ctx.inputs.slow as number);
return {
series: {
fast: { type: "line", color: "#34D399", title: "EMA Fast", data: fast },
slow: { type: "line", color: "#F59E0B", title: "EMA Slow", data: slow },
},
};
},
});
Example — RSI (14)
Momentum oscillator. Values appear overlaid on the price chart — look for the purple line near the bottom.
import { defineIndicator, input, ak } from "@arkenwell/quant";
export default defineIndicator({
name: "RSI",
inputs: {
period: input.number({ default: 14, min: 2, max: 100, label: "Period" }),
},
calculate(ctx) {
const rsi = ak.technical.rsi(ctx.candles, ctx.inputs.period as number);
return {
series: {
rsi: { type: "line", color: "#A78BFA", title: "RSI", data: rsi },
},
};
},
});
Example — MACD
Classic trend-following indicator. Three series: MACD line, signal line, histogram bars.
import { defineIndicator, ak } from "@arkenwell/quant";
export default defineIndicator({
name: "MACD",
calculate(ctx) {
const result = ak.technical.macd(ctx.candles, 12, 26, 9);
return {
series: {
macd: { type: "line", color: "#60A5FA", title: "MACD", data: result.macd },
signal: { type: "line", color: "#F59E0B", title: "Signal", data: result.signal },
histogram: { type: "histogram", color: "#10B981", title: "Histogram", data: result.histogram },
},
};
},
});
Example — Bollinger Bands
Three lines that form a price envelope. Price touching the outer bands can signal mean-reversion. Narrow bands = low volatility.
import { defineIndicator, input, ak } from "@arkenwell/quant";
export default defineIndicator({
name: "Bollinger Bands",
inputs: {
period: input.number({ default: 20, min: 5, max: 200, label: "Period" }),
stddev: input.number({ default: 2, min: 1, max: 5, label: "Std Dev" }),
},
calculate(ctx) {
const bb = ak.technical.bollingerBands(
ctx.candles,
ctx.inputs.period as number,
ctx.inputs.stddev as number
);
return {
series: {
upper: { type: "line", color: "#60A5FA", title: "BB Upper", data: bb.upper },
mid: { type: "line", color: "#94A3B8", title: "BB Mid", data: bb.mid },
lower: { type: "line", color: "#60A5FA", title: "BB Lower", data: bb.lower },
},
};
},
});
Example — VWAP
Volume-Weighted Average Price — used by professional traders as a fair-value benchmark.
import { defineIndicator, ak } from "@arkenwell/quant";
export default defineIndicator({
name: "VWAP",
calculate(ctx) {
const vwap = ak.technical.vwap(ctx.candles);
return {
series: {
vwap: { type: "line", color: "#F59E0B", title: "VWAP", data: vwap },
},
};
},
});
Example — Multiple Series on One Script
You can output as many series as you like from a single script — useful for composite indicators.
import { defineIndicator, input, ak } from "@arkenwell/quant";
// SMA ribbon — three SMAs at once
export default defineIndicator({
name: "SMA Ribbon",
calculate(ctx) {
const s20 = ak.technical.sma(ctx.candles, 20);
const s50 = ak.technical.sma(ctx.candles, 50);
const s200 = ak.technical.sma(ctx.candles, 200);
return {
series: {
sma20: { type: "line", color: "#34D399", title: "SMA 20", data: s20 },
sma50: { type: "line", color: "#60A5FA", title: "SMA 50", data: s50 },
sma200: { type: "line", color: "#F87171", title: "SMA 200", data: s200 },
},
};
},
});
Example — Custom Logic (Midpoint Line)
AQR gives you the full candle array. You can write any calculation you want — loop over bars, compute custom values, build your own indicator from scratch.
import { defineIndicator } from "@arkenwell/quant";
// Midpoint = (high + low) / 2 for each candle
export default defineIndicator({
name: "Midpoint",
calculate(ctx) {
const midpoint = ctx.candles.map(c => ({
time: c.time,
value: (c.high + c.low) / 2,
}));
return {
series: {
mid: { type: "line", color: "#A78BFA", title: "Midpoint", data: midpoint },
},
};
},
});
Another example — percentage distance of close from VWAP:
import { defineIndicator, ak } from "@arkenwell/quant";
export default defineIndicator({
name: "VWAP Deviation %",
calculate(ctx) {
const vwap = ak.technical.vwap(ctx.candles);
// Build a lookup map for VWAP values by time
const vwapMap = new Map(vwap.map(v => [v.time, v.value]));
const deviation = ctx.candles
.filter(c => vwapMap.has(c.time))
.map(c => ({
time: c.time,
value: +((c.close - vwapMap.get(c.time)!) / vwapMap.get(c.time)! * 100).toFixed(3),
}));
return {
series: {
dev: { type: "histogram", color: "#60A5FA", title: "VWAP Dev %", data: deviation },
},
};
},
});
Problems & Autocorrect
Quant Studio checks your script on every keystroke — before compiling or running — and underlines each mistake where it is. Hover an underline for the explanation. Where the intent is unambiguous, the check carries a fix:
- Ctrl+. on an underline → Autocorrect: …
- Fix on a row of the Problems list (click the row to jump to the code)
- AUTOCORRECT ALL — applies every fix, re-checking between fixes so they never collide; Ctrl+Z undoes it
Runtime errors (which have no line number) come with a hint naming the usual cause, e.g. reading .value of an
object-returning function.
| Code | What it catches | Autocorrect |
|---|---|---|
| AQR-001 | Import from anything other than @arkenwell/quant | Close misspellings → @arkenwell/quant |
| AQR-002 | defineIndicator / input / ak used but not imported | Adds them to the import (or adds the import) |
| AQR-003 | No defineIndicator({…}) | — |
| AQR-004 | Indicator not exported | Adds export default |
| AQR-005 / 006 | No calculate(ctx), or it never returns | — |
| AQR-010 | Unknown ak.technical function (typos, Pine/TA-Lib names like bbands, supertrend, stoch) | Nearest real function |
| AQR-011 / 012 / 013 | Period < 1, fractional period, missing period | Valid value |
| AQR-014 | Object result (macd, bollingerBands, …) passed as data | Picks .macd / .mid / .k / .vwap |
| AQR-015 | Legacy vwap(c).vwap / .upper1 / .lower1 | Simplifies / switches to vwapBands |
| AQR-020 / 021 / 022 | ctx.close, ctx.candle, ctx.candles.close | ctx.candles.map(c => c.close) / nearest ctx field |
| AQR-030 | ctx.inputs.x read but never declared (it would be undefined) | Nearest declared name, or declares x |
| AQR-031 / 032 | input.number without a default, or default outside min/max | Adds a default |
| AQR-040 … 045 | Invalid series type, pane, colour, signal type, marker shape or position | Nearest valid value |
| AQR-050 | Browser/network APIs that do not exist in the sandbox (fetch, window, timers, storage…) | — |
| AQR-051 | Non-deterministic code (Math.random, Date.now) | — |
Checks never look inside comments or strings, and every built-in template passes them.
Inputs & detected values
PARAMS shows two kinds of tunable value:
| Kind | Where it comes from | Editing it |
|---|---|---|
| Declared inputs | inputs: { period: input.number({ … }) } | Changes the value passed in ctx.inputs; your code is untouched. Saved with alerts at arm time. |
| Detected in code | Numbers you pass to ak.technical (e.g. the 9 in ema(c, 9)) and colour literals in your output | Rewrites exactly that literal in the code and re-runs |
→ INPUT on a detected number promotes it to a real input: the literal becomes ctx.inputs.emaPeriod as number and
emaPeriod: input.number({ default: 9, … }) is added to inputs.
On the chart
Once added, a script behaves like the built-in indicators:
- Legend (top-left of the chart): eye = hide without removing, gear or click the name = settings, × = remove.
- Click a plotted line to select that indicator and open its settings.
- Style tab: per-series colour (palette, hex, opacity), width and solid/dashed/dotted. These overrides are stored with your chart preferences and applied on top of the colours in your code; Defaults clears them.
- Inputs and logic stay in the script — Open in Quant Studio from the same card.
Built-in indicators (EMA, SMA, Bollinger, VWAP, SuperTrend, Pivots, RSI, MACD, Stochastic, CCI, ROC, %R) use the same card: an Inputs tab with every parameter (length, multiplier, RSI levels, MACD fast/slow/signal …) and a Style tab.
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
| Ctrl+Enter | Run the script |
| Ctrl+Z | Undo in editor |
| Ctrl+/ | Toggle line comment |
| Ctrl+D | Select next occurrence of word |
| Alt+↑ / ↓ | Move line up / down |
| Escape | Close Save / Help modal |
Keyboard shortcuts on the main chart (1–5 for timeframes, etc.) are blocked while your cursor is inside the AQR editor, so typing in the editor never accidentally switches the chart view.
Run-time Error Codes
These come from compiling or executing the script and appear in the console as
[ARK-008] Cannot read properties of undefined (reading 'value'), followed by a hint. Most are caught earlier by
the AQR checks.
| Code | Meaning | Usual fix |
|---|---|---|
| ARK-001 | Import not allowed | Only @arkenwell/quant can be imported (AQR-001) |
| ARK-002 | Compile failed (syntax) | The line & column are marked in the editor — usually a missing bracket or comma |
| ARK-003 | Execution timed out | A loop that never ends, or O(n²) work over all bars — see limits |
| ARK-004 | No default export | export default defineIndicator({…}) (AQR-004) |
| ARK-005 | calculate() did not return an object | Return { series: … } (AQR-006) |
| ARK-006 | No calculate function | Add calculate(ctx) { … } (AQR-005) |
| ARK-007 | Data unavailable | The chart has no bars loaded for this symbol/timeframe yet |
| ARK-008 | Runtime exception inside your code | Read the hint under the error |
| ARK-009 | Invalid input value | Check min/max/options of the input |
| ARK-010 | Output too large | More than 50 000 points in total — return fewer series or fewer bars |
Runtime Limits
| Limit | Value | Notes |
|---|---|---|
| Max execution time | 3 000 ms | Per Run — script is killed after this |
| Max output points | 50 000 | Total across all series |
| Max candles | Depends on timeframe | The chart sends all loaded candles |
| Network access | Blocked | Scripts run in a sandboxed Web Worker (chart) or node:vm (alerts) — no fetch |
| DOM access | Blocked | No document or window |
| Saved scripts | Unlimited | Saved to your Arkenwell account — accessible from any device, any browser |
Scripts are stored server-side in the Arkenwell database, tied to your user account. They persist across browsers, devices, and sessions — log in from anywhere and your scripts are there. You must be logged in to save; the editor will show an error in the console if the save fails.