// Compare quarterly measured WA price effect with auction-implied carbon cost per gallon. // Usage: node passthrough.js const fs = require('fs'); const [resFile, cpiFile] = process.argv.slice(2); const res = JSON.parse(fs.readFileSync(resFile, 'utf8')); // Ecology current-vintage quarterly auction settlement prices ($/allowance = $/t CO2e) const AUCTIONS = { '2023Q1': 48.50, '2023Q2': 56.01, '2023Q3': 63.03, '2023Q4': 51.89, '2024Q1': 25.76, '2024Q2': 29.92, '2024Q3': 29.88, '2024Q4': 40.26, '2025Q1': 50.00, '2025Q2': 58.51, '2025Q3': 64.30, '2025Q4': 70.86, '2026Q1': 65.26, '2026Q2': 64.56, '2026Q3': 39.50, }; // EPA 40 CFR 98 Table MM-1 motor gasoline 8.78 kg CO2/gal; E10 -> 90% fossil (ethanol not covered) const KG_PER_GAL = 8.78 * 0.9; const cpi = new Map(); for (const l of fs.readFileSync(cpiFile, 'utf8').trim().split(/\r?\n/).slice(1)) { const [d, v] = l.split(','); if (v) cpi.set(d.slice(0, 7), +v); } cpi.set('2025-10', (cpi.get('2025-09') + cpi.get('2025-11')) / 2); const base = cpi.get('2024-01'); const midMonth = (q) => `${q.slice(0, 4)}-${String((+q[5] - 1) * 3 + 2).padStart(2, '0')}`; const rows = []; for (const [q, p] of Object.entries(AUCTIONS)) { const implied = p * KG_PER_GAL / 1000 * 100; // cents/gal nominal const real = implied * base / (cpi.get(midMonth(q)) ?? [...cpi.values()].pop()); const m = (name) => res.quarterly[name].effects.find((e) => e.period === q).effect * 100; rows.push({ q, allowance: p, impliedReal: real, us: m('U.S. average'), padd4: m('Rocky Mountain (PADD 4, incl. Idaho)'), states7: m('7 EIA states w/o fuel carbon pricing') }); } const corr = (a, b) => { const n = a.length, ma = a.reduce((s, x) => s + x) / n, mb = b.reduce((s, x) => s + x) / n; let c = 0, va = 0, vb = 0; for (let i = 0; i < n; i++) { c += (a[i] - ma) * (b[i] - mb); va += (a[i] - ma) ** 2; vb += (b[i] - mb) ** 2; } return c / Math.sqrt(va * vb); }; const slope = (x, y) => { const n = x.length, mx = x.reduce((s, v) => s + v) / n, my = y.reduce((s, v) => s + v) / n; let c = 0, v = 0; for (let i = 0; i < n; i++) { c += (x[i] - mx) * (y[i] - my); v += (x[i] - mx) ** 2; } return c / v; }; console.log('Quarter Allowance$ Implied¢(real) vsUS vsPADD4 vs7states'); for (const r of rows) console.log(`${r.q} ${r.allowance.toFixed(2).padStart(7)} ${r.impliedReal.toFixed(1).padStart(10)} ${r.us.toFixed(1).padStart(6)} ${r.padd4.toFixed(1).padStart(7)} ${r.states7.toFixed(1).padStart(9)}`); const x = rows.map((r) => r.impliedReal); for (const k of ['us', 'padd4', 'states7']) { const y = rows.map((r) => r[k]); console.log(`${k.padEnd(8)} corr=${corr(x, y).toFixed(2)} slope(measured on implied)=${slope(x, y).toFixed(2)} mean measured=${(y.reduce((a, b) => a + b) / y.length).toFixed(1)} mean implied=${(x.reduce((a, b) => a + b) / x.length).toFixed(1)}`); } // Excluding 2023Q1 (program start; first compliance costs not yet in prices) const r2 = rows.filter((r) => r.q !== '2023Q1'); for (const k of ['us', 'states7']) console.log(`excl 2023Q1 ${k}: corr=${corr(r2.map((r) => r.impliedReal), r2.map((r) => r[k])).toFixed(2)}`); fs.writeFileSync(resFile.replace('did_results', 'passthrough'), JSON.stringify(rows, null, 2));