Dan Lichtenberger · New York Six systems, and what
each one actually proved.

I build things that make a measurable claim — and then I try to break the claim. Three of the projects below failed to beat their baseline, and they say so, with the confidence intervals that show it.

Built May to October 2026 and ordered strongest first. Every project is deployed to a public URL, and every screen below is a recording of the real deployment, not a mockup.

6 / 6
projects deployed to a public URL — not just a repository
1,337
automated tests running in CI across five of the codebases
24 weeks
from the first commit of the oldest project to the most recent
3
report null or negative results — the finding is that it didn't work
01Start hereTry it live
Research study Reinforcement learning Live dashboard + LaTeX paper

How Reinforcement Learning Agents Fool You

PPO implemented from scratch, then pointed at a deliberately brutal testbed — financial markets — to ask one question: does an agent that looks brilliant in a single run survive being run five times?

TakeawayA +275% single run was luck: across 10 seeds the edge over buy-and-hold is not significant (p = 0.79).

Screen recording · the live lab The Agent Playground, running in the browser. A trained policy steps through real BTC-USD bars while equity, target exposure, per-bar reward and drawdown stream in together — with transaction cost and slippage as live inputs. Every run ends with a reproducibility receipt naming the experiment, the data source and the settings.
The run I nearly published
+275% vs. +19% · superseded v1, commit d4c0ef9
Rebuilt, 10 seeds
+57% vs. +34% buy-and-hold · p = 0.79
Domain randomization, held-out
−25% → +31% stocks, 10 seeds

Every seed, plotted against the benchmark it has to beat

Each dot is one training seed's return minus buy-and-hold over the same window, so the zero line means “tied with simply holding the asset.” The bracket is the 95% confidence interval of the mean.

-300% -200% -100% 0 +100% +200% ties buy-and-hold Crypto · 10 seeds mean +23% · 95% CI [-17%, +73%] · p = 0.79 Stocks · 10 seeds mean -259% · 95% CI [-266%, -251%] · p = 0.002

The crypto cloud straddles zero — its interval includes “no edge at all,” which is what p = 0.79 means. Every stock seed lands far left of it: that arm is not inconclusive, it is reliably worse than holding the index. A single dot from the right-hand tail, reported alone, would have looked like a discovery.

Table view
MarketWorst seedBest seedMean excess95% CIp
Crypto-62%+192%+23%[-17%, +73%]0.79
Stocks-276%-235%-259%[-266%, -251%]0.002

This is the project I'd point at first, and it is first for that reason. It is designed to produce a negative result honestly, and it does. The framework implements Proximal Policy Optimization with Generalized Advantage Estimation from first principles in PyTorch — gradient clipping, entropy bonus, orthogonal initialization, running observation normalization via Welford's method — over two custom Gymnasium environments sharing identical accounting and cost mechanics.

Screenshot · the finding The whole argument in one figure. Both arms were trained identically and scored on the same held-out paths; the only difference is how many assets they saw. The overfit arm's in-sample return of +4.0M% becomes −44% the moment it meets data it has never seen. Each dot is one seed. Scroll it sideways to read the detail.

Reported finding

No seed-robust edge over buy-and-hold. One favorable run returned +275% against a +19% benchmark, and that was the version I nearly wrote up as the finding — it is superseded, and the project site cites the commit (d4c0ef9, test window ending 2026‑06‑17) rather than quoting it as current. Rebuilt and rerun under ten seeds, individual returns ranged from −29% to +226%; the mean, +57% [+16%, +106%], cannot be distinguished from buy-and-hold's +34% (p = 0.79). The stock agent returned −19% [−27%, −11%] against buy-and-hold's +240% — significantly worse, at p = 0.002. Retrained across four separate slices of Bitcoin's history, it lost money in every one. A replication on ten fresh seeds, with its predictions committed before the run, held on all four counts (stock p = 0.002; crypto p = 0.88); the one figure that did not survive was the crypto mean's interval, which now includes zero. Separately, an agent trained on a single price path memorizes it catastrophically; training on a freshly generated synthetic path each episode flips held-out return from −25% to +31% on equities and from −44% to +187% on crypto — across ten seeds, with all four confidence intervals excluding zero. Reported as a distribution rather than a single run, because the in-sample figure that version quoted swings by an order of magnitude between seeds. The result reproduces Henderson et al. (2018) on the unreliability of single-run RL evaluation, in a domain where the incentive to publish the lucky seed is strongest.

Method
  • Observation space. 28 engineered features spanning momentum horizons, trend indicators, oscillators, volatility-regime signals, volume microstructure and cross-asset context. Continuous action targeting a position fraction (long / short / flat).
  • Leakage control. Chronological train/validation/test splits with feature scalers fit on training data only.
  • Statistics. Bootstrap confidence intervals across seeds, paired permutation tests against buy-and-hold, rolling multi-fold walk-forward evaluation. Results are reported as distributions, never single runs.
  • Replication. The harder cross-sectional problem — allocating portfolio weights rather than timing one asset — reaches identical conclusions.
  • Deployment. The trained policy is exported to NumPy archives so the inference API runs with no PyTorch dependency.
PyTorch 2.xGymnasiumNumPyMatplotlibLaTeXVercelRender
02Shipped productTry it live
Developer tool Post-quantum cryptography quantumsafescan.com

QuantumSafe Scan

Finds the encryption inside a codebase that quantum computers are expected to break, scores how exposed the project is from 0–100, and names the specific NIST-standard replacement for every finding.

TakeawayUsage-aware scanning reaches 100% precision on its own benchmark (naive matching: 49.1%) and 93% on a random, hand-labeled sample of real-world findings.

Screenshot · a real scan A live scan, no account needed. Paste a snippet and the engine scores it, flags each at-risk primitive with its line number, and names the FIPS 203/204 replacement. The same engine runs against a whole repository or an uploaded .zip. Scroll it sideways to read the detail.
Precision on labeled benchmark
100% vs. 49.1% naive baseline
Languages parsed
11
Real-world audit
5,609 findings, 11,275 files

The interesting problem isn't matching the string RSA — it's not matching it inside a comment, a docstring, or a code path nothing ever calls. A hybrid AST-and-regex engine with usage awareness and reachability ranking eliminated 27 false positives the naive pattern-matching baseline produced, taking precision from 49.1% to 100% on an 18-file, 9-language benchmark, with 100% recall on 50 seeded cases under mutation testing. Because that benchmark is the one it was tuned on, precision was also measured on 100 findings drawn at random from real PyPI packages and labeled by hand: 93% (95% CI 86–97%). Four of the seven misses were one rule flagging the flags that turn old TLS off; that rule is now fixed.

Method
  • Attack. Shor's and Grover's algorithms implemented on the Qiskit simulator, so the HIGH risk rating on RSA, ECC and Diffie-Hellman is demonstrated rather than asserted.
  • Detect in code. Cross-language AST + regex scanning with dependency analysis; results export to JSON, HTML, SARIF, CycloneDX CBOM and SVG badges so it drops into an existing CI pipeline.
  • Detect on the wire. A website's quantum exposure is in its TLS handshake, not its source. TLS 1.3 removed the key exchange from the cipher-suite name and Python's ssl cannot read the group, so the scanner builds a ClientHello by hand: it offers post-quantum hybrids but a key share for X25519 only, so a server preferring a hybrid has to name it in a HelloRetryRequest. It also reads every signature in the certificate chain — Cloudflare's leaf is modern ECDSA, but an intermediate above it is signed with RSA, which a leaf-only check misses.
  • Prioritise. A severity score says how much vulnerable cryptography exists, not whether you will finish migrating in time. Mosca's inequality — secrecy lifetime plus migration time, against time-to-quantum — turns it into a deadline. No default is supplied for the quantum date, because nobody knows it; the tool reports the break-even year instead, which needs no such guess.
  • Fix. A lattice-based (LWE) key-encapsulation mechanism written from scratch — the underlying math of CRYSTALS-Kyber / ML-KEM — rather than a library call.
  • Field results. Paramiko, the standard Python SSH library, scores 100/100 Critical with 110 findings. Across 37 PyPI packages, 32 (86%) contained at least one finding. In a study of 8 widely-used projects, 88% contained HIGH-risk usage, mean score 61.4/100.
  • Assurance. 559 automated tests in continuous integration, plus a baseline mode that fails a build only on new findings — so a project with an existing backlog can ratchet it down instead of disabling the check.
PythonQiskitFlaskSQLite / PostgresJWTDocker ComposePyPI
03Statistical rigorTry it live
Live system Statistical validation Probabilistic forecasting alphaglyph.org

AlphaGlyph

A backtesting and forecasting laboratory built on the assumption that its own results are wrong until they survive the statistical tests institutional quant funds use to separate skill from luck — including the forecast it makes about tomorrow.

TakeawayThe best backtest in a 42-variant search is more likely overfit than not (72%), and the forecaster scores a coin flip (AUC ≈ 0.51).

Screen recording · a real backtest “Watch it trade” — the backtest replayed day by day. The equity curve grows against the SPY benchmark while each trade streams in with a plain-English reason (“bought because the stock was trading near its 52-week low”) and its realised P&L. Nothing about the run is hidden behind a single summary number.
Forecaster test AUC
≈0.51 a coin flip
In-sample Sharpe surviving OOS
~52% median
Tests in CI
192

Probability that the best backtest in the search is fooling you

Combinatorially-symmetric cross-validation over a 42-variant trend and mean-reversion grid on SPY. Above the 50% mark, the in-sample winner is more likely than not to land in the bottom half out-of-sample.

0%50% · coin flip100%

72% on the run of 2026-08-01. An earlier run of the identical procedure returned 63% — same code, same grid, same ticker, a different window. The drift is the point: a single PBO quoted as a constant is the exact error the metric exists to catch.

Anyone can produce a backtest with a good-looking equity curve. This one runs day-by-day simulations with walk-forward cross-validation, rolling Kelly position sizing with no look-ahead, transaction costs, regime tagging and an SPY benchmark — then subjects the output to Probabilistic and Deflated Sharpe ratios, Fama-French factor decomposition, cost-sensitivity sweeps, and an append-only forward ledger that grades live predictions after the fact so past calls can't be quietly revised.

The other half of the system points forward. A 106k-parameter multi-modal transformer — deliberately tiny, because daily market data is small data drowning in noise — reads 60 days of 13 features and emits three things: the probability of an up-move, a q10–q90 distribution of the return built so the quantiles can never cross, and a volatility forecast. A Predictor view blends that price channel with post-earnings drift and news sentiment, weights each by its own confidence, and lowers the combined confidence when the channels disagree. That is the part I'd defend: the useful output of a forecaster isn't a number, it's a calibrated distribution and an honest confidence attached to it.

Reported finding

Run over a 42-variant trend and mean-reversion grid on SPY using Combinatorial Purged Cross-Validation, the platform reports a 72% probability of backtest overfitting — the in-sample winner lands in the bottom half out-of-sample more often than not — and a median of only ~52% of in-sample Sharpe surviving out of sample. An earlier run of the identical procedure returned 63% and ~38%: same code, same grid, same ticker, different window. That instability is itself the finding, and the report says so rather than quoting whichever number flatters the system. The ML forecaster's test AUC of ≈0.51 on next-week direction is, correctly and publicly, a coin flip. The one clean positive: a dip-buying strategy on AAPL/MSFT in ranging markets, January 2021 to January 2024, returning +21.6% against SPY's +5.6%. One more year of data shrinks that edge to +3.6 points, and the report says so.

Method
  • The Data-Mining Lab. Generates hundreds of random long/flat strategies that have no edge by construction, keeps the luckiest, and shows the naive statistics calling it significant while the Deflated Sharpe correctly calls it noise. The gap between those two numbers is the argument the whole system is built on.
  • Architecture. The browser holds all UI state and the backend is a pure compute layer; the ML model is trained offline and shipped as a portable ONNX artifact — ~40 MB of ONNX Runtime instead of PyTorch's 700 MB, which is what lets it serve inside a 512 MB free tier. 15 API endpoints, five of them forward-looking rather than backtest-looking.
  • Forecast honesty. Calibration scores the model only on data after its validation cutoff — a claim of “60% up” is worthless unless the market rises about 60% of the time it is said. Test AUC comes back at ≈0.51 and the page says so. The forward ledger then logs every live prediction at the moment it is made and grades it once the horizon elapses, append-only, so the hit rate cannot be refitted in hindsight.
  • What the shipped model can't do. The committed checkpoint was trained with the macro and news blocks zero-filled, so it currently predicts from price alone; the multi-modal plumbing is real and tested, and a retrain activates the other two channels with no code change. The metadata file shows it — those five features carry a standard deviation of exactly zero.
  • Indicators from first principles. SMA, EMA, RSI, MACD, ADX and Bollinger Bands implemented directly rather than pulled from a TA library, across six strategies plus a no-code custom rule builder and a regime-detecting adaptive mode.
  • Data resilience. A three-stage cascade — Tiingo, then yfinance, then Stooq — so a single upstream outage doesn't take the system down.
  • Pipeline. GitHub Actions running 192 pytest cases, flake8 linting and smoke tests on every push.
Python 3.11Flask 3pandasSciPy (SLSQP)ONNX RuntimeChart.jsPostgres
04Play it yourselfTry it live
Research study Reinforcement learning rlchess.xyz

AlphaZero-style Chess Engine

An engine given the rules of chess and nothing else — no openings, no human games, no evaluation function — that learns entirely from playing itself. You can play the trained network in your browser.

TakeawayThe plateau was a training-signal problem, not compute: 10× more self-play moved Elo from −21 to −7; a material signal reached +28.

Screen recording · self-play in the browser The network playing itself, client-side. Candidate moves and their evaluations appear on the left as the search runs; the move list and captured material fill in as the game goes. The same page lets you take White and play the trained network yourself.
Self-play games trained
1,000 ~9.8h on 2×T4
Elo vs. random baseline
−21 → +28
Policy head
4,672 move logits

A dual-headed ResNet reads an 18×8×8 position tensor and emits both a move policy and a position value in [−1, 1]; PUCT Monte-Carlo tree search balances the running value estimate against the network's priors; self-play generates the (position, search policy, outcome) tuples it then trains on. The browser opponent runs the exported network client-side through ONNX Runtime Web.

Reported finding

Scaling self-play 10× barely moved the needle: 92% of games were still non-decisive and Elo went from −21 to only −7. Adding a material signal reached +28. The diagnosis in the writeup is that this is “a self-play signal problem, not a compute ceiling” — draw-heavy self-play starves the value head of gradient. The classical alpha-beta engine built as a control beat the random baseline 10–0, confirming the harness itself was sound.

Method
  • Modular by component so each piece is testable in isolation: chess_game.py (rules and encoding), model.py (network), mcts.py (search), self_play.py / training.py (the learning loop), evaluation.py (strength measurement).
  • Trained with cross-entropy loss against the MCTS visit distribution and mean-squared error against the final game outcome.
  • Three ways in: a browser UI, a console client, and a FastAPI HTTP API.
  • 39-test pytest suite on GitHub Actions, including an ONNX-vs-PyTorch parity check on the exported network.
PyTorchPython 3.10+FastAPIONNX Runtime Webchessboard.jsVercel
05Real-time infrastructureTry it live
Live system Real-time infrastructure tradeski.dev

Tradeski

A real-time market dashboard — streaming prices, Federal Reserve data, news sentiment and an assistant that answers questions about your portfolio rather than the market in general.

TakeawayEvery indicator hand-coded on NumPy and streamed live, with an assistant that reasons about your actual holdings.

Screenshot · the live dashboard The terminal in normal operation. Streaming ticker strip, seven FRED macro series, candlesticks with hand-rolled overlays, and a live indicator panel — every quantitative figure computed on raw NumPy arrays rather than a TA library. Scroll it sideways to read the detail.
Symbols tracked live
11 ticker + correlation matrix
Fed series, hourly refresh
7 via FRED API
Tests in CI
112 across 14 files

The engineering interest here is everything around the charts. Every quantitative indicator — SMA, EMA, RSI with Wilder smoothing, MACD, Bollinger Bands, ATR, stochastic, Z-score, volatility, linear regression — is hand-coded on raw NumPy arrays: no TA-Lib, no pandas. A persistent WebSocket backend feeds a fully static frontend, with a caching layer tuned per data source (prices 5 minutes, news 30, FRED and correlations an hour) and per-route rate limiting to keep third-party API budgets intact.

Method
  • Risk metrics. Sharpe ratio, beta against the S&P 500 and annualized volatility computed from a year of daily returns weighted by current market value, plus a 90-day pairwise return-correlation heatmap across all tracked symbols.
  • Sentiment. VADER extended with roughly 30 finance-specific terms — beats → +2.0, bankruptcy → −3.0 — because general-purpose sentiment models misread earnings language.
  • Assistant. Grounded in the user's live macro, portfolio and news context before the model is called, so answers are about their actual holdings.
  • Security. PBKDF2 password hashing, signed bearer tokens, and per-user row scoping isolating portfolios, alerts and watchlists.
  • Screener. A 29-symbol universe — 27 stocks across five sectors plus SPY and QQQ — fetched concurrently through an 8-worker thread pool.
Python 3.12Flask-SocketIOgeventPlotly.jsFRED APINewsAPIClaude Haiku 4.5Postgres
06Hackathon pieceTry it live
Artwork Hack the Arts 2026 Runs in your browser

Neural Canvas

Paint glowing light in mid-air with your bare hands. No controller, no mouse, no stylus — a webcam, a neural network, and a pinch of your fingers.

TakeawayHand tracking as the medium: 21 3D landmarks per hand at ~60 frames a second, entirely on-device, in one HTML file.

Screen recording · the app's idle demo The brush engine, in the app's own idle mode. With no hand in frame the canvas drives itself, which shows the rendering honestly: one stroke path mirrored into six-fold symmetry, additive blending, shadow-blur glow and a slow fade. With a webcam, that path is your fingertip.
Hand landmarks tracked
21 per hand, in 3D
Inference rate
~60/s fully on-device
Build step
None one HTML file, no server

Built for Hack the Arts 2026, whose theme was art that wouldn't exist without technology — so the hand tracking isn't a control scheme bolted onto a paint program, it's the medium. MediaPipe Tasks Vision locates 21 3D points per hand about sixty times a second entirely in the browser; a one-euro filter smooths the jitter out of raw landmark data so strokes stay clean; Canvas 2D renders them with additive blending and shadow-blur glow. Nothing leaves the device.

Method
  • Gesture vocabulary. Pinch to paint; other hand positions erase, change color and clear the canvas — no UI chrome between the person and the drawing.
  • Brushes. Neon glow, ribbon, spray, comet and embers, each a different stroke-accumulation model.
  • Symmetry. Kaleidoscope mirroring at 2, 4, 6 and 8 folds, applied as a transform on the stroke path.
  • Extras. Full-body pose painting, an attract mode for unattended displays, and save/record output.
  • Constraint. Pure HTML, CSS and JavaScript in a single file — no framework, no build, no backend. MediaPipe runs via WebAssembly and GPU.
JavaScriptMediaPipe Tasks VisionWebAssemblyCanvas 2DOne-euro filter