Skip to content

Regime-dependent stochastic transaction cost process #4170

@stranske

Description

@stranske

Why

Add a cost process that samples per-rebalance transaction costs conditional on regime (and optionally turnover/volatility).

Add a cost process that samples per-rebalance transaction costs conditional on regime (and optionally turnover/volatility).

Parent Epic: #4156
Depends on: #4162 (regime labeling)

Configuration

costs:
  kind: regime_stochastic
  calm:
    trade_cost_bps:
      dist: lognormal
      mean: 6
      sigma: 0.25
    slippage_multiplier: 1.0
  stress:
    trade_cost_bps:
      dist: lognormal
      mean: 18
      sigma: 0.35
    slippage_multiplier: 1.5

Scope

Not provided.

TODO: define scope and boundaries.

Non-Goals

Not provided.

TODO: list explicit exclusions.

Tasks

  • Implement regime-aware CostProcess with configurable distributions (lognormal, etc.) in src/trend_analysis/monte_carlo/costs.py.
    • Implement the CostProcess class structure. (verify: confirm completion in repo)
    • Define (verify: confirm completion in repo)
    • implement configurable distributions (e.g. (verify: config validated)
    • lognormal). (verify: config validated)
    • Add regime-awareness logic to the CostProcess. (verify: confirm completion in repo)
  • Integrate cost sampling into runner.py with regime inputs.
    • Modify runner.py to accept regime inputs. (verify: confirm completion in repo)
    • Integrate cost sampling logic into the runner. (verify: confirm completion in repo)
    • Test the integration with sample inputs. (verify: confirm completion in repo)
  • Record per-period realized costs and total cost drag in summary outputs.
  • Add unit tests for distributions, regimes, and integration behavior in tests/monte_carlo/test_costs.py.
    • Add unit tests for configurable distributions (e.g. (verify: tests pass)
    • lognormal). (verify: confirm completion in repo)
    • Add unit tests for regime-specific behavior. (verify: tests pass)
    • Add integration tests for the CostProcess in the runner. (verify: tests pass)
  • ## Cost Process Output (Per Rebalance)
  • Expected cost in basis points
  • Optional slippage multiplier by regime
  • Applied to portfolio turnover
  • Implement regime-aware CostProcess with configurable distributions (lognormal, etc.).
  • Integrate cost sampling into runner/evaluation with regime inputs.
  • Record per-period realized costs and total cost drag in summary outputs.
  • Add tests for distributions, regimes, and integration behavior.

Acceptance Criteria

  • Costs vary across time with regime.
  • Lognormal sampling produces reasonable cost distributions.
  • Stress regime has higher mean and variance.
  • Output bundle includes realized costs per period.
  • Total cost drag reported in summary metrics.
  • Integrates with existing cost model (no new reporting universe).
  • Unit tests for each distribution type and regime pass.
  • Costs vary across time with regime
  • Lognormal sampling produces reasonable cost distributions
  • Stress regime has higher mean and variance
  • Output bundle includes realized costs per period
  • Total cost drag reported in summary metrics
  • Integrates with existing cost model (no new reporting universe)
  • Unit tests for each distribution type and regime
  • ## Files to Create/Modify
  • src/trend_analysis/monte_carlo/costs.py
  • Integrate with runner.py
  • tests/monte_carlo/test_costs.py

Implementation Notes

File paths to modify: src/trend_analysis/monte_carlo/costs.py, runner.py, tests/monte_carlo/test_costs.py.
Configuration example:

costs:
  kind: regime_stochastic
  calm:
    trade_cost_bps:
      dist: lognormal
      mean: 6
      sigma: 0.25
    slippage_multiplier: 1.0
  stress:
    trade_cost_bps:
      dist: lognormal
      mean: 18
      sigma: 0.35
    slippage_multiplier: 1.5

Example methods to implement:

def sample_cost(self, regime: str, turnover: float, rng: np.random.Generator) -> float:
    """Sample transaction cost in bps for a rebalance."""
    base_cost = self._sample_base_cost(regime, rng)
    slippage = self._get_slippage_multiplier(regime)
    return base_cost * slippage

def apply_costs(self, weights_before: pd.Series, weights_after: pd.Series, regime: str, rng: np.random.Generator) -> tuple[float, dict]:
    """Apply costs to a rebalance, return cost drag and diagnostics."""
    turnover = (weights_after - weights_before).abs().sum() / 2
    cost_bps = self.sample_cost(regime, turnover, rng)
    cost_drag = turnover * cost_bps / 10000
    return cost_drag, {"turnover": turnover, "cost_bps": cost_bps, "regime": regime}

<details>
<summary>Original Issue</summary>

````text

TODO: add technical notes, file paths, or constraints.

Deferred Tasks (Requires Human)

  • Integrate cost sampling into runner.py with regime inputs. (The agent cannot modify repository settings or retry CI pipelines, which may be required for integration testing. | Ensure a human reviewer handles repository settings and CI pipeline adjustments.)
  • Add unit tests for distributions, regimes, and integration behavior in tests/monte_carlo/test_costs.py. (The agent cannot guarantee specific test coverage percentages, which may be required for this task. | Define specific test coverage goals and have a human verify the results.)

Metadata

Metadata

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions