Skip to content

Config Verifier (pm-cverifier)

Learning objectives

After reading this page you will understand:

  • Why pm-cverifier exists and what problems it solves
  • How to run it from the command line and interpret its output
  • What each verification layer checks and what the check codes mean
  • How to use it in CI pipelines with --strict and --format json
  • How to read the Risk Summary to confirm your intended risk posture

Why a Config Verifier?

engine_config.yaml covers symbols, gateways, risk controls, collar bands, circuit breakers, market-maker obligations, session schedules, combo seeds, index definitions, and four optional gateway subsystems. Getting all of this right by hand is error-prone.

The current engine loader (load_engine_config()) reports only the first hard error it encounters before aborting. This means operators spend time in a trial-and-error loop: edit the file, start the engine, read a terse ValueError, fix one problem, repeat. There is also no concept of advisory warnings — a valid config that quietly uses defaults the operator did not intend is accepted without comment.

pm-cverifier is a standalone, read-only tool that inspects a config file deeply and produces a human-friendly report covering:

  • every YAML syntax error that would prevent the file from being parsed
  • every hard error that load_engine_config() would raise, plus additional ones
  • completeness gaps — settings that are missing but probably should be present
  • advisory suggestions for settings that are valid but likely to surprise
  • a plain-English Risk Summary that shows what risk controls are actually active
  • an overall OK / WARNING / ERROR verdict

Quick Start

# verify a config file and print a human-readable report
pm-cverifier engine_config.yaml

# only show warnings and above (suppress info-level advisories)
pm-cverifier --level warn engine_config.yaml

# use in CI — fail if any warnings exist, output machine-readable JSON
pm-cverifier --strict --format json engine_config.yaml

Exit codes:

Code Meaning
0 All checks passed; zero errors and zero warnings
1 One or more warnings; no errors
2 One or more hard errors

With --strict, any warning also produces exit code 2.

CLI Reference

pm-cverifier [OPTIONS] CONFIG_FILE

Arguments:
  CONFIG_FILE   Path to engine_config.yaml to verify.

Options:
  --format      Output format: text (default), json
  --level       Minimum severity to show: info, warn, error (default: info)
  --no-color    Disable ANSI color in text output
  --strict      Treat warnings as errors for CI exit-code purposes
  --help        Show help and exit

Verification Layers

The verifier runs checks in four sequential layers. A hard error in Layer 1 (unparseable file) or in Layer 2 (schema errors) stops the later layers, which are reported as skipped (fix schema errors first). Within a layer, every independent problem is reported, so a single pass surfaces as many issues as possible.

flowchart TD
    L1["Layer 1 — YAML Syntax\nCan the file be parsed?"]
    L2["Layer 2 — Schema\nDo all required fields exist\nand have the right types?"]
    L3["Layer 3 — Semantic\nAre values consistent\nand cross-field rules satisfied?"]
    L4["Layer 4 — Completeness\nAre recommended settings\npresent and plausible?"]

    L1 -->|pass| L2
    L2 --> L3
    L3 --> L4
    L1 -->|fail| STOP["Stop — report error"]
Layer Severity What it checks
1 — YAML Syntax ERROR File readable, valid YAML, top-level is a mapping
2 — Schema ERROR Required keys, correct types, in-range values
3 — Semantic ERROR or WARN Cross-field consistency, referential integrity
4 — Completeness WARN or INFO Missing-but-recommended settings, default-value notices

Understanding the Output

Text output (default)

pm-cverifier engine_config.yaml

✓ YAML syntax: OK
✓ Schema: 0 errors
⚠ Semantic: 1 warning
i Completeness: 2 advisories

────────────────────────────────────────────────────────
Warnings
────────────────────────────────────────────────────────

[M013] WARN  No gateway has role: ADMIN.
  → Without an admin gateway, halt, resume, kill-switch, and emergency
    commands cannot be issued at runtime. Add a gateway with role: ADMIN:
    - id: OPS01
      role: ADMIN
      disconnect_behaviour: LEAVE_ALL

────────────────────────────────────────────────────────
Risk Summary
────────────────────────────────────────────────────────

Symbols          2  (AAPL, MSFT)
Gateways         2  (TRADER01: TRADER, MM01: MARKET_MAKER)
Sessions         disabled — always CONTINUOUS
Collars          enabled — DEFAULT: static=20%, dynamic=2%
Circuit breakers enabled — L1=7% (5 min), L2=13% (15 min), L3=20% (rest-of-day)
MM obligations   not enforced
Admin gateway    none ⚠

────────────────────────────────────────────────────────
Verdict:  ⚠ 1 WARNING, 2 ADVISORIES — engine can start but review warnings
────────────────────────────────────────────────────────

Every finding has:

  1. A check code (e.g. M013) — use this to look up the exact condition in the check catalogue below.
  2. A severityERROR, WARN, or INFO.
  3. A message — one sentence naming the problem and the exact field.
  4. A suggestion — a concrete fix, including a YAML snippet where useful.

JSON output (--format json)

{
  "file": "engine_config.yaml",
  "verdict": "WARN",
  "summary": { "errors": 0, "warnings": 1, "info": 2 },
  "checks": [
    {
      "code": "M013",
      "severity": "WARN",
      "message": "No gateway has role: ADMIN.",
      "suggestion": "...",
      "path": "gateways.alf"
    }
  ],
  "risk_summary": { ... }
}

The JSON output is stable and suitable for parsing in CI pipelines or custom reporting scripts.

Check Catalogue

Layer 1 — YAML Syntax (Y001Y004)

Code Condition
Y001 File not found
Y002 File is not readable
Y003 YAML parse error
Y004 Top-level document is not a mapping

Layer 2 — Schema (S001S084)

Top-level structure

Code Condition
S001 symbols absent or not a mapping
S002 gateways absent or not a mapping
S003 gateways.alf absent or not a list
S004 symbols is empty
S005 gateways.alf is empty

Symbol fields

Code Condition
S010 tick_decimals not an integer in 0..8
S011 last_buy_price or last_sell_price not numeric
S012 outstanding_shares not a positive integer
S013 level references an undefined risk level
S014 market_maker_quotes[n] missing required fields
S015 market_maker_quotes[n].bid_price >= ask_price
S016 market_maker_quotes[n] has a non-numeric price, non-positive/non-integer quantity, or invalid tif
S017 market_maker_quotes is present but not a list
S018 market_maker_quotes[n] is not a mapping
S019 market_maker_quotes[n].gateway_id is blank when provided

Gateway fields

Code Condition
S020 Gateway entry missing id
S021 Duplicate gateway ID
S022 role is not a recognised value
S023 disconnect_behaviour is not a recognised value
S024 quote_refresh_policy is not a recognised value
S025 enforce_mm_obligation is not a boolean
S026 mm_max_spread_ticks or mm_min_qty invalid
S027 mm_obligations is present but not a mapping
S028 mm_obligations.<symbol> entry is invalid
S029 gateways.alf[n] is not a mapping
S084 Two gateways.alf IDs are prefixes of each other

Circuit breaker fields

Code Condition
S030 circuit_breaker_defaults or .levels not a mapping
S031 CB level missing price_shift_pct
S032 price_shift_pct out of range (0, 1)
S033 halt_duration_ns not a positive integer
S034 resumption_mode not AUCTION or CONTINUOUS
S035 circuit_breaker present inside a risk level

Risk controls

Code Condition
S040 risk_controls.default_level references an undefined level
S041 collar.static_band_pct not in (0, 1)
S042 collar.dynamic_band_pct not in (0, 1)

Indices (indices)

Code Condition
S043 indices is present but not a list
S044 indices[n] is not a mapping
S045 indices[n].id invalid (empty, non-alnum, or duplicate)
S046 indices[n].description is empty or not a string
S047 indices[n].base_value or publish_interval_sec invalid
S048 indices[n].history_file or state_file invalid
S049 indices[n].constituents invalid shape or duplicates

Combo seeds (market_maker_combos)

Code Condition
S055 market_maker_combos is present but not a list
S056 market_maker_combos[n] invalid mapping/combo_id
S057 market_maker_combos[n].combo_type or .tif invalid
S058 market_maker_combos[n].legs invalid shape/length
S059 market_maker_combos[n].legs[m] invalid, duplicate, or unknown symbol

Runtime / schedule / top-level flags

Code Condition
S060 sessions_enabled present but not a boolean
S061 snapshot_interval_sec present but not a positive number
S062 enforce_collars present but not a boolean
S063 enforce_circuit_breakers present but not a boolean
S064 schedule present but not a mapping

MM obligation defaults (mm_obligation_defaults)

Code Condition
S070 mm_obligation_defaults is present but not a mapping
S071 mm_obligation_defaults.enforce_mm_obligation is not a boolean
S072 mm_obligation_defaults.mm_max_spread_ticks invalid
S073 mm_obligation_defaults.mm_min_qty invalid
S074 mm_obligation_defaults.symbols is present but not a mapping
S075 mm_obligation_defaults.symbols.<symbol> is not a mapping
S076 mm_obligation_defaults.symbols.<symbol>.enforce_mm_obligation invalid
S077 mm_obligation_defaults.symbols.<symbol>.mm_max_spread_ticks/min_qty invalid

API gateway sections

Code Condition
S080 api_gateways section fails runtime-loader validation

RALF and market-data gateway sections

Code Condition
S082 post_trade_gateway section fails runtime-loader validation
S083 market_data_gateway section fails runtime-loader validation

BALF gateway fields (balf_gateway)

Code Condition
S050 balf_gateway is present but not a mapping
S051 balf_gateway.port not an integer in 1..65535
S052 BALF capacity fields not positive integers (max_connections, etc.)
S053 BALF timeout/interval fields not positive numbers
S054 balf_gateway.duplicate_session_policy not REJECT_NEW or EVICT_OLD

Layer 3 — Semantic (M001M022)

M014 is currently emitted during the schema pass because CB threshold ordering is validated while parsing circuit_breaker_defaults.

Code Severity Condition
M001 ERROR MM gateway present but a symbol has no seed quotes
M002 ERROR MM seed references a gateway ID not in gateways.alf
M003 WARN MM seed spread exceeds mm_max_spread_ticks
M004 ERROR sessions_enabled: true but no schedule
M005 WARN sessions_enabled: false but a schedule is present
M006 WARN Schedule times not in chronological order
M007 WARN enforce_collars: false while collars are defined
M008 WARN enforce_circuit_breakers: false while CB levels are defined
M009 ERROR Index constituent not in symbols
M010 ERROR Index constituent missing outstanding_shares
M011 ERROR More than 5 indices defined
M012 WARN Combo uses tif: GTC
M013 WARN No ADMIN gateway configured
M014 WARN CB level thresholds not strictly increasing
M015 ERROR Combo leg references symbol not in symbols
M016 WARN post_trade_gateway configured but no ADMIN gateway
M017 WARN balf_gateway.heartbeat_timeout_sec <= heartbeat_interval_sec
M018 ERROR balf_gateway.port conflicts with another gateway port
M019 ERROR mm_obligation_defaults.symbols references an unknown symbol
M020 ERROR MM seed gateway_id exists but is not a MARKET_MAKER gateway
M022 ERROR API credential gateway_id is not defined in gateways.alf

Layer 4 — Completeness (C001C013)

C010 is emitted during the semantic pass (Layer 3) but kept in the C code family because it is an operational-completeness warning.

Code Severity Condition
C001 WARN No symbol has a reference price
C002 INFO Symbol has only one of last_buy_price / last_sell_price
C003 INFO enforce_collars: true but no collar configured
C004 INFO enforce_circuit_breakers: true but no CB levels
C005 WARN MM gateway present but mm_obligation_defaults absent
C006 WARN enforce_mm_obligation: false
C007 INFO snapshot_interval_sec is at the default 0.5
C008 WARN Index constituent has no reference price
C009 INFO sessions_enabled: false and no schedule (always-on)
C010 WARN disconnect_behaviour: LEAVE_ALL on a non-ADMIN gateway
C011 INFO Risk level defined but no symbol uses it
C012 WARN More than 20 symbols with snapshot_interval_sec < 0.2
C013 WARN Index history_file or state_file parent directory may not exist

Using in CI

# pre-flight check in a deployment script
pm-cverifier --strict --format json engine_config.yaml
echo "Config OK"

# GitHub Actions step example
- name: Verify engine config
  run: pm-cverifier --strict --format json engine_config.yaml

With --strict, exit code 2 is returned for any warning, so CI will fail fast rather than silently deploying a misconfigured exchange.

The Risk Summary

The Risk Summary is always printed at the end of the text report (and included in the risk_summary key of JSON output). It answers: "What does this config actually do at runtime?"

Field Description
Symbols Total count and names
Gateways Total count, ID, and role for each
Sessions Enabled/disabled, schedule summary
Collars Whether enforced and which levels are configured
Circuit breakers Whether enforced and threshold summary
MM obligations Whether enforcement is active
Admin gateway Which gateway (if any) has role ADMIN
Indices Index IDs if any are configured

A ⚠ next to a Risk Summary line marks a potentially risky or incomplete subsystem configuration (for example missing ADMIN gateway or missing collar configuration), even if the overall verdict is OK.

Relationship to Other Tools

Tool Purpose
pm-config-gen Generate an engine_config.yaml from CLI flags
pm-cverifier Verify an existing config file before use
pm-engine Load the config and start the matching engine

pm-cverifier is read-only and has no effect on the engine or any runtime state. It is safe to run at any time, including while the engine is running.