Skip to content

Market Simulator Service

The Market Simulator Service generates real trading activity by driving the live service pipeline over HTTP — exactly as a real client would. It provisions fleets of synthetic users (signup → KYC → deposit), then has them place randomized orders against the Order Service REST API. There is no special path: every simulated order goes through auth, risk, wallet locking, the matching engine, and downstream consumers identically to a real user, making it suitable for integration testing, load testing, and demo environments without real capital.

Not a market-data generator

This service does not publish md.* or order.command.v1 Kafka topics, and it does not run a synthetic price walk. It is an HTTP client of the other services. Market data (md.trades.v1, md.orderbook.delta.v1, OHLCV, etc.) emerges naturally downstream once the matching engine processes the orders it submits.

  • Account provisioning: Create synthetic accounts via the Auth Service (Signup/v1/api-keys), wait for the User Service to confirm the profile, and approve KYC using an admin JWT.
  • Funding: Deposit funds via the Wallet Service (POST /v1/transactions/deposits) and re-deposit (top-up) when an account’s available balance drops below a configurable threshold.
  • Order generation: Place randomized orders via the Order Service (POST /v1/orders) per account on a randomized interval — randomizing symbol, side (honoring a buy bias), size, order type, leverage, aggressiveness, price offset, and post-only flag.
  • Job orchestration: Run multiple named simulation jobs concurrently (actor-per-job, one account loop per synthetic account), each independently configurable, startable, pausable, and stoppable via the admin REST API.
  • Stats & analytics: Track orders placed / filled / rejected, fill rate, total volume, active accounts, and top-ups; snapshot them periodically to Postgres and expose live + historical stats and per-account analytics.
graph TB subgraph Sim[Market Simulator] Mgr[Job Manager] --> Actor[Job Actor
one per job] Actor --> Loop[Account loops
one per synthetic account] end Loop -->|signup + api-key| Auth[Auth Service] Loop -->|profile poll| UserSvc[User Service] Loop -->|deposit| Wallet[Wallet Service] Loop -->|POST /v1/orders| OrderSvc[Order Service] Loop -->|fees / instruments| Meta[Metadata Service] Loop -. index price WS .-> MD[Market Data Service] Sim --> PG[(Postgres
jobs / accounts / orders / stats)]

The simulator owns its own Postgres database (jobs, accounts, orders, stats — SQLC + Atlas) to track what it created, but it reaches all trading services exclusively over their public HTTP/WS APIs. It never writes to another service’s database, Kafka topics, or the matching engine directly.

Go (Fiber HTTP server, actor model, SQLC + Atlas).

External Dependencies (outbound HTTP / WS)

Section titled “External Dependencies (outbound HTTP / WS)”
ClientTargetUsed for
auth_clientAuth ServiceSignup, login, /v1/api-keys, api-key exchange
user_clientUser ServiceProfile-creation polling
wallet_clientWallet ServicePOST /v1/transactions/deposits, GET /v1/balance
order_clientOrder ServicePOST /v1/orders, GET /v1/orders/:id, GET /v1/market/mark-price/:symbol
metadata_clientMetadata ServiceInstrument fees / definitions for analytics
index_price_clientMarket Data Service WSRead-only index-price subscription (mid-price reference)

All routes are under /v1:

MethodPathDescription
GET/sim-jobsList jobs
POST/sim-jobsCreate a job
GET/sim-jobs/:idGet a job
PUT/sim-jobs/:idUpdate a job
DELETE/sim-jobs/:idDelete a job (only if not running)
POST/sim-jobs/:id/startStart a job
POST/sim-jobs/:id/pausePause a job
POST/sim-jobs/:id/stopStop a job
GET/sim-jobs/:id/statsLive stats (actor memory, or last snapshot if stopped)
GET/sim-jobs/:id/stats/historyHistorical stats snapshots
GET/sim-jobs/:id/accountsSynthetic accounts for a job
GET/sim-jobs/:id/ordersOrders placed by a job
GET/sim-jobs/:id/analyticsPer-account trading analytics
GET/sim/statusSummary of all currently running jobs

Set via environment variables (defaults shown):

VariableDescriptionDefault
PORTHTTP port3007
POSTGRES_URLSimulator’s own database — required
ADMIN_JWT_TOKENAdmin token used for KYC approval + deposits — required
AUTH_SERVICE_URLAuth Service base URLhttp://localhost:3001
USER_SERVICE_URLUser Service base URLhttp://localhost:3002
WALLET_SERVICE_URLWallet Service base URLhttp://localhost:3003
ORDER_SERVICE_URLOrder Service base URLhttp://localhost:3004
METADATA_SERVICE_URLMetadata Service base URLhttp://localhost:8080
MARKETDATA_SERVICE_WS_URLMarket Data WS URL (index prices)ws://localhost:8080/ws
KYC_LEVEL / KYC_PROVIDERKYC approval settingsL1 / hyperverge
STATS_SNAPSHOT_INTERVALStats snapshot cadence10s
PROFILE_POLL_MAX_RETRIES / PROFILE_POLL_DELAYUser-profile confirmation polling20 / 3s
HTTP_CLIENT_TIMEOUTOutbound HTTP timeout30s

Supplied in the POST /v1/sim-jobs body (CreateJobRequest):

FieldDescription
nameJob name
symbolsSymbols to trade (randomly chosen per order)
num_accountsNumber of synthetic accounts to provision
order_interval_min_ms / order_interval_max_msRandomized sleep between orders per account
deposit_amount_min / deposit_amount_maxInitial deposit range
deposit_top_up_min_inrRe-deposit when available balance falls below this
order_size_min / order_size_maxOrder quantity range
order_typesAllowed order types (default ["limit","market"])
leverage_min / leverage_maxLeverage range (default 110)
price_offset_min_pct / price_offset_max_pctPassive-order price offset range
buy_bias_pctProbability an order is a BUY (default 50)
aggressive_order_pctProbability an order crosses the spread
post_only_pctProbability a limit order is post-only
duration_secondsAuto-stop the job after this duration

Run a job against a local or staging stack to exercise the full signup → KYC → deposit → order → match → settle → position path with real cross-service calls — no manual order placement.

Raise num_accounts and lower order_interval_*_ms to drive high order rates, stressing Order Service admission throughput, matching-engine processing, Kafka consumer lag tolerance, and WebSocket broadcast latency.

Run a long-lived job to produce a live-looking order book, trade feed, and candlestick charts driven by genuine (synthetic) order flow.

  • ❌ Does NOT publish to Kafka or write to any other service’s database directly.
  • ❌ Does NOT bypass Auth, Risk, Wallet, Order Service, or the Matching Engine — every action is an authenticated public-API call.
  • ✅ Only ever touches synthetic accounts it created (tracked in its own database); never real user accounts.
  • ✅ Requires an admin JWT solely for KYC approval and deposits of those synthetic accounts.
  • ⚠️ Synthetic account emails are derived deterministically — run a single instance. Multiple replicas would collide on signup and double the generated load. Scale via job config (num_accounts, interval), not pod count.