Instruments, groups and sessions

The tradable catalog and its lifecycle, symbol groups as your dealing-desk economics, account tiers with margin call and stop-out levels, and trading-hours calendars.

6 min readUpdated 3 August 2026instruments, symbol-groups, sessions, leverage, spread, swap

Four tables decide what a customer can trade and what it costs them.

  • Instruments — the catalog. One row per tradable symbol, with its contract specification and its lifecycle status.
  • Symbol groups — your economics. Leverage, spread markup, commission, swap policy and which calendar applies. Every instrument belongs to one.
  • Account groups — your risk tiers. Margin-call level, stop-out level, negative-balance protection and a leverage cap. Every account belongs to one.
  • Session calendars — when a market is open. Attached to symbol groups.

Get these right and the desk mostly runs itself. Get them wrong and you either have no customers or no money.

The instrument catalog

Admin → Forex Trading → Market Data → Instruments

A symbol is written CURRENCY/PAIR: EUR/USD, XAU/USD for gold, AAPL/USD for an Apple CFD, SPY/USD for the S&P proxy. The combination is unique.

Each row carries a metadata block holding the contract specification, which is what turns an order size into money:

Field Meaning
contractSize Units in one standard lot — 100,000 for FX, 100 for gold, 5,000 for silver, 1,000 for energy, 1 for equities
pipSize / pointSize The pip and the point, in price terms. JPY pairs use 0.01 / 0.001; everything else 0.0001 / 0.00001
digits Price precision shown in the terminal
limits.amount Minimum, maximum and step size in base units
stopsLevel The minimum distance, in points, a stop loss, take profit or pending order may sit from the market
delayed True for seeded stocks and indices — display-only unless you opt in

Pip value and point value are deliberately never stored. They are derived at use time from pointSize × contractSize in the quote currency and converted at the current rate, because storing them would make every value wrong the moment the market moved.

swapLong and swapShort sit on the instrument, in points. Negative means the customer is charged, positive means credited.

Two display flags, isTrending and isHot, do more than decorate the markets rail: the tick engine keeps those symbols subscribed even when nobody holds a position in them.

The lifecycle

Status is not a boolean, because "off" while a customer holds a position is not a state you want to be able to reach.

Status Meaning
INACTIVE Imported, invisible to customers, not tradable
ACTIVE Fully tradable
CLOSE_ONLY No new positions and no margin-increasing orders; existing positions can still be closed
HALTED Temporary freeze — a corporate action, a provider outage, a news event
DELISTED Terminal. Nothing reaches this except an empty CLOSE_ONLY instrument

Allowed moves are exactly:

INACTIVE   -> ACTIVE
ACTIVE     -> CLOSE_ONLY | HALTED | INACTIVE
CLOSE_ONLY -> ACTIVE | HALTED | DELISTED
HALTED     -> ACTIVE | CLOSE_ONLY
DELISTED   -> (nothing)

Two guards apply:

  • Activating requires a provider symbol mapping for the currently active provider. A mapping belonging to a different, inactive vendor is refused with the list of vendors that are mapped, because such instruments activate cleanly and then quote nothing.
  • INACTIVE and DELISTED are refused while open positions exist. The error names the count. Route through CLOSE_ONLY and wait.

ACTIVECLOSE_ONLY → watch the position count fall to zero → DELISTED. Customers keep the ability to exit the whole time. Trying to shortcut it just produces a 400.

Symbol groups — your economics

Admin → Forex Trading → Market Data → Symbol Groups

Everything a fill costs a customer, and therefore everything it earns you, is here.

Maximum leverage for instruments in this group. Effective leverage is the minimum of this, the account's own setting and the account tier cap.
Total spread widening in pips. Half is added to the ask, half subtracted from the bid.
Commission in account currency per standard lot per side, charged in full at open (round-turn priced).
Fraction of margin charged on the covered leg of a hedged position. 0 means a perfectly hedged pair costs nothing extra.
Percentage markup applied on top of the instrument's swap points.
The weekday carrying the triple swap charge — WED for FX and metals, FRI for indices and equity CFDs.
WEEKDAYS charges swap Monday to Friday; ALL charges every day, for 24/7 crypto CFDs.
Whether swap-free (Islamic) accounts skip swap on this group.
Trading-hours calendar. Null means 24/7.

Three of these deserve elaboration.

Spread markup is your revenue line. With spreadMarkupPips at 1 on EUR/USD, a customer buying pays the feed's ask plus half a pip and sells at the feed's bid minus half a pip. On a standard lot that is roughly $10 round turn. Set it to 0 and you are quoting raw broker prices with nothing in it for you.

Hedged margin rate decides whether hedging is free. At 0, a customer holding one lot long and one lot short of the same symbol pays margin on the larger side only — the classic MetaTrader hedged-margin behaviour. Raise it towards 1 and the covered volume starts costing margin too. The formula the engine uses per symbol is max(long, short) / leverage + hedgedMarginRate × min(long, short) / leverage.

Commission is charged in full at open. It is priced as a round turn, so there is no second charge at close. commissionPerLot is in the account currency, not the quote currency.

The marginCurrency column is a reserved no-op. Margin is always computed in the instrument's quote currency and converted to the account currency at the current rate; the column is kept for schema compatibility and is not accepted by the admin API.

Account groups

Account groups are your risk tiers. Admin → Forex Trading → Trading → Accounts shows the accounts; the tiers behind them are fx_account_group rows.

Nothing creates a default tier. An account with no group falls back to a margin-call level of 100% and a stop-out level of 50%, and no tier leverage cap applies. That is a workable ESMA-ish default, but it is a fallback, not a decision. Create your tiers and mark one defaultForType: LIVE and one defaultForType: DEMO so new accounts land somewhere on purpose.

Margin level percent below which the margin call fires — the customer is notified and blocked from adding risk or withdrawing.
Margin level percent below which forced liquidation runs, largest losing open-session position first. ESMA preset 50; offshore presets 20-30.
Zero a negative balance after full liquidation, booking the deficit against operator P&L as an NBP_CORRECTION deal.
Account-tier leverage cap. Effective leverage is the minimum of this, the symbol group's leverage and the account's own setting.
Auto-assign this group to newly created accounts of the given type (DEMO or LIVE).

Margin level is equity / usedMargin × 100. At 100% the customer's equity exactly covers their requirement. At the margin-call level they get an email and an in-app notification (FxTradingMarginCall), and the platform refuses any order that would increase margin, plus any withdrawal. At the stop-out level the risk engine starts closing positions — largest loser first, only positions whose session is open — and re-checks after each close, up to ten in one sweep.

Negative-balance protection runs after everything is closed and only if the balance is still below zero. It writes an NBP_CORRECTION deal that zeroes the account. That deficit is yours.

Session calendars

Admin → Forex Trading → Market Data → Calendars

A calendar is a timezone, a list of weekly session windows and a list of holidays. A window is openDay + openTimecloseDay + closeTime, with day 0 being Sunday.

Three ship with the seed catalog, all anchored to America/New_York:

Calendar Windows
FX 24/5 Sunday 17:00 → Friday 17:00, one continuous window
US Stocks RTH Monday–Friday 09:3016:00
CME Metals & Energy Sunday–Thursday 18:00 → next day 17:00

Attach null for a 24/7 market — that is what the Crypto CFDs group does.

The calendar is authoritative, not advisory. When it says closed, the tick engine stamps the symbol SESSION_CLOSED, market orders and closes are refused with "Market is closed", and the terminal shows the next open time. Pending orders may still be placed while closed — they park and trigger on reopen ticks — with one exception: a DAY order needs a session close to expire against, so it can only be placed while the session is open.

Deleting a calendar is refused while any symbol group still references it.

Nothing populates the holiday list. If you leave it empty, Christmas Day is an ordinary trading day as far as the engine is concerned — the desk will quote whatever thin, gapped prices the provider streams, and fill against them.

Overnight swaps

Financing settles at 17:00 America/New_York, and the swap cron runs hourly rather than once a day. Every run settles the most recent elapsed 17:00 cutoff plus any earlier ones missed during an outage, up to seven days back. Each (position, rollover date) pair is idempotent, so running every hour is safe.

The amount is derived from the instrument's swapLong or swapShort points, the position size and contract size, then the group's swapMarkupPercent, then a day multiplier — 3 on the group's triple-swap day, 0 on days the group does not charge. The result is converted into the account currency and booked as a SWAP deal.

Converting the quote currency into the account currency routes through a USD hub pair, and those pairs must be ACTIVE and streaming. A EUR-denominated account holding GBP/JPY needs USD/JPY and EUR/USD listed and quoting. If they are not, the rollover cannot be converted, is retried hourly, and then expires uncharged after seven days. The engine pins the missing legs to the tick stream and raises a critical swap-conversion-unavailable alert when this happens — but only if those instruments exist in your catalog at all.

Accounts flagged swapFree skip the charge on groups where swapFreeAllowed is true. That combination is how you offer Islamic accounts without exempting them from crypto CFD financing.

Economic calendar and news

Two optional feeds render in the terminal.

Admin → Forex Trading → Market Data → Economic Calendar and Market News both hold two kinds of row: PROVIDER rows written by the 15-minute sync, and MANUAL rows you author. Manual rows are permanently exempt from the sync, which is how a desk runs a usable calendar with no paid calendar feed at all.

Content edits to a provider-sourced row are not durable — the next sync refreshes rows whose published figures moved. To suppress a bad provider row permanently, set its status to hidden rather than editing or deleting it; the sync never writes status, but it will happily re-insert a row you deleted.

Calendar → Feed status tells you why the tables look the way they do: the last sync's outcome per feed (OK, NOT_IMPLEMENTED, PLAN_LIMIT, ERROR), which provider served it, and which of your configured providers could serve it at all. Neither feed is bound to the active quote provider — a Finnhub key alone is enough for news.

Next