Skip to content

Configuration reference

The engine reads config.json from the working directory. The file is JSONC: // and /* */ comments, trailing commas, and a UTF-8 BOM are accepted. Credentials and one-off overrides come from the environment; if a .env file is present it is loaded dotenv-style (KEY=value per line). Start from config.example.json and .env.example (both ship inside the image at /usr/share/doc/dio/; see DEPLOYMENT.md for extracting them).

The trading schema is strict: an unknown key under trading, an unknown key in the oracle section, or a key that does not apply to the selected venue causes the engine to exit at startup. This is deliberate, because a typo should not silently trade the wrong thing. A transient read or JSON syntax failure after a successful load keeps the last known good configuration; a schema violation always exits.

Contents

Top-level keys

Key Default Description
logging_level INFO One of DEBUG, INFO, WARN, ERROR, CRITICAL.
logging_sections unset Comma-separated section filters; unset enables all sections.
logging_width autodetect Message column width; autodetected from the terminal or COLUMNS.
cycle_mod 10000 Legacy interval for periodic background work; unused by current strategies.
latency_window_seconds 5.0 Rolling window for network latency profiling statistics.
latency_spike_threshold_us 10.0 Per-stage ceiling; a window that breaches it emits one INFO line naming the offending stages, their worst spike, and breach count.
latency_spike_report internal Which latency families emit spike logs: internal (per-domain pipeline), network (ws_ping/ws_feed/rest_request/signer), both, or none.
latency_spike_report_seconds 30.0 Minimum wall-clock seconds between per-domain internal spike log lines; 0 logs every window.
latency_network_spike_threshold_us 20000.0 Ceiling for the network spike family, in microseconds (20 ms); separate from the 10 us internal-operation target.
gc see below OCaml GC tunables applied before the engine starts.
oracle see below Capital-oracle knobs. Accepted only with the legacy flat trading array; the grouped layout uses trading.defaults.oracle and per-asset overrides.
trading required Grouped { "defaults", "assets" } object, or the legacy flat array of entries.
fng_check_threshold 1.5 Price movement (percent) from baseline that re-triggers a Fear & Greed check.
theme unset Default dashboard theme identifier; overridable with dio-dashboard --theme.
alert_level warn Lowest severity that raises the dashboard alert popup: off, info, warn, error, or critical.
flow_log disabled Decision-flow telemetry section; see below.
strategy_trace false Record per-cycle observable traces for the behavioral-equivalence harness.

GC tunables

Applied at process start through Gc.set. Units for minor_heap_size and major_heap_increment are OCaml words (8 bytes on 64-bit platforms).

Key Default Example value
minor_heap_size 33554432 524288
space_overhead 120 200
max_overhead 1000000 1000000
window_size 10 10
allocation_policy 2 2
major_heap_increment 100 1048576

The example values are tuned for tail latency rather than throughput: a small minor heap makes minor collections short and frequent instead of one long pause, and the smaller major-heap increment keeps heap-growth slices fine. Combined with the allocation reductions on the trading hot path, this targets a sub-10 us internal-pipeline p99.

Trading layout

trading accepts two shapes. The grouped layout is current; the flat array is the legacy form. Both flatten to the same per-instrument entries.

Grouped layout

"trading": {
  "defaults": {
    "strategy": {
      "hyperliquid": {
        "name": "jacobs_ladder",
        "sell_mult": "0.98",
        "sell_levels": false
      },
      "kraken": {
        "name": "jacobs_ladder",
        "sell_mult": "0.99",
        "accumulation_buffer": [0.1, 0.5]
      }
    },
    "overlay": {
      "name": "carry_overlay",
      "exchange": "hyperliquid",
      "min_entry_margin_ratio": 1.0
    },
    "oracle": {
      "target_survival": 0.95,
      "qty_cap_mult": 1.5,
      "refresh_seconds": 300
    }
  },
  "assets": [
    {
      "asset": "BTC",
      "oracle": { "target_survival": 0.65, "qty_cap_mult": 3.0 },
      "strategy": {
        "exchange": "hyperliquid",
        "symbol": "BTC/USDC",
        "qty": "0.0003",
        "grid_interval": [0.15, 0.18],
        "cpu_priority": 10
      },
      "overlay": {
        "leverage": 20,
        "start_ratio_pct": 25,
        "add_threshold_pct": 5,
        "max_ratio_pct": 75,
        "inventory": [{ "exchange": "kraken", "symbol": "BTC/USD" }]
      }
    }
  ]
}
Key Required Description
defaults.strategy no Defaults keyed by venue (kraken, hyperliquid, lighter, ibkr, alpaca), merged into every group whose strategy.exchange matches.
defaults.overlay no Defaults merged into every group's overlay section.
defaults.oracle no Global oracle knobs, equivalent to the flat layout's top-level oracle.
assets yes Array of asset groups.

Merge rules: objects merge key by key, recursively; lists and scalars replace. After the merge a group's strategy section must resolve exchange, symbol, and name, and its overlay section must resolve exchange.

Each asset group:

Key Required Description
asset yes Label for the group (perpetual coin or logical name). Used in errors and logs, not to derive a symbol.
strategy no Primary strategy for this asset.
overlay no Overlay strategy fed by this group's strategy and inventory legs.
oracle no Per-symbol oracle overrides for this group's strategy.symbol. Requires strategy.

A group must declare strategy, overlay, or both; a group with neither is a startup error. An overlay with no primary strategy still needs at least one inventory leg, otherwise it has nothing to hedge.

Strategy section

"strategy": {
  "name": "jacobs_ladder",
  "exchange": "hyperliquid",
  "symbol": "BTC/USDC",
  "qty": "0.0003",
  "grid_interval": [0.15, 0.18]
}

name is the strategy file (strategies/<name>.strategy, then .json) and is required here; the grouped layout reserves strategy for the section itself. exchange and symbol are required. Every key in Trading entry keys is accepted except strategy, overlay, and oracle. The engine validates the file at startup: a missing file, a parse error, or a declared name that differs from name stops startup.

Overlay section

The grouped overlay is a section on the asset group. Its carry parameters sit directly under overlay, and it may name balance-only inventory legs:

"overlay": {
  "name": "carry_overlay",
  "exchange": "hyperliquid",
  "leverage": 20,
  "start_ratio_pct": 25,
  "max_ratio_pct": 75,
  "inventory": [
    { "exchange": "kraken", "symbol": "BTC/USD" }
  ]
}
Key Required Description
name no Overlay strategy file. Default carry_overlay.
exchange yes Venue the overlay trades (the hedge/perp venue). It may differ from the strategy's exchange.
symbol no Overlay symbol. Defaults to the owning strategy symbol when the venue matches, or <COIN>/USDC for a Hyperliquid hedge.
inventory no Balance-only legs that feed the same overlay. Each leg takes { "exchange", "symbol", "testnet" } and runs no primary strategy.
carry keys no start_ratio_pct, add_threshold_pct, max_ratio_pct, recycle_profit_pct, recycle_base_share_pct, min_entry_margin_ratio, min_liq_mult, and leverage; see Carry overlay params.

An asset group runs one overlay, which owns one perpetual position keyed by hedge venue and perp coin. Every strategy and inventory leg in the group feeds it. Legs of the same hedge must declare the same overlay strategy and params; a conflicting strategy or params, or a repeated inventory leg (exchange + symbol), is a startup error.

Legacy flat entries

The flat array remains accepted. Each element configures one symbol on one exchange. strategy is the strategy file name, and an optional nested overlay object binds a second strategy to the same entry:

{
  "symbol": "BTC/USDC",
  "exchange": "hyperliquid",
  "qty": "0.01",
  "grid_interval": [1.0, 5.0],
  "strategy": "jacobs_ladder",
  "maker_fee": null
}

The flat layout has no inventory legs. Use the grouped layout when one hedge must combine several balance sources.

Flat overlay entries

An overlay is a second strategy file run in the same asset domain as the primary strategy. It is not a separate domain: it sees the same balances, state, book, and account. It is cycled by the asset's normal book/event cycle plus perp top-of-book, position, and funding updates; there is no timer-based overlay loop.

In the flat layout the overlay is nested inside the trading entry that owns it, and it inherits that entry's exchange and symbol:

{
  "symbol": "BTC/USDC",
  "exchange": "hyperliquid",
  "qty": "0.0001",
  "strategy": "jacobs_ladder",
  "overlay": {
    "strategy": "<overlay-strategy>",
    "params": { "start_ratio_pct": 25, "max_ratio_pct": 75 }
  }
}
Key Description
strategy Overlay strategy file. Resolved and validated like a primary strategy; the declared name must match.
exchange Optional. Must match the owning entry's exchange when present.
symbol Optional. Must match the owning entry's symbol when present; omit it.
params Optional carry-overlay policy object.

A trading entry has at most one overlay. An overlay with an empty or missing strategy is ignored, so it does not bind. A missing file, a name mismatch, or a mismatched exchange/symbol is a startup failure.

Trading entry keys

These keys apply to a flat entry, and to a grouped strategy section except for strategy, overlay, and oracle: those are group-level sections, and name replaces strategy as the file name.

Key Applies to Description
symbol all Exchange symbol, e.g. BTC/USD (Kraken), BTC (Hyperliquid perpetual), BTC/USDC (Hyperliquid spot), AAPL (IBKR, Alpaca).
exchange all kraken, hyperliquid, lighter, ibkr, or alpaca.
qty all Base order size in base currency, encoded as a string (e.g. "0.01").
grid_interval jacobs_ladder [gi_min, gi_max]: the hardened bounds (in percent) walked by the oracle's parameter search; the strategy reads only the oracle's resolved interval, never these bounds.
min_usd_balance legacy Accepted for backward compatibility; unused by the shipped strategy.
max_exposure legacy Accepted for backward compatibility; unused by the shipped strategy.
strategy flat only Name of the bound strategy file. In the grouped layout this is the strategy section, and the file name is name.
maker_fee, taker_fee all Explicit fee fraction (e.g. 0.0016). Both are required for a custom exchange; recognized venues resolve their own fees at startup.
testnet HL, Lighter, IBKR, Alpaca Route to sandbox/paper endpoints; rejected for Kraken. Accepted on inventory legs too.
hedge Hyperliquid only Legacy flag, parsed but unused. Perpetual overlays are configured through overlay; positions are left open across a graceful shutdown and reconciled on restart.
accumulation_buffer all [min, max] retained quote profit buffer required before base accumulation; resolved live from Fear & Greed on crypto venues.
data_feed Alpaca iex (free) or sip (paid, full-market); accepted on any recognized venue, consumed only by Alpaca.
sell_mult jacobs_ladder Fraction of each ladder rung's quantity sold per rung fill (1.0 sells the full rung; smaller values accrue base).
base_accumulation all Persist accumulated base and profit state for this entry (default true).
sell_levels jacobs_ladder Persist pending sell levels for this entry (default false).
cpu_priority all Integer priority for core assignment; assets with higher values claim a performance core first. Ties keep configuration order. Default 0.

Venue-specific restrictions are enforced at startup:

  • hedge is Hyperliquid-only.
  • testnet is accepted for Hyperliquid, Lighter, IBKR, and Alpaca; it is rejected for Kraken.
  • testnet, hedge, data_feed, and accumulation_buffer are rejected for unrecognized (custom) exchanges.

Carry overlay params

The carry params apply to both layouts: directly under overlay in the grouped layout, and under overlay.params in the flat layout. A custom overlay leaves them out and uses its own tunable: defaults.

key default meaning
start_ratio_pct 25 Initial hedge ratio at the ladder anchor, as a whole-number percentage.
add_threshold_pct 10 ROE loss step that triggers the next add, as a whole-number percentage.
max_ratio_pct 75 Quantity cap, as a whole-number percentage of accumulated base.
recycle_profit_pct 1 ROE profit target for the recycle take-profit, as a whole-number percentage.
recycle_base_share_pct 50 Share of the realized recycle PnL that goes back into base.
min_entry_margin_ratio 2.0 Minimum margin ratio for the first entry. Zero or unknown also passes.
min_liq_mult 2.5 Multiple of the mark used for the liquidation-floor reserve. The reserve is sized to the configured max_ratio_pct short, not the live position, so the ladder's funding stays reserved as it adds.
leverage 0 Venue leverage used to seed the take-profit before the first position snapshot. Set it to your actual leverage if you want the first take-profit to be immediate after a cold start. 0 means unknown.

The add count and add size are derived from start_ratio_pct, max_ratio_pct, and add_threshold_pct; they are not config keys. The engine uses leverage first, then the last leverage persisted in carry_state.json, then the first position snapshot.

The overlay file owns its policy. The engine supplies generic building blocks: live market and perp facts, generic spot/perp order placement and replacement with IOC/GTC/ALO and reduce-only, quote reservation, and base reservation. A quote reservation is subtracted from the primary grid's available quote; a base reservation reduces its sellable base. This is how an overlay can hold senior margin or inventory without a static capital split. A perp short is opened with a perp sell and closed with a reduce-only perp buy; see A short position, start to finish. Your first strategy is a working overlay example.

Set DIO_OVERLAY_DRY_RUN=1 to log intended overlay orders and reservations without sending orders or mutating reservations. That is the shadow mode for bringing up a new overlay.

Oracle section

The oracle is optional; every key falls back to its default. Unknown keys are rejected. Its placement depends on the trading layout:

Layout Global knobs Per-symbol overrides
Grouped trading.defaults.oracle oracle on the asset group (requires strategy)
Flat (legacy) top-level oracle oracle on a trading entry, or the top-level oracle.assets map

A top-level oracle is rejected when trading is grouped. Per-symbol overrides are keyed by the strategy symbol, accept only { target_survival, min_active_dsurv, qty_cap_mult }, and may be declared once: a duplicate symbol is a startup error.

Key Default Description
qty_cap_mult 1.5 Buy-size upper-bound multiplier: the search sizes buy_qty within [qty, qty * qty_cap_mult].
target_survival 0.95 Fraction of the historical maximum drawdown the runway covers: runway_pct = max_drawdown_pct * target_survival. Drives sizing only, never activity.
min_active_dsurv 0.0 Active gate: a strategy is active if and only if its replayed d_surv >= min_active_dsurv (or it has a resting buy to preserve), subject to affordability.
refresh_seconds 300.0 Background fallback poll for history and balances; decisions remain event-driven.
assets unset Flat layout only. Per-symbol overrides keyed by symbol, each accepting { target_survival, min_active_dsurv, qty_cap_mult }.

Decision-flow telemetry

Optional. When enabled is true the engine emits structured decision-flow blocks to the log stream, and the dashboard logs view shows them. There is one block per observed top-of-book update (TICK) and per decision or order event (SIGNAL, GATE, SEND, AMEND, CANCEL, FILL, PULSE). An unchanged decision digest collapses to a heartbeat, so a steady market does not repeat identical lines. Disabled by default. Set DIO_FLOW_LOG=1 to enable it without editing config.json. Unknown keys are rejected.

Key Default Description
enabled false Emit decision-flow blocks.
min_interval_seconds 0.2 Minimum seconds between decision digests for one symbol.
heartbeat_seconds 5.0 Emit one decision digest per symbol at least this often when nothing changed.
pulse_seconds 5.0 Cross-symbol pulse line cadence; 0 or less disables it.
price_ticks true Emit one TICK block per top-of-book update. High volume; set false to keep decision and order blocks only.

Environment variables

Variable Used by Notes
KRAKEN_API_KEY Kraken
KRAKEN_API_SECRET Kraken
HYPERLIQUID_WALLET_ADDRESS Hyperliquid Account address used for balance and fee queries.
HYPERLIQUID_PRIVATE_KEY Hyperliquid Private key for the EIP-712 order signer.
DIO_OVERLAY_DRY_RUN overlay 1/true/yes logs overlay orders and reservations without sending or mutating them. Unset means live.
IBKR_GATEWAY_HOST IBKR Default 127.0.0.1.
IBKR_GATEWAY_PORT IBKR Default 4002; live mode forces 4001 when unset.
IBKR_TRADING_MODE IBKR paper or live; default paper.
IBKR_CLIENT_ID IBKR Default 0.
IBKR_ACCOUNT_ID IBKR Optional; auto-detected when unset.
ALPACA_API_KEY Alpaca
ALPACA_API_SECRET Alpaca
LIGHTER_API_PRIVATE_KEY Lighter
LIGHTER_API_KEY_INDEX Lighter
LIGHTER_ACCOUNT_INDEX Lighter
LIGHTER_SIGNER_LIB_PATH Lighter Path to the signer shared library (.dylib on macOS, .so on Linux).
LIGHTER_PROXY_URL Lighter Comma-separated list of relay proxy URLs.
CMC_API_KEY Fear and Greed CoinMarketCap API key; a missing key falls back to a neutral value.
DISCORD_WEBHOOK_URL Discord notifier Fill notifications; unset disables Discord.
DIO_CONFIG Engine Path to the config file; defaults to config.json in the working directory.
DIO_BACKTRACE Engine When set, pretty-prints OCaml backtraces on crashes.
DIO_FLOW_LOG Engine 1/true/on/yes enables decision-flow telemetry without editing config.json.
DIO_DATA_DIR Persistence State-directory override; defaults to /app/data when /app exists, otherwise data.
DIO_WATCHDOG_OFF Engine When set, disables the main-loop watchdog.
DIO_CANARY Engine 0/false/off/no disables the stop-the-world canary domain (which busy-spins one core while enabled).
DIO_CANARY_THRESHOLD_US Engine Canary spike threshold in microseconds (default 10).
DIO_CANARY_WINDOW_S Engine Canary report window in seconds (default 5).
COLUMNS Engine Fallback log width when stdout is not a TTY (default width 200).
DIO_MOTION Dashboard off/0/false/no disables animations.
DIO_FPS Dashboard Caps the animated frame rate (default 30.0).
DIO_DAMAGE Dashboard off/0/false/no forces full-frame redraws instead of incremental damage rendering.
HOME Dashboard Used for the persisted ~/.dio_theme theme path.

Persistence

State lives in two JSON files under the state directory (data/, $DIO_DATA_DIR, or /app/data in Docker), written atomically:

  • accumulation_state.json: accumulated base and realized profit, keyed by {strategy}:{symbol}:{venue}.
  • sell_levels_state.json: pending sell levels for entries with sell_levels: true.

See SPEC.md ยง 4.8 for the full field list. In Docker, /app/data must be mounted to a volume to survive container restarts.