# Trade Ledger: Debugging Exercise **Time:** 60 minutes **Language:** Python 3.10+ (standard library only) **AI:** Allowed (Codex in VS Code) ## Files | File | Purpose | |---|---| | `logger.py` | A small logger that buffers records and writes them to a stream | | `ledger.py` | Trades, positions, and the ledger that applies trades, rebuilds from history, and reconciles | | `main.py` | Replays a sample trade list (a "blotter") through a live ledger, then does an end-of-day (EOD) rebuild and reconcile | | `test_ledger.py` | The existing test suite | ```bash python3 main.py python3 -m unittest -v ``` ## The ticket > **From:** Trading Ops > **Priority:** High > > The end-of-day reconcile is reporting breaks between the live ledger and the EOD rebuild, > and some desks say their average prices look wrong after big sells. We checked the logs > and found no errors, and the log lines don't seem to match the trades they're about. > CI is green. ## Your tasks 1. **Clarify first.** Before you change any code, tell the interviewer which assumptions you are making, and ask about any that are unclear. 2. **Reproduce** the reported behaviour. 3. **Find and fix the 4 bugs** in the codebase. For each one, explain the root cause, not only the symptom. 4. **Review the test suite.** CI is green even though the code is broken. Explain why. 5. **Write your own tests.** Each bug needs at least one test that fails before your fix and passes after it. 6. **Talk through your trade-offs** as you go. ## Given assumptions 1. A `Trade` has `trade_id`, `symbol`, `side` (`"BUY"` or `"SELL"`), `qty`, and `price`. 2. `qty` on a trade is always entered as a positive whole number. The side gives the direction. 3. A trade with a qty or price of zero or less, or an unknown side, is invalid. It must be rejected and logged at `ERROR`. 4. Positions use **weighted-average cost**. 5. Realized P&L is booked only when a position is reduced. 6. `Ledger.history` is the record of truth for every accepted trade. The EOD job rebuilds a fresh ledger from it. 7. The two ledgers reconcile when every symbol has the same `qty` and `avg_price`. 8. Every log line must name the trade it is about. 9. A logger only writes messages at or above its configured level. 10. Single-threaded. You don't need to handle persistence, partial fills, or fees. ## Assumptions you should clarify with the interviewer These are left open on purpose. Asking about them is part of the exercise. - Is short selling allowed? Can one trade move a position from long, through zero, to short? - When a position flips from long to short, or short to long, what should `avg_price` be on the new side? - Is a `Trade` object safe to modify once it has been submitted, or should it be treated as immutable? - Should a rejected trade appear in `history`? - Should the EOD rebuild stop on the first bad trade, or skip it and carry on? Either way, how should it report the problem? - Can records be lost if the process exits before `flush()` is called? Does that matter here? - Can level names be given in lowercase? Should custom levels be supported? - Is a tolerance of `1e-9` right for comparing prices?