Skip to content

Choosing a configuration

There is no universally optimal configuration. Choose settings from the scientific question, then hold them fixed or treat them as a documented sensitivity analysis.

Start with a preset

from mfdro import SignalConfig

config = SignalConfig.projected(
    n_projections=100,
    n_quantiles=100,
    random_state=20250301,
)

projected_quantile avoids a multivariate free-support solve. It is useful for examples, pipeline development, and broad robustness grids.

from mfdro import SignalConfig

config = SignalConfig.reference()

The reference preset uses a free-support barycenter, sliced distance, 50 center atoms, and 200 directions. It is a declared project reference, not a claim of statistical optimality.

Configurations are immutable. Derive a validated variant without repeating unchanged fields:

weighted = config.with_updates(frequency_weighting="sample_size")

Decision table

Question Main options Practical consequence
How should horizons be made comparable? power, realized_volatility Changes the geometry before any barycenter calculation
How is the center represented? free_support, projected_quantile Free support stores one multivariate center; projected quantiles define a center direction by direction
How is dispersion evaluated? sliced, exact Exact transport is typically much more expensive and requires free_support
How much does each frequency affect dispersion? uniform, sample size, log sample size, explicit Changes lambda_k, not the center weights
How much does each frequency affect the center? uniform or barycenter_weights Changes beta_k, independently of dispersion weights
How much Monte Carlo precision? n_projections More directions usually reduce projection noise and increase runtime
How fine is the projected quantile grid? n_quantiles More grid points increase resolution and work
How large is the free-support center? barycenter_size More atoms increase flexibility, memory, and optimal-transport work

Declare a full experiment

from mfdro import FrequencySpec, SignalConfig

config = SignalConfig(
    frequency_specs=(
        FrequencySpec("daily", 1.0),
        FrequencySpec("weekly", 5.0, rule="W-FRI", min_observations=3),
        FrequencySpec("monthly", 21.0, rule="ME", min_observations=10),
    ),
    scaling="power",
    scaling_exponent=0.5,
    frequency_weighting="explicit",
    explicit_frequency_weights=(1.0, 1.0, 1.0),
    barycenter="free_support",
    barycenter_size=50,
    barycenter_weights=(1.0, 1.0, 1.0),
    barycenter_random_state=0,
    distance="sliced",
    n_projections=500,
    n_quantiles=200,
    random_state=20250301,
    barycenter_max_iter=30,
    barycenter_tolerance=1e-4,
)

All positive weights are normalized internally. Frequency horizons are not inferred from offset aliases: horizon=21.0 is a scientific effective-horizon choice, while rule="ME" is a calendar aggregation choice.

At minimum, inspect sensitivity to:

  • projection count and random seed;
  • lookback length;
  • frequency grid and effective horizons;
  • power versus volatility scaling;
  • barycenter construction;
  • center and dispersion weights;
  • minimum observations in partial aggregation bins.

Use the same effective seeds across comparable projected specifications when common random numbers are part of the design. Do not select the best-looking configuration from the final backtest without accounting for that selection.

Save the exact choice

config.write_json("config.json")
restored = SignalConfig.read_json("config.json")

assert restored == config
assert restored.digest == config.digest

Configuration JSON has an explicit schema version and strict fields. The SHA-256 digest identifies the serialized scientific choices; it does not identify source data or numerical dependency versions.