NautilusTrader
Integrations
These docs track the unreleased nightly build and may change without notice. Switch to the latest stable docs.

Lighter

Lighter is a decentralized central-limit-order-book exchange for spot and perpetual futures. The venue settles through an Ethereum zero-knowledge rollup, while matching and sequencing run off-chain.

The NautilusTrader Lighter adapter is implemented by the nautilus-lighter crate. It provides Rust data and execution clients, typed REST and WebSocket models, and an in-tree L2 transaction signer for the venue's Schnorr / ECgFp5 signing flow.

Overview

The main components are:

  • LighterRawHttpClient: low-level REST client for the public and account endpoints.
  • LighterHttpClient: domain client which parses instruments, trades, books, orders, and account state into Nautilus model types.
  • LighterWebSocketClient: reconnecting WebSocket client for public market and private account streams.
  • LighterDataClient: Nautilus data client for instruments, trades, quotes, and L2 MBP books.
  • LighterExecutionClient: Nautilus execution client for account streams, order submission, modification, cancellation, and reconciliation reports.
  • LighterDataClientFactory and LighterExecutionClientFactory: live-node factory wiring.

The Python surface is intentionally narrow. The Python extension exposes configuration, environment selection, factory classes, and integrator revocation; data and execution clients are consumed through the Rust trait surface.

Examples

Python v2 examples live in python/examples/lighter/ and default to a dry build. Pass --run to connect; the execution tester also requires --live-orders to disable dry_run.

cd python
.venv/bin/python examples/lighter/data_tester.py --lighter-environment testnet
.venv/bin/python examples/lighter/exec_tester.py --lighter-environment testnet

To connect to mainnet with explicit instruments:

cd python
.venv/bin/python examples/lighter/data_tester.py \
    --lighter-environment mainnet \
    --instrument BTC-PERP.LIGHTER \
    --run
.venv/bin/python examples/lighter/exec_tester.py \
    --lighter-environment mainnet \
    --instrument DOGE-PERP.LIGHTER \
    --run

Rust examples live under crates/adapters/lighter/examples/. Both testers connect when run, but the execution tester submits orders only after its source sets DRY_RUN = false:

cargo run --example lighter-data-tester --package nautilus-lighter --features examples
cargo run --example lighter-exec-tester --package nautilus-lighter --features examples

Examples can connect to live venues. Execution examples with live order flow enabled can submit orders when pointed at a funded mainnet account. Review the selected instrument, quantity, and environment before running them.

For emergency account cleanup, cargo run --bin lighter-flatten -p nautilus-lighter cancels open orders and closes positions for the configured Lighter account. It scans all registered markets, so the standard 60 req/min REST quota can make the run take several minutes. Review the active account and positions first because it is account-wide, not strategy-scoped.

Product support

Product typeData feedTradingNotes
SpotSpot markets using Lighter market indexes 2048-4094.
Perpetual futuresLinear perpetual markets using Lighter market indexes 0-254.
Dated futures--Not supported.
Options--Not supported.

Limitations

The current adapter scope is deliberately narrower than the venue's full transaction surface:

  • Grouped order lists, OCO/OTO groups, brackets, TWAP, trailing stops, and iceberg display size are not implemented. Batch submit does not use CreateGroupedOrders.
  • Order-list submit and batch cancel fan out independent transactions sequentially over WebSocket. Both operations are capped at 15 transactions per command.
  • CancelAllOrders uses cached open orders for the requested instrument. The adapter does not use Lighter's native account-wide cancel-all transaction because it can affect unrelated markets.
  • Spot trading supports market and limit orders. Conditional stop-loss and take-profit orders are limited to perpetual markets.
  • Account state and position reports come from private WebSocket streams. query_account and position status generation replay the latest cached stream state.
  • Unscoped order reconciliation is bounded to configured or observed active markets to avoid a full venue-wide fan-out under the standard REST quota.

Symbology

Lighter identifies markets by numeric market_index values. The adapter bootstraps the mapping from GET /api/v1/orderBookDetails, then converts the raw venue symbol into a Nautilus InstrumentId.

Venue productNautilus symbol formatExampleNotes
Perpetual futures{BASE}-PERP.LIGHTERBTC-PERP.LIGHTERRaw venue symbol BTC.
Spot{BASE}/{QUOTE}-SPOT.LIGHTERETH/USDC-SPOT.LIGHTERRaw venue symbol ETH/USDC.

The suffix separates spot and perpetual listings. Outbound requests strip it and use the cached market_index; spot symbols retain the venue pair.

Environments

EnvironmentREST URLWebSocket URLChain ID
Mainnethttps://mainnet.zklighter.elliot.aiwss://mainnet.zklighter.elliot.ai/stream304
Testnethttps://testnet.zklighter.elliot.aiwss://testnet.zklighter.elliot.ai/stream300

Use LighterEnvironment::Mainnet or LighterEnvironment::Testnet in data and execution configuration. URL overrides are available for private gateways or local test fixtures.

Integrator attribution

Create and modify transactions carry the NautilusTrader integrator account index in L2TxAttributes to measure adapter usage. Maker and taker integrator fees are zero. The execution client submits the required zero‑fee ApproveIntegrator approval during startup.

Revoking the approval

Use revocation as cleanup when leaving the adapter. It sends ApproveIntegrator with approval_expiry = 0 and zero max fees. The next execution‑client startup records a new zero‑fee approval.

export LIGHTER_API_KEY_INDEX=5
export LIGHTER_API_SECRET=REPLACE_ME
export LIGHTER_ACCOUNT_INDEX=123456
cargo run -p nautilus-lighter --bin lighter-integrator-revoke           # mainnet
cargo run -p nautilus-lighter --bin lighter-integrator-revoke testnet   # testnet

Script source: crates/adapters/lighter/bin/integrator_revoke.rs.

# Python (PyO3 binding) - reads the same env vars as the Rust bin
from nautilus_trader.adapters.lighter import revoke_lighter_integrator
from nautilus_trader.adapters.lighter import LighterEnvironment

await revoke_lighter_integrator()  # mainnet (default)
await revoke_lighter_integrator(LighterEnvironment.TESTNET)  # testnet

The Rust script prints a summary of the action and pauses for an Enter keypress before signing or sending; abort with Ctrl+C before that point if anything in the summary looks wrong. The Python binding does not prompt: review the active env vars yourself before calling.

Data subscriptions

Data typeSub.SnapshotHist.Nautilus typeNotes
Instrument metadataCache replay-InstrumentAnyLoaded from orderBookDetails.
Trade ticks-TradeTickWebSocket trades; public recentTrades REST history.
Quote ticks--QuoteTickBest bid and ask ticker stream.
Order book deltas-OrderBookDeltasL2_MBP only.
Order book depth10--OrderBookDepth10Live top-10 view from maintained book; no REST snapshot.
Order book snapshots--OrderBookREST snapshot, max depth 250.
Mark prices--MarkPriceUpdatePerp market stats stream.
Index prices--IndexPriceUpdateMarket and spot stats streams.
Funding rates-FundingRateUpdateCurrent estimates and REST hourly history.
Bars-BarWebSocket candle stream; REST history for backfill.
Instrument statusREST-InstrumentStatusactive / inactive snapshots.

Only BookType::L2_MBP is accepted for book-delta and depth10 subscriptions. Other book types return an error before subscribing.

The WebSocket order book initializes only from subscribed/order_book. If an update/order_book arrives before that snapshot, the adapter drops it and waits for the real snapshot because incremental updates do not contain the full visible book.

Depth10 subscriptions use the same WebSocket order_book stream as deltas. The adapter emits a refreshed top-10 view after each accepted snapshot or incremental update.

Bar subscriptions use the venue's candle/{market_id}/{resolution} WebSocket channel. Lighter batches in-progress updates for the open bar every ~500 ms; the adapter emits a Nautilus Bar only when the candle start timestamp advances, so consumers see one event per closed period. The in-progress cache is cleared on reconnect and on unsubscribe.

The stream supports 1m, 5m, 15m, 30m, 1h, 4h, 12h, and 1d. 1w is REST-only via request_bars; subscribing to a 1-WEEK bar type returns an error.

REST bar history omits venue gap rows whose open, high, low, or close is missing, null, zero, or negative. These rows cannot form valid Nautilus bars and do not stop later valid rows from loading.

Instrument status subscriptions replay the latest cached orderBookDetails status when available and otherwise fetch a REST snapshot. Lighter does not expose a WebSocket status-change stream.

See Funding rates for live and historical funding semantics.

Trade subscriptions use the public WebSocket trade stream. Historical trade requests use the public /api/v1/recentTrades endpoint, which needs no credentials; the adapter clamps the request to the venue per-call cap and filters the returned ticks to the requested time range.

Unsupported data requests

request_quotes is not implemented. Lighter exposes best bid and offer data through the WebSocket ticker stream, but the REST endpoints available to the adapter do not provide a timestamped quote snapshot or quote history that can map safely to QuoteTick.

request_book_depth is not implemented. The documented REST book endpoints do not provide a venue event timestamp for OrderBookDepth10.ts_event; use subscribe_book_depth10 for a live depth10 stream or request_book_snapshot for a REST OrderBook snapshot.

Orders capability

Order identification

Lighter uses a numeric venue order index and a caller-supplied client_order_index. The adapter derives a 31‑bit index from the Nautilus ClientOrderId and probes forward on a collision. Because the collision‑probed value cannot be re‑derived after restart, order reconciliation resolves each raw venue order ID through the core cache and restores its actual client_order_index before translating order and fill reports. Open cached orders return to active tracking, while terminal orders use bounded replay tracking.

Recovery never infers a client order ID from the integer alone: the cached venue order ID must match. It requires reconciliation to include the order and the core cache to retain its venue‑order‑ID mapping; otherwise reports use the unique venue order ID as their external client order ID.

Query paths use the numeric venue order ID for active or terminal history. Before that ID is known, a Nautilus client order ID can query active orders by its derived client index. Client-index-only queries do not search terminal history, and duplicate active matches fail as ambiguous.

Order types

Order typePerpetualsSpotNotes
MARKETCap derived from cached far‑side quote + slippage.
LIMITRequires a limit price.
STOP_MARKET-Perp only; cap derived from trigger_price + slippage.
STOP_LIMIT-Perp only; maps to Lighter stop‑loss limit orders.
MARKET_IF_TOUCHED-Perp only; cap derived from trigger_price + slippage.
LIMIT_IF_TOUCHED-Perp only; maps to Lighter take‑profit limit orders.
MARKET_TO_LIMIT--Not supported.
TRAILING_STOP_MARKET--Not supported.
TRAILING_STOP_LIMIT--Not supported.
TWAP--Not supported; no Nautilus mapping.

Conditional orders require trigger_price. The adapter rejects missing triggers for STOP_MARKET and MARKET_IF_TOUCHED, any trigger that truncates to 0 ticks at the instrument's price precision, and spot conditionals that Lighter does not support.

Lighter requires a worst‑acceptable price for market‑style orders. The adapter starts from the cached far‑side QuoteTick for MARKET, or trigger_price for STOP_MARKET and MARKET_IF_TOUCHED, then applies market_order_slippage_bps (default 50 bps) and rounds at the instrument's price precision, up for buys or down for sells. A MARKET order without a cached quote is denied. Override the slippage with SubmitOrder.params["market_order_slippage_bps"].

Contingent orders

FeaturePerpetualsSpotNotes
Stop‑loss market-STOP_MARKET maps to Lighter STOP_LOSS.
Stop‑loss limit-STOP_LIMIT maps to Lighter STOP_LOSS_LIMIT.
Take‑profit market-MARKET_IF_TOUCHED maps to Lighter TAKE_PROFIT.
Take‑profit limit-LIMIT_IF_TOUCHED maps to TAKE_PROFIT_LIMIT.
Trigger price-Required for every supported conditional order.
Trigger price type--Not supported; no trigger source selector.
Grouped order lists--Not supported.
OCO / OTO orders--Not supported.
Bracket orders--Not supported.
CreateGroupedOrders--Not supported; order lists use independent txs.

Order options

OptionPerpetualsSpotNotes
post_onlyMaps to Lighter's post‑only time‑in‑force.
reduce_only-Passed through to CreateOrder; use only to reduce an existing position.
quote_quantity--Not supported; submit base quantity instead.
display_qty--Not supported; Lighter exposes no iceberg display quantity field.

Adapter order params

ParamPerpetualsSpotNotes
market_order_slippage_bpsOverrides the config default for market‑style caps.
post_only through SubmitOrder.params--Not supported; use the Nautilus order flag.
reduce_only through SubmitOrder.params--Not supported; use the Nautilus order flag.

Time in force

Time in forcePerpetualsSpotNotes
GTCLimit‑style uses GoodTillTime; market‑style uses IOC.
DAYLimit‑style and conditional orders use a positive order expiry.
GTDSupplied expiry must be 5 minutes to 30 days from submission.
IOCPlain MARKET/LIMIT use expiry 0; conditional limit uses trigger expiry.
FOK--Not supported.
AT_THE_OPEN--Not supported.
AT_THE_CLOSE--Not supported.

The adapter sends MARKET, STOP_MARKET, and MARKET_IF_TOUCHED as Lighter ImmediateOrCancel; the venue rejects market‑style GoodTillTime orders. Plain MARKET uses OrderExpiry = 0, while conditional market orders keep a positive expiry until triggered. The adapter denies Nautilus IOC for conditional market orders because Lighter reserves IOC for post‑trigger execution. Conditional limit orders can use IOC: their trigger rests with a positive expiry, then the child uses ImmediateOrCancel.

Without an explicit GTD expiry, limit‑style GTC, DAY, and GTD orders default to the current time plus 28 days; conditional GTC, DAY, and limit‑style IOC use the same default. Lighter rejects -1 and accepts expiries from 5 minutes to 30 days after submission. The adapter enforces that window with a one‑second signing and transport margin.

Execution instructions

InstructionPerpetualsSpotNotes
post_onlyOverrides the TIF and sends Lighter PostOnly.
reduce_only-Position‑reducing flag for existing derivative positions.

Use post_only on limit-style orders. The adapter does not synthesize maker-only market orders. Live mainnet testing confirms reduce_only=true for closing perpetual positions. Invalid reduce-only opens can be dropped by Lighter without a venue order report; the adapter reconciles them as INFLIGHT_TIMEOUT rather than a venue-supplied rejection reason.

Advanced order features

FeaturePerpetualsSpotNotes
Order modificationModify quantity, price, and trigger price on a live order.
Bracket orders--Not supported.
Iceberg orders--Not supported.
Trailing stops--Not supported.
Pegged orders--Not supported.
TWAP orders--Not supported; no Nautilus mapping.
Leverage update-Perp only; submits a signed UpdateLeverage tx.
Native cancel‑all--Not supported; adapter scopes cancel‑all per instrument.
Dead man's switch--Not supported.

Order operations

OperationPerpetualsSpotNotes
Submit orderSends a signed L2CreateOrder transaction over WebSocket.
Submit order listSequential fanout of up to 15 independent create transactions.
Modify orderSends a signed ModifyOrder; reports may restate accepts.
Cancel orderSends a signed L2CancelOrder transaction.
Cancel all ordersIterates cached open orders for the requested instrument.
Set leverage-Perp only; submits a signed UpdateLeverage tx.
Batch cancel ordersSequential fanout of up to 15 independent cancel transactions.
Query orderRequires credentials and REST lookup.
Query accountReplays the latest private WebSocket account state.
Mass statusBounded to account‑active markets from WS and REST reports.

SubmitOrderList and BatchCancelOrders sign and hand off each child transaction in order through the hash-correlated WebSocket sendTx path. The adapter allocates each nonce only after the prior child handoff completes. Each transaction therefore receives normal acknowledgement, rejection, and nonce recovery handling. Fanout is not atomic: it does not create grouped venue orders or provide OCO/OTO or bracket semantics.

UpdateLeverage is exposed as LighterExecutionClient::update_leverage(instrument_id, initial_margin_fraction, margin_mode). The initial_margin_fraction is in venue ticks (1e-4 fraction): 500 is 5% initial margin (20x leverage), 1000 is 10% (10x), and so on.

UpdateLeverage, CancelAllOrders, modify orders with integrator attributes, and conditional create orders are byte-pinned against the official Lighter v1.1.2 signer.

Order querying and reconciliation

FeaturePerpetualsSpotNotes
Query open ordersREST accountActiveOrders scoped by market.
Query order historyREST accountInactiveOrders with cursor pagination.
Order status updatesPrivate WebSocket order streams plus status reports.
Trade historyREST trades; credentials are required for account history.
Fill reportsREST and private WebSocket trade payloads.
Position reports-Perp only; replays cached position stream.
Account stateReplays the cached merged account state snapshot.
Mass statusCombines orders, fills, and cached positions.

Authenticated inactive-order and fill pagination rejects repeated cursors and stops after 1,000 pages. Fill reconciliation remains repeatable across calls while suppressing fills already emitted from the live WebSocket stream. Historical order and fill reports bind a mapped client index only to its matching venue order ID so reused numeric indexes cannot merge unrelated lifecycles.

Account and position management

Authenticated execution clients subscribe to these private streams:

  • account_all_orders: order status reports.
  • account_all_trades: fill reports.
  • account_all_positions: position snapshots.
  • account_all_assets: per-asset balance snapshots (spot balance plus perp collateral).
  • user_stats: perp-account margin rollup (collateral and available balance).

The adapter merges account_all_assets and user_stats into a single account state and emits it only after both streams have delivered their first frame.

The execution client requires credentials before connecting because private account streams and nonce refresh are mandatory. A client can be constructed without credentials, but live execution will not connect until private_key, account_index, and api_key_index resolve.

Perpetual positions use netting mode with one position per market; spot balances use account asset state. Each account_all_positions frame is a snapshot: cached markets omitted from the frame, or present with a zero position value, flatten. An empty positions map flattens all cached positions. Unmapped or unparsable rows with a non‑zero position remain keyed by market ID so they do not cause false flat reports.

FeaturePerpetualsSpotNotes
Account balancesMerged assets + user_stats, replayed from cache on query.
Position snapshots-Perp only; account_all_positions stream.
Netting positions-One Nautilus position per perpetual market.
Cross margin-Passed through LighterPositionMarginMode::Cross.
Isolated margin-Passed through LighterPositionMarginMode::Isolated.
Leverage updates-Signed UpdateLeverage transaction.
Spot margin / borrowing--Not supported.
Deposits / withdrawals--Use venue tools or Lighter APIs outside the trading adapter.

Liquidation and ADL handling

Event or fieldSupportNotes
Liquidation tradesAccount trade rows can parse as fills, with no special event.
Deleverage tradesAccount trade rows can parse as fills, with no special event.
Liquidation price reporting-Not supported; reports omit this field.
ADL event stream-Not supported.

Funding rates

Perpetual market_stats frames emit MarkPriceUpdate, IndexPriceUpdate, and FundingRateUpdate. The live funding update uses current_funding_rate as the upcoming estimate; funding_rate and funding_timestamp describe the last completed payment. Because market stats provide no future settlement time, live updates leave interval and next_funding_ns unset. Spot spot_market_stats frames emit IndexPriceUpdate.

Historical requests use public /api/v1/fundings rows at 1h resolution and set interval=60. direction=long stays positive, while short becomes negative. Pagination covers the requested range up to the adapter's page cap, subject to an explicit limit; see Rate limiting. Account‑specific positionFunding is not used.

Account tiers

Lighter account tiers set latency, rate limits, and fees. The execution client reads the tier from GET /api/v1/account and logs it, including unknown raw account_type values. It does not raise limits automatically because higher venue limits require IP registration.

TierLatency (maker / taker)REST weighted limitsendTx limitFees (maker / taker)Notes
Standard200 ms / 300 ms60 req/min60 req/min0 / 0Zero‑fee default tier.
Premium0 ms / 140-200 ms24,000 req/min4,000-40,000 req/min0.28-0.40 / 1.96-2.80 bpsLowest latency; scales with staked LIT.
Plus200 ms / 300 ms120,000 req/min8,000 req/min0.5 / 0.5 bpsRaised limits, standard latency.
Builder-240,000 req/min--Highest REST throughput.

Premium figures scale with staked LIT and can change. To use a higher tier, register the caller IP and set the quota explicitly (see Rate limiting).

Rate limiting

Lighter limits both IP and L1 addresses. Both clients default to standard‑account quotas; using higher account tiers requires IP registration and explicit client quotas:

  • rest_quota_per_min: REST read-bucket quota in requests per minute. Unset keeps 60 req/min. Available on both the data and execution clients.
  • sendtx_quota_per_min: transaction quota in requests per minute, metered in a bucket separate from reads. Unset keeps it at the standard 60 req/min, independent of rest_quota_per_min. Execution client only.

The REST limiter counts one token per call rather than venue endpoint weights. Set rest_quota_per_min for the effective endpoint mix: a 24,000 weighted req/min premium limit yields 40 calls/minute to endpoints with weight 600, such as /api/v1/trades and /api/v1/recentTrades.

The venue meters transactions per account across both transports in one bucket. The execution client enforces sendtx_quota_per_min with a single shared limiter across WebSocket sendTx (including order-list and cancel fanout) and the HTTP sendTx used for startup integrator approval. The public low-level HTTP sendTxBatch API uses the same limiter when called directly.

The clients share one WebSocket message limiter per venue URL. It paces non‑transaction control frames at 200 messages/minute across both clients. A closed‑loop subscription gate caps unacknowledged requests at 35, below the venue's 50‑message per‑IP ceiling; this count depends on acknowledgement latency, not send rate. sendTx does not count against the client‑message bucket.

ScopeVenue limitAdapter behavior
REST, standard account60 req/minDefault; set rest_quota_per_min to override.
REST, premium account24,000 weighted req/minLogged; set rest_quota_per_min to use it.
REST, plus account120,000 weighted req/minLogged; set rest_quota_per_min to use it.
REST, builder account240,000 weighted req/minLogged; set rest_quota_per_min to use it.
sendTx / sendTxBatch, standard60 req/minExecution orders use WebSocket sendTx.
sendTx / sendTxBatch, plus8,000 req/minSet sendtx_quota_per_min to use it.
sendTx / sendTxBatch, premium4,000-40,000 req/minSet sendtx_quota_per_min (scales with staked LIT).
Default transaction type limit40 req/minApplies to tx types not covered by volume quota.
L2UpdateLeverage transaction limit40 req/minRelevant to update_leverage.
Pending orders500/account, 16/marketVenue limit; adapter does not pre‑count it.
Active orders1,500/account, 1,000/marketVenue limit; adapter does not pre‑count it.

Common REST endpoint weights from the official docs:

Endpoint groupWeightAdapter behavior
sendTx, sendTxBatch, nextNonce6Tx calls use tx limiter; nextNonce uses REST.
accountInactiveOrders100Adapter counts one REST token per HTTP call.
trades, recentTrades600Adapter counts one REST token per HTTP call.
Other endpoints300Adapter counts one REST token per HTTP call.
Endpoint or transportLimitNotes
/api/v1/trades100 rowsAdapter paginates reconciliation at this cap.
/api/v1/accountInactiveOrders100 rowsAdapter follows next_cursor at this cap.
/api/v1/orderBookOrders250 levelsSnapshot depth is clamped to the venue cap.
/api/v1/candles500 rowsAdapter caps REST bar pages at this venue maximum.
/api/v1/fundings100 rowsAdapter paginates funding pages at this venue cap.
WebSocket connections255 / IPVenue limit.
WebSocket subscriptions / connection500Venue limit.
WebSocket unique accounts / connection500Venue limit.
WebSocket connections / minute255Venue limit.
WebSocket client messages / minute200Adapter paces non‑tx control frames at this cap.
WebSocket inflight messages50Venue cap; subscriptions use a 35-frame closed loop.
sendTxBatch batch size15 txsLow‑level API limit; fanout cap is also 15.
WebSocket keepalive2 minutesAdapter sends heartbeats every 30 seconds.
WebSocket outbound command queueNot cappedPaced before writes; no queue‑depth cap.

Historical bar and funding‑rate requests stop after 500 REST pages. This covers up to 250,000 bars or 49,500 hourly funding intervals. If the cap leaves part of the requested range uncovered, the HTTP client returns LighterHttpError::HistoryIncomplete instead of partial history and does not retry the capped request. Completion on the final allowed page remains successful. A request with an explicit start also remains successful when its explicit limit is satisfied. The data client logs the incomplete error and emits no response; narrow the requested range to continue.

Volume quota and no-fill quoting

Volume quota is separate from transport limits. L2CreateOrder, L2CancelAllOrders, L2ModifyOrder, and L2CreateGroupedOrders spend it; completed volume and any free allowance replenish it. The adapter does not inspect remaining quota. See Lighter's Volume Quota documentation for current rules and figures.

Repeated no‑fill quote refreshes can exhaust this quota even when the WebSocket and sendTx limiters work. For live tests, prefer slower one‑sided quoting, wider refresh thresholds, testnet, or a bounded strategy that earns enough fills to replenish its quota.

Connection management

The WebSocket client sends heartbeats every 30 seconds and reconnects with exponential backoff from 250 milliseconds to 30 seconds. Private subscriptions use auth tokens with an 8‑hour maximum TTL; the adapter mints 7‑hour tokens, rotates them every 6 hours, and resubscribes. A transparent reconnect triggers a fresh token and account resubscription after tracked subscriptions start replaying.

On execution reconnect, the adapter starts a nonce‑baseline refresh through GET /api/v1/nextNonce. New signed transaction dispatch is rejected until that refresh, or its background retry, installs the replacement connection's nonce baseline.

Within a session, venue confirmations advance the local nonce window, definitive rejections or pre‑handoff failures may roll back its latest nonce, and stale state triggers a GET /api/v1/nextNonce resync. Outcomes that may have reached the venue retain their pending nonce and order identity for WebSocket or reconciliation recovery.

LighterExecutionClient::connect() waits up to 30 seconds for every account stream (account_all_orders, account_all_trades, account_all_positions, account_all_assets, user_stats) to deliver its first frame. Lighter has no REST source for account or position state, so connect() blocks on these streams as its only ground truth. Each attempt clears old position and account caches before awaiting the current session's frames. Transparent WebSocket reconnects do not re‑enter connect(): they retain cached positions until the next account_all_positions frame applies the snapshot replacement rules.

API credentials

Lighter signing requires all three credential values:

  • Account index: numeric Lighter account identifier.
  • API key index: numeric API key slot. Use the user-created key index assigned by Lighter, avoid reserved low indexes, and do not use 255; it is an apikeys query sentinel, not a signing key.
  • API private key: 40-byte hex private key, with or without a 0x prefix.

Config values take precedence. A missing config field, or a blank API private key (empty or whitespace only), falls back to the corresponding environment variable for the selected environment.

EnvironmentAPI key indexAPI private keyAccount index
MainnetLIGHTER_API_KEY_INDEXLIGHTER_API_SECRETLIGHTER_ACCOUNT_INDEX
TestnetLIGHTER_TESTNET_API_KEY_INDEXLIGHTER_TESTNET_API_SECRETLIGHTER_TESTNET_ACCOUNT_INDEX

Execution rejects incomplete credentials. The data client runs without credentials: its subscriptions and REST requests (instruments, book, trades, bars, funding) all use public endpoints.

Configuration

Data client configuration options

OptionDefaultDescription
base_url_httpNoneOptional REST URL override.
base_url_wsNoneOptional WebSocket URL override.
proxy_urlNoneOptional proxy URL for HTTP and WebSocket.
environmentMainnetLighterEnvironment::Mainnet or Testnet.
account_indexNoneOptional factory field; public data calls do not use it.
api_key_indexNoneOptional factory field; public data calls do not use it.
private_keyNoneOptional factory field; public data calls do not use it.
http_timeout_secs60HTTP request timeout in seconds.
ws_timeout_secs30WebSocket connection and reconnection timeout.
update_instruments_interval_mins60Instrument metadata refresh interval in minutes.
rest_quota_per_minNoneREST quota override; unset keeps 60 req/min.
transport_backendDefaultWebSocket transport backend.

Execution client configuration options

OptionDefaultDescription
trader_idTRADER-001Nautilus trader identifier.
account_idLIGHTER-001Nautilus account identifier for the venue.
account_indexNoneLighter account index.
api_key_indexNoneLighter API key slot.
private_keyNoneHex private key for auth and L2 transaction signing.
base_url_httpNoneOptional REST URL override.
base_url_wsNoneOptional WebSocket URL override.
proxy_urlNoneOptional proxy URL for HTTP and WebSocket.
environmentMainnetLighterEnvironment::Mainnet or Testnet.
http_timeout_secs60HTTP request timeout in seconds.
ws_timeout_secs30WebSocket connection and reconnection timeout.
market_order_slippage_bps50Slippage cap (bps) for MARKET / STOP_MARKET / MIT.
rest_quota_per_minNoneREST quota override; unset keeps 60 req/min.
sendtx_quota_per_minNoneTransaction quota override; unset keeps 60 req/min.
transport_backendDefaultWebSocket transport backend.

Configuration example

use nautilus_lighter::{
    common::enums::LighterEnvironment,
    config::{LighterDataClientConfig, LighterExecClientConfig},
};

let data_config = LighterDataClientConfig::builder()
    .environment(LighterEnvironment::Testnet)
    .build();

let exec_config = LighterExecClientConfig::builder()
    .trader_id(trader_id)
    .account_id(account_id)
    .environment(LighterEnvironment::Testnet)
    .build();

The execution config resolves credentials from the matching testnet environment variables; set its credential fields directly to override them. Use LiveExecEngineConfig.reconciliation_instrument_ids to scope reconciliation and reconciliation_lookback_mins to bound inactive order and fill replay.

Official documentation

Contributing

For additional features or to contribute to the Lighter adapter, please see our contributing guide.

On this page