6. Execution config

July 26, 2026 · View on GitHub

An ExecutionConfig records the cost / slippage / commission / fill assumptions that produced a study's runs. It lives on Study.execution_config (one per study; MAY be nil when the producer did not capture it — e.g. legacy bundles or ad-hoc smoke tests).

ExecutionConfig is small, dict-shaped, and always inline in the msgpack body. It is NOT eligible for Parquet blob extraction; there are no @blob references anywhere under it.

Why on the study, not the bundle?

Two studies inside the same algorithm_id bundle MAY validly use different execution assumptions — e.g. an in-sample sweep run zero-cost next to an out-of-sample validation study run under realistic fees. Anchoring execution_config to Study (rather than to Bundle) keeps each study self-describing and lets one bundle hold optimistic / pessimistic overlays of the same algorithm side by side.

Shape

ExecutionConfig (msgpack map)
├── blotter_type: str | nil              # producer-defined identifier of the
│                                        #   blotter / matching engine used by
│                                        #   this study's runs. Identity-only.
│                                        #   Behavioural details live under the
│                                        #   *_model fields below.
├── slippage_model: dict | nil           # {"type": "<name>", "params": {...}}
├── commission_model: dict | nil         # same shape as slippage_model
├── fill_model: dict | nil               # same shape as slippage_model
├── trading_costs: array[TradingCost]    # per-symbol cost overrides (may be empty)
└── metadata: map                        # free-form (runtime flags, blotter
                                         #   module path, etc.)

All fields are OPTIONAL. Every combination of present / absent fields is a valid ExecutionConfig. Absence of a *_model field means the producer did not capture that model's identity — it does NOT imply "no slippage / no commission / full fill".

Absence of the whole ExecutionConfig (i.e. Study.execution_config == nil) means the producer did not capture any cost information for this study.

Pluggable model pattern

The three pluggable model fields (slippage_model, commission_model, fill_model) all use the same shape:

{
  "type":   "<string identifier>",   # required if the map is present
  "params": <map>                    # optional; producer-defined shape per type
}

The type field is a string that names a model class. Its params field is a map whose shape depends on type. Producers MUST emit type when the outer map is present. Readers MUST tolerate a missing params (treat as empty map).

TradingCost

Each entry in trading_costs is a per-symbol cost snapshot:

TradingCost (msgpack map)
├── symbol: str | nil               # target symbol (upper-cased, e.g. "BTC/EUR"),
│                                   #   or nil for a market-level default that
│                                   #   applies to any symbol not otherwise named.
├── fee_percentage: float           # percentage-of-value fee (0.0025 = 0.25%)
├── slippage_percentage: float      # flat slippage as % of price
├── fee_fixed: float                # fixed fee per trade in trading currency
└── slippage_model: dict | nil      # optional nested {"type": "...", "params": {...}}
                                    #   overrides slippage_percentage for this symbol

At most one entry with symbol == nil SHOULD appear in a trading_costs array; readers encountering multiple MAY pick the first and ignore the rest.

Reserved built-in type names

The following type values are RESERVED by this specification. Each reserved type has a fixed set of params keys — these key names are part of the format and MUST be used verbatim by conforming producers. Consumers of a reserved type MAY rely on the params schema below.

Producers that need parameters not covered by a reserved schema SHOULD invent a new type name (see 10. Extensions § x- prefix) rather than extending a reserved name with extra keys.

slippage_model.type

"NoSlippage"

No slippage — fills at the exact modelled price.

params keyTypeDefaultNotes
(none)params MAY be {} or absent.

"PercentageSlippage"

Flat percentage-of-price slippage. Buys fill higher, sells lower.

params keyTypeDefaultNotes
percentagefloat0.001Decimal fraction (0.001 = 0.1%).

"FixedSlippage"

Flat absolute slippage in price units. Buys fill at price + amount, sells at price − amount.

params keyTypeDefaultNotes
amountfloat0.01In trading_symbol price units.

"FixedBasisPointsSlippage"

Slippage expressed in basis points of price. slippage = price × basis_points / 10000.

params keyTypeDefaultNotes
basis_pointsfloat51 bp = 0.01%.

"VolumeImpactSlippage"

Power-law market-impact slippage. impact = base_percentage × (amount / volume) ^ impact_power. Falls back to base_percentage when volume is unavailable.

params keyTypeDefaultNotes
base_percentagefloat0.001Base slippage as a decimal.
impact_powerfloat0.5Exponent for participation rate. 0.5 = square-root impact.

"VolumeShareSlippage"

Quadratic-in-participation slippage with a volume cap. impact = price_impact × (amount / volume)². In addition, at most volume_limit × volume of the bar's volume can be filled per bar.

params keyTypeDefaultNotes
volume_limitfloat0.025Maximum fraction of bar volume fillable in one bar (2.5%).
price_impactfloat0.1Coefficient for the quadratic price impact term.

commission_model.type

"NoCommission"

Zero fee on every trade.

params keyTypeDefaultNotes
(none)params MAY be {} or absent.

"PercentageCommission"

Commission as a percentage of notional. fee = price × amount × percentage.

params keyTypeDefaultNotes
percentagefloat0.001Decimal fraction (0.001 = 0.1%).

"FixedCommission"

Fixed fee per trade regardless of trade size.

params keyTypeDefaultNotes
amountfloat1.0Flat fee per trade, in trading_symbol units.

fill_model.type

"FullFill"

Orders fill in full at the first opportunity.

params keyTypeDefaultNotes
(none)params MAY be {} or absent.

"VolumeBasedFill"

Fill quantity is capped by a fraction of bar volume. The remainder stays open and is re-evaluated on subsequent bars.

params keyTypeDefaultNotes
max_volume_fractionfloat0.1Maximum fraction of bar volume fillable per bar (10%).

Vendor-defined type names

Producers MAY emit type values not on the reserved list above. Vendor-defined types SHOULD use the x-<vendor>- prefix from 10. Extensions, e.g. "x-quantos-AdaptiveSlippage". Their params schema is producer-defined and consumers MUST treat them as opaque (see reader tolerance below).

Reader tolerance

Conforming readers MUST:

  • Accept execution_config: nil and treat it as "no info captured".
  • Accept an ExecutionConfig that is present but has every field nil or empty. Treat as "no info captured".
  • Accept unknown type values in any of slippage_model, commission_model, fill_model, or a nested TradingCost.slippage_model. Unknown types are advisory metadata: readers SHOULD surface them to callers as opaque {type, params} pairs, and MUST NOT invalidate any runs, metrics, or summaries when they cannot reconstruct the model.
  • Accept unknown keys inside params for known type values. Preserve them on round-trip.
  • Accept unknown top-level keys inside ExecutionConfig. Preserve them on round-trip.

Conforming readers MUST NOT:

  • Reject a bundle solely because its ExecutionConfig references an unknown model type.
  • Attempt to infer semantics for an unknown type from its name.

Reference-implementation note (non-normative)

The reference implementation uses a process-local class-name registry (populated automatically via __init_subclass__ on SlippageModel, CommissionModel, FillModel) to reconstruct concrete model instances from {type, params} pairs. The registry is not persisted; bundles carry only the type name and its parameters, keeping the on-disk format free of Python-class references.

This mechanism is a property of the reference reader/writer, not of the format itself. Conforming implementations in other languages MAY use any mechanism they like to map type names to code, or MAY skip reconstruction entirely and treat all model definitions as opaque {type, params} records.