← All work

Foundations

Starting from a known answer

Seven independent builds implement well-studied methods: option pricing, experiment statistics, double-entry accounting, SaaS metrics, search ranking, color contrast and cited question answering. Each one is checked against a reference it did not produce.

Independent portfolio projects · Synthetic and fictional data

01 Quantitative finance

quantkit

Option pricing with Black–Scholes–Merton formulas, Cox–Ross–Rubinstein trees and Monte Carlo; mean–variance and risk-parity portfolios; and a vectorized daily backtester, in Python.

A strategy decides with prices through day t, and those weights earn day t+1's return. A checker changes later prices and fails any strategy whose earlier weights move.

Complete workflow: generate fictional prices, estimate monthly allocations from prior closes and compare on shared dates and costs. Inspect six CSV exports in the walk-forward guide and its timing regression tests.

PythonNumPySciPypandas
Checked againstIndependent references
Hull's worked example
S = 42, K = 40, r = 10%, σ = 20%, T = 0.5 prices the call at 4.76 and the put at 0.81.
Put–call parity
Holds for randomly generated contracts.
Closed forms
The numerical optimizer matches the analytic minimum-variance, tangency and target-return portfolios.
Convergence
The European binomial price approaches Black–Scholes as steps increase.

Constant-volatility models on synthetic prices. Educational software, not investment advice.

Review the code Catch a strategy that peeks

Run the boundary

From a quantkit checkout with uv installed, run the look-ahead tests.

uv run pytest tests/test_backtest_engine.py -k lookahead -v

Expected: 7 passed. Six built-in strategy configurations pass the checker, and a deliberately peeking strategy is caught.

Follow the decision

Read the timing contract at the top of engine.py. Weights set at the close of day t are held during day t+1; drift, turnover and costs follow from share counts without a loop over days.

Passing the checker shows a strategy cannot see future prices. It says nothing about whether the strategy would have made money.

02 Experimentation statistics

powerlift

Experiment design and analysis in Python: sample sizes and power, frequentist and Bayesian comparisons, CUPED variance reduction, sample-ratio-mismatch checks and multiple-testing corrections.

Group sequential boundaries come from the joint normal distribution of the interim z-statistics, so a team can look early without inflating its false-positive rate.

Complete workflow: read a seeded per-user CSV, check allocation and the declared outcome window, then inspect a CUPED readout with uncertainty. The experiment walkthrough includes blocked scenarios and an inconclusive synthetic result.

PythonSciPyNumPySimulation

The README's peeking simulation runs 10,000 A/A experiments (seed 2026): stopping at the first p < 0.05 across ten looks flags 19.5% of them, against a 5% target.

Checked againstPublished values
Jennison & Turnbull tables
Pocock constants for 5 and 10 looks; O'Brien–Fleming constants for 2, 3, 4, 5 and 10 looks.
Lan–DeMets bounds
O'Brien–Fleming-type alpha spending with five looks.
SciPy and statsmodels
Tests, intervals, power and multiple-testing corrections agree with reference implementations.

Large-sample normal approximations with two arms. Matching published constants checks the arithmetic, not whether an approximation suits a small experiment.

Review the code Reproduce published boundaries

Run the boundary

From a powerlift checkout with uv installed, run the published-value tests.

uv run pytest tests/test_sequential.py -k "jennison_turnbull or published_bounds or five_looks" -v

Expected: 12 passed. Each constant and boundary matches its published value within the stated tolerance.

Follow the decision

Read group_sequential_boundaries in sequential.py. It solves for the constant whose crossing probability under no effect equals alpha, using correlation √(ti/tj) between looks.

Sequential monitoring controls false positives across planned looks. It does not rescue a test that was underpowered from the start.

03 Fintech backend

ledgerline

A double-entry ledger API built with FastAPI and SQLAlchemy. Journal entries are integer minor units that must balance per currency, posted transactions are immutable, and corrections are reversals.

Each write is one database transaction that replays the stored response for a repeated Idempotency-Key and compare-and-swaps every account version it touches, so concurrent requests cannot overdraw an account.

Complete workflow: run an isolated wallet over real local HTTP. Verify retry identity, overdraft rejection, reversal and historical balances, then reconcile and clean up. The wallet demo guide explains the receipt and disposable storage.

PythonFastAPISQLAlchemy 2.0PostgreSQL / SQLite
InvariantsEnforced by tests
Balanced
Every transaction sums to zero in each currency.
Immutable
Corrections are reversals, at most one per transaction.
Idempotent
The same key and body replay the original response; a different body is rejected.
Reconciled
Stored balances equal the sum of their entries.

A Hypothesis state machine checks these after random sequences of operations, and CI repeats the suite on PostgreSQL.

Review the code Race transfers against one balance

Run the boundary

From a ledgerline checkout with uv installed, run the concurrency tests.

uv run pytest tests/test_concurrency.py -v

Expected: 6 passed. Concurrent transfers never overdraw an account or lose an update, and duplicate requests take effect once.

Follow the decision

Read _write and _post in service.py: idempotency lookup, validation, a version check on each account in sorted order, then the insert. A conflict rolls back and retries with fresh data a bounded number of times.

Shared API keys and SQLite's single writer are deliberate limits. The design notes list what a production ledger would add.

04 Analytics engineering

saas-analytics-warehouse

Analytics engineering for a fictional SaaS company. A seeded generator writes eight raw tables, a dbt and DuckDB warehouse models MRR movements, cohort retention and unit economics, and a report rebuilds its charts.

Complete workflow: trace a selected month from the MRR bridge to fictional customer records, payment-failure losses and channel/reason totals. The read-only investigation walkthrough reconciles exact cents and customer counts before writing three CSVs and a Markdown report. Payment-failure churn remains a subset of total loss.

Every customer-month's MRR change gets exactly one label (new, expansion, contraction, churn or reactivation), so the monthly bridge has to reconcile to the cent.

dbtDuckDBSQLPython
Data testsFail the build
Bridge reconciles
Starting MRR plus every movement equals ending MRR, every month.
One movement each
Each changed customer-month matches exactly one category.
Two systems agree
MRR from subscription events matches the invoices in force.
No overlaps
A customer is never on two subscriptions at once.

All data is synthetic. The numbers demonstrate the method, not market benchmarks.

Review the code Reconcile the MRR bridge

Run the boundary

From a warehouse checkout with uv and make installed, generate the data and build the warehouse.

make data build

Expected: dbt reports PASS=173 WARN=0 ERROR=0, including assert_mrr_bridge_reconciles.

Follow the decision

Read fct_mrr_movements.sql. MRR is a month-end snapshot on a gap-free customer-month spine; a customer is new in the first paid month and reactivated after a gap.

Month-end snapshots hide a cancellation and return within one month. The metric definitions list that and every other edge case.

05 Search and retrieval

searchlite

A zero-dependency TypeScript search engine: Porter stemming, a positional inverted index, BM25F ranking across weighted fields, a query language with phrases, prefixes and fuzzy terms, highlighting, and hybrid retrieval with reciprocal rank fusion.

explain() rebuilds a result's score clause by clause and field by field, and a test requires it to equal the score the search returned.

Complete workflow: index a local documentation folder, save a snapshot and query it with snippets and score explanations. The local docs walkthrough uses the built library; searches read the snapshot and do not reread source files.

Review a retrieval change: open the relevance workbench, compare BM25F with stemming disabled, and inspect a query to see its ranked documents, judged grades and missed relevant entries. Download the full inputs and report; the browser and native command produce the same JSON. Aggregate gains can hide an individual query regression.

For your own notes, label questions and set per-query thresholds. A saved snapshot is enough to rerun the checks; unmet criteria return a distinct process exit code. The separate reproducible timing method records raw searches at 1,000 and 10,000 synthetic documents. Repeated glossary vocabulary and one machine's measurements do not establish production latency or general retrieval quality.

Use your own files: the local-file workbench compares two saved indexes against one judgments file in a browser worker. Inspect each query's rankings and download the same JSON as the native command. A synthetic fixture in the real browser tests has a positive average gain yet still fails an individual regression. Files stay in page memory; Clear releases application references. Reports contain supplied query text and document IDs, and the tool does not authenticate postings or labels. The workflow and limits explain bounded input handling and cancellation; real browser checks exercise report parity, offline use after loading and long-query layouts.

Inspect the execution: candidate-aware scoring narrows conjunctions and avoids scoring exclusions while preserving exact rankings and explanations. Paired worker-process measurements retain every raw trial, including slower cases and a broad-query control; their engine hashes identify the measured revision.

Challenge the vector arithmetic: aligned extreme vectors once returned NaN or zero and could lose top1 to an orthogonal document. The numerical boundary checks compare 96 synthetic cases with an independent high-precision reference, test mutation and snapshot round trips, and refuse sparse arrays atomically. Near cancellation and rounding still limit precision; the older timing report is not a measurement of this later numerical revision.

TypeScriptBM25FViteVitest
AblationnDCG@10
BM25F with stemming and field boosts
0.927
Without stemming
0.904
Hybrid with toy trigram vectors (RRF)
0.935

60 glossary entries and 22 queries labelled by the author. The table demonstrates the evaluation harness; it is not a benchmark.

Review the code Try to inject markup

Run the boundary

From a searchlite checkout after npm ci, run the two focused tests.

npx vitest run test/engine/highlight.test.ts test/engine/scoring.test.ts -t "hostile|exactly the score"

Expected: 2 passed, 47 skipped. Hostile document text cannot inject HTML, and every explanation adds up to the returned score.

Follow the decision

Read highlight.ts. Document text is escaped before <mark> tags are inserted, so those tags are the only markup that can reach the page.

The index lives in memory and analysis is English-only: suited to small collections, not millions of documents.

06 Accessibility and HCI

contrastkit

WCAG 2.2 contrast checks, perceptual OKLCH fixes for failing pairs, color-vision-deficiency simulation and palette audits: a zero-dependency TypeScript library, a CLI that can gate design tokens in CI, and a React app.

A fix keeps the hue and changes only OKLCH lightness, searching both directions for the closest passing color. Ratios are truncated, so a near miss never displays as a pass.

Complete workflow: download the current palette as tokens JSON, audit it with the CLI, apply the suggested fix and audit the next download. The web-to-CLI walkthrough preserves the selected pair and target; simulations do not change exported colors.

TypeScriptReactOKLCHWCAG 2.2
Checked againstStandards and properties
WCAG 2.2 formula
Black on white is 21:1; #777777 on white is 4.47:1 and fails AA for normal text.
Published matrices
Color-vision simulation follows Machado, Oliveira and Fernandes (2009).
Fix properties
Every suggested fix meets its target and stays inside the sRGB gamut.
The app itself
Component tests include automated axe accessibility checks.

WCAG 2 contrast is a coarse legibility model, and simulation approximates dichromacy.

Review the code Fail a gray that looks fine

Run the boundary

From a contrastkit checkout, run npm ci and npm run build, then check the pair.

npx contrastkit check 777 fff

Expected: 4.47:1, failing AA for normal text, with exit code 1 so a CI job stops on it.

Follow the decision

Read suggestFix in fix.ts. It binary-searches OKLCH lightness in both directions, maps each candidate into gamut, and ranks passing colors by their distance from the original.

Contrast is one input to legibility. Font size, weight and context still need human review.

07 AI engineering

citecheck

Question answering over a document collection with the Claude API. Passages come from a SQLite FTS5 index ranked with BM25, the model must cite a retrieved passage for every sentence through a strict tool schema, and the app abstains without a model call when nothing relevant is found.

A checker rejects citations to passages that were never retrieved and flags any sentence whose numbers or content words are missing from the passages it cites.

A boundary worth checking: headings inside fenced code examples keep their enclosing section citation. Real index and retrieval regressions exercise shorter or invalid closing fences and the genuine section that follows.

PythonClaude APISQLite FTS5FastAPI

Tests and the bundled evaluation replay hand-written synthetic responses, so no API key is needed and no real model output is claimed. Live mode uses your own key.

Checked againstIts own sources
Retrieved passages
Every citation must point to a passage that was actually retrieved.
Cited text
A number missing from the cited passages makes a sentence unsupported, however many words match.
Human labels
On the fixtures the checker flagged 5 of 7 sentences labelled unsupported, and 2 of 42 labelled supported.
Abstention
Questions the handbook cannot answer should get no answer.

Word overlap is not entailment: a negation flip passes the checker, and a test pins that limitation.

Review the code Catch an invented number

Run the boundary

From a citecheck checkout with uv installed, index the fictional handbook and ask a prepared question. No API key is needed.

uv run citecheck index examples/handbook
uv run citecheck ask "What is the deadline for submitting an expense?"

Expected: the first sentence is supported (9 of 9 content words found); the second is flagged unsupported because "60" appears in no cited passage. No API call is made.

Follow the decision

Read verify.py. Citations outside the retrieved set are rejected first; support is then the share of a sentence's content words found in its cited passages, and any number missing from them overrides a high score.

The checker catches invented numbers and unsupported wording, not reasoning errors. Its evaluation set is small, fictional and labelled by one author.

How to review the work

Check the reference, then the code

1. Run

Reproduce one check

Each section gives one focused command and its expected result. No paid service or account is needed.

2. Compare

Find the reference

Textbook values, published tables, reference libraries and accounting invariants come from outside the code they check.

3. Weigh

Read the limits

Each README and design note states what the project does not do and what its evidence does not establish.

These independent, AI-assisted portfolio builds connect finance, statistics, data, interface and applied AI work with the habits I care about: explicit contracts, reproducible checks and stated limits. They are educational projects with synthetic or fictional data and make no claims of production use or adoption.

Back to all projects