Welcome to GTS Trading 👋

New here and it feels like a lot? It isn't. Everything you do follows one simple chain: write a condition → wrap it in a strategy → get alerted or trade it → watch it run. This guide answers the exact questions in the order most people ask them.

Tip: use the table of contents on the left to jump to any question. Click a question to expand its answer.

Two 2-minute recipes

The fastest way to "get" the platform: do one of these start-to-finish. Each takes about two minutes — no real funds at risk, everything runs on paper.

🛠️ Recipe 1 — Your idea, live on paper
  1. Get your expression. Builder: type the condition (RSI(14) < 30 5min) or describe it in plain English and let AI build it — then Save.
  2. Apply it to a strategy. Builder → Strategies → New: attach your expression to the Open handler, set Units / Stop-Loss / Take-Profit, Save.
  3. Test it on history. History Requests: pick one or more strategies, set the date range, name the request, Run — then read the report (trades, win rate, PnL).
  4. Run it live (paper). My Strategies: pick your strategy → Deploy to server. Then start the server and open Monitor — watch quotes tick in, positions open and PnL move in real time.
🌐 Recipe 2 — Backtest a community strategy
  1. Pick a strategy. History Requests → Strategies → Community: browse the library, open one you like, Import it.
  2. Set the run. Pick one or more strategies, set the date range (e.g. the last 3 months) and give the request a name.
  3. Run it. Submit and watch the progress — longer ranges take a few minutes.
  4. Read the results. Open the run's report: trades, win rate, profit factor, drawdown. Like it? Deploy the same strategy to paper from the Monitor and watch it live.

1 Getting started

Read this section first — it removes 90% of the confusion. The three words below (expression, strategy, handler) are the whole mental model.

Q What is this app and what can I do here?

GTS Trading lets you describe market conditions (e.g. "RSI on the 1h is below 30") and then choose what happens when that condition becomes true: get a signal/alert, or open and close positions on a connected exchange (paper or real).

You do all of this without writing code — though you can bring your own C++ or Lua if you want. The main areas, from the top nav:

AreaWhat it's for
BuilderCreate expressions, wire strategy handlers, and generate mass/permutation strategy sets. The left library gives access to Mine plus 74k+ Community expressions in the GUI, also accessible through the API for agents.
My ExpressionsYour saved conditions library.
My StrategiesStrategies you've assembled and can deploy.
Signal MonitorDatabase-backed tables/feed capturing every live alert fire in the system: expression, strategy, symbol, time, delivery status, and channel results.
History RequestsGateway for history runs: select one or many strategies, include permutation packs, set plan/range options, deploy the relevant strategy definitions into the history runtime, run them, and review reports.
MonitorDeep operational view of running strategies & positions.
Charts / SEC EdgarPrice charts and company filings for research.
Plans / Settings / API DocsBilling, channel/provider configuration, API keys for agentic access, runtime risk limits, and programmatic access docs.
Top navigation bar🖼️Top navigation barscreenshot: full-width nav — Builder · My Expressions · My Strategies · …
The top nav is your map. Builder on the left, account/quota on the right.
Q What's the difference between an Expression, a Strategy, and a Handler?

This is the thing to understand. They stack:

TermPlain meaningExample
ExpressionA single condition that is either true or false right now.RSI(14) < 30
HandlerA rule group that watches its inner expression list and relation, then emits one configured signal/action: open buy, open sell, close buy, close sell, or alert-only/no trade. Trading settings such as size, TP, and SL apply only to trading handlers."RSI<30 AND spread OK → alert only" or "RSI<30 → open buy"
StrategyA deployable per-symbol runtime/reporting entity: it contains a set of handlers plus the user-set trading and risk parameters. Handlers emit signals; the strategy applies the relevant logic: units, max open positions, SL/TP, trailing, hedging, limits, dynamic close/report behavior, and paper/live runtime settings."On EURUSD 1h, open buy handler on RSI<30, close buy handler on RSI>70, max 2 positions, 10 units, SL/TP, and trailing rules"
Mental model: Expression = the condition. Handler = the signal/action decision. Strategy = handlers + user trading/risk/reporting parameters, deployed per symbol.
Q What's the typical flow from start to finish? (the 60-second tour)
1 · Write an expression 2 · Wrap it in a strategy + handler 3 · Choose alert or trade 4 · Deploy (paper or real) 5 · Watch it in Monitor
  1. In Builder → Expression Builder, type a condition in plain English and hit Parse. Save it.
  2. Switch to Builder → Strategy & Handler. Pick your expression as the entry condition, set the action (alert or open position).
  3. Decide: do you just want to be alerted, or do you want it to open/close positions?
  4. Deploy it. Start on paper/demo until you trust it.
  5. Open Monitor / Signal Monitor to watch fires and positions live.
You can stop after step 3 if you only want alerts. Trading is optional.
Q Alerts vs trades — what actually happens when a signal fires?
  • Alert: you get notified (in Signal Monitor and any channels you've set up). Nothing is bought or sold.
  • Trade (paper): the strategy opens/closes a position on the GTS paper-trading account — a simulated account GTS manages. This is the default and needs no broker.
Important — your strategies never trade a real account. Automated execution only ever happens on the GTS paper account. Connecting a real exchange (Bybit / FXCM / Binance / OANDA) is for monitoring and alerting: at deploy GTS forces live trading off and coerces handlers to alert-only, so a strategy cannot send a real order. The one exception is a manual trade you place and confirm yourself — see §7 connect an exchange.

2 Expressions (the Builder)

An expression is one true/false condition. There are seven ways to make one — pick whichever feels easiest. They all produce the same thing. Compare all seven routes ↓

Expression Builder — full screen🖼️Expression Builder — full screenscreenshot: Quick Create box + Generate with AI + JSON Import, left expression list
Expression Builder. Left = your library. Right = the creation methods — see all seven routes.
Q How do I create an expression?

There are seven ways in, and they all end at the same place — a saved expression in My Expressions. Pick by how you like to work, not by what the expression does: every route reaches the full operand surface.

RouteUse it whenAI tokens
Builder selection§manualYou want full control: pick the left operand type, the operator, and the right operand from the type grid.Free
Quick Template§simpleStandard indicator check. Pick the indicator, fill the params, Save.Free
Quick Create§simpleYou'd rather type it in plain English. Deterministic parser, no model involved. Works on the Free plan.Free
Generate with AI§aiFree-form description, or logic Quick Create won't parse.Spends
JSON Import ADVANCED§jsonDeeply nested logic, or JSON written in your own ChatGPT/Claude and pasted back.Free
Community import§communitySomeone already built it. 74k+ shared expressions — import and edit freely.Free
Agent / API§apiProgrammatic or agent-driven creation with your API key.Free
On the Free plan? You still have five full routes — Builder selection, Quick Template, Quick Create, JSON Import, and Community import. Only Generate with AI needs tokens. Being on Free never blocks an expression type.
Asking an external AI chat how to build something here? Point it at this page and at the Expression Creation Schema. Models that see only part of the docs tend to report a route as impossible when a free one exists.
Q Show me one expression built all 7 ways

Worked example: "EMA(50) was below EMA(200) on the previous bar, and is above it now" — an up-cross. It's a good test case because it is a two-bar event, so it can't live in a single condition. Every route below produces the same three expressions.

Why three? A cross needs two bars of state. It always decomposes into leg 1 (previous bar), leg 2 (now), and an AND combiner that references both by name. Any route that claims to do it in one expression is wrong.

1 · Quick Create — the fastest, and free

Type it and press Parse:

EMA(50) crossed EMA(200) 1Hour

The parser auto-expands the cross into all three pieces and loads the combiner for review. Clicking Save creates the two legs at the same time. Names are generated for you: EMA_50_below_EMA_200_prev, EMA_50_above_EMA_200_now, EMA_50_cross_up_EMA_200. Use crossed below for the down-cross.

2 · Builder selection — three passes

  1. Leg 1: left = Indicators → EMA, period 50, frame 1Hour, History = 1. Operator <. Right = EMA(200), 1Hour, History = 1. Save as EMA_50_below_EMA_200_prev.
  2. Leg 2: same two operands with History = 0, operator >. Save as EMA_50_above_EMA_200_now.
  3. Combiner: left = User Expression → ..._prev, operator AND, right = User Expression → ..._now. Save.
Leg 1 is prev vs prev, leg 2 is now vs now. Here both sides are dynamic, so both carry History = 1 on leg 1 — comparing EMA(50)[1] against a live EMA(200) fires false crosses whenever the slow line moves. Against a fixed number instead (RSI(14) crossed 30) only the indicator needs the index; a constant reads the same on every bar. Quick Create handles both cases for you.

3 · Quick Template — not usable here

Templates cover a single indicator against a value. A two-bar cross is out of scope — use route 1 or 2. (Templates remain the fastest path for things like RSI(14) < 30.)

4 · Generate with AI — describe it

EMA 50 crosses above EMA 200 on the 1 hour, as three expressions:
previous-bar leg, current-bar leg, and an AND combiner

Spends tokens; no Free-plan allowance. Say "as three expressions" or the model may try to emit one.

5 · JSON Import — full control

Leg 1, with both operands one bar back:

{
  "ExpressionName": "EMA_50_below_EMA_200_prev",
  "Expression_left_ElementType": "GTSIndicators",
  "Expression_left_ElementValue": "EMA",
  "Expression_left_Frame": "1Hour",
  "Expression_left_historyIndex": 1,
  "Expression_left_UseHistoryElem": true,
  "Expression_left_collectionSize": 2,
  "Expression_left_operation": "<",
  "Expression_right_ElementType": "GTSIndicators",
  "Expression_right_ElementValue": "EMA",
  "Expression_right_Frame": "1Hour",
  "Expression_right_historyIndex": 1
}

Leg 2 is the same with historyIndex dropped and operation flipped to >. The combiner references both by name:

{
  "ExpressionName": "EMA_50_cross_up_EMA_200",
  "Expression_left_ElementType": "UserExpression",
  "Expression_left_ElementValue": "EMA_50_below_EMA_200_prev",
  "Expression_left_operation": "AND",
  "Expression_right_ElementType": "UserExpression",
  "Expression_right_ElementValue": "EMA_50_above_EMA_200_now"
}

collectionSize is historyIndex + 1. Free — no tokens, whether you write the JSON yourself or have an external model write it.

6 · Community import — already built

Search the Community tab for EMA_CROSS_UP. EMA_CROSS_UP_CORE is exactly this pattern; EMA_CROSS_UP_STRICT adds a separation filter (EMA_DIFF_NOW_ABS > 0.0002) so tiny touches don't count as a cross. Import and edit freely.

7 · Agent / API — programmatic

POST /api/v1/expressions/validate   # optional dry run
POST /api/v1/expressions            # leg 1
POST /api/v1/expressions            # leg 2
POST /api/v1/expressions            # combiner (references both by name)

Same JSON as route 5, one call per expression, legs before the combiner. See §API.

Want a separation threshold ("must cross by N")? There is no inline arithmetic in Quick Create — EMA(50) > EMA(200) + 2 will not parse. Build it bottom-up: an ABS difference expression, then a > FixedVal check, then AND it onto the cross. That is exactly how EMA_CROSS_UP_STRICT is assembled in route 6.
Q How do I build a simple expression in plain English?

The quickest way is a Quick Template: pick an indicator, fill in the params, and Save — done. Prefer to type it out in plain English? Use the Quick Create box at the top of the Expression Builder:

  1. Type the condition the way you'd say it: RSI(14) < 30 5min or MACD cross above signal 15min.
  2. Click ⚡ Parse.
  3. The builder fills in below. Give it a name and Save.

Don't know where to start? Click one of the example chips under the box — RSI(14) < 30, SMA(50) > SMA(200) Daily, Close > BBANDS(20).Upper 1Hour — to load it instantly.

What the quick parser understands: LEFT OPERATOR RIGHT with an optional timeframe/symbol — e.g. RSI(14) < 30 5min, RSI(14) crossed 30, MACD cross above signal 15min, RSI(14) < 30 and balance > 100 — or a unary signal: a single operand on its own, like bullish engulfing 5min, hammer 15min, KalmanTrendFilter.trend — or a bare indicator as a value getter, e.g. RSI(14) 5min. Do not write a symbol — expressions on this site carry none; the strategy binds the instrument at deploy. Indicator parameters always need parentheses: RSI(14) parses, RSI 14 doesn't. Wordier or calculated conditions need AI Generate, formula blocks, or JSON. For example, a same-bar 5-minute +2% candle body means 5Min Close >= Open * 1.02 — current 5Min close compared to the same bar's open.

The basic shape is LEFT  OPERATOR  RIGHT with an optional timeframe (no symbol — on the trading side a symbol is applied later, at the strategy) — but Quick Create handles more than that:

  • Unary (getter) — a single value on its own, no operator, e.g. RSI(14) or Donchian upper. (A comparison like bullish engulfing > 0 is binary — it has two sides.)
  • Compound — type the whole condition and it auto-builds the legs plus combiner: RSI(14) < 30 and balance > 100, 3+ terms, mixed AND/OR with parentheses ((a OR b) AND c), and crossings (RSI(14) crossed 30). Intermediate steps are created automatically on Save.
  • Saved-expression refs — reuse a saved expression by name as an operand: My_SignalA AND My_SignalB.

Prefer plain English? The Generate with AI box turns a free-form description into a validated expression. Full syntax reference: Quick Create — NLP Reference.

Quick Create box with example chips🖼️Quick Create box with example chipsscreenshot: input + Parse + chips row
You can omit indicator parameters — sensible GTS defaults are used.
Quick Create is free and unlimited. Parsing is deterministic — it costs no AI tokens, on any plan including Free. Tokens are only spent by Generate with AI and AI JSON generation (next questions).
Q How do I let AI build the expression for me?

Use the Generate with AI box. Describe what you want in everyday language and the AI writes, validates, and loads the expression for you.

  1. Type a description: "EURUSD when RSI 14 on 1h is below 30" or "gold 5min close crosses above EMA 200".
  2. Press Generate with AI (or Ctrl+Enter).
  3. Review what it loaded, then Save.
Unlike Quick Create, AI generation calls a model and spends AI tokens from your daily allotment — shown right under the box (e.g. 0 / 1,000,000 tokens used · 100% left on the max plan). Every paid plan includes a daily token allowance; the Free plan has none (use Quick Create instead). Run out? Wait for the daily reset or upgrade to a higher plan. See §6 plans & tokens.
Code vs. JSON — which AI path? Generating Lua / C++ code is much faster and uses far fewer tokens — the model writes concise code instead of a long object, so it's the cheap, quick path for most ideas. AI JSON generation is more complex and token-heavy, but it supports every expression option — use it when you need the full surface that code or Quick Create can't express.
Q How do I import an expression as JSON, or from ChatGPT / Claude?

Use JSON Import ADVANCED at the bottom of the Expression Builder.

  1. Have a complete expression JSON object? Paste it in the box and hit Import — it loads straight into the builder.
  2. Don't want to hand-write JSON? Click Copy AI Schema Link, paste it into ChatGPT / Claude / Gemini, and ask the model to generate the expression JSON for you.
  3. Paste the model's result back here and Import.
The schema link is self-contained — the model gets everything it needs to produce valid GTS JSON without browsing.
Pasting/importing JSON is free — no AI tokens. Tokens are only spent if you use the in-app Generate with AI to write the JSON for you; generating it in your own ChatGPT/Claude and pasting it back costs you nothing here.
Q How do I build an expression by hand (visual builder)?

Below the Quick Create methods is the visual operand builder for full control:

  1. Set the left operand (e.g. an indicator, price, or account value).
  2. Pick the operator (>, <, cross above, etc.).
  3. Set the right operand (another indicator, or a fixed value).
  4. Choose the symbol and timeframe, then Save.
Visual operand builder🖼️Visual operand builderscreenshot: left panel / operator / right panel / timeframe
Q How do I add an expression from the Community?

There are 74k+ shared Community expressions you can reuse from the GUI library panel, alongside your own Mine expressions. Agents can access the same expression library through the API.

  1. In the left library panel, click the Community tab.
  2. Search or browse, then click an expression to preview it.
  3. Use Import / Copy to mine — it's added to My Expressions so you can edit it freely.
Community tab in the library🖼️Community tab in the libraryscreenshot: ALL / MINE / COMMUNITY tabs + count
If an import says "Blocked / missing deps", the shared item is missing its building-block expressions. See §8 troubleshooting.
Q What's the difference between All / Mine / Community / Old?
  • All — everything visible to you.
  • Mine — only expressions you created or imported.
  • Community — shared by other users; import to reuse.
  • Old — archived/previous versions (e.g. items moved aside on a plan downgrade).

The filter chips ANY HELPER ALERT narrow the list by role (next question).

Q What's a Helper expression vs an Alert expression?
  • ALERT Alert expression — a complete condition meant to fire on its own (notify you / trigger a handler).
  • HELPER Helper expression — a reusable building block you reference inside bigger expressions. Helpers usually don't fire by themselves; they feed the ones that do.
Best practice: build small named helpers (atomic value-getters) first, then compose alert expressions on top of them. See composing complex expressions.
Q What expression types exist?

GTS has 35+ operand types — far more than the basics. Each is an ElementType plus that type's required fields, producing a value or a boolean event you compare. Grouped by family:

FamilyTypes (ElementType)What they read
IndicatorsTA_Indicators, ExtendedIndicatorsRSI, SMA, MACD, ATR… plus advanced tools (FVG, Order Block, SMC/ICT, adaptive & ML/regime)
Price & barsOHLC_Bar, Quotes, RenkoBar, HeikinAshiBar, TickRevBar, TicksPerPeriodOpen/High/Low/Close/volume, bid/ask, and alternative bar types
PatternsTA_CandlePatterns, PricePattern, ABCPattern, FibonnaciBar, PivotObjectCandlestick & price-structure events, pivots, Fibonacci, ABC waves
Trend & channelsTrendObject, ChannelObject, PriceMoveObject, ConsensusScoreTrend side/length/speed, channel bounds, price moves, multi-signal consensus
Account & positionsAccountObject, AccFrameObject, RollingAccFrameObject, LastOpenPositionObject, ClosePositionObject, StrategyOpenPositionsBalance, equity, used margin, open P&L, and open/closed position details
FundamentalsFundamentalEDGAR financials — revenue, EPS, ratios…
Date & timeDateValue, HourValueA date, or the hour of day
Fixed valuesFixedVal, SideValueA fixed number, or a trade direction (long / short)
Reference dataSymbolSymbol context — tick size, margin, contract specs, and other instrument reference data
Reusable & compositionUserExpression, StateMachineExpression, CollectionTransform, HistValue, ExpressionMetaAny saved expression is reusable — reference it by name as a UserExpression building block. Plus multi-step state machines, history lookback & transforms
Code (Private)LuaScriptOperand (Lua), CustomizedUserOperand (C++)Inject your own Lua or C++ operand — private servers only (2 code-injection types)
Candle patterns are boolean directional events — compare with > 0 (not == 100), and a trend is required.
Which fields does a type need? The Expression Types — Field Reference has one card per type: what it returns, its mandatory fields, its optional ones, and the logic in a sentence. The mandatory list is exactly what the builder validator enforces, so it tells you in advance why a Save would fail.
"100 ticks" means two different things — pick the right family. Price distance ("price moved 100 ticks in 2 minutes") is PriceMoveObject — it measures a move's width in ticks and its speed in ticks/second. Tick count ("100 quote updates arrived in 2 minutes") is TicksPerPeriod — market activity/liquidity, not distance. Same words, different operands — if you want movement, you want Price Move. Price move can be started from Quick Create with the full phrase price moves 100 ticks up within 120 sec (width, direction and time limit are prefilled; the builder then asks for the source expression). TicksPerPeriod is built in the builder or via JSON Import. price move is a unary operand whose width and time limit are parameters (Min Width ticks + Max Seconds + Direction, over a mandatory source expression) rather than comparisons you type. See the Price Move reference for the exact fields.
Every expression you save becomes a reusable, composable building block: any element can be referenced by name (a UserExpression) inside a bigger expression. That's how small helpers compose upward into full alert/strategy logic.
Building by API / AI agent? GET /api/v1/expression-schema?type=<ElementType> returns that type's exact required fields and a runnable example (add ?format=json for structured output); POST /api/v1/expressions/validate checks any JSON against the live validator before saving; POST /api/v1/expressions pushes it. The schema endpoint is the authoritative, always-current list of every type — see Control via API.
Q Can an expression return a value instead of true/false? (getters)

Yes. Not every expression is a condition. An expression is one of two things:

KindHow you build itReturns
Condition (boolean)Two sides with a comparison or logic operator — > < == != >= <=, AND, OR, crossedtrue / false — this is what an alert or handler fires on
Getter (value)No operator at all — just a left operand. Or two sides with an arithmetic operator: - + * /a number — a building block, never fires by itself

In the builder, leave the operator unselected and the preview flips to "Unary getter""works as a getter without a binary operator". Save it and it's a named, reusable value. In Quick Create, just type the operand alone: RSI(14) 5min, Donchian upper.

What consumes a getter

Once saved, reference it by name as a User Expression. Three things can consume it:

  1. A condition — put a threshold on it: UserExpression.My_RSI_1h < 30. Value in, boolean out.
  2. Another getter — combine two values arithmetically: UserExpression.EMA_50 - UserExpression.EMA_200. Value in, value out — still a getter, so it chains as deep as you need.
  3. A unary operation — wrap it: ABS, NOT, Max/Min over N, History (N bars back), Z-Score, Momentum, Accel, Pct Rank, Dir Strength. Most return a value; NOT returns a boolean.
The rule: values compose into values for as many levels as you like, and the top of the chain is a condition. Only that top expression fires. This is why the builder lets you save something with no operator — you're saving a part, not a signal.
Think of an expression as a class, and the strategy as the instance. An expression is a blueprint — logic with no symbol attached, deliberately generic. The strategy instantiates it by binding a symbol at deploy, and the engine resolves that instrument's tick size, point value and contract specs for that instance. Attach one expression to five symbols and you have five instances of one class — same logic, each correctly parameterised. You never duplicate an expression per instrument. See the class/instance model.
Unary does not mean simple, and composition does not require a binary. A unary expression — no operator, no right side — can still consume other saved expressions. That is the whole point of several types: UserExpression references one directly; PriceMoveObject and ExpressionMeta each take a mandatory source expression; CollectionTransform takes a SOURCE (and SOURCE B); ConsensusScore, StateMachineExpression and HistValue take two or more children. Any of the unary operations (ABS, NOT, Max, Min, History, Z-Score, Momentum, Accel, Pct Rank, Dir Strength) can wrap those too. So a unary can sit several levels deep in a chain. Anyone who tells you "a GTS expression is binary" is wrong — see the expression model.

A real chain, bottom to top

These ship in the Community library — search EMA_DIFF or EMA_CROSS_UP:

ExpressionBuilt fromReturns
EMA_1H_50_GETTER_H1EMA(50) 1Hour, no operatorvalue
EMA_1H_200_GETTER_H1EMA(200) 1Hour, no operatorvalue
EMA_DIFF_H1_ABSthe two above with -, ABS appliedvalue — a getter made of getters
EMA_DIFF_H1_ABS_GT_0_0002that > FixedVal 0.0002boolean — first condition in the chain
EMA_CROSS_UP_STRICTthe cross AND thatboolean — the one you attach to an alert
This is also the answer to "the two EMAs must be at least N apart". There's no inline arithmetic in Quick Create (EMA(50) > EMA(200) + 2 won't parse) — you express it by building the difference as a getter, then comparing that getter to a number. Three small expressions instead of one impossible line.
Q How do I compose a complex expression?

Build bottom-up:

  1. Define the atomic value getters first as named, reusable helpers (e.g. a named "RSI 1h" or "ATR band").
  2. Compose the upper levels on top by referencing those helpers instead of re-typing them.
  3. Combine with AND / OR to express multi-condition logic.
For deeply nested logic, the Expression Creation Schema (used by JSON Import) is the most powerful path — NLP stays flat, complex goes through JSON.
Q How do I view / inspect an expression (and see its JSON)?

Click an expression in the left library to open the Expression Viewer. It shows the full definition — what it reads, the operator, the symbol/timeframe, and any helpers it references.

  • Copy JSON — copy the raw expression JSON (useful to share, version, or feed to an AI model).
  • Export — save the definition out (see Share / export).
  • Site link — get a link back to the expression.
Expression Viewer with Copy JSON🖼️Expression Viewer with Copy JSONscreenshot: viewer modal — definition + Copy JSON / Export buttons
Reading the JSON is the fastest way to learn how complex expressions are wired — it shows exactly which helpers feed the top-level condition.
Q How do I test an expression's live value before I save it?

As you build, the Expression Preview panel evaluates your expression and shows the result so you can sanity-check it before saving.

  1. Build or load the expression in the builder.
  2. Look at the Expression Preview panel — it reads "Build your expression above to see a preview here" until the expression is valid, then shows the evaluated value/result.
  3. If the preview looks wrong, fix the operands and watch it update.
Expression Preview panel🖼️Expression Preview panelscreenshot: live preview / evaluate result
A valid preview is the green light that the expression will work in a strategy or alert.
Q How do I edit, duplicate, or delete an expression?

Each item in the left library has three icons:

  • ✏️ Edit — load it back into the builder to change it.
  • 📋 Duplicate — make a copy to tweak without touching the original.
  • 🗑️ Delete — remove it from your library.
Deleting frees a slot against your saved quota (the 8/100000 saved counter in the header).
Q How do I share or export an expression?

Open the expression in the Expression Viewer and use:

  • Copy JSON — paste the definition anywhere (chat, docs, another account) and re-import via JSON Import.
  • Export — save the expression's JSON out as a file. There's also Export all expressions to back up your whole library at once.
About Community publishing: the Community library is curated — there isn't a one-click "publish to Community" button in the current build. To share publicly, export the JSON and send it, or contact support@gts-trade.com. Note: private expressions are never published automatically — only when you deliberately export/share them.
Q How do I create and control expressions remotely via the API?

Everything you do in the Builder you can also do programmatically with your API key (Settings / Account → API). The versioned endpoints:

ActionEndpoint
Create an expressionPOST /api/v1/expressions
List your expressionsGET /api/v1/expressions
Validate without savingPOST /api/v1/expressions/validate
Update an expressionPOST /api/v1/expressions/update
DuplicatePOST /api/v1/expressions/copy
Pause / unpausePOST /api/v1/expressions/pause · /unpause
DeletePOST /api/v1/expressions/delete
Browse communityGET /api/v1/community/expressions

Full request/response shapes and auth: API Docs.

Pair this with the JSON schema link to have an external AI model generate expressions, then POST them straight to /api/v1/expressions.

3 Using an expression as an Alert

On the Free plan, check which alert path you are on before you build. Free is capped at 1 active alert and 5 saved expressions in total — and remember a compound rule costs several of those (a cross saves three: two legs plus the combiner), so one signal can consume most of the library. The important distinction is which handler you attach: Alert-only handler — the expression notifies you directly, with no position. This is the "custom expression alert" the Plans page prices per tier. Trading handler — the expression drives an open or close, and the built-in open/close position alert delivers the notification when that position happens. Free includes this path, so an expression can reach you on Free — through the position, not standalone. If you build an expression, attach it to an alert-only handler on Free and nothing arrives, this is why. Use a trading handler, or check the Plans page for your tier's exact numbers.

The simplest use of an expression: get notified when it becomes true, without any trading.

Q How do I turn an expression into an alert?
  1. Make sure the expression is marked as an ALERT (not just a helper).
  2. Attach it to a strategy/handler whose action is alert / notify rather than open-position.
  3. Deploy it. It now evaluates on live data and notifies you when true.
Want alerts only and never trades? See "alert-only / report-only" below.
Q Where do alerts get delivered?

Every live alert fire is captured in the Signal Monitor database-backed feed/table, including expression, strategy, symbol, time, delivery status, and channel results. Additional delivery channels are configured in your account/settings.

Signal Monitor feed🖼️Signal Monitor feedscreenshot: live fires list
Q How do I make something alert-only / report-only (no trade)?

Choose a handler action that notifies but never issues an order. In strategy terms this is a report/alert-only handler — it reports the fire and stops there, so no position is opened even on a real account.

This is the safe default while you're testing a new idea.
Q How do I pause or stop an alert?

Un-deploy / stop the strategy that carries the expression (from My Strategies or Monitor). The expression stays saved in your library; it just stops evaluating.

4 Strategies, handlers & positions

A strategy is the per-symbol runtime entity that owns handlers, trading parameters, risk management, and reporting. Handlers emit signals; the strategy decides what those signals mean using the user-set parameters. It can trade, close dynamically, report account state, or run alert-only with no position created.

Q How do I build a strategy?
  1. Open Builder → Strategy & Handler.
  2. Pick the symbol. The timeframe is derived from the expressions you select — a default is used only if it can’t be derived.
  3. Add the handlers you need: open buy, open sell, close buy, close sell, or alert-only/no trade. Any operation can be left empty.
  4. Set the strategy-level parameters: units, max open positions, SL/TP, trailing, hedging, limits, and reporting/alert behavior.
  5. Name and Save it — it appears under My Strategies.
Strategy & Handler builder🖼️Strategy & Handler builderscreenshot: Strategy & Handler sub-tab
Q How do I view / inspect a strategy?

Open My Strategies to see your library. Each strategy shows its symbol, timeframe, the expressions it uses, its handlers, and its current status (saved / deployed). Click one to inspect the full definition.

The strategy library has source tabs so you can also browse strategies that aren't hand-built:

  • Config — your own saved strategies.
  • Community — strategies shared by others.
  • Permutation — strategies generated by a Mass / permutation session.
My Strategies library🖼️My Strategies libraryscreenshot: strategy list with Config / Community / Permutation source tabs + status
Q How do I edit or duplicate a strategy?
  1. From My Strategies, open the strategy and choose Edit — it loads back into the Strategy & Handler builder.
  2. Change the symbol/timeframe, swap the arm expressions, or adjust the strategy parameters (position size, SL/TP, hedging, trailing).
  3. Save to update it.

To experiment without touching the original, Duplicate it first and edit the copy.

Editing a deployed strategy: stop/undeploy it first, make your changes, then re-deploy — otherwise the live run keeps the old definition.
Q How do expressions drive a strategy? (the 5 handler modes)

This is the key idea. A handler watches its inner expression list and the relation between those expressions (for example AND / OR / sequence). When that handler evaluates true, it emits the signal/action configured on the handler.

Handler modeWhat happens when its expression relation is true
Open BuyEmits an open-buy signal and, on a paper trading strategy, opens the buy-side position.
Open SellEmits an open-sell signal and, on a paper trading strategy, opens the sell-side position.
Close BuyEmits a close-buy signal and closes the buy-side paper position when one is open.
Close SellEmits a close-sell signal and closes the sell-side paper position when one is open.
Alert only / no tradeEmits a signal/notification only. No position is created or closed. Use this for account alerts, report handlers, and strategies that should fire without trading.

You map saved expressions into the handler, choose the relation between them, then choose the handler mode. The rule is identical for all modes: when the handler expression relation evaluates true, that handler emits its configured signal/action. Alert-only is a real handler mode, not a trading operation.

  1. Open Builder → Strategy & Handler.
  2. Add the expressions the handler should watch and choose the relation between them.
  3. Choose the handler mode: open buy, open sell, close buy, close sell, or alert-only/no trade.
  4. For trading modes, set the strategy trading controls. For alert-only handlers, configure notification/report behavior and Save.
The handler mode boxes🖼️Handler modesscreenshot: Open Buy / Open Sell / Close Buy / Close Sell / Alert-only handler options
Handler vs strategy — what lives where: a handler carries its expression list, relation, and configured mode. The strategy is the entity that applies trading/risk logic to handler signals: position size, max open positions, SL/TP, hedging, trailing, limits, static exits, dynamic close logic, and reporting. Each operation can have separate expression logic or be left empty. Alert-only/no-trade handlers can still fire signals and reports without creating positions.
An expression must exist and validate before an arm can use it — the builder enforces this. The same expression can be reused across arms or strategies.
Q How does a position get opened?

You don't open it by hand — the engine does it for you. That's the whole point of algotrading: the strategy triggers the position automatically when your condition becomes true. There's no manual "buy" step.

  1. Put your entry expression in the Open Bid or Open Ask arm (whichever side you want).
  2. Deploy the strategy to a trading account.
  3. When that arm's expression evaluates true, the engine opens the position automatically — on the GTS paper-trading account (no real order is ever placed).

How big, and with what risk controls, is decided once at the strategy level — position size, SL/TP, hedging, trailing — not on the arm.

Alert vs trading: on an alert deployment no position is ever opened — you only get notified. Positions are only triggered on a trading deployment. Same expression, different action.
On crypto, fractional sizing (e.g. 0.01 units) may be needed — set the strategy size accordingly and check your symbol's minimum.
Q How do I close a position?

Closing works exactly like opening — you just use the Close Bid / Close Ask arms instead. Any expression you set in a close box can trigger the close; there is no special "exit expression" type.

  1. Put the condition you want to close on (e.g. RSI(14) > 70 or an account-P&L condition) into the Close Bid or Close Ask box.
  2. When that expression evaluates true, that side's position is closed automatically.

Because it's the same mechanism as open, you can close the bid side and the ask side dynamically and independently, each by whatever expression you choose. (Strategy-level SL/TP and trailing also close positions automatically — those are set once on the strategy, separate from the close arms.)

A complete strategy usually fills both an open arm and a matching close arm — otherwise positions only close via strategy-level SL/TP, or not at all.
Safe switch — you can always close on the exchange: regardless of what the strategy is doing, you can close a position directly on the exchange yourself at any time. Our system gets notified of that change and reconciles its state correctly — your strategy and Monitor stay in sync with what actually happened on the exchange.
Q What are Mass / Permutation strategies for?

The Mass Strategies tab generates many strategy permutations at once: expression-handler wiring, symbol/range sets, and parameter sweeps such as RSI thresholds, SL/TP, units, or other user-defined variables. It is how you test a whole grid of ideas instead of building each strategy by hand.

Mass Strategies tab🖼️Mass Strategies tabscreenshot: permutation builder
Q How do I manage risk and more than one position?

A strategy is not one position. Max Positions lets it hold several at once, and every one of them is governed by the same strategy-level risk settings — you set the rule once, it applies to position 1 through position n.

The strategy-level fields (Builder → Strategy & Handler):

FieldJSONWhat it does
UnitsUnitsSize of each position. 0 loads the strategy but places no trades — valid for alert-only.
Max PositionsMaxNumPositionsHow many positions this strategy may hold at the same time. 1 = single position; higher = a ladder.
Stop LossStopLossFixed stop applied to every position the strategy opens.
Take ProfitTPFixed target, same deal — one setting, every position.
TP Tick RevTPTickRevValReversal close: give back this many ticks against your direction and the position is closed. Requires TP > 0 — saving it without a target is rejected.
Tick Diff OpenDiffTicksFromLastOpenMinimum distance from the last entry before another position may open. Only meaningful when Max Positions > 1.

These numbers are in ticks. The engine measures the move as (price − open price) / tip, so Stop Loss, Take Profit and Tick Diff are all tick counts — which is what makes one setting portable across instruments. (The engine also supports a percent mode for the same thresholds, but it is not exposed in the builder or the API today, so what you set here is ticks.)

If you want a percent stop or target instead, express it as a close handler on LastOpenPositionObject.CurrentProfitPercent — that field is already a percent of the position's own open price, and it works on both sides.

Each position is tracked separately. The stop manager runs per position id, so on a 5-position ladder each entry gets its own stop and target measured from its own open price — not from the average.

Spacing the entries (using the distance between positions)

Tick Diff Open is the built-in version: price must travel N ticks away from the last entry before the next one opens, which stops a ladder from dumping all its positions on one candle. For anything more specific, build the distance yourself as an expression and put it in the Open handler — e.g. the live price against LastOpenPositionObject.OpenPrice, converted to ticks with Symbol.TicksInPoint, so the spacing rule holds on every symbol.

Closing on any expression you can build

SL/TP are the fixed floor and ceiling. Everything else is an expression in a close handlerClose Bid (CloseHandler1) and Close Ask (CloseHandler2). Whatever you can express, you can close on:

  • Profit in percentlast_pos_currentprofitpercent < 0.1 closes when the position gives back below 0.10%. Positive always means winning, for BID and ASK alike, so one expression covers both sides.
  • Profit in tickslast_pos_currentprofitticks > 50.
  • Agelast_pos_currentdurationminutes > 60 for a time-based exit.
  • Anything else — a pattern, a trend flip, a channel break: if it saves as an expression, it can close a position.
Guard a bare profit threshold. A new position starts at ~0% profit, so CurrentProfitPercent < 0.1 is already true the moment it opens and would close it immediately. For a real "dropped below" rule, pair it with proof the position got there — the peak (unary MAX) at or above your level, AND the current value under it. MIN will not do this: it reports the worst point, which never shows that profit had risen.

Worked example — a percent take-profit and stop, on both sides

Two expressions, each saved once, then dropped into both close boxes with OR. Because the percent field is signed by side, the same pair governs the long and the short:

ExpressionQuick CreateDoes
POS_TP_0_2profit_percent >= 0.2Target — position is up 0.2% of its entry price
POS_SL_0_2profit_percent <= -0.2Stop — position is down 0.2%

Close Bid = POS_TP_0_2 OR POS_SL_0_2. Close Ask = the same two. Set Stop Loss and Take Profit to 0 if this pair is the only exit you want, and remember nothing else will take you out.

Check the operator, not the name. An expression named ..._LTE_MINUS_0_2 can still be built with >= — the suggested name stops tracking the builder once it has been edited by hand. Get this one backwards and the stop reads profit >= -0.2, which is true at entry (profit starts at ~0), so the OR is always true and every position closes the instant it opens. Open the expression and confirm the operator button before deploying.

What to read when deciding

OperandAnswers
LastOpenPositionObjectThe most recent open position — profit in ticks or percent, duration, open price, side, units.
StrategyOpenPositionsThe whole open collection — TotalPositions, TotalUnits, AverageOpenPrice, OpenPNL, optionally per side. Use it to decide whether to add another rung or close the book.
ClosePositionObjectThe last closed position — realized profit, close price, duration. Use it to decide whether to re-enter or stand down after a loser.
AccountObject / AccFrameObjectAccount-level metrics (balance, open PnL, equity, per period). Use these as the global gate — stop opening anything once the account is down for the day.

These are ordinary operands, so they combine with AND/OR like any other condition: add a rung only while fewer than 3 are open AND the account's open PnL is above −500.

Q How do I deploy a strategy?
  1. From My Strategies (or the builder), choose Deploy.
  2. Confirm. It begins evaluating on live data; opened/closed positions and alerts appear in Monitor.
Where trades go: execution always lands on the GTS paper-trading account — the default, and the only place trades happen. GTS does not place live orders on a real exchange. Connecting a real exchange (next sections) adds account monitoring + alerting, it does not redirect your trades to a real broker.
Q How do I stop / undeploy a strategy?
  1. Go to My Strategies or Monitor and find the running strategy.
  2. Choose Stop / Undeploy. It stops evaluating live data and stops issuing alerts/orders.

The strategy definition stays saved — you can re-deploy it later. Undeploying is the safe way to pause something that's misbehaving.

Open positions are not auto-closed when you undeploy. If you have a live position you don't want left running, close it first (via an exit/close handler or directly on your exchange).
Q Why is my strategy limited when a real exchange is active?

Because GTS never places live orders, a strategy running against a real-mode exchange may only use account-monitoring expressions (e.g. account balance, margin, open P&L). There are no buy/sell orders to place on a real account — only metrics to watch and alert on. A safety guard at deploy time enforces this. Common causes of a block:

  • The strategy uses trade/entry expressions that aren't valid against a monitor-only real account.
  • The provider isn't connected, has no valid credentials, or isn't Set Active.
  • The strategy bundle didn't pass validation (missing handler/expression pieces).

Want order-opening behaviour? Run on the default GTS paper-trading account — paper execution has no such restriction. Use real-mode providers for monitoring + alerting on your actual account.

Q How do I delete a strategy?
  1. If it's deployed, stop/undeploy it first (see above).
  2. From My Strategies, choose Delete on the strategy.
Deleting stops its alerts/positions for good. Close any open positions on your exchange first if you don't want them left running. The expressions it used stay in your library — only the strategy is removed.
Q How do I share or export a strategy?

Use Export all strategies to save your strategies out as JSON — a portable backup you can re-import or hand to someone else.

Like expressions, there's no one-click "publish to Community" button in the current build — the Community strategy library is curated. To share publicly, export and send the JSON, or contact support@gts-trade.com. A shared strategy must carry its full set of referenced expressions or the import will be blocked (see §8).
Q How do I create and control strategies remotely via the API?

Everything in the Strategy builder is available programmatically with your API key:

ActionEndpoint
Create / save a strategyPOST /api/v1/strategies
List your strategiesGET /api/v1/strategies
Save a strategy + handlerPOST /api/v1/strategy-handler
Get the strategy JSON schemaGET /api/v1/strategy-schema
DuplicatePOST /api/v1/strategies/copy
DeletePOST /api/v1/strategies/delete
Deploy (one / many)POST /api/v1/deploy · /deploy-batch
List deployed strategiesGET /api/v1/deployed

Full request/response shapes and auth scopes: API Docs.

Fetch /api/v1/strategy-schema, give it to an AI model, and have it generate a valid strategy JSON you POST straight to /api/v1/strategies — the same pattern as expressions.

5 Monitoring & history

See what's firing now, and test how a strategy would have behaved in the past.

Q How do I see my live strategies and fired signals?

Two views:

  • Signal Monitor — database-backed alert-fire tables/feed for live conditions and strategies, including delivery status and channel results.
  • Monitor — the deep operational view: running strategies, open positions, P&L, status.
Monitor page🖼️Monitor pagescreenshot: running strategies + positions
Q How do I run History Requests and read the results?
  1. Open History Requests.
  2. Select one or many strategies to test, including Mine / Community strategies or generated permutation packs.
  3. Set the allowed history range and run options based on your plan/quota.
  4. Submit the run. GTS stages the relevant strategy definitions into the history runtime and executes them as a batch. Submit = start, Cancel = stop.
  5. Watch status, then open the reports to compare trades, signals, PnL, win rate, profit factor, drawdown, and top permutations.
History runs are batch jobs and may be queued; they're separate from live deployment.
Q How do I view Charts?

Open Charts from the nav for price charts, or SEC Edgar for company filings/fundamentals research. These are read-only research tools — they don't change your strategies.

6 Quotas, limits & plans

Every plan has limits. The good news: the app shows them to you right in the header.

Q How do I know my quota?

Look at the top-right of the header — it shows live counters, e.g. 8 / 100000 saved and 2.4k / 100k fires. The kinds of quota:

QuotaWhere to see it
Saved expressions/strategiesHeader X/Y saved counter.
Configured expression alertsA separate limit from the one above — how many saved expressions you may wire up as live alerts.
Signal firesHeader X/Y fires counter.
AI tokensUnder the Generate-with-AI box (0 / 1,000,000 used on the max plan).
History runsOn the History Requests page.
Full breakdownAccount → Usage tab.
Q What are the Plans and what does each include?
Two limits, often confused. Saved expressions is storage — everything in My Expressions, including helpers and getters that never fire on their own. Configured expression alerts is how many of those you may run as live alerts. They are counted separately, and the alert limit is the smaller of the two on every tier. Hitting one does not mean you have hit the other.
Composition costs storage — budget for it. Because GTS builds bottom-up, one logical rule is often several saved expressions. A cross is the clearest case: EMA(50) crossed EMA(200) saves three — the previous-bar leg, the current-bar leg, and the AND combiner. A calculated threshold is similar: a getter for each input, one for the difference, one for the comparison. On a small plan a single compound rule can consume most of the saved-expression allowance, so count the parts, not the rules. See value getters and the seven-route example.
An expression only needs an alert slot if you want it to notify you directly. Expressions consumed as building blocks — legs, getters, helpers referenced by name inside a bigger expression — occupy storage only. And an expression driving a trading handler works through the strategy, which is a different path from a standalone expression alert. Check the Plans page for the exact numbers on your tier; the two counters are shown separately in the header.

Open the Plans page from the nav to compare tiers and what each unlocks (limits on saved items, fires, AI tokens, history runs, real-account trading, API access). Upgrade/downgrade from there or the Account → Billing tab.

AI tokens — what each plan includes

Every paid plan comes with a free daily AI-token allotment that resets each day (a per-plan staircase — higher tiers get more, up to 1,000,000/day on the max plan). The Free plan includes no AI tokens, so use the always-free Quick Create there. Tokens are spent only by Generate with AI and in-app AI JSON generation — never by Quick Create, hand-building, or pasting your own JSON.

Need more than your daily amount? Upgrade to a higher plan or wait for the daily reset. The live Plans page shows the exact daily amount for each tier.

Q What happens when I hit a limit or downgrade?

If you exceed a cap (or drop to a smaller plan), over-cap saved items aren't deleted — they're archived to a "Previous-Config" area and retained (90 days). Your live items keep working up to the new cap.

Archived items are read-only until you make room or upgrade. Nothing is silently destroyed.
Q How do I upgrade or restore archived config?
  1. Upgrade your plan from Plans or Account → Billing.
  2. Open the Previous-Config tab to review archived items.
  3. Restore the ones you want back into your active library (within the 90-day window).

7 Settings, account & deletion

Q What is the Settings page for?

Settings is where you configure the operating environment around your strategies and alerts:

TabWhat it controls
ChannelsNotification delivery: Telegram, email, Discord, browser push, HTTP/webhook endpoints, and related channel credentials.
ProvidersBroker/data-provider credentials, demo/real mode, and which provider is active for account monitoring and supported runtime operations.
API KeyAgentic access for Codex, Claude, IDE scripts, CI, or external services using /api/v1. Treat the key like a password.
Runtime LimitsStatic risk-management limits saved before the runtime starts: per-account, per-symbol, and per-strategy caps for units, positions, loss, margin, and minimum balance gates.
Runtime limits are applied by the engine on the next runtime start/restart. They are not retroactively injected into an already-running process.
Q How do I connect an exchange, and what does it do?

Open Settings → Providers. Supported providers: Bybit, FXCM, Binance, OANDA. Credentials are stored per-user.

  1. On the provider you want, click Configure and enter your API credentials. Pick the mode: Demo or Real (Run Mode).
  2. You must explicitly turn it on — flip Set Active on that provider's card. (Set Active stays disabled until valid credentials are saved.)
  3. Only one provider is active at a time — activating one switches the others off. Turning the active one off returns you to the GTS default stream + paper trading.
Settings → Providers🖼️Settings → Providersscreenshot: Bybit / FXCM / Binance / OANDA cards with Configure + Set Active
What connecting does — and the one thing it does not do. A connected provider lets GTS watch your account (balances, margin, positions and P&L) and alert you on any change. Your strategies never place live orders on a real account. At deploy time GTS forces EnableLiveTrading="False" for real broker accounts and coerces every handler to alert-only, and the engine refuses to publish an order while that flag is off. Automated execution happens on the paper account only. The single exception is a manual trade you issue yourself from the Manual Trade dialog or the trade API — that is a deliberate, per-order action, and in real mode it must be explicitly confirmed. A manual real-mode order places a live trade with real funds at risk. Nothing automated will ever do this for you.
ModeWhat happens
No provider (default)GTS default stream + GTS paper-trading account. Strategies open/close positions internally. Nothing real.
Provider — Demo modeLive trading on the provider's demo account — strategy-automated or manual — plus monitoring & alerts. No real funds at risk.
Provider — Real modeMonitoring & alerts on balances/P&L, plus manual buy/sell orders you issue and confirm yourself (real funds at risk). Strategies are alert-only on a real account — automated orders are blocked at deploy.
GTS never holds or custodies your funds — they stay at your broker. Your API keys are used to read account state and to place the orders your strategies (or you, manually) trigger, including live futures trades.
Q How do I manage my account, security, and billing?

Click your avatar (top-right) to open Account. Tabs: Overview, Usage, Security (password / sessions), Billing (plan / invoices), and History.

Q How do I delete an expression, a strategy, or my account?
  • Expression / strategy — use the 🗑️ delete icon in its library list.
  • Deployed strategy — stop/un-deploy it first (Monitor / My Strategies), then delete.
  • Your account — Account → Security/Billing, or contact support@gts-trade.com.
Deleting a deployed strategy stops its alerts/positions. Close any open positions on your exchange first if you don't want them left running.
Q How do I use the API?

For terminal, IDE scripts, AI agents, Postman, CI, or another backend, use your personal API key with the versioned /api/v1/* API. Send it as X-API-Key: gts_... or Authorization: Bearer gts_....

Browser session cookies are only for the logged-in website UI and same-site browser JavaScript. They are not the normal CLI/agent integration path.

Open API Docs from the nav for examples to create expressions/strategies, run history requests, deploy, and pull stats programmatically.

Generating expression JSON via an external model? Point it at the schema link from JSON Import.

8 Troubleshooting

Q My expression won't parse or validate — why?
  • Check the LEFT OPERATOR RIGHT shape — a missing operand is the usual cause.
  • Candle patterns need a trend and compare > 0 (not == 100).
  • Decimal indicator params (e.g. SAR(0.02,0.2)) must use function syntax, not dot-notation.
  • Still stuck? Try Generate with AI — it validates as it builds — or check the NLP reference.
Q Why is a Community import "blocked / missing deps"?

The shared expression references helper building-blocks that weren't bundled when it was published, so its dependency closure is incomplete. It's a data issue with that shared item, not your account.

Pick a different community item, or rebuild the logic yourself with the four creation methods in §2.

Q My strategy didn't fire or didn't trade — why?
  • Not deployed, or deployed to the wrong account (paper vs real).
  • The condition simply wasn't true yet on live data — check Signal Monitor.
  • Real-account guard blocked the trade — see §4.
  • No exit path, so a position opened but never closed (or vice-versa).
  • You hit a fires/quota cap — check the header counters.
  • The handler is alert/report-only, so it notifies but never trades by design.