19  Reproducible Documents with Quarto

Research in empirical finance requires more than just running analyses and generating results. You need to communicate your findings clearly, ensure others can verify your work, and often revisit your analysis months or years later. This is where reproducible documents become essential.

A reproducible document combines three critical elements in a single file:

  1. Narrative text that explains your research question, methodology, and interpretation
  2. Code that performs the analysis
  3. Results such as tables, figures, and statistical output

When you update your data or refine your methodology, the entire document updates automatically. No more copying and pasting regression tables from Stata to Word, or manually updating figure numbers when you add a new chart. No more wondering which version of the code produced which table in your paper.

Quarto is a modern, open-source scientific publishing system that makes creating reproducible documents straightforward. It works seamlessly with Python (and R, Julia, and Observable), produces beautiful output in multiple formats (HTML, PDF, Word), and is designed specifically for technical and scientific communication.

In this chapter, you’ll learn how to create professional research documents with Quarto. We’ll start with the basics of document structure, then explore how to integrate code, figures, and tables into your narrative. By the end, you’ll be able to produce publication-quality papers and reports where every number, table, and figure is generated directly from your analysis code.

NoteWhy Quarto over Jupyter Notebooks?

You may already be familiar with Jupyter notebooks, which are excellent for exploratory analysis and interactive computing. Quarto builds on this foundation but offers several advantages for research communication:

  • Better narrative flow: Quarto documents are written in plain text with markdown, making them easier to version control and collaborate on
  • Multiple output formats: One source file can generate HTML, PDF, Word documents, presentations, and more
  • Publication quality: Built-in support for citations, cross-references, equations, and academic formatting
  • Computational efficiency: Smart caching means long-running computations don’t need to re-run every time you fix a typo
  • Separation of concerns: You can focus on writing when writing, and on code when coding

That said, Jupyter notebooks remain invaluable for interactive exploration. Many researchers use notebooks for analysis development and Quarto for final communication.

19.1 A Note on LaTeX

Before diving into Quarto, it’s worth understanding where it fits in the landscape of academic writing tools. For decades, researchers in quantitative fields have used LaTeX (pronounced “LAY-tek” or “LAH-tek”) to produce publication-quality documents. LaTeX is a typesetting system that excels at handling mathematical notation, citations, and complex document structures—all things that word processors traditionally struggle with.

If you’re interested in learning LaTeX directly, Overleaf offers an excellent Learn LaTeX in 30 minutes tutorial. Overleaf itself is a web-based LaTeX editor that many researchers use for collaborative writing. If you prefer working locally, see this guide to setting up VS Code for LaTeX.

The good news is that you don’t need to learn LaTeX to use Quarto effectively. Quarto uses LaTeX as an intermediary when producing PDF output, but it hides most of the complexity behind a simpler Markdown-based syntax. You write in Markdown, and Quarto handles the conversion to LaTeX and then to PDF. This gives you the beautiful typography and equation rendering of LaTeX without the steep learning curve.

That said, understanding that LaTeX powers the PDF output helps explain some of Quarto’s behavior. When you see references to “LaTeX packages” or when you need fine-grained control over PDF formatting, you’re interacting with this underlying system. For most use cases, Quarto’s defaults produce journal-quality documents without any LaTeX knowledge required.

19.2 Beyond Manuscripts

While this chapter focuses primarily on research papers and reports, Quarto’s capabilities extend well beyond traditional manuscripts. The same Markdown-based workflow can produce:

  • Presentations: Create slides using Reveal.js (interactive HTML), PowerPoint, or Beamer (LaTeX-based PDF slides)
  • Websites and blogs: Build complete static sites with navigation, search, and multiple pages
  • Interactive dashboards: Combine data visualizations with user inputs for dynamic exploration
  • Books: Write multi-chapter documents with cross-references, indexes, and consistent styling (these course notes are built with Quarto)

This flexibility means that once you learn Quarto for research writing, you can apply the same skills to course materials, project documentation, or personal websites. The underlying syntax remains consistent across output formats, so transitioning between them requires minimal additional learning.

19.3 Pros and Cons of Quarto

Like any tool, Quarto involves trade-offs. Understanding these helps you decide when it’s the right choice.

Strengths:

  • Simpler than LaTeX: Markdown is far easier to read and write than LaTeX source code. Most formatting is intuitive (bold is **bold**, headers use #)
  • Reproducible by design: Code is embedded in the document, so results always match the analysis
  • Flexible output: One source file can generate PDF for journal submission, HTML for your website, and Word for collaborators who prefer it
  • Modern tooling: Integrates with VS Code, RStudio, and other development environments with syntax highlighting and live preview

Drawbacks:

  • PDF compilation can be slower: Because Quarto converts to LaTeX and then to PDF, rendering takes longer than pure LaTeX for complex documents
  • Deep LaTeX customization requires effort: While Quarto handles common formatting well, unusual requirements (custom journal templates, specialized packages) sometimes require understanding LaTeX
  • Dependency on execution environment: Your document’s reproducibility depends on maintaining the same Python environment over time

For most empirical research workflows, Quarto’s advantages substantially outweigh its limitations. The key is knowing when you might need to drop down to LaTeX for specialized formatting and when Quarto’s defaults are sufficient.

19.4 Prerequisites

This chapter assumes you have:

  • Python and Quarto installed (see the installation chapter)
  • Basic familiarity with Python and pandas
  • A text editor or IDE (VS Code works particularly well with Quarto)

You should be comfortable reading and writing basic Python code, but you don’t need advanced programming skills. We’ll explain all the Quarto-specific syntax as we go.

19.4.1 Installing a LaTeX distribution

To render PDF output, Quarto needs a LaTeX distribution. The simplest option is TinyTeX, a minimal distribution designed specifically for Quarto:

quarto install tinytex

TinyTeX automatically installs any LaTeX packages Quarto needs during rendering. This is the recommended approach for most users.

Alternatively, if you work with LaTeX outside of Quarto or need a full installation, you can install a complete distribution:

These full distributions are larger but include every package you might need.

19.4.2 VS Code extension

If you use VS Code, install the Quarto extension for the best experience. It provides:

  • Syntax highlighting for .qmd files
  • Integrated preview with live updates
  • Code completion for YAML options
  • Run buttons for code chunks

The extension transforms VS Code into a powerful Quarto development environment.

19.5 Quarto Project Structure

19.5.1 Your first Quarto document

A Quarto document is a plain text file with a .qmd extension. Let’s create a simple example to understand the basic structure.

Create a file called analysis.qmd with the following content:

---
title: "Stock Returns Analysis"
author: "Your Name"
date: today
format: html
---

## Introduction

This document analyzes daily stock returns for major tech companies.

## Data Loading

```{python}
import pandas as pd
import numpy as np

# Generate sample data
np.random.seed(42)
dates = pd.date_range('2023-01-01', '2023-12-31', freq='D')
returns = pd.DataFrame({
    'Date': dates,
    'AAPL': np.random.normal(0.001, 0.02, len(dates)),
    'GOOGL': np.random.normal(0.0008, 0.022, len(dates)),
    'MSFT': np.random.normal(0.0012, 0.018, len(dates))
})

print(f"Loaded {len(returns)} observations")

19.6 Summary Statistics

The average daily return for AAPL was {{{python}} f”{returns[‘AAPL’].mean():.4f}“}.


This simple document has three key components:

1. **YAML header** (between `---` markers): Metadata about the document
2. **Markdown sections**: Your narrative text with headings, paragraphs, lists, etc.
3. **Code chunks**: Python code that executes when you render the document

::: {.callout-tip}
## YAML header

The YAML (Yet Another Markup Language) header at the top of your document controls how Quarto processes and formats your output. Common options include:

- `title`, `author`, `date`: Document metadata
- `format`: Output format (html, pdf, docx, etc.)
- `execute`: Control code execution behavior
- `bibliography`: Citation file location
- `number-sections`: Automatically number sections

We'll explore these options in detail throughout this chapter.
:::

### Rendering your document

To generate output from your Quarto document, use the `quarto render` command:

```bash
quarto render analysis.qmd

This creates an HTML file (by default) in the same directory. Open analysis.html in your browser to see the result.

For a live preview that updates as you edit:

quarto preview analysis.qmd

This opens your browser and automatically refreshes whenever you save changes to the .qmd file.

19.6.1 Running Quarto with uv

If you use uv to manage your Python environment, you’ll want to ensure Quarto runs within that environment. This guarantees that Quarto uses the same packages and versions as your project:

uv run quarto render manuscript.qmd --to pdf
uv run quarto preview manuscript.qmd

This approach ensures your document renders with the exact dependencies specified in your project, making the output truly reproducible.

TipIDE integration

Most modern IDEs provide Quarto support:

  • VS Code: Install the Quarto extension for syntax highlighting, preview, and rendering
  • RStudio: Built-in Quarto support with visual editor
  • PyCharm: Use the Quarto plugin

These tools make it easier to write and preview your documents without switching to the command line.

19.6.2 Document structure and organization

As your analysis grows, you’ll want to organize it into sections and subsections. Quarto uses standard markdown heading syntax:

# Main Section (Level 1)
## Subsection (Level 2)
### Sub-subsection (Level 3)
#### Paragraph Heading (Level 4)

For academic papers, a typical structure might be:

---
title: "The Impact of Earnings Announcements on Stock Volatility"
author: "Your Name"
format: pdf
---

## Abstract

Brief summary of your research...

## Introduction

Research question and motivation...

## Literature Review

Previous work on this topic...

## Data and Methodology

### Data sources

Description of your data...

### Empirical strategy

Your econometric approach...

## Results

### Descriptive statistics

Summary statistics tables...

### Main findings

Regression results and interpretation...

### Robustness checks

Alternative specifications...

## Conclusion

Summary and implications...

## References

::: {#refs}
:::
NoteSectioning in PDF vs. HTML

When rendering to PDF, Quarto maps markdown headings to LaTeX sectioning commands:

  • # becomes \chapter (in book format) or \section
  • ## becomes \section or \subsection
  • ### becomes \subsection or \subsubsection

For HTML output, headings become <h1>, <h2>, <h3>, etc.

You can control numbering with the number-sections: true option in the YAML header.

19.6.3 Multi-file projects

For larger projects like a thesis or a working paper with multiple analyses, you can organize content across multiple files:

my-project/
  _quarto.yml          # Project configuration
  index.qmd            # Main document
  01-introduction.qmd
  02-data.qmd
  03-methodology.qmd
  04-results.qmd
  references.bib
  data/
    stock_data.csv
  figures/

The _quarto.yml file controls the project structure:

project:
  type: book

book:
  title: "Essays on Market Microstructure"
  author: "Your Name"
  chapters:
    - index.qmd
    - 01-introduction.qmd
    - 02-data.qmd
    - 03-methodology.qmd
    - 04-results.qmd
    - references.qmd

bibliography: references.bib

format:
  pdf:
    documentclass: book
  html:
    theme: cosmo

This approach keeps individual files manageable while maintaining a cohesive document. You can render the entire project with quarto render or individual chapters with quarto render 02-data.qmd.

19.7 Code Execution and Output

The real power of Quarto lies in how it integrates code execution into your document. Let’s explore how to control what code runs, what gets displayed, and how results appear in your output.

19.7.1 Code chunks

Code chunks are fenced blocks of Python code that Quarto executes when rendering your document. The basic syntax is:

```{python}
# Your Python code here
import pandas as pd
data = pd.read_csv('data.csv')
print(data.head())
```

When rendered, this shows both your code and its output. Here’s a complete example:

import pandas as pd
import numpy as np

# Create sample data
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=5, freq='D')
data = pd.DataFrame({
    'Date': dates,
    'Price': [100, 102, 101, 103, 105],
    'Volume': [1000000, 1200000, 900000, 1100000, 1300000]
})

print(data)
        Date  Price   Volume
0 2023-01-01    100  1000000
1 2023-01-02    102  1200000
2 2023-01-03    101   900000
3 2023-01-04    103  1100000
4 2023-01-05    105  1300000

19.7.2 Chunk options

You can control code chunk behavior using special comments or chunk options. The most common options are:

  • echo: Show the code in output (default: true)
  • eval: Execute the code (default: true)
  • output: Show code output (default: true)
  • warning: Show warnings (default: true)
  • error: Continue rendering if code errors (default: false)

Here’s how to use them:

```{python}
#| echo: false
#| warning: false

# This code runs but doesn't appear in the output
# Warnings are suppressed
import pandas as pd
data = pd.read_csv('data.csv')
```

Common combinations:

Show code only, don’t run it:

```{python}
#| eval: false

# This demonstrates syntax but doesn't execute
model = LinearRegression()
model.fit(X, y)
```

Run code silently, show results only:

```{python}
#| echo: false

# Setup code that readers don't need to see
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')
```

Run code, show results, hide warnings:

```{python}
#| warning: false

# Code that might generate deprecation warnings
results = some_analysis_function(data)
print(results)
```
TipGlobal execution options

You can set default chunk options for the entire document in the YAML header:

---
title: "My Analysis"
execute:
  echo: false      # Hide all code by default
  warning: false   # Suppress all warnings
  message: false   # Suppress messages
---

Individual chunks can override these defaults by specifying their own options.

19.7.3 Inline code

For incorporating single values or calculations directly into your prose, use inline code expressions:

The sample includes {{{python}} len(data)} observations spanning
{{{python}} data['Date'].min().strftime('%B %Y')} to
{{{python}} data['Date'].max().strftime('%B %Y')}.

This renders as: “The sample includes 1,234 observations spanning January 2020 to December 2023.”

Inline code is perfect for:

  • Reporting sample sizes
  • Citing specific coefficients or statistics
  • Including computed dates or ranges
  • Any number that should update automatically when data changes
WarningInline code must be self-contained

Inline code expressions can only reference variables already defined in previous code chunks. They’re evaluated in the same Python session, but you can’t define complex logic inline. For anything beyond simple variable references or method calls, use a regular code chunk first:

```{python}
#| echo: false
average_return = returns['AAPL'].mean()

The average return was {{{python}} f”{average_return:.2%}“}.

:::

### Output formats and display

Different types of output appear differently in your rendered document:

**Print statements** appear as console output:

::: {#b06673cf .cell execution_count=2}
``` {.python .cell-code}
print("The analysis is complete.")
print(f"Average return: {0.0123:.2%}")
The analysis is complete.
Average return: 1.23%

DataFrames display as formatted tables:

summary = pd.DataFrame({
    'Mean': [0.0012, 0.0008, 0.0015],
    'Std Dev': [0.018, 0.022, 0.020],
    'Sharpe': [0.067, 0.036, 0.075]
}, index=['AAPL', 'GOOGL', 'MSFT'])

summary
Mean Std Dev Sharpe
AAPL 0.0012 0.018 0.067
GOOGL 0.0008 0.022 0.036
MSFT 0.0015 0.020 0.075

Matplotlib/Seaborn plots appear as embedded figures:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(8, 5))
data = np.random.normal(0.001, 0.02, 1000)
ax.hist(data, bins=50, edgecolor='black', alpha=0.7)
ax.set_xlabel('Daily Return')
ax.set_ylabel('Frequency')
ax.set_title('Distribution of Stock Returns')
plt.show()
Figure 19.1: Distribution of daily returns
NoteControlling figure output

For plots, use these chunk options:

  • label: Unique identifier for cross-referencing (must start with fig-)
  • fig-cap: Figure caption
  • fig-width: Width in inches
  • fig-height: Height in inches
  • fig-align: Alignment (left, center, right)

Example:

```{python}
#| label: fig-scatter
#| fig-cap: "Returns vs. Volume"
#| fig-width: 6
#| fig-height: 4
#| fig-align: center

plt.scatter(data['Volume'], data['Returns'])
plt.xlabel('Volume')
plt.ylabel('Returns')
plt.show()
```

19.7.4 Working with Jupyter notebooks

If you have existing analysis in Jupyter notebooks, Quarto can work with them directly. You can render a .ipynb file to PDF, HTML, or any other format without converting it first:

quarto render analysis.ipynb --to pdf
quarto render analysis.ipynb --to html

This makes it easy to produce polished output from notebook-based analysis. Quarto respects cell metadata in notebooks, so you can add chunk options like #| echo: false in notebook code cells.

For more control, you can convert a notebook to a Quarto document:

quarto convert notebook.ipynb

This creates a .qmd file that you can edit with full access to Quarto’s features.

19.7.5 Caching and freeze

Research computations can be time-consuming. If your analysis takes 30 minutes to run, you don’t want to wait that long every time you fix a typo. Quarto offers two solutions: caching and freezing.

Freeze stores the results of code execution and reuses them unless the code changes:

---
title: "My Analysis"
execute:
  freeze: auto  # Only re-run if code changes
---

With freeze: auto, Quarto:

  1. Runs all code chunks the first time you render
  2. Stores results in a _freeze directory
  3. On subsequent renders, reuses stored results unless code changed
  4. Detects code changes and re-runs only affected chunks

To force a complete re-execution:

quarto render --execute-all

Cache provides finer control at the chunk level:

```{python}
#| cache: true

# This expensive computation is cached
results = run_monte_carlo_simulation(n_simulations=100000)
```
TipWhen to use freeze vs. cache
  • Freeze (freeze: auto): Best for most research documents. Simple, automatic, and works at the document level.
  • Cache (cache: true): Use for specific expensive chunks within a document that otherwise runs quickly.
  • Neither (freeze: false): Only for documents with fast computations or when you need to ensure results are always fresh.

For empirical research papers, freeze: auto is typically the best choice. It ensures reproducibility while keeping rendering fast during the writing process.

19.8 Integrating Figures and Tables

Research documents live and die by their figures and tables. Quarto makes it easy to create, format, and reference high-quality visual content.

19.8.1 Creating figures

Figures in Quarto typically come from matplotlib, seaborn, or other plotting libraries. Here’s a complete example showing best practices:

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

# Generate sample data
np.random.seed(42)
dates = pd.date_range('2023-01-01', '2023-12-31', freq='D')
returns = pd.DataFrame({
    'Date': dates,
    'AAPL': np.random.normal(0.001, 0.02, len(dates)),
    'GOOGL': np.random.normal(0.0008, 0.022, len(dates)),
    'MSFT': np.random.normal(0.0012, 0.018, len(dates))
})

# Calculate rolling volatility
volatility = returns.set_index('Date')[['AAPL', 'GOOGL', 'MSFT']].rolling(30).std() * np.sqrt(252)

# Create plot
fig, ax = plt.subplots(figsize=(8, 5))
for col in volatility.columns:
    ax.plot(volatility.index, volatility[col], label=col, linewidth=1.5)

ax.set_xlabel('Date')
ax.set_ylabel('Annualized Volatility')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Figure 19.2: 30-Day Rolling Volatility for Tech Stocks

Notice the chunk options:

  • label: fig-volatility: Creates a reference ID for citing this figure
  • fig-cap: Adds a caption that appears below the figure
  • fig-width and fig-height: Control size in inches
  • warning: false: Suppresses any warnings from the plotting code

You can reference this figure in your text using @fig-volatility, which automatically numbers it and creates a clickable link.

TipPublication-quality figure tips

For figures that might appear in publications:

  1. Size appropriately: Most journals want figures around 6-8 inches wide
  2. Use vector formats: In PDF output, matplotlib figures are automatically vector (scalable)
  3. Choose readable fonts: Increase font sizes for small figures
  4. Simplify: Remove chart junk, use clear labels, and ensure sufficient contrast
  5. Consistent styling: Set a style once at the document start

Example setup chunk:

```{python}
#| echo: false

import matplotlib.pyplot as plt
import seaborn as sns

# Set consistent style for all figures
sns.set_style('whitegrid')
plt.rcParams['figure.dpi'] = 300
plt.rcParams['font.size'] = 10
plt.rcParams['axes.labelsize'] = 11
plt.rcParams['axes.titlesize'] = 12
```

19.8.2 Creating tables

Tables in empirical finance typically show summary statistics, regression results, or data samples. Quarto handles several approaches:

1. Simple DataFrame display

The simplest approach is to just display a pandas DataFrame:

summary_stats = pd.DataFrame({
    'Mean': [0.0012, 0.0008, 0.0015],
    'Std Dev': [0.0180, 0.0220, 0.0200],
    'Sharpe': [0.067, 0.036, 0.075],
    'Min': [-0.0523, -0.0601, -0.0489],
    'Max': [0.0612, 0.0701, 0.0623]
}, index=['AAPL', 'GOOGL', 'MSFT'])

summary_stats
Table 19.1: Summary statistics for daily returns
Mean Std Dev Sharpe Min Max
AAPL 0.0012 0.018 0.067 -0.0523 0.0612
GOOGL 0.0008 0.022 0.036 -0.0601 0.0701
MSFT 0.0015 0.020 0.075 -0.0489 0.0623

This works well for HTML output but may not be publication-ready for PDF.

2. Formatted tables with styling

For better control, use pandas styling:

summary_stats.style \
    .format('{:.4f}') \
    .set_caption("Daily Return Statistics") \
    .set_table_styles([
        {'selector': 'th', 'props': [('font-weight', 'bold')]},
        {'selector': 'td', 'props': [('text-align', 'right')]}
    ])
Table 19.2: Formatted summary statistics
(a) Daily Return Statistics
  Mean Std Dev Sharpe Min Max
AAPL 0.0012 0.0180 0.0670 -0.0523 0.0612
GOOGL 0.0008 0.0220 0.0360 -0.0601 0.0701
MSFT 0.0015 0.0200 0.0750 -0.0489 0.0623

3. Regression tables

For regression output, specialized packages like stargazer (Python port) or creating custom tables work well:

Table 19.3: OLS regression results
Variable Coefficient Std Error t-stat
0 Intercept 0.0023* 0.0012 1.92
1 Market Return 0.9800*** 0.0800 12.25
2 SMB 0.4500*** 0.1500 3.00
3 HML -0.1200 0.0900 -1.33

4. Cross-tabulations

For contingency tables or grouped summaries:

# Create sample panel data
np.random.seed(42)
panel_data = pd.DataFrame({
    'Year': np.repeat([2021, 2022, 2023], 4),
    'Quarter': [1, 2, 3, 4] * 3,
    'Avg_Return': np.random.normal(0.02, 0.05, 12)
})

# Create pivot table
pivot = panel_data.pivot(index='Year', columns='Quarter', values='Avg_Return')
pivot.columns = [f'Q{i}' for i in pivot.columns]
pivot
Table 19.4: Returns by year and quarter
Q1 Q2 Q3 Q4
Year
2021 0.044836 0.013087 0.052384 0.096151
2022 0.008292 0.008293 0.098961 0.058372
2023 -0.003474 0.047128 -0.003171 -0.003286
NoteTable references

Like figures, tables with label: tbl-name and tbl-cap can be cross-referenced in text using @tbl-name. Quarto automatically numbers tables and creates hyperlinks in HTML and PDF output.

For example, you might write: “Table Table 19.1 shows that MSFT has the highest Sharpe ratio among the three stocks.”

19.8.3 Cross-referencing

Professional documents need consistent numbering and easy cross-references. Quarto handles this automatically:

Figures:

See @fig-volatility for the time series of rolling volatility.

Tables:

Summary statistics are presented in @tbl-summary.

Sections:

## Data Sources {#sec-data}

Our data sources are described in @sec-data.

Equations:

The CAPM equation is:

$$
E[R_i] = R_f + \beta_i(E[R_m] - R_f)
$$ {#eq-capm}

As shown in @eq-capm, expected returns depend on systematic risk.

Quarto automatically:

  • Numbers all figures, tables, equations, and sections
  • Updates numbers when you add or remove items
  • Creates hyperlinks for easy navigation
  • Formats references according to output type (HTML vs. PDF)
TipPrefix conventions

Quarto uses prefixes to determine reference types:

  • fig-*: Figures
  • tbl-*: Tables
  • sec-*: Sections
  • eq-*: Equations
  • lst-*: Code listings
  • thm-*: Theorems

Following these conventions ensures proper formatting and numbering.

19.8.4 Figure and table layout

For professional documents, you often need precise control over layout:

Side-by-side figures:

::: {#fig-comparison layout-ncol=2}

```{python}
#| label: fig-returns
#| fig-cap: "Returns"

plt.plot(dates, returns)
plt.show()
```

```{python}
#| label: fig-volume
#| fig-cap: "Volume"

plt.plot(dates, volume)
plt.show()
```

Returns and trading volume comparison
:::

Subfigures:

```{python}
#| label: fig-multi
#| fig-cap: "Stock Market Analysis"
#| fig-subcap:
#|   - "Price levels"
#|   - "Daily returns"
#| layout-ncol: 2

# First subplot
plt.subplot(1, 2, 1)
plt.plot(prices)
plt.title('Prices')

# Second subplot
plt.subplot(1, 2, 2)
plt.plot(returns)
plt.title('Returns')

plt.tight_layout()
plt.show()
```

Custom positioning:

For PDF output, you can control figure placement:

```{python}
#| label: fig-important
#| fig-cap: "Critical results"
#| fig-pos: 'H'  # Force exact position (requires float package in LaTeX)

plt.plot(data)
plt.show()
```

19.9 LaTeX Configuration

When you need fine-grained control over PDF output, Quarto exposes many LaTeX options through the YAML header. This section covers the most useful configurations for academic writing.

19.9.1 PDF engine selection

By default, Quarto uses LuaLaTeX to compile PDFs. You can specify an alternative:

format:
  pdf:
    pdf-engine: xelatex  # or pdflatex, lualatex

LuaLaTeX (the default) handles Unicode well and supports modern font features. XeLaTeX is another good choice for Unicode and custom fonts. The older pdflatex is faster but has limited Unicode support.

19.9.2 Document classes

The documentclass option controls the overall document structure:

format:
  pdf:
    documentclass: article  # Standard LaTeX classes

Standard classes include article, report, and book. For better typography, consider the KOMA-Script classes:

format:
  pdf:
    documentclass: scrartcl  # KOMA-Script article

The KOMA-Script classes (scrartcl, scrreprt, scrbook) offer improved typography and more customization options compared to standard LaTeX classes.

19.9.3 Custom styling with external files

For extensive customization, you can include external LaTeX files:

format:
  pdf:
    include-in-header: preamble.tex
    include-before-body: titlepage.tex

The preamble.tex file might contain:

% preamble.tex
\usepackage{setspace}
\doublespacing

\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead[L]{Working Paper}
\fancyhead[R]{\thepage}

This approach lets you apply consistent styling across multiple documents by sharing a common preamble.

19.9.4 Raw LaTeX in documents

You can include LaTeX commands directly in your Quarto document when needed:

Inline LaTeX:

The result is significant at the \( p < 0.01 \) level.

Display equations:

$$
\hat{\beta} = (X'X)^{-1}X'y
$$

Raw LaTeX blocks:

```{=latex}
\begin{theorem}
Under standard assumptions, $\hat{\beta}$ is consistent.
\end{theorem}

Use raw LaTeX sparingly—it only appears in PDF output and breaks the multi-format capability.

### Conditional content for different formats

Sometimes you need different content for PDF versus HTML. Use conditional blocks:

```markdown
::: {.content-visible when-format="html"}
Click the button below to download the data.
:::

::: {.content-visible when-format="pdf"}
Data available at: https://example.com/data
:::

This is useful for:

  • Interactive elements that only work in HTML
  • Print-specific instructions for PDF readers
  • Format-appropriate download links

19.10 PDF and HTML Output

One of Quarto’s greatest strengths is generating multiple output formats from a single source. Let’s explore how to create polished PDF and HTML documents suitable for academic publication.

19.10.1 PDF output basics

To generate PDF output, specify it in the YAML header:

---
title: "Earnings Announcements and Volatility"
author: "Your Name"
format: pdf
---

Then render with:

quarto render document.qmd --to pdf

Quarto uses LaTeX behind the scenes, but you don’t need to know LaTeX to create beautiful PDFs. Quarto handles the conversion automatically.

19.10.2 PDF formatting options

Customize PDF appearance with format options:

---
title: "My Research Paper"
author: "Your Name"
date: today
format:
  pdf:
    documentclass: article  # article, report, or book
    fontsize: 11pt         # 10pt, 11pt, or 12pt
    geometry:
      - margin=1in         # Page margins
    number-sections: true  # Numbered sections
    toc: true             # Table of contents
    toc-depth: 2          # How many levels to include
    colorlinks: true      # Colored hyperlinks
    biblio-style: apalike # Citation style
---

Common document classes:

  • article: Standard papers (default)
  • scrartcl: KOMA-Script article (better typography)
  • report: Longer documents with chapters
  • book: Books with parts and chapters

Page layout:

format:
  pdf:
    geometry:
      - top=1in
      - bottom=1in
      - left=1.25in
      - right=1.25in
    papersize: letter  # or a4

Typography:

format:
  pdf:
    mainfont: "Times New Roman"      # Main text font
    sansfont: "Arial"                # Sans-serif font
    monofont: "Courier New"          # Code font
    fontsize: 12pt
    linestretch: 1.5                 # Line spacing (1.5 = 1.5-spaced)
NoteWorking paper vs. journal submission

For working papers, you control the formatting completely. For journal submissions, most journals provide LaTeX templates or Word templates. You can:

  1. Use Quarto with journal formatting: Some journals have Quarto templates
  2. Export to LaTeX: Render with quarto render --to latex and edit the .tex file
  3. Export to Word: Use format: docx and apply journal styles in Word

Many researchers write in Quarto for working papers and early drafts, then convert to journal format for final submission.

19.10.3 HTML output basics

HTML output is ideal for online sharing, course materials, and interactive documents:

---
title: "My Analysis"
format: html
---

HTML output automatically includes:

  • Interactive table of contents
  • Code folding (readers can show/hide code)
  • Responsive design (works on mobile devices)
  • Fast navigation and search

19.10.4 HTML themes and styling

Quarto includes several built-in themes:

format:
  html:
    theme: cosmo  # Options: default, cosmo, flatly, journal, etc.

Popular themes for academic content:

  • cosmo: Clean, modern
  • flatly: Flat design, very readable
  • journal: Newspaper-style
  • litera: Classic, professional
  • sandstone: Warm, welcoming

Customize further:

format:
  html:
    theme: cosmo
    toc: true                 # Table of contents
    toc-depth: 3             # Levels to include
    toc-location: left       # left, right, or body
    number-sections: true
    number-depth: 3
    code-fold: true          # Collapsible code blocks
    code-tools: true         # Show/hide all code button
    code-link: true          # Hyperlink function names
    df-print: paged          # Paginate large DataFrames

Custom CSS:

For fine-grained control, add custom CSS:

format:
  html:
    theme: cosmo
    css: custom-styles.css

Then create custom-styles.css:

/* custom-styles.css */
body {
    font-family: 'Georgia', serif;
    font-size: 18px;
}

h1, h2, h3 {
    color: #2c3e50;
}

.table {
    font-size: 0.9em;
}

19.10.5 Multiple output formats

You can configure multiple outputs simultaneously:

---
title: "My Research"
author: "Your Name"
format:
  html:
    theme: cosmo
    toc: true
    code-fold: true
  pdf:
    documentclass: article
    number-sections: true
    colorlinks: true
  docx:
    reference-doc: custom-template.docx
---

Render all formats:

quarto render document.qmd

Or render a specific format:

quarto render document.qmd --to pdf
quarto render document.qmd --to html
quarto render document.qmd --to docx
TipFormat-specific content

Sometimes you need different content for different outputs. Use conditional blocks:

::: {.content-visible when-format="html"}
This paragraph only appears in HTML output.
You can include interactive widgets here.
:::

::: {.content-visible when-format="pdf"}
This paragraph only appears in PDF output.
You might include a note about the HTML version.
:::

This is useful for:

  • Adding interactive elements to HTML but not PDF
  • Including print-specific formatting notes
  • Providing format-appropriate download links

19.10.6 Citations and references

Academic writing requires proper citations. Quarto integrates with BibTeX and other reference managers:

1. Create a bibliography file (references.bib):

@article{fama1993,
  title={Common risk factors in the returns on stocks and bonds},
  author={Fama, Eugene F and French, Kenneth R},
  journal={Journal of Financial Economics},
  volume={33},
  number={1},
  pages={3--56},
  year={1993}
}

@book{campbell1997,
  title={The Econometrics of Financial Markets},
  author={Campbell, John Y and Lo, Andrew W and MacKinlay, A Craig},
  year={1997},
  publisher={Princeton University Press}
}

2. Reference it in your YAML header:

---
title: "My Research"
bibliography: references.bib
---

3. Cite in your text:

The Fama-French three-factor model [@fama1993] extends the CAPM by including
size and value factors. For a comprehensive treatment of financial econometrics,
see @campbell1997.

This renders as:

The Fama-French three-factor model (Fama and French, 1993) extends the CAPM by including size and value factors. For a comprehensive treatment of financial econometrics, see Campbell, Lo, and MacKinlay (1997).

4. Include a references section:

## References

::: {#refs}
:::

Quarto automatically generates a formatted reference list here.

Citation styles:

Change citation format with CSL (Citation Style Language):

---
bibliography: references.bib
csl: american-economic-review.csl  # Download from Zotero Style Repository
---

Popular styles for finance:

  • apa.csl: American Psychological Association
  • chicago-author-date.csl: Chicago style
  • american-economic-review.csl: AER style
  • journal-of-finance.csl: JF style

Download CSL files from the Zotero Style Repository.

NoteReference managers

Quarto works seamlessly with reference managers:

  • Zotero: Export your library as BibTeX
  • Mendeley: Use Better BibTeX plugin
  • EndNote: Export to BibTeX format

Many researchers maintain a master .bib file for all their work and reference it in each Quarto document.

19.10.7 Mathematics and equations

Quarto supports LaTeX-style mathematical notation using MathJax (HTML) or native LaTeX (PDF):

Inline math:

The expected return is $E[R_i] = \mu_i$ with variance $\sigma_i^2$.

Display equations:

$$
R_{i,t} - R_{f,t} = \alpha_i + \beta_i(R_{m,t} - R_{f,t}) + \epsilon_{i,t}
$$

Numbered equations:

$$
\text{Sharpe Ratio} = \frac{E[R_p] - R_f}{\sigma_p}
$$ {#eq-sharpe}

As shown in @eq-sharpe, the Sharpe ratio measures risk-adjusted returns.

Aligned equations:

$$
\begin{aligned}
E[R_i] &= R_f + \beta_i(E[R_m] - R_f) \\
\beta_i &= \frac{\text{Cov}(R_i, R_m)}{\text{Var}(R_m)} \\
\alpha_i &= E[R_i] - \left[R_f + \beta_i(E[R_m] - R_f)\right]
\end{aligned}
$$

Common financial math symbols:

  • Greek letters: $\alpha, \beta, \gamma, \sigma, \mu$ renders as α, β, γ, σ, μ
  • Subscripts: $R_{i,t}$ renders as R_{i,t}
  • Superscripts: $e^{-rt}$ renders as e^{-rt}
  • Fractions: $\frac{a}{b}$ renders as a/b
  • Summations: $\sum_{i=1}^{n} x_i$ renders as Σ_{i=1}^n x_i
  • Expectations: $E[X]$ or $\mathbb{E}[X]$

19.11 Collaborative Commenting with quarto-comments

When writing research documents collaboratively, you often need to leave notes, questions, or todos for yourself or co-authors. The quarto-comments extension provides shortcodes for inline comments that appear in your output (useful during drafting) but can be hidden for final submission.

19.11.1 Installing the extension

Install the extension in your Quarto project:

quarto add vgreg/quarto-comments

This adds the extension to your project’s _extensions directory.

19.11.2 Using comment shortcodes

The extension provides three shortcode types:

Todos for tasks that need attention:

We need to add robustness checks here. {{< todo >}}

The data section should cite the original source. {{< todo author="AB" >}}

Notes for explanatory comments:

This result is surprisingly strong. {{< note >}}

Consider alternative specifications. {{< note author="CD" >}}

Questions for items needing discussion:

Should we include financial firms? {{< question >}}

Is this the right sample period? {{< question author="EF" >}}

19.11.3 Configuration

Configure the extension in your _quarto.yml or document YAML:

comments:
  show: true  # Set to false for final submission
  authors:
    AB:
      name: "Alice Brown"
      color: "#e41a1c"
    CD:
      name: "Chris Davis"
      color: "#377eb8"

When you’re ready to submit, simply set show: false to hide all comments from the output without deleting them from your source file.

TipHiding comments for submission

A common workflow is to keep show: true during drafting and collaboration, then change to show: false when generating the final PDF for submission. The comments remain in your source document for future reference but don’t appear in the output.

19.12 Best Practices for Research Documents

As you create research documents with Quarto, keep these practices in mind:

19.12.1 1. Separate data processing from presentation

Structure your workflow with distinct stages:

```{python}
#| echo: false
#| output: false

# Data loading and cleaning (run silently)
import pandas as pd
data = pd.read_csv('raw_data.csv')
data = data.dropna()
data = data[data['date'] >= '2020-01-01']
```{python}
#| echo: false

# Analysis and results (show output)
summary_stats = data.describe()
summary_stats
```

This keeps your document clean and focused on results rather than data munging details.

### 2. Use meaningful chunk labels

Good labels make documents maintainable:

```markdown
#| label: fig-event-study
#| label: tbl-regression-main
#| label: sec-robustness-checks

Not:

#| label: fig-1
#| label: tbl-stuff
#| label: sec-section3

19.12.2 3. Document your data and code

Include a setup section explaining data sources and key assumptions:

## Data

We obtain daily stock returns from CRSP for all NYSE, AMEX, and NASDAQ stocks
from January 1990 to December 2023. We require firms to have at least 12 months
of return data and exclude financial firms (SIC codes 6000-6999).

```{python}
#| echo: false

# Data filters
min_months = 12
start_date = '1990-01-01'
end_date = '2023-12-31'
exclude_sic = range(6000, 7000)

### 4. Use freeze for long-running documents

For computationally intensive research:

```yaml
---
title: "Monte Carlo Analysis"
execute:
  freeze: auto
---

This lets you edit text freely without re-running expensive simulations.

19.12.3 5. Version control with Git

Quarto documents are plain text, making them perfect for Git:

git add analysis.qmd references.bib
git commit -m "Add robustness checks section"
git push

Benefits:

  • Track changes over time
  • Collaborate with co-authors
  • Revert mistakes easily
  • Maintain complete history
Tip.gitignore for Quarto projects

Add these to your .gitignore:

_freeze/
_book/
*.html
*.pdf
.quarto/

This keeps your repository focused on source files, not generated output.

19.12.4 6. Create reusable templates

For papers following a standard structure:

---
title: "19  Reproducible Documents with Quarto"
author: "Your Name"
date: today
format:
  pdf:
    template: research-paper.tex
    number-sections: true
    colorlinks: true
bibliography: ~/Documents/references.bib
csl: journal-of-finance.csl
---

Save this as a template and reuse it for each new project.

19.13 Comparison with Jupyter Notebooks

You may be wondering how Quarto documents differ from Jupyter notebooks, which you might already use. Here’s a detailed comparison:

Jupyter Notebooks:

  • Interactive, cell-by-cell execution
  • Immediate visual feedback
  • Great for exploration and experimentation
  • Output embedded in the .ipynb file
  • Can become cluttered with many cells
  • Challenging for version control (JSON format)
  • Limited output format options

Quarto Documents:

  • Batch execution (render entire document)
  • Focus on narrative and final output
  • Excellent for communication and publication
  • Source is plain text, output separate
  • Clean, linear structure
  • Git-friendly (plain text)
  • Multiple output formats (PDF, HTML, Word, etc.)

When to use each:

  • Jupyter: Initial data exploration, interactive analysis, teaching demonstrations
  • Quarto: Research papers, reports, documentation, reproducible analyses for publication

Workflow integration:

Many researchers use both:

  1. Explore in Jupyter notebooks
  2. Refine analysis into Quarto document
  3. Publish final document in multiple formats

You can even convert between formats:

# Convert notebook to Quarto
quarto convert notebook.ipynb

# Render notebook as PDF
quarto render notebook.ipynb --to pdf

19.14 Additional Resources

To deepen your knowledge of Quarto:

Official Documentation:

Learning Resources:

Templates and Examples:

Community:

Quarto bridges the gap between LaTeX’s typesetting power and Jupyter’s reproducibility, letting you keep your analysis and writing in one place. Whether you’re writing a working paper, preparing course materials, or building a research website, Quarto makes the entire process more reproducible, collaborative, and efficient.

Knaflic, Cole Nussbaumer. 2015. Storytelling with Data: A Data Visualization Guide for Business Professionals. Wiley.
Rougier, Nicolas P. 2021. Scientific Visualization: Python + Matplotlib.
Tufte, Edward R. 2001. The Visual Display of Quantitative Information. 2nd ed. Graphics Press.
Wilke, Claus O. 2019. Fundamentals of Data Visualization: A Primer on Making Informative and Compelling Figures. 1st ed. Sebastopol, CA: O’Reilly Media.
Yadan, Omry. 2019. “Hydra - a Framework for Elegantly Configuring Complex Applications.” Github. https://github.com/facebookresearch/hydra.