The config said starting_capital: 100000, and the chart's y-axis said USD. Every fill in the log was a USDC number. For eleven months of that run the two facts lived in the same cell and nobody had a reason to split them.
Then Silicon Valley Bank failed, Circle said $3.3 billion of USDC reserves were stuck there, and USDC traded at 87 cents. Our "dollar" book went up. BTC priced in USDC ripped. The perp printed green. The cash we had treated as a risk-free 100,000 was the thing that moved.
I still catch research pipelines that mark USDC, USDT and USD as one column. This is that weekend, pulled apart in the order the ledger saw it.
The line that said $100,000
Paper account, USDC-margined BTC perpetual, modest long. 30,000 USDC of BTC notional, about 1.5 BTC at the Friday mark near $20,150. The other 70,000 sat as USDC collateral. Under-deployed on purpose. We were warming a strategy up. The rest of the stack sat in cash.
The NAV function did what most of them do. It added USDC cash to USDC unrealized pnl and wrote the sum into a series named equity_usd. There was a comment in the config I wrote myself:
stablecoins = 1 dollar. don't double-count FX.
I had meant: don't turn a USDT funding payment into a second dollar move by converting it twice. What I had actually done was freeze the FX rate at 1.0 for the rest of the file.
quote_asset = "USDC"
The venue was a USDC venue. Fills, margin, funding, liquidation marks, the lot size, the min notional, all of it. Hyperliquid, dYdX v3, Coinbase International, a dozen others, same shape. The research notebook imported a BTCUSDC candle and a BTCUSD candle, used the first for trading, and labeled the second on the plot because the plot looked more professional in dollars.
And this is the small, stupid part I still think about at odd hours: the plotting library's default axis title is "USD". I never overrode it. People walked by the screen and read a unit the series did not have.
Friday night, Circle's tweet
March 10, 2023. SVB is already gone. Circle discloses that $3.3 billion of its then ~$40 billion USDC reserve sat at the failed bank, about 8%. Coinbase suspends USDC conversions that same night. Redemption, the actual peg mechanism, is closed for the weekend.
The backtest had no row for any of this. No redemption halt flag, no issuer-reserve field, no "can I get dollars for this token before Monday." It had a close. The close on Friday was still 1.0000 in our FX file, because the FX file was a stub that returned 1.0 for anything whose ticker started with USD.
I used to treat a USDT premium as textbook noise, the sort of thing you mention in a footnote and then ignore. We once spent twenty minutes in a kitchen arguing about whether 1.03 was "worth booking." We were both editorializing. The pair is a series. Store it. Stop voting on it.
0.8789
Saturday morning. CoinGecko's low on USDC is $0.8789. Other tapes print $0.87. A few DEX pools go through $0.80. DAI, stuffed with USDC as collateral, tracks it down. USDT goes the other way, $1.03 on some screens, $1.06 on Kraken.
The stub FX file still returns 1.0. The backtest's clock knows it is Saturday. It does not know that the unit of the book is now a distressed token.
The Kraken print at 26,000
Bitstamp BTC/USD is circling $20,200. Kraken BTC/USDC prints above $26,000. That is the same bitcoin. The second number is a dollar price divided by a broken peg, plus whatever air a thin USDC book adds on a Saturday.
Our venue marked the perpetual in USDC. Take a conservative tape, BTC/USDC at ~$23,000, which is $20,200 / 0.88. The 1.5 BTC long is up about 4,300 USDC. Account in native units: 104,300 USDC. The engine reports $104,300, a 4.3% up weekend, and a funding payment lands on top of that in the same unit.
In dollars the 70,000 USDC of idle collateral is worth about $61,600. The whole 104,300 USDC is worth about $91,800. The book that "made" 4.3% lost 8% of its dollar capital while it was mostly sitting in cash.
The weekend return was a change of unit, not a change of fortune. A long BTC/USDC position on a USDC-down day is mechanically long the depeg. You can want that bet. You cannot discover it afterwards in a series labeled USD.
cash_usd = cash_usdc * 1.0
That line is the one I keep. It ran on every bar, including the bars where USDCUSD was 0.87. Multiplying by 1.0 feels like a no-op, which is why it survives code review. It is an FX mark. A frozen one.
The 70,000 USDC of collateral never triggered a trade, so no slippage model ever looked at it, no fee table, no funding. Cash is supposed to be the boring column. On this book it was the largest risk, and the only one we did not price.
A maker rebate on the BTC fills that weekend was 2 or 3 bps, if you got filled at all. The quote-asset move was 1,300 bps. We have a whole stack for the 3 and a stub for the 1,300.
The green weekend on the chart
Monday morning Slack, before anyone had opened USDCUSD: we printed 4% overnight. Someone asked if we should scale the paper size. I looked at the chart, then at Kraken, then at the stub.
| Ledger item | What the engine stored | Saturday, native | Same moment, in dollars |
|---|---|---|---|
| Idle collateral | 70,000 USDC × 1.00 | 70,000 USDC | ~$61,600 |
| BTC perp mark | BTCUSDC last | ~23,000 USDC (26,000 on Kraken) | BTCUSD ~$20,200 |
| Unrealized on 1.5 BTC | +4,300 USDC | +4,300 USDC | mostly the peg, some bitcoin |
| Reported NAV | 104,300 "USD" | 104,300 USDC | ~$91,800 |
USDT books had the opposite cartoon. USDT traded rich, so a USDT-margined flat account showed a dollar gain it also had not earned, and a BTCUSDT mark that was too low relative to BTCUSD. If you ranked two "identical" BTC perps that weekend, one on a USDC venue and one on a USDT venue, you ranked the quote tokens and called it alpha.
The paper account, the one that actually held USDC, was closer to the truth than the research dashboard. It never claimed to be dollars. We did that in the export.
The export we shipped on Monday
USDC crawled back toward $1 after the Treasury, Fed and FDIC said SVB depositors would be made whole, and after Circle said it would cover a shortfall. By Monday the peg was mostly patched. The stub FX file, which had been wrong for 48 hours, was right again.
But daily bars hid it. The 00:00 UTC sample on March 11 caught USDC already off, or already bouncing, depending on the vendor, and the March 12 sample was a different movie. If you only stored NAV and not the quote mark, you could not reconstruct which. The ugly weekend flattened into a blip small enough to ignore, which we did, because we sampled daily.
What we keep on the book now is three series, always, even when they sit on top of each other for months:
- NAV in the venue's quote asset, the unit you can actually withdraw.
- Quote asset versus USD, a real tape, including Saturday, including 0.87, including the hours Coinbase was not converting.
- NAV in dollars, as a derived series, so a plot label of USD has a parent.
If an agent writes the backtest, the check is the same. Ask it for the quote asset of every position and of cash. If it answers USD on a USDC venue, the run is not a dollar run. March 2023 is the week I replay when someone tells me the distinction does not move the Sharpe. It moved this one by more than the strategy did, and the strategy was the part we had models for.
← All posts


