36 "session name, used in logs, journal file names and the status file (default \"fastmm\")"},
41 "CPU core of the engine thread; -1 = not pinned (default -1)"},
46 "CPU cores of the network threads, one per venue in order; with spin_mode = \"adaptive\" a "
47 "thread whose core it shares with the engine or another network thread blocks when idle "
48 "instead of spinning (default [])"},
53 "busy (spin forever) | adaptive (spin briefly, then block until work arrives; use on WSL2 "
54 "and laptops) (default adaptive)"},
59 "event loop of fastmm-live and fastmm-sim-exchange: epoll | io_uring (Linux 5.13 or newer; "
60 "falls back to epoll with a warning) (default epoll)"},
65 "fastmm-live: split (engine and network threads, rings between them) | single (one venue; "
66 "its network loop, the engine and order sending run on the engine thread, cpu; net_cpus is "
67 "ignored) (default split)"},
72 "record every consumed event to a .fmj journal (default true)"},
73 {
"engine",
"journal_dir",
KeyType::String,
false,
"directory for journals (default \"runs\")"},
78 "how far a journal write is pushed: async (msync every 100 ms; survives the process dying) | "
79 "fdatasync (msync + fdatasync on the same tick; survives power loss) (default async)"},
84 "roll the journal over to the next part at this size, bytes; each part is a complete journal "
85 "(\"x.fmj\", \"x.1.fmj\", ...); 0 = one file per session (default 0)"},
87 "journal_retention_days",
90 "delete .fmj files in journal_dir last written more than this many days ago, at start; "
91 "0 = keep everything (default 0)"},
96 "session epoch file, keeps client order ids unique across restarts (default "
97 "\"runs/session_epoch\")"},
102 "latched kill switch and cumulative PnL, so [risk] max_loss is a budget across restarts "
103 "(default \"<journal_dir>/<name>.kill\")"},
108 "fastmm-live: take an exclusive lock on lock_file before touching the account and refuse to "
109 "start (exit code 8) while another process holds it; --standby and --takeover take it "
110 "whatever this says (default false)"},
115 "the instance lock's file (default \"<journal_dir>/<name>.lock\")"},
117 "handoff_timeout_ms",
120 "fastmm-live --takeover: how long to wait for the running session to release the instance "
121 "lock once it accepted the handoff, ms (default 75000)"},
126 "force-cancel an order whose ack has not arrived within this long, ms; 0 = off (default 0)"},
128 "queue_conservatism",
131 "the queue position estimate ctx.queue_ahead, from 0 to 1: at 0 a level's shrink is shared "
132 "between cancels ahead of and behind our order, at 1 cancels never move it up (default 1.0; "
133 "a backtest uses [backtest] queue_conservatism when set)"},
135 "flatten_interval_ms",
138 "how often an operator flatten looks at the position left and sends the next reduce-only "
139 "slice, ms (default 500)"},
141 "flatten_timeout_ms",
144 "how long an operator flatten keeps trying before it gives up and leaves the position, ms; "
145 "0 = until it is flat or an operator stops it (default 60000)"},
147 "flatten_slippage_bps",
150 "how far through the touch a flatten prices its orders when the command gives no "
151 "--max-slippage-bps, basis points (default 25)"},
156 "seed of the strategy random generator ctx.rng() (default 1)"},
161 "market-data ring per venue, bytes, a power of two (default 4194304)"},
166 "order-event ring per venue, bytes, a power of two (default 1048576)"},
168 "journal_ring_bytes",
171 "engine-to-journal ring, bytes, a power of two (default 16777216)"},
173 "max_events_per_step",
176 "events taken from each ring per engine iteration (default 64)"},
178 "feed_budget_per_ring",
181 "fastmm-live: events the engine takes from one input ring before the next ring's turn; 1 "
182 "interleaves the rings, so a burst of one account's order events does not hold up the "
183 "market data behind it (default 64)"},
185 "net_spin_dedicated",
188 "with spin_mode = \"adaptive\": a network thread pinned to a core of its own (net_cpus, "
189 "shared with neither the engine nor another network thread) polls without ever blocking, as "
190 "with \"busy\"; threads on shared or unpinned cores still block when idle (default false)"},
195 "tolerate a crossed book this long before pulling its quotes, ms (default 100)"},
197 "latency_publish_ms",
200 "latency histogram publish interval, ms (default 1000)"},
205 "fastmm-live: TSC recalibration period, s; 0 = off (default 10)"},
210 "fastmm-live: timer slack of its threads, ns; how late a timed wait may end (adaptive "
211 "spin_mode: the engine's idle wait, at most 1 ms, and with threading = \"single\" a 50 us "
212 "sleep); 0 = the kernel's, 50000 (default 0)"},
217 "carry the previous session's positions over from the store and replay the venue's executions "
218 "since its last recorded fill (venues that can replay executions); default true"},
223 "fastmm-live: mlockall() the process, so no page is swapped out or faulted in on the hot "
224 "path; needs ulimit -l above the process size, a warning otherwise (default false)"},
226 "cpu_dma_latency_us",
229 "fastmm-live: holds /dev/cpu_dma_latency at this many us for the session, so no CPU enters "
230 "an idle state that takes longer to wake; 0 keeps every CPU polling in C0; needs write "
231 "access to the device, a warning otherwise; -1 = no request (default -1)"},
236 "fastmm-live: SCHED_FIFO priority 1..99 of the engine thread (with threading = \"single\" it "
237 "also runs the network loop); needs CAP_SYS_NICE or ulimit -r at least as high, a warning "
238 "otherwise; a busy-spinning FIFO thread starves the kernel's threads on its core unless the "
239 "core is isolated; 0 = the default scheduler (default 0)"},
244 "fastmm-live: SCHED_FIFO priority 1..99 of the network threads, as rt_priority; ignored with "
245 "threading = \"single\"; 0 = the default scheduler (default 0)"},
250 "fastmm-live: at start, log each network interface's queue interrupts with the CPUs they may "
251 "run on and which of them are cpu or net_cpus, and warn about any that may run on the "
252 "engine's CPU; read only (default false)"},
257 "keep a resting quote whose price is within this many ticks of the desired price (default 1)"},
259 "min_requote_interval_ms",
262 "change the same quote slot at most this often, ms (default 50)"},
267 "keep a resting quote whose remaining quantity covers this share of the desired quantity, "
268 "bps (default 8000 = 80 %)"},
269 {
"engine",
"post_only",
KeyType::Bool,
false,
"send quotes as post-only orders (default true)"},
274 "let the quote manager amend orders in place where the venue supports it (default true)"},
279 "after a venue reject other than a post-only cross, no new orders on that side for this "
280 "long, doubling with each further reject, ms; 0 = off (default 1000)"},
282 "reject_backoff_max_ms",
285 "cap of the doubling reject backoff, ms (default 60000)"},
287 "quote_token_reserve",
290 "order tokens of [risk] orders_per_sec a quote that does not reduce the position leaves for "
291 "the strategy's own orders and for quotes that do; quotes short of a token wait and are "
292 "placed in turn as tokens return, not refused (default 0)"},
297 "fastmm-live after a kill switch the engine trips itself ([risk] max_loss, a full ring, every "
298 "venue killed): exit (normal shutdown, exit code 6) | stay (keep running with quoting off) "
305 "name of a registered connector; the keys it adds to this section are its own "
306 "(see Connectors below)"},
307 {
"venues.*",
"ws_url",
KeyType::String,
false,
"market-data WebSocket URL"},
312 "order-entry WebSocket API URL, where the venue has one"},
314 {
"venues.*",
"api_key",
KeyType::String,
false,
"API key, written as \"${VARIABLE}\""},
315 {
"venues.*",
"api_secret",
KeyType::String,
false,
"API secret, written as \"${VARIABLE}\""},
320 "API key passphrase where the venue has one (OKX, Coinbase Exchange), written as "
326 "the endpoints are a testnet or demo environment (default true)"},
331 "the venue can amend an order in place (default false)"},
336 "skip TLS certificate verification; local simulator only (default false)"},
341 "extra CA certificate, for example tests/fixtures/tls/cert.pem for the local simulator"},
346 "market data only: no keys, no private sessions, no orders on this venue (default false)"},
351 "name of another [venues.<name>] of the same kind whose instruments this account takes orders "
352 "for, with its own keys, balances and order-count windows; no instruments or market data of "
353 "its own (default none)"},
358 "validity window of signed requests, ms (default 3000)"},
363 "the venue's connections leave this host from this address, IPv4 or IPv6; it must be one of "
364 "the host's (default: the kernel chooses)"},
369 "the venue's connections leave this host from this network interface's address; exclusive "
370 "with source_ip (default: the kernel chooses)"},
372 "fill_audit_interval_s",
375 "fastmm-live compares the venue's trade history with the stored fills this often, s; 0 = off "
376 "(default 0, at least 10)"},
381 "a fill audit reads up to this long ago, s, so the execution replay has booked what it will "
387 "\"report\" logs and counts what differs; \"book\" also books the executions the engine "
389 "(default \"report\")"},
394 "[venues.<name>.fees] table: maker_bps and taker_bps, used for PnL"},
399 "maker fee, bps; negative = rebate (default 0)"},
400 {
"venues.*.fees",
"taker_bps",
KeyType::Float,
false,
"taker fee, bps (default 0)"},
405 "[venues.<primary>.treasury] table: transfers that keep one asset spread over the accounts "
406 "of the pool this venue is the primary of (fastmm-live)"},
407 {
"venues.*.treasury",
411 "move the asset between the pool's accounts (default false)"},
412 {
"venues.*.treasury",
416 "log and journal the planned transfers without sending them; needs no transfer key (default "
418 {
"venues.*.treasury",
422 "the asset kept spread, for example USDT; required when enabled"},
423 {
"venues.*.treasury",
427 "{ <account> = <weight>, ... }: each account's share of the pool's free balance (default 1 "
429 {
"venues.*.treasury",
433 "{ <account> = \"<decimal>\", ... }: free balance an account is topped up to and never gives "
434 "below (default 0)"},
435 {
"venues.*.treasury",
439 "an account is short below its target times (1 - threshold), 0 to 1 (default 0.2)"},
440 {
"venues.*.treasury",
444 "smallest transfer, decimal (default 0)"},
445 {
"venues.*.treasury",
449 "largest transfer, decimal; 0 = no cap (default 0)"},
450 {
"venues.*.treasury",
454 "transfer amounts round down to a multiple of it, decimal (default 0.01)"},
455 {
"venues.*.treasury",
459 "how often the balances are looked at and a transfer in flight is asked for, s (default 10)"},
460 {
"venues.*.treasury",
464 "least time between two transfers sent, s (default 60)"},
465 {
"venues.*.treasury",
469 "transfers sent in any hour; 0 = no limit (default 12)"},
470 {
"venues.*.treasury",
474 "no transfer for this long after one failed or timed out, s (default 300)"},
475 {
"venues.*.treasury",
479 "a transfer unresolved this long times out (cooldown_s; still asked for), and one the venue "
480 "has no record of fails, s (default 60)"},
481 {
"venues.*.treasury",
485 "longest wait after a transfer for both accounts' balances to report it, s (default 30)"},
486 {
"venues.*.treasury",
490 "the transfers in flight, the id counter and the limiter across restarts (default "
491 "<journal_dir>/<engine name>.<primary>.treasury)"},
493 {
"instruments[]",
"venue",
KeyType::String,
true,
"name of a [venues.<name>] table"},
494 {
"instruments[]",
"symbol",
KeyType::String,
true,
"venue symbol, for example BTCUSDT"},
495 {
"instruments[]",
"base",
KeyType::String,
false,
"base asset, for example BTC"},
496 {
"instruments[]",
"quote",
KeyType::String,
false,
"quote asset, for example USDT"},
501 "spot | perpetual (perp) | future (futures) | option | fx | equity (default spot)"},
502 {
"instruments[]",
"tick",
KeyType::Any,
true,
"price increment, decimal"},
503 {
"instruments[]",
"lot",
KeyType::Any,
true,
"quantity increment, decimal"},
504 {
"instruments[]",
"min_qty",
KeyType::Any,
false,
"smallest order quantity, decimal"},
505 {
"instruments[]",
"max_qty",
KeyType::Any,
false,
"largest order quantity, decimal"},
510 "smallest order value, settlement currency (base coin for an inverse contract), "
513 "contract_multiplier",
516 "units of the underlying per contract, decimal; PnL and notional use it (default 1)"},
521 "trade this instrument; false loads it without trading (default true)"},
522 {
"instruments[]",
"price_decimals",
KeyType::Int,
false,
"price display precision (default 8)"},
523 {
"instruments[]",
"expiry",
KeyType::String,
false,
"expiry of a derivative, ISO-8601"},
524 {
"instruments[]",
"strike",
KeyType::Any,
false,
"option strike, decimal"},
525 {
"instruments[]",
"option_type",
KeyType::String,
false,
"call | put"},
530 "maker fee of this instrument, bps; negative = rebate. Overrides the venue's fees table"},
535 "taker fee of this instrument, bps. Overrides the venue's fees table"},
540 "initial margin of a derivative, fraction of the notional (0.1 at 10x leverage), decimal; the "
541 "balance check and estimate use it (default: none, an order passes while the venue reports "
542 "margin available)"},
544 {
"strategy",
"name",
KeyType::String,
true,
"registered strategy name (see --list-strategies)"},
549 "disable quoting before the first parameter update and while none was applied for this long, "
550 "ms of engine time (default 0: off)"},
555 "file the strategy's state() is written to every state_interval_s and at the end, and "
556 "restore() reads at the start (a strategy that keeps state; default: none)"},
561 "how often the state is written, seconds of engine time (default 300)"},
563 "state_snapshot_interval_s",
566 "with a journal, how often the strategy's state() and the positions are written to it, "
567 "seconds of engine time, for a backtest to start from (default 0: never)"},
572 "[strategy.params] table: the strategy's parameters"},
573 {
"strategy.params",
"*",
KeyType::Any,
false,
"strategy parameters (validated by the schema)"},
575 {
"risk",
"max_order_qty",
KeyType::Any,
false,
"largest order quantity, decimal"},
577 "max_order_notional",
580 "largest order value, settlement currency (base coin for an inverse contract), decimal"},
585 "largest absolute position per instrument, counting same-side open orders, decimal"},
586 {
"risk",
"max_open_orders",
KeyType::Int,
false,
"open orders per instrument"},
591 "refuse limit prices further than this from the mid, bps"},
596 "refuse limit prices further than this from the last trade, bps"},
601 "refuse orders when the instrument's book is older than this, ms"},
606 "trip the kill switch when net PnL falls to -max_loss, settlement currency (the "
607 "[accounting] reporting_currency when set), decimal; latched across restarts in kill_file"},
609 "max_gross_notional",
612 "refuse an order that would take the portfolio's summed |position| at the last marks past "
613 "this, settlement currency (or reporting_currency), decimal"},
618 "refuse an order that would take the portfolio's signed position sum further past this, "
619 "settlement currency (or reporting_currency), decimal"},
620 {
"risk",
"orders_per_sec",
KeyType::Int,
false,
"token-bucket order rate, orders/s"},
625 "token-bucket capacity, orders (default orders_per_sec)"},
630 "self-trade prevention against our own resting orders (default true)"},
635 "pull a venue's quotes and refuse orders that could rest there without reducing the "
636 "position while its market data arrives this much later than its baseline, ms; resumes "
637 "100 ms after the last message over it (default 0: off)"},
642 "refuse an order the venue's reported balance, less this session's orders and fills since, "
643 "cannot cover; off for a venue until it reports balances (default true)"},
648 "one [risk.underlying.BTC] table per base asset: a net position limit over every instrument "
649 "and venue that trades it"},
650 {
"risk.underlying.*",
654 "refuse an order that would take the net position in this base asset, open orders on the "
655 "order's side included, further past this; base units (contracts times contract_multiplier, "
656 "inverse contracts at the mark), decimal; 0: tracked, no limit"},
662 "fastmm-gateway: new orders and replaces per second per venue, over every attached strategy "
668 "fastmm-gateway: token-bucket capacity of orders_per_sec, orders (default orders_per_sec)"},
673 "fastmm-gateway: refuse an order that would take the notional working at its venue, over "
674 "every attached strategy and both sides, past this; settlement currency, decimal"},
679 "fastmm-gateway: trip the account's kill switch when the net PnL of every strategy together, "
680 "carried across restarts in the gateway's kill file, reaches -max_loss; decimal, in "
681 "[accounting] reporting_currency when set"},
683 "max_gross_notional",
686 "fastmm-gateway: refuse an order that would take the sum of the account's |position| at the "
687 "marks past this, unless it reduces its instrument's position; decimal"},
692 "fastmm-gateway: refuse an order that would take the account's net position at the marks "
693 "further past this, unless it reduces its instrument's position; decimal"},
698 "fastmm-gateway: one [gateway.underlying.BTC] table per base asset: the account's net limit, "
699 "over every strategy and venue"},
704 "fastmm-gateway: refuse an order the venue's reported balance, less the orders and fills of "
705 "every attached strategy since, cannot cover (default true)"},
706 {
"gateway.underlying.*",
710 "fastmm-gateway: refuse an order that would take the account's net position in this base "
711 "asset, working orders on the order's side included, further past this; base units, decimal"},
716 "fastmm-gateway: one [gateway.shared.\"venue:symbol\"] table per instrument that several "
717 "strategies may trade at once (default: one strategy per instrument)"},
722 "fastmm-gateway: [engine] name of the strategy that books this instrument's events naming no "
723 "order (funding, liquidation fills, executions of orders placed elsewhere); default none: "
724 "the account alone books them"},
727 "reporting_currency",
730 "currency the PnL totals, [risk] max_loss and the exposure caps (and fastmm-gateway's) are in "
731 "when instruments settle in more than one; every other settlement currency needs a source in "
732 "[accounting.fx] (default unset: no conversion)"},
737 "[accounting.fx] table: one entry per other settlement currency, the instrument whose mid "
738 "prices it in reporting_currency (BTC = \"binance:BTCUSDT\"; a pair quoted the other way "
739 "round, USDTBTC, is inverted)"},
740 {
"accounting.fx",
"*",
KeyType::String,
false,
"FX source of one settlement currency"},
745 "what a derivative's position is valued at for the unrealized PnL, max_loss and the "
746 "exposure caps (fastmm-gateway's too): venue (its venue's mark price while it is fresh, else "
747 "the book's mid) | mid (default venue)"},
752 "a venue's mark or index price older than this is stale: ctx.mark and ctx.index flag it and "
753 "the position is valued at the mid, ms (default 15000)"},
758 "a venue's funding rate older than this is stale (ctx.funding flags it), ms (default "
764 "an [accounting.fx] rate whose source's book has not updated for this long is not current: "
765 "an order that adds exposure in that currency is refused; ms (default 60000; 0: a valid "
766 "book is enough). Not [risk] stale_md_ms: a quiet FX book can go seconds without a change"},
772 "trace | debug | info | warn (or warning) | error | off (default info)"},
773 {
"logging",
"file",
KeyType::String,
false,
"log file; empty = stderr only"},
778 "records at or above this level are also written to stderr (default warn)"},
783 "seconds between the per-venue status lines, 0 = none (default 1 with a log file, 10 on "
786 {
"sim",
"*",
KeyType::Any,
false,
"simulator settings"},
787 {
"sim.*",
"*",
KeyType::Any,
false,
"simulator sub-tables"},
788 {
"backtest",
"*",
KeyType::Any,
false,
"backtester settings"},
789 {
"backtest.*",
"*",
KeyType::Any,
false,
"backtester sub-tables"},
792[[nodiscard]]
inline std::span<const KeySpec>
config_schema() noexcept {
Definition strategy.hpp:16
constexpr KeySpec kConfigSchema[]
Definition schema.hpp:32
std::span< const KeySpec > config_schema() noexcept
Definition schema.hpp:794
KeyType
Definition schema.hpp:22
std::string_view key
Definition schema.hpp:26
std::string_view doc
Definition schema.hpp:29
KeyType type
Definition schema.hpp:27
std::string_view section
"engine", "venues.*", "instruments[]", "strategy", ...
Definition schema.hpp:25
bool required
Definition schema.hpp:28