AlgoHive
Reference

Expression Syntax

Complete guide to writing expressions in AlgoHive analysis blocks.

Expressions are the language you use to describe calculations and conditions inside your strategy.

They’re used in three places:

  • Analysis blocks: compute values/signals across the whole chart (time-series).
  • Entry rules (when): decide when to open a position (evaluated bar-by-bar).
  • Manage rules (when, dynamic levels): manage an open position (evaluated bar-by-bar).
📝Note

This is not Python. Some examples use a python code fence only for syntax highlighting — the language is AlgoHive’s expression grammar.

The mental model

Think of expressions as two layers:

  1. Analysis layer (precompute): indicators, crossovers, filters → outputs like signals.ma_cross_up
  2. Trading layer (decide): entry and manage rules that mostly reference those outputs
💡Tip

Put time-series math (indicators, lookbacks, crossovers) in Analysis. Keep Entry/Manage rules simple: reference signals/values, plus position helpers for manage.

Where expressions run (and what you can use)

WhereWhat it’s forRuns asWhat you should write
Analysis outputsbuild signals + metricstime-seriesindicators, comparisons, crossovers
Entry whenopen a positionper-bar booleansignals.ma_cross_up AND filters.ok
Manage rulesexit/stop/take-profit logicper-bar boolean/numberposition_pnl_r > 2 or signals.exit_now

References

Data sources (market data)

Access data source columns with dot notation:

btc.close
btc.high
btc.low
btc.open
btc.volume

For custom data sources, it’s the same pattern: alias.column (whatever columns your data source returns).

⚠️Warning

Always use the explicit prefix (btc.close, not just close). It prevents ambiguity when you have multiple data sources.

Analysis block outputs

Reference analysis outputs using:

  • block.output for multi-output blocks
  • block for single-output blocks (simple blocks)
signals.ma_cross_up
indicators.rsi
trend.fast_ma

daily_trend      # single-output block

Operators

Arithmetic

a + b
a - b
a * b
a / b

Comparisons (return boolean)

a > b
a < b
a >= b
a <= b
a == b
a != b

Boolean logic

a AND b
a OR b
NOT a

Functions

Indicators (analysis blocks)

Indicators are called like:

RSI(btc.close, 14)
EMA(btc.close, 20)
ATR(btc.high, btc.low, btc.close, 14)
MACD(btc.close, 12, 26, 9)

Some indicators return structs (multiple outputs):

MACD(btc.close, 12, 26, 9).line
MACD(btc.close, 12, 26, 9).signal
BOLLINGER(btc.close, 20, 2).upper
STOCH(btc.high, btc.low, btc.close, 14, 3, 3).k

Crossover helpers (analysis blocks)

These are implemented in the engine and are the correct way to express “crosses above/below”:

cross_above(fast_ma, slow_ma)
cross_below(rsi, 70)

Other helpers (analysis blocks)

rising(btc.close, 5)
falling(rsi, 3)

HIGHEST(btc.high, 20)
LOWEST(btc.low, 20)
STDEV(btc.close, 20)

CHANGE(btc.close, 1)
PCT_CHANGE(btc.close, 1)

IF(condition, thenValue, elseValue)

Time helpers (anywhere)

HOUR_UTC()
DAY_OF_WEEK()
IS_WEEKEND()
IS_DATE("2026-02-02")

Lookback (previous bars)

Use lookbacks in analysis outputs:

btc.close[1]
rsi[1]
range_high[5]
⚠️Warning

Avoid using [...] lookbacks directly in Entry conditions. Entry conditions run per-bar and are intended to reference analysis outputs (e.g. signals.cross_up). If you need “previous bar” logic for entry, compute it in analysis first.

Dynamic lookback (manage only)

Manage rules can do dynamic lookbacks for advanced workflows (example: “read the value at entry bar”):

my_block.stop_at_entry: my_block.stop_loss[entry_bar()]
📝Note

Dynamic lookbacks require an open position and are only available during Manage rule evaluation.

Constants you can tune (Value Blocks)

If you want adjustable constants (periods, thresholds), define them as Value Blocks and reference them by name:

fast_period: 9
slow_period: 21

fast_ma: EMA(btc.close, fast_period)
slow_ma: EMA(btc.close, slow_period)

This works because value blocks are treated as numeric constants where needed (for example indicator periods).

Practical recipes

1) MA crossover entry (recommended structure)

Analysis block outputs (signals):

fast_ma: EMA(btc.close, fast_period)
slow_ma: EMA(btc.close, slow_period)

ma_cross_up: cross_above(fast_ma, slow_ma)
ma_cross_down: cross_below(fast_ma, slow_ma)

Entry when:

signals.ma_cross_up

2) RSI threshold + “cross down” exit signal

Analysis outputs:

rsi: RSI(btc.close, 14)

oversold: rsi < 30
overbought: rsi > 70

rsi_cross_down: cross_below(rsi, 70)

Entry when:

signals.oversold

Manage rule when (exit):

signals.rsi_cross_down

3) Manage rule using position context

Use these only in Manage rules:

position_pnl_r > 2
bars_since_entry() > 24

What’s allowed where (quick reference)

FeatureAnalysisEntryManage
btc.close, alias.column
signals.foo, block.value
Indicators like RSI(...)⚠️ (not recommended)⚠️ (not recommended)
cross_above, cross_below⚠️ (not recommended)⚠️ (not recommended)
Lookback [1]❌ (compute in analysis)⚠️ (use sparingly)
Dynamic lookback [entry_bar()]
Position vars (position_pnl_r, entry_price, …)
Trade/portfolio vars (in_position, daily_trades, …)
Time funcs (HOUR_UTC(), IS_WEEKEND(), …)
💡Tip

If something feels “stateful” (entry price, PnL, time held), it belongs in Manage, not Analysis/Entry.

On this page