Type natural language or structured dot-notation to instantly build trading signal expressions.
Expressions follow the pattern: LEFT OPERATOR RIGHT with optional symbol and timeframe.
bullish engulfing > 0 // unary — candle / pattern flag (compare > 0) doji > 0 15min RSI(14) < 30 SMA(50) > SMA(200) Close > SMA(200) 5min
When an AI/model generates expression JSON or dot-notation from this page, use these rules first.
| Case | Canonical Output |
|---|---|
| Regular indicators: RSI, SMA, EMA, MACD, ATR, BBANDS, STOCH, ADX, SAR... | Use GTSIndicators for every regular indicator. |
| Extended/subsystem indicators: InertialRsi, AdaptiveCentricMa, AdaptiveMacdR2, BreakOfStructure... | Use ExtendedIndicators. |
Decimal parameters such as SAR(0.02,0.2) | Use function syntax. Do not encode decimal params in dot notation because dots are separators. |
| Single value on a multi-param extended indicator | The value fills the first configured parameter; remaining parameters use defaults. |
| Subfields with spaces | Prefer compact aliases in text/dot notation: Upper, Lower, SenkouA, AroonUp. JSON may use the runtime field name. |
| Subfield case | Use the documented convention in generated dot notation: regular GTSIndicators subfields are capitalized (.Signal, .SlowK); ExtendedIndicators outputs are lowercase (.signal, .value). |
| Candle patterns | Canonical natural order is trend pattern, e.g. bullish engulfing. Dot notation is TA_CandlePatterns.PATTERN.Trend.Frame. |
| Negative fixed values | Use natural/formula syntax: openpnl < -500. Dot notation may use FixedVal.-500 for integers. |
LEFT OPERATOR RIGHT (or a single unary operand). Arithmetic (+ - * /) is not supported by this parser — use an indicator, field, or saved expression directly, not a calculation. This limit is scoped to Quick Create only. GTS itself has arithmetic operators: the visual builder lists math (+ − × ÷) alongside compare and logic, and JSON Import accepts them in Expression_left_operation. To use arithmetic, build the calculation as its own expression there, save it, then reference it by name here. See the expression model.LEFT and RIGHT must each be a single numeric field, fixed value, indicator, pattern, account field, or date/time value.0 — use > 0 or != 0, never == 100..signal, .value); do not invent subfields.Bullish when unspecified; write bearish <pattern> or both <pattern> to change it.GTSIndicators (RSI, SMA, EMA, MACD, ATR, BBANDS, STOCH...). Extended Indicators use ExtendedIndicators: advanced / subsystem indicators (adaptive smoothers, SMC-ICT structure, ML/regime).Use NAME(param) or NAME(p1,p2,p3) syntax. Subfields use .SubField.
| Indicator | Syntax | Default Params | Subfields |
|---|---|---|---|
| RSI | RSI(14) | 14 | — |
| SMA | SMA(200) | 20 | — |
| EMA | EMA(50) | 30 | — |
| MACD | MACD(12,26,9) | 12, 26, 9 | Macd, Signal, Hist |
| BBANDS | BBANDS(20,2) | 20, 2 | Upper Band (Upper), Middle Band (Middle), Lower Band (Lower) |
| STOCH | STOCH(14,3,3) | 14, 3, 3 | SlowK, SlowD |
| ATR | ATR(14) | 14 | — |
| ADX | ADX(14) | 14 | — |
| CCI | CCI(20) | 14 | — |
| WILLR | WILLR(14) | 14 | — |
| SUPERTREND | SUPERTREND(10,3) | 10, 3 | Trend, Direction, Long, Short |
| ICHIMOKU | ICHIMOKU | — | Tenkan, Kijun, Senkou A (SenkouA), Senkou B (SenkouB), Chikou |
| AROON | AROON(25) | 25 | Aroon Up (AroonUp), Aroon Down (AroonDown) |
| SAR | SAR(0.02,0.2) | 0.02, 0.2 | — |
| MFI | MFI(14) | 14 | — |
| OBV | OBV | — | — |
MACD(12,26,9).Signal > 0 // MACD signal line BBANDS(20,2).Upper > Close // Bollinger upper band STOCH(14,3,3).SlowK < 20 // Stochastic %K ICHIMOKU.Tenkan > ICHIMOKU.Kijun // Ichimoku crossover
Append a timeframe token at the end of the expression. With no timeframe, the expression runs on whatever frame the alert/strategy is configured for. (Full token list in §10 Timeframes.)
RSI(14) < 30 5min // 5-minute RSI EMA(50) > EMA(200) 1h // hourly EMA cross SMA(200) > Close Daily // daily SMA MACD(12,26,9).Signal > 0 15min // 15-minute MACD signal ATR(14) > 5 4h // 4-hour ATR
Expressions are symbol-agnostic on Trading — the symbol is bound by the strategy/handler at deploy, not on the expression. Write just the condition (and an optional timeframe); don’t add on SYMBOL.
// WITHOUT a symbol — runs on the configured symbol(s) RSI(14) < 30 RSI(14) < 30 5min // WITH a symbol (typical for alerts) — name the market, then the frame RSI(14) < 30 5min Close > SMA(200) 1h
The same signal can be written in plain formula or in structured dot-notation.
| Notation | Example |
|---|---|
| Natural / formula | RSI(14) < 30 5min |
| Dot-notation | GTSIndicators.RSI.14.5Min < FixedVal.30 |
| Multi-param + frame | GTSIndicators.MACD.12.26.9.15Min.Signal > FixedVal.0 |
GTSIndicators.NAME.param(s).FRAME.subfield. See §11 Dot-Notation.| Category | Indicators |
|---|---|
| Overlap / Moving Avg | ALMA, DEMA, EMA, FWMA, HMA, JMA, KAMA, RMA, SMA, TEMA, TRIMA, VWAP, VWMA, WMA, ZLMA, T3, SUPERTREND, ... |
| Momentum | RSI, MACD, STOCH, STOCHRSI, CCI, MOM, ROC, WILLR, TSI, KDJ, QQE, SQUEEZE, TD_SEQ, AO, BOP, ... |
| Volatility | ATR, BBANDS, KC, DONCHIAN, NATR, TRUE_RANGE, RVI, SQUEEZE_PRO, ... |
| Volume | OBV, MFI, AD, ADOSC, CMF, EFI, KVO, NVI, PVI, VPT, ... |
| Trend | ADX, AROON, ICHIMOKU, SAR, VORTEX, CHOP, DPO, LINREG, CKS, ... |
| Statistics | STDDEV, VARIANCE, ZSCORE, SKEW, KURTOSIS, ENTROPY, MAD, ... |
| Smart Money (SMC) | BOS, CHOCH, FVG, ORDER_BLOCK, SWING_HIGH, SWING_LOW, MSB, ... |
Advanced / subsystem indicators (adaptive smoothers, SMC-ICT structure, ML / regime). Use NAME(param) syntax in natural/formula input; use ExtendedIndicators.NAME... in dot notation. Each indicator exposes named subfields such as .signal, .value, .trend, .upper — use only the subfields listed for that indicator.
| Indicator | Syntax | Default Params | Subfields |
|---|---|---|---|
| InertialRsi | InertialRsi(14).signal | 5, 21, 3, 9 | signal, rsi |
| AdaptiveCentricMa | AdaptiveCentricMa(200).value | 14, 2, 2, 0.01, 0.5 | value |
| AdaptiveEmaSlopeBand | AdaptiveEmaSlopeBand(50).trend | 20, 80, 2 | trend, upper, lower |
| AdaptiveMacdR2 | AdaptiveMacdR2(20,12,26,9).signal | 20, 12, 26, 9 | macd, signal, histogram |
| QuantileVolatilityBands | QuantileVolatilityBands(100,2).upper | 100, 2 | upper, mid, lower |
| InertialStochastic | InertialStochastic(5,34,3,3).signal | 5, 34, 3, 3 | signal, k, d |
| TrueRangePercentile | TrueRangePercentile.percentile | default | percentile |
| BreakOfStructure | BreakOfStructure(5,5).signal | 5, 5 | signal |
| ChangeOfCharacter | ChangeOfCharacter(5,5).signal | 5, 5 | signal |
| SuperTrendAiClustering | SuperTrendAiClustering.signal | default | trend, signal |
| KalmanTrendFilter | KalmanTrendFilter.trend | default | trend, value |
| VolumeZscoreImpulse | VolumeZscoreImpulse.signal | default | signal, zscore |
AdaptiveMacdR2(20,12,26,9).signal > 0 // native MACD-style signal QuantileVolatilityBands(100,2).upper > Close // upper volatility band InertialStochastic(5,34,3,3).signal < 20 // native stochastic-style signal BreakOfStructure(5,5).signal != 0 // structure break event
| Category | Indicators |
|---|---|
| Adaptive Smoothers | AdaptiveCentricMa, AdaptiveEmaSlopeBand, KalmanTrendFilter, Trama, TrendRegularityAdaptiveMa, ... |
| Oscillators | InertialRsi, AdaptiveMacdR2, InertialStochastic, MultiLookbackRsiConsensus, StochasticAdaptiveD, ... |
| Volatility | TrueRangePercentile, QuantileVolatilityBands, StatisticalTrailingStop, VolatilityConeProjection, VolatilityOfVolatility, ... |
| Volume | VolumeZscoreImpulse, SignedVolumePressure, VolumePercentileRank, VolumeWeightedTrendScore, VolumeProfileMatrix, ... |
| Regime / Trend | MarketStateMatrix, DirectionalMatrix, BayesianTrendProbability, SmaDirectionalMatrix, VolatilityStateMachine, ... |
| Statistics | ReturnMadZscore, RollingSkewKurtosis, RollingPercentileRank, PermutationEntropyRegime, IsotonicRegression, ... |
| Smart Money (SMC) | BreakOfStructure, ChangeOfCharacter, FvgDetector, OrderBlockDetector, LiquiditySweepReversal, ... |
Specify pattern name with optional trend (Bullish/Bearish). Compare against 0 to detect formation.
doji > 0 // any doji bullish engulfing > 0 15min bearish doji > 0 // bearish doji only hammer > 0 morningstar > 0 4h
DOJI, HAMMER, ENGULFING, MORNINGSTAR, EVENINGSTAR, SHOOTINGSTAR, HANGINGMAN, HARAMI, HARAMICROSS, SPINNINGTOP, MARUBOZU, PIERCING, DARKCLOUDCOVER, 3WHITESOLDIERS, 3BLACKCROWS, ABANDONEDBABY, INVERTEDHAMMER, DRAGONFLYDOJI, GRAVESTONEDOJI, HIKKAKE, KICKING, BELTHOLD, COUNTERATTACK, ...
| Alias | Maps To | Alias | Maps To |
|---|---|---|---|
close | CLOSE_PRICE | volume | VOLUME |
open | OPEN_PRICE | body_len | BODY_LEN |
high | MAX_PRICE | typical_price | TYPICAL_PRICE |
low | MIN_PRICE | direction | DIRECTION |
| Alias | Maps To | Alias | Maps To |
|---|---|---|---|
bid | Bid | mid | MidPrice |
ask | Ask | spread | SpreadInTicks |
| Alias | Maps To | Alias | Maps To |
|---|---|---|---|
ha_close | CLOSE_PRICE | ha_high | HIGH_PRICE |
ha_open | OPEN_PRICE | ha_low | LOW_PRICE |
Close > AdaptiveCentricMa(200).value // price above native smoother bid > 1.1000 // bid price check spread > 5 // spread filter ha_close > ha_open // Heikin Ashi bullish bar TrueRangePercentile.percentile > 80 // range/volatility pressure
Chart patterns detected automatically. Compare against 0 to detect formation.
DoubleBottom > 0 HeadAndShoulders > 0 BullFlag > 0 AscendingTriangle > 0 1h
DoubleBottom, DoubleTop, TripleBottom, TripleTop, HeadAndShoulders, InverseHeadAndShoulders, AscendingTriangle, DescendingTriangle, SymmetricalTriangle, BullFlag, BearFlag, BullPennant, BearPennant, RisingWedge, FallingWedge, CupAndHandle, DiamondTop, DiamondBottom, VBottom, WBottom, MTop, GapUp, GapDown, ...
OnFormationDone — the only field: a true/false flag that fires when the pattern completes. Compare against 0 (e.g. DoubleBottom > 0).
| Alias | Field | Description |
|---|---|---|
trendside | TrendSide | 1=Up, -1=Down, 0=None |
trendspeedperhour | TrendSpeedPerHour | Trend speed in ticks/hour |
fastprice | FastPrice | Fast EMA current price |
slowprice | SlowPrice | Slow EMA current price |
trendside == 1 // uptrend active trendside == -1 // downtrend active fastprice > slowprice // fast EMA above slow
TicksPerPeriod (the "Quote Updates" tile) has no Quick Create syntax at all. There is no alias for it — it is visual builder or JSON Import only. See schema §22.5 for the one-expression form.Symbol.TicksInPoint and scale it inside the logic so the threshold becomes instrument-relative too.A price move is a directional move measured from where it started: its width in ticks, its speed in ticks per second, and how long it has been running. Use this when you mean "price travelled N ticks" — not when you mean "N quote updates arrived".
| Alias | Also accepts | Field | Description |
|---|---|---|---|
price_move | pricemove, on_price_move, price_moved, price_moves, price_move_event | onPriceMove | Move detected (boolean event) |
price_move_up | price_moves_up, price_moved_up, price_going_up, price_moving_up | onPriceMove | Up move — direction preset for you |
price_move_down | price_moves_down, price_moved_down, price_going_down, price_moving_down | onPriceMove | Down move — direction preset for you |
price_move_width | move_size, move_width, price_width, price_move_size, price_move_distance, move_distance, price_move_range | getCurrentWidth | Distance from the base, in ticks — live and continuous, and signed: negative while price moves against the configured direction |
move_speed | price_speed, price_move_speed, price_velocity, speed, ticks_per_second, ticks_per_sec | getCurrentSpeed | Ticks per SECOND (width ÷ duration, both in their stated units) |
price_move_duration | move_duration, price_move_time, price_move_seconds | getCurrentDuration | How long the move has run, in seconds |
price_move_direction | move_direction, price_direction | Direction | 1=Up, -1=Down |
price_move_volume | move_volume | Volume | Volume traded during the move |
price_move_base | move_base_price, price_move_start_price | getBasePrice | Price the move started from |
price_move_start | price_move_start_time | getStartTime | When the move started |
price moves 100 ticks up within 120 sec. The parser recognises it and prefills the width, the direction and the time limit for you. It loads as a unary operand; the one thing text cannot supply is the source expression, so the builder asks for that before you Save.
✘ The field aliases on their own do not: move_size > 50 resolves a field but leaves the mandatory width, direction and source unset, so Save fails with Expression_left_ElementValue is required. The alias table above is a field reference for the builder and for JSON — not a way to build the operand by comparison.
Remember the width and the time limit are parameters, not comparisons: never write width > 100 and duration < 120.price moves 100 ticks up within 120 sec // ✔ prefills value=100, dir=Up, limit=120s price move down 25 ticks // ✔ distance-only (no time limit) price moved up 15 ticks in 3 seconds // ✔ same shape, other wording move_size > 50 // ✘ no width/direction/source — Save fails
price_move_width (getCurrentWidth) is signed relative to your configured direction — positive as price moves the way you asked, negative while it moves against. It is live and continuous, never zero-when-idle and never a stale copy of the last move. And because detection is quote-driven, a timeframe token on a price-move expression does not gate the move logic — it only sets how often the source expression yields a new value, which is why a coarse frame blinds a short move.TicksPerPeriod ("Quote Updates"). When in doubt, price move.A · Tick source (recommended for seconds-scale rules). Quotes.MidPrice updates on every quote and takes no timeframe:
[
{ "ExpressionName": "SRC_MID", // tick-resolution getter
"Expression_left_ElementType": "Quotes",
"Expression_left_ElementValue": "MidPrice" }, // no Frame — Quotes is not frame-required
{ "ExpressionName": "PM_UP_100_IN_120S",
"Expression_left_ElementType": "UserExpression",
"Expression_left_ElementValue": "SRC_MID",
"Expression_left_FieldName": "onPriceMove",
"Expression_left_PriceMoveValue": 100,
"Expression_left_PriceMoveDirection": "Up",
"Expression_left_TimeLimitSeconds": 120 }
]
B · Bar source. Same shape, but the getter is a bar field and needs a Frame. Detection can only advance once per bar, so keep the frame well below the time limit:
[
{ "ExpressionName": "SRC_CLOSE_1Min", // bar-resolution getter
"Expression_left_ElementType": "OHLC_Bar",
"Expression_left_ElementValue": "CLOSE_PRICE",
"Expression_left_Frame": "1Min" }, // 5Min here would blind a 120s rule
{ "ExpressionName": "PM_UP_100_IN_120S_BAR",
"Expression_left_ElementType": "UserExpression",
"Expression_left_ElementValue": "SRC_CLOSE_1Min",
"Expression_left_FieldName": "onPriceMove",
"Expression_left_PriceMoveValue": 100,
"Expression_left_PriceMoveDirection": "Up",
"Expression_left_TimeLimitSeconds": 120 }
]
C · Quick Create (NLP). The phrase form prefills width, direction and time limit — you then pick the source getter in the builder and Save:
price moves 100 ticks up within 120 sec // -> value=100, dir=Up, limit=120s price move down 25 ticks // -> distance only, no time limit
PriceMoveDirection accepts Up or Down — there is no "both" setting. The normal solution is PM_UP_100_IN_120S, PM_DOWN_100_IN_120S, then a third combining them with OR. Both reference the same source getter.Quotes.MidPrice updates on every quote and needs no timeframe — the right default for short windows. A 1Min bar close updates once a minute; a 5Min close may produce no new sample at all inside a 120-second window, so it cannot detect the move. The move only sees what the source samples.| Setting | Required? | Meaning |
|---|---|---|
| Source expression | Mandatory | A saved value expression supplying the price series (e.g. a 5-minute OHLC close getter). This becomes Expression_left_ElementValue. |
| Price Move Field | Mandatory | onPriceMove for the event; or a value field like getCurrentWidth / getCurrentSpeed. |
| Min Width (ticks) | Mandatory, must be > 0 | How far price must travel to trigger. This is the "100 ticks". |
| Direction | Mandatory | Up or Down. |
| Max Seconds | Optional | 0 = distance only, any duration. > 0 = must cover the width within that many seconds. This is the "in 2 minutes" — 120. |
| Frame | Mandatory | Price move is a frame-required operand. |
So "price moved 100 ticks up within 2 minutes" is one expression, not a comparison:
{
"ExpressionName": "PM_UP_100_IN_120S",
"Expression_left_ElementType": "PriceMoveObject",
"Expression_left_ElementValue": "SRC_OHLC_Close_5Min", // source expression
"Expression_left_Frame": "5Min",
"Expression_left_PriceMoveValue": 100, // the 100 ticks
"Expression_left_PriceMoveDirection": "Up",
"Expression_left_TimeLimitSeconds": 120 // the 2 minutes
}
trend_speed) is ticks/hour. Tick-reversal legs (ab_speed, bc_speed) and ABC waves (abc_speed) are ticks/minute. Convert before comparing them to a number.TicksPerPeriod, a different operand about market activity. Both are built in the visual builder or via JSON Import.Any price-move field also works in the generic form pricemove_<field> — e.g. pricemove_getcurrentwidth.
| Alias | Field |
|---|---|
upperchannel | UpperChannel |
lowerchannel | LowerChannel |
channellength | ChannelLength (width in ticks) |
upperchannel > close // price below upper channel close < lowerchannel // breakdown below channel
| Alias | Field | Alias | Field |
|---|---|---|---|
pp | Pivot Point | r1 | Resistance 1 |
s1 | Support 1 | r2 | Resistance 2 |
s2 | Support 2 | r3 | Resistance 3 |
s3 | Support 3 |
pp > close // price below pivot s1 < low // low broke support 1 close > r1 // price above resistance 1
| Alias | Field | Description |
|---|---|---|
balance | Balance | Account balance |
openpnl | OpenPNL | Open position profit/loss |
closedpnl | ClosedPNL | Closed profit/loss |
usedmargin | UsedMargin | Used margin |
availablemargin | AvailableMargin | Available margin |
openunits | OpenUnits | Total open units |
balance >= 10000 openpnl < -500
Every field of LastOpenPositionObject — the strategy's most recent open position — is reachable as last_pos_<field>.
| Alias | Field | Description |
|---|---|---|
last_pos_currentprofitticks | CurrentProfitTicks | Unrealised profit in ticks |
last_pos_currentprofitpercent | CurrentProfitPercent | Unrealised profit as a percent of the position's own open price |
last_pos_openpnl | OpenPNL | Unrealised profit in account currency |
last_pos_openprice | OpenPrice | Entry price |
last_pos_currentdurationminutes | CurrentDurationMinutes | Minutes since the position opened |
last_pos_positionside | PositionSide | Side as a number (BID=0, ASK=1) |
Both profit fields are signed by side: positive means the position is winning, for a BID and an ASK alike — so one expression covers both directions. Percent is 0.2 for 0.2%.
The two profit getters also have short aliases: profit_percent / pos_profit_pct and profit_ticks / pos_profit_ticks.
profit_percent >= 0.2 // take profit at +0.2% profit_percent <= -0.2 // stop at -0.2% — put BOTH in Close Bid and Close Ask last_pos_currentprofitpercent > 0.5 // winner: up 0.5% of entry last_pos_currentprofitpercent < -0.2 // loser: percent stop, same on every symbol last_pos_currentprofitpercent >= 0.1 // give-back level (see warning below) last_pos_currentprofitticks > 50 // same idea in ticks
Careful with a bare profit threshold. A position opens at ~0% profit, so last_pos_currentprofitpercent < 0.1 is already true at entry and would close it immediately. For "profit dropped below 0.10%", AND it with proof the position reached that level first — the peak (unary MAX) at or above 0.1. MIN cannot express this: it reports the worst point, never that profit had risen.
| Alias | Type | Field |
|---|---|---|
datetime | DateValue | CurrentSysDateTime |
day | DateValue | CurrentSysDay |
time | DateValue | CurrentSysTime |
14:00 | HourValue | Specific hour |
datetime > DateValue.2026-01-01 // current date/time after fixed date time < 14:00 // before 2 PM (intraday hour check)
| Operator | Meaning | Example |
|---|---|---|
> | Greater than | InertialRsi(14).signal > 70 |
< | Less than | InertialRsi(14).signal < 30 |
>= | Greater or equal | balance >= 10000 |
<= | Less or equal | TrueRangePercentile.percentile <= 50 |
== | Equal | trendside == 1 |
!= | Not equal | trendside != 0 |
AND | Both saved expressions must be true | UserExpression.RSI_Below_30 AND UserExpression.Balance_GT_100 |
OR | Either saved expression may be true | UserExpression.RSI_Below_30 OR UserExpression.Price_Above_SMA |
crossed / crosses [above] | Up-cross: was below last bar, above now | RSI(14) crossed 30 |
crossed below / crosses under | Down-cross: was above last bar, below now | Close crossed below EMA(50) |
+ - * / | Not available in Quick Create. Arithmetic is a real operator in the visual builder and JSON Import, where it returns a value rather than a boolean — build the calculation as its own expression there, then reference it here by name. | builder / JSON only |
My_Signal, or My_SignalA AND My_SignalB. Must match a saved expression exactly (built-in words like RSI/close still resolve to the indicator/field, not an expression).exp.My_Signal or exp(My_Signal). Use this when the name shadows a built-in word, to force the expression.UserExpression.My_Signal or UserExpression(My_Signal).exp. prefix.RSI(14) < 30 and balance > 100 auto-builds the two legs plus the combiner for you (no need to pre-save anything). You can also combine already-saved expressions by name with AND/OR (any of the reference forms above) when you want to reuse them.AND binds tighter than OR, so a AND b OR c AND d means (a AND b) OR (c AND d). Use parentheses to group explicitly and override precedence — (a OR b) AND c, even nested: a OR (b AND (c OR d)). Function calls like RSI(14) / MACD(12,26,9) / exp(X) are understood as operands, not grouping. The final loads in the builder; the intermediate step(s) are created automatically on Save. A leg can be a saved-expression name, a single operand, or a raw comparison — e.g. (bullish engulfing > 0) and RSI(14) > 50 auto-builds each condition into its own building-block expression, then combines them.// Input — (Trend_Up OR Reversal) AND RSI_Below_30 ( exp.Trend_Up OR exp.Reversal ) AND exp.RSI_Below_30 // step1 (intermediate, auto-created on Save) exp.Trend_Up OR exp.Reversal // final (loaded for approval) UserExpression.[step1] AND exp.RSI_Below_30
// Leg 1 RSI(14) < 30 // save as RSI_Below_30 // Leg 2 balance > 100 // save as Balance_GT_100 // Combiner UserExpression.RSI_Below_30 AND UserExpression.Balance_GT_100
[
{
"ExpressionName": "RSI_Below_30",
"Expression_left_ElementType": "GTSIndicators",
"Expression_left_ElementValue": "RSI",
"Expression_left_Frame": "5Min",
"Expression_left_Size1": 14,
"Expression_left_operation": "<",
"Expression_right_ElementType": "FixedVal",
"Expression_right_ElementValue": "30"
},
{
"ExpressionName": "Balance_GT_100",
"Expression_left_ElementType": "AccountObject",
"Expression_left_ElementValue": "Balance",
"Expression_left_operation": ">",
"Expression_right_ElementType": "FixedVal",
"Expression_right_ElementValue": "100"
},
{
"ExpressionName": "RSI_Below_30_AND_Balance_GT_100",
"Expression_left_ElementType": "UserExpression",
"Expression_left_ElementValue": "RSI_Below_30",
"Expression_left_operation": "AND",
"Expression_right_ElementType": "UserExpression",
"Expression_right_ElementValue": "Balance_GT_100"
}
]
a crossed b means a was below b on the previous bar AND a is above b now. Leg 1 indexes both sides (a[1] < b[1]) — it compares prev against prev, and leg 2 compares now against now. How much that matters depends on the right side: against a fixed number (RSI(14) crossed 30) the index is a no-op, since a constant is identical on every bar. When both sides are dynamic (EMA(50) crossed EMA(200)) it is essential — comparing a[1] to a live b fires false crosses whenever the slow line is moving. Typing a cross in Quick Create auto-expands it into two history-indexed legs plus an AND combiner (built bottom-up). The combiner loads into the builder for you to review; the two legs are created automatically when you click Save. crossed below / crosses under flips it (was above, now below).// Input RSI(14) crossed 30 // Leg 1 — prev vs prev (a[1] < b[1]; index is a no-op if b is a constant) RSI(14)[1] < 30 // RSI_14_below_30_prev // Leg 2 — now above (a > b) RSI(14) > 30 // RSI_14_above_30_now // Combiner (loaded for approval) UserExpression.RSI_14_below_30_prev AND UserExpression.RSI_14_above_30_now
Append the timeframe at the end, e.g. 15min. (Expressions carry no symbol on Trading — the symbol is set on the strategy.)
| Input | Resolves To | Input | Resolves To |
|---|---|---|---|
1min 1mins 1m | 1Min | 1hour 1hours 1h | 1Hour |
5min 5mins 5m | 5Min | 2hour 2hours 2h | 2Hours |
10min 10mins 10m | 10Min | 4h 4hour 4hours | 4Hour |
15min 15mins 15m | 15Min | 12hour 12hours 12h | 12Hours |
30min 30mins 30m | 30Min | daily 1d | Daily |
weekly 1w | Weekly | monthly 1mo | Monthly |
For precise control, use structured TYPE.FIELD.PARAMS.FRAME syntax.
GTSIndicators.EMA.50.1Hour - GTSIndicators.EMA.200.1Hour will not parse. Dot-notation is a precise way to name one operand — it does not add operators the parser lacks.Expression_left_operation accepts all twelve of these values (they are exactly the operator buttons in the visual builder):
| Group | Accepted values | Expression returns | Quick Create? |
|---|---|---|---|
| Compare | < > == != <= >= | boolean | Yes |
| Logic | AND OR | boolean | Yes |
| Math | - + * / | value (a getter) | No — builder / JSON only |
A concrete, real example — this is how the bundled EMA_DIFF_H1_ABS Community expression is built. Note operation is "-" and the result is a number, so it is a helper, not an alert:
{
"ExpressionName": "EMA_DIFF_H1_ABS",
"Expression_left_ElementType": "UserExpression",
"Expression_left_ElementValue": "EMA_1H_50_GETTER_H1",
"Expression_left_operation": "-", // math operator — returns a value
"Expression_left_applyABS": true,
"Expression_right_ElementType": "UserExpression",
"Expression_right_ElementValue": "EMA_1H_200_GETTER_H1"
}
Then a second expression turns that number into a condition — UserExpression.EMA_DIFF_H1_ABS > FixedVal.0.0002. That two-step shape is the answer to every "calculated condition".
// Regular indicators GTSIndicators.RSI.14.5Min < FixedVal.30 GTSIndicators.MACD.12.26.9.15Min.Signal > FixedVal.0 OHLC_Bar.CLOSE_PRICE.Daily > GTSIndicators.SMA.30.Daily // Extended indicators / subsystems ExtendedIndicators.InertialRsi.14.5Min.signal > FixedVal.30 ExtendedIndicators.AdaptiveMacdR2.20.12.26.9.15Min.signal > FixedVal.0 ExtendedIndicators.QuantileVolatilityBands.100.2.1Hour.upper > OHLC_Bar.CLOSE_PRICE.1Hour // Candle patterns TA_CandlePatterns.DOJI.Bullish.5Min > FixedVal.0 // Price bars OHLC_Bar.CLOSE_PRICE.Daily > ExtendedIndicators.AdaptiveCentricMa.200.Daily.value HeikinAshiBar.CLOSE_PRICE.5Min > HeikinAshiBar.OPEN_PRICE.5Min // Objects TrendObject.TrendSide == FixedVal.1 ChannelObject.UpperChannel > OHLC_Bar.CLOSE_PRICE PivotObject.S1.Daily > OHLC_Bar.MIN_PRICE.Daily // Account & date AccountObject.Balance >= FixedVal.10000 AccountObject.OpenPNL < FixedVal.-500 DateValue.2026-06-01 > DateValue.2026-01-01 HourValue.14:00 > DateValue.CurrentSysDateTime // Price patterns PricePattern.DoubleBottom.OnFormationDone > FixedVal.0
TYPE.FIELD format.SAR(0.02,0.2) > Close 5min or bid > 1.1000.InertialRsi(14).signal < 30 // oversold-style momentum InertialRsi(14).signal > 70 // overbought-style momentum AdaptiveMacdR2(20,12,26,9).signal > 0 // MACD-style signal positive InertialStochastic(5,34,3,3).signal < 20 // stochastic-style oversold BreakOfStructure(5,5).signal != 0 // structure event
Close > AdaptiveCentricMa(200).value daily // above native smoother AdaptiveCentricMa(50).value > AdaptiveCentricMa(200).value // fast above slow KalmanTrendFilter.trend > 0 // strong trend trendside == 1 // GTS uptrend active
a > b > c.// Step 1 — helper getter: one RSI value from close RSI_Close = RSI applied to Close, collection size 15, frame 5Min // Step 2 — helper getter: one EMA value applied to the RSI output EMA9_RSI_Close = EMA applied to UserExpression.RSI_Close, collection size 9 // Step 3 — compare current EMA(RSI) to previous EMA(RSI) EMA9_RSI_Close > EMA9_RSI_Close[1] // a > b // Step 4 — compare previous EMA(RSI) to two-bars-back EMA(RSI) EMA9_RSI_Close[1] > EMA9_RSI_Close[2] // b > c // Final expression — true only when the last 3 EMA(RSI) values are rising UserExpression.EMA9_RSI_now_gt_prev AND UserExpression.EMA9_RSI_prev_gt_prev2
// Exact JSON shape for the same recipe
[
{
"ExpressionName": "RSI_Close",
"Expression_left_ElementType": "OHLC_Bar",
"Expression_left_ElementValue": "CLOSE_PRICE",
"Expression_left_Frame": "5Min",
"Expression_left_UseRSI": true,
"Expression_left_collectionSize": 15,
"Expression_left_numElement": 15
},
{
"ExpressionName": "EMA9_RSI_Close",
"Expression_left_ElementType": "UserExpression",
"Expression_left_ElementValue": "RSI_Close",
"Expression_left_UseEMA": true,
"Expression_left_collectionSize": 9,
"Expression_left_numElement": 9
},
{
"ExpressionName": "EMA9_RSI_now_gt_prev",
"Expression_left_ElementType": "UserExpression",
"Expression_left_ElementValue": "EMA9_RSI_Close",
"Expression_left_operation": ">",
"Expression_right_ElementType": "UserExpression",
"Expression_right_ElementValue": "EMA9_RSI_Close",
"Expression_right_UseHistoryElem": true,
"Expression_right_historyIndex": 1,
"Expression_right_collectionSize": 2
},
{
"ExpressionName": "EMA9_RSI_prev_gt_prev2",
"Expression_left_ElementType": "UserExpression",
"Expression_left_ElementValue": "EMA9_RSI_Close",
"Expression_left_UseHistoryElem": true,
"Expression_left_historyIndex": 1,
"Expression_left_collectionSize": 2,
"Expression_left_operation": ">",
"Expression_right_ElementType": "UserExpression",
"Expression_right_ElementValue": "EMA9_RSI_Close",
"Expression_right_UseHistoryElem": true,
"Expression_right_historyIndex": 2,
"Expression_right_collectionSize": 3
},
{
"ExpressionName": "EMA9_RSI_last3_rising",
"Expression_left_ElementType": "UserExpression",
"Expression_left_ElementValue": "EMA9_RSI_now_gt_prev",
"Expression_left_operation": "AND",
"Expression_right_ElementType": "UserExpression",
"Expression_right_ElementValue": "EMA9_RSI_prev_gt_prev2"
}
]
Close > QuantileVolatilityBands(100,2).upper // above upper band TrueRangePercentile.percentile > 80 // high range percentile VolatilityStateMachine.state != 0 // volatility regime active
bullish engulfing > 0 4h // candle pattern DoubleBottom > 0 // chart pattern hammer > 0 1h // hammer on hourly
close > r1 // above resistance 1 s1 < low // broke support 1 close > upperchannel // channel breakout
balance >= 10000 // min balance openpnl < -500 // max open loss spread < 3 // max spread filter
A unary expression is a single operand with no comparison — it fires on its own when the operand is truthy / non-zero. Just type the operand by itself (no operator, no right side):
bullish engulfing // candle pattern — fires on formation doji 1h // pattern on a timeframe UserExpression.My_Signal // a saved expression, on its own KalmanTrendFilter.trend // fires when the value is non-zero
RSI(14) < 30). A bare numeric indicator (e.g. RSI(14)) becomes a unary getter — valid as a building block, but you usually want a comparison.Append [N] to any operand to read it N bars back — the same notation the builder shows in dot-notation. [0] or no suffix = the current bar.
Close[1] > Close[2] // previous close above the one before RSI(14)[1] < 30 // RSI on the previous bar direction[1] > 0 // previous bar closed up high[1] > SMA(20) // prior high vs current SMA(20)
a<b then a>b) needs two bars of state, which one single-condition expression can't hold. You don't have to build it by hand — type a crossed b (or crossed below) and it auto-expands into the two history-indexed legs (a[1] < b[1] and a > b) plus an AND combiner — prev vs prev, then now vs now. Against a fixed number the index on the right side does nothing; when both sides move it is what stops false crosses. See the cross rows in Operators.