# Venue controls and evidence

Source: https://docs.settlemint.com/docs/concepts/issuing-and-markets/venue-controls
How a Trading Venue checks admission, which roles change a running instrument and who must agree, and the FIX drop copy and surveillance files it produces.

A [Trading Venue](https://docs.settlemint.com/docs/concepts/issuing-and-markets/trading-venue) is a tenant's own exchange for tokenised assets. It checks a participant's admission on every new order and ties each change to a running instrument to a venue role. When the deployment enables its FIX drop copy, it also sends a surveillance provider each order and trade event as the venue records it. The Trading Venue page explains how an order becomes a delivered trade.

Admission is checked when an order arrives. Revoking a participant's admission does not cancel the orders they already have resting. Cancel those from the **Orders** screen. At intake the maker must be a participant the venue admits and, on a private instrument, a participant of that instrument. Removing a wallet from a private instrument cancels its open orders on that instrument and leaves one cancellation batch. An [instrument denial](https://docs.settlemint.com/docs/concepts/issuing-and-markets/venue-market-denials) is narrower than losing admission: it bars one wallet from one instrument, records the reason and who recorded it, and leaves the venue's other instruments open to that wallet.

Evidence is identity-linked, sequenced, and venue-scoped: the trail carries signed orders, book events, session transitions, fills, deliveries, and undos with their reason, each naming the venue it belongs to. The platform rebuilds a book's state by replaying that trail, and it rebuilds the drop-copy reports and the daily surveillance files from the same trail. The trail is the platform's own record. Order commits, fills, deliveries and undos are also on chain, and the [retention policy](https://docs.settlemint.com/docs/concepts/issuing-and-markets/venue-retention) moves expired records to the venue archive.

Every order also has a trading member: the broker a venue operator assigned to the participant that owns the wallet, or the participant itself when it has no broker. The platform works the member out as of the moment of the order, so assigning or clearing a broker changes the member of later orders only. An earlier order keeps the member it had. Only venue operators receive the member (`PATCH /api/v2/addons/trading-venues/{venueAddress}/participants/{walletAddress}/access` assigns it). A trader or a broker never sees a member, another participant's name or the counterparty's order on the platform. The venue contract's trade events name both wallets on chain, so a reader of the chain can still link a trade to the buyer's and the seller's wallets.

## Who can change a running venue, and who must agree [#who-can-change-a-running-venue-and-who-must-agree]

An operator changes a running instrument through a fixed set of controls, each tied to a venue role. Whether a control waits for a second person follows one organisation setting, the **Trading venues** row under "Approvals" on the "General" page of "Organisation settings", and an exception there can turn it on or off for one venue. With the row on, a different holder of the required role must approve, and the platform refuses a decision from the person who proposed. With the row off, the change applies at once and "Actions" logs it as "No second approval (approval off)".

| Control | What it changes | Role | With the Trading venues row on |
| --- | --- | --- | --- |
| Desk limit | Caps the open order value one trader wallet may carry on one instrument. Intake refuses an order that would pass the latest approved limit | Venue operator | A second operator approves. An undecided proposal lapses after a configurable window, 24 hours by default |
| Trade bust | Undoes a trade within 24 hours of it, before or after delivery | Venue operator | A second operator approves. An undecided proposal lapses like a desk limit |
| Order cancel and mass cancel | Cancels one open order, or every open order that matches a filter. A mass cancel leaves one cancellation batch per instrument | Venue operator | A second operator approves |
| Venue configuration | The venue calendar and its closed days, the venue price band, admission, listing rules (including order-rule limits and starting values), the [VAT](https://docs.settlemint.com/docs/concepts/issuing-and-markets/venue-vat) and [retention](https://docs.settlemint.com/docs/concepts/issuing-and-markets/venue-retention) policies, and resuming the venue | Venue admin | A second venue admin approves within 24 hours. A pending change blocks a second proposal for the same setting, and an approval whose recorded before-state no longer matches the live configuration is refused |
| Venue approval requirement | Switches the venue's second-person requirement off, or lets an emergency halt skip it | Venue admin | A second venue admin approves, even when the venue has no other admin yet: add one first. Switching the requirement on, or tightening the emergency halt, applies at once. With the row off there is nothing to approve, so the change applies at once |
| Instrument configuration | Instrument listings, instrument calendars, collection windows, price bands, instrument fields and limits such as the holding period, fees, fee tiers, instrument transitions, scheduled and dated changes, participant removal, and pausing the venue | A platform administrator or the organisation owner, signing with a venue operator wallet | A venue operator other than the proposer approves, on the same terms as venue configuration |
| Emergency halt | Halts one instrument at once, through the Platform API | Venue operator | Runs at once when a venue admin allowed emergency halts for the venue and the halt request asks for one. It records the halt as executed, with no checker, and a resume is its own request. Resume and delist never take this path |
| Instrument groups | Defines the groups, such as equities or fixed income, that the venue sorts its instruments into | Venue admin | Applies at once |

The second approval binds the Console and the Platform API. A holder of the venue operator role can also cancel any order directly on the venue contract, or flag and clear a pending trade there, with no second approval, and that cancel does not name its caller on chain. A venue administrator grants and revokes the venue operator and venue submitter roles through the Platform API.

The same venue keeps read-only records an auditor can check against these controls. The Orders screen lists every order submitted to the venue. Each operator action that cancels orders in bulk leaves a cancellation batch with the operator, the reason and the orders it closed. The settlement instructions show which token moved from whom to whom for each trade. **Held settlement fills** lists the fills settlement could not complete, each with its reason. The venue read, `GET /api/v2/addons/trading-venues/{venueAddress}`, adds a summary: the orders resting now and the instruments they rest in, the trades of the last 24 hours and of the 24 hours before, the settlement batches that fully delivered in each of those windows, and the batches still awaiting delivery. An undone trade counts in none of them. [Venue operations](https://docs.settlemint.com/docs/console/asset-lifecycle/venues), [Orders](https://docs.settlemint.com/docs/console/asset-lifecycle/venues/orders), [Mass cancellations](https://docs.settlemint.com/docs/console/asset-lifecycle/venues/mass-cancellations), [Instrument](https://docs.settlemint.com/docs/console/asset-lifecycle/venues/instrument) and [Trading calendar](https://docs.settlemint.com/docs/console/asset-lifecycle/venues/trading-calendar) cover each control and record in the Console.

## How the drop copy session runs [#how-the-drop-copy-session-runs]

The drop copy is one Financial Information eXchange (FIX) session from one venue to one surveillance provider, and the provider sees each order and trade event as the venue records it. It runs FIX 5.0 SP2 over the FIXT.1.1 session layer. The session belongs to the deployment's configuration, not to a Console setting. It stays off until it is enabled with the venue's network and address, the port, the SenderCompID and TargetCompID, the time reporting starts from and the venue's market identifier code. A session that dials out also needs the provider's host name. The Console has no drop copy screen. An operator reaches the session's output through the venue reports API: the replay preview and request, and the daily file list and download.

The session either dials the provider or waits for the provider to dial in. Dialling out is the default and uses TLS unless the configuration turns it off. Waiting for the provider needs three settings, and the configuration is refused without any of them: the provider's host on the session's allow list, plain TCP over a private link, and no sequence reset at logon. For an incoming session, the deployment must also make the listening port reachable from the provider, and it does not do that by default.

One process holds the session at a time. When the provider's sequence numbers skip, the session asks for a resend. A SequenceReset whose NewSeqNo is missing or lower than the number the session expects ends the session. The expected number never moves backwards. An incoming message body longer than 65,536 bytes is refused. Once each UTC day the session also sends start-of-day position reports, built from indexed balances at midnight UTC. While the balances, accounts or instrument mappings for the day are incomplete, the session sends no positions and tries again through that UTC day. It never reports a missing balance as zero.

An undone trade reaches the provider as a Trade Cancel ExecutionReport (`150=H`) on each leg. Its Text (58) carries the undo reason, such as `bust` for an approved bust or `refused` for a delivery an outside cause blocked. The leg of the side that failed delivery adds `;failed-side`, and a report timed by the trade's execution time, because no undo time was recorded, adds `;undo-time-unrecorded`.

## What each drop copy report carries [#what-each-drop-copy-report-carries]

The drop copy sends one FIX ExecutionReport for each order and trade event, and one for each new, validly signed order that intake refused. It also sends instrument events in the same sequence: SecurityStatus (`35=f`) carries halts, resumes, suspensions, band changes, configuration changes and official close, while TradingSessionStatus (`35=h`) carries session transitions. These messages carry the symbol and the status or session fields a surveillance system uses to attribute and time the event, and they support the same resend path as order reports.

The field table below describes order and trade ExecutionReports (`35=8`). Instrument messages (`35=f` and `35=h`) use the status, session and configuration fields described above.

Instrument status reports use `326=2` for halted, `326=3` for resumed and `326=4` for suspended or delisted. Band reports use `326=6` with `332` and `333` when bounded, while session reports use `340=4/2/5/3` for pre-open, open, closing auction and closed. An auction collection reports as open (`340=2`), and a Trade-At-Last session reports as open (`340=2`) with `625=5` for post-trading on that report only.

A volatility interruption reaches the provider as a sequenced SecurityStatus message (`35=f`). When it starts, the report carries `326=2`, the trigger price in LastPx (31) and a Text (58) of `volatility-interruption:started:resumes-at=<time>:reference=<price>`. When it ends, the report carries `326=3` and the Text `volatility-interruption:ended`.

| FIX field | Carried on | Value |
| --- | --- | --- |
| PartyID (448), PartyIDSource (447), PartyRole (452) | Fills and trade cancels | The account mnemonic of the other side of the trade, sent as a custom-coded party with role 37 (contra trader). The venue knows participants by wallet, so the mnemonic from the drop copy account map stands in for a trader id, and a wallet the map does not name is sent as its address. |
| ManualOrderIndicator (1028) | Every order/trade report (`35=8`) | `N`. Every order is signed by the maker's wallet. The venue has no path for an operator to place an order for a client. |
| OrdType (40) | Every order report (`35=8`) | `1` market, `2` limit, `4` (stop-limit) for an order placed as a stop. An armed stop sends no report. The New report arrives when the stop fires and joins the book, and carries StopPx. |
| StopPx (99) | Reports for a stop order | The trigger price, in the same decimal form as Price. |
| Capacity (528) | Reports for an account with a profile | `P` for a principal account, `G` when proprietary is set, `A` for agency, `W` for sponsored. Sponsored takes precedence when both sponsored and proprietary are configured. An account without a profile carries no Capacity. |
| ExecInst (18) | Reports for a fill-or-kill order | `G` (all or none). Other orders carry no ExecInst. |
| OrigTime (42) | Every order/trade report (`35=8`) | The order acceptance time in UTC, kept across its reports. A refusal report carries the refusal time instead, because the order was never accepted. |

The first restart after an upgrade that changes these fields rebuilds the drop copy's saved order state from the venue log.

## Order-lifecycle file mapping [#order-lifecycle-file-mapping]

The order-lifecycle (OLC) file is a comma-separated file with a fixed 68-column schema. The platform builds the file from the drop-copy reports, and a venue operator lists and downloads the files for a finished trading day from the venue reports API. The file opens with a header row and holds one row per order or trade report. Market status and session messages are left out. Every value keeps printable ASCII characters only, and a time is epoch seconds with three decimals. The mapping below records the source used for each column. `empty` means that the drop-copy report does not carry a value for that field.

| OLC column | Source | Mapping note |
| --- | --- | --- |
| `TM` | report transaction time | Epoch seconds. |
| `ORIG_TIME` | FIX tag 42 | OrigTime, the order acceptance time, parsed to epoch seconds. |
| `SENDING_TIME` | empty | The report does not carry the session sending time. |
| `TRANSACT_TIME` | report transaction time | Epoch seconds for every event type. |
| `SIDE` | FIX tag 54 |  |
| `QTY` | FIX tags 32, 151, 38, and 14 | Fill size for F/G/H, remaining quantity from tag 151 for 5/E, exact order quantity minus cumulative fills for 4/8/9/C/I, and otherwise order quantity. |
| `PRICE` | FIX tag 31, 99, or 44 | Last price for F/G/H, limit price for 4/I, and `0` for a market order. A stop-limit order uses the limit price, then the stop price. Any other order uses the stop price when present, then the limit price. |
| `SYMBOL` | FIX tag 55 |  |
| `SECURITY_ID` | FIX tag 48 |  |
| `CLIENT_OID` | FIX tag 11 |  |
| `EXEC_ID` | FIX tag 17 |  |
| `VENUE_OID` | FIX tag 37 |  |
| `SECONDARY_CLIENT_OID` | empty | No secondary client order ID in the report. |
| `SECONDARY_EXEC_ID` | empty | No secondary execution ID in the report. |
| `COUNTERPARTY_ID` | FIX tag 448 | Contra-party ID from the party block. Empty on a report that has no party block. |
| `ACCOUNT` | FIX tag 1 |  |
| `ROOT_SYMBOL` | empty | No root symbol is present in the report. |
| `TRADEID` | FIX tag 880 |  |
| `TRD_MATCH_ID` | FIX tag 880 | Same trade ID. No distinct match ID is present. |
| `SELF_MATCH_PREVENTION_ID` | empty |  |
| `CURRENCY` | FIX tag 15 |  |
| `SENDER_COMP_ID` | drop-copy settings SenderCompID | Empty when the settings name no SenderCompID. |
| `SENDER_SUB_ID` | empty | The drop-copy settings carry no sender SubID. |
| `TARGET_COMP_ID` | drop-copy settings TargetCompID | Empty when the settings name no TargetCompID. |
| `TARGET_SUB_ID` | empty | The drop-copy settings carry no target SubID. |
| `DELIVER_TO_COMP_ID` | empty | The drop-copy settings carry no deliver-to CompID. |
| `DELIVER_TO_SUB_ID` | empty | The drop-copy settings carry no deliver-to SubID. |
| `EXEC_BROKER` | empty |  |
| `TEXT_NOTE` | FIX tag 58 | Commas are removed, and so is any leading space, `=`, `+`, `-` or `@`. |
| `CLEARING_ACCOUNT` | empty |  |
| `CLEARING_FIRM` | empty |  |
| `ISIN` | FIX tag 48 when tag 22 is `4` | Empty for other security ID sources. |
| `EXEC_INST` | FIX tag 18 |  |
| `TIME_IN_FORCE` | FIX tag 59 |  |
| `EXPIRE_TIME` | FIX tag 126 | Parsed to epoch seconds. |
| `LAST_MKT` | FIX tag 30 |  |
| `EX_DESTINATION` | empty | No distinct execution destination is present. |
| `SECURITY_EXCHANGE` | FIX tag 30 | Same instrument identifier as `LAST_MKT`. |
| `ORD_TYPE` | FIX tag 40 |  |
| `CAPACITY` | FIX tag 528 |  |
| `ORIG_CLIENT_OID` | FIX tag 41 |  |
| `RELATED_ID` | Earliest known order ID in the file | Keeps the same root across replacement reports. When earlier reports are outside the file, retains the earliest available tag 41, and otherwise tag 11. |
| `ON_BEHALF_OF_COMP_ID` | empty | The drop-copy settings carry no on-behalf-of CompID. |
| `ON_BEHALF_OF_SUB_ID` | empty | The drop-copy settings carry no on-behalf-of SubID. |
| `CUM_QTY` | FIX tag 14 |  |
| `LEAVES_QTY` | FIX tag 151 |  |
| `CFI_CODE` | empty |  |
| `MATURITY_DATE` | FIX tag 541 | The drop-copy reports do not carry this tag, so the column is empty. |
| `PUT_OR_CALL` | FIX tag 201 | The drop-copy reports do not carry this tag, so the column is empty. |
| `STRIKE_PRICE` | FIX tag 202 | The drop-copy reports do not carry this tag, so the column is empty. |
| `OPEN_CLOSE` | FIX tag 77 | The drop-copy reports do not carry this tag, so the column is empty. |
| `TRADE_CLIENT_ID` | empty | No distinct trade-client ID is present. |
| `VENUE` | FIX tag 30 |  |
| `ORD_STATUS` | FIX tag 39 |  |
| `LOCATEREQD` | empty |  |
| `CLIENT_TAGS` | empty | No client tags are present in the report. |
| `DISPLAY_QTY` | empty |  |
| `OPERATOR_ID` | empty | The drop-copy report carries no operator identity. |
| `SPREAD_ID` | empty |  |
| `MSG_CLASS` | FIX tag 150 and tag 39 | `FILL` for `F` with tag 39 `2`, `PARTIAL_FILL` for other `F`, `SUSPENDED` for `9` with tag 39 `9`. Other combinations map to a fixed class, or empty. |
| `IS_OPERATIONAL` | constant | `1`. |
| `SOD` | constant | `0`. The file carries no start-of-day snapshot rows. |
| `LEG_FILL` | constant | `0`. No leg fills are present. |
| `STOP` | FIX tag 99 and tag 40 | `1` when StopPx is present or order type is stop-market or stop-limit, otherwise `0`. |
| `SPREAD` | constant | `0`. No spread orders are present. |
| `TAG_1028` | FIX tag 1028 |  |
| `GEO_LOCATION` | empty |  |
| `AGGRESSOR` | FIX tag 1057 | `Y` becomes `1`, `N` becomes `0`. Other values are empty. |

The drop-copy reports carry no spread, leg, trader or operator identity, so `SPREAD_ID` and `OPERATOR_ID` stay empty, `LEG_FILL` and `SPREAD` are `0`, and `RELATED_ID` carries the replacement ancestry.

The report body supplies the event fields. The body holds no session identifiers, so `SENDER_COMP_ID` and `TARGET_COMP_ID` come from the SenderCompID and TargetCompID in the venue's drop-copy settings, and the other session columns stay empty.

## Replaying a completed trading day [#replaying-a-completed-trading-day]

A provider can lose messages after acknowledging them, and the venue answers with the day it already recorded rather than new trading. A replay sends one trading day over the same FIX session: stored bodies go back as they were first sent, and only the reports the provider refused are built again.

An operator previews the day before asking for it. `GET /api/v2/addons/trading-venues/{venueAddress}/reports/{tradingDay}/replay` reads the day and changes nothing:

```json
{
  "data": {
    "tradingDay": "2026-06-12",
    "ended": true,
    "regenerated": 0,
    "stored": 1284,
    "differing": [],
    "issues": [],
    "request": null
  },
  "links": {
    "self": "/api/v2/addons/trading-venues/0x7f3c2b5e8a1d4c6f9b0e2a5d8c1f4b7e0a3d6c9f/reports/2026-06-12/replay"
  }
}
```

`stored` counts the reports the first send kept for the day, and `regenerated` counts the ones this preview built again from the venue log, plus the stored instrument reports that have no rebuild. `issues` lists what the venue log could not be mapped into. `differing` lists the exec ids whose rebuilt body differs from the body the provider received, which is how a divergence between the log and the provider's copy shows before a resend goes out. `ended` is false while the UTC day is still running, and the preview answers in that case too, so an operator sees the day take shape.

`POST .../replay-requests` records the request and answers with the queued entry:

```json
{
  "data": {
    "id": "0b7c1f5e-2f2a-4c39-9a51-1d3f2c8e4b60",
    "requestedAt": "2026-06-13T08:12:44.000Z",
    "enqueuedAt": null,
    "reportCount": null
  },
  "links": {
    "self": "/api/v2/addons/trading-venues/0x7f3c2b5e8a1d4c6f9b0e2a5d8c1f4b7e0a3d6c9f/reports/2026-06-12/replay"
  }
}
```

`enqueuedAt` and `reportCount` stay null until the session that holds the drop-copy lease has built and queued the day, and that session fills both in the same write. The two null fields are the state an operator watches: a request still waiting while the session runs means the day cannot be built, and the platform logs the exec ids it could not rebuild and the mapping issues it met. A day that cannot be built sends nothing, because a partial day leaves the provider further from the venue log than it was.

A replay sends each report of the day with its own exec id and PossResend (97) set to `Y`. A report the provider accepted keeps the body it first carried, and a report the provider refused when it was first sent is built again from the venue log. An instrument report whose transition was polled from the venue instead of derived from the engine log has no rebuild, so its stored copy is sent. Each resent message takes a new sequence number from the session, and the provider reads it by its exec id.

A venue the drop-copy settings do not name and a session that is not enabled answer both calls with 409 and DALP-14389, and so does a request for a day that has not ended. Both calls need the venue operator or venue admin role, and the request also needs a wallet verification.

## Exporting the daily surveillance files [#exporting-the-daily-surveillance-files]

The FIX session is the venue's real-time drop copy. A daily file is the other way to hand a provider the venue's activity, in one of two formats, and the OLC file mapped above is one of them. Both are rebuilt from the venue log with the same mapping that builds the drop-copy reports, so a file and the FIX feed describe a day with the same fields.

VALCSV is an optional drop-copy transport for a provider that takes a finished day as a file instead of, or next to, the FIX session. For each asset class it carries an order lifecycle file with one row for each order and trade report of the day: accepted orders, replacements, fills, cancels, expiries, intake refusals and Trade Cancels, with the instrument halts, session changes and other instrument reports left out. Its 22 columns hold the side, symbol, account mnemonic, quantity, price, venue and client order ids, leaves quantity, time in force, market identifier code, transaction time, execution type, order status and type, the report's Text, and a JSON column with the trade id, aggressor flag, execution reference, security id and currency. Next to those files it carries one accounts file that lists each account profile with its capacity, ISO approval, market maker and proprietary flags, supervisor, aggregation unit and label. OLC carries one order lifecycle file per asset class with its 68 columns.

`GET .../reports/{tradingDay}/files?format=valcsv` answers with the day's file names and byte sizes, or the reasons that stop them:

```json
{
  "data": {
    "tradingDay": "2026-06-12",
    "format": "valcsv",
    "reports": 1284,
    "issues": [],
    "files": [
      { "name": "valcsv_NORTHWIND_EQ_20260612.csv", "bytes": 481203 },
      { "name": "valcsv_accounts_NORTHWIND_20260612.csv", "bytes": 412 }
    ]
  },
  "links": {
    "self": "/api/v2/addons/trading-venues/0x7f3c2b5e8a1d4c6f9b0e2a5d8c1f4b7e0a3d6c9f/reports/2026-06-12/files"
  }
}
```

A file is named `<format>_<exchange OMS name>_<asset class>_<YYYYMMDD>.csv`, and the accounts file is `valcsv_accounts_<exchange OMS name>_<YYYYMMDD>.csv`. The exchange OMS name and the asset class of each instrument come from the venue's drop-copy settings.

`files` stays empty while `issues` is not, and an issue names what blocks the day: the UTC day has not ended, a trade of the day is still pending delivery, an approved bust of the day has not executed, a venue instrument has no asset class mapped, a report cannot be built from the venue log, or, for VALCSV, an account in the reports has no profile.

`GET .../reports/{tradingDay}/files/{fileName}` returns one file as `text/csv` with an attachment disposition and records the download in the request audit trail. A name the day does not produce answers 404 with DALP-14390.

These files need no live FIX session. A venue turns VALCSV on in the deployment's drop-copy settings, not in the Console: the venue's chain and address (`TRADING_VENUE_DROP_COPY_CHAIN_ID`, `TRADING_VENUE_DROP_COPY_VENUE_ADDRESS`), an exchange OMS name (`TRADING_VENUE_DROP_COPY_EXCHANGE_OMS_ISV`), an asset class on every instrument in `TRADING_VENUE_DROP_COPY_INSTRUMENTS`, and, in `TRADING_VENUE_DROP_COPY_ACCOUNT_PROFILES`, a profile for every account mnemonic the day's reports name. `TRADING_VENUE_DROP_COPY_ENABLED` can stay `false`. The platform does not push the files: a venue operator or venue admin lists and downloads a finished day through the routes above, or a platform operator writes it to a directory with the scheduler's operations command `drop-copy valcsv --date YYYY-MM-DD --out DIR`, and the venue delivers the files to its provider. VALCSV is off by default. A venue that reports to its provider over the FIX session alone leaves the exchange OMS name unset, and every file request for it then answers 409 with DALP-14389.
