Why New Items Should Start at Zero (No Retroactive Catch-Up)
During cs2stack's development, its founder added one case to a running config and watched the allocator try to buy nineteen of them in a single morning. Nothing was technically wrong — the code did exactly what its accounting implied. That's what makes catch-up the most instructive bug in automated buying: it's not a typo, it's a philosophy error.
Anatomy of a violent buy
Here's how a reasonable-looking allocator produces that result. To spread a budget fairly, you track each item's "entitlement" — its share of the daily budget accumulating over time — against its actual spending, and buy whenever entitlement exceeds spend. Elegant. Now add a Gamma case to a plan that started three weeks ago. The allocator computes the item's entitlement from the plan's start date, finds twenty-odd days of unspent share, and concludes the new case is owed a small fortune immediately. Nineteen cases, one run, one very surprised founder.
The accounting was self-consistent. The premise was wrong: the item didn't exist in your strategy three weeks ago, so it never earned those days. The fix — the one that shipped — is a fresh-start clock: every item's entitlement accrues from the moment it first appears in the config, not from the plan's birth. New line today, first share today, first fill maybe tomorrow. History before an item's first appearance simply doesn't count.
Why catch-up is wrong even when it's affordable
It's tempting to shrug — nineteen cheap cases is dinner money, and didn't you want that much exposure eventually? Three reasons the answer is still no.
- It's a lump sum wearing a DCA costume. The entire point of daily accumulation is spreading entry across time so no single day's price dominates your cost basis. A catch-up buy concentrates weeks of exposure at one price — the exact opposite — and it does so at a moment chosen by your config edit, not by any market judgment. If you genuinely want immediate exposure, that's a decision worth making consciously; the lump-sum-versus-DCA tradeoff deserves a decision, not an accident.
- It moves thin markets. Nineteen units into one morning's order book is a size that cheap-case listings absorb badly and mid-tier cases absorb worse. You'd walk the book upward and hand the spread to whoever posted the ladder — the mechanics in slippage in small markets. Small daily buys exist precisely because your size should stay invisible.
- It makes editing your config feel dangerous. This is the quiet killer. If adding a line can trigger a violent buy, you'll hesitate to tune your own strategy — and a tool whose configuration you're afraid to touch has failed at being a tool. The same principle drives safety engineering generally: no innocent action should have a non-obvious blast radius.
There's a symmetric rule on the other side, worth stating once: removing an item should just stop its buys — no "farewell purchase," no reshuffling of its past spending onto other items' books. Edits change the future, never the past. The past belongs to the ledger, which is append-only for exactly this kind of reason.
Fresh starts generalize further than you'd think
Once you see the principle — entitlements accrue only while the intent exists, and missed intent expires — it shows up everywhere in a well-built buyer:
- Downtime. Your bot misses two days because a cloud scheduler hiccuped. Should Wednesday's run spend three days of budget? A bounded carry-forward (cs2stack lets unspent shares roll, but a single run never exceeds the day's budget cap) keeps recovery gentle. The streak matters; the makeup exam doesn't.
- Price skips. An item that spent a week over its max-price line accumulates some backlog by design — that's the dip-buying feature — but the daily cap still bounds how fast the backlog can deploy. Patience, with a speed limit.
- Standing orders. "Buy one per week" that went unfilled for three weeks should buy one, not three, when the price finally cooperates. Periods stand alone — the same fresh-start logic, applied to quantity-based orders.
In every variant, the naive alternative optimizes a spreadsheet fiction (total entitlement matched exactly) at the cost of the two things that actually matter: bounded worst-case behavior and an entry price built from many days, per entry smoothing.
How to check your own tooling
Whether you run cs2stack, a homegrown script, or anything between, this is a five-minute audit. In a dry run, add a fake item to a plan that's been running for weeks and read the planned purchases: the new item should get roughly one day's share, nothing more. Then simulate a two-day gap and re-run: total planned spend should still respect one day's cap. If either test shows a spike, your allocator is carrying the catch-up philosophy, and it will eventually meet a config edit — or an outage — that makes it expensive. The broader fairness rules an allocator should satisfy are laid out in equal-split allocation.
The deeper lesson from the nineteen-case morning isn't about one bug. It's that automated money needs a principle for handling change, because configs are living documents — you will add cases, drop them, adjust ceilings, pause plans. "Start at zero, expire what was missed, bound every run" is that principle. It costs you a few theoretical dollars of missed accumulation per edit, and it buys you a system whose response to any change is the only response you should accept from software holding your wallet: small, calm, and boring.