DocsStrategy APIhelpers

Helper Functions (q.*)

QSL provides 12 global helper functions in the q namespace for common trading logic.

Quick Reference

FunctionSignatureReturns
q.crossOver(seriesA, seriesB, i)boolean
q.crossUnder(seriesA, seriesB, i)boolean
q.highest(series, period, i)number
q.lowest(series, period, i)number
q.sma(series, period, i)number
q.isNaN(value)boolean
q.clamp(value, min, max)number
q.between(value, min, max)boolean
q.pctChange(a, b)number
q.sign(value)-1 | 0 | 1
q.abs(value)number
q.round(value, decimals)number

Crossover Detection

q.crossOver(seriesA, seriesB, i)

Returns true when seriesA crosses above seriesB at bar i. Compares current and previous bar values to detect the crossing.

if (q.crossOver(ctx.ind.fastEma, ctx.ind.slowEma, i)) { ctx.order.market('ASSET', 1, { signal: 'buy' }); }

q.crossUnder(seriesA, seriesB, i)

Returns true when seriesA crosses below seriesB at bar i.

if (q.crossUnder(ctx.ind.fastEma, ctx.ind.slowEma, i)) { ctx.order.close('ASSET', { signal: 'sell' }); }

Lookback Functions

q.highest(series, period, i)

Returns the highest value in the last period bars ending at i.

const highest20 = q.highest(ctx.series.high, 20, i);

q.lowest(series, period, i)

Returns the lowest value in the last period bars ending at i.

const lowest20 = q.lowest(ctx.series.low, 20, i);

q.sma(series, period, i)

Returns the simple moving average over the last period bars ending at i. Useful for quick calculations inside onBar without declaring a full indicator.

const avgVolume = q.sma(ctx.series.volume, 20, i); if (ctx.series.volume[i] > avgVolume * 2) { // Volume spike detected }

Validation

q.isNaN(value)

Returns true if the value is NaN. Essential for checking whether an indicator has enough bars to produce valid output.

if (q.isNaN(ctx.ind.ema[i])) return; // Indicator not ready

Math Utilities

q.clamp(value, min, max)

Constrains a value within the range [min, max].

const size = q.clamp(rawSize, 0.1, 5); // Limit position size

q.between(value, min, max)

Returns true if value is within [min, max] (inclusive).

if (q.between(ctx.ind.rsi[i], 30, 70)) { // RSI in neutral zone }

q.pctChange(a, b)

Returns the percentage change from b to a as a decimal (e.g., 0.05 for 5%).

const change = q.pctChange(ctx.series.close[i], ctx.series.close[i - 1]); if (change > 0.03) { // Price jumped more than 3% }

q.sign(value)

Returns -1, 0, or 1 indicating the sign of the value.

const direction = q.sign(ctx.ind.macd.histogram[i]);

q.abs(value)

Returns the absolute value.

const spread = q.abs(ctx.ind.fastEma[i] - ctx.ind.slowEma[i]);

q.round(value, decimals)

Rounds a value to the specified number of decimal places.

const price = q.round(ctx.series.close[i] * 1.02, 2); // 2% above, 2 decimals

Example: Using Multiple Helpers

function onBar(ctx, i) { const ema = ctx.ind.ema; const close = ctx.series.close; // Skip if indicator not ready if (q.isNaN(ema[i])) return; // Trend filter: price above 20-bar high const above20High = close[i] > q.highest(close, 20, i - 1); // Volume filter: above-average volume const highVolume = ctx.series.volume[i] > q.sma(ctx.series.volume, 20, i); // Entry on bullish crossover with filters if (above20High && highVolume && ctx.position('ASSET').qty === 0) { if (q.crossOver(ctx.ind.fastEma, ctx.ind.slowEma, i)) { const size = q.clamp(1, 0.5, 3); ctx.order.market('ASSET', size, { signal: 'buy' }); } } // Exit on bearish crossover if (q.crossUnder(ctx.ind.fastEma, ctx.ind.slowEma, i)) { ctx.order.close('ASSET', { signal: 'sell' }); } }

Related

Related

helpersqcrossoverutilitiesqsl