Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ jobs:
- name: Run ty check
run: uv run ty check

docs:
name: Docs
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]

- name: Install uv
uses: astral-sh/[email protected]
with:
version: "latest"

- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}

- name: Install dependencies
run: uv sync --dev --extra docs

- name: Build docs
run: uv run mkdocs build --strict

test:
name: Test (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
Expand Down Expand Up @@ -158,7 +178,7 @@ jobs:
build:
name: Build Package
runs-on: ubuntu-latest
needs: [lint, typecheck, test, test-package-smoke, mps-smoke]
needs: [lint, typecheck, docs, test, test-package-smoke, mps-smoke]
steps:
- uses: actions/[email protected]
with:
Expand Down
6 changes: 5 additions & 1 deletion docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ The package root re-exports the main model classes, configs, batches, results, a
- PortfolioAllocationPipeline
- PortfolioPipelineFitResult

## Stability

The [API Stability](../reference/api-stability.md) page defines the public
surface intended to remain stable through the `0.1` beta series.

## Integration

::: ml4t.models.integration
Expand All @@ -87,4 +92,3 @@ The package root re-exports the main model classes, configs, batches, results, a
| `ml4t.models.stochastic_discount_factor` | weight-native SDF estimation and return projections |
| `ml4t.models.asset_prediction` | direct asset-level predictors |
| `ml4t.models.portfolio` | end-to-end portfolio learners |

48 changes: 48 additions & 0 deletions docs/book-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,54 @@ The case studies are intended to act as:

They should not define the public API by accident.

## Compatibility Status

The `0.1.0b0` beta line is validated against the Chapter 14 teaching flow and the shared
case-study latent-factor bridge.

| Book surface | Validation status |
|---|---|
| `14_latent_factors/04_ipca.ipynb` | full notebook execution passed |
| `14_latent_factors/05_rp_pca.ipynb` | full notebook execution passed |
| `14_latent_factors/06_conditional_autoencoder.ipynb` | full notebook execution passed |
| `14_latent_factors/07_stochastic_discount_factor.ipynb` | full notebook execution passed |
| `14_latent_factors/08_supervised_autoencoder.ipynb` | Papermill smoke execution passed; full production training is long-running |
| `14_latent_factors/09_case_study_insights.ipynb` | full notebook execution passed |
| `case_studies.utils.latent_factors.library_bridge` | synthetic PCA, IPCA, CAE, SAE, and SDF bridge smoke checks passed |

The teaching notebooks keep hand-built implementations where that improves exposition. The
case-study path uses `ml4t-models` through the shared latent-factor bridge so the same
contracts are exercised in walk-forward validation and registry-backed analysis.

## Case-Study Validation

The beta gate also checks that each case-study family can execute its model-specific
latent-factor notebooks through the shared bridge.

| Case study | Validation status |
|---|---|
| ETF returns | PCA, IPCA, SDF, and SAE cached executions passed; CAE passed in cached three-fold validation mode |
| US firm characteristics | IPCA, CAE, SDF, and SAE passed in cached three-fold validation mode |
| S&P 500 option analytics | PCA, IPCA, CAE, SDF, and SAE passed in cached three-fold validation mode |

The ETF CAE notebook also reached the cached full-fold execution path and loaded the
registry-backed model outputs before the notebook kernel exited while processing the large
cached result set. The three-fold validation run exercises the same library bridge,
checkpoint handling, prediction schema, and registry persistence path with a bounded
runtime footprint.

## Evaluation Boundary

Case-study IC reporting is delegated to `ml4t-diagnostic`:

- fold-level scoring calls `ml4t.diagnostic.metrics.cross_sectional_ic`
- pooled model-analysis summaries call `cross_sectional_ic` and `cross_sectional_ic_series`
- model outputs are converted into `PredictionsFrame`, `SignalsFrame`, `WeightsFrame`, and
`ml4t-backtest` handoff payloads by library adapters

`ml4t-models` remains responsible for fitting and output contracts. Statistical diagnostics
and execution simulation remain owned by `ml4t-diagnostic` and `ml4t-backtest`.

## Recommended Reading Order

If you are moving from the book notebooks to the library:
Expand Down
3 changes: 3 additions & 0 deletions docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ This quickstart shows the three main workflows in the library:
2. stochastic discount factor extraction
3. end-to-end portfolio learning

The same workflows are available as executable smoke examples in the repository's
`examples/` directory.

## 1. Latent-Factor Forecasting

The latent-factor path is intentionally three-stage:
Expand Down
72 changes: 72 additions & 0 deletions docs/reference/api-stability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# API Stability

This page defines the public surface that is intended to be stable for the
`0.1` beta series.

## Stable Import Surface

Use these modules in notebooks, examples, and downstream libraries:

| Surface | Purpose |
|---|---|
| `ml4t.models` | convenience imports for reader-facing workflows |
| `ml4t.models.api` | structural protocols for model families |
| `ml4t.models.types` | batch, state, prediction, and frame contracts |
| `ml4t.models.configs` | frozen dataclass configs |
| `ml4t.models.integration` | adapters for long frames, diagnostics, and backtests |

The package root re-exports the core model classes, configs, result contracts,
and integration helpers used throughout the documentation.

## Stable Families

The beta API is organized around four model families:

| Family | Stable models |
|---|---|
| Latent factors | `PCAModel`, `RPPCAModel`, `IPCAModel`, `CAEModel` |
| Stochastic discount factor | `StochasticDiscountFactorModel` |
| Direct asset prediction | `SAEModel` |
| Portfolio learning | `LinearFeaturePortfolioModel`, `LSTMPortfolioModel`, `DeepPortfolioModel` |

Each family keeps its own contract because the native object differs by model:
latent-factor state, SDF weights, direct signals, or portfolio weights.

## Naming Conventions

Reader-facing result names describe the object they carry:

| Name | Object |
|---|---|
| `AssetForecastResult` | expected returns by timestamp and asset |
| `AssetSignalResult` | direct asset-level scores or signals |
| `AssetWeightsResult` | cross-sectional SDF or asset weights |
| `PortfolioWeightsResult` | sequence-model portfolio allocations |
| `PredictionsFrame` | long-format expected-return predictions |
| `SignalsFrame` | long-format direct signals |
| `WeightsFrame` | long-format portfolio or asset weights |
| `ResultsFrame` | common frame protocol implemented by integration outputs |

Avoid names that describe implementation mechanics rather than the financial
object. If a new public result type is added, it should fit this pattern.

## Extension Points

The following extension points are intentionally public:

- `LatentFactorModel`
- `FactorForecaster`
- `AssetMapper`
- `AssetPredictionModel`
- `StochasticDiscountFactorEstimator`
- `PortfolioModel`
- `PortfolioPostprocessor`

New models should implement the relevant protocol and return the existing result
contracts before introducing a new public type.

## Deferred Surface

The beta release does not freeze internals under `ml4t.models._internal`.
Functions and modules prefixed with `_` are implementation details and may change
between `0.1` releases.
42 changes: 42 additions & 0 deletions docs/reference/release-gates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Release Gates

This page defines the checks required before tagging a beta or stable release.

## Local Gate

Run the full gate from the repository root:

```bash
uv run ruff check src/ tests/ examples
uv run ruff format --check src/ tests/ examples
uv run ty check
uv run pytest tests/ -q
uv run mkdocs build --strict
uv build
```

For optional integration paths, also run:

```bash
uv run --extra integration pytest \
tests/test_integration_backtest.py \
tests/test_integration_data.py \
tests/test_integration_surfaces.py \
-q
```

## GitHub Gate

The main CI workflow must pass before a beta tag is created:

- lint
- type check
- docs
- Python 3.12 tests
- Python 3.13 tests
- Python 3.14 tests
- package smoke test
- MPS smoke test
- package build

The release workflow publishes only from `v*` tags after the beta branch has merged.
11 changes: 8 additions & 3 deletions docs/user-guide/direct-asset-prediction.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ decomposition.

## Why It Lives Outside Latent Factors

The library originally explored treating `SAE` as another latent-factor model. That turns
out to be the wrong abstraction.

In the current design:

- `SAEModel` is a direct predictor
Expand All @@ -49,6 +46,10 @@ not:
cross-section batch -> factor state -> factor forecaster -> beta × lambda
```

| Input contract | Native output | Assumption |
|---|---|---|
| `CrossSectionBatch` | `AssetSignalResult` | supervised targets are the object of interest |

## Checkpoints

`SAEModel` supports:
Expand Down Expand Up @@ -82,6 +83,10 @@ with:
- asset IDs
- selected checkpoint metadata

The signal is not automatically calibrated as an expected return. Use the integration
helpers to convert it to long-format signal frames, then evaluate or transform it in the
diagnostic and backtest layers.

## When To Use It

Use `SAEModel` when you want:
Expand Down
16 changes: 16 additions & 0 deletions docs/user-guide/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ These help when your data starts as:
- a polars frame
- a long-format table with ML4T-style schema metadata

If `ml4t-specs` is installed, the adapters accept `FeedSpec` objects directly. Without that
optional dependency, they still accept explicit column names and plain metadata mappings.
The library does not require users to source data through `ml4t-data`.

## Frame Adapters

The frame helpers normalize model outputs into standard long-format tables.
Expand Down Expand Up @@ -74,6 +78,18 @@ These construct:
- optional context frames
- `FeedSpec`-compatible metadata for `ml4t-backtest`

The handoff payload is intentionally shallow: it prepares frames and metadata, then lets
`ml4t-backtest` own execution simulation.

## Diagnostics Handoff

`ml4t-models` emits prediction, signal, and weight frames with standard timestamp and asset
columns. Use those frames as inputs to `ml4t-diagnostic` for cross-sectional IC, portfolio
diagnostics, tearsheets, and validation reports.

This library does not compute IC summaries or diagnostics itself. Keeping diagnostics in
`ml4t-diagnostic` prevents model implementations from carrying a second evaluation stack.

## Artifact Writing

Use:
Expand Down
23 changes: 17 additions & 6 deletions docs/user-guide/latent-factor-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ The family splits into two groups:

## Overview

| Model | Contract | Loading structure | Predictive object |
|---|---|---|---|
| `PCAModel` | `PersistentPanelBatch` | static loadings | `beta × premium` after a forecaster |
| `RPPCAModel` | `PersistentPanelBatch` | static loadings with risk-premium-aware extraction | `beta × premium` after a forecaster |
| `IPCAModel` | `CrossSectionBatch` | linear characteristic-implied betas | `beta × premium` after a forecaster |
| `CAEModel` | `CrossSectionBatch` | nonlinear characteristic-implied betas | `beta × premium` after a forecaster |
| Model | Input contract | Native output | Main assumption | Predictive object |
|---|---|---|---|---|
| `PCAModel` | `PersistentPanelBatch` | `LatentFactorState` with static loadings and factor returns | stable asset IDs | `beta × premium` after a forecaster |
| `RPPCAModel` | `PersistentPanelBatch` | `LatentFactorState` with pricing-aware static loadings | stable asset IDs and meaningful cross-sectional means | `beta × premium` after a forecaster |
| `IPCAModel` | `CrossSectionBatch` | `LatentFactorState` with characteristic-implied betas | dated characteristics parameterize conditional exposures | `beta × premium` after a forecaster |
| `CAEModel` | `CrossSectionBatch` | `LatentFactorState` with nonlinear characteristic betas | nonlinear exposure map can be learned from cross-sections | `beta × premium` after a forecaster |

## PCA

Expand All @@ -46,6 +46,9 @@ It does not directly forecast expected returns. That requires:
1. a factor-premium forecast
2. an asset mapper

Do not use PCA when the asset axis is an unstable identifier field. PCA and RP-PCA rely on
the same entity occupying the same panel slot through time.

## RP-PCA

`RPPCAModel` extends PCA by letting expected-return information influence factor extraction.
Expand Down Expand Up @@ -74,6 +77,9 @@ Key config fields:
- `normalize_loadings`
- `orthogonalize_factors`

The `gamma` parameter in `RPPCAConfig` is the RP-PCA pricing-error weight, not an exposure
matrix. The fitted loadings are returned in `LatentFactorState.asset_betas`.

## IPCA

`IPCAModel` is the linear bridge between PCA and neural conditional factor models.
Expand All @@ -88,6 +94,7 @@ In the library, `IPCAModel`:
- consumes `CrossSectionBatch`
- alternates between factor estimation and gamma estimation
- returns a `LatentFactorState` with conditional betas and factor history
- reports ALS convergence metadata in `LatentFactorState.metadata`

The economic interpretation follows
[Kelly, Pruitt, and Su (2019)](../reference/academic-references.md#ref-kelly-pruitt-su-2019):
Expand Down Expand Up @@ -128,6 +135,10 @@ Current implementation features:
- optional ensemble averaging across saved checkpoints
- classification mode that still keeps factor construction tied to continuous returns

Checkpoint selection changes the extracted structural state. If you pass
`checkpoint=10`, the betas and factor history come from the checkpoint saved at epoch 10,
not from the final training epoch.

This is the right way to think about the model:

- `CAEModel` is a nonlinear extension of IPCA
Expand Down
14 changes: 9 additions & 5 deletions docs/user-guide/portfolio-learning.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ This family covers two related ideas:

## Family Overview

| Model | Style | Native output |
|---|---|---|
| `LinearFeaturePortfolioModel` | deterministic baseline | `PortfolioWeightsResult` |
| `LSTMPortfolioModel` | sequence baseline | `PortfolioWeightsResult` |
| `DeepPortfolioModel` | structured DeePM-style allocator | `PortfolioWeightsResult` |
| Model | Input contract | Style | Native output | Main assumption |
|---|---|---|---|---|
| `LinearFeaturePortfolioModel` | `PortfolioSequenceBatch` | deterministic baseline | `PortfolioWeightsResult` | pooled feature scores can rank allocation desirability |
| `LSTMPortfolioModel` | `PortfolioSequenceBatch` | sequence baseline | `PortfolioWeightsResult` | recent feature paths contain allocation-relevant state |
| `DeepPortfolioModel` | `PortfolioSequenceBatch` | structured DeePM-style allocator | `PortfolioWeightsResult` | temporal, cross-sectional, and graph structure can improve direct allocations |

## Shared Contract

Expand Down Expand Up @@ -135,3 +135,7 @@ Current helper:

Use it when you want to transform raw learned weights into a stricter target-weights frame
before handing them to `ml4t-backtest`.

Postprocessing is deliberately separate from model fitting. This lets the same trained
allocator be evaluated under different exposure caps, leverage normalization rules, or
turnover controls.
Loading