Documentation

Compound Strategy Lab is an educational financial-systems simulator. This page explains what the product is designed to help you explore, how each piece works, the equations behind it, and — just as importantly — what the model leaves out.

Purpose

Compound Strategy Lab helps people visualize and test financial strategies. It represents income, accounts, investments, debts, expenses, and cash flows as one connected system so a user can change one part and observe how that decision affects everything linked to it.

The product is built for questions such as: What happens to the rest of the strategy if more cash goes toward debt? How does reinvesting income change the long-term result? When does internally generated income begin funding more of the system? How sensitive is the outcome to a different return, yield, fee, expense, or time horizon?

It does not solve a user's financial life, choose investments, or determine the correct strategy. It provides a controlled place to explore assumptions, compare alternatives, and understand compounding before making real-world decisions. Every result remains an estimate produced by the inputs and model described in these docs.

General overview

The site contains two complementary simulation tools:

Use the Compound Visualizer to isolate the basic mechanism of compounding. Use the Strategy Lab to explore how that mechanism behaves inside a larger financial system where contributions, income, withdrawals, debts, expenses, and reinvestment affect one another.

Both tools run entirely in your browser. The Strategy Lab autosaves its diagram to this browser's local storage; the current product does not upload the strategy or connect to financial accounts.

Compound Visualizer

Compound interest

Interest is added to the balance, and then itself earns interest. With monthly compounding:

balance ×= (1 + rate/12) each month

Daily compounding uses (1 + rate/365)^365/12 per month. Annual compounding credits the full rate on last year's balance once a year, plus half a year's rate on that year's contributions (they were in the account for six months on average).

Simple interest

Interest accrues only on the money you put in — past interest never earns anything, so growth is a straight line:

interest each month = contributions so far × rate/12

The money stacks

A US bill is about 0.10922 mm thick, so a strap of one hundred $100 bills ($10,000) stands about 1.09 cm tall:

stack height = (balance / $100) × 0.10922 mm

$1M ≈ 1.09 m, $100M ≈ 109 m, $1B ≈ 1.09 km. The comparison objects (coffee cup through Mount Everest) are drawn at their true heights against the same scale.

Strategy Lab basics

The Lab simulates your diagram in monthly steps over the chosen horizon. Two kinds of money movement exist, and the distinction drives everything:

Routing rules

Assets, debts and money spent

Not every node is something you own. Each one counts toward the headline figure in one of three ways, and the inspector says which:

This changes what "Contributed" and "Return" mean, in three ways worth knowing:

Employer match is deliberately excluded from contributed as well — it is return, not savings. A 50% match is an immediate 50% gain on the money, and showing it that way is the honest presentation.

Switching things on and off

Monthly Income, Pension / Social Security and Expenses / Spending carry an Active from year / Active until year pair (0 means "from the start" and "never stops"). Outside that window the node produces nothing, and arrows pointing into it carry nothing. Everything else on the canvas is assumed active for the whole horizon.

That single control is what draws most retirement shapes: a paycheck that stops at year 12 is Coast FIRE; a benefit that starts at year 25 is Social Security; a spending sink that begins at retirement is a drawdown. Income nodes also take an annual raise, and spending sinks a cost inflation that grows any fixed-dollar arrow feeding them — leaving that at 0 models a retiree whose costs never rise, which is comfortable on the chart and wrong in life.

Because a node window lives on the node, two things that share a node but happen at different times need arrow timing instead. Every arrow has its own Active from year / Active until year pair, and the distinction between the two is worth holding onto:

The two behave differently on purpose. When a node goes dormant its share cascades to sibling arrows (the debt-payoff snowball). When an arrow is paused, its share does not cascade — it simply reinvests in place — so switching one arrow off is a clean way to stop a single transfer without disturbing the rest of the diagram. A retirement drawdown that begins at year 30 is an arrow with Active from year 30; a mortgage overpayment that you expect to stop once the loan clears takes care of itself, because the debt node goes dormant on its own.

Taxes and inflation

Two switches sit above the results chart. Both are display-only lenses — they re-skin the numbers you already simulated rather than re-running anything, and both are off by default so the raw projection is what you see first.

They are intentionally crude, and the honest limitations matter:

For a genuine tax projection you would need account-level treatment, realization on sale, and brackets — a larger system than a single rate. The toggle is here for a ballpark on the drag, not a tax return.

Contribution caps and the employer match

The Tax-Advantaged Account node wraps an investment in a 401(k), IRA, HSA or 529. Picking an account type fills in that wrapper's annual contribution limit and tax treatment, both of which stay editable. The cap applies to everything arriving in a plan year — the node's own monthly contribution and anything routed down its arrows — and contributions above it are turned away rather than carried over. The node summary reports how much was blocked; blocked money is not counted as contributed, so the return isn't penalised for a deposit that was never accepted.

Tax treatment is recorded but not yet simulated. Every projection on this site is pre-tax, so a Roth and a pre-tax account currently grow identically here. In reality a pre-tax dollar is worth roughly 1 − your retirement rate when it comes out. Read the field as a label on the diagram until tax drag ships.

The 401(k) + Employer Match node adds free money on top of every contribution it receives, up to a monthly cap. Employer matches are normally quoted against salary ("50% of the first 6%"), so convert: on a $75,000 salary that caps the match at 75,000 × 6% × 50% ÷ 12 ≈ $188/month.

Yields don't last forever

Some income-bearing nodes include a yield decay in %/yr: APR(t) = APR₀ · (1 − decay)^t. This is useful anywhere a headline yield is unlikely to last: bank rates change, business margins compress, and high-yield credit spreads mean-revert. It is especially important for pools, lending and staking, where fee APRs can compress as capital arrives and reward schedules dilute. At 15%/yr, a 40% APR is about 21% by year five. The field defaults to 0 so nothing changes silently; use it when a constant income assumption would flatter the plan.

Editor

Reading the results

Flywheel momentum

The momentum strip turns “flywheel” into a measurable property rather than a name. It compares the capital you supplied with income the assets themselves generated over the trailing 12 months, then reports how much of that income acquired more productive assets. Routing costs are deducted. Money sent to spending, debt, or an idle treasury remains part of the strategy, but does not count as productive reinvestment.

Templates

The template picker loads a worked example onto the canvas. Each one carries a plain-English brief — what it does, what it takes on faith, and where it breaks — shown in the report-card panel whenever nothing is selected. They are illustrative settings, not recommendations. The numbers are there to be changed; a template you don't edit is just someone else's guess.

Flywheels are reserved for systems where income-producing assets generate cash that acquires more productive assets and can be measured against the owner's continuing contributions. Other useful examples are labeled honestly as portfolios, foundations, debt systems, income systems, retirement plans, goals, or cautionary examples. A compounding cascade can still be effective; it simply is not presented as a closed, self-reinforcing flywheel.

The Rental Acquisition Flywheel uses continuously purchasable real-estate/REIT equity as a proxy. Real buildings are discrete purchases with financing, closing costs, and reserve thresholds; the current engine does not simulate that acquisition event.

One of them, Narrow Range Trap, is deliberately a losing strategy. A ±5% range on ETH, left un-managed, sits in range only about 12% of a five-year horizon, so its advertised 120% APR is collected barely a tenth of the time — nowhere near enough to cover a ~46% impermanent loss. It exists because a gallery where every example wins teaches the wrong lesson.

TemplateTypeHorizonWhat it doesMain risk

Node reference

Parameters, default equations, and per-node caveats. Generated from the same definitions the simulator runs, so this list can't drift out of date.

Advanced crypto & DeFi mechanics

These reference notes cover the optional cryptocurrency nodes. They are kept separate from the core Lab guide because the same routing, compounding, risk, and reporting rules apply to traditional-finance strategies without them.

Classic AMM math (Uniswap v2 style)

A 50/50 constant-product pool holds two tokens, A and B. As prices move, arbitrage keeps rebalancing the pool, so the position's value tracks the geometric mean of the two token price factors:

position value ×= √(fA · fB) each month

where fA, fB are the tokens' monthly price factors. Trading fees and incentives arrive separately as routable income (the LP APR).

Impermanent loss

Because the pool keeps selling the outperformer, an LP position is worth less than simply holding the two tokens whenever their price ratio moves. With r = the change in the A/B price ratio:

IL = 2·√r / (1 + r) − 1
Price ratio changeImpermanent loss vs HODL

IL is symmetric in direction — a ratio of 2× and ½× lose the same amount — and it's "impermanent" only if the ratio returns to where you entered.

Volatility drag

Even with no net price trend, volatility alone bleeds an AMM position, because √(fA·fB) is concave. What matters is the volatility of the A/B ratio, which depends on how much the two tokens move together (their correlation ρ). The size of that bleed over a year is roughly:

drag ≈ (σA² + σB² − 2ρ·σA·σB) / 8 per year

An ETH/stablecoin pool at 70% ETH volatility loses about 6.1%/yr to this — the fee APR has to clear that hurdle before the position beats holding. Correlation is why volatile/volatile pairs can still be gentle: ETH and BTC each swing hard, but at ρ ≈ +0.8 their ratio barely moves, so an ETH/BTC pool suffers a fraction of the IL that the individual volatilities suggest. This is the intuition behind the impermanent-loss figure you set on the node; when a new pool seeds its starting estimate, this is the calculation it uses, with ρ and the volatilities auto-filled from the last 90 days of real data.

Concentrated liquidity (Uniswap v3 style)

Instead of spreading liquidity over every price, you pack it into a range around your entry price. You set the range as absolute min/max prices (exactly as a pool UI shows them) or as a percent below/above the current price. The range can be asymmetric, so it's described by two bounds relative to the entry price P₀:

a = √(P_min / P₀), b = √(P_max / P₀)

A full-range position is the limit a → 0, b → ∞; a symmetric geometric range is b = 1/a.

Capital efficiency

Inside the range, the same dollars provide more liquidity and earn proportionally more fees than a full-range position (Uniswap v3 whitepaper):

efficiency E = 2 / (2 − a − 1/b)
Range widthFee efficiency vs full range

The catch: impermanent loss is amplified by roughly the same factor, and outside the range you earn no fees at all.

Concentration multiplies your edge. Fees and impermanent-loss drag are both amplified by the same factor E, so a fair comparison must scale fees with concentration: if the full-range pool out-earns the volatility drag, a tighter range multiplies the profit — if it doesn't, it multiplies the loss (plus range-management work). The Lab asks for the total in-range APR of your actual position, as your pool dashboard shows it — so when you compare a concentrated range against a classic pool, remember the same pool's concentrated APR is roughly E× its full-range APR, and enter each number accordingly. Typing the same APR into both compares two different pools and makes concentration look unconditionally bad.

Position value and impermanent loss

With r = the A/B ratio change since entry, the position's value in token-B terms, relative to entry, follows:

in range: V(r) = (2√r − a − r/b) / (2 − a − 1/b) above range: V = (b − a) / (2 − a − 1/b) (100% token B) below range: V = r·(1/a − 1/b) / (2 − a − 1/b) (100% token A)

Impermanent loss is that value divided by what the tokens you actually deposited would be worth at the same price — not by a presumed 50/50 hold. The deposit's own hold factor is:

H(r) = (r·(1 − 1/b) + (1 − a)) / (2 − a − 1/b) IL = 1 − V(r) / H(r)

A concentrated deposit is generally not 50/50. It is half-and-half only when the range is geometrically centred (b = 1/a, i.e. √(P_min·P_max) = P₀), where H collapses to (1+r)/2 and this reduces to the classic formula. A range entered as ±30% is arithmetically symmetric, not geometrically, and actually deposits about 43% of the volatile token — dividing by (1+r)/2 there, as many simplified calculators do, mis-states the loss. See the verification section for how this is checked against external references.

Range management

  • Re-center monthly — an active LP moves the range to follow the price. Fees keep flowing, but every move locks in that month's impermanent loss permanently. Under high volatility this realized-IL bleed can overwhelm even large fee APRs.
  • Set and forget — the range is fixed at entry. If the price drifts out, fees stop and the position sits 100% in one token until the price comes back. The inspector estimates when the assumed drift walks out of your range.

Exact-mechanics cross-check

The Lab's closed-form position curves are cross-validated in your browser, on every load, against a contract-exact port of Uniswap's whitepaper accounting — liquidity minting, token composition, and position value, as implemented by the uniswappy reference library (DeFiPy). The two agree to machine precision across symmetric, asymmetric, and razor-thin ranges; the same exact math powers the "exact split at entry" readout in the LP inspector.

How the expected line handles this

The pool projection is net of holding: token prices are held flat, so "holding" stays at your deposit, and the position ends at (1 − your expected impermanent loss) of that hold, plus fees earned at your APR × fee uptime. Impermanent loss and fee uptime are inputs you own (see below), so the expected line is a clean, deterministic point estimate rather than an opaque simulation. Range re-centering is charged as a real per-move cost, and for a trigger range the number of re-centers per year is derived from the pair's volatility and range width (a driftless walk crosses a log-distance d in expected time d²/σ², so crossings scale as σ²/d²).

The risk band is where uncertainty enters: each of the 200 Monte Carlo runs draws its own realized impermanent loss and fee uptime from a mean-preserving spread around your estimates, so the band brackets the expected line instead of collapsing onto it — and if you assert zero IL and perfect uptime, there is genuinely nothing left uncertain, the band collapses, and the report card says so rather than awarding a pass.

Monte Carlo

The band reruns your whole strategy 200 times with random monthly price shocks (geometric Brownian motion). Each asset's monthly factor is:

factor = exp( (μ − σ²/2)/12 + σ·√(1/12)·z ), z ~ N(0,1)

where μ = ln(1 + growth) and σ is the node's volatility. This keeps the average yearly growth equal to your input while volatility spreads the outcomes. The runs use fixed random seeds, so the band is stable — it only changes when your inputs do.

Backtesting & decision tools

Whole-strategy historical replay

Choose Historical backtest when you want to see how the strategy would have behaved through a real, continuous stretch of market history. The Lab starts at the beginning of the selected window and moves forward one month at a time until its last complete month. A five-year window is a five-year replay, not a five-year sample projected out to your usual horizon.

Every eligible asset follows the same calendar. If the window ends in July 2026, a five-year replay begins in July 2021: its first simulated month uses the real July-to-August 2021 return, then August-to-September 2021, and so on through July 2026. When stocks, gold, ETH, or BTC moved together in a particular historical month, the replay shows that same relationship. The risk band resamples blocks of those same shared months, preserving the market regimes and cross-asset correlation that actually occurred.

ETH and BTC use bundled CoinGecko history. Stock Index uses VTI or SPY, International Equity uses VXUS, and Gold uses GLD monthly adjusted-close total returns generated from Alpha Vantage. Adjusted close already includes splits and reinvested distributions, so the Lab ignores those nodes' entered growth and volatility in historical mode and does not add a second dividend.

Some nodes intentionally keep their assumptions. Dividend stocks, bonds, REITs, savings, and other income-routing nodes continue using the values you enter. Their income needs to travel through the arrows, while an adjusted-close return already includes distributions; combining both would count the same income twice. The inspector tells you whether each node is replaying history or using its assumption.

A historical replay is evidence, not a forecast. The available window is limited to dates shared by every historical asset on the canvas. Funds start at their real inception dates; the Lab does not invent earlier proxy history or fill missing months. If you want a 20-year forward-looking range, switch back to Assumed rates. A future feature may use historical periods as scenarios for longer projections, but that would be labeled a projection rather than presented as a literal backtest.

Fee-uptime backtest

The fee-uptime measurement lives inside the Edit Pool Simulator. It replays real bundled prices — CoinGecko daily closes for ETH and BTC, USDC pegged at $1 — against the exact range you set, then reports the share of days the price actually sat inside it. To reduce entry-timing luck it uses a rolling-anchor method: a fresh range of your width is planted on every day of the selected 30 / 90 / 180 / 365-day window, each is followed for a month, and the results are averaged. It is a useful input for your pool estimate, but it describes one recent market regime rather than your whole holding period; fee uptime therefore defaults to a figure you can set yourself.

Scenarios & comparison

Save stores the current diagram as a named scenario; Compare runs any set of them side by side — overlaid expected and 10th-percentile lines plus a metrics table (contributed, expected, p10/p90, safe-layer share, house-money year, underwater verdict). The HODL twin option auto-builds a benchmark where every LP is replaced by simply holding its two tokens 50/50 — same contributions, no fees, no impermanent loss. Export/Import moves strategies as JSON files.

The report card

When nothing is selected, the right panel grades the current strategy. Every test grades something the projection actually simulates — a check that reads a parameter the engine ignores would be certifying a result it never measured, so where a test can't apply it abstains () and says why instead of passing by default:

Failing and abstaining rows carry a concrete next step. When the strategy contains a pool, the card says up front that token prices are held flat — so these grades are about fees versus impermanent loss, not about what happens if ETH halves.

Is the impermanent-loss math right?

Impermanent loss is defined here the standard way: the position's value divided by what the tokens you actually deposited would be worth, at the same price. It is checked four ways.

  • Against the whitepaper, symbolically. Both the position-value factor and the hold factor are derived from the Uniswap v3 amounts x = L(1/√P − 1/√pb), y = L(√P − √pa) and match the closed forms the code uses exactly.
  • Against contract-exact liquidity math, numerically. Build the position, price it at the exit price, divide by a plain hold — 40 range/move combinations agree to 1 part in 1013.
  • Against the published amplification factor. For a geometric range [P/n, P·n], concentrated impermanent loss is the classic v2 loss multiplied by exactly √n / (√n − 1), independent of how far the price moved — the factor quoted in the standard v3 analyses. A ±100% range (n = 2) amplifies IL 3.414×.
  • Against the v2 limit. Widen the range to (0, ∞) and the formula collapses algebraically onto 1 − 2√k/(1+k), which matches defipy/uniswappy's calc_iloss bit-for-bit.

One subtlety worth knowing, because it is easy to get wrong: a concentrated deposit is not 50/50. It is only half-and-half when the range is geometrically centred, √(pa·pb) = P. A range entered as ±30% is arithmetically symmetric, not geometrically, and actually deposits about 43% of the volatile token — so dividing by (1+k)/2 to get the hold value, as many simplified derivations do, misprices it. Where this tool disagrees with another calculator, that assumption is the first thing to check; the second is the benchmark, since "IL" is variously quoted against a hold of the deposit, a 50/50 hold, or a v2 position.

References used to define and verify the impermanent-loss math:

  • defipy-devs/uniswappy — reference Uniswap v2/v3 library (DeFiPy). Our v2 loss matches its UniswapImpLoss.calc_iloss bit-for-bit, and the v3 position/liquidity mechanics are cross-validated against its accounting on every page load. Worked tutorials: v2 IL, v3 IL.
  • Jiahua Xu et al., “Impermanent Loss in Uniswap v3” (arXiv:2111.09192) — closed-form derivation of the concentrated-liquidity loss and its amplification over the v2 case.
  • Peteris Erins (Auditless), “Impermanent Loss in Uniswap V3” — source of the √n/(√n−1) amplification factor we reproduce for a geometric range [P/n, P·n].
  • Uniswap v3 whitepaper / book — the liquidity and token-amount identities x = L(1/√P − 1/√pb), y = L(√P − √pa) our value and hold factors are derived from.

How a liquidity pool is modeled

Both pool types — classic (v2) and concentrated (v3) — run through one projection that answers a single honest question: did providing liquidity beat just holding the two tokens? Token prices are held flat, so "holding" stays at your deposit, and the position ends at (1 − your expected impermanent loss) of that hold, plus fees earned at your APR scaled by a fee uptime — the share of the horizon the position is in range and collecting (a full-range classic pool is always in range, so 100%). There is no separate "simple" and "advanced" toggle; the two inputs that used to distinguish them — impermanent loss and fee uptime — are just parameters you set.

  • Impermanent loss is yours. Type it in, or compute it with the built-in IL calculator: give it each token's price at deposit and at exit and it returns the loss for exactly that move, from the exact v2/v3 formulas, measured against your range. Four prices is all IL depends on — only the ratio between the tokens matters, so the dollar levels cancel (both tokens doubling is no loss). A brand-new pool is seeded with an estimate from its pair's 90-day volatility, so a fresh node never starts out claiming zero downside — but nothing recomputes it behind you afterwards.
  • Fee uptime is yours too, and defaults to a figure you set. A new pool is seeded with the share of recent real history the exact range actually held; you adjust from there. You can switch the source to the live backtest window, but it measures a 30-day regime, so over a multi-year horizon it describes one market rather than your whole holding period.
  • The risk band carries the uncertainty. The expected line is a deterministic point estimate, but each of the 200 Monte Carlo runs draws its own realized IL and uptime from a mean-preserving spread around your figures — so the band brackets the estimate instead of collapsing onto it. Assert zero IL and perfect uptime and there is nothing left uncertain: the band collapses and the report card says so rather than awarding a pass.
  • Range management is a cost, not a toggle. A concentrated position picks one of three styles — re-center monthly, re-center at trigger, or set and forget — each with a configurable cost per re-center. Re-centering realizes impermanent loss rather than avoiding it; what it buys you is fee uptime. For a trigger range the expected number of re-centers per year is derived from the pair's volatility and range width.

Every one of these settings lives in the Edit Pool Simulator panel; the node inspector shows them read-only, so what the node is simulating is always visible without there being two places to change it. A concentrated position is set up like a real pool position, modeled after defi-lab's Uniswap v3 simulator: min/max bounds as actual prices (or a percent of the current price) snapped to the pool's tick grid, an investment amount, and a fee tier. The panel is a full-screen dashboard — the asset-value-vs-price payoff curve with draggable range handles (against unbounded v2 and HODL 50/50), the token breakdown across prices, an impermanent-loss chart versus a selectable benchmark, and the fee-uptime backtest. Fee income can be estimated from pool stats (your share of daily fees is investment ÷ TVL, times volume × fee tier) or typed as a manual APR; either way it feeds the node's APR so the diagram and the panel always agree.

Caveats & limitations

Read this list before trusting any number on the site.

The verification page shows the browser-based calculation checks, with each test's executable assertion and runtime result. Schema, template, saved-data, and live-data safeguards still run in the background but are excluded because they do not validate a formula; the page is evidence that the implemented mechanics match its mathematical checks, not a promise that any projection will come true.

Glossary

APR
Annual percentage rate — a yearly rate quoted without compounding. 12% APR paid monthly is 1% per month.
APY
Annual percentage yield — the yearly rate after compounding. 12% APR compounded monthly ≈ 12.68% APY.
AMM
Automated market maker — a smart contract that prices trades from a formula (like constant-product x·y=k) instead of an order book.
Compounding
Earning returns on past returns. The engine behind every curve on this site.
Concentrated liquidity
Providing AMM liquidity only within a chosen price range (Uniswap v3) — more fees per dollar in range, none outside, amplified impermanent loss.
DCA
Dollar-cost averaging — investing a fixed amount on a schedule regardless of price.
Dividend yield
Yearly cash dividends as a percentage of the position's value.
Drift
The average direction prices trend over time, separate from the random wiggle (volatility) around it.
GBM
Geometric Brownian motion — the standard "random walk with drift" model of prices used by the Monte Carlo runs.
Impermanent loss (IL)
How much less an AMM liquidity position is worth compared to simply holding its tokens, caused by the pool rebalancing as the price ratio moves.
Lending / supply APR
Interest earned for depositing assets into a money market where others borrow them.
Liquidity pool (LP)
A pot of two tokens that traders swap against; providers own a share and earn the trading fees.
Monte Carlo
Answering "what's the range of outcomes?" by simulating many randomized futures and reading percentiles off the results.
Percentile
The 10th percentile is the value that 10% of outcomes fall below. The band spans the 10th–90th.
Principal
The money you start a position with, before any growth.
Staking
Locking a token (e.g. ETH) to help secure its network in exchange for protocol rewards.
Treasury
In this Lab: a sink node that accumulates whatever flows into it without investing it.
Volatility (σ)
The standard deviation of yearly returns — how wildly the price swings around its average path. Stocks ≈ 15%, ETH ≈ 60–90%.