Running the desk
The operator's daily screens — the risk desk, client accounts and the kill-switch, orders and positions, the immutable deals ledger, manual adjustments and withdrawal approvals.
The admin lives at /admin/forex-trading and is organised the way a desk
actually works: the risk desk first, then the book, then the money, then setup.
| Screen | Path | What it answers |
|---|---|---|
| Risk Desk | /admin/forex-trading |
Am I about to lose money |
| Accounts | /admin/forex-trading/account |
Who is trading and can I stop them |
| Positions | /admin/forex-trading/position |
What is open right now |
| Orders | /admin/forex-trading/order |
What is waiting to happen |
| Deals Ledger | /admin/forex-trading/deal |
Where did the money go |
| Withdrawals | /admin/forex-trading/withdraw |
What is waiting on me |
| Execution | /admin/forex-trading/execution |
Is my hedging healthy |
| Market Data | /admin/forex-trading/provider and below |
Setup |
| Settings | /admin/forex-trading/settings |
Policy |
The Risk Desk
This is the B-book survival dashboard, and on a desk carrying its own risk it is the screen to leave open.
Net exposure by symbol. Client longs minus shorts, in lots and USD notional, per instrument. On a pure B-book install every unit of that is your exposure — if clients are net long 40 lots of gold and gold rises, you pay. With A-book routing enabled the same block splits into the internal net (yours) and the external net (hedged one-for-one at a broker), plus the broker-side net and the delta between them, taken from the reconciler's exposure snapshot when it is fresh. A snapshot older than five minutes is not shown inline rather than masquerading as live data.
Accounts at risk. Every account near its margin-call or stop-out level, sorted worst first. Two things put an account on this list: a margin level approaching the tier's threshold, and holding a position on a symbol that is not currently quoting — the second one is the dangerous case, because that account's equity cannot be marked and its stop-out cannot fire.
Client balances against equity. Aggregate balance versus aggregate equity across the book. A widening gap in your favour is unrealised client losses; the reverse is unrealised client profit you will eventually have to pay.
Quoting health. Every instrument with its current state and the age of its last tick. This is where a dying feed shows up before customers notice.
Hedge monitor. A-book installs only. One card per enabled execution provider with its NAV, margin used, margin-use ratio, closeout percentage, how long ago it was synced, whether it is degraded, and how many alerts on it are unacknowledged. Providers that have been disabled but still carry open external positions appear here too, marked as draining — so "am I hedged" stays answerable mid-drain.
Everything on this screen is aggregated in SQL. No query returns a row per open position, which is what keeps it usable on a busy book.
The daily routine
-
Open the Risk Desk. Read net exposure first. If one symbol dominates, decide whether you are comfortable carrying it, or widen that group's spread, or route it externally.
-
Work the at-risk list. Anything holding positions on a non-quoting symbol is an incident, not a data point — fix the feed.
-
Clear the withdrawal queue. Approvals are the only step in the money path that waits on a human.
-
Check the alert inbox if you run A-book. Unacknowledged critical alerts are the ones that cost money.
-
Scan the deals ledger for
ADJUSTMENTandNBP_CORRECTIONentries you did not expect.
Client accounts
From the account row you can change:
- Trading enabled — the kill-switch. Setting it false blocks every new order on that account while still allowing closes. It is the correct response to suspected abuse, a disputed fill or a compliance hold, and it does not trap the customer's money.
- Leverage — the account's own knob. Remember effective leverage is the minimum of this, the symbol group and the account tier cap.
- Account group — move a customer between risk tiers.
- Withdraw limits — daily and monthly, per account.
- Swap-free — the Islamic-account flag, honoured on groups where
swapFreeAllowedis true.
Balance is deliberately not editable here.
Manual adjustments
Goodwill credits, error corrections and compensation all go through this
endpoint, which writes an ADJUSTMENT deal.
The account balance is a denormalisation of the deals ledger. The reconciler
verifies that the ledger sums to the balance every five minutes and raises a
critical ledger drift alert — by email, to every Super Admin — when it does
not. A direct UPDATE fx_account SET balance will set that alarm off within
five minutes and leave you unable to tell a real accounting problem from your
own edit.
Orders and positions
Order statuses you will see:
| Status | Meaning |
|---|---|
OPEN |
A pending order resting, waiting for its level |
TRIGGERED |
Level crossed, fill in progress |
ROUTING |
A-book only — intent journaled, awaiting broker confirmation |
FILLED |
Done |
CANCELLED |
Cancelled by the customer |
REJECTED |
Refused at trigger, usually on margin, or refused by the venue |
EXPIRED |
A GTD or DAY order that ran out of time |
Positions are OPEN, CLOSED or LIQUIDATED, and every close records why.
Six reasons are ever written:
closeReason |
Written by |
|---|---|
SL |
The risk engine, when the customer's stop loss is hit |
TP |
The risk engine, when the take profit is hit |
TRAILING |
The risk engine, when the trailing stop is hit |
STOP_OUT |
The same engine's stop-out loop; the position also ends LIQUIDATED |
ADMIN |
Force-close below, the orphan book late action, and the reconciler booking a close your broker made server-side |
MANUAL |
Everything else — it is the default in utils/engine/execution.ts for any close that does not name a reason, so an ordinary customer close lands here |
The validator on models/ext/forex-trading/fxPosition.ts also accepts GAP,
DELISTED and CORPORATE_ACTION. No code path produces any of them. GAP is
a member of the reason union in utils/engine/execution.ts with no caller;
DELISTED and CORPORATE_ACTION appear nowhere in the backend as a close
reason at all — DELISTED elsewhere is an instrument status, which is a
different column on a different table.
They are left in place because they are real enum values a row could carry, so
you may still meet one in the database. If you do, the platform did not write
it: it came from an import, another system, or a hand-edited row. A delisting
or a corporate action is handled today by force-closing the positions, which
stamps ADMIN.
Force-closing
Reserved for dispute resolution, toxic flow and corporate actions. It settles
through the same execution service a customer close uses, so the deals ledger
stays consistent and the close is stamped ADMIN. The customer sees the
position close at a real price with real P&L.
The deals ledger
This is the audit source of truth and it is append-only. Each row carries the executed price, the raw feed bid and ask at the moment of the fill, any conversion rate used, and the balance the account was left with.
The raw feed prices are the important part. When a customer claims they were filled at a bad price, the ledger shows both what the provider was quoting and what your markup made of it, in the same row.
| Deal kind | Written when |
|---|---|
OPEN |
A position opens |
CLOSE / PARTIAL_CLOSE |
A position closes, wholly or partly |
COMMISSION |
Commission charged at open |
SWAP |
Overnight financing at the 17:00 New York rollover |
DIVIDEND |
A dividend on a stock CFD, where the provider supplies them |
DEPOSIT / WITHDRAW |
Transfers to and from the platform wallet |
WITHDRAW_REVERSAL |
A rejected withdrawal refunded |
ADJUSTMENT |
An operator adjustment |
NBP_CORRECTION |
Negative-balance protection zeroing an account |
An NBP_CORRECTION is money you have written off. It should be rare; a pattern
of them means your stop-out level is too low, your leverage too high, or a
symbol is gapping past your stops.
Withdrawal approvals
The money has already left the trading account by the time you see the request — that happened at request time, under the free-margin gate. Your decision moves it the rest of the way, or puts it back.
- COMPLETED credits the destination platform wallet.
- REJECTED refunds the trading account with a
WITHDRAW_REVERSALdeal.
If the trading account's balance has gone negative since the request — the customer was stopped out while the withdrawal sat in the queue — approving is refused. The response names the deficit and tells you to pass an explicit confirmation to override. In almost every case the right answer is to reject: that refunds the trading account and covers the shortfall.
Approve and reject are guarded by a row lock, so two operators clicking at the same time cannot both apply.
Permissions
Every screen and endpoint is gated. Grant these under Users → Roles & Permissions; nothing is granted by default and Super Admin bypasses the check entirely.
A read-only risk analyst needs view.forex_trading.risk,
view.forex_trading.account, view.forex_trading.position,
view.forex_trading.order and view.forex_trading.deal.
A dealing-desk operator adds edit.forex_trading.account (the kill-switch),
edit.forex_trading.position (force close), create.forex_trading.adjustment
and edit.forex_trading.withdraw.
A setup administrator adds the instrument, group, calendar, provider and
settings keys. The full list is in the
settings reference.
fxTradingExternalRoutingEnabled is rejected with a 403 by the extension's own
settings endpoint no matter what permissions the caller holds — it is writable
only through the core system settings page, where the protected-keys check
enforces Super Admin. That is the switch that starts sending real client flow to
a real broker.
Next
- External execution — the A-book layer and its alert inbox
- Settings reference — keys, permissions and jobs
- Troubleshooting — symptoms and causes