Running the Fund, Not Just the Strategy: Unitized NAV, Fees, and an Audit Trail
Jonny Bravo
-Running the Fund, Not Just the Strategy: Unitized NAV, Fees, and an Audit Trail
A strategy that makes money is an achievement. A fund is something else entirely. The moment you take outside capital, you inherit a second job that has nothing to do with alpha: you have to account for who owns what, strike a fair value when money comes and goes, charge fees that survive an audit, and prove — line by line — what you traded and why.
Most quant tooling stops at the equity curve and leaves this to a spreadsheet. And spreadsheets are where funds quietly break. A NAV struck a day late, a deposit priced at the wrong unit value, a performance fee charged below the high-water mark — these aren't trading losses, they're accounting losses, and they're the ones that end up in front of a regulator.
We built the back office into the platform. This post walks the accounting engine that turns a working strategy into a vehicle you can run other people's money in.
Units, not percentages
The foundation is a unitized NAV-per-unit model — the same one mutual funds and hedge funds have used for decades, because it's the only model where pro-rata P&L falls out automatically:
* A fund_portfolio is the capital vehicle. Its NAV is struck from the live
balances of the fund_accounts linked to it.
* Ownership is denominated in units. A client's stake value is units * nav_per_unit.
* A deposit buys amount_base / nav_per_unit units; a withdrawal redeems units.
Because deposits/withdrawals change NAV and units in the same proportion,
nav_per_unit is unchanged by a cashflow — only trading P&L moves it. This is
what makes P&L accrue pro-rata and time-weighted automatically.
That last sentence is the whole trick. When an investor deposits, you mint units at the current NAV-per-unit. The fund's total NAV goes up and the unit count goes up, in exact proportion — so the per-unit value doesn't budge. Existing investors aren't diluted; the new investor doesn't get a free ride on yesterday's gains. Only trading moves NAV-per-unit, and when it does, every unitholder's stake moves with it, pro-rata, time-weighted, with no manual allocation step.
The manager isn't an exception to this — the GP is just another unitholder (kind='gp'), so the cap table always sums to exactly 100% of NAV. There's no "and the rest is the manager's" hand-wave. Everyone is units.
Striking a NAV that can't drift
The single most important operation in fund accounting is striking the NAV — and the single most common way it goes wrong is unit drift after fees crystallize. The engine makes units a derived quantity, recomputed from the ledger rather than carried forward:
"""Authoritative units outstanding = Σ settled cashflow units − Σ crystallized fee units. The previous carry-forward left units_outstanding too high after crystallization, understating nav_per_unit and ... """
That comment is a scar from a real bug, and the fix is the right one: units outstanding is always recomputed as total subscribed units minus total redeemed-for-fees units. There's no running counter to fall out of sync. Strike the NAV a thousand times and it reconciles to the ledger every time, because it is the ledger.
Cashflows are priced at a freshly struck NAV, and the strike reflects both subscriptions and fee-crystallization redemptions — so a deposit recorded right after a fee crystallization gets the correct, post-fee unit price. No window where the books are inconsistent.
Fees: accrue daily, crystallize honestly
Two fees, two phases, and the distinction matters enormously for fairness.
Management fees accrue continuously — a daily drag on net NAV — but don't redeem units until crystallized. Performance fees are where the integrity test lives, and the engine enforces the two protections every serious investor demands: a hurdle and a high-water mark.
"""Performance fee per client = max(0, navpu - hwm - hurdle) * units * perf_bps. On a profitable crystallization the high-water mark is raised to navpu."""
hwm = _f((terms or {}).get("high_water_mark")) or nav_per_unit hurdle_bps = _effective_rate(terms, portfolio, "hurdle_bps") hurdle_px = hwm * (1 + hurdle_bps / BPS) gain_per_unit = nav_per_unit - hurdle_px
Read what that guarantees:
- High-water mark. You only earn a performance fee on gains above the previous peak. Lose 20% and then make it back, and the investor pays nothing on the recovery — they were made whole first. The fee is on new high ground only.
- Hurdle. Even above the high-water mark, the fee only applies above a hurdle return (
hwm * (1 + hurdle_bps)). Beat cash, then charge. max(0, ...). Below the hurdle-adjusted high-water mark, the performance fee is structurally zero. Not negotiated — computed.
And the mark only ratchets up on a profitable crystallization (high_water_mark → navpu), so once a peak is set, it stays set until genuinely exceeded. These aren't policies a manager remembers to apply; they're arithmetic the engine applies for them. That's exactly what an allocator's operational-due-diligence team wants to hear — the fee math isn't trusted, it's enforced.
Per-client overrides are supported (mgmt_fee_bps, perf_fee_bps, hurdle_bps), so a founding LP on better terms and a later investor on standard terms can sit in the same fund and each be charged correctly, automatically.
And the part regulators actually ask about: the audit trail
When P&L moves real money across real exchanges, "we think we sent the right orders" is not an answer. The mandate executor persists every execution — the intended targets, the orders placed, the fills received, and every error — into fund_mandate_executions and fund_mandate_execution_orders. The executor is also defensively built around the reality of live venues, refusing to retry into permanent failures:
# exchange error codes that will never succeed on retry. Retrying these just # burns the run window and pushes transient failures past their budget. PERMANENT_ERROR_MARKERS = ( "-2019", # binance: margin is insufficient "insufficient margin", "insufficient balance", "exceeded the maximum allowable position", ... )
The result is a durable, queryable record of what was intended, what was executed, what filled, and what failed and why — per mandate, per run. When an LP, an auditor, or a regulator asks "show me," you query a table, you don't reconstruct a story from exchange emails. That's the difference between a fund that can pass due diligence and one that can't.
Why this is the part that lets you scale
Plenty of traders have a strategy that works. Very few can take outside capital, because the gap between "I trade my own account" and "I run a fund" is precisely this back office — NAV, units, fees, cashflows, audit — and it's unglamorous, error-prone, and ruinous to get wrong.
By making investor accounting a first-class, ledger-reconciled, fee-enforced part of the same platform that backtests and trades your strategy, we close that gap. You don't graduate from a backtesting tool to a fund administrator to a custodian's portal. You strike a NAV, record a subscription, accrue and crystallize fees, and execute mandates — all in one place, all reconciling to the same books.
The strategy is the half that gets you noticed. This is the half that lets you raise.
Ready to take capital? Stand up a portfolio, build the cap table, and strike your first NAV — fees and audit trail included. Explore fund operations →