spb/metrika Public
Stata-class statistics, GPU-accelerated by Apple Silicon. Native Swift — no Electron, no Python runtime, no compromises.
Swift 92.4%
HTML 3.3%
R 3%
Shell 1.3%
1//2// PanelIVTests.swift3// Metrika4//5// Author: Simon-Pierre Boucher6// Contact: contact@spboucher.ai7// Copyright © 2026 Simon-Pierre Boucher. All rights reserved.8//910import Foundation11import Testing12import ZQEngine13import ZQParser1415/// xtreg FE and ivregress 2SLS against R fixtures, end-to-end through the16/// session (which exercises the parser's IV-group grammar too).17@Suite("Panel FE and IV", .serialized)18struct PanelIVTests {19 let fixtures: Fixtures20 let session: ZQSession2122 init() async throws {23 self.fixtures = try Fixtures()24 self.session = try ZQSession(discoverUserCommands: false)25 _ = try await session.execute("use \(fixtures.datasetURL.path)")26 _ = try await session.execute("gen log_rev = ln(revenue)")27 _ = try await session.execute("xtset firm_id")28 }2930 @Test("ivregress parser grammar")31 func parserIVGroup() throws {32 let parser = ZQCommandParser()33 let command = try #require(34 try parser.parse("ivregress 2sls y x1 (p q = z1 z2 z3), robust")35 )36 #expect(command.verb == "ivregress")37 #expect(command.subverb == "2sls")38 #expect(command.varlist == [.simple("y"), .simple("x1")])39 #expect(command.ivSpec == ZQIVSpec(40 endogenous: ["p", "q"], instruments: ["z1", "z2", "z3"]41 ))42 #expect(command.hasOption("robust"))43 }4445 @Test("xtreg fe matches the R within estimator")46 func fixedEffects() async throws {47 let result = try await session.execute("xtreg log_rev price, fe")48 #expect(result.scalars["N"] == fixtures["fe_N"])49 #expect(result.scalars["N_g"] == fixtures["fe_G"])50 expectClose(try #require(result.scalars["b_price"]), fixtures["fe_b_price"], "b[price]")51 expectClose(try #require(result.scalars["b__cons"]), fixtures["fe_b_cons"], "b[_cons]")52 expectClose(53 try #require(result.scalars["se_price"]),54 fixtures["fe_se_price"], "se[price]"55 )56 expectClose(try #require(result.scalars["r2_w"]), fixtures["fe_r2_within"], "within R²")57 expectClose(try #require(result.scalars["rmse"]), fixtures["fe_rmse"], "root MSE")58 #expect(result.text.contains("Fixed-effects (within) regression"))59 }6061 @Test("xtreg fe with panel-clustered SE matches R")62 func fixedEffectsClustered() async throws {63 let result = try await session.execute("xtreg log_rev price, fe cluster(firm_id)")64 expectClose(65 try #require(result.scalars["se_price"]),66 fixtures["fe_se_cluster_price"], "cluster se[price]"67 )68 #expect(result.scalars["df_r"] == fixtures["fe_G"] - 1)69 }7071 @Test("xtreg without xtset or fe errors clearly")72 func xtregValidation() async throws {73 let fresh = try ZQSession(discoverUserCommands: false)74 _ = try await fresh.execute("use \(fixtures.datasetURL.path)")75 await #expect(throws: ZQEngineError.self) {76 _ = try await fresh.execute("xtreg revenue price, fe") // no xtset77 }78 await #expect(throws: ZQEngineError.self) {79 _ = try await session.execute("xtreg log_rev price") // no fe80 }81 }8283 @Test("ivregress 2sls matches the R manual 2SLS")84 func twoStageLeastSquares() async throws {85 let result = try await session.execute(86 "ivregress 2sls log_rev (price = z1 z2)"87 )88 #expect(result.scalars["N"] == fixtures["iv_N"])89 expectClose(try #require(result.scalars["b_price"]), fixtures["iv_b_price"], "b[price]")90 expectClose(try #require(result.scalars["b__cons"]), fixtures["iv_b_cons"], "b[_cons]")91 expectClose(92 try #require(result.scalars["se_price"]), fixtures["iv_se_price"], "se[price]"93 )94 expectClose(95 try #require(result.scalars["se__cons"]), fixtures["iv_se_cons"], "se[_cons]"96 )97 expectClose(try #require(result.scalars["r2"]), fixtures["iv_r2"], "R²")98 expectClose(try #require(result.scalars["rmse"]), fixtures["iv_rmse"], "root MSE")99 #expect(result.text.contains("Instrumented: price"))100 }101102 @Test("ivregress robust SE matches R HC1-small")103 func ivRobust() async throws {104 let result = try await session.execute(105 "ivregress 2sls log_rev (price = z1 z2), robust"106 )107 expectClose(108 try #require(result.scalars["se_price"]),109 fixtures["iv_se_hc1_price"], "robust se[price]"110 )111 }112113 @Test("ivregress order condition is enforced")114 func orderCondition() async throws {115 await #expect(throws: (any Error).self) {116 _ = try await session.execute(117 "ivregress 2sls log_rev (price orders = z1)"118 )119 }120 }121}122