Lapse rates by policy year
Exposure is measured in policy-years; rates are crude.
Write in plain text, publish to HTML, PDF, or Word — and mix live code with prose in notebooks.
pandoc and render Markdown to HTML, PDF, and WordWhy programmers write documents in plain text — and how Markdown makes it pretty.
Rename report.docx to report.zip and open it:
report.zip\
├── [Content_Types].xml
├── _rels\
├── docProps\
└── word\
├── document.xml your words, buried in XML
├── styles.xml
├── settings.xml
└── theme\
A .docx is a compressed bundle of machine-oriented files.
Select-String works on them.txt.md.docx / .pagesMarkdown is designed to be readable as-is — the source looks like a neatly typed email — and to render beautifully when processed.
You’ll meet it everywhere: GitHub READMEs, Jupyter, VS Code, Teams & Slack messages, Obsidian notes, Quarto reports… and these slides.
# Lapse Study 2026
## Summary
Lapses were **higher** than expected in
policy years 2–3, *especially* for
single-premium business.
- Data: `claims_2025.csv`
- Method: actual vs. expected
- See the [SOA table](https://mort.soa.org)
1. Load data
2. Clean data
3. Compare to basis
> Draft — not for distribution.
Lapses were higher than expected in policy years 2–3, especially for single-premium business.
claims_2025.csvDraft — not for distribution.
Run the model with:
```powershell
uv run python src/model.py
```
| Policy year | A/E |
|------------:|-----:|
| 1 | 0.94 |
| 2 | 1.12 |
| 3 | 1.07 |
Annuity-due: $\ddot{a}_x = \sum_{k\ge0} v^k\,{}_kp_x$
Run the model with:
uv run python src/model.py
| Policy year | A/E |
|---|---|
| 1 | 0.94 |
| 2 | 1.12 |
| 3 | 1.07 |
Annuity-due: $\ddot{a}_x = \sum_{k\ge0} v^k\,{}_kp_x$
Math uses LaTeX notation between $…$ (inline) or $$…$$ (display). Supported by pandoc, Jupyter, and GitHub.
One Markdown source → HTML, PDF, Word, slides, and more.
winget install --id JohnMacFarlane.Pandoc -e
# new terminal:
pandoc --version
Write the valuation memo in Markdown next to the code. Produce a Word copy for the reviewer — with the company template — in one command.
pandoc converts to PDF by handing a typesetting program the job. Pick one:
Small, fast, modern, single .exe.
winget install --id Typst.Typst -e
pandoc README.md -o README.pdf --pdf-engine=typst
Gorgeous math typesetting; a large install. Also needed later by nbconvert --to pdf.
winget install --id MiKTeX.MiKTeX -e
pandoc README.md -o README.pdf
Zero-install fallback: render to HTML, open it in a browser, and Print → Save as PDF.
pandoc README.md -o README.html # HTML fragment
pandoc README.md -s -o README.html # complete, standalone page
pandoc README.md -s --toc --mathml -o README.html # + table of contents + math
pandoc README.md -s -c style.css --embed-resources -o README.html # styled, single file
pandoc README.md -o README.pdf --pdf-engine=typst # PDF
pandoc memo.md -o memo.docx --reference-doc=company_template.docx # Word, house style
-o file | Output file; the extension picks the format | -s | Standalone: full document with header |
--toc | Add a table of contents | --mathml | Render math in HTML (no internet needed) |
-c style.css | Apply a stylesheet | --embed-resources | Inline CSS & images into one file |
---
title: "Lapse Experience Study"
author: "Jane Doe"
date: 2026-09-29
---
## Summary
Lapses were higher than expected…
--- lines at the very top of the file<title>## — the title is already the top headingWithout a title, pandoc -s warns you. Either add front matter or pass --metadata title="…".
Literate programming: code, results, and explanation in one document.
Write programs for people first, computers second.
— the idea behind Donald Knuth’s “literate programming” (1984)
A Jupyter notebook (.ipynb) interleaves:
Exposure is measured in policy-years; rates are crude.
| year | exposure | lapses | rate | |
|---|---|---|---|---|
| 0 | 1 | 1000 | 120 | 0.120000 |
| 1 | 2 | 880 | 90 | 0.102273 |
| 2 | 3 | 790 | 55 | 0.069620 |
Run one step, look at the result, adjust, run again. Ideal for exploring a new dataset.
The reasoning, the code, and the numbers sit together — a reviewer can follow every step.
GitHub renders .ipynb files; export to HTML or PDF for non-programmers (Module 8).
Think of a notebook as an Excel workbook where every formula is visible, documented, and re-runnable from top to bottom.
cd C:\actuarial_work\lapse_analytics
uv add --dev jupyterlab # once per project (done in Module 4)
uv run jupyter lab # every session
.venv PythonBecause Jupyter runs via uv run, every notebook sees exactly the packages in uv.lock. No “but it worked in my notebook” mysteries.
Finished? Save, close the browser tab, then Ctrl+C in the terminal to stop the server.
Two modes: Edit (typing in a cell, Enter) and Command (acting on cells, Esc).
| Shift+Enter | Run cell, move to next |
| Ctrl+Enter | Run cell, stay put |
| Esc / Enter | Command / edit mode |
In command mode:
| A / B | Insert cell above / below |
| M / Y | Make cell Markdown / code |
| D D | Delete cell |
| Z | Undo cell deletion |
| 0 0 | Restart the kernel |
| Pitfall | Why it bites | Habit |
|---|---|---|
| Hidden state | Cells run out of order; a deleted cell’s variable still exists in memory | Kernel → Restart Kernel and Run All Cells before trusting or sharing results |
| Noisy diffs | .ipynb is JSON with outputs embedded; Git diffs are hard to read | Clear outputs before committing when outputs don’t matter |
| Everything in one notebook | Reusable logic gets copy-pasted between notebooks | Move stable functions into src\*.py and import them (Module 8) |
| Absolute paths | C:\Users\Jane\… breaks for everyone else | Relative paths from the project root (Module 1!) |
pandoc and Typst with wingetREADME.md for lapse_analytics (Module 5 repo)output\uv runnotebooks\01_first_look.ipynbcd C:\actuarial_work\lapse_analytics then notepad README.md — replace uv’s stub with something like:
---
title: "Lapse Analytics"
author: "Your Name"
date: 2026-09-29
---
## Purpose
Explore policy **lapse experience** by policy year.
## Setup
```powershell
uv sync
uv run check_env.py
```
## Layout
| Folder | Contents |
|--------------|-----------------------|
| `data/` | inputs (not in Git) |
| `notebooks/` | exploratory notebooks |
| `output/` | rendered reports |
## Method
Crude lapse rate in policy year $t$:
$$ w_t = \frac{L_t}{E_t} $$
mkdir output -Force
pandoc README.md -s --mathml -o output\README.html
pandoc README.md -o output\README.pdf --pdf-engine=typst
start output\README.html
start output\README.pdf
git status # output\ is ignored — only README.md shows
git add README.md
git commit -m "Write project README"
Edit, re-render, refresh the browser. Tweak until you’re happy — then commit.
Push to GitHub (if you set up a remote) and see GitHub render the same README on your repo’s front page.
mkdir notebooks -Force
uv run jupyter lab
notebooks, then File → New → Notebook (kernel: Python 3)01_first_look.ipynbThen, in a second terminal tab:
git add notebooks\01_first_look.ipynb
git commit -m "Add first-look lapse notebook"
git log --oneline
Stretch: add a cell computing the average rate with df["rate"].mean(), and a Markdown cell with the formula in $$…$$.
pandoc and a PDF engine are installed; I can render HTML and PDF-s, -o, --toc, and YAML front matteruv run jupyter lab and stop it cleanlyTerminal, Git, notebooks, and Python — all in one window.