Documentation

Command reference, language syntax, and practical recipes for econometric work.

Language basics

Hayashi uses a Stata-like command grammar with modern expression support. Statements are separated by newlines or semicolons. Results are printed unless suppressed with a trailing semicolon.

basics.hay
// Load data
load "wage1.csv" as df

// Variables and assignment
let x = 10
let y = x + 5

// Conditional
if y > 10 {
  print("large")
}

// Loop
for i in 1..5 {
  print(i)
}

Data I/O

Hayashi reads and writes several tabular formats. ODBC is available as an optional feature.

CommandDescription
load "file.csv" as dfLoad CSV/TSV
load "file.json" as dfLoad JSON array
load "file.dta" as dfLoad Stata .dta
load "file.xlsx" as dfLoad Excel sheet
load "file.parquet" as dfLoad Parquet
load "sqlite://path.db" as dfSQL query to DataFrame
save df, "csv", "out.csv"Save DataFrame

Block expressions & output control

A block can be used as an expression: it runs statements and returns the last expression. Variables declared inside are local to the block.

blocks.hay
let df = {
  let raw = load("data.csv")
  generate raw y = log(x)
  keep(raw, ["date", "y"])
  raw
}

quietly on
let m = merge(df2, df3, key=id, type=inner)
quietly off

print("done")

quietly on suppresses automatic output from statements and estimators. print(...) and display ... still appear. The flag is scope-aware: toggling inside a block reverts when the block ends.

Data & I/O utilities

Common helpers for missing values, file-system checks, and simple caching.

CommandDescription
dropna(df, col1, col2)Remove rows with NaN
ffill(df)Forward-fill NaN in float columns
file_exists("path")Check if path exists
ensure_dir("cache")Create directory if missing
write("text", "file.txt")Write string to file
export(df, "csv", "out.csv")Save DataFrame

Estimators

All estimators share a common formula syntax and return a model object with post-estimation methods.

CommandFamily
ols(y ~ x1 + x2, df)Ordinary least squares
iv(y ~ x1 + x2, ~ z1 + z2, df)Instrumental variables / 2SLS
logit(y ~ x1 + x2, df)Binary logistic regression
probit(y ~ x1 + x2, df)Binary probit regression
poisson(y ~ x1 + x2, df)Poisson regression
nbreg(y ~ x1 + x2, df)Negative binomial
tobit(y ~ x1 + x2, df)Censored regression
qreg(y ~ x1 + x2, df, tau=0.5)Quantile regression
fe(y ~ x1 + x2 | id, df)Panel fixed effects
re(y ~ x1 + x2 | id, df)Panel random effects
ab(y ~ x1 + x2 | id + year, df)Arellano-Bond dynamic panel
heckman(y ~ x1 + x2, s ~ z1 + z2, df)Heckman two-step
garch(y ~ 1, df, p=1, q=1)GARCH volatility
var(df, lags=2)Vector autoregression
vecm(df, lags=2, rank=1)Vector error correction
did(y ~ treat + post, df)Difference-in-differences
gmm(y ~ x1, df, instruments=z1)Generalized method of moments
rolling(y ~ x1 + x2, df, window=30)Rolling OLS with optional date=date_col

Post-estimation

After fitting a model, use these verbs for inference, prediction, and diagnostics.

CommandDescription
test(model, "x1 = 0")Wald test
predict(model, df)Generate predictions
margins(model, dydx(x1))Marginal effects
bootstrap(model, reps=1000)Bootstrap inference
estat(model, "vif")Diagnostic statistics
nlcom(model, "x1/x2")Nonlinear combination
esttab(model)Regression table output
tidy(model)Convert model to a tidy DataFrame of coefficients
glance(model)Convert model to a one-row summary DataFrame

Modern syntax

Hayashi adds modern conveniences to the Stata-like grammar without breaking the REPL workflow.

modern.hay
// Pipes
let df2 = df |> filter(educ > 12) |> mutate(lwage = log(wage))

// F-strings
print("R² = {model.r2}")

// Match expression
let sign = match coef > 0 { true => "+", false => "-" }

// Functions
fn my_mean(x) {
  return sum(x) / len(x)
}

// Import a plugin
import("sheep-farm/hayplot", as=gg)
let p = gg::hayplot(df, {"x": "educ", "y": "lwage"})
    |> gg::geom_point("blue", 5.0)
    |> gg::save_svg("scatter.svg")

Recipe: rolling CAPM

A complete daily CAPM pipeline with rolling betas and an SVG chart.

capm.hay
import("sheep-farm/hayahoo", as=yahoo)
import("sheep-farm/hayfred", as=fred)
import("sheep-farm/hayplot", as=gg)

fred::set_apikey("sua-chave")

let asset = yahoo::history("AAPL", {"range": "50y", "interval": "1d"})
generate asset asset_ret = log(close / L.close)
asset |> keep(["date", "asset_ret"])

let market = yahoo::history("SPY", {"range": "50y", "interval": "1d"})
generate market market_ret = log(close / L.close)
market |> keep(["date", "market_ret"])

let merged = merge(asset, market, key=date, type=inner)
let rf = fred::fred("TB3MS", {"start": merged["date"][0], "end": merged["date"][len(merged["date"])-1]})
generate rf rf_period = (value / 100.0) / 365
rf |> keep(["date", "rf_period"])

let with_rf = merge(merged, rf, key=date, type=left)
let filled = ffill(with_rf)
generate filled excess_asset = asset_ret - rf_period
generate filled excess_market = market_ret - rf_period
let df = dropna(filled, excess_asset, excess_market)

let model = df |> ols(excess_asset ~ excess_market, _)
print(model)

let roll = rolling(excess_asset ~ excess_market, df, window=252, date=date)
let betas = tidy(roll)
let coefs = betas["excess_market"]

print("mean =", mean(coefs))
print("sd   =", sd(coefs))
print("min  =", min(coefs))
print("max  =", max(coefs))

generate betas idx = _n
let plot = gg::hayplot(betas, {"x": "idx", "y": "excess_market"})
    |> gg::geom_line("#1f77b4", 1.5)
    |> gg::geom_hline("red", 1.0, mean(coefs))
    |> gg::labs("Rolling beta: AAPL vs SPY", "observation", "beta")
    |> gg::set_dimensions(1200, 600)
    |> gg::save_svg("rolling_beta.svg")

CLI commands

CommandDescription
hay script.hayRun a script
hayStart REPL
hay install user/repoInstall plugin
hay update [user/repo]Update plugin(s)
hay remove user/repoRemove plugin
hay listList installed plugins
hay validateRun empirical validation suite
hay dist-updateUpdate Hayashi binary to latest release