New Functions: Conditionals, Rolling Sums, and RSI Building Blocks

January 30, 2026 Luis Gomez
New Functions: Conditionals, Rolling Sums, and RSI Building Blocks

We've added five new functions to StonQL. Rather than more indicators with fixed formulas, these are building blocks - primitives that let you construct scans the standard indicators can't express.

Conditionals with cond()

Most scanners force you to think in AND/OR logic: this condition AND that condition. But trading decisions often have an "it depends" quality. You want to apply different rules based on context.

The cond() function brings if-then-else logic to your expressions:

cond(condition, true_value, false_value)

Context-dependent filtering

Say you only care about a stock's price when it's trading on above-average volume. Low-volume moves are noise - you want to ignore them entirely.

x = cond(volume > sma(volume, 20), close, 0);
x > 50;

When volume is below average, x becomes zero and the stock gets filtered out. The close price only "counts" when volume confirms it.

Building scoring systems

You can nest multiple cond() calls to build simple scoring systems - assigning points based on various conditions, then filtering on the total score. Binary classification is the simplest version:

trend_strength = cond(close > ema(close, 50), 1, -1);
trend_strength > 0;

Rolling sums with sum()

Moving averages smooth data. But sometimes you want the raw accumulation over a window - how much total volume traded, how much price moved, how many up days occurred.

sum(field, period)

Comparing accumulation periods

Volume spikes matter, but so does sustained accumulation. This scan finds stocks where recent 5-day volume exceeds what you'd expect from the 20-day pattern:

sum(volume, 5) > sum(volume, 20) / 4;

You're comparing actual recent accumulation against the proportional slice of a longer window. A simple ratio that standard volume indicators don't capture directly.

Cumulative sums with cumsum()

While sum() gives you a rolling window, cumsum() accumulates from the start of available data. This is the building block behind On-Balance Volume - arguably the oldest accumulation indicator in technical analysis.

OBV works by signing each day's volume (positive on up days, negative on down days) and keeping a running total:

obv = cumsum(cond(close > close[1], volume, 0 - volume));

The idea: volume precedes price. If OBV is rising while price is flat, accumulation is happening under the surface. We exposed cumsum() so you can build OBV variants with different logic for how volume gets signed.

RSI building blocks: gain() and loss()

RSI is calculated by separately averaging gains and losses, then computing their ratio. Most platforms give you the final RSI number. We're exposing the components:

gain(field)  -- positive changes only (zeros for down days)
loss(field)  -- negative changes as positive values (zeros for up days)

Why would you want these? Standard RSI uses the same lookback period for both gains and losses. But gains and losses aren't symmetric in markets - momentum tends to behave differently in each direction. With the primitives exposed, you can apply different smoothing, weighting, or filtering to each side.

If you're happy with standard RSI, you don't need these. They're here for when you're not.

Putting it together

These functions are most interesting in combination. Here's a scan for stocks with volume-confirmed momentum - only counting gains that happen on above-average volume days:

vol_confirmed_gain = cond(volume > sma(volume, 20), gain(close), 0);
confirmed_momentum = sum(vol_confirmed_gain, 10);
confirmed_momentum > 5;

The logic: gains on low volume might be noise. Gains on high volume suggest real buying pressure. By combining cond(), gain(), and sum(), you can express "show me stocks that have gained at least $5 on high-volume days over the past two weeks."

Here's another: a filtered OBV that ignores low-volume days entirely.

signed_vol = cond(close > close[1], volume, 0 - volume);
significant_vol = cond(volume > sma(volume, 20), signed_vol, 0);
filtered_obv = cumsum(significant_vol);

Standard OBV treats a 100-share day the same as a 10-million-share day in terms of direction. This variant only accumulates when volume is above average - focusing on the days that actually matter. You won't find this indicator in your charting package, but now you can scan for it.

All five functions are available now. Check the StonQL reference documentation for the full syntax details.

Related Posts

Introducing StocksFast: A Stock Scanner for Technical Traders
Introducing StocksFast: A Stock Scanner for Technical Traders

A high-level introduction to StocksFast - motivation, what it is, what it does, and who it's for.

New Features: IPO Date Tracking & Synthetic Price Fields
New Features: IPO Date Tracking & Synthetic Price Fields

Find recent IPOs, filter stocks by listing age, and use new synthetic price fields like typical price (HLC3), OHLC4, and …

Undo/Redo Now Available in the Expression Editor
Undo/Redo Now Available in the Expression Editor

Edit your scan expressions with confidence. Made a mistake? Just undo it. Our expression editor now features undo and redo …