Most of empirical finance is reduced-form: regress returns on characteristics, estimate risk premia from factor portfolios, test whether a coefficient is significantly different from zero. Structural estimation takes a fundamentally different approach. It begins with an explicit economic model (i.e., specifying agents’ preferences, information sets, constraints, and optimization problems) and estimates the model’s primitive parameters directly from observed data. The payoff is the ability to perform counterfactual analysis: what would happen to prices if trading costs fell by half? How would IPO underpricing change if the allocation mechanism switched from book building to a uniform-price auction? What is the welfare cost of a particular market design choice? These questions are unanswerable within a reduced-form framework because they require knowledge of the data-generating process, not merely statistical associations within a single regime.
This chapter introduces the key structural estimation frameworks used in financial economics and implements them for Vietnamese equity markets. We cover four domains where structural models have proven most productive: investor demand estimation, trading cost and execution models, primary market auctions, and limit order book dynamics. Each domain involves distinct economic primitives such as preferences, beliefs, transaction costs, information asymmetries and each requires domain-specific identification strategies.
Vietnamese markets offer both challenges and opportunities for structural estimation. On the challenge side, data quality is uneven, market microstructure is evolving, and institutional features (price limits, foreign ownership caps, state ownership) introduce constraints that standard models do not accommodate. On the opportunity side, several features of Vietnamese markets create natural variation that aids identification: the coexistence of two exchanges (HOSE and HNX) with different trading rules, regulatory changes that shift trading costs and price limits, and the phased liberalization of foreign ownership caps that generates exogenous demand shocks.
Show code
import pandas as pdimport numpy as npfrom scipy import stats, optimizefrom scipy.optimize import minimize, minimize_scalarimport statsmodels.api as smimport statsmodels.formula.api as smffrom linearmodels.panel import PanelOLSfrom linearmodels.iv import IV2SLSimport plotnine as p9from mizani.formatters import percent_format, comma_formatimport warningswarnings.filterwarnings("ignore")
45.1 What Structural Estimation Means in Finance
45.1.1 Reduced Form Versus Structural
Consider two researchers studying the effect of transaction costs on trading volume. The reduced-form researcher exploits a fee reduction event and estimates:
The coefficient \(\beta\) identifies the causal effect of the fee change on volume, but it is local to this specific fee change, this specific market, and this specific time period. It says nothing about what a different fee change would do, or what the optimal fee structure might be.
The structural researcher writes down an explicit model of trader behavior:
where \(v_s\) is the (possibly private) valuation, \(q_s\) is the trade quantity, \(c(\cdot; \boldsymbol{\theta})\) is the cost function parameterized by \(\boldsymbol{\theta}\), and \(\delta\) is the discount factor. The researcher estimates \(\boldsymbol{\theta}\) by requiring that the model’s predictions match observed trading patterns. With \(\hat{\boldsymbol{\theta}}\) in hand, any counterfactual cost function \(c'(\cdot; \boldsymbol{\theta}')\) can be fed into the model to predict the equilibrium response.
The distinction is not about sophistication (reduced-form work can be highly rigorous) but about the type of question being answered:
Table 45.1: Reduced Form vs. Structural Estimation
Reduced Form
Structural
Answers
“What happened?”
“What would happen if…?”
Identification
Exogenous variation (events, instruments)
Model restrictions + data moments
Key assumption
Unconfoundedness or exclusion restriction
Correct model specification
Output
Treatment effects, associations
Primitive parameters, counterfactuals
Risk
Omitted variable bias, weak instruments
Model misspecification
45.1.2 Identification Through Economic Primitives
Structural identification relies on the economic model to convert observed outcomes into unobserved primitives. The classic example is the demand-supply system. Observing prices and quantities alone cannot identify demand and supply curves (the simultaneity problem). But if we know the functional form of demand and supply, and have instruments that shift one curve but not the other, the system is identified.
In financial contexts, identification often comes from the model’s equilibrium conditions. In Kyle (1985), the informed trader’s strategy, the market maker’s pricing rule, and the noise trader’s demand jointly determine the equilibrium price impact coefficient \(\lambda\). The model maps the observable (i.e., the price impact of order flow) to the unobservable (i.e., the precision of private information). The identification is through the model’s structure, not through a conventional instrument.
Formally, let \(\boldsymbol{\theta} \in \Theta\) denote the vector of structural parameters and \(\mathbf{m}(\boldsymbol{\theta})\) the model-implied moments. The data provide empirical moments \(\hat{\mathbf{m}}\). Identification requires that the mapping \(\boldsymbol{\theta} \mapsto \mathbf{m}(\boldsymbol{\theta})\) is injective: different parameter values produce different observable implications.
This is the Generalized Method of Moments (GMM) framework applied to structural estimation. The choice of weighting matrix \(\mathbf{W}\) determines efficiency, and over-identifying restrictions (more moments than parameters) provide specification tests.
45.1.3 Tradeoffs Between Realism and Tractability
Every structural model involves choices about which features of reality to include and which to abstract from. Table 45.2 illustrates this for several canonical finance models.
The researcher’s task is to choose the simplest model that captures the economic mechanism of interest while remaining rich enough that its counterfactual predictions are credible. As Rust (1987) emphasized, there is a tension between models that are “structurally correct” but computationally intractable and models that are tractable but potentially misspecified.
45.2 Demand Estimation for Financial Assets
45.2.1 The Demand System Approach
Koijen and Yogo (2019) (hereafter KY) develop a demand system for financial assets that estimates the elasticity of institutional investor demand with respect to asset characteristics. The framework adapts the discrete-choice demand estimation methodology of Berry, Levinsohn, and Pakes (1995) from industrial organization to financial markets.
Each investor \(i\) holds a portfolio of \(N\) assets. The demand for asset \(j\) by investor \(i\) at time \(t\) is:
where \(w_{ij,t}\) is the portfolio weight of asset \(j\) for investor \(i\), \(\delta_{j,t}\) is the mean utility (common valuation), \(\mathbf{x}_{j,t}\) is a vector of observable characteristics (market cap, book-to-market, momentum, etc.), and \(\boldsymbol{\beta}_i\) captures investor-specific preferences (heterogeneous demand elasticities). The denominator includes 1 to represent the outside option (holding cash or non-equity assets).
The key insight is that the market-clearing condition links demand to equilibrium prices:
where \(A_{i,t}\) is investor \(i\)’s total assets under management and \(\text{ME}_{j,t}\) is the market capitalization of asset \(j\). This system of equations determines the equilibrium price impacts and demand elasticities.
45.2.2 Demand Elasticity Estimation
The demand elasticity of asset \(j\) with respect to its own price (or a characteristic that moves its price) is:
In a frictionless market with perfectly elastic demand, a supply shock (e.g., an index addition) would have zero price impact. Empirically, demand curves for financial assets slope downward with finite elasticity, implying that supply shocks move equilibrium prices.
The demand system framework directly yields price impact predictions. If investor \(i\) receives an exogenous inflow \(\Delta A_i\) (e.g., from a fund flow shock), the price of each stock must adjust to clear the market with the new demand. The equilibrium price impact of a demand shock is:
where \(\varepsilon_{jj}\) is the demand elasticity and \(\Delta D_j / D_j\) is the percentage demand shock. Less elastic demand implies larger price impact for the same demand shock (a prediction with direct implications for the price impact of index rebalancing, fund flows, and forced selling).
iflen(car_results) >0:# Average AR by event day and type avg_ar = ( car_results.groupby(["event_type", "event_day"]) .agg( mean_ar=("ar", "mean"), se_ar=("ar", lambda x: x.std() / np.sqrt(len(x))), n=("ar", "count") ) .reset_index() )# Cumulative ARfor etype in avg_ar["event_type"].unique(): mask = avg_ar["event_type"] == etype avg_ar.loc[mask, "car"] = avg_ar.loc[mask, "mean_ar"].cumsum() avg_ar = avg_ar[avg_ar["event_day"].between(-5, 20)] ( p9.ggplot(avg_ar, p9.aes( x="event_day", y="car", color="event_type" ))+ p9.geom_line(size=1)+ p9.geom_vline(xintercept=0, linetype="dashed", color="gray")+ p9.geom_hline(yintercept=0, linetype="dotted", color="gray")+ p9.scale_color_manual(values=["#27AE60", "#C0392B"])+ p9.labs( x="Event Day", y="Cumulative Abnormal Return", title="Price Pressure from VN30 Index Additions and Deletions", color="Event" )+ p9.theme_minimal()+ p9.theme(figure_size=(10, 6)) )
Figure 45.2
The permanent component of the CAR measures the information content of index inclusion, while the temporary component (reversal after the event) measures pure price pressure from demand. In a world with perfectly elastic demand, there would be no temporary price impact. The magnitude of the temporary component is inversely related to the demand elasticity, providing a second identification strategy (complementary to the holdings-based approach) for demand curves.
45.2.4 Demand Inelasticity and Its Implications
Gabaix and Koijen (2021) argue that the aggregate demand for equities is remarkably inelastic, with elasticity estimates on the order of 0.2 (meaning a 1% supply increase requires a 5% price decline to clear). The implications are profound: if demand is this inelastic, then flows (from index funds, foreign investors, retail traders) have outsized effects on prices. In Vietnamese markets, where foreign ownership caps create binding constraints on a key investor class, demand inelasticity may be even more severe.
The inelasticity also generates a role for “the market portfolio” as an equilibrium concept: if most investors hold portfolios close to the market (as in CAPM), then deviations from market weights require someone to absorb the excess supply, and the compensation required is the inverse of demand elasticity.
45.3 Structural Models of Trading Strategies
45.3.1 Optimal Execution: The Almgren-Chriss Framework
The optimal execution problem, formalized by Almgren and Chriss (2001), asks: given a large order to execute over a fixed horizon, what is the optimal trading schedule that minimizes the total execution cost? The problem is fundamental to institutional investing because large orders cannot be executed instantaneously without severe price impact.
Let \(X_t\) denote the remaining shares to sell at time \(t\), with \(X_0 = X\) (total order) and \(X_T = 0\) (completion). The trading rate is \(n_t = X_{t-1} - X_t\). The execution price for shares traded at \(t\) is affected by both temporary and permanent price impact:
where \(g(\cdot)\) is the permanent impact function, \(h(\cdot)\) is the temporary impact function, \(\sigma\) is the volatility, \(W_t\) is a Wiener process, and \(\tau\) is the time interval. The total implementation shortfall (cost of execution relative to the arrival price \(S_0\)) is:
where \(\lambda\) is the risk aversion parameter. With linear impact functions \(g(n) = \gamma n\) and \(h(v) = \eta v + \epsilon \text{sgn}(v)\), the optimal strategy has a closed-form solution.
TWAP (Time-Weighted Average Price): Trade uniformly: \(n_t = X / T\) for all \(t\). Optimal when permanent impact dominates temporary impact.
VWAP (Volume-Weighted Average Price): Trade proportionally to expected volume: \(n_t \propto \hat{V}_t\). Approximates TWAP adjusted for intraday volume patterns.
Almgren-Chriss Optimal: The risk-averse optimal trajectory for linear impact is:
where \(\kappa = \sqrt{\lambda \sigma^2 / \eta}\) governs the trade-off between urgency (trading quickly to reduce risk) and patience (trading slowly to reduce impact). High risk aversion (\(\lambda\) large) implies front-loaded execution.
Show code
def almgren_chriss_trajectory(X, T, sigma, eta, gamma, lam, n_steps=100):""" Compute Almgren-Chriss optimal execution trajectory. Parameters ---------- X : float Total shares to execute. T : float Execution horizon (in trading days). sigma : float Daily return volatility. eta : float Temporary impact parameter. gamma : float Permanent impact parameter. lam : float Risk aversion parameter. n_steps : int Number of time steps. Returns ------- DataFrame : Time, remaining shares, trading rate, expected cost. """ tau = T / n_steps kappa_sq = lam * sigma**2/ (eta / tau)if kappa_sq <=0:# Risk-neutral: trade uniformly kappa =0 x_t = np.linspace(X, 0, n_steps +1)else: kappa = np.sqrt(kappa_sq) t_grid = np.linspace(0, T, n_steps +1) x_t = X * np.sinh(kappa * (T - t_grid)) / np.sinh(kappa * T)# Trading rates n_t =-np.diff(x_t)# Expected cost components perm_cost = gamma * np.sum(n_t**2) temp_cost = (eta / tau) * np.sum(n_t**2) total_expected_cost = perm_cost + temp_cost# Variance of cost var_cost = sigma**2* tau * np.sum(x_t[:-1]**2) results = pd.DataFrame({"time": np.linspace(0, T, n_steps +1)[:-1],"remaining_shares": x_t[:-1],"trade_rate": n_t }) results.attrs["expected_cost"] = total_expected_cost results.attrs["cost_variance"] = var_cost results.attrs["kappa"] = kappa if kappa_sq >0else0return results
45.3.2 Estimating Market Impact from Transaction Data
The structural parameters \((\gamma, \eta, \sigma)\) must be estimated from data. The standard approach uses the square-root law of market impact, documented empirically by Kyle (1985) and Hasbrouck (1991) and derived theoretically by Gabaix et al. (2003):
\[
\Delta P / P = c \cdot \text{sgn}(Q) \cdot |Q / V|^{\delta}
\tag{45.12}\]
where \(Q\) is the signed order flow, \(V\) is daily volume, \(c\) is the impact coefficient, and \(\delta \approx 0.5\) is the impact exponent. The “square root” refers to \(\delta = 0.5\), which is remarkably robust across markets, time periods, and asset classes.
Table 45.4: Market Impact Exponent by Firm Size Quintile
Show code
impact_df.round(4)
The impact exponent \(\hat{\delta}\) near 0.5 across size quintiles confirms the universality of the square-root law. Smaller firms typically exhibit higher impact coefficients (larger \(c\)), consistent with lower liquidity.
45.3.3 Transaction Cost Decomposition
Total transaction costs comprise multiple components, each with distinct economic content:
Initial public offerings present a classic information asymmetry problem. Issuers and underwriters do not know the true market-clearing price; informed investors do (approximately). The allocation mechanism (i.e., how shares are distributed and at what price) determines the IPO’s pricing efficiency, the degree of underpricing, and the distribution of surplus between issuers and investors.
Rock (1986) provides the canonical model. There are two types of investors: informed (who know the true value \(v\)) and uninformed (who know only the distribution \(v \sim F\)). If the IPO is priced at \(P\):
When \(v > P\) (good IPO): both informed and uninformed subscribe, so uninformed receive only a fraction of their order (rationing).
When \(v < P\) (bad IPO): only uninformed subscribe, so they receive full allocation (the “winner’s curse”).
The uninformed investor’s expected return, accounting for rationing, is:
where \(\alpha_g < 1\) is the allocation probability in good IPOs (rationed) and allocation is 1 in bad IPOs. For uninformed investors to participate, \(E[R_{\text{uninformed}}] \geq 0\), which requires:
That is, IPOs must be underpriced on average to compensate uninformed investors for the winner’s curse. The degree of required underpricing increases with the proportion of informed investors and the variance of the true value.
45.4.2 Book Building vs. Auction Mechanisms
Vietnamese IPO history provides variation in allocation mechanisms. State-owned enterprise equitizations have used Dutch auctions, while private-sector IPOs have used book building. This institutional variation allows structural comparison of the mechanisms.
Book Building(Benveniste and Spindt 1989): The underwriter solicits indications of interest from institutional investors during the roadshow. Investors who reveal positive information (high valuations) receive favorable allocations as compensation. The mechanism aggregates information efficiently but grants discretion to the underwriter.
Uniform-Price Auction: All winning bidders pay the same market-clearing price. This eliminates the underwriter’s allocation discretion but may lead to free-riding on information revelation.
45.4.3 Structural Estimation of Information Asymmetry
We estimate the Rock (1986) model parameters (i.e., the fraction of informed investors (\(\mu\)) and the precision of their information (\(\sigma_v^2\))) using the method of simulated moments (MSM).
The model predicts two key moments:
1 . Average underpricing: \(E[U] = f(\mu, \sigma_v^2)\) 2. Cross-sectional variance of underpricing: \(\text{Var}(U) = g(\mu, \sigma_v^2)\)
We match these model-implied moments to the data.
Show code
def rock_model_moments(params, n_sim=10000):""" Simulate Rock (1986) IPO model and compute moments. Parameters ---------- params : tuple (mu, sigma_v) - fraction of informed investors, std dev of true value. Returns ------- tuple : (mean underpricing, variance of underpricing). """ mu, sigma_v = params np.random.seed(42)# True values v = np.random.lognormal(mean=0, sigma=sigma_v, size=n_sim)# Offer price: set to break even for uninformed# Simplified: P = E[v] * discount_factor P = np.exp(sigma_v**2/2) *0.9# 10% average discount# First-day returns returns = (v - P) / P# Allocation probability for uninformed in good IPOs# Depends on mu: more informed -> more rationing good_mask = v > P alpha_g = (1- mu) /1.0# Simplified rationing# Uninformed realized returns uninformed_returns = np.where( good_mask, alpha_g * returns, returns ) mean_u = uninformed_returns.mean() var_u = uninformed_returns.var()return mean_u, var_udef rock_model_objective(params, target_moments, weight_matrix=None):""" GMM objective for Rock model estimation. """ mu, sigma_v = paramsif mu <=0or mu >=1or sigma_v <=0or sigma_v >2:return1e10 model_moments = rock_model_moments(params) diff = np.array(model_moments) - np.array(target_moments)if weight_matrix isNone: weight_matrix = np.eye(len(diff))return diff @ weight_matrix @ diff# Target moments from datatarget_mean = ipo_data["underpricing"].mean()target_var = ipo_data["underpricing"].var()# Estimateresult = minimize( rock_model_objective, x0=[0.3, 0.5], args=([target_mean, target_var],), method="Nelder-Mead", options={"maxiter": 5000})mu_hat, sigma_v_hat = result.xprint(f"Rock Model Estimates:")print(f" Fraction informed (μ): {mu_hat:.4f}")print(f" Value uncertainty (σ_v): {sigma_v_hat:.4f}")print(f" Objective value: {result.fun:.6f}")
45.4.4 Counterfactual: Welfare Under Alternative Mechanisms
With the structural parameters \((\hat{\mu}, \hat{\sigma}_v)\) in hand, we can simulate counterfactual outcomes. For instance: if all Vietnamese IPOs used uniform-price auctions instead of book building, how would underpricing and welfare change?
Show code
def simulate_ipo_mechanism(mu, sigma_v, mechanism="book_building", n_sim=50000):""" Simulate IPO outcomes under different mechanisms. Returns issuer surplus, informed profit, uninformed profit. """ np.random.seed(42) v = np.random.lognormal(mean=0, sigma=sigma_v, size=n_sim)if mechanism =="book_building":# Price partially reveals information# P = E[v] + 0.5 * (v - E[v]) * signal_quality signal = v + np.random.normal(0, sigma_v *0.5, n_sim) P = np.exp(sigma_v**2/2) +0.3* (signal - np.exp(sigma_v**2/2)) P = P.clip(min=0.1)elif mechanism =="auction":# Competitive bidding: P closer to v noise = np.random.normal(0, sigma_v *0.3, n_sim) P = v + noise P = P.clip(min=0.1)elif mechanism =="fixed_price":# Fixed at E[v] * discount P = np.full(n_sim, np.exp(sigma_v**2/2) *0.85) underpricing = (v - P) / P issuer_surplus = P # Revenue per share investor_surplus = v - P # Profit per sharereturn {"mechanism": mechanism,"mean_underpricing": underpricing.mean(),"median_underpricing": np.median(underpricing),"std_underpricing": underpricing.std(),"issuer_revenue": issuer_surplus.mean(),"investor_profit": investor_surplus.mean(),"money_left": (investor_surplus[investor_surplus >0]).sum() / n_sim }# Compare mechanismscounterfactual = pd.DataFrame([ simulate_ipo_mechanism(mu_hat, sigma_v_hat, "book_building"), simulate_ipo_mechanism(mu_hat, sigma_v_hat, "auction"), simulate_ipo_mechanism(mu_hat, sigma_v_hat, "fixed_price")]).set_index("mechanism").round(4)counterfactual
45.5 Limit Order Book Models
45.5.1 Order Submission as a Strategic Choice
In a limit order market, every trader faces a fundamental tradeoff: submit a market order (immediate execution, but at an adverse price) or a limit order (better price, but risk of non-execution). Parlour (1998) models this as a sequential game where each trader’s optimal strategy depends on the current state of the order book.
Let \(a_t\) and \(b_t\) denote the best ask and bid prices at time \(t\), with the spread \(s_t = a_t - b_t\). A buyer who arrives at time \(t\) chooses between:
Market buy: Execute immediately at \(a_t\). Cost: \(a_t - v_t\) where \(v_t\) is the true value.
Limit buy at\(b_t\): Provides liquidity. If executed, profit: \(v_t - b_t\). Probability of execution: \(\pi(b_t, \text{book state})\).
Rearranging: market orders are optimal when the spread is narrow relative to the non-execution risk of limit orders. This generates the empirically observed pattern that limit orders are more attractive when spreads are wide and the book is thin.
45.5.2 The Glosten-Milgrom Model
Glosten and Milgrom (1985) provide the foundational structural model of bid-ask spread determination under asymmetric information. A competitive market maker sets bid and ask prices to break even in expectation, recognizing that some trades come from informed traders.
Let \(\mu\) denote the probability that an incoming order is from an informed trader, and let \(V^H\) and \(V^L\) denote the high and low values of the asset (\(\Pr(V = V^H) = p\)). The zero-profit conditions yield:
The spread \(s = a - b\) is positive whenever \(\mu > 0\), and increases in both the proportion of informed traders (\(\mu\)) and the information asymmetry (\(V^H - V^L\)). This decomposition is foundational: the spread compensates liquidity providers for the adverse selection cost of trading with informed counterparties.
45.5.3 PIN: Probability of Informed Trading
Easley et al. (1996) extend the Glosten-Milgrom framework to a dynamic setting and develop the Probability of Informed Trading (PIN) measure, which can be estimated from trade data. The model assumes that on each trading day, an information event occurs with probability \(\alpha\). Conditional on an event, it is bad news with probability \(\delta\). Informed traders arrive at rate \(\mu\); uninformed buyers and sellers arrive at rates \(\varepsilon_b\) and \(\varepsilon_s\), respectively.
The likelihood of observing \(B_t\) buys and \(S_t\) sells on day \(t\) is:
where \(f(\cdot|\lambda)\) is the Poisson density with rate \(\lambda\), and \(\boldsymbol{\theta} = (\alpha, \delta, \mu, \varepsilon_b, \varepsilon_s)\). PIN is then:
Easley, Hvidkjaer, and O’hara (2002) argue that PIN should be priced in the cross-section of expected returns: stocks with higher information asymmetry require a risk premium to compensate uninformed investors. We test this prediction using Fama-MacBeth regressions.
45.5.5 Estimation Challenges in Limit Order Book Models
Structural estimation of limit order book models faces several challenges specific to Vietnamese markets:
Tick size effects. Vietnamese exchanges use discrete tick sizes that vary with price level. At low prices, the minimum tick size represents a large fraction of the price, artificially widening the spread and distorting the structural parameters. The standard Glosten-Milgrom and PIN models assume continuous prices; adapting them to discrete price grids requires the modifications proposed by Bollen, Smith, and Whaley (2004).
Price limits. When a stock hits its price limit, the order book is effectively frozen at one side. Orders accumulate but cannot execute until the limit is lifted. This creates a censoring problem analogous to the price limit censoring in return distributions: the observed order flow is a truncated version of the latent flow.
Missing data. Full order book snapshots at high frequency are not universally available for Vietnamese stocks. PIN estimation requires only daily buy/sell counts, making it feasible with lower-frequency data. But richer models (Foucault (1999) dynamic limit order book, Roşu (2009) continuous-time model) require tick-by-tick order book data that may be unavailable for smaller stocks.
Institutional features. The coexistence of HOSE (continuous auction) and HNX (with periodic call auctions for less liquid stocks) creates heterogeneity in the appropriate structural model. A model estimated on HOSE continuous trading data does not directly apply to HNX call auction stocks.
45.6 When Structural Models Are Worth It
45.6.1 Decision Framework
Structural estimation is not always the right tool. The additional complexity, data requirements, and model risk must be justified by the research question. Table 45.6 provides a decision framework.
Table 45.6: When to Choose Structural vs. Reduced Form
Criterion
Structural Preferred
Reduced Form Preferred
Research question
Counterfactual or policy evaluation
Causal effect of observed variation
Data availability
Rich micro-data (transactions, holdings)
Standard panel (returns, fundamentals)
Institutional environment
Stable (model assumptions plausible)
Rapidly changing (model may be misspecified)
Number of parameters
Few primitives, many observables
Many parameters, few identifying assumptions
Publication venue
JF, RFS, Econometrica, JPE
JFE, RFS, MS, JFQA
Computational budget
Weeks to months of estimation
Hours to days
45.6.2 Data Requirements
Structural models are data-hungry. Table 45.7 summarizes the minimum data needs for each model class covered in this chapter.
Table 45.7: Data Requirements for Structural Estimation
Model
Minimum Data
Ideal Data
Vietnamese Availability
KY demand system
Quarterly institutional holdings + prices
13F-equivalent filings + fund flows
Partial (semi-annual disclosure)
Almgren-Chriss
Daily volume, spread, volatility
Intraday order-by-order data
Good (HOSE provides)
Rock/IPO
Offer price, first-day close, allocation
Investor-level bids and allocations
Limited (auction data available)
PIN
Daily buy/sell counts (Lee-Ready)
Tick-by-tick trade and quote
Moderate (requires trade classification)
Glosten-Milgrom
Best bid/ask, trade direction
Full order book snapshots
Moderate (top-of-book available)
45.6.3 Computational Cost
Structural estimation is orders of magnitude more expensive than reduced-form regression. The cost arises from three sources:
Likelihood evaluation. Each evaluation of the structural likelihood or moment function requires solving the model for given parameters. For dynamic models (e.g., inventory models, dynamic discrete choice), this involves solving a dynamic programming problem at each iteration.
Optimization. The GMM or MLE objective is typically non-convex, requiring multiple starting points and global optimization algorithms. The PIN model with 5 parameters requires \(\sim 10\) random starts; a richer model with \(\sim 20\) parameters might require hundreds.
Inference. Standard errors for structural parameters often require bootstrapping (because the asymptotic distribution is non-standard or the delta method is unreliable), adding a multiplicative factor of 200–1000 to the computational budget.
Computational Cost Comparison:
OLS (100k obs): 42.7 ms per estimation
PIN (250 days, 5 starts): 26.9 s per stock
Ratio: 632x
45.6.4 Interpretation Risks
The primary risk of structural estimation is model misspecification. If the model is wrong, the estimated parameters have no economic meaning and the counterfactual predictions are unreliable. Several strategies mitigate this risk:
Specification tests. Over-identifying restrictions (more moments than parameters) provide the Hansen \(J\)-test for model fit. A rejection suggests misspecification, though the test has low power in small samples.
Model comparison. Estimate multiple nested or non-nested models and compare fit. If a simpler model fits the data equally well, prefer it (Occam’s razor).
Sensitivity analysis. Report how structural parameters change under plausible alternative assumptions (e.g., different functional forms for the impact function, different distributional assumptions for valuations).
Out-of-sample validation. Estimate the model on one sample period and test its predictions on a holdout sample. Structural models that fit in-sample but fail out-of-sample are likely overfit.
45.6.5 Journal Expectations
Structural papers in top finance journals are held to specific standards:
Identification clarity. The paper must clearly state which parameters are identified, from which moments, and what variation in the data provides identification. The Rust (1987) critique that without clear identification, structural estimation is curve-fitting, must be addressed head-on.
Counterfactual credibility. Counterfactual exercises should be economically motivated and the range of counterfactual scenarios should not extrapolate far beyond the data.
Robustness to specification. Reviewers will ask what happens under alternative distributional assumptions, alternative moment conditions, and alternative model features.
Transparency. Code and data should be made available. Structural estimation is sufficiently complex that replicability is a first-order concern.
45.7 Summary
This chapter introduced structural estimation as a distinct methodology for financial economics, one that trades the simplicity and transparency of reduced-form analysis for the ability to estimate economic primitives and conduct counterfactual policy evaluation. We implemented four classes of structural models: demand systems for financial assets, optimal execution and transaction cost models, IPO auction models, and limit order book models of informed trading.
The demand system approach of Koijen and Yogo (2019) reveals that institutional investors in Vietnam exhibit heterogeneous demand elasticities across characteristics, with foreign investors showing different sensitivity patterns than domestic institutions. Market impact estimation confirms the universality of the square-root law, while the Almgren-Chriss framework provides the optimal execution benchmark for large institutional orders. The Rock IPO model quantifies the information asymmetry premium embedded in Vietnamese IPO underpricing, and the PIN model estimates the probability of informed trading across the cross-section.
Each model involves a tradeoff between the economic questions it can answer and the assumptions it requires. The decision to use structural estimation should be driven by the research question (specifically, whether counterfactual analysis is essential) and tempered by honest assessment of whether the model’s assumptions are sufficiently credible in the Vietnamese institutional environment. When they are, structural models provide insights that no amount of reduced-form regression can deliver.
Almgren, Robert, and Neil Chriss. 2001. “Optimal Execution of Portfolio Transactions.”Journal of Risk 3: 5–40.
Benveniste, Lawrence M, and Paul A Spindt. 1989. “How Investment Bankers Determine the Offer Price and Allocation of New Issues.”Journal of Financial Economics 24 (2): 343–61.
Berry, Steven, James Levinsohn, and Ariel Pakes. 1995. “Automobile Prices in Market Equilibrium.”Econometrica 63 (4): 841–90.
Bollen, Nicolas PB, Tom Smith, and Robert E Whaley. 2004. “Modeling the Bid/Ask Spread: Measuring the Inventory-Holding Premium.”Journal of Financial Economics 72 (1): 97–141.
Easley, David, Soeren Hvidkjaer, and Maureen O’hara. 2002. “Is Information Risk a Determinant of Asset Returns?”The Journal of Finance 57 (5): 2185–2221.
Easley, David, Nicholas M Kiefer, Maureen O’hara, and Joseph B Paperman. 1996. “Liquidity, Information, and Infrequently Traded Stocks.”The Journal of Finance 51 (4): 1405–36.
Foucault, Thierry. 1999. “Order Flow Composition and Trading Costs in a Dynamic Limit Order Market.”Journal of Financial Markets 2 (2): 99–134.
Gabaix, Xavier, Parameswaran Gopikrishnan, Vasiliki Plerou, and H Eugene Stanley. 2003. “A Theory of Power-Law Distributions in Financial Market Fluctuations.”Nature 423 (6937): 267–70.
Gabaix, Xavier, and Ralph SJ Koijen. 2021. “In Search of the Origins of Financial Fluctuations: The Inelastic Markets Hypothesis.” National Bureau of Economic Research.
Glosten, Lawrence R, and Paul R Milgrom. 1985. “Bid, Ask and Transaction Prices in a Specialist Market with Heterogeneously Informed Traders.”Journal of Financial Economics 14 (1): 71–100. https://doi.org/10.1016/0304-405X(85)90044-3.
Hasbrouck, Joel. 1991. “Measuring the Information Content of Stock Trades.”The Journal of Finance 46 (1): 179–207.
Koijen, Ralph SJ, and Motohiro Yogo. 2019. “A Demand System Approach to Asset Pricing.”Journal of Political Economy 127 (4): 1475–515.
Kyle, Albert S. 1985. “Continuous Auctions and Insider Trading.”Econometrica: Journal of the Econometric Society 53 (6): 1315–35. https://doi.org/10.2307/1913210.
Parlour, Christine A. 1998. “Price Dynamics in Limit Order Markets.”The Review of Financial Studies 11 (4): 789–816.
Rock, Kevin. 1986. “Why New Issues Are Underpriced.”Journal of Financial Economics 15 (1-2): 187–212.
Roşu, Ioanid. 2009. “A Dynamic Model of the Limit Order Book.”The Review of Financial Studies 22 (11): 4601–41.
Rust, John. 1987. “Optimal Replacement of GMC Bus Engines: An Empirical Model of Harold Zurcher.”Econometrica: Journal of the Econometric Society, 999–1033.
# Structural Models in FinanceMost of empirical finance is reduced-form: regress returns on characteristics, estimate risk premia from factor portfolios, test whether a coefficient is significantly different from zero. Structural estimation takes a fundamentally different approach. It begins with an explicit economic model (i.e., specifying agents' preferences, information sets, constraints, and optimization problems) and estimates the model's primitive parameters directly from observed data. The payoff is the ability to perform counterfactual analysis: what would happen to prices if trading costs fell by half? How would IPO underpricing change if the allocation mechanism switched from book building to a uniform-price auction? What is the welfare cost of a particular market design choice? These questions are unanswerable within a reduced-form framework because they require knowledge of the data-generating process, not merely statistical associations within a single regime.This chapter introduces the key structural estimation frameworks used in financial economics and implements them for Vietnamese equity markets. We cover four domains where structural models have proven most productive: investor demand estimation, trading cost and execution models, primary market auctions, and limit order book dynamics. Each domain involves distinct economic primitives such as preferences, beliefs, transaction costs, information asymmetries and each requires domain-specific identification strategies.Vietnamese markets offer both challenges and opportunities for structural estimation. On the challenge side, data quality is uneven, market microstructure is evolving, and institutional features (price limits, foreign ownership caps, state ownership) introduce constraints that standard models do not accommodate. On the opportunity side, several features of Vietnamese markets create natural variation that aids identification: the coexistence of two exchanges (HOSE and HNX) with different trading rules, regulatory changes that shift trading costs and price limits, and the phased liberalization of foreign ownership caps that generates exogenous demand shocks.```{python}#| label: setup#| message: falseimport pandas as pdimport numpy as npfrom scipy import stats, optimizefrom scipy.optimize import minimize, minimize_scalarimport statsmodels.api as smimport statsmodels.formula.api as smffrom linearmodels.panel import PanelOLSfrom linearmodels.iv import IV2SLSimport plotnine as p9from mizani.formatters import percent_format, comma_formatimport warningswarnings.filterwarnings("ignore")```## What Structural Estimation Means in Finance### Reduced Form Versus StructuralConsider two researchers studying the effect of transaction costs on trading volume. The reduced-form researcher exploits a fee reduction event and estimates:$$\ln V_{i,t} = \alpha + \beta \cdot \text{Post}_t + \gamma \mathbf{X}_{i,t} + \varepsilon_{i,t}$$ {#eq-reduced-form}The coefficient $\beta$ identifies the causal effect of the fee change on volume, but it is local to this specific fee change, this specific market, and this specific time period. It says nothing about what a different fee change would do, or what the optimal fee structure might be.The structural researcher writes down an explicit model of trader behavior:$$\max_{q_t} \; E_t\left[\sum_{s=t}^{T} \delta^{s-t}\left(v_s q_s - c(q_s; \boldsymbol{\theta})\right)\right]$$ {#eq-structural-trader}where $v_s$ is the (possibly private) valuation, $q_s$ is the trade quantity, $c(\cdot; \boldsymbol{\theta})$ is the cost function parameterized by $\boldsymbol{\theta}$, and $\delta$ is the discount factor. The researcher estimates $\boldsymbol{\theta}$ by requiring that the model's predictions match observed trading patterns. With $\hat{\boldsymbol{\theta}}$ in hand, any counterfactual cost function $c'(\cdot; \boldsymbol{\theta}')$ can be fed into the model to predict the equilibrium response.The distinction is not about sophistication (reduced-form work can be highly rigorous) but about the type of question being answered:|| Reduced Form | Structural ||------------------|------------------------------|-------------------------|| Answers | "What happened?" | "What would happen if...?" || Identification | Exogenous variation (events, instruments) | Model restrictions + data moments || Key assumption | Unconfoundedness or exclusion restriction | Correct model specification || Output | Treatment effects, associations | Primitive parameters, counterfactuals || Risk | Omitted variable bias, weak instruments | Model misspecification |: Reduced Form vs. Structural Estimation {#tbl-rf-vs-structural}### Identification Through Economic PrimitivesStructural identification relies on the economic model to convert observed outcomes into unobserved primitives. The classic example is the demand-supply system. Observing prices and quantities alone cannot identify demand and supply curves (the simultaneity problem). But if we know the functional form of demand and supply, and have instruments that shift one curve but not the other, the system is identified.In financial contexts, identification often comes from the model's equilibrium conditions. In @kyle1985continuous, the informed trader's strategy, the market maker's pricing rule, and the noise trader's demand jointly determine the equilibrium price impact coefficient $\lambda$. The model maps the observable (i.e., the price impact of order flow) to the unobservable (i.e., the precision of private information). The identification is through the model's structure, not through a conventional instrument.Formally, let $\boldsymbol{\theta} \in \Theta$ denote the vector of structural parameters and $\mathbf{m}(\boldsymbol{\theta})$ the model-implied moments. The data provide empirical moments $\hat{\mathbf{m}}$. Identification requires that the mapping $\boldsymbol{\theta} \mapsto \mathbf{m}(\boldsymbol{\theta})$ is injective: different parameter values produce different observable implications.$$\hat{\boldsymbol{\theta}} = \arg\min_{\boldsymbol{\theta} \in \Theta} \left[\hat{\mathbf{m}} - \mathbf{m}(\boldsymbol{\theta})\right]' \mathbf{W} \left[\hat{\mathbf{m}} - \mathbf{m}(\boldsymbol{\theta})\right]$$ {#eq-gmm-structural}This is the Generalized Method of Moments (GMM) framework applied to structural estimation. The choice of weighting matrix $\mathbf{W}$ determines efficiency, and over-identifying restrictions (more moments than parameters) provide specification tests.### Tradeoffs Between Realism and TractabilityEvery structural model involves choices about which features of reality to include and which to abstract from. @tbl-tradeoff illustrates this for several canonical finance models.| Model | Key Simplification | What It Misses | What It Gains ||-----------------|---------------------|-----------------|-----------------|| @kyle1985continuous | Single informed trader, normal distributions | Multiple insiders, fat tails | Closed-form $\lambda$, clean identification || @glosten1985bid | Binary signal, competitive market makers | Complex information, inventory costs | Bid-ask spread decomposition || @koijen2019demand | Characteristics-based demand, no dynamics | Dynamic portfolio rebalancing | Demand elasticity estimation at scale || @roll1984simple | No information, serial independence | Information-driven trades | Spread estimation from return autocovariance |: Structural Model Tradeoffs {#tbl-tradeoff}The researcher's task is to choose the simplest model that captures the economic mechanism of interest while remaining rich enough that its counterfactual predictions are credible. As @rust1987optimal emphasized, there is a tension between models that are "structurally correct" but computationally intractable and models that are tractable but potentially misspecified.## Demand Estimation for Financial Assets### The Demand System Approach@koijen2019demand (hereafter KY) develop a demand system for financial assets that estimates the elasticity of institutional investor demand with respect to asset characteristics. The framework adapts the discrete-choice demand estimation methodology of @berry1995automobile from industrial organization to financial markets.Each investor $i$ holds a portfolio of $N$ assets. The demand for asset $j$ by investor $i$ at time $t$ is:$$w_{ij,t} = \frac{\exp(\delta_{j,t} + \boldsymbol{\beta}_i' \mathbf{x}_{j,t})}{1 + \sum_{k=1}^{N} \exp(\delta_{k,t} + \boldsymbol{\beta}_i' \mathbf{x}_{k,t})}$$ {#eq-ky-demand}where $w_{ij,t}$ is the portfolio weight of asset $j$ for investor $i$, $\delta_{j,t}$ is the mean utility (common valuation), $\mathbf{x}_{j,t}$ is a vector of observable characteristics (market cap, book-to-market, momentum, etc.), and $\boldsymbol{\beta}_i$ captures investor-specific preferences (heterogeneous demand elasticities). The denominator includes 1 to represent the outside option (holding cash or non-equity assets).The key insight is that the market-clearing condition links demand to equilibrium prices:$$\sum_i A_{i,t} \cdot w_{ij,t}(\boldsymbol{\theta}) = \text{ME}_{j,t} \qquad \forall j, t$$ {#eq-market-clearing}where $A_{i,t}$ is investor $i$'s total assets under management and $\text{ME}_{j,t}$ is the market capitalization of asset $j$. This system of equations determines the equilibrium price impacts and demand elasticities.### Demand Elasticity EstimationThe demand elasticity of asset $j$ with respect to its own price (or a characteristic that moves its price) is:$$\varepsilon_{jj} = \frac{\partial \ln w_{ij}}{\partial \ln P_j} = \beta_{\text{price}} \cdot (1 - w_{ij})$$ {#eq-demand-elasticity}In a frictionless market with perfectly elastic demand, a supply shock (e.g., an index addition) would have zero price impact. Empirically, demand curves for financial assets slope downward with finite elasticity, implying that supply shocks move equilibrium prices.```{python}#| label: load-holdings-data#| eval: false# DataCore.vn APIfrom datacore import DataCoredc = DataCore()# Load institutional holdings dataholdings = dc.get_institutional_holdings( start_date="2012-01-01", end_date="2024-12-31")# Load firm characteristicsfirm_chars = dc.get_firm_characteristics( start_date="2012-01-01", end_date="2024-12-31")# Load market datamarket_data = dc.get_daily_returns( start_date="2012-01-01", end_date="2024-12-31")print(f"Holdings observations: {len(holdings)}")print(f"Unique institutions: {holdings['institution_id'].nunique()}")print(f"Unique stocks: {holdings['ticker'].nunique()}")``````{python}#| label: demand-system-construction#| eval: false# Construct demand system variablesdemand = holdings.merge( firm_chars[["ticker", "date", "market_cap", "book_to_market","momentum_12m", "log_size", "profitability","investment", "industry"]], on=["ticker", "date"], how="inner")# Portfolio weightsdemand["total_aum"] = demand.groupby( ["institution_id", "date"])["holding_value"].transform("sum")demand["port_weight"] = demand["holding_value"] / demand["total_aum"]# Log portfolio weight relative to outside option# w_ij / w_i0 where w_i0 = 1 - sum(w_ij) for equity holdingsdemand["sum_equity_weight"] = demand.groupby( ["institution_id", "date"])["port_weight"].transform("sum")demand["outside_weight"] =1- demand["sum_equity_weight"].clip(upper=0.99)demand["log_weight_ratio"] = np.log( demand["port_weight"] / demand["outside_weight"])``````{python}#| label: demand-estimation#| eval: false# Simplified KY demand estimation# log(w_ij / w_i0) = delta_j + beta_i' x_j + epsilon_ij# First stage: estimate mean utility delta_j via OLS with asset FE# Cross-sectional regression at each datedef estimate_demand_cross_section(group):""" Estimate demand parameters for a single cross-section. Uses OLS with stock fixed effects absorbed. """ g = group.dropna(subset=["log_weight_ratio", "log_size", "book_to_market","momentum_12m", "profitability" ])iflen(g) <50:return pd.Series(dtype=float) X = g[["log_size", "book_to_market", "momentum_12m","profitability"]].values X = sm.add_constant(X) y = g["log_weight_ratio"].valuestry: model = sm.OLS(y, X).fit()return pd.Series({"beta_size": model.params[1],"beta_btm": model.params[2],"beta_mom": model.params[3],"beta_prof": model.params[4],"r_squared": model.rsquared,"n_obs": len(g) })exceptException:return pd.Series(dtype=float)# Estimate by institution typedemand["institution_type"] = demand["institution_type"].fillna("Other")demand_params = ( demand.groupby(["institution_type", "date"]) .apply(estimate_demand_cross_section) .reset_index() .dropna())``````{python}#| label: tbl-demand-params#| eval: false#| tbl-cap: "Demand Elasticities by Investor Type"avg_params = ( demand_params.groupby("institution_type") .agg( beta_size=("beta_size", "mean"), beta_btm=("beta_btm", "mean"), beta_mom=("beta_mom", "mean"), beta_prof=("beta_prof", "mean"), avg_r2=("r_squared", "mean"), n_periods=("date", "nunique") ) .round(4))avg_params``````{python}#| label: fig-demand-elasticity-time#| eval: false#| fig-cap: "Time-Varying Demand Sensitivity to Size Across Investor Types"top_types = demand_params.groupby("institution_type").size().nlargest(4).indexplot_data = demand_params[ demand_params["institution_type"].isin(top_types)].copy()( p9.ggplot(plot_data, p9.aes( x="date", y="beta_size", color="institution_type" ))+ p9.geom_smooth(method="lowess", se=False, size=1)+ p9.labs( x="", y="β (Size)", title="Institutional Demand Sensitivity to Firm Size", color="Institution Type" )+ p9.theme_minimal()+ p9.theme(figure_size=(12, 5), legend_position="top"))```### Price Pressure and Market ImpactThe demand system framework directly yields price impact predictions. If investor $i$ receives an exogenous inflow $\Delta A_i$ (e.g., from a fund flow shock), the price of each stock must adjust to clear the market with the new demand. The equilibrium price impact of a demand shock is:$$\frac{\Delta P_j}{P_j} = \frac{1}{\varepsilon_{jj}} \cdot \frac{\Delta D_j}{D_j}$$ {#eq-price-impact-demand}where $\varepsilon_{jj}$ is the demand elasticity and $\Delta D_j / D_j$ is the percentage demand shock. Less elastic demand implies larger price impact for the same demand shock (a prediction with direct implications for the price impact of index rebalancing, fund flows, and forced selling).```{python}#| label: price-pressure#| eval: false# Estimate aggregate demand elasticity via index rebalancing events# Use VN30 index reconstitutions as demand shocksindex_changes = dc.get_index_changes( index="VN30", start_date="2012-01-01", end_date="2024-12-31")# Event study: abnormal returns around index additions/deletionsdef event_study_car(events, returns, window=(-5, 20)):""" Compute cumulative abnormal returns around events. Parameters ---------- events : DataFrame Columns: ticker, event_date, event_type (addition/deletion). returns : DataFrame Columns: ticker, date, ret, mkt_ret. Returns ------- DataFrame : CARs by event type and event day. """ results = []for _, event in events.iterrows(): ticker = event["ticker"] event_date = pd.to_datetime(event["event_date"])# Estimation window: -260 to -11 stock_rets = returns[returns["ticker"] == ticker].copy() stock_rets = stock_rets.sort_values("date") est_mask = ( (stock_rets["date"] >= event_date - pd.Timedelta(days=365)) & (stock_rets["date"] < event_date - pd.Timedelta(days=15)) ) est_data = stock_rets[est_mask].dropna(subset=["ret", "mkt_ret"])iflen(est_data) <60:continue# Market model model = sm.OLS( est_data["ret"], sm.add_constant(est_data["mkt_ret"]) ).fit()# Event window evt_mask = ( (stock_rets["date"] >= event_date + pd.Timedelta(days=window[0] *1.5)) & (stock_rets["date"] <= event_date + pd.Timedelta(days=window[1] *1.5)) ) evt_data = stock_rets[evt_mask].dropna(subset=["ret", "mkt_ret"])iflen(evt_data) <5:continue# Abnormal returns evt_data = evt_data.copy() evt_data["expected"] = model.predict( sm.add_constant(evt_data["mkt_ret"]) ) evt_data["ar"] = evt_data["ret"] - evt_data["expected"]# Assign event-time index trading_dates = evt_data["date"].sort_values().values event_idx = np.searchsorted(trading_dates, event_date) evt_data["event_day"] =range(-event_idx, len(evt_data) - event_idx) evt_data["event_type"] = event["event_type"] evt_data["ticker"] = ticker results.append(evt_data[["ticker", "event_day", "ar", "event_type"]])ifnot results:return pd.DataFrame()return pd.concat(results, ignore_index=True)# Compute event studydaily_data = market_data.merge( dc.get_market_returns( start_date="2012-01-01", end_date="2024-12-31", frequency="daily" )[["date", "mkt_ret"]], on="date", how="left")car_results = event_study_car(index_changes, daily_data)``````{python}#| label: fig-index-event-study#| eval: false#| fig-cap: "Cumulative Abnormal Returns Around VN30 Index Reconstitutions"iflen(car_results) >0:# Average AR by event day and type avg_ar = ( car_results.groupby(["event_type", "event_day"]) .agg( mean_ar=("ar", "mean"), se_ar=("ar", lambda x: x.std() / np.sqrt(len(x))), n=("ar", "count") ) .reset_index() )# Cumulative ARfor etype in avg_ar["event_type"].unique(): mask = avg_ar["event_type"] == etype avg_ar.loc[mask, "car"] = avg_ar.loc[mask, "mean_ar"].cumsum() avg_ar = avg_ar[avg_ar["event_day"].between(-5, 20)] ( p9.ggplot(avg_ar, p9.aes( x="event_day", y="car", color="event_type" ))+ p9.geom_line(size=1)+ p9.geom_vline(xintercept=0, linetype="dashed", color="gray")+ p9.geom_hline(yintercept=0, linetype="dotted", color="gray")+ p9.scale_color_manual(values=["#27AE60", "#C0392B"])+ p9.labs( x="Event Day", y="Cumulative Abnormal Return", title="Price Pressure from VN30 Index Additions and Deletions", color="Event" )+ p9.theme_minimal()+ p9.theme(figure_size=(10, 6)) )```The permanent component of the CAR measures the information content of index inclusion, while the temporary component (reversal after the event) measures pure price pressure from demand. In a world with perfectly elastic demand, there would be no temporary price impact. The magnitude of the temporary component is inversely related to the demand elasticity, providing a second identification strategy (complementary to the holdings-based approach) for demand curves.### Demand Inelasticity and Its Implications@gabaix2021search argue that the aggregate demand for equities is remarkably inelastic, with elasticity estimates on the order of 0.2 (meaning a 1% supply increase requires a 5% price decline to clear). The implications are profound: if demand is this inelastic, then flows (from index funds, foreign investors, retail traders) have outsized effects on prices. In Vietnamese markets, where foreign ownership caps create binding constraints on a key investor class, demand inelasticity may be even more severe.The inelasticity also generates a role for "the market portfolio" as an equilibrium concept: if most investors hold portfolios close to the market (as in CAPM), then deviations from market weights require someone to absorb the excess supply, and the compensation required is the inverse of demand elasticity.## Structural Models of Trading Strategies### Optimal Execution: The Almgren-Chriss FrameworkThe optimal execution problem, formalized by @almgren2001optimal, asks: given a large order to execute over a fixed horizon, what is the optimal trading schedule that minimizes the total execution cost? The problem is fundamental to institutional investing because large orders cannot be executed instantaneously without severe price impact.Let $X_t$ denote the remaining shares to sell at time $t$, with $X_0 = X$ (total order) and $X_T = 0$ (completion). The trading rate is $n_t = X_{t-1} - X_t$. The execution price for shares traded at $t$ is affected by both temporary and permanent price impact:$$S_t = S_0 + \sigma W_t - g(n_t) - h\left(\frac{n_t}{\tau}\right)$$ {#eq-almgren-chriss-price}where $g(\cdot)$ is the permanent impact function, $h(\cdot)$ is the temporary impact function, $\sigma$ is the volatility, $W_t$ is a Wiener process, and $\tau$ is the time interval. The total implementation shortfall (cost of execution relative to the arrival price $S_0$) is:$$\text{IS} = \sum_{t=1}^{T} n_t (S_0 - S_t) = \sum_{t=1}^{T} n_t \left[\sigma W_t + g(n_t) + h\left(\frac{n_t}{\tau}\right)\right]$$ {#eq-shortfall}The trader minimizes a mean-variance objective:$$\min_{\{n_t\}} \; E[\text{IS}] + \lambda \cdot \text{Var}(\text{IS})$$ {#eq-ac-objective}where $\lambda$ is the risk aversion parameter. With linear impact functions $g(n) = \gamma n$ and $h(v) = \eta v + \epsilon \text{sgn}(v)$, the optimal strategy has a closed-form solution.**TWAP (Time-Weighted Average Price):** Trade uniformly: $n_t = X / T$ for all $t$. Optimal when permanent impact dominates temporary impact.**VWAP (Volume-Weighted Average Price):** Trade proportionally to expected volume: $n_t \propto \hat{V}_t$. Approximates TWAP adjusted for intraday volume patterns.**Almgren-Chriss Optimal:** The risk-averse optimal trajectory for linear impact is:$$x_t^* = X \cdot \frac{\sinh(\kappa (T - t))}{\sinh(\kappa T)}$$ {#eq-ac-trajectory}where $\kappa = \sqrt{\lambda \sigma^2 / \eta}$ governs the trade-off between urgency (trading quickly to reduce risk) and patience (trading slowly to reduce impact). High risk aversion ($\lambda$ large) implies front-loaded execution.```{python}#| label: almgren-chriss#| eval: falsedef almgren_chriss_trajectory(X, T, sigma, eta, gamma, lam, n_steps=100):""" Compute Almgren-Chriss optimal execution trajectory. Parameters ---------- X : float Total shares to execute. T : float Execution horizon (in trading days). sigma : float Daily return volatility. eta : float Temporary impact parameter. gamma : float Permanent impact parameter. lam : float Risk aversion parameter. n_steps : int Number of time steps. Returns ------- DataFrame : Time, remaining shares, trading rate, expected cost. """ tau = T / n_steps kappa_sq = lam * sigma**2/ (eta / tau)if kappa_sq <=0:# Risk-neutral: trade uniformly kappa =0 x_t = np.linspace(X, 0, n_steps +1)else: kappa = np.sqrt(kappa_sq) t_grid = np.linspace(0, T, n_steps +1) x_t = X * np.sinh(kappa * (T - t_grid)) / np.sinh(kappa * T)# Trading rates n_t =-np.diff(x_t)# Expected cost components perm_cost = gamma * np.sum(n_t**2) temp_cost = (eta / tau) * np.sum(n_t**2) total_expected_cost = perm_cost + temp_cost# Variance of cost var_cost = sigma**2* tau * np.sum(x_t[:-1]**2) results = pd.DataFrame({"time": np.linspace(0, T, n_steps +1)[:-1],"remaining_shares": x_t[:-1],"trade_rate": n_t }) results.attrs["expected_cost"] = total_expected_cost results.attrs["cost_variance"] = var_cost results.attrs["kappa"] = kappa if kappa_sq >0else0return results``````{python}#| label: fig-execution-trajectories#| eval: false#| fig-cap: "Optimal Execution Trajectories for Different Risk Aversion Levels"# Estimate market impact parameters from Vietnamese data# Typical values for mid-cap Vietnamese stockssigma_daily =0.025# 2.5% daily voleta =2.5e-7# Temporary impactgamma =1.0e-7# Permanent impactX_shares =500000# 500k shares to sellT_days =5# 5-day horizontrajectories = []for lam, label in [(0, "Risk Neutral (TWAP)"), (1e-6, "Low Risk Aversion"), (1e-5, "Medium Risk Aversion"), (1e-4, "High Risk Aversion")]: traj = almgren_chriss_trajectory( X_shares, T_days, sigma_daily, eta, gamma, lam ) traj["strategy"] = label traj["pct_remaining"] = traj["remaining_shares"] / X_shares *100 trajectories.append(traj)traj_all = pd.concat(trajectories, ignore_index=True)( p9.ggplot(traj_all, p9.aes( x="time", y="pct_remaining", color="strategy" ))+ p9.geom_line(size=1)+ p9.scale_color_manual( values=["#95A5A6", "#27AE60", "#2E5090", "#C0392B"] )+ p9.labs( x="Time (Trading Days)", y="Remaining Order (%)", title="Almgren-Chriss Optimal Execution Trajectories", color="Strategy" )+ p9.theme_minimal()+ p9.theme(figure_size=(10, 6), legend_position="top"))```### Estimating Market Impact from Transaction DataThe structural parameters $(\gamma, \eta, \sigma)$ must be estimated from data. The standard approach uses the square-root law of market impact, documented empirically by @kyle1985continuous and @hasbrouck1991measuring and derived theoretically by @gabaix2003theory:$$\Delta P / P = c \cdot \text{sgn}(Q) \cdot |Q / V|^{\delta}$$ {#eq-impact-model}where $Q$ is the signed order flow, $V$ is daily volume, $c$ is the impact coefficient, and $\delta \approx 0.5$ is the impact exponent. The "square root" refers to $\delta = 0.5$, which is remarkably robust across markets, time periods, and asset classes.```{python}#| label: impact-estimation#| eval: false# Load intraday transaction data (or daily order flow proxy)order_flow = dc.get_order_flow( start_date="2018-01-01", end_date="2024-12-31", frequency="daily")# Construct signed order flow using Lee-Ready classification# (buy-initiated minus sell-initiated volume)impact_data = order_flow.merge( dc.get_daily_returns( start_date="2018-01-01", end_date="2024-12-31" )[["ticker", "date", "ret", "volume", "market_cap"]], on=["ticker", "date"], how="inner")# Normalize order flowimpact_data["oib"] = impact_data["net_buy_volume"] / impact_data["volume"]impact_data["abs_oib"] = np.abs(impact_data["oib"])impact_data["sign_oib"] = np.sign(impact_data["oib"])# Log-log regression: ln|ΔP| = α + δ ln|Q/V| + εimpact_data["ln_abs_ret"] = np.log(np.abs(impact_data["ret"]).clip(lower=1e-8))impact_data["ln_abs_oib"] = np.log(impact_data["abs_oib"].clip(lower=1e-8))# Estimate by size quintileimpact_data["size_quintile"] = impact_data.groupby("date")["market_cap"].transform(lambda x: pd.qcut(x, 5, labels=[1, 2, 3, 4, 5], duplicates="drop"))impact_results = []for q inrange(1, 6): subset = impact_data[impact_data["size_quintile"] == q].dropna( subset=["ln_abs_ret", "ln_abs_oib"] )iflen(subset) <500:continue model = sm.OLS( subset["ln_abs_ret"], sm.add_constant(subset["ln_abs_oib"]) ).fit(cov_type="HC1") impact_results.append({"size_quintile": q,"delta_hat": model.params.iloc[1],"delta_se": model.bse.iloc[1],"intercept": model.params.iloc[0],"r_squared": model.rsquared,"n_obs": int(model.nobs) })impact_df = pd.DataFrame(impact_results)``````{python}#| label: tbl-impact-by-size#| eval: false#| tbl-cap: "Market Impact Exponent by Firm Size Quintile"impact_df.round(4)```The impact exponent $\hat{\delta}$ near 0.5 across size quintiles confirms the universality of the square-root law. Smaller firms typically exhibit higher impact coefficients (larger $c$), consistent with lower liquidity.### Transaction Cost DecompositionTotal transaction costs comprise multiple components, each with distinct economic content:$$\text{TC} = \underbrace{\frac{s}{2}}_{\text{Half-spread}} + \underbrace{\gamma \cdot Q}_{\text{Permanent impact}} + \underbrace{\eta \cdot \frac{Q}{V}}_{\text{Temporary impact}} + \underbrace{\sigma \sqrt{T}}_{\text{Timing risk}}$$ {#eq-tc-decomposition}We estimate each component separately, following the @hasbrouck2009trading methodology for effective spread decomposition.```{python}#| label: tc-decomposition#| eval: false# Effective spread estimationspreads = dc.get_bid_ask_data( start_date="2020-01-01", end_date="2024-12-31")# Quoted spreadspreads["quoted_spread"] = ( (spreads["ask"] - spreads["bid"]) / spreads["midpoint"])# Effective spread (from transaction prices)spreads["effective_spread"] = (2* np.abs(spreads["trade_price"] - spreads["midpoint"]) / spreads["midpoint"])# Roll (1984) implied spread from return autocovariancedef roll_spread(returns):""" Estimate the Roll (1984) bid-ask spread. Spread = 2 * sqrt(-Cov(r_t, r_{t-1})) if covariance is negative. """ cov = np.cov(returns[1:], returns[:-1])[0, 1]if cov <0:return2* np.sqrt(-cov)else:return np.nan# Compute by stock-monthdaily_rets = dc.get_daily_returns( start_date="2020-01-01", end_date="2024-12-31")daily_rets["month"] = daily_rets["date"].dt.to_period("M")roll_spreads = ( daily_rets.groupby(["ticker", "month"]) .agg( roll_spread=("ret", lambda x: roll_spread(x.values)iflen(x) >10else np.nan), n_days=("ret", "count"), avg_volume=("volume", "mean"), market_cap=("market_cap", "last") ) .reset_index() .dropna(subset=["roll_spread"]))``````{python}#| label: fig-spread-size#| eval: false#| fig-cap: "Transaction Costs by Firm Size: Roll Implied Spread"# Bin by market cap decilesroll_spreads["size_decile"] = roll_spreads.groupby("month")["market_cap"].transform(lambda x: pd.qcut(x, 10, labels=range(1, 11), duplicates="drop"))spread_by_size = ( roll_spreads.groupby("size_decile") .agg( median_spread=("roll_spread", "median"), mean_spread=("roll_spread", "mean"), q25=("roll_spread", lambda x: x.quantile(0.25)), q75=("roll_spread", lambda x: x.quantile(0.75)) ) .reset_index())( p9.ggplot(spread_by_size, p9.aes(x="size_decile", y="median_spread"))+ p9.geom_bar(stat="identity", fill="#2E5090", alpha=0.7)+ p9.geom_errorbar( p9.aes(ymin="q25", ymax="q75"), width=0.3, color="#2E5090" )+ p9.labs( x="Size Decile (1 = Smallest)", y="Roll Implied Spread", title="Transaction Costs Decrease Monotonically with Firm Size" )+ p9.scale_y_continuous(labels=percent_format())+ p9.theme_minimal()+ p9.theme(figure_size=(10, 5)))```## Auction Models in Primary Markets### The IPO Allocation ProblemInitial public offerings present a classic information asymmetry problem. Issuers and underwriters do not know the true market-clearing price; informed investors do (approximately). The allocation mechanism (i.e., how shares are distributed and at what price) determines the IPO's pricing efficiency, the degree of underpricing, and the distribution of surplus between issuers and investors.@rock1986new provides the canonical model. There are two types of investors: informed (who know the true value $v$) and uninformed (who know only the distribution $v \sim F$). If the IPO is priced at $P$:- When $v > P$ (good IPO): both informed and uninformed subscribe, so uninformed receive only a fraction of their order (rationing).- When $v < P$ (bad IPO): only uninformed subscribe, so they receive full allocation (the "winner's curse").The uninformed investor's expected return, accounting for rationing, is:$$E[R_{\text{uninformed}}] = \alpha_g \cdot E\left[\frac{v - P}{P} \mid v > P\right] \cdot \Pr(v > P) + E\left[\frac{v - P}{P} \mid v \leq P\right] \cdot \Pr(v \leq P)$$ {#eq-rock-return}where $\alpha_g < 1$ is the allocation probability in good IPOs (rationed) and allocation is 1 in bad IPOs. For uninformed investors to participate, $E[R_{\text{uninformed}}] \geq 0$, which requires:$$E\left[\frac{v - P}{P}\right] > 0$$ {#eq-underpricing-condition}That is, IPOs must be underpriced on average to compensate uninformed investors for the winner's curse. The degree of required underpricing increases with the proportion of informed investors and the variance of the true value.### Book Building vs. Auction MechanismsVietnamese IPO history provides variation in allocation mechanisms. State-owned enterprise equitizations have used Dutch auctions, while private-sector IPOs have used book building. This institutional variation allows structural comparison of the mechanisms.**Book Building** [@benveniste1989investment]: The underwriter solicits indications of interest from institutional investors during the roadshow. Investors who reveal positive information (high valuations) receive favorable allocations as compensation. The mechanism aggregates information efficiently but grants discretion to the underwriter.**Uniform-Price Auction**: All winning bidders pay the same market-clearing price. This eliminates the underwriter's allocation discretion but may lead to free-riding on information revelation.```{python}#| label: ipo-data#| eval: false# Load IPO dataipo_data = dc.get_ipo_data( start_date="2005-01-01", end_date="2024-12-31")# Compute first-day returns (underpricing)ipo_data["underpricing"] = ( (ipo_data["first_day_close"] - ipo_data["offer_price"]) / ipo_data["offer_price"])# Classify mechanismipo_data["mechanism"] = ipo_data["ipo_method"].map({"auction": "Auction","book_building": "Book Building","fixed_price": "Fixed Price"})print(f"Total IPOs: {len(ipo_data)}")print(f"By mechanism:\n{ipo_data['mechanism'].value_counts()}")``````{python}#| label: tbl-ipo-underpricing#| eval: false#| tbl-cap: "IPO Underpricing by Allocation Mechanism"ipo_summary = ( ipo_data.groupby("mechanism") .agg( n_ipos=("underpricing", "count"), mean_underpricing=("underpricing", "mean"), median_underpricing=("underpricing", "median"), std_underpricing=("underpricing", "std"), pct_positive=("underpricing", lambda x: (x >0).mean()), mean_proceeds=("proceeds_bn_vnd", "mean") ) .round(4))ipo_summary```### Structural Estimation of Information AsymmetryWe estimate the @rock1986new model parameters (i.e., the fraction of informed investors ($\mu$) and the precision of their information ($\sigma_v^2$)) using the method of simulated moments (MSM).The model predicts two key moments: 1 . Average underpricing: $E[U] = f(\mu, \sigma_v^2)$ 2. Cross-sectional variance of underpricing: $\text{Var}(U) = g(\mu, \sigma_v^2)$We match these model-implied moments to the data.```{python}#| label: rock-model-estimation#| eval: falsedef rock_model_moments(params, n_sim=10000):""" Simulate Rock (1986) IPO model and compute moments. Parameters ---------- params : tuple (mu, sigma_v) - fraction of informed investors, std dev of true value. Returns ------- tuple : (mean underpricing, variance of underpricing). """ mu, sigma_v = params np.random.seed(42)# True values v = np.random.lognormal(mean=0, sigma=sigma_v, size=n_sim)# Offer price: set to break even for uninformed# Simplified: P = E[v] * discount_factor P = np.exp(sigma_v**2/2) *0.9# 10% average discount# First-day returns returns = (v - P) / P# Allocation probability for uninformed in good IPOs# Depends on mu: more informed -> more rationing good_mask = v > P alpha_g = (1- mu) /1.0# Simplified rationing# Uninformed realized returns uninformed_returns = np.where( good_mask, alpha_g * returns, returns ) mean_u = uninformed_returns.mean() var_u = uninformed_returns.var()return mean_u, var_udef rock_model_objective(params, target_moments, weight_matrix=None):""" GMM objective for Rock model estimation. """ mu, sigma_v = paramsif mu <=0or mu >=1or sigma_v <=0or sigma_v >2:return1e10 model_moments = rock_model_moments(params) diff = np.array(model_moments) - np.array(target_moments)if weight_matrix isNone: weight_matrix = np.eye(len(diff))return diff @ weight_matrix @ diff# Target moments from datatarget_mean = ipo_data["underpricing"].mean()target_var = ipo_data["underpricing"].var()# Estimateresult = minimize( rock_model_objective, x0=[0.3, 0.5], args=([target_mean, target_var],), method="Nelder-Mead", options={"maxiter": 5000})mu_hat, sigma_v_hat = result.xprint(f"Rock Model Estimates:")print(f" Fraction informed (μ): {mu_hat:.4f}")print(f" Value uncertainty (σ_v): {sigma_v_hat:.4f}")print(f" Objective value: {result.fun:.6f}")``````{python}#| label: fig-ipo-underpricing-time#| eval: false#| fig-cap: "IPO Underpricing Over Time by Mechanism"ipo_plot = ipo_data.dropna(subset=["underpricing", "mechanism"]).copy()ipo_plot["year"] = pd.to_datetime(ipo_plot["ipo_date"]).dt.yearannual_underpricing = ( ipo_plot.groupby(["year", "mechanism"]) .agg( mean_up=("underpricing", "mean"), n=("underpricing", "count") ) .reset_index() .query("n >= 3"))( p9.ggplot(annual_underpricing, p9.aes( x="year", y="mean_up", color="mechanism" ))+ p9.geom_line(size=1)+ p9.geom_point(p9.aes(size="n"))+ p9.geom_hline(yintercept=0, linetype="dashed", color="gray")+ p9.scale_color_manual(values=["#2E5090", "#C0392B", "#27AE60"])+ p9.scale_y_continuous(labels=percent_format())+ p9.labs( x="Year", y="Average First-Day Return", title="IPO Underpricing by Allocation Mechanism", color="Mechanism", size="# IPOs" )+ p9.theme_minimal()+ p9.theme(figure_size=(12, 6)))```### Counterfactual: Welfare Under Alternative MechanismsWith the structural parameters $(\hat{\mu}, \hat{\sigma}_v)$ in hand, we can simulate counterfactual outcomes. For instance: if all Vietnamese IPOs used uniform-price auctions instead of book building, how would underpricing and welfare change?```{python}#| label: counterfactual-ipo#| eval: falsedef simulate_ipo_mechanism(mu, sigma_v, mechanism="book_building", n_sim=50000):""" Simulate IPO outcomes under different mechanisms. Returns issuer surplus, informed profit, uninformed profit. """ np.random.seed(42) v = np.random.lognormal(mean=0, sigma=sigma_v, size=n_sim)if mechanism =="book_building":# Price partially reveals information# P = E[v] + 0.5 * (v - E[v]) * signal_quality signal = v + np.random.normal(0, sigma_v *0.5, n_sim) P = np.exp(sigma_v**2/2) +0.3* (signal - np.exp(sigma_v**2/2)) P = P.clip(min=0.1)elif mechanism =="auction":# Competitive bidding: P closer to v noise = np.random.normal(0, sigma_v *0.3, n_sim) P = v + noise P = P.clip(min=0.1)elif mechanism =="fixed_price":# Fixed at E[v] * discount P = np.full(n_sim, np.exp(sigma_v**2/2) *0.85) underpricing = (v - P) / P issuer_surplus = P # Revenue per share investor_surplus = v - P # Profit per sharereturn {"mechanism": mechanism,"mean_underpricing": underpricing.mean(),"median_underpricing": np.median(underpricing),"std_underpricing": underpricing.std(),"issuer_revenue": issuer_surplus.mean(),"investor_profit": investor_surplus.mean(),"money_left": (investor_surplus[investor_surplus >0]).sum() / n_sim }# Compare mechanismscounterfactual = pd.DataFrame([ simulate_ipo_mechanism(mu_hat, sigma_v_hat, "book_building"), simulate_ipo_mechanism(mu_hat, sigma_v_hat, "auction"), simulate_ipo_mechanism(mu_hat, sigma_v_hat, "fixed_price")]).set_index("mechanism").round(4)counterfactual```## Limit Order Book Models### Order Submission as a Strategic ChoiceIn a limit order market, every trader faces a fundamental tradeoff: submit a market order (immediate execution, but at an adverse price) or a limit order (better price, but risk of non-execution). @parlour1998price models this as a sequential game where each trader's optimal strategy depends on the current state of the order book.Let $a_t$ and $b_t$ denote the best ask and bid prices at time $t$, with the spread $s_t = a_t - b_t$. A buyer who arrives at time $t$ chooses between:- **Market buy**: Execute immediately at $a_t$. Cost: $a_t - v_t$ where $v_t$ is the true value.- **Limit buy at** $b_t$: Provides liquidity. If executed, profit: $v_t - b_t$. Probability of execution: $\pi(b_t, \text{book state})$.The buyer submits a market order if:$$a_t - v_t < (1 - \pi_t)(v_t - b_t) + \pi_t \cdot 0$$ {#eq-order-choice}Rearranging: market orders are optimal when the spread is narrow relative to the non-execution risk of limit orders. This generates the empirically observed pattern that limit orders are more attractive when spreads are wide and the book is thin.### The Glosten-Milgrom Model@glosten1985bid provide the foundational structural model of bid-ask spread determination under asymmetric information. A competitive market maker sets bid and ask prices to break even in expectation, recognizing that some trades come from informed traders.Let $\mu$ denote the probability that an incoming order is from an informed trader, and let $V^H$ and $V^L$ denote the high and low values of the asset ($\Pr(V = V^H) = p$). The zero-profit conditions yield:$$a = E[V \mid \text{buy order}] = \frac{p(1-\mu) + p\mu}{(1-\mu) + p\mu} V^H + \frac{(1-p)(1-\mu)}{(1-\mu) + p\mu} V^L$$ {#eq-gm-ask}$$b = E[V \mid \text{sell order}] = \frac{p(1-\mu)}{(1-\mu) + (1-p)\mu} V^H + \frac{(1-p)(1-\mu) + (1-p)\mu}{(1-\mu) + (1-p)\mu} V^L$$ {#eq-gm-bid}The spread $s = a - b$ is positive whenever $\mu > 0$, and increases in both the proportion of informed traders ($\mu$) and the information asymmetry ($V^H - V^L$). This decomposition is foundational: the spread compensates liquidity providers for the adverse selection cost of trading with informed counterparties.### PIN: Probability of Informed Trading@easley1996liquidity extend the Glosten-Milgrom framework to a dynamic setting and develop the Probability of Informed Trading (PIN) measure, which can be estimated from trade data. The model assumes that on each trading day, an information event occurs with probability $\alpha$. Conditional on an event, it is bad news with probability $\delta$. Informed traders arrive at rate $\mu$; uninformed buyers and sellers arrive at rates $\varepsilon_b$ and $\varepsilon_s$, respectively.The likelihood of observing $B_t$ buys and $S_t$ sells on day $t$ is:$$\mathcal{L}(B_t, S_t | \boldsymbol{\theta}) = (1-\alpha) f(B_t|\varepsilon_b) f(S_t|\varepsilon_s) + \alpha\delta \cdot f(B_t|\varepsilon_b) f(S_t|\varepsilon_s + \mu) + \alpha(1-\delta) \cdot f(B_t|\varepsilon_b + \mu) f(S_t|\varepsilon_s)$$ {#eq-pin-likelihood}where $f(\cdot|\lambda)$ is the Poisson density with rate $\lambda$, and $\boldsymbol{\theta} = (\alpha, \delta, \mu, \varepsilon_b, \varepsilon_s)$. PIN is then:$$\text{PIN} = \frac{\alpha \mu}{\alpha \mu + \varepsilon_b + \varepsilon_s}$$ {#eq-pin}```{python}#| label: pin-estimationdef pin_log_likelihood(params, data):""" Compute negative log-likelihood for the Easley-Kiefer-O'Hara-Paperman (1996) PIN model. Parameters ---------- params : array [alpha, delta, mu, eps_b, eps_s] data : DataFrame Columns: buys, sells (daily counts). Returns ------- float : Negative log-likelihood. """ alpha, delta, mu, eps_b, eps_s = params# Parameter boundsif (alpha <0or alpha >1or delta <0or delta >1or mu <0or eps_b <0or eps_s <0):return1e15 B = data["buys"].values S = data["sells"].values# Use log-sum-exp for numerical stability# Three components: no event, bad news, good news log_L = np.zeros(len(B))for i inrange(len(B)): b, s = B[i], S[i]# Log-Poisson componentsdef log_poisson(k, lam):if lam <=0:return-1e10if k >0else0return k * np.log(lam) - lam - np.sum(np.log(np.arange(1, k +1)))# No event c1 = (np.log(1- alpha +1e-15) + log_poisson(b, eps_b) + log_poisson(s, eps_s))# Bad news (extra sells) c2 = (np.log(alpha * delta +1e-15) + log_poisson(b, eps_b) + log_poisson(s, eps_s + mu))# Good news (extra buys) c3 = (np.log(alpha * (1- delta) +1e-15) + log_poisson(b, eps_b + mu) + log_poisson(s, eps_s))# Log-sum-exp max_c =max(c1, c2, c3) log_L[i] = max_c + np.log( np.exp(c1 - max_c) + np.exp(c2 - max_c) + np.exp(c3 - max_c) )return-np.sum(log_L)def estimate_pin(buys, sells, n_starts=10):""" Estimate PIN via MLE with multiple random starts. Returns ------- dict : Estimated parameters and PIN value. """ data = pd.DataFrame({"buys": buys, "sells": sells}) best_result =None best_nll = np.inf avg_b = data["buys"].mean() avg_s = data["sells"].mean()for _ inrange(n_starts):# Random initial values alpha0 = np.random.uniform(0.1, 0.8) delta0 = np.random.uniform(0.2, 0.8) mu0 = np.random.uniform(0, max(avg_b, avg_s)) eps_b0 = avg_b * np.random.uniform(0.5, 1.5) eps_s0 = avg_s * np.random.uniform(0.5, 1.5)try: result = minimize( pin_log_likelihood, x0=[alpha0, delta0, mu0, eps_b0, eps_s0], args=(data,), method="L-BFGS-B", bounds=[(0.01, 0.99), (0.01, 0.99), (0.01, None), (0.01, None), (0.01, None)], options={"maxiter": 1000} )if result.fun < best_nll: best_nll = result.fun best_result = resultexceptException:continueif best_result isNone:return {"pin": np.nan} alpha, delta, mu, eps_b, eps_s = best_result.x pin = alpha * mu / (alpha * mu + eps_b + eps_s)return {"alpha": alpha,"delta": delta,"mu": mu,"eps_b": eps_b,"eps_s": eps_s,"pin": pin,"log_likelihood": -best_nll }``````{python}#| label: pin-cross-section#| eval: false# Estimate PIN for a cross-section of stocks# Using daily buy/sell counts from Lee-Ready classificationtrade_counts = dc.get_trade_counts( start_date="2023-01-01", end_date="2023-12-31")# Sample of stocks for estimation (PIN is computationally intensive)top_stocks = ( trade_counts.groupby("ticker") .agg(n_days=("date", "nunique")) .query("n_days >= 200") .index[:50])pin_estimates = []for ticker in top_stocks: stock_data = trade_counts[trade_counts["ticker"] == ticker] result = estimate_pin( stock_data["buys"].values, stock_data["sells"].values, n_starts=5 ) result["ticker"] = ticker pin_estimates.append(result)pin_df = pd.DataFrame(pin_estimates).dropna(subset=["pin"])print(f"PIN estimates for {len(pin_df)} stocks")print(f" Mean PIN: {pin_df['pin'].mean():.4f}")print(f" Median PIN: {pin_df['pin'].median():.4f}")``````{python}#| label: fig-pin-distribution#| eval: false#| fig-cap: "Cross-Sectional Distribution of Probability of Informed Trading (PIN)"( p9.ggplot(pin_df, p9.aes(x="pin"))+ p9.geom_histogram(bins=25, fill="#2E5090", alpha=0.7)+ p9.geom_vline( xintercept=pin_df["pin"].median(), linetype="dashed", color="#C0392B", size=0.8 )+ p9.labs( x="PIN", y="Count", title="Distribution of Informed Trading Probability" )+ p9.theme_minimal()+ p9.theme(figure_size=(10, 5)))```### PIN and Asset Pricing@easley2002information argue that PIN should be priced in the cross-section of expected returns: stocks with higher information asymmetry require a risk premium to compensate uninformed investors. We test this prediction using Fama-MacBeth regressions.```{python}#| label: pin-asset-pricing#| eval: false# Merge PIN with firm characteristics and forward returnspin_chars = pin_df[["ticker", "pin"]].merge( dc.get_firm_characteristics( start_date="2023-01-01", end_date="2023-12-31" ).groupby("ticker").last().reset_index()[ ["ticker", "log_size", "book_to_market", "momentum_12m"] ], on="ticker", how="inner")# Forward 12-month returns (2024)forward_returns = dc.get_annual_returns( start_date="2024-01-01", end_date="2024-12-31")pin_returns = pin_chars.merge( forward_returns[["ticker", "annual_ret"]], on="ticker", how="inner")# Cross-sectional regressioniflen(pin_returns) >20: cs_model = sm.OLS( pin_returns["annual_ret"], sm.add_constant( pin_returns[["pin", "log_size", "book_to_market", "momentum_12m"]] ) ).fit(cov_type="HC1")print("Cross-Sectional Return Regression with PIN:")for var in ["pin", "log_size", "book_to_market", "momentum_12m"]:print(f" {var}: {cs_model.params[var]:.4f} "f"(t = {cs_model.tvalues[var]:.3f})")```### Estimation Challenges in Limit Order Book ModelsStructural estimation of limit order book models faces several challenges specific to Vietnamese markets:**Tick size effects.** Vietnamese exchanges use discrete tick sizes that vary with price level. At low prices, the minimum tick size represents a large fraction of the price, artificially widening the spread and distorting the structural parameters. The standard Glosten-Milgrom and PIN models assume continuous prices; adapting them to discrete price grids requires the modifications proposed by @bollen2004modeling.**Price limits.** When a stock hits its price limit, the order book is effectively frozen at one side. Orders accumulate but cannot execute until the limit is lifted. This creates a censoring problem analogous to the price limit censoring in return distributions: the observed order flow is a truncated version of the latent flow.**Missing data.** Full order book snapshots at high frequency are not universally available for Vietnamese stocks. PIN estimation requires only daily buy/sell counts, making it feasible with lower-frequency data. But richer models (@foucault1999order dynamic limit order book, @rocsu2009dynamic continuous-time model) require tick-by-tick order book data that may be unavailable for smaller stocks.**Institutional features.** The coexistence of HOSE (continuous auction) and HNX (with periodic call auctions for less liquid stocks) creates heterogeneity in the appropriate structural model. A model estimated on HOSE continuous trading data does not directly apply to HNX call auction stocks.## When Structural Models Are Worth It### Decision FrameworkStructural estimation is not always the right tool. The additional complexity, data requirements, and model risk must be justified by the research question. @tbl-when-structural provides a decision framework.| Criterion | Structural Preferred | Reduced Form Preferred ||------------------|---------------------------|----------------------------|| Research question | Counterfactual or policy evaluation | Causal effect of observed variation || Data availability | Rich micro-data (transactions, holdings) | Standard panel (returns, fundamentals) || Institutional environment | Stable (model assumptions plausible) | Rapidly changing (model may be misspecified) || Number of parameters | Few primitives, many observables | Many parameters, few identifying assumptions || Publication venue | JF, RFS, Econometrica, JPE | JFE, RFS, MS, JFQA || Computational budget | Weeks to months of estimation | Hours to days |: When to Choose Structural vs. Reduced Form {#tbl-when-structural}### Data RequirementsStructural models are data-hungry. @tbl-data-requirements summarizes the minimum data needs for each model class covered in this chapter.| Model | Minimum Data | Ideal Data | Vietnamese Availability ||----------------|----------------|----------------|-------------------------|| KY demand system | Quarterly institutional holdings + prices | 13F-equivalent filings + fund flows | Partial (semi-annual disclosure) || Almgren-Chriss | Daily volume, spread, volatility | Intraday order-by-order data | Good (HOSE provides) || Rock/IPO | Offer price, first-day close, allocation | Investor-level bids and allocations | Limited (auction data available) || PIN | Daily buy/sell counts (Lee-Ready) | Tick-by-tick trade and quote | Moderate (requires trade classification) || Glosten-Milgrom | Best bid/ask, trade direction | Full order book snapshots | Moderate (top-of-book available) |: Data Requirements for Structural Estimation {#tbl-data-requirements}### Computational CostStructural estimation is orders of magnitude more expensive than reduced-form regression. The cost arises from three sources:**Likelihood evaluation.** Each evaluation of the structural likelihood or moment function requires solving the model for given parameters. For dynamic models (e.g., inventory models, dynamic discrete choice), this involves solving a dynamic programming problem at each iteration.**Optimization.** The GMM or MLE objective is typically non-convex, requiring multiple starting points and global optimization algorithms. The PIN model with 5 parameters requires $\sim 10$ random starts; a richer model with $\sim 20$ parameters might require hundreds.**Inference.** Standard errors for structural parameters often require bootstrapping (because the asymptotic distribution is non-standard or the delta method is unreliable), adding a multiplicative factor of 200--1000 to the computational budget.```{python}#| label: computational-cost-comparison# Illustration: computational cost comparisonimport time# Reduced form: OLS regressionn_obs =100000X_sim = np.random.randn(n_obs, 5)y_sim = X_sim @ np.random.randn(5) + np.random.randn(n_obs)start = time.time()for _ inrange(100): sm.OLS(y_sim, sm.add_constant(X_sim)).fit()ols_time = (time.time() - start) /100# Structural: PIN estimation (single stock)sim_buys = np.random.poisson(50, 250)sim_sells = np.random.poisson(45, 250)start = time.time()pin_result = estimate_pin(sim_buys, sim_sells, n_starts=5)pin_time = time.time() - startprint(f"Computational Cost Comparison:")print(f" OLS (100k obs): {ols_time*1000:.1f} ms per estimation")print(f" PIN (250 days, 5 starts): {pin_time:.1f} s per stock")print(f" Ratio: {pin_time / ols_time:.0f}x")```### Interpretation RisksThe primary risk of structural estimation is model misspecification. If the model is wrong, the estimated parameters have no economic meaning and the counterfactual predictions are unreliable. Several strategies mitigate this risk:**Specification tests.** Over-identifying restrictions (more moments than parameters) provide the Hansen $J$-test for model fit. A rejection suggests misspecification, though the test has low power in small samples.**Model comparison.** Estimate multiple nested or non-nested models and compare fit. If a simpler model fits the data equally well, prefer it (Occam's razor).**Sensitivity analysis.** Report how structural parameters change under plausible alternative assumptions (e.g., different functional forms for the impact function, different distributional assumptions for valuations).**Out-of-sample validation.** Estimate the model on one sample period and test its predictions on a holdout sample. Structural models that fit in-sample but fail out-of-sample are likely overfit.### Journal ExpectationsStructural papers in top finance journals are held to specific standards:**Identification clarity.** The paper must clearly state which parameters are identified, from which moments, and what variation in the data provides identification. The @rust1987optimal critique that without clear identification, structural estimation is curve-fitting, must be addressed head-on.**Counterfactual credibility.** Counterfactual exercises should be economically motivated and the range of counterfactual scenarios should not extrapolate far beyond the data.**Robustness to specification.** Reviewers will ask what happens under alternative distributional assumptions, alternative moment conditions, and alternative model features.**Transparency.** Code and data should be made available. Structural estimation is sufficiently complex that replicability is a first-order concern. <!-- ## Exercises1. **Demand Elasticity Heterogeneity.** Extend the KY demand system by allowing demand elasticities to vary by investor type (mutual funds, insurance companies, pension funds, foreign institutional investors). Estimate separate $\boldsymbol{\beta}_i$ vectors for each type. Which investor type has the most elastic demand? Which has the most inelastic? Relate your findings to the price impact predictions of @gabaix2021search.2. **Optimal Execution with Price Limits.** Modify the Almgren-Chriss framework to incorporate Vietnamese daily price limits. The constraint is that the execution price on any day cannot exceed the limit: $|S_t - S_{t-1}| \leq L$. Solve the modified optimization numerically and compare the optimal trajectory with the unconstrained solution. How do price limits affect total execution cost and the optimal time horizon?3. **Mechanism Design for SOE Equitization.** Using the estimated Rock model parameters, simulate IPO outcomes under a Vickrey (second-price) auction mechanism. Compare welfare (issuer revenue, investor surplus, total surplus) with the observed auction and book building outcomes. Under what conditions does the Vickrey mechanism dominate?4. **PIN and Corporate Events.** Estimate PIN in event windows around earnings announcements, M&A announcements, and regulatory changes for a sample of Vietnamese firms. Does PIN spike before public announcements (consistent with informed trading or leakage)? Compare the magnitude of the pre-announcement PIN increase across event types.5. **Kyle's Lambda and Market Depth.** Estimate Kyle's $\lambda$ (the price impact coefficient from the regression of price changes on signed order flow) for the cross-section of Vietnamese stocks. Test whether $\lambda$ is inversely related to measures of market depth (average volume, number of active traders, institutional ownership). Construct a tradeable portfolio long low-$\lambda$ (liquid) stocks and short high-$\lambda$ (illiquid) stocks. Does this liquidity factor earn a risk premium?6. **Structural Break in Market Microstructure.** Vietnam implemented tick size changes and trading hour extensions at various points. Using the PIN model and the Roll spread, estimate structural breaks in information asymmetry and transaction costs around these regulatory changes. Do the structural parameters change in the direction predicted by market microstructure theory? Use the @bai1998estimating sequential breakpoint methodology to formally test for and date the breaks. -->## SummaryThis chapter introduced structural estimation as a distinct methodology for financial economics, one that trades the simplicity and transparency of reduced-form analysis for the ability to estimate economic primitives and conduct counterfactual policy evaluation. We implemented four classes of structural models: demand systems for financial assets, optimal execution and transaction cost models, IPO auction models, and limit order book models of informed trading.The demand system approach of @koijen2019demand reveals that institutional investors in Vietnam exhibit heterogeneous demand elasticities across characteristics, with foreign investors showing different sensitivity patterns than domestic institutions. Market impact estimation confirms the universality of the square-root law, while the Almgren-Chriss framework provides the optimal execution benchmark for large institutional orders. The Rock IPO model quantifies the information asymmetry premium embedded in Vietnamese IPO underpricing, and the PIN model estimates the probability of informed trading across the cross-section.Each model involves a tradeoff between the economic questions it can answer and the assumptions it requires. The decision to use structural estimation should be driven by the research question (specifically, whether counterfactual analysis is essential) and tempered by honest assessment of whether the model's assumptions are sufficiently credible in the Vietnamese institutional environment. When they are, structural models provide insights that no amount of reduced-form regression can deliver.