Skip to content

Strategy Validation Properties (Exhaustive)

This is the canonical running list of properties that validation enforces for strategies.

Framework contract reference: docs/framework.md

Validation Execution Flow

flowchart LR
    A["Input strategy + date range"] --> B["Contract checks"]
    B --> C["Backtest metrics"]
    C --> D["Leakage + strict diagnostics"]
    D --> E["Threshold checks"]
    E --> F["Validation pass/fail summary"]

Validation Property Checklist

  1. [ ] Allocation span config is valid
  2. STACKSATS_ALLOCATION_SPAN_DAYS must be an integer in [90, 1460] (default 365).

  3. [ ] Strategy cannot bypass framework orchestration

  4. Custom compute_weights overrides are rejected.

  5. [ ] Strategy must implement an allowed hook path

  6. At least one is required: propose_weight(state) or build_target_profile(ctx, features_df, signals).

  7. [ ] Strategy feature sets must be registry-backed

  8. required_feature_sets() must resolve to registered framework-owned feature providers.
  9. Strategy classes should avoid sourcing external files, databases, or network data directly.

  10. [ ] AST lint blockers are not present

  11. Best-effort static lint hard-fails on patterns such as negative .shift(...), centered .rolling(..., center=True), direct file I/O, direct DB access, and direct network access inside strategy methods.

  12. [ ] transform_features output type is valid

  13. Must return a Polars DataFrame.

  14. [ ] Observed-only feature context is enforced

  15. ctx.features_df only contains rows from start_date through current_date.
  16. Strategy hooks never receive rows after current_date.

  17. [ ] build_signals output type is valid

  18. Must return dict[str, pl.Series].

  19. [ ] Signal and profile series shape/date contract holds

  20. Each series must be a Polars Series, with exact positional alignment to the observed window date column.

  21. [ ] Signal and profile series numeric validity holds

  22. Series values must be finite numeric (no NaN, inf, or non-numeric coercion failures).

  23. [ ] propose_weight outputs are finite

  24. Every proposal must be finite numeric.

  25. [ ] Target profile mode is valid

  26. Only preference and absolute modes are allowed.

  27. [ ] Allocation index monotonicity holds for temporal counting

  28. Allocation index used for n_past must be monotonic increasing.

  29. [ ] Locked-prefix structural validity holds

  30. locked_weights must be 1D, finite, values in [0, 1], and length <= n_past.

  31. [ ] Locked-prefix budget feasibility holds

  32. Running sum of locked weights must never exceed total budget 1.0.

  33. [ ] Locked-prefix daily bounds hold on contract-length windows

  34. When window length equals configured span, locked values must be within [1e-5, 0.1].

  35. [ ] Per-day clipping obeys future feasibility constraints

  36. For contract-length windows, daily clipping enforces both day bounds and future-budget feasibility.
  37. Infeasible bounds must hard-fail.

  38. [ ] Output weight vector structure is valid

  39. Final weights must be 1D and finite.

  40. [ ] Output weight values are valid

  41. No negative weights; no values above 1.0 (within tolerance).

  42. [ ] Output weights sum exactly to budget (within tolerance)

  43. Final weight sum must be 1.0.

  44. [ ] Contract-length day bounds hold on final weights

  45. For span-length windows, each day weight must be within [1e-5, 0.1].

  46. [ ] Daily bounds are globally feasible for span length

  47. Span must satisfy feasibility: n_days * min_daily_weight <= 1.0 <= n_days * max_daily_weight.

  48. [ ] Historical lock immutability is enforced in strict checks

  49. Injected locked prefix must be preserved exactly when recomputing under perturbed future features.

  50. [ ] Forward-leakage resistance: truncated-source invariance

  51. Prefix outputs at probe date must match when source data is truncated at the probe date and rematerialized as-of that date.

  52. [ ] Forward-leakage resistance: perturbed-future invariance

  53. Prefix outputs at probe date must match when source data strictly after the probe date is strongly perturbed before rematerialization.

  54. [ ] Profile-only leakage resistance is enforced

  55. For profile-hook strategies (without propose hook), prefix profile values must be invariant under masked/perturbed future inputs.

  56. [ ] Strict mode forbids in-place feature mutation

  57. Strategy must not mutate ctx.features_df during weight computation.

  58. [ ] Strict mode forbids profile-build feature mutation

  59. Strategy must not mutate ctx.features_df during transform/signal/profile build path.

  60. [ ] Strict mode determinism holds

  61. Repeated runs with identical inputs must produce exactly matching weights (within atol=1e-12).

  62. [ ] Weight constraints hold across validation windows

  63. Validation windows must not violate sum, negativity, or (when applicable) min/max day bounds.

  64. [ ] Boundary saturation stays below strict threshold

  65. In strict mode, boundary-hit rate (days at MIN or MAX) must be <= max_boundary_hit_rate (default 0.85).

  66. [ ] Purged fold robustness minimum is met in strict mode (when fold checks run)

  67. Minimum fold win rate must be >= min_fold_win_rate (default 20.0).
  68. Folds use a purged walk-forward split with an embargo equal to the allocation span.
  69. Fold checks are skipped when there is insufficient date range for at least two valid folds.

  70. [ ] Purged fold instability is bounded in strict mode (when fold checks run)

  71. Fold win-rate standard deviation must be <= max_fold_win_rate_std (default 35.0).
  72. Fold checks are skipped when there is insufficient date range for at least two valid folds.

  73. [ ] Block-shuffled null robustness threshold is met in strict mode (when shuffled checks run)

  74. Mean win rate on block-shuffled price trials must be <= max_shuffled_win_rate (default 80.0) across shuffled_trials (default 3).
  75. Shuffled checks are skipped when price_usd is missing, the shuffled window is empty, or shuffled_trials <= 0.

  76. [ ] Bootstrap confidence interval threshold is met in strict mode

  77. Lower bootstrap CI for anchored excess must be >= min_bootstrap_ci_lower_excess.

  78. [ ] Block-permutation null threshold is met in strict mode

  79. Block-permutation p-value for anchored excess versus uniform baseline must be <= max_permutation_pvalue.

  80. [ ] Feature drift is bounded in strict mode

  81. Feature PSI must be <= max_feature_psi.
  82. Feature KS statistic must be <= max_feature_ks.

  83. [ ] Global win-rate threshold is met

  84. Validation backtest win rate must be >= min_win_rate (default 50.0).

  85. [ ] Daily paper/live execution is gated on strict validation

  86. run_daily() performs strict validation on the same strategy fingerprint and data snapshot before order submission.

  87. [ ] Validation receipts and run fingerprints are persisted

  88. Daily runs persist validation receipt IDs, source-data hashes, and observed feature snapshot hashes for reconciliation.

  89. [ ] Validation date range must contain data

  90. Empty requested validation range is an automatic validation failure.

  91. [ ] Backtest path must generate windows

  92. Validation relies on backtest windows; if none are generated, validation cannot pass.