Thursday, April 2nd, 16:02 Eastern. The paper book looked the way we had designed it: long about $240,000 of the BTCUSDT perpetual, short a beta-weighted basket of COIN, MSTR, MARA, and RIOT, residual under $500. The Friday hedge job had run at 15:45 the week before. Funding at 16:00 UTC had been a rounding error. I logged off thinking we finally had a crypto sleeve that did not just recreate Nasdaq beta.
Friday was Good Friday. I only clocked that because the crypto chat was still posting liquidation screenshots and the equity chat had gone quiet in the specific way that means nobody is in a chair. NYSE and Nasdaq were closed. Binance was not. From Thursday's cash close to Friday 16:00 Eastern the perp was down 4.8%. The equity shorts did not exist as a market that day. They were a row of Thursday prices in a risk pane.
The risk bot posted anyway.
beta to hedge basket: 0.04. residual 1d: −$11,184. status: NEUTRAL
I still have that screenshot. A colleague printed it and stuck it on the fridge in the office we barely use. The green NEUTRAL was doing a lot of work. The bot computed beta from the last equity prints it had, which were Thursday's closing auction, against a BTC mark that had been moving for twenty-four hours. A frozen mark is a photograph of Thursday. Calling the photograph a hedge is how you walk into Easter weekend with a naked crypto position and a report that congratulates you for it.
Monday the cash market opened. The basket gapped down, but not 4.8%, and not together. COIN opened −2.1%. MSTR did more. MARA did less. We spent the morning trading into a book that had lived a whole extra session we could not hedge, then pulled the backtest that had blessed this design and asked it a rude question. What did you do last Good Friday, and the one before that, and every weekday the stock market sat out while BTC traded through the holiday?
The research agent that coded the first version had resampled everything to 15-minute bars and forward-filled. Pandas freq='B' was in the notebook, which looks like a grown-up choice until you remember what it actually does. 'B' means Monday through Friday. Good Friday is a Friday. Juneteenth 2026 was a Friday. The Independence Day observance was a Friday. The calendar function did not have a calendar. It had a weekday filter.
The hedge job runs Fridays at 15:45 Eastern, which is a reasonable time to trade toward the cash close on a normal week. Good Friday is always a Friday. In the 2023–2026 window that job lined up with four Good Fridays, Juneteenth, the July 3rd full close, and four day-after-Thanksgiving sessions that had already rung a 1:00 PM bell. Ten Friday sessions. On every one of them the simulator had accepted equity orders. The fills were Thursday's close, or Friday's last regular print carried forward, dressed up as live trades in the blotter. Crypto marks ran live. The residual series got credit for hedging moves that one leg never had a chance to answer.
Weekends were the same bug on a schedule we had at least joked about over beers. Holidays were the version nobody joked about, because the notebook said business days and Good Friday still feels, viscerally, like a day the grown-up financial system is at work. Banks are open. Fedwire is up. A broker API can look awake and still have nowhere to send a COIN order.
Early closes were quieter and, in the blotter, ruder. On November 28th, 2025, the day after Thanksgiving, NYSE closed at 1:00 PM. Our job still woke up at 15:45, looked at a 15-minute series that had been carrying 12:59's last print for almost three hours, and "traded the close." The fill model even charged us closing-auction slippage. There was no auction at 16:00. The auction had happened at 13:00, without us. Paper, that afternoon, had the more honest failure: new orders rejected, working orders cancelled into the early close, a fill rate of zero.
| Session | Cash market | Backtest fill | Paper |
|---|---|---|---|
| Good Friday 2026 | Closed all day | Basket filled at Thursday's close | Rejects; marks stale from Thursday |
| Juneteenth 2026 | Closed all day | Same Friday job, same phantom fills | Caught later, in the replay |
| July 3, 2026 | Closed (July 4 observed) | Friday 15:45 against a Thursday print | No cash session to join |
| Nov 28, 2025 | 1:00 PM close | 15:45 rebalance, 12:59 last print, auction slippage | Rejects after 13:00 |
| Ordinary Saturday | Closed | Forward-filled equity bars, live perp | Frozen names, live perp |
We reran the book with a per-venue session file: regular hours, holidays, early closes, the 16:00 vs 13:00 close, and a stale-hours counter on every mark. Orders into a closed cash session do not fill at last. They reject. The risk pane is not allowed to print NEUTRAL if any hedge leg has been stale more than a few hours. The unit test is ugly and I like it: take a normal Friday, paint it closed, assert zero COIN fills and a stale flag on the basket. And then paint 13:00 as the close and assert the 15:45 job does not so much as look at the auction model.
The residual Sharpe went from 1.14 to 0.61. I do not treat either number as a reason to trade. I treat the gap as a confession. A lot of what we had been calling hedge residual was crypto holiday volatility, collected on days the short stock leg was a souvenir of the previous session. Weekends did some of that work too, which we already discounted in conversation. The holidays were hiding in the business-day resample, which is a much more embarrassing place to hide.
We still run the Friday 15:45 job. It now reads a file with twelve rows for 2026, two of which have not happened yet. Thanksgiving is the 26th. The day after, the cash market dies at 13:00. I put that 1:00 PM close in the calendar twice, under two different names, because I do not trust myself in November, and because pandas will still happily call that Friday a business day if I let it.
← All posts


