---
title: "Credit Markets: simple MTGP and TROP-lite"
format:
  html:
    theme: cosmo
    toc: true
    embed-resources: true
    self-contained-math: true
    html-math-method: katex
    code-copy: true
    include-in-header:
      text: |
        <style>
        body{font-family:system-ui,sans-serif;line-height:1.6}
        main,.cell,.cell-output-display{min-width:0;max-width:100%}
        .cell-output-display,table{overflow-x:auto}
        table{display:block;font-size:.85rem}
        img{max-width:100%;height:auto!important}
        .katex-display{overflow-x:auto;overflow-y:hidden;max-width:100%}
        p code,li code{white-space:normal;overflow-wrap:anywhere}
        </style>
jupyter: python3
execute:
  warning: false
  error: false
  cache: false
  fig-dpi: 140
---

This separate experiment uses two minimal implementations from `mtgpax`. It does not replace or modify the [established estimator comparison](https://lalten.org/pages/did-replication/credit-markets-counterfactuals.html).

```{python}
#| echo: false
import sys
from pathlib import Path

ROOT = next(
    p
    for p in [Path.cwd(), *Path.cwd().parents]
    if (p / "analyses/credit-markets/mtgp-trop/report.py").exists()
)
sys.path.insert(0, str(ROOT / "analyses/credit-markets/mtgp-trop"))
from report import estimates, events, tuning, export
```

## Sample and point estimates

The balanced Credit Markets sample has **723 stock–country units × 53 years (1960–2012)**: 529 treated units in 15 adoption cohorts and 194 never-treated units. The 21 initially treated units are excluded, exactly as in the established comparison. The outcome is divided by the original source-sample SD, **1.3291711872**, without centering. Both scalar estimates average the same **17,811 observed treated unit-years**. Neither model changes the sample or uses additional covariates.

```{python}
estimates()
```

These are point estimates, without bootstrap intervals or posterior uncertainty. The common sample, scale and scalar weighting match the established all-treated-cell comparisons; CS-DID's separately reported equal-event-time aggregate uses different weights.

## Rank-one MTGP MAP

`mtgpax.gp.map.MTGPCausalEstimator` fits a Gaussian outcome model with a unit intercept and **one latent time factor governed by a Gaussian process**, with unit-specific factor loadings. It is fitted jointly to untreated observations from all units: never-treated histories and each treated unit's pre-adoption history. Every treated outcome is masked from the likelihood.

Settings are rank 1, 1,500 Adam steps, learning rate 0.03, seed 0, `scalar_effect=False`, and `additive_fixed_effects=False`. Other numerical settings retain the package defaults. This is a fixed-budget MAP fit, not full Bayesian posterior sampling. The JSON records its loss trace; finite losses are verified, without claiming that the optimizer reached a global optimum. Post-treatment effects are observed outcomes minus predicted untreated outcomes.

## Rank-one TROP-lite

`mtgpax.trop.lite.TROPLiteEstimator` is the repository's **simplified TROP-style residual-augmentation estimator**, not the paper's full nuclear-norm estimator or its full tuning procedure.

The current API takes one treated series. For each adoption cohort, this adapter supplies the cohort's mean outcome as that series, plus the same **194 never-treated donor units**. It fits 15 models independently. Other adoption cohorts are not donors and there is no cross-cohort pooling parameter.

A rank-one iterative-SVD imputer estimates the untreated low-rank surface. For each post-treatment cell, TROP-lite adds the treated pre-residual average and the donor contemporaneous residual average, then subtracts the donor pre-residual average. Unit and time residual averages use exponential-distance weights.

The rank is fixed at 1, with 35 SVD iterations. The time-localization grid is `[0, 0.05, 0.15, 0.4, 1]`; the unit-localization grid is `[0, 0.5, 1, 2, 4]`. Each cohort selects the pair minimizing held-out pre-treatment prediction error over the last `min(10, floor(pre_periods/2))` pre-periods. The low-rank surface is refitted with each validation outcome masked. These are the repository's default localization grids and validation rule; only the rank is restricted to the simplest nonzero choice.

Cohort paths are aggregated using cohort sizes at each event time, and scalar ATTs use cohort size × number of observed post periods. Unlike the nuclear-norm scalar adapter, TROP-lite produces genuinely time-varying post-treatment effects.

## Post-treatment event studies

No pre-treatment effect curve or period −1 recentering is constructed. These plots show only out-of-treatment-sample predictions and observed post-treatment outcomes. No uncertainty bands were computed. Longer event times are supported by fewer cohorts; support is shown below.

```{python}
events()
```

## TROP-lite cohort tuning

```{python}
tuning()
```

## Verification and reproduction

On a separate deterministic panel, adding 3 to all treated post outcomes leaves both models' counterfactual predictions unchanged and shifts their effects by exactly 3. TROP-lite's output also matches an independent reconstruction of its four-term residual-augmentation identity. For the Credit fits, post-effect counts and weighted event-path averages reproduce the scalar ATTs. The JSON includes those checks, package versions, source hashes and the `mtgpax` commit.

```{python}
#| echo: false
export()
```

[Results JSON](https://lalten.org/pages/did-replication/credit-markets-mtgp-trop-results.json) · [Quarto source](https://lalten.org/pages/did-replication/credit-markets-mtgp-trop.qmd) · [Executable helpers and settings](https://lalten.org/pages/did-replication/credit-markets-mtgp-trop-code.zip)

Run `prepare.py` from the replication environment to rebuild the eligible panel locally. Run `mtgpax_worker.py` with the separate `mtgpax` Python environment and the supplied parameter JSONs; render this QMD with the replication environment. The archive microdata are not included. The README in the bundle gives the exact commands. The existing comparison files and environment are unchanged.

### Source

- [mtgpax](https://github.com/apoorvalal/mtgpax), `mtgpax/gp/map.py` and `mtgpax/trop/lite.py`.
- Noack and Costello (2024), [Credit Markets, Property Rights, and the Commons](https://doi.org/10.1086/729065).
