LTA COE bidding results via data.gov.sg · 396 auctions × 5 categories, 2010–2026 · 3 of 6

Since May 2022, Cat A/B premium changes have a stronger descriptive association with changes in bids per quota than with quota changes. Cat C/D show the opposite ordering. Yet Cat A’s September 2026 record occurred while quota was recovering. Bid counts cannot establish causes or reveal bidders’ reserve values.

ρ +0.37 / +0.55
premium moves vs bids-per-quota, Cat A/B since May 2022 — far above vs quota (ρ −0.14 / −0.09); Cat C/D reverse (quota is their larger correlate)
median −0.1%
quota move at the top-5 increases per category (25 jumps) — 13 down / 11 up; bids-per-quota rose in 17 of 25
133,009
Cat A record (2026-09) on a recovering quota — January–September in both years: 2024 → 2026 Cat A average quota 962 → 1,252 (+30%) while the premium kept climbing; latest 131,890
1,980/1,980 · 10/10
rows retained, structural checks passed, 0 staging exclusions — two source conflicts remain; 396 exercises × 5 categories, 2010-01 → 2026-09
Quota premium and quota per exercise, Category A and B, 2010–2026, with the May-2022 redefinition marked
Quota premium (left) vs quota (right), Category A and B — the quota cycles; Cat A's premium climbs to a record, Cat B stays below its 2023 peak. Lines mark February 2014 and May 2022 definition changes. Earlier definitions are historical context, not the earlier attribution sample. No exercises Apr–Jun 2020 (bidding pause).

More views

Bids per quota and success rate over time, Category A and B
Count pressure: bids per quota and the success rate — the lower panel is almost the reciprocal of the upper (corr 0.98 vs 1/bids-per-quota; the quota is nearly filled), so it is not a second measure. The spikes are exercises where bid counts surged relative to quota.
The attribution table. Premium moves vs quota vs bids-per-quota — five A/B variants plus all-category source-conflict exclusions; A/B direction and ordering stay stable. Earlier A/B n=190 each (February 2014 R2–April 2022), later estimates start May 2022 R2. The rank-residual partial (ρ −0.20/−0.22) is descriptive, not a causal supply estimate.

outputs/coe_attribution.csv — full write-up in the decision memo.

What the file can and cannot say

Premium is the common final quota premium paid by every successful bidder in that category, not the lowest winning reserve price or the PQP. Bids-per-quota measures bids submitted per certificate, not unique participants; quota is announced before bidding. LTA rules and worked example.

Two selected jumps followed quota cuts above 40%: C, 2023-02 R1, +S$8,010 after −43.6%; D, 2020-08 R1, +S$1,191 after −42.1%. Co-occurrence is not causation.

Cat A included taxis before August 2012; A/B gained a power criterion in February 2014. Earlier A/B estimates therefore begin February 2014 R2, not 2010. Base sample: 1,975 changes − 5 pause − 2 May-boundary − 196 earlier-definition = 1,772.

Two CSV cells disagree with LTA’s historical table: 2010-02 R1 B quota 1,154 vs 693; 2010-01 R2 D premium S$20,090 vs S$852. Source values are unchanged; sensitivity excludes changes touching disputed cells. The apparent undersubscription is not treated as established auction behaviour.

Method

  1. Pull — LTA COE bidding results from data.gov.sg; structure-validated before replacing files, SHA-256 + coverage in a manifest (1,980 rows = 396 exercises × 5 categories, 2010-01 → 2026-09). src/download.py
  2. Audit — round structure, category completeness, the 2020 pause and dirty cells resolved from the live file before analysis. docs/data_audit.md
  3. Stage & check — comma-formatted thousands ("1,438") parsed with a counted rule set; month + round → an ordered exercise; 10 assertions run before the parquet is written. sql/01 · sql/05
  4. Measure & attribute — exercise-over-exercise deltas per category; premium moves vs quota vs bids-per-quota (premium % / quota % vs bids-per-quota level change), 5 A/B variants, source-conflict sensitivity and a partial; the top-5 positive S$ changes per category. Spearman uses average ranks for ties. src/analysis.py → coe_attribution.csv · coe_jumps.csv
  5. Draw & write — three figures as code, light and dark (src/figures.py); then the decision memo and the sensitivity checks.

Reproduce

Linux/macOS (Bash):

git clone https://github.com/faizsaifulnizam/coe-quota-premium && cd coe-quota-premium
uv venv .venv --python 3.12          # or: python -m venv .venv
source .venv/bin/activate
uv pip install -r requirements.txt   # or: pip install -r requirements.txt

python src/download.py       # raw CSV → data/raw/ (gitignored; add --force to re-pull)
python src/build_dataset.py  # staging + 10 checks → data/processed/coe_exercises.parquet
python src/analysis.py       # pressure table + jumps + attribution + sensitivity → outputs/
python src/figures.py        # re-renders reports/figures/ (light + dark)
python tests/smoke_test.py
python -m unittest discover -s tests -p "test_*.py"

Windows (PowerShell, no activation needed):

git clone https://github.com/faizsaifulnizam/coe-quota-premium
cd coe-quota-premium
$env:PYTHONUTF8 = "1"
uv venv .venv --python 3.12
uv pip install --python .venv\Scripts\python.exe -r requirements.txt

.venv\Scripts\python.exe src/download.py
.venv\Scripts\python.exe src/build_dataset.py
.venv\Scripts\python.exe src/analysis.py
.venv\Scripts\python.exe src/figures.py
.venv\Scripts\python.exe tests/smoke_test.py
.venv\Scripts\python.exe -m unittest discover -s tests -p "test_*.py"

The latest month may contain R1 only; older incomplete months fail. January–September receipts stay fixed to those months. CSVs, figures/site copies and raw/manifest publish as separate staged batches with rollback for ordinary exceptions, not a pipeline-wide transaction or a power-loss guarantee. A refresh requires rerunning all stages and reviewing prose against outputs.

Then check outputs/coe_pressure.csv: the 2026-09 R1 Category A row reads premium 133,009 with quota 1,195 and bids per quota 1.43; R2 reads 131,890. And the first Category B row of outputs/coe_jumps.csv reads +26,990 (2024-01 R2; bids per quota 1.34 → 1.99). Data as of the 2026-10-04 pull — a later re-pull can move the newest exercise.

Read more