Code Blocks
Write custom logic in JavaScript code blocks.
Code blocks let you write custom JavaScript logic when expressions aren't enough.
When to Use Code Blocks
Use code blocks for:
- Complex calculations that don't fit expression syntax
- Looping over arrays with custom logic
- External API calls (with permissions)
- Multi-step algorithms
- Custom indicators not in the library
Prefer expressions when possible — they're faster and easier to debug.
Creating a Code Block
- Click Add Analysis in the workflow editor
- Select Code Block
- Write your JavaScript code
- Define outputs
Code Format
Code blocks use arrow function syntax with destructured context parameters:
({ data, analysis, utils, barCount, timestamps }) => {
// Your analysis logic here
return { output1: [...], output2: [...] };
}Return a flat object with named outputs. The type is automatically inferred:
- Arrays of numbers become values
- Arrays of booleans become signals
Context Parameters
| Parameter | Type | Description |
|---|---|---|
data | Map<string, DataSeries> | Access OHLCV market data via data.get('alias') |
analysis | Object | Results from prior analysis blocks (value blocks, computed values) |
utils | Object | Helper functions and indicators via utils.indicators |
barCount | number | Total number of bars in dataset |
timestamps | number[] | Array of timestamps for all bars (ms since epoch) |
secrets | Record<string, string> | API keys (when secrets configured) |
Data Access
Access market data using the alias defined in your strategy:
({ data, utils }) => {
// Get market data by alias
const btc = data.get('btc');
if (!btc) return {};
// Destructure OHLCV arrays from .values
const { close, high, low, volume } = btc.values;
// Now use the arrays with indicators
const { RSI, SMA } = utils.indicators;
const rsi = RSI(close, 14);
const sma = SMA(close, 20);
const oversold = rsi.map(r => r < 30);
return { rsi, sma, oversold };
}Return Format
Return a flat object with named outputs:
return {
rsi: rsi, // Array of numbers → becomes a "value"
sma_diff: diff, // Array of numbers → becomes a "value"
buy: crossAbove, // Array of booleans → becomes a "signal"
oversold: isOversold // Array of booleans → becomes a "signal"
};The type is automatically inferred from the data — arrays of booleans become signals, arrays of numbers become values.
Examples
Custom RSI with Smoothing
({ data, analysis, utils }) => {
const btc = data.get('btc');
if (!btc) return {};
const { close } = btc.values;
// Get values from prior analysis blocks (e.g., value blocks)
const period = analysis.rsi_period ?? 14;
// Calculate indicators
const { RSI, SMA } = utils.indicators;
const rsi = RSI(close, period);
const smoothRsi = SMA(rsi, 3);
const oversold = rsi.map(r => r < 30);
return { rsi, smoothRsi, oversold };
}Volume Spike Detection
({ data, utils }) => {
const btc = data.get('btc');
if (!btc) return {};
const { volume } = btc.values;
const { SMA } = utils.indicators;
const avgVolume = SMA(volume, 20);
const spike = volume.map((v, i) => {
if (i < 20) return false;
return v > avgVolume[i] * 2;
});
const ratio = volume.map((v, i) => {
if (i < 20 || avgVolume[i] === 0) return 0;
return v / avgVolume[i];
});
return { ratio, spike };
}Combining Multiple Analysis Blocks
({ data, analysis, utils }) => {
// Reference outputs from other analysis blocks
const trend = analysis.trendSignals.bullish; // boolean[]
const rsiValue = analysis.indicators.rsi; // number[]
// Combine signals
const entry = trend.map((t, i) => t && rsiValue[i] < 40);
return { entry };
}Available Indicators
Code blocks include 40 built-in indicators via utils.indicators. For the full 100+ indicators, use expression-based analysis blocks instead.
You can destructure the indicators you need:
const { RSI, SMA, MACD, BOLLINGER } = utils.indicators;Trend Indicators (6)
| Function | Description |
|---|---|
SMA(source, period) | Simple Moving Average |
EMA(source, period) | Exponential Moving Average |
WMA(source, period) | Weighted Moving Average |
DEMA(source, period) | Double EMA |
TEMA(source, period) | Triple EMA |
HMA(source, period) | Hull Moving Average |
Momentum Indicators (9)
| Function | Description |
|---|---|
RSI(source, period) | Relative Strength Index |
MACD(source, fast, slow, signal) | Returns { line, signal, histogram } |
STOCH(high, low, close, k, kSmooth, dSmooth) | Returns { k, d } |
CCI(high, low, close, period) | Commodity Channel Index |
WILLR(high, low, close, period) | Williams %R |
ADX(high, low, close, period) | Average Directional Index |
ROC(source, period) | Rate of Change |
MOM(source, period) | Momentum |
AROON(high, low, period) | Returns { aroonup, aroondown } |
Volatility Indicators (4)
| Function | Description |
|---|---|
ATR(high, low, close, period) | Average True Range |
BOLLINGER(source, period, stddev) | Returns { upper, middle, lower } |
KELTNER(high, low, close, period, mult) | Returns { upper, middle, lower } |
DONCHIAN(high, low, period) | Returns { upper, middle, lower } |
Math & Stats (10)
| Function | Description |
|---|---|
STDEV(source, period) | Standard Deviation |
HIGHEST(source, period) | Highest value in period |
LOWEST(source, period) | Lowest value in period |
SUM(source, period) | Sum over period |
MAX(a, b) | Element-wise maximum |
MIN(a, b) | Element-wise minimum |
CHANGE(source, period) | Value change from n bars ago |
PCT_CHANGE(source, period) | Percent change from n bars ago |
CROSS_ABOVE(a, b) | True when a crosses above b |
CROSS_BELOW(a, b) | True when a crosses below b |
Network Access (Auto-Detected)
Just use fetch() in your code — domains are automatically detected!
async ({ data, secrets }) => {
// Domain (api.example.com) is auto-detected from the URL
const response = await fetch('https://api.example.com/data');
const externalData = await response.json();
// ...process data...
return { values: { metric: externalData.values }, signals: {} };
}Network-enabled code blocks cannot be backtested.
Secrets (Auto-Detected)
Just use secrets.KEY_NAME in your code — keys are automatically detected!
async ({ data, secrets }) => {
// Secret key (API_KEY) is auto-detected from usage
const apiKey = secrets.API_KEY;
const response = await fetch('https://api.example.com/data', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
// ...
}Configure actual secret values in Settings → Credentials.
Best Practices
- Keep it simple — Complex code is hard to debug
- Handle edge cases — Check for empty arrays and division by zero
- Return consistent arrays — All outputs must have the same length
- Use built-in indicators — They're optimized and tested
- Document your logic — Add comments explaining the algorithm
- Test thoroughly — Backtest extensively before live trading
Limitations
- Memory limit — 128MB (Paper), 256MB (Trader), 1GB (Pro)
- Execution time — Code must complete within timeout
- No persistent state — Each bar evaluation is independent
- Network access — Auto-detected and displayed for transparency (shown on marketplace listings)
Debugging
- Use the backtest console to see errors
- Return intermediate values as outputs to inspect them
- Start with simple logic and add complexity gradually
- Test with small date ranges first