nautilus_paramx connects Nautilus Trader 2.0 to ParamX.
Tiles become Nautilus instruments. The data client streams their books, quotes and trades,
and the execution client signs orders with your trading key. The same strategy code runs:
- paper, in Nautilus’s sandbox, against ParamX’s live books;
- live on devnet, the paper-trading exchange with test USDC;
- live on mainnet.
nautilus_trader.live.clients), and needs nautilus_trader>=2.0.0rc5 and Python 3.12+.
Install
From a checkout of nautilus-paramx:An account
Orders need an account whose trading key is registered.paramx-setup, this repo’s CLI,
makes one:
paramx-setup writes:
.paramx/keys.jsonon mainnet;.paramx/devnet-keys.jsonon devnet;- or any path, with
keys_file.
Instruments
Tiles. Each grid tile is aBinaryOption:
- Id:
BTC-5M-20260927T2300-109000-109100.PARAMX, meaning the underlying, the grid, the window’s UTC start, and the min and max strike in USD. raw_symbol: the decimal contract id.- Units: USDC; prices from 0 to 1, and sizes, both to 6 decimals.
- Timing:
activation_nsandexpiration_nsare the window. - Fees:
taker_fee0.003 andmaker_fee0. info:contract_id,underlying_id,grid, the strikes, andstart/endin Unix seconds.
IndexInstrument, BTC.PARAMX. Its info holds
its grids (window length and price step), so a strategy can build tile ids itself (see
nautilus_paramx.tiles and examples/mm_strategy.py).
Which tiles load is set in ParamxDataClientConfig:
Loading and rolling:
- Any tile works. Subscribing to, or requesting, a tile outside the loaded set builds it from its id.
- Rolling: every
roll_interval_secs, the data client publishes the tiles of windows that have come into range. - Settlement: a tile you subscribe to with
subscribe_instrument_closegets anInstrumentCloseonce it settles, priced 1 if touched and 0 if not.
Data
The WebSocket client:
- reconnects with backoff, including on the server’s restart close (1012);
- resubscribes everything;
- reconnects if a message’s
seqskips.
Execution
ParamxExecClientConfig(environment=..., keys_file=..., default_expiry_secs=3600, account_id="PARAMX-001"):
a MARGIN account in USDC, with NETTING positions.
Placing orders:
- Totals: a limit’s price is per share, so the order’s totals are rounded to micro-USDC: a buy’s total down (it never pays more than its price), a sell’s up.
- Expiry: without GTD, an order expires at its tile’s end or after
default_expiry_secs, whichever is sooner. - Idempotent submits: the client order id is sent to ParamX, so a retried submit can’t place twice.
- Shorts: selling past zero opens a short, which locks $1 a share. Both sides of one tile can be quoted from flat.
cancel_ordersigns a cancel with the order’s own nonce and expiry.cancel_all_ordersuses the exchange’s cancel-all, one signature for the whole tile.cancel_ordersuses cancel-batch.- A cancel takes the order off the book at once. Its signed intent stays valid until its expiry, but only the operator can submit fills, and only from the book. Short expiries keep that window small.
/orders/batch, up to 20 orders;
/orders/cancel-batch, up to 200), so a requote of many orders costs a few requests.
Events come from the account’s WebSocket channel:
- Fills carry the liquidity side and the commission (0.30% of the total, for the taker only).
- Cancels and expiries, including an IOC’s dropped remainder.
- Cash, reported as the account state.
.with_reconciliation(reconciliation=True)), a new
node rebuilds its state from the exchange:
- resting orders, from
/v1/accounts/{a}/orders, which also returns what a cancel needs to sign; - positions, from
/positions; - fills, from
/fills.
EXTERNAL strategy. Cancel them with
cancel_all_orders(tile, strategy_only=False), or claim their instruments with
StrategyConfig(external_order_claims=[...]).
Running it
add_simulated_exec_client(PARAMX, SandboxExecutionClientFactory(), SandboxExecutionClientConfig(venue=Venue.from_str(PARAMX), account_type=AccountType.MARGIN, ...))
(see examples/paper_mm.py).
Examples
Limits and differences
- Tiles expire. A strategy has to move to new windows as time passes, like
mm_strategy.py’s roll. - Settlement doesn’t close Nautilus positions. A settled tile pays out on the exchange, and the new cash arrives as an account update, but the position stays open in Nautilus. Use
InstrumentCloseto close your own records; the next restart’s reconciliation matches the exchange. - Big tiles (the web app’s multi-tile contracts) aren’t mapped.
- Market hours: tiles entirely outside an underlying’s sessions can’t be traded, and aren’t loaded unless you ask for them by id.
- Nautilus 2.0 Python API:
Strategy.cancel_orderandmodify_ordertake a client order id, andsubscribe_book_deltasneeds aBookType;- order lists come only from brackets, which ParamX doesn’t support;
- use
node.handle().stop()to stop a node from another thread.