Quant interview preparation
Prop market making and quantitative research, weighted the way the interviews actually are: probability and expected value, statistics and machine learning, market making logic, programming and options. Every question is either traced to a named firm from a public candidate report, or tagged at desk level when we could not trace it, and every probability answer shows the reasoning path rather than just the number.
100 questions, mapped to the firms that asked them
- Questions
- 100
- Traced to a firm
- 53
- Firms
- 15
- Updated
- September 2026
076You have K sorted arrays on disk, too large to load at once. How do you merge them into one sorted output?CitadelEquity Capital Markets · New York · 2026
Say this
K-way merge with a min heap of size K. Push the first element of each array into the heap, repeatedly pop the minimum and write it out, then push the next element from whichever array the minimum came from. Time is N log K, memory is O(K) plus your buffers.
Then walk it
- The heap holds one candidate per array, each entry tagged with which array it came from and the index within it. Pop the smallest, emit it, and refill from that same array.
- Complexity: N total elements, each pushed and popped once, each operation log K. So N log K, which beats concatenate-and-sort at N log N whenever K is much smaller than N.
- The disk part is the real content of the question. You do not read element by element, you read blocks. Keep a buffer per array, say a few megabytes each, refill it when it drains, and write the output through a large buffer too. The heap operations are free compared with I/O, so the design goal is sequential reads and few of them.
- If K is very large, K times the buffer size exceeds memory, and then you merge in passes: merge groups of, say, 100 files at a time, then merge the results. That is exactly how external merge sort works, and total I/O is N times the number of passes.
- Practical notes I would raise: use a tournament tree or a loser tree instead of a binary heap if you want fewer comparisons per element, handle the tie-breaking rule explicitly if stability matters, and if this is a real system, check whether the operating system's readahead is already doing your buffering for you before you build it yourself.
Where candidates lose it
Answering merge them pairwise, which is K times N in the worst case, or ignoring the on-disk part entirely. The interviewer put the data on disk deliberately, so talk about block-sized buffered reads and what happens when K is too large to buffer. State the N log K complexity explicitly.
Expect next
- What if K is a million?
- How large would you make the buffers, and why?
- How would you parallelise it?
Reported by candidates at Citadel (Equity Capital Markets, New York, 2026). Source: Wall Street Oasis.
077Given an array and a window of size k, return the maximum in each window as it slides.Akuna CapitalQuant Development · Chicago · 2025
Say this
Monotonic deque, O(n) total. Keep a deque of indices whose values are strictly decreasing. Before pushing a new index, pop from the back everything smaller than the new value, and pop from the front anything that has fallen out of the window. The front is always the maximum.
Then walk it
- Why the deque is monotonic: if a new element is larger than something behind it, that older smaller element can never be the maximum of any future window, because the new one is both larger and more recent. So it is safe to discard permanently.
- Each index is pushed once and popped once, so the total work is O(n) even though a single step can pop many elements. That amortised argument is the thing to say out loud, because it is what distinguishes this from the naive O(n k).
- Store indices, not values, so you can test whether the front has expired by comparing front index against i minus k plus 1.
- Alternatives and why they are worse: a max heap gives O(n log k) and needs lazy deletion of expired entries. A balanced BST or a multiset gives O(n log k) too. Both are fine and both are beaten by the deque.
- Where this actually matters on a trading system, which is worth mentioning: rolling extremes over a tick window, running high and low for a breakout signal, and rolling maximum drawdown. The same structure with the comparison reversed gives you the rolling minimum, and the O(1) amortised cost per tick is what makes it usable in a hot path.
Where candidates lose it
Reaching for a heap and stopping there. The heap answer is acceptable but it is not the answer to this question, and the interviewer is specifically looking for the monotonic deque and the amortised O(n) argument. Also remember to expire the front by index, which is the bug that shows up most often in live coding.
Expect next
- Prove the amortised complexity.
- Now give me the rolling median instead.
- How would you handle a window defined by time rather than by count?
Reported by candidates at Akuna Capital (Quant Development, Chicago, 2025). Source: Wall Street Oasis.
078Given a stream of numbers, return the median after each element arrives.Old Mission CapitalEquities · Boston · 2024
Say this
Two heaps. A max heap for the lower half and a min heap for the upper half, kept balanced so their sizes differ by at most one. The median is the top of the larger heap, or the average of the two tops. Insert is O(log n), query is O(1).
Then walk it
- Insert rule: if the new value is at most the max of the lower heap, push it there, otherwise push to the upper heap. Then rebalance by moving one element across if the sizes differ by more than one.
- Query: if the sizes are equal, the median is the average of the two tops. Otherwise it is the top of the larger heap. Constant time either way.
- Total cost for n elements is n log n, and memory is O(n) because you must retain everything. That memory cost is the honest limitation, and it is the first thing an interviewer will probe.
- If the median must be over a sliding window rather than the whole prefix, the two-heap approach needs deletions from the middle. Use an indexed multiset or two heaps with lazy deletion and a hash of pending removals. That is the version that comes up in practice on a tick stream.
- And if approximate is acceptable, which on a trading system it usually is, the right answer is a streaming quantile sketch: t-digest or the Greenwald-Khanna algorithm, giving you any quantile in bounded memory rather than O(n). Naming that unprompted is what turns a correct interview answer into a practical one.
Where candidates lose it
Sorting on every element, which is O(n squared log n) overall, or maintaining a sorted list with insertion, which is O(n) per element because of the shifting even though the binary search is fast. Say two heaps immediately, then volunteer the sliding-window and bounded-memory variants, because that is where the conversation is heading.
Expect next
- Now do it over a sliding window of the last thousand values.
- What if you cannot store all the data?
- How would you get the 99th percentile instead of the median?
Reported by candidates at Old Mission Capital (Equities, Boston, 2024). Source: Wall Street Oasis.
079How would you store key-value pairs, and what are the tradeoffs between the implementations?Jump TradingEngineering · Cambridge · 2019
Say this
Hash table for O(1) average lookup with no ordering, balanced tree for O(log n) with ordered iteration and range queries, and a flat sorted array if the data is static and you care about cache behaviour. The choice is driven by whether you need ordering and what your access pattern looks like in memory.
Then walk it
- Hash table: O(1) average, O(n) worst case on collisions, no ordering, and rehashing causes an occasional large latency spike. That spike is a real problem on a trading hot path and it is why people pre-size their maps.
- Balanced tree, red-black or B-tree: O(log n) guaranteed, ordered traversal, range queries, and predictable latency. Worse constants and worse cache locality because of pointer chasing.
- The tradeoff that matters most in practice is memory layout, not big-O. C++ unordered_map uses separate chaining with nodes scattered across the heap, so every lookup is potentially a cache miss. An open-addressing flat hash map keeps everything in one array and is commonly two to three times faster in real workloads at the same asymptotic complexity.
- For a mostly-static table, a sorted array with binary search beats both: contiguous memory, no pointers, and for small n a linear scan beats binary search because it is branch-predictable and prefetchable. Under about 16 to 32 entries, linear wins.
- And on disk the answer changes completely: B-trees for read-heavy workloads because of the branching factor against block size, LSM trees for write-heavy because they turn random writes into sequential ones. I would want to know the read-write ratio and whether the working set fits in cache before choosing anything.
Where candidates lose it
Answering hash map, O(1), done. The question says tradeoffs, so it is a systems question and the interviewer at a trading firm cares about tail latency and cache behaviour more than asymptotic complexity. Mention rehashing spikes and pointer chasing, and ask what the access pattern is.
Expect next
- Why is std::unordered_map often slow in practice?
- How would you avoid latency spikes from rehashing?
- What changes if the data lives on disk?
Reported by candidates at Jump Trading (Engineering, Cambridge, 2019). Source: Wall Street Oasis.
080Can you implement a linked list, and when would you actually use one on a trading system?Jump TradingProp Trading · Remote · 2022
Say this
Yes, a node with a value and a next pointer, plus a head, and the usual care about the empty list and about updating head when you insert or delete at the front. But the honest answer to the second half is: rarely, because pointer chasing destroys cache performance.
Then walk it
- The implementation: struct with value and next, insert at head in O(1), search in O(n), delete given the previous node in O(1). Use a dummy head node and most of the edge cases disappear, which is the trick worth knowing for interviews.
- The standard edge cases they will check: empty list, single element, deleting the head, and not leaking the node you unlinked. In C++ that means being explicit about ownership, and in a real codebase it means a unique pointer or an arena.
- What a linked list genuinely buys you: O(1) splice of a node from the middle if you already hold a pointer to it, and stable addresses so a pointer stays valid across insertions. That is exactly the requirement in a limit order book, where you need to cancel an arbitrary resting order in constant time, so orders at a price level are typically an intrusive doubly linked list with a hash from order id to node.
- What it costs: every traversal is a potential cache miss, and a vector beats a list for iteration by an order of magnitude even when the asymptotics say otherwise.
- So the real-world answer is an intrusive list over a pre-allocated node pool, not a textbook list with individual heap allocations. Saying that is the difference between having done the exercise and having written low-latency code.
Where candidates lose it
Writing the code correctly and having nothing to say about why you would use one. At a trading firm the interesting half is the memory and cache discussion, and the order book cancel case is the one concrete example where a linked list is genuinely the right structure. Also do not forget the dummy head trick, it removes most of the bugs.
Expect next
- Reverse it in place.
- Detect a cycle in constant space.
- Why would a vector usually beat a list even when the complexity says otherwise?
Reported by candidates at Jump Trading (Prop Trading, Remote, 2022). Source: Wall Street Oasis.
081Write me an unordered_map class. What is actually inside a hash map?Old Mission CapitalTrading · Chicago · 2021
Say this
An array of buckets, a hash function mapping keys to bucket indices, a collision resolution strategy, and a resize policy driven by load factor. The three decisions that define the implementation are the hash, the collision handling, and when you grow.
Then walk it
- Core operations: index equals hash of key modulo bucket count, then search within that bucket comparing keys for equality. Insert, find and erase all follow that pattern, and all are O(1) expected under a good hash.
- Collision resolution, and this is the main design choice. Separate chaining stores a list per bucket, which is simple and is what the C++ standard effectively mandates for unordered_map because of its iterator and reference stability guarantees. Open addressing stores entries inline and probes forward, which is far more cache-friendly but complicates erase, since you need tombstones or backward shifting.
- Resize: track load factor as elements over buckets, and when it exceeds a threshold, typically 0.75 for chaining or 0.5 to 0.7 for open addressing, allocate a bigger array and rehash everything. Use a power-of-two bucket count so the modulo is a bitmask, but then your hash must mix the high bits or a weak hash collides badly.
- The correctness details an interviewer will probe: key equality is separate from the hash, two equal keys must hash the same, iterator invalidation on rehash, and what happens when the key type has a bad hash. A hash that is the identity on integers plus power-of-two buckets means sequential keys with a stride collide catastrophically.
- If I were writing this for a trading system I would use open addressing with linear probing over a pre-allocated power-of-two array, reserve capacity up front so no rehash ever happens in the hot path, and store keys and values in separate arrays if the values are large. The reason is tail latency: one rehash mid-session is a millisecond spike, and a millisecond is forever.
Where candidates lose it
Describing the interface rather than the internals. The question is about buckets, hashing, collisions and resizing. Also be ready for why is std::unordered_map slow, whose answer is node-per-element allocation and the standard's stability guarantees forcing chaining. And never forget that erase under open addressing needs tombstones, which is the bug candidates ship.
Expect next
- How does erase work under open addressing?
- What load factor would you choose and why?
- What makes a good hash function, and what happens with a bad one?
Reported by candidates at Old Mission Capital (Trading, Chicago, 2021). Source: Wall Street Oasis.
083Write an algorithm to find all the primes from one to n, and then optimise it.AQR Capital ManagementResearch · Greenwich · 2015
Say this
Sieve of Eratosthenes. Mark every multiple of each prime as composite, and the unmarked survivors are the primes. Time is n log log n, which is essentially linear, and memory is n bits.
Then walk it
- The baseline to reject first: trial division on each number up to its square root is about n times root n over log n, far worse. Say why the sieve wins before you write it.
- The sieve itself: start at p equal to 2, mark 4, 6, 8 and so on, then advance to the next unmarked number. Two optimisations that come free: start marking at p squared rather than 2p, because smaller multiples are already marked, and stop the outer loop at root n.
- Memory optimisations: store only odd numbers, halving memory, use a bit array rather than bytes for an eightfold saving, and if n is large, sieve in cache-sized blocks. That last one matters more than anything else in practice, because a naive sieve over 10 to the 9 is dominated by cache misses, and segmenting it can be several times faster at identical complexity.
- Further refinements if pushed: a wheel sieve skipping multiples of 2, 3 and 5 removes about 77 percent of the candidates, and the sieve of Atkin is asymptotically better at n over log log n but is slower in practice and much harder to get right.
- And the answer to a different question they may be asking: if you want to test whether one large number is prime rather than enumerate a range, the sieve is the wrong tool entirely and you want Miller-Rabin, which is probabilistic and fast. Recognising that enumerate and test are different problems is worth saying.
Where candidates lose it
Giving trial division and calling it done, or giving the sieve with no optimisation when the question explicitly asks for one. The optimisations they want in order are: start at p squared, skip evens, use a bit array, then segment for cache. Naming cache blocking is what marks you out, because it is the one that matters at scale and it is not in the textbook answer.
Expect next
- What is the memory cost for n equal to a billion, and how would you reduce it?
- How would you parallelise the sieve?
- Now test whether one very large number is prime.
Reported by candidates at AQR Capital Management (Research, Greenwich, 2015). Source: Wall Street Oasis.
085C++ or Python? Where does each belong in a quant stack?Quant researchQuant development
Say this
Both, in different places. Python for research, where iteration speed and the data-science ecosystem dominate. C++ for anything on the critical path, where you need deterministic microsecond latency and control over memory. The split is a question of which cost dominates, developer time or machine time.
Then walk it
- Python's real advantage is not the language, it is pandas, numpy, scipy, statsmodels and scikit-learn plus notebooks. A research idea gets tested in an afternoon. The performance is acceptable because the heavy loops sit in vectorised C underneath.
- Python's disqualifying weakness for execution is non-determinism: garbage collection pauses, the global interpreter lock, and unpredictable allocation. A tail latency you cannot control is worse than a mean latency that is higher.
- C++ gives you no garbage collector, control of memory layout and cache behaviour, zero-cost abstractions, and access to kernel bypass networking. The cost is development speed and a large surface for undefined behaviour.
- How real stacks resolve it: C++ or Rust for the gateway, book building and order entry, Python for research, signal development and analysis, with the shared logic compiled once and bound into Python through pybind11 so research and production use the same code. That last point matters, because a research-production mismatch is a reliable source of live losses.
- And I would name the middle ground rather than pretend the choice is binary. Numba, Cython, JAX and polars cover a lot of ground where Python is too slow but full C++ is unjustified, and Rust is genuinely taking share on the systems side. The judgement I would offer is: write it in Python until you have measured that it is too slow, then move only the measured hot spot.
Where candidates lose it
Picking a side as a matter of taste. It is a judgement question about where each tool fits, and a candidate who says C++ is better shows they have only worked on one side of the stack. Mention the research-to-production consistency problem, because it is the practical issue this split creates and few candidates raise it.
Expect next
- How would you keep research and production code consistent?
- What specifically makes Python unsuitable for the critical path?
- Where would you use Rust?
Firm tags come from public, anonymous candidate reports on Wall Street Oasis: strong signal, not sworn testimony. Firms are named as the places a question was reported, not as partners of Fin Maverick. Answers are written for this page to show how to think out loud; they are not scripts to recite.

