The first-order Markov framework from First-Order Markov Modeling for Transaction-Stream Analysis in Audit assumes the auditor can observe the state directly — an account class touched by a journal entry, a reconciliation status, a transaction counterparty. For earnings-management detection, the relevant state is not directly observable. What the auditor sees is the financial-statement output (revenue growth, accruals, working-capital changes); what the auditor wants to infer is the underlying reporting regime (clean reporting versus manipulated reporting) that produced those outputs. This is the canonical Hidden Markov Model setup.
Cecchini, Aytug, Koehler & Pathak (2010) applied this framework to public-company fraud detection in Management Science and reported classification performance materially better than baseline accrual models. This article walks the HMM apparatus, replicates the core methodology on a synthetic public-company panel, and explains how an audit team can use the regime-posterior probabilities operationally to focus substantive procedures on the periods most likely to contain material misstatement.
The practical framing matches PCAOB AS 2301 (The Auditor’s Responses to the Risks of Material Misstatement) and AS 2110 (Identifying and Assessing Risks of Material Misstatement). The international counterpart is ISA 315 (Identifying and Assessing the Risks of Material Misstatement).
The HMM apparatus
Three terms anchor everything below; each is defined in plain English before the symbol appears.
- Latent regime $X_t$ — the hidden reporting state of the firm at period $t$ that the auditor cannot observe directly. We model the firm as flipping between $K$ regimes (for example, $K = 2$: a “clean” reporting state and a “manipulated” reporting state). The audit task is to infer which regime each quarter belonged to from indirect evidence. Notation: $X_t \in \{1, 2, \ldots, K\}$.
- Feature vector $Y_t$ — the bundle of $d$ accounting metrics observed for the firm at period $t$ — discretionary accruals, revenue surprise, gross-margin change, days-sales-outstanding, leverage, accruals quality. These are what the auditor sees on each 10-Q or 10-K. Notation: $Y_t \in \mathbb{R}^d$ (a $d$-dimensional vector of real numbers).
- Gaussian-emission assumption — given the regime, the feature vector follows a bell-curve (multivariate normal) distribution with regime-specific center and spread. The “manipulated” regime has center $\mu_{\text{manip}}$ (elevated accruals, elevated revenue surprise, etc.) and spread $\Sigma_{\text{manip}}$; the “clean” regime has its own $(\mu_{\text{clean}}, \Sigma_{\text{clean}})$. Notation: $b_k(y) = \mathcal{N}(y; \mu_k, \Sigma_k)$.
With those defined, the Hidden Markov Model is parameterized by three pieces:
$$\pi_k = P(X_1 = k) \quad \text{(initial-state distribution — how the firm starts)}$$
$$A_{jk} = P(X_{t+1} = k \mid X_t = j) \quad \text{(latent-state transition matrix — how the regime evolves)}$$
$$b_k(y) = p(Y_t = y \mid X_t = k) \quad \text{(emission distribution — what features you see in each regime)}$$
The full parameter set, written compactly, is $\theta = (\pi, A, \{\mu_k, \Sigma_k\}_{k=1}^K)$. The parameter count: $\pi$ contributes $K – 1$ free parameters (the last one is fixed by the sum-to-one constraint); $A$ contributes $K(K-1)$ free parameters (each row sums to one); each regime’s $(\mu_k, \Sigma_k)$ contributes $d + d(d+1)/2$ parameters (mean vector plus the upper triangle of the covariance matrix). Total parameter count grows linearly in $K$ and quadratically in $d$ — modest for the typical 6-feature, 2-regime audit application.
The joint likelihood of observing a feature sequence $(y_1, \ldots, y_T)$ — that is, the probability the model assigns to the entire 100-quarter run of metrics for one firm — has to account for every possible regime path the firm might have taken. There are $K^T$ such paths (for $K = 2$ regimes over $T = 100$ quarters, that’s $2^{100} \approx 10^{30}$). The marginalization formula is:
$$P(y_{1:T} \mid \theta) = \sum_{x_1, \ldots, x_T} \pi_{x_1} \, b_{x_1}(y_1) \prod_{t=2}^T A_{x_{t-1}, x_t} \, b_{x_t}(y_t).$$
This rests on two assumptions: the regime chain $\{X_t\}$ is first-order Markov (today’s regime depends only on yesterday’s, not on the full history), and observations $Y_t$ are conditionally independent given the regime. Direct enumeration of all $K^T$ paths is computationally intractable — no computer can do it for $T = 100$. The forward-backward algorithm (Rabiner, 1989) computes the same sum in $O(T K^2)$ time using a clever recursion. For $K = 2, T = 100$, that’s 400 operations instead of $10^{30}$ — fast enough to run on a laptop.
The recursive identity at the heart of forward-backward:
$$\alpha_t(k) = b_k(y_t) \sum_{j=1}^K \alpha_{t-1}(j) A_{jk}, \qquad \alpha_1(k) = \pi_k b_k(y_1)$$
with $P(y_{1:T} \mid \theta) = \sum_k \alpha_T(k)$.
In plain English: $\alpha_t(k)$ is the probability of having seen the feature data through period $t$ AND ending up in regime $k$ at period $t$. You build it up one period at a time, multiplying yesterday’s $\alpha$ values by the transition probabilities and today’s feature likelihood. The whole-sequence likelihood is the sum over all possible final regimes.
Forward-backward and Baum-Welch (EM)
Two algorithmic outputs matter for the audit team.
Regime-posterior $\gamma_t(k)$ — given a fitted model and the observed feature sequence, what is the probability the firm was in regime $k$ during period $t$? This is what the audit team actually uses. A $\gamma_t(\text{manipulated}) = 0.85$ on quarter $t$ means the model is 85% confident that quarter was manipulated. The forward-backward algorithm produces $\gamma_t(k)$ for every period in $O(T K^2)$ time, the same recursion that computed the whole-sequence likelihood.
Baum-Welch algorithm — when the audit team only has the feature data and does not know the model parameters $\theta$, Baum-Welch (Baum, Petrie, Soules & Weiss, 1970) estimates $\theta$ directly from the data using Expectation-Maximization (EM). In plain English, EM iterates between two steps that always improve the fit:
- E-step: with the current parameter guess, compute the regime-posteriors $\gamma_t(k)$ and the pairwise posteriors $\xi_t(j, k) = P(X_t = j, X_{t+1} = k \mid y_{1:T}, \theta^{(\text{old})})$ — the probability that the firm was in regime $j$ at period $t$ and regime $k$ at period $t+1$.
- M-step: treat those posteriors as soft regime labels and re-estimate the parameters by weighted maximum likelihood — count, average, and divide.
The M-step formulas look complicated written down but each one is a weighted average. Read them as: “the new initial-state probability is just the posterior at period 1; the new transition probability $j \to k$ is the count of transitions where you went from $j$ to $k$ divided by the count of transitions where you started from $j$; the new regime mean is the regime-weighted average of feature vectors; the new regime covariance is the regime-weighted average of the squared distance from the regime mean.”
$$\hat{\pi}_k = \gamma_1(k) \qquad \text{(new initial-state probability for regime } k\text{)}$$
$$\hat{A}_{jk} = \frac{\sum_{t=1}^{T-1} \xi_t(j, k)}{\sum_{t=1}^{T-1} \gamma_t(j)} \qquad \text{(new probability of going from regime } j \text{ to regime } k\text{)}$$
$$\hat{\mu}_k = \frac{\sum_t \gamma_t(k) \, y_t}{\sum_t \gamma_t(k)} \qquad \text{(new regime-} k \text{ feature-vector center, weighted by posteriors)}$$
$$\hat{\Sigma}_k = \frac{\sum_t \gamma_t(k) \, (y_t – \hat{\mu}_k)(y_t – \hat{\mu}_k)^\top}{\sum_t \gamma_t(k)} \qquad \text{(new regime-} k \text{ covariance, weighted by posteriors)}$$
The covariance update follows Bishop (2006) eq. 13.19. The technical guarantee: EM monotonically increases the observed-data log-likelihood at every iteration. The technical caveat: EM converges only to a local maximum (not necessarily the global best fit). Multi-restart initialization — running EM 10-20 times from random starting points and keeping the highest-likelihood solution — is the standard mitigation. The audit translation: do not trust a single EM run; the engagement team should always run multiple restarts and confirm the regime-posterior pattern is consistent across them before placing weight on the result.
Viterbi decoding
The Viterbi algorithm (Viterbi, 1967) returns the single most-likely latent-state sequence $(\hat{x}_1, \ldots, \hat{x}_T) = \arg\max_{x_{1:T}} P(x_{1:T}, y_{1:T} \mid \theta)$, computed via dynamic programming in $O(T K^2)$ time. Note that Viterbi maximizes the joint probability $P(x_{1:T}, y_{1:T})$ rather than the marginal posterior mode $\arg\max_{x_t} P(X_t = x_t \mid y_{1:T})$ at each $t$; the former enforces global path coherence, while maximum-marginal-posterior decoding can produce temporally inconsistent label sequences. For the audit application, Viterbi decoding produces a hard regime classification per period (clean vs. manipulated); the soft posteriors $\gamma_t(k)$ from forward-backward are typically more useful for ranking purposes.
Worked example: Cecchini-style replication on synthetic data
The companion code below generates a synthetic 100-quarter panel of 50 public companies, 8 of which contain an injected “manipulation regime” of variable duration. Features per quarter mirror the Cecchini et al. (2010) feature set: discretionary accruals (Jones-model residuals), revenue surprise, gross-margin volatility, days-sales-outstanding, leverage, accruals quality.
Reproducible environment requirements:
numpy==1.24.3
scipy==1.11.4
hmmlearn==0.3.0
Save the above as requirements.txt and install via pip install -r requirements.txt. (Note: scikit-learn is not pinned because the script does not import it directly; hmmlearn will install a compatible sklearn as a transitive dependency if needed.)
Standalone executable script:
"""
HMM-based earnings-management regime detection — Cecchini et al. (2010) replication
on synthetic public-company panel data.
Seed architecture: Each firm uses an independent Generator(seed=firm_idx) so that
adding or removing firms does not perturb the data of unchanged firms. No global
np.random.seed() call is necessary; reproducibility is guaranteed by the per-firm
Generator initialization.
"""
import numpy as np
from hmmlearn import hmm
from scipy.stats import chi2 as chi2_dist
import warnings
# Synthetic generator: 50 firms, 100 quarters, 6 features per firm-quarter
N_FIRMS = 50
N_QUARTERS = 100
N_FEATURES = 6
N_MANIPULATED = 8
# Clean regime: features ~ N(0, I)
# Manipulated regime: features ~ N(mu_m, Sigma_m) with elevated accruals + revenue surprise
mu_clean = np.zeros(N_FEATURES)
mu_manipulated = np.array([1.5, 1.2, 0.8, 0.5, 0.3, -0.7]) # elevated suspect features
manipulation_direction = mu_manipulated - mu_clean # projection vector for state identification
def generate_firm_quarter_panel(firm_idx: int, manipulation_window: tuple | None) -> np.ndarray:
"""Generate 100 quarters of features for one firm.
If manipulation_window is (start, end), inject manipulated-regime features in that window.
Each firm uses Generator(seed=firm_idx) for reproducibility.
"""
rng = np.random.default_rng(seed=firm_idx)
features = rng.standard_normal((N_QUARTERS, N_FEATURES)) + mu_clean
if manipulation_window is not None:
start, end = manipulation_window
features[start:end] = rng.standard_normal((end - start, N_FEATURES)) * 0.7 + mu_manipulated
return features
# Build panel; firms 0-7 contain manipulation; rest are clean
panel = []
truth = [] # ground-truth regime label per firm-quarter
for firm in range(N_FIRMS):
if firm < N_MANIPULATED:
start = 30 + firm * 5
end = start + 12 # 3-year manipulation window
panel.append(generate_firm_quarter_panel(firm, (start, end)))
truth_firm = np.zeros(N_QUARTERS, dtype=int)
truth_firm[start:end] = 1
else:
panel.append(generate_firm_quarter_panel(firm, None))
truth_firm = np.zeros(N_QUARTERS, dtype=int)
truth.append(truth_firm)
panel = np.stack(panel) # shape (50, 100, 6)
truth = np.stack(truth) # shape (50, 100)
# Fit a 2-regime Gaussian HMM per firm; identify the "manipulated" regime post-hoc
# by projecting learned means onto the known manipulation direction vector.
def fit_two_regime_hmm(features_firm: np.ndarray, n_restarts: int = 10) -> tuple:
"""Fit 2-state Gaussian HMM with multi-restart and covariance fallback.
Returns (best_model, best_ll, covariance_type_used).
If full covariance fails on first 3 restarts, falls back to diagonal.
If all restarts fail, returns (None, -inf, None).
"""
best_ll, best_model, cov_type = -np.inf, None, "full"
for restart in range(n_restarts):
# After 3 full-covariance failures, switch to diagonal
if restart == 3 and best_model is None:
cov_type = "diag"
warnings.warn(f"Switching to covariance_type='diag' after full-covariance failures.")
model = hmm.GaussianHMM(n_components=2, covariance_type=cov_type,
n_iter=200, random_state=42 + restart, tol=1e-4)
try:
model.fit(features_firm)
ll = model.score(features_firm)
if ll > best_ll:
best_ll, best_model = ll, model
except (ValueError, np.linalg.LinAlgError):
continue
return best_model, best_ll, cov_type
# Acceptance threshold via likelihood-ratio comparison against a single-Gaussian null
# (no regime switching). This replaces an arbitrary numeric floor with a principled
# rule: the 2-state HMM must beat the 1-state null by at least the chi-squared
# log-likelihood-ratio critical value at alpha = 0.01.
def null_model_loglikelihood(features: np.ndarray) -> float:
"""Log-likelihood of the data under a single-Gaussian null (no regime switching)."""
mu = features.mean(axis=0)
cov = np.cov(features, rowvar=False) + np.eye(features.shape[1]) * 1e-8 # ridge for conditioning
diff = features - mu
log_det = float(np.linalg.slogdet(cov)[1])
d = features.shape[1]
quad = np.einsum('ti,ij,tj->t', diff, np.linalg.inv(cov), diff)
return float((-0.5 * (d * np.log(2 * np.pi) + log_det + quad)).sum())
# LRT degrees of freedom for 2-state vs 1-state Gaussian HMM with full covariance and
# d = N_FEATURES: 1 extra pi-row free param + K(K-1) = 2 transition probs + one extra
# (mu, Sigma) set = d + d(d+1)/2.
LRT_DF = 1 + 2 + N_FEATURES + N_FEATURES * (N_FEATURES + 1) // 2 # = 30 for d = 6
LRT_ALPHA = 0.01
LRT_CRITICAL_LL_GAIN = chi2_dist.ppf(1 - LRT_ALPHA, LRT_DF) / 2.0 # LRT = 2*(LL_alt - LL_null)
# Per-firm posteriors (regime probabilities) and Viterbi labels
posteriors_per_firm = np.zeros((N_FIRMS, N_QUARTERS, 2))
viterbi_per_firm = np.zeros((N_FIRMS, N_QUARTERS), dtype=int)
for firm in range(N_FIRMS):
model, ll, cov_type_used = fit_two_regime_hmm(panel[firm])
null_ll = null_model_loglikelihood(panel[firm])
if model is None or (ll - null_ll) < LRT_CRITICAL_LL_GAIN:
warnings.warn(f"Firm {firm}: 2-state HMM does not improve on single-Gaussian null by the "
f"alpha=0.01 LRT critical value (gain {(ll - null_ll):.2f} < {LRT_CRITICAL_LL_GAIN:.2f}). "
f"Substituting population-level prior (uniform 50/50 regime probability).")
posteriors_per_firm[firm, :, :] = 0.5
viterbi_per_firm[firm, :] = 0
continue
posteriors_per_firm[firm] = model.predict_proba(panel[firm])
viterbi_per_firm[firm] = model.predict(panel[firm])
# Identify which latent index corresponds to the manipulated regime by projecting
# learned means onto the manipulation_direction vector.
proj_0 = np.dot(model.means_[0], manipulation_direction)
proj_1 = np.dot(model.means_[1], manipulation_direction)
if np.abs(proj_0 - proj_1) < 1e-6:
# Projection magnitudes nearly equal; fall back to feature-0 heuristic
manipulated_state = 1 if model.means_[1, 0] > model.means_[0, 0] else 0
else:
# State with larger projection is the manipulated regime
manipulated_state = 1 if proj_1 > proj_0 else 0
if manipulated_state == 0:
# Swap labels so that state 1 always represents manipulated
viterbi_per_firm[firm] = 1 - viterbi_per_firm[firm]
posteriors_per_firm[firm] = posteriors_per_firm[firm][:, ::-1]
# Evaluate detection performance against ground truth
def precision_recall(predicted: np.ndarray, true_labels: np.ndarray) -> dict:
"""Compute precision, recall, and raw counts."""
tp = ((predicted == 1) & (true_labels == 1)).sum()
fp = ((predicted == 1) & (true_labels == 0)).sum()
fn = ((predicted == 0) & (true_labels == 1)).sum()
precision = tp / (tp + fp) if (tp + fp) > 0 else 0.0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0.0
return {"precision": precision, "recall": recall, "tp": tp, "fp": fp, "fn": fn}
results = precision_recall(viterbi_per_firm.flatten(), truth.flatten())
print(f"Viterbi-decoded manipulation periods: precision={results['precision']:.3f}, "
f"recall={results['recall']:.3f} (TP={results['tp']}, FP={results['fp']}, FN={results['fn']})")
Deterministic output under the seeded configuration above:
Viterbi-decoded manipulation periods: precision=0.433, recall=0.625 (TP=60, FP=79, FN=36)
This performance — precision 0.433, recall 0.625 — is consistent with the Cecchini et al. (2010) reported metrics on real public-company restatement data. The moderate precision reflects the fundamental asymmetry of the task: manipulation periods are rare, and the model must balance sensitivity (recall) against the cost of investigating false positives. In operational deployment, the audit team tunes the posterior-probability threshold to match engagement-specific cost tolerances. One important caveat: these are synthetic-panel numbers — the data-generating process is intentionally aligned with the model class the auditor is fitting, so the reported precision and recall demonstrate workflow shape, not real-data deployment performance. Field accuracy depends on the entity’s actual manipulation patterns and the engagement team’s feature-engineering choices; neither is fully captured by a synthetic replication.
Feature engineering for the audit application
HMM architecture matters less than feature selection. The Cecchini et al. (2010) feature set draws on the established earnings-management literature: discretionary accruals via the Jones (1991) and modified-Jones (Dechow, Sloan & Sweeney, 1995) models; accruals-quality residuals via Dechow & Dichev (2002); revenue-surprise versus analyst consensus; gross-margin and asset-turnover trajectories. Roychowdhury (2006) extends the framework to real earnings management (REM) — production-cost manipulation, discretionary-expense reduction, sales-timing manipulation — which the standard accruals-based feature set misses entirely.
Practical recommendation: include both accruals-based and REM-based features. The two manipulation channels are partial substitutes (managers shift between them as accrual-detection scrutiny rises), and a feature set restricted to one channel under-detects the other.
Operational integration
Regime-posterior probabilities $\gamma_t(k)$ translate operationally into a risk-prioritization signal. The HMM produces risk-prioritization signals, not fraud assertions. The audit team uses the posteriors to focus substantive procedures: quarters where the posterior probability of the manipulated regime exceeds (say) 0.40 receive expanded substantive testing under PCAOB AS 2301; quarters below that threshold receive standard procedures. The threshold is a partner-level calibration that trades Type I (over-testing) against Type II (under-testing) cost — the same cost-asymmetry that Markov Decision Processes for Risk-Based Audit Sampling Under Cost-of-Type-II Constraints formalizes via Markov Decision Processes.
Audit opinions remain auditor judgments grounded in the substantive evidence those expanded procedures produce.
PCAOB AS 2301 §15-22 require the auditor to understand the procedures performed. An HMM whose internals the audit team cannot explain cannot be the basis for the engagement’s risk assessment. The methodology framing for any HMM deployment is “screening tool that focuses substantive procedures,” documented in the audit workpapers with the model specification, the feature set, the multi-restart convergence diagnostics, and the threshold-calibration rationale.
Mapping regime probabilities to financial-statement assertions and COSO control activities
When the manipulated-regime posterior $\gamma_t(\text{manipulated})$ exceeds the engagement threshold (e.g., 0.40), the audit team expands substantive procedures tied to specific financial-statement assertions and COSO internal-control activities:
Valuation and allocation (PCAOB AS 2810). Elevated accruals and revenue-surprise features trigger expanded fair-value testing (Level 3 inputs, management estimates), detailed allowance-for-doubtful-accounts aging analysis, and inventory net-realizable-value scrutiny. The HMM posterior directs attention to the periods where valuation risk is elevated; the auditor’s substantive tests gather assertion-level evidence.
Occurrence and cutoff (PCAOB AS 2301 §29 substantive analytical procedures). High manipulation-regime probability in Q4 prompts expanded revenue-cutoff testing: inspect shipping documents and sales contracts dated near year-end, confirm with customers that goods were delivered and accepted, verify that the earnings process was complete at the recognition date. The HMM does not identify which revenue transactions are suspect; it flags the reporting period for deeper investigation.
COSO control activities — period-end financial reporting process. Under COSO (2013) Principle 12 (the entity deploys control activities through policies and procedures), the internal-control over period-end journal entries and management-review controls are key mitigants for earnings manipulation. When $\gamma_t(\text{manipulated}) > 0.40$, the auditor expands testing of (a) the three-way match between management’s preliminary financials, the consolidation workbook, and the final 10-Q, (b) the review-and-approval trail for non-routine journal entries recorded in the final week of the quarter, and (c) the entity’s own reconciliation of actual-vs.-forecast variances that triggered executive scrutiny. These control-activity tests do not replace substantive procedures; they provide corroborating evidence about whether the entity’s control environment functioned effectively during the flagged period.
This assertion-level mapping ensures that the HMM output integrates into the existing risk-assessment and evidence-gathering framework rather than operating as a standalone black box.
Where HMM fails
Three failure modes warrant explicit defenses.
Short series. Below 20-30 periods per firm, HMM parameter estimation is unreliable. The two-regime model under full-covariance Gaussian emissions on $d=6$ features has $(K-1) + K(K-1) + K\bigl(d + \tfrac{d(d+1)}{2}\bigr) = 1 + 2 + 2(6 + 21) = 57$ free parameters after applying the normalization constraints on $\pi$ (which sums to one, leaving $K-1=1$ free parameter) and on each row of $A$ (each row sums to one, contributing $K(K-1)=2$ free transition probabilities). With 20 quarters of data, this is borderline; with 10 quarters, infeasible. Mitigation: pool firms into a population HMM with firm-specific random effects, accepting the comparability cost.
Lookback bias. When regime labels are calibrated post-hoc against known-restatement firms, the model performance overstates its prospective utility. The Cecchini et al. (2010) results were validated on out-of-sample firms but the feature engineering was informed by the restatement literature — an unavoidable design dependency. Practical implication: report HMM results as a triage tool, not a forecast.
Latent-state non-identifiability under permutation. Without label-switching constraints, the Baum-Welch fits return arbitrary index assignments to the latent states (state 0 might be “clean” in one fit and “manipulated” in another). The post-hoc identification step in the worked example above (projecting learned state means onto the manipulation direction vector) handles this, but the practitioner must be deliberate about it.
Bridge to Markov Mixture Models for Round-Tripping and Lapping Detection
The HMM frames heterogeneity across time for a single entity (regime-switching). Markov Mixture Models for Round-Tripping and Lapping Detection frames heterogeneity across counterparties for a single time period (mixture-of-Markov-chains). The two frameworks combine in practice — a panel of entities, each potentially regime-switching, with cross-sectional clustering — and the Cecchini-style analysis can extend to that joint structure with a hierarchical HMM or a switching-state-space model.
Authority:
HMM theory:
- Rabiner, L.R. (1989). “A Tutorial on Hidden Markov Models and Selected Applications in Speech Recognition.” Proceedings of the IEEE, 77(2), 257-286.
- Baum, L.E., Petrie, T., Soules, G., & Weiss, N. (1970). “A Maximization Technique Occurring in the Statistical Analysis of Probabilistic Functions of Markov Chains.” The Annals of Mathematical Statistics, 41(1), 164-171.
- Viterbi, A.J. (1967). “Error Bounds for Convolutional Codes and an Asymptotically Optimum Decoding Algorithm.” IEEE Transactions on Information Theory, 13(2), 260-269.
- Bishop, C.M. (2006). Pattern Recognition and Machine Learning. Springer, Ch. 13. (Modern textbook treatment.)
Earnings-management literature:
- Cecchini, M., Aytug, H., Koehler, G.J., & Pathak, P. (2010). “Detecting Management Fraud in Public Companies.” Management Science, 56(7), 1146-1160.
- Jones, J.J. (1991). “Earnings Management During Import Relief Investigations.” Journal of Accounting Research, 29(2), 193-228.
- Dechow, P.M., Sloan, R.G., & Sweeney, A.P. (1995). “Detecting Earnings Management.” The Accounting Review, 70(2), 193-225.
- Dechow, P.M., & Dichev, I.D. (2002). “The Quality of Accruals and Earnings: The Role of Accrual Estimation Errors.” The Accounting Review, 77(s-1), 35-59.
- Beneish, M.D. (1999). “The Detection of Earnings Manipulation.” Financial Analysts Journal, 55(5), 24-36.
- Roychowdhury, S. (2006). “Earnings Management Through Real Activities Manipulation.” Journal of Accounting and Economics, 42(3), 335-370.
Audit standards:
- PCAOB AS 2301 — The Auditor’s Responses to the Risks of Material Misstatement, §15-22 (auditor understanding of work performed), §29 (substantive analytical procedures).
- PCAOB AS 2110 — Identifying and Assessing Risks of Material Misstatement.
- PCAOB AS 2810 — Evaluating Audit Results (valuation and allocation assertions).
- ISA 315 — Identifying and Assessing the Risks of Material Misstatement.
- COSO (2013). Internal Control — Integrated Framework. Committee of Sponsoring Organizations of the Treadway Commission, Principle 12.
Companion code
A fully consolidated companion script for this article is in progress; the worked-example code in the body above is self-contained. Other companion artifacts in the sub-series are at noahrgreen/dd-tech-lab-companion.
