FIX 4.4

Connect an execution algo to the simulated venue over a real FIX session.

HTTP endpoints on this page

  • GET/fix/usage

Parameters and response shapes for each are in the API reference.

Pulse exposes its simulated exchange as a FIX 4.4 acceptor over raw TCP. Your algo logs on with its own FIX engine, starts a simulation, subscribes to the book, and sends orders into the live agent-based market — the same message types it would speak to a real venue. Nothing in your order-entry code needs to know it is talking to a simulation.

For why you would do that rather than submit a batch job, see the FIX overview. This page is the technical reference: connection details, identity, the message set, and the custom tags.

FIX trading requires a Pro or demo tier account. Entitlement is checked live at every logon from the key you present, so an upgrade or downgrade takes effect on your next session with no coordination needed.

Connecting

The acceptor is a plain TCP listener, not an HTTP endpoint, so it sits on its own address rather than behind the API host.

SettingValue
BeginStringFIX.4.4
Port5001
HeartBtInt30 seconds
Session hours00:00:00–23:59:59 — the venue is always open
ResetOnLogonY — sequence numbers restart every session, so you never need to resend or reset by hand
Data dictionaryStandard FIX 4.4, with user-defined fields permitted (see custom tags below)

Ask support@simudyne.com for the venue address and to have your account enabled for FIX.

Identity and authentication

Your CompID is your user_id

There is nothing to provision and no second credential to keep in step. Your SenderCompID is your Pulse account id, which you can read straight off your profile:

python
me = client.profile.get()

print(f"SenderCompID: {me['user_id']}")
print(f"tier: {me['tier']}")
Output
SenderCompID: user_76b8ee0fe28cdbbf42ebc0e763c76750
tier: pro

The venue's CompID

Set TargetCompID to PULSE. The venue announces that name on every message it sends you, so a session configured for a different venue cannot log on by accident.

text
TargetCompID (56) = PULSE

The API key rides in the Logon

Put your Pulse API key in Password(554) on the 35=A Logon. A FIX credential is presented once and the session then lives for hours, so this is the only point at which you prove who you are — a rejected logon refuses the session outright rather than letting an unknown client trade.

text
8=FIX.4.4|35=A|49=user_76b8ee0f…|56=PULSE|98=0|108=30|554=pk_live_…|

Revoke the key and the session dies with it — there is no separate FIX credential to chase. That makes a dedicated key per algo the natural setup: revoking one stops that algo and nothing else.

Why a logon is rejected

RejectLogon textCause
missing Password(554): send your Pulse API keyThe Logon carried no key
API key rejected (401) / (403)The key is unknown, revoked or expired, or the account is inactive
this API key belongs to a different accountThe key is valid but resolves to a different account than the SenderCompID claims. A valid key cannot be used to trade as somebody else
tier 'free' may not trade over FIXThe account is not on a tier permitted to run simulations
<CompID> is not a provisioned CompIDThe venue has no session declared for that account — it is brand new. The session list is rebuilt periodically; contact support if it persists
authentication is temporarily unavailableThe venue could not reach the account store. It fails closed rather than admitting everyone

A session, end to end

The one step that is easy to miss is the first: you must start a simulation before there is any market to subscribe to. A 35=V sent before that comes back as 35=Y MarketDataRequestReject.

StepYou sendYou get back
1. Log on35=A with your key in 55435=A, or RejectLogon
2. Start a simulation35=g with the symbol, date and speed in custom tags35=h TradingSessionStatus, Open (2)
3. Subscribe35=V MarketDataRequest35=W, a 10-level book snapshot
4. Watch—35=X incremental refreshes: book moves and trades, stamped with simulation time
5. Trade35=D NewOrderSingle35=8 ExecutionReport — New, then fills
6. End—35=8 DoneForDay, then 35=h Closed (3) carrying the cost report

Simulation time, not wall time, is what the market data is stamped with. Tag 20010 carries it as a display string and 20011 as seconds, with 20012 giving the time-of-day the clock started — enough to plot a session on its own axis regardless of how fast you ran it.

Message set

MsgTypeNameDirectionPurpose
35=ALogon→Authenticate with your API key in Password(554)
35=gTradingSessionStatusRequest→Start a simulation, or change the speed of a running one
35=hTradingSessionStatus←Open (2) when the simulation is live, Closed (3) when it ends. The final one carries the cost report
35=VMarketDataRequest→Subscribe to the book for a symbol
35=WMarketDataSnapshotFullRefresh←A 10-level book snapshot
35=XMarketDataIncrementalRefresh←Book and trade updates, throttled and simulation-time stamped
35=YMarketDataRequestReject←You subscribed with no simulation running — send 35=g first
35=DNewOrderSingle→Market or limit order into the live market
35=8ExecutionReport←New, partial fill, filled, and DoneForDay at the end
35=9OrderCancelReject←Cancel and amend are not supported; the order stands
35=jBusinessMessageReject←An application message the venue does not handle

Orders

40=2 Limit with a price in 44 rests in the book. Anything else — including an omitted OrdType — is treated as a market order and executes against resting liquidity immediately.

Cancel and amend are not implemented. An OrderCancelRequest or OrderCancelReplaceRequest is answered with 35=9 OrderCancelReject and the original order is left working. Size your child orders on the assumption that once sent, they stand.

Custom tags

The venue permits user-defined fields, and uses them to carry the simulation parameters FIX has no standard field for. These go on 35=g:

TagFieldDefaultDescription
20001speed1Simulation clock multiplier. 1 is real time
20002symbolSIMInstrument to simulate
20003seed42Random seed — the same seed reproduces the same market
20004horizon_s600How long to run, in simulation seconds
20005provider—Data provider, e.g. omd or bmll
20006exchange—Exchange protocol, e.g. hkex_securities
20007ref_price—Reference price to start from
20008cal_date—Calibration date the market is built from
20009scenarionormalMarket scenario to inject — the same set as a batch run

Use calibrated_data() to pick a symbol, provider, exchange and cal_date combination that is actually calibrated, and list_scenarios() for the scenario names.

Outbound

TagFieldOnDescription
20010sim_time35=W / 35=XSimulation time as a display string
20011sim_secs35=W / 35=XThe same instant as seconds, for plotting
20012day_open35=hTime of day the simulation clock starts, as seconds since midnight

Speed

Send 35=g again on a running session with a new 20001 to change speed mid-simulation. Around 10× is what the engine sustains in practice — above that the clock is limited by how fast the market can actually be generated, not by the throttle.

The end-of-session cost report

When the simulation ends, the final 35=h Closed carries your execution costs, measured against a baseline twin of the same simulation that never saw your orders. That is what makes the number meaningful: it is the cost of your flow specifically, not of the day.

TagFieldDescription
20030executed_volTotal volume executed
20031avg_exec_prcVolume-weighted average execution price
20032market_slippage_bpsSlippage against the reference price, in basis points
20033market_risk_bpsTiming risk in basis points
20034market_impact_bpsImpact against the baseline twin, in basis points

The same figures also arrive as JSON in Text(58) on that message, so a client that does not want to read custom tags can parse one field instead. The batch equivalent is exec_results.parquet — see Execution algorithms.

What you have run

The venue records the shape of every session it starts, which you can read back over HTTP with client.fix.usage(). Only the parameters are kept — symbol, date, scenario, speed, horizon. No orders, no schedules, no fills: that is your strategy, and it is not stored.

python
usage = client.fix.usage()

print(f"{usage['total_runs']} FIX runs, "
      f"{usage['simulated_seconds']:.0f} simulated seconds")

for run in usage["runs"][:5]:
    print(f"  {run['symbol']} {run['cal_date']} {run['scenario']}: "
          f"{run['runs']}x")
Output
12 FIX runs, 37800 simulated seconds
  1024 2025-09-01 normal: 9x
  1024.HK 2025-10-14 normal: 1x
FieldTypeDescription
total_runsintSessions recorded
simulated_secondsfloatTotal horizon across those sessions
by_symbol / by_scenario / by_cal_datedictRun counts grouped each way
runslistOne {symbol, cal_date, scenario, runs} per distinct combination, most-run first
last_run_atstr | nullWhen the most recent session started
truncatedboolTrue if the history hit the server's cap

Getting access

You need a Pro or demo tier account, an API key, and the venue address. Email support@simudyne.com telling us which system you want to connect and we will confirm the address and check your account has a session declared.

A brand-new account needs the venue's session list rebuilt before it can log on, because a FIX engine will not accept a Logon for a session it was never configured with. Everything else — tier changes included — applies immediately, with no restart.