Helper Functions (q.*)
QSL provides 12 global helper functions in the q namespace for common trading logic.
Quick Reference
| Function | Signature | Returns |
|---|---|---|
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
- onBar() — Where helpers are used
- Indicators — Declaring indicators