FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
schema.hpp
Go to the documentation of this file.
1#pragma once
2// Declarative config schema (8.7): every known key with its section, type and whether it
3// is required. config.cpp validates the parsed TOML against this table and reports
4// errors with line numbers; an unknown key is an error too, so a misspelled or renamed key never
5// leaves the real one at its default.
6// Sections "sim", "backtest" and "storage" are free-form (owned by the sim, backtest and store
7// libraries), and a [venues.<name>] key this table does not list belongs to the connector its
8// `kind` names: the venue declares, validates and documents it
9// (include/fastmm/venues/registry.hpp).
10//
11// The doc strings are the key tables of docs/reference/configuration.md
12// (python3 tools/docs_config_ref.py regenerates them): say what the key does, its unit and its
13// default.
14#include <cstddef>
15#include <span>
16#include <string_view>
17
18namespace fastmm {
19
20enum class KeyType : unsigned char { String, Int, Float, Bool, IntArray, StringArray, Table, Any };
21
22struct KeySpec {
23 std::string_view section; // "engine", "venues.*", "instruments[]", "strategy", ...
24 std::string_view key;
27 std::string_view doc;
28};
30inline constexpr KeySpec kConfigSchema[] = {
31 // [engine]
32 {"engine",
33 "name",
35 false,
36 "session name, used in logs, journal file names and the status file (default \"fastmm\")"},
37 {"engine",
38 "cpu",
40 false,
41 "CPU core of the engine thread; -1 = not pinned (default -1)"},
42 {"engine",
43 "net_cpus",
45 false,
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 [])"},
49 {"engine",
50 "spin_mode",
52 false,
53 "busy (spin forever) | adaptive (spin briefly, then block until work arrives; use on WSL2 "
54 "and laptops) (default adaptive)"},
55 {"engine",
56 "net_backend",
58 false,
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)"},
61 {"engine",
62 "threading",
64 false,
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)"},
68 {"engine",
69 "journal",
71 false,
72 "record every consumed event to a .fmj journal (default true)"},
73 {"engine", "journal_dir", KeyType::String, false, "directory for journals (default \"runs\")"},
74 {"engine",
75 "journal_sync",
77 false,
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)"},
80 {"engine",
81 "journal_max_bytes",
83 false,
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)"},
86 {"engine",
87 "journal_retention_days",
89 false,
90 "delete .fmj files in journal_dir last written more than this many days ago, at start; "
91 "0 = keep everything (default 0)"},
92 {"engine",
93 "epoch_file",
95 false,
96 "session epoch file, keeps client order ids unique across restarts (default "
97 "\"runs/session_epoch\")"},
98 {"engine",
99 "kill_file",
101 false,
102 "latched kill switch and cumulative PnL, so [risk] max_loss is a budget across restarts "
103 "(default \"<journal_dir>/<name>.kill\")"},
104 {"engine",
105 "instance_lock",
107 false,
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)"},
111 {"engine",
112 "lock_file",
114 false,
115 "the instance lock's file (default \"<journal_dir>/<name>.lock\")"},
116 {"engine",
117 "handoff_timeout_ms",
119 false,
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)"},
122 {"engine",
123 "ack_timeout_ms",
125 false,
126 "force-cancel an order whose ack has not arrived within this long, ms; 0 = off (default 0)"},
127 {"engine",
128 "queue_conservatism",
130 false,
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)"},
134 {"engine",
135 "flatten_interval_ms",
137 false,
138 "how often an operator flatten looks at the position left and sends the next reduce-only "
139 "slice, ms (default 500)"},
140 {"engine",
141 "flatten_timeout_ms",
143 false,
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)"},
146 {"engine",
147 "flatten_slippage_bps",
149 false,
150 "how far through the touch a flatten prices its orders when the command gives no "
151 "--max-slippage-bps, basis points (default 25)"},
152 {"engine",
153 "rng_seed",
155 false,
156 "seed of the strategy random generator ctx.rng() (default 1)"},
157 {"engine",
158 "md_ring_bytes",
160 false,
161 "market-data ring per venue, bytes, a power of two (default 4194304)"},
162 {"engine",
163 "order_ring_bytes",
165 false,
166 "order-event ring per venue, bytes, a power of two (default 1048576)"},
167 {"engine",
168 "journal_ring_bytes",
170 false,
171 "engine-to-journal ring, bytes, a power of two (default 16777216)"},
172 {"engine",
173 "max_events_per_step",
175 false,
176 "events taken from each ring per engine iteration (default 64)"},
177 {"engine",
178 "feed_budget_per_ring",
180 false,
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)"},
184 {"engine",
185 "net_spin_dedicated",
187 false,
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)"},
191 {"engine",
192 "crossed_grace_ms",
194 false,
195 "tolerate a crossed book this long before pulling its quotes, ms (default 100)"},
196 {"engine",
197 "latency_publish_ms",
199 false,
200 "latency histogram publish interval, ms (default 1000)"},
201 {"engine",
202 "tsc_recalibrate_s",
204 false,
205 "fastmm-live: TSC recalibration period, s; 0 = off (default 10)"},
206 {"engine",
207 "timer_slack_ns",
209 false,
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)"},
213 {"engine",
214 "restore_position",
216 false,
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"},
219 {"engine",
220 "lock_memory",
222 false,
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)"},
225 {"engine",
226 "cpu_dma_latency_us",
228 false,
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)"},
232 {"engine",
233 "rt_priority",
235 false,
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)"},
240 {"engine",
241 "net_rt_priority",
243 false,
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)"},
246 {"engine",
247 "log_irq_affinity",
249 false,
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)"},
253 {"engine",
254 "min_requote_ticks",
256 false,
257 "keep a resting quote whose price is within this many ticks of the desired price (default 1)"},
258 {"engine",
259 "min_requote_interval_ms",
261 false,
262 "change the same quote slot at most this often, ms (default 50)"},
263 {"engine",
264 "min_qty_bps",
266 false,
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)"},
270 {"engine",
271 "supports_replace",
273 false,
274 "let the quote manager amend orders in place where the venue supports it (default true)"},
275 {"engine",
276 "reject_backoff_ms",
278 false,
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)"},
281 {"engine",
282 "reject_backoff_max_ms",
284 false,
285 "cap of the doubling reject backoff, ms (default 60000)"},
286 {"engine",
287 "quote_token_reserve",
289 false,
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)"},
293 {"engine",
294 "on_kill",
296 false,
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) "
299 "(default exit)"},
300 // [venues.<name>]
301 {"venues.*",
302 "kind",
304 true,
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"},
308 {"venues.*",
309 "ws_api_url",
311 false,
312 "order-entry WebSocket API URL, where the venue has one"},
313 {"venues.*", "rest_url", KeyType::String, false, "REST base URL"},
314 {"venues.*", "api_key", KeyType::String, false, "API key, written as \"${VARIABLE}\""},
315 {"venues.*", "api_secret", KeyType::String, false, "API secret, written as \"${VARIABLE}\""},
316 {"venues.*",
317 "api_passphrase",
319 false,
320 "API key passphrase where the venue has one (OKX, Coinbase Exchange), written as "
321 "\"${VARIABLE}\""},
322 {"venues.*",
323 "testnet",
325 false,
326 "the endpoints are a testnet or demo environment (default true)"},
327 {"venues.*",
328 "supports_replace",
330 false,
331 "the venue can amend an order in place (default false)"},
332 {"venues.*",
333 "insecure_tls",
335 false,
336 "skip TLS certificate verification; local simulator only (default false)"},
337 {"venues.*",
338 "ca_file",
340 false,
341 "extra CA certificate, for example tests/fixtures/tls/cert.pem for the local simulator"},
342 {"venues.*",
343 "public_only",
345 false,
346 "market data only: no keys, no private sessions, no orders on this venue (default false)"},
347 {"venues.*",
348 "pool_of",
350 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)"},
354 {"venues.*",
355 "recv_window_ms",
357 false,
358 "validity window of signed requests, ms (default 3000)"},
359 {"venues.*",
360 "source_ip",
362 false,
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)"},
365 {"venues.*",
366 "source_interface",
368 false,
369 "the venue's connections leave this host from this network interface's address; exclusive "
370 "with source_ip (default: the kernel chooses)"},
371 {"venues.*",
372 "fill_audit_interval_s",
374 false,
375 "fastmm-live compares the venue's trade history with the stored fills this often, s; 0 = off "
376 "(default 0, at least 10)"},
377 {"venues.*",
378 "fill_audit_lag_s",
380 false,
381 "a fill audit reads up to this long ago, s, so the execution replay has booked what it will "
382 "(default 180)"},
383 {"venues.*",
384 "fill_audit_mode",
386 false,
387 "\"report\" logs and counts what differs; \"book\" also books the executions the engine "
388 "missed "
389 "(default \"report\")"},
390 {"venues.*",
391 "fees",
393 false,
394 "[venues.<name>.fees] table: maker_bps and taker_bps, used for PnL"},
395 {"venues.*.fees",
396 "maker_bps",
398 false,
399 "maker fee, bps; negative = rebate (default 0)"},
400 {"venues.*.fees", "taker_bps", KeyType::Float, false, "taker fee, bps (default 0)"},
401 {"venues.*",
402 "treasury",
404 false,
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",
408 "enabled",
410 false,
411 "move the asset between the pool's accounts (default false)"},
412 {"venues.*.treasury",
413 "dry_run",
415 false,
416 "log and journal the planned transfers without sending them; needs no transfer key (default "
417 "false)"},
418 {"venues.*.treasury",
419 "asset",
421 false,
422 "the asset kept spread, for example USDT; required when enabled"},
423 {"venues.*.treasury",
424 "weights",
426 false,
427 "{ <account> = <weight>, ... }: each account's share of the pool's free balance (default 1 "
428 "each)"},
429 {"venues.*.treasury",
430 "min_free",
432 false,
433 "{ <account> = \"<decimal>\", ... }: free balance an account is topped up to and never gives "
434 "below (default 0)"},
435 {"venues.*.treasury",
436 "threshold",
438 false,
439 "an account is short below its target times (1 - threshold), 0 to 1 (default 0.2)"},
440 {"venues.*.treasury",
441 "min_amount",
443 false,
444 "smallest transfer, decimal (default 0)"},
445 {"venues.*.treasury",
446 "max_amount",
448 false,
449 "largest transfer, decimal; 0 = no cap (default 0)"},
450 {"venues.*.treasury",
451 "step",
453 false,
454 "transfer amounts round down to a multiple of it, decimal (default 0.01)"},
455 {"venues.*.treasury",
456 "interval_s",
458 false,
459 "how often the balances are looked at and a transfer in flight is asked for, s (default 10)"},
460 {"venues.*.treasury",
461 "min_interval_s",
463 false,
464 "least time between two transfers sent, s (default 60)"},
465 {"venues.*.treasury",
466 "max_per_hour",
468 false,
469 "transfers sent in any hour; 0 = no limit (default 12)"},
470 {"venues.*.treasury",
471 "cooldown_s",
473 false,
474 "no transfer for this long after one failed or timed out, s (default 300)"},
475 {"venues.*.treasury",
476 "timeout_s",
478 false,
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",
482 "settle_s",
484 false,
485 "longest wait after a transfer for both accounts' balances to report it, s (default 30)"},
486 {"venues.*.treasury",
487 "state_file",
489 false,
490 "the transfers in flight, the id counter and the limiter across restarts (default "
491 "<journal_dir>/<engine name>.<primary>.treasury)"},
492 // [[instruments]]
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"},
497 {"instruments[]",
498 "asset_class",
500 false,
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"},
506 {"instruments[]",
507 "min_notional",
509 false,
510 "smallest order value, settlement currency (base coin for an inverse contract), "
511 "decimal"},
512 {"instruments[]",
513 "contract_multiplier",
515 false,
516 "units of the underlying per contract, decimal; PnL and notional use it (default 1)"},
517 {"instruments[]",
518 "enabled",
520 false,
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"},
526 {"instruments[]",
527 "maker_bps",
529 false,
530 "maker fee of this instrument, bps; negative = rebate. Overrides the venue's fees table"},
531 {"instruments[]",
532 "taker_bps",
534 false,
535 "taker fee of this instrument, bps. Overrides the venue's fees table"},
536 {"instruments[]",
537 "initial_margin",
539 false,
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)"},
543 // [strategy]
544 {"strategy", "name", KeyType::String, true, "registered strategy name (see --list-strategies)"},
545 {"strategy",
546 "max_param_age_ms",
548 false,
549 "disable quoting before the first parameter update and while none was applied for this long, "
550 "ms of engine time (default 0: off)"},
551 {"strategy",
552 "state_file",
554 false,
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)"},
557 {"strategy",
558 "state_interval_s",
560 false,
561 "how often the state is written, seconds of engine time (default 300)"},
562 {"strategy",
563 "state_snapshot_interval_s",
565 false,
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)"},
568 {"strategy",
569 "params",
571 false,
572 "[strategy.params] table: the strategy's parameters"},
573 {"strategy.params", "*", KeyType::Any, false, "strategy parameters (validated by the schema)"},
574 // [risk]
575 {"risk", "max_order_qty", KeyType::Any, false, "largest order quantity, decimal"},
576 {"risk",
577 "max_order_notional",
579 false,
580 "largest order value, settlement currency (base coin for an inverse contract), decimal"},
581 {"risk",
582 "max_position",
584 false,
585 "largest absolute position per instrument, counting same-side open orders, decimal"},
586 {"risk", "max_open_orders", KeyType::Int, false, "open orders per instrument"},
587 {"risk",
588 "price_collar_bps",
590 false,
591 "refuse limit prices further than this from the mid, bps"},
592 {"risk",
593 "fat_finger_bps",
595 false,
596 "refuse limit prices further than this from the last trade, bps"},
597 {"risk",
598 "stale_md_ms",
600 false,
601 "refuse orders when the instrument's book is older than this, ms"},
602 {"risk",
603 "max_loss",
605 false,
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"},
608 {"risk",
609 "max_gross_notional",
611 false,
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"},
614 {"risk",
615 "max_net_notional",
617 false,
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"},
621 {"risk",
622 "burst",
624 false,
625 "token-bucket capacity, orders (default orders_per_sec)"},
626 {"risk",
627 "stp",
629 false,
630 "self-trade prevention against our own resting orders (default true)"},
631 {"risk",
632 "max_feed_lag_ms",
634 false,
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)"},
638 {"risk",
639 "check_balance",
641 false,
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)"},
644 {"risk",
645 "underlying",
647 false,
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.*",
651 "max_net",
653 true,
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"},
657 // [gateway]
658 {"gateway",
659 "orders_per_sec",
661 false,
662 "fastmm-gateway: new orders and replaces per second per venue, over every attached strategy "
663 "(default 0: off)"},
664 {"gateway",
665 "burst",
667 false,
668 "fastmm-gateway: token-bucket capacity of orders_per_sec, orders (default orders_per_sec)"},
669 {"gateway",
670 "max_open_notional",
672 false,
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"},
675 {"gateway",
676 "max_loss",
678 false,
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"},
682 {"gateway",
683 "max_gross_notional",
685 false,
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"},
688 {"gateway",
689 "max_net_notional",
691 false,
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"},
694 {"gateway",
695 "underlying",
697 false,
698 "fastmm-gateway: one [gateway.underlying.BTC] table per base asset: the account's net limit, "
699 "over every strategy and venue"},
700 {"gateway",
701 "check_balance",
703 false,
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.*",
707 "max_net",
709 true,
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"},
712 {"gateway",
713 "shared",
715 false,
716 "fastmm-gateway: one [gateway.shared.\"venue:symbol\"] table per instrument that several "
717 "strategies may trade at once (default: one strategy per instrument)"},
718 {"gateway.shared.*",
719 "primary",
721 false,
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"},
725 // [accounting]
726 {"accounting",
727 "reporting_currency",
729 false,
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)"},
733 {"accounting",
734 "fx",
736 false,
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"},
741 {"accounting",
742 "mark",
744 false,
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)"},
748 {"accounting",
749 "stale_mark_ms",
751 false,
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)"},
754 {"accounting",
755 "stale_funding_ms",
757 false,
758 "a venue's funding rate older than this is stale (ctx.funding flags it), ms (default "
759 "180000)"},
760 {"accounting",
761 "stale_fx_ms",
763 false,
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"},
767 // [logging]
768 {"logging",
769 "level",
771 false,
772 "trace | debug | info | warn (or warning) | error | off (default info)"},
773 {"logging", "file", KeyType::String, false, "log file; empty = stderr only"},
774 {"logging",
775 "mirror_level",
777 false,
778 "records at or above this level are also written to stderr (default warn)"},
779 {"logging",
780 "status_interval_s",
782 false,
783 "seconds between the per-venue status lines, 0 = none (default 1 with a log file, 10 on "
784 "stderr)"},
785 // free-form
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"},
790};
791
792[[nodiscard]] inline std::span<const KeySpec> config_schema() noexcept {
793 return kConfigSchema;
795
796} // namespace fastmm
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
Definition schema.hpp:24
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