The source notes for this guide describe the Software Engineer role at Hudson River Trading (HRT) as building high-performance, low-latency systems that process large volumes of market data. The responsibilities listed there are writing performance-sensitive code, optimizing existing modules for latency, building tools for research and trading teams, working with quantitative researchers to turn models into production code, and taking part in on-call rotations that involve troubleshooting live issues and analyzing performance regressions.
The listed must-have skills are proficiency in C++ or Python with a solid understanding of the standard library and memory model, a strong foundation in data structures and algorithms, and experience with Linux and command-line tools. Exposure to low-latency or high-frequency trading systems, multithreaded or parallel programming, and computer architecture (CPU caches, the TLB) are listed as nice-to-have.
The reported questions fall into three groups. Systems and low-level questions cover stack versus heap and process memory layout, MMU address translation, the C++ inline keyword, segmentation faults, struct alignment and Small String Optimization. Algorithm questions include a deque with O(1) operations at both ends plus random access, a hashmap with amortized resizing, merging sorted files too large for memory, a diamond-shaped matrix sum and a streetlight coverage count. Scenario questions cover latency spikes, a cancellable timer scheduler, a trading host running out of disk, and parallelism with threads versus processes. Prepare each group down to the mechanism, not the definition.
Online Coding Challenges
reportedCandidates report that the process opens with a series of online coding challenges meant to assess technical foundations. The source does not say which problems appear at this stage, so prepare for the algorithm category as a whole. Reported problems such as counting how many streetlights cover each point of a line segment, or finding the maximum sum in a diamond-shaped region of a matrix, reward a precise model (a difference array or event sweep for overlapping intervals, prefix sums over rotated coordinates) over brute force. If the challenge is auto-graded, the worked examples are the only specification you get: read them as a contract and test the cases they leave out, such as empty input, a single element, touching intervals, values at the bounds, and inputs large enough that a quadratic pass would not finish.
What to demonstrate
- Whether your solution handles cases the examples omit: empty input, a single element, overlapping or touching intervals, and coordinates at the edges of the range
- Whether you choose an approach whose complexity fits the stated input size before writing it, instead of finding a timeout after submission
- Whether the code is correct on first submission, since an online challenge has no interviewer to catch a misread requirement
How to prepare
- Solve the streetlight problem with a difference array: a light covering [p - r, p + r] adds +1 at the clamped start and -1 just past the clamped end, and one prefix-sum pass gives the count at every point
- Before coding any challenge, write a small harness that runs the given examples plus empty, single-element and boundary cases and prints expected against actual
- Do timed sets in a plain editor with autocomplete off, in the language you will interview in (C++ or Python)
Live Technical Interviews
reportedCandidates describe several rounds of fast-paced live technical interviews, and the source notes that these may use remote coding environments or traditional paper-and-pencil problem solving. Prepare for both formats. Prepare the algorithm and low-level categories together: the reported data-structure questions ask you to build things from scratch (a deque with O(1) push and pop at both ends plus random access, a hashmap that handles collisions and keeps amortized cost through resizing), and the reported systems questions ask how code meets memory and the operating system. State the invariant before you write, then trace a small input by hand. On paper there is no compiler to find the bug for you, so the trace is your test suite.
What to demonstrate
- Whether you can implement a core structure from scratch and justify its complexity, including the doubling argument behind amortized O(1) and what one resize costs in the worst case
- Whether memory answers go one level below the definition: page tables and the TLB behind virtual addresses, what makes the kernel deliver SIGSEGV, why padding follows alignment
- Whether code written without a compiler can be traced line by line on a small input and still holds on empty and one-element cases
How to prepare
- Write a ring-buffer deque by hand: head index, count, element i at (head + i) mod capacity, growth by copying into a buffer twice the size. Then write a chained hashmap that doubles when the load factor passes a threshold you can defend
- For each reported low-level topic (stack vs heap and process layout, MMU translation, inline, segfault causes, alignment and SSO), write a one-paragraph answer plus two follow-ups an interviewer could ask next
- Solve one problem a week entirely on paper, then type it in unchanged and count the bugs the compiler and your tests find
Onsite or Final-Round Interview
reportedThe source says a final round can cover multiple domains, including system design, algorithm development, and potentially probability or statistics depending on the team. The reported scenario questions deal with one machine or a handful of hosts, not web-scale architecture: causes of latency spikes in a low-latency trading system, a scheduler that fires a callback after T milliseconds and supports cancel, a trading system with insufficient disk space, and parallel computation with threads versus processes. For each one, name the mechanism, explain how you would measure it, and give the trade-off of each fix. Moving between domains is a skill of its own, so restate each problem and its constraints before starting it, however the final round is scheduled.
What to demonstrate
- Whether a latency answer names concrete tail sources (allocation, lock contention, page faults, synchronous logging or I/O, context switches, cache misses) and how each would be measured
- Whether a scheduler design states its data structure, the cost of cancel, which thread fires callbacks, and what happens when cancel races with firing
- Whether probability or statistics answers state the assumption they rest on, for example when a median describes heavy-tailed profit data better than a mean
How to prepare
- Design the timer scheduler twice, once as a min-heap keyed by deadline with lazy cancellation through a per-timer flag and once as a hashed timing wheel, and compare insert, cancel and fire costs
- For the disk-space scenario, order the levers: find what is growing (du versus df, deleted files still held open), rotate and compress logs, move cold data off the host, and alert well before the disk fills
- Explain threads versus processes for a parallel job: shared memory and synchronization cost versus isolation and IPC cost, and in standard CPython builds, why the GIL limits CPU-bound threads
- Review expectation, variance, conditional probability and common distributions, and practise explaining aloud when a mean misleads
16 candidate reports. Individual accounts describe a particular role and hiring cycle.
Hudson River Trading Software Engineer interview on OS and C++
The process was low-level from the start. After an initial online assessment or coding step, I entered live rounds that demanded deep knowledge rather than surface familiarity. I was asked how things work under the hood, down to memory and kernel operation, with an emphasis on the kind of understanding that gives a developer full control. One screen focused on OS topics at an implementation level…
Read full experienceHudson River Trading Software Engineer interview
After a recruiter screen, I entered a long technical process centered on a take-home. The lack of feedback was difficult. I spent hours trying to meet the expected behavior without useful checkpoints. The RTL and verification assignment was ambiguous. I adapted my scoreboard to match the buggy RTL behavior, even though I thought a model should stay more abstract and respond only to the specified…
Read full experienceHudson River Trading Algo Engineer Interview Experience — Rejected After Two Phone Screens
View report detailsHudson River Trading New Grad Software Engineer Interview Experience — Online Assessment: Text Segmentation and Suffix-Pair Problems
View report detailsHudson River Trading Quant Researcher Interview Experience — A 45-Minute CodeSignal C++ OA
View report detailsPracHub editorial advice for the preparation topics above.
Reciting definitions for the reported C++ and memory questions without the mechanism underneath.
Prepare every low-level topic one layer down. For a segfault: a missing or disallowed translation makes the MMU raise a page fault, and the kernel's handler delivers SIGSEGV only when the address lies outside every mapped region of the process or the access breaks that region's permissions. A missing page-table entry inside a valid region is ordinary demand paging, not a segfault. Name the usual causes (null or dangling pointers, out-of-bounds writes, stack overflow, writes to read-only memory) and how you would diagnose one (core dump and debugger backtrace, AddressSanitizer). The C++ inline keyword is mainly a linkage rule that allows identical definitions in several translation units; whether a call is actually inlined is the optimizer's decision. SSO keeps short strings in a buffer inside the string object, with an implementation-defined capacity.
Claiming amortized O(1) for the reported deque or hashmap without being able to show why, or ignoring the cost of a single resize.
For the deque, use a ring buffer with a head index and count, element i at (head + i) mod capacity, doubling when full. Be ready to show that doubling copies at most about 2n elements over n pushes, which is what makes the average constant. Apply the same argument to hashmap resizing, and say how you handle collisions (chaining, or open addressing with its tombstone and probe-length issues). Then address the gap between amortized and worst case: one resize is O(n), and on a latency-sensitive path that single operation is the spike. Mention preallocating capacity or rehashing incrementally as the fix.
Writing live-round code that only works with a compiler and test runner to lean on.
The source notes live technical interviews may be paper-and-pencil as well as remote coding. Practise writing complete functions by hand, then tracing them on a small input with a table of variable values per step. Write loop bounds and index arithmetic deliberately and walk the empty and one-element cases before declaring the code done. For the reported merge of N sorted files that do not fit in memory, that means tracing the min-heap of file heads through at least one file running dry, which is where hand-written merges usually break.
Answering the latency-spike and timer-scheduler scenario questions with load balancers and databases instead of what happens inside the process.
These reported scenario questions are about the host. For latency spikes, list tail sources you can measure: allocation that faults in a page, lock contention that hands the core to another thread, synchronous logging or disk writes, context switches, cache misses, and interrupts. The usual remedies are preallocating, moving logging to a separate thread over a single-producer single-consumer ring, and keeping the hot path out of the kernel. Confirm each one by measuring it, because a lock-free queue whose counters share a cache line can be slower than the mutex it replaced. For the scheduler, state the structure, the cancel cost, the thread that fires callbacks, and the cancel-versus-fire race.
Skipping probability and statistics because the title says Software Engineer.
The source says the final round may cover probability or statistics depending on the team's focus, and the question bank for this role includes a question on choosing a mean or median for trading profit metrics. Review expectation, variance, conditional probability and common distributions, and practise explaining why a median resists a few extreme outcomes while a mean does not. State the assumption behind each answer out loud; a correct number with no stated assumption is hard for an interviewer to credit.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Implement a deque with O(1) complexity for push/pop at both ends and r…
Implement a deque with O(1) complexity for push/pop at both ends and random access.
Approach
- Choose the data structure from the access pattern, not from familiarity.
- Name the brute-force solution and its complexity before improving on it.
- Walk one small example through your approach before writing the whole thing.
Follow-up
- How does this change if the input no longer fits in memory?
- Which test case would catch an off-by-one here?
Calculate the number of streetlights that illuminate each point on a o…
Calculate the number of streetlights that illuminate each point on a one-dimensional line segment.
Approach
- Choose the data structure from the access pattern, not from familiarity.
- Name the brute-force solution and its complexity before improving on it.
- Walk one small example through your approach before writing the whole thing.
Follow-up
- What is the worst case, and how likely is it on real data?
- Which test case would catch an off-by-one here?
How would you implement a hashmap, manage collisions, and maintain amo…
How would you implement a hashmap, manage collisions, and maintain amortized time complexity during resizing?
Approach
- Walk one small example through your approach before writing the whole thing.
- State the target complexity and say which constraint rules the naive version out.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- What is the worst case, and how likely is it on real data?
- How does this change if the input no longer fits in memory?
Given N large sorted log files that do not fit in memory, how would yo…
Given N large sorted log files that do not fit in memory, how would you merge them into a single sorted file?
Approach
- Name the brute-force solution and its complexity before improving on it.
- Choose the data structure from the access pattern, not from familiarity.
- Restate the input: its shape, its size, and what is guaranteed about it.
Follow-up
- Which test case would catch an off-by-one here?
- How does this change if the input no longer fits in memory?
How does memory alignment affect struct sizes, and what is Small Strin…
How does memory alignment affect struct sizes, and what is Small String Optimization (SSO)?
Approach
- Identify the window where an invariant is briefly untrue.
- Reach for the cheapest primitive that closes the race, not the broadest lock.
- Distinguish a value from a reference to it, and say which one you handed out.
Follow-up
- Where could this allocate more than you expect?
- How would you prove the race exists rather than suspect it?
What is the difference between stack and heap memory, and what is the …
What is the difference between stack and heap memory, and what is the memory layout of a process?
Approach
- Say what the runtime actually does before reasoning about the code.
- Reach for the cheapest primitive that closes the race, not the broadest lock.
- Identify the window where an invariant is briefly untrue.
Follow-up
- How would you prove the race exists rather than suspect it?
- Where could this allocate more than you expect?
Resolve position identity across a corporate action graph
Corporate actions are edges (from_instrument_id, to_instrument_id, action_type in symbol_change, merger, spinoff, expiry_roll, ratio_num, ratio_den, effective_ts) — up to 2 x 10^6 edges over 10^6 instruments. Given a position in instrument I held at t0, return the set of (instrument_id, exact rational multiplier) that it becomes at t1 > t0, following only edges whose effective_ts lies in (t0, t1]. Mergers give a node in-degree above one; spinoffs give out-degree above one. Detect cycles and report them as data errors rather than breaking them. Give both the per-query and the batch complexity.
Approach
- Start from what the shape of the answer has to be. A spinoff makes out-degree greater than one, so a position maps to a set, not to a single successor; a merger makes in-degree greater than one, so the reverse map is not a function. Anything that collapses each instrument to one representative — union-find being the usual reflex — cannot express either, and also destroys the as-of property that makes the query meaningful.
- Filter the edge set to effective_ts in (t0, t1] and traverse forward from I with DFS, carrying an exact rational multiplier along each path as a reduced (num, den) pair. Multiply at each hop, run gcd immediately to keep the components small, and use checked 128-bit multiplication so a long chain fails loudly rather than wrapping.
- Accumulate at the terminals by summing multipliers across distinct paths that reach the same instrument, because a spinoff that later re-merges into its parent genuinely contributes twice. Summing rationals means a common denominator, then a gcd reduction again.
- Run the cycle check before the path-carrying traversal, not after it. The DFS above carries a multiplier down every distinct path and so does not terminate on a cyclic subgraph at all; the traversal's termination is a precondition that Kahn establishes, not a property the traversal has on its own.
- Complexity: a single query is O(V + E) in the worst case. For a batch — end-of-day, every position in the book — sort nodes by effective_ts to get a topological order of the filtered subgraph and memoise the resolution per (node, t1), which makes the whole batch O(V + E) once instead of O(positions x (V + E)).
- Detect cycles with Kahn's algorithm on the filtered subgraph: after the queue drains, the residual set is exactly the nodes on or downstream of a cycle. Run Tarjan on the residual to name the strongly connected components of size above one, report them with the offending edges and exit non-zero. Time is monotone along real corporate actions, so a cycle is always a vendor or load error; deleting an edge to make the traversal terminate hides the error and leaves positions mapped to the wrong instrument.
- Keep ratios rational end to end. A 1/3 hop followed by a 3/1 hop — a consolidation and then a re-split, through distinct instruments — must compose to exactly 1/1, which it does with reduced rationals and does not with binary64, where the round trip lands near 0.9999999999999998 and a position quantity is then off by a share after rounding.
Worked solution 40 min
- Build the fixture inside the query window (t0, t1]: A -> B symbol_change 1/1 at ta; A -> D spinoff 1/4 at ta; B -> C merger 2/5 at tb, with t0 < ta < tb <= t1.
- Resolve a position of 1000 units of A by hand along both paths, multiplying reduced rationals rather than decimals.
- Run the traversal and compare its (instrument, multiplier) set against the hand computation.
- Extend the fixture with a chain through distinct nodes rather than a round trip: A -> E symbol_change 1/3 at ta and E -> F symbol_change 3/1 at tb, both inside the window, and confirm the multiplier carried to F composes to exactly 1/1. Pointing the second leg back at A instead would close a cycle, which is the next step's fixture and not a test of rational arithmetic.
- Add an edge C -> A at a timestamp inside the window, rerun, and confirm Kahn leaves a residual and Tarjan names the cycle before any path-carrying traversal starts.
- Remove the C -> A edge, delete the spinoff edge, and rerun to confirm the result set shrinks without disturbing C's or F's multiplier.
Follow-up
- A vendor correction arrives at 18:00 changing yesterday's merger ratio. What do you recompute, and what does that do to already-published positions?
- A cash-in-lieu component means the mapping is not purely quantity-to-quantity. Where does that land in the model?
- How do you make this resolution replayable, so a backtest run in six months resolves the same identity the live session did?
A desk report that silently multiplies filled quantity
A report joins order_event (many rows per order_id: submit, ack, each partial_fill, fill), execution_report (one row per venue execution, keyed (venue_mic, venue_exec_id)) and risk_limit (PRIMARY KEY (limit_id, limit_version), one row per version per effective window) to show, per strategy_run_id, filled quantity, filled notional, and the max_order_notional in force. Filled quantity comes back roughly five times too large and notional shifts run to run. Identify both fan-outs, give the diagnostic that proves each one, and rewrite the query so the totals are correct.
Approach
- Name the mechanism rather than the symptom: an inner join multiplies rows, and SUM over a multiplied row set multiplies the measure. Each execution matches every lifecycle row of its order, and each limit scope matches every stored version, so the two factors compound.
- Prove each fan-out with a cardinality probe on the key alone, before touching the aggregate: GROUP BY order_id HAVING count() > 1 on order_event, and GROUP BY limit_id HAVING count() > 1 on risk_limit. Then compare COUNT() of the joined set against COUNT() of execution_report restricted the same way; the ratio is the multiplier.
- Fix the execution side by aggregating before joining: a subquery summing last_qty and the notional per order_id, joined one-to-one against a genuine one-row-per-order source such as the submit event, instead of joining the event log directly.
- Fix the limit side with a LATERAL that picks the one version in force: effective_from <= the order's sent_ts AND (effective_to IS NULL OR effective_to > sent_ts) ORDER BY effective_from DESC LIMIT 1. Picking by MAX(limit_version) is a different answer and usually the wrong one, because the newest version may not have been in force when the order was sent.
- Scale notional correctly while you are in there: last_px is an integer at the instrument's price_scale and derivatives carry a contract_multiplier, so notional is last_qty * last_px * contract_multiplier / 10^price_scale, with both attributes resolved from the instrument version as of the execution.
- Reject the reflex fixes explicitly: SELECT DISTINCT and SUM(DISTINCT last_qty) both change the answer by collapsing two legitimately equal fills into one.
Follow-up
- A LEFT JOIN to risk_limit would keep orders that matched no limit. What does that do to your totals against an inner join, and which do you actually want?
- How would you catch this class of bug automatically before the report ships to a desk?
- Which of these joins would you push into a materialized view, and what event invalidates it?
Why the execution-report lookup stopped using its composite index
execution_report holds 4 x 10^9 rows with an index on (account_id, received_ts). The query WHERE account_id = $1 AND received_ts::date = $2 runs a sequential scan. A second query, WHERE venue_exec_id LIKE $1 || '%', also scans despite a btree on venue_exec_id VARCHAR(48) in a database with a non-C default collation. A third, WHERE account_id = $1 alone, scans and the planner is right to. Explain each, give the rewrite for the first (a business date is a venue-local concept, not a server-timezone one), and say what EXPLAIN output you would ask for.
Approach
- First query: received_ts::date wraps the indexed column in a function, so the index's second column can no longer bound a range and only the account_id prefix is usable. If that prefix is not selective, a sequential scan is the cheaper plan and the index was never going to help.
- Get the rewrite's boundaries right rather than just removing the cast. Casting timestamptz to date depends on the session's TimeZone setting, so the same query means different instants in different sessions. A business date is defined by the venue's calendar, so take the session open and the next open from that calendar and write received_ts >= $open AND received_ts < $next_open.
- If the cast semantics are genuinely wanted, index the expression: (received_ts AT TIME ZONE 'UTC')::date is immutable because the zone is a literal, so it can be indexed, while the bare ::date is only stable and PostgreSQL will refuse it. The cost is pinning the zone into the index definition.
- Second query: a default-collation btree cannot serve a prefix LIKE, because the collation's sort order is not the byte order the pattern match needs. Rebuild it with varchar_pattern_ops (or run the database in the C collation), and keep the plain index too if equality lookups still need collation-aware comparison.
- Third query: an index scan returning a large fraction of the table reads most of the heap in random order on top of the index, so the sequential scan is correct. The fix is a different layout, such as partitioning by session_date or a BRIN on received_ts over a naturally time-clustered table, not a different btree.
- Ask for EXPLAIN (ANALYZE, BUFFERS) and compare estimated against actual rows. A two-orders-of-magnitude estimate error points at stale or correlated statistics, which CREATE STATISTICS or a composite index can fix; an accurate estimate that still chooses a scan means the scan is the right plan and the question is wrong.
Worked solution 30 min
- Build a few million rows with (account_id, received_ts), ANALYZE, and run all three queries under EXPLAIN (ANALYZE, BUFFERS).
- Rewrite the first as a half-open range on the raw column and compare buffers read against the original.
- Create the index on ((received_ts AT TIME ZONE 'UTC')::date) and confirm the cast form now uses it; then attempt to index the bare ::date expression and read the error.
- Recreate the varchar index with varchar_pattern_ops and re-run the prefix query.
Follow-up
- The rewritten range query is still slow because one account-day is 200 million rows. What changes about the physical design?
- Before dropping an index you believe is unused, how do you establish that in production?
- When does BRIN on received_ts beat the composite btree here, and what breaks that assumption?
Analyze the potential causes of latency spikes in a low-latency tradin…
Analyze the potential causes of latency spikes in a low-latency trading system.
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Name the failure you are designing for, then the recovery path.
- Choose a partition key and say what query it makes expensive.
Follow-up
- How does this behave when that dependency is down for an hour?
- What breaks first when traffic grows ten times?
How would you design a scheduler that triggers a callback after T mill…
How would you design a scheduler that triggers a callback after T milliseconds, including a cancel function?
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Choose a partition key and say what query it makes expensive.
- Name the failure you are designing for, then the recovery path.
Follow-up
- How does this behave when that dependency is down for an hour?
- What breaks first when traffic grows ten times?
Serve as-of instrument definitions to every service on the path
instrument_version is effective-dated (SCD2), keyed by instrument_id plus effective_from and effective_to, carrying venue_mic, venue_symbol, tick_size, lot_size, price_scale, contract_multiplier, trading_status and expiry_date. Every service resolves through it: the order path reads price_scale and tick_size on every order at 10,000 orders/second per process, and the replay engine must resolve a symbol exactly as the live session did months earlier. Writes are a nightly corporate-action load plus intraday vendor corrections. Design the read path: cache shape and key, how a correction reaches processes already holding a snapshot, and what invalidation rule you enforce.
Approach
- Separate the two read patterns instead of building one lookup API. The order path needs an as-of-now read in nanoseconds with no network hop. Replay needs an as-of-T read, resolving the version whose [effective_from, effective_to) window contains T, and can afford one lookup per instrument at run start. Both are materialised from the same rows by one loader.
- Shape the hot-path cache as a process-local array indexed by a dense internal instrument index, resolved from symbol once at subscription time. At 10,000 to 1,000,000 instruments and roughly 100 bytes per definition that is 1 MB to 100 MB, which fits in RAM; a direct array index costs single-digit nanoseconds where a hash lookup on a symbol string costs tens and touches more cache lines.
- Invalidate by version rather than TTL. The loader builds a complete immutable snapshot with a monotonic snapshot_id and the reader swaps a pointer to it, so no reader ever observes a half-applied corporate action. This falls out of the storage model anyway: SCD2 rows are closed and superseded, never edited, so a correction is always a new snapshot.
- Pin replay to the snapshot the live session used by recording snapshot_id on strategy_run. A vendor correcting yesterday's tick size today will otherwise change a replay's output with no code change, and the divergence gets attributed to whatever was edited most recently.
- State the trade-off: preloading the full active set costs memory and a slow process start, and a lazy per-symbol fetch costs a network round trip inside the order path. Take the memory, and size it rather than assuming it.
Worked solution 20 min
- Load 200,000 instrument_version rows covering 50,000 instruments with several versions each, add the non-overlap constraint (in PostgreSQL, EXCLUDE USING gist (instrument_id WITH =, tstzrange(effective_from, effective_to) WITH &&) with btree_gist installed), then try to insert an overlapping version.
- Time three resolutions over the same data: the as-of SQL query with a range predicate, a process-local hash lookup keyed by the symbol string, and a direct array index by dense instrument index.
- Swap the snapshot pointer while a reader loop is running and measure the reader's pause and the version stamped on each read.
- Resolve one symbol that was reassigned mid-year, once as of March and once as of today.
Follow-up
- A vendor corrects a tick size for a date three months back. Which research runs are now unreproducible, and how do you enumerate them?
- An instrument is halted at 10:00:00. How many milliseconds until every gateway refuses new orders in it, and what happens to the orders already in flight?
- Where does the price_scale integer convention break down, and what would you do for an instrument quoted in fractions?
Order gateway heap grows only on reconnect days
The order gateway's resident set grows about 400 MB a day, masked by a nightly restart. On a day with three session drops it grew 6 GB in an hour and was OOM-killed mid-session. Order state is indexed two ways: a map keyed by order_id and a map keyed by client_order_id, and client_order_id changes on every replace. Give an ordered checklist that separates a real leak from resident-set growth, attributes it to an allocation site, and names the fix and the regression test.
Approach
- Separate resident set from live heap before anything else. Memory freed to the allocator is not necessarily returned to the operating system, and transparent huge pages and per-thread arenas inflate resident set without live bytes. Compare resident set against the allocator's own accounting over the same window; if live bytes are flat, this is a retention or fragmentation question and a different investigation.
- Take two heap profiles an hour apart under representative load and diff them by allocation site rather than reading current totals. A profiler sorted by total size points at whatever is largest, which is rarely what is growing.
- Correlate growth with traffic shape rather than with elapsed time. Plot live bytes against submits, replaces and inbound execution reports. Growth proportional to replaces implicates the per-message index; growth proportional to reconnects implicates the resend path.
- Audit lifetime against the order state machine. Terminal state is a property of order_id, not of client_order_id: an eviction that fires once when an order reaches filled, canceled, rejected or expired removes exactly one client_order_id and orphans every earlier one the order used. Entries also survive indefinitely when a send times out and no terminal event ever arrives.
- Fix by bounding the per-message index's lifetime rather than its size. Keep it as a lookup from client_order_id to order_id, evict every id attached to an order when that order goes terminal, and give an unresolved send an explicit expiry that resolves through an order status request or the drop-copy stream instead of lingering.
- Add the test that would have caught it: replay a recorded session containing replaces and a mid-session disconnect, and assert both index sizes return to their pre-session values.
Follow-up
- A send times out and no terminal event ever arrives for that order. How long do you hold the entry, and what resolves it?
- Someone proposes an LRU on the client_order_id map. What does that turn the leak into?
- The heap profiler costs around 3% on the order path. Where and when do you run it?
Four days sample coding, design, fundamentals and the practical rounds at deliberately shallow depth, which is enough to surface the topics you did not know were in scope. That map, rather than a guess made on day one, decides where the last three days go.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Online-challenge algorithms: sweeps, prefix sums and edge cases
- Solve the reported streetlight count twice, with a difference array plus prefix sum and with a sorted event sweep, and write when each is preferable (small coordinate range versus sparse lights on a long line).
- Solve the reported diamond-shaped maximum sum: brute force first with its cost, then prefix sums over coordinates rotated 45 degrees so each diamond becomes an axis-aligned square; check the parity of rotated cells on a small matrix by hand.
- Warm up on easy bank-style problems (Two Sum with a hash map, binary string addition from the rightmost digit) writing the test harness before the solution.
- Write the edge-case checklist you will run before every submission.
Deliverable: Two reported algorithm problems solved with complexity stated, plus a reusable pre-submission edge-case checklist.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Data structures from scratch: the reported deque and hashmap
- Implement a ring-buffer deque with push and pop at both ends and O(1) indexing, growing by doubling; write the amortized argument in three sentences.
- Implement a hashmap with chaining, then sketch the open-addressing variant and explain what deletion does to probe sequences.
- State the worst-case cost of a single resize and two ways to keep it off a latency-sensitive path (preallocation, incremental rehash).
- Test both structures with random operation sequences checked against the standard library (std::deque and std::unordered_map, or collections.deque and dict).
Deliverable: Two hand-built structures that pass randomized comparison tests, with written complexity arguments.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Systems and C++ fundamentals
- Write answers for stack versus heap and process layout (text, data, bss, heap, mapped regions, stack), and for MMU translation with page tables, the TLB and page faults.
- Cover the inline keyword (linkage rule versus the optimizer's inlining decision), segfault causes and diagnosis with a core dump, a debugger backtrace and AddressSanitizer.
- Compile a small program that prints sizeof for the same struct with members reordered, and explain every padding byte; then explain Small String Optimization.
- Add the related bank topics: new versus malloc versus placement new, pointers versus references, and what vector growth does to virtual memory.
Deliverable: One page of mechanism-level answers, each with two follow-up questions, and a sizeof experiment you can explain.
Practice prompt ↗Practice prompt ↗04Live-round rehearsal on paper: external merge
- On paper, write the reported merge of N sorted files that do not fit in memory: a min-heap holding one head per file, buffered reads and writes, total cost O(M log N) for M records.
- Trace it by hand on three short files, including one that runs out first and duplicate keys across files.
- Answer the follow-up: if N exceeds the open-file limit, merge in passes and state how many passes a given N and fan-in need.
- Have a partner interrupt with follow-ups while you write, and practise answering without losing your place in the code.
Deliverable: A handwritten, hand-traced k-way merge and a written answer for the multi-pass case.
Practice prompt ↗Practice prompt ↗Worked solution ↗05Scenario design close to the machine
- List causes of latency spikes in a low-latency trading system and, for each, the measurement that would confirm it and the fix.
- Design the cancellable timer scheduler as a min-heap with lazy cancel and as a hashed timing wheel; compare costs and the cancel-versus-fire race.
- Answer the insufficient-disk-space scenario, including du versus df discrepancies from deleted files still held open, and the threads-versus-processes question for a parallel job.
- Work the guide's design exercise 'Serve as-of instrument definitions to every service on the path' and compare your cache and invalidation choices with the worked solution.
Deliverable: Written answers to the four reported scenario questions and a scored attempt at the design exercise.
Practice prompt ↗Practice prompt ↗06Final-round breadth: statistics, debugging and the worked coding exercise
- Review expectation, variance, conditional probability and common distributions; answer when a median fits trading profit data better than a mean.
- Work the debugging drill 'Order gateway heap grows only on reconnect days' as an ordered checklist before reading the approach.
- Solve the worked coding exercise 'Resolve position identity across a corporate action graph' by hand on its fixture, keeping multipliers as reduced rationals, then check against the expected result.
Deliverable: Statistics notes with stated assumptions, a debugging checklist, and the coding exercise's fixture results matched by hand.
Practice prompt ↗Practice prompt ↗07Mock final round and behavioral stories
- Run a back-to-back mock: one coding problem from days 1-2, one scenario design from day 5, one statistics question and one behavioral question, naming the domain aloud before each.
- Prepare stories for disagreeing on a technical direction, prioritizing across competing projects, and a performance problem you diagnosed and confirmed fixed.
- Note where the later questions suffered from the earlier ones, then reduce the week to one card of rules you can state without reading them.
Deliverable: Mock notes on cross-domain carryover, three behavioral stories, and a one-card summary of the week.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
The source does not describe a dedicated behavioral round, but the question bank for this role includes behavioral prompts on disagreeing over a technical direction and prioritizing across competing projects, and the role's on-call duties make production stories relevant. Be specific about the decision you owned and the evidence you used, and be honest about the limits of your expertise; if you do not know a tool, say so and explain how you would reason from fundamentals.
How do you handle segmentation faults, and what are the common causes …
How do you handle segmentation faults, and what are the common causes in high-performance C++ code?
Approach
- State the situation in two sentences and spend the rest on the reasoning.
- Name the disagreement and how you resolved it with evidence.
- Close with what you would do differently, concretely.
Follow-up
- What did you decide not to do, and why?
- How did you know your change caused the improvement?
Ship an order gateway with named, scheduled reconciliation debt
A venue connection must be live for a session start in three weeks. The order state machine, idempotent application on (venue_mic, venue_exec_id) and the pre-trade check are done. Drop-copy reconciliation and trade_correct / trade_cancel handling are not. Describe a time you shipped with known debt. State exactly what you cut, the detector you put in its place, the date the debt was scheduled for, who agreed to it, and what the debt actually cost before it was paid. Include the item you cut that you should not have.
Approach
- Split the cut list into two piles out loud: things that make the system slower to operate, and things that make it silently wrong. Only the first pile is negotiable, and saying so is the whole judgement being tested.
- Place trade corrections in the right pile. A fold that only adds quantities cannot represent a negating entry referencing an earlier execution at all, so cutting trade_correct is not an unhandled edge case — it is a position that is wrong with nothing raised. If you cut it, the compensating control is a break report that blocks the next session, not a log line.
- Attach a detector, a threshold and an owner to every item in the silently-wrong pile. Without drop-copy reconciliation the control is an end-of-day diff of derived against clearing positions, paging a named person on break_qty != 0, with a stated maximum position size until the real thing lands.
- Make the schedule concrete. The debt lands in a named release with a ticket, and the interim risk limit is what gets relaxed when it lands — which gives the desk a reason to care about the date instead of treating it as engineering's problem.
- Report what it actually cost: how many breaks per day, how much manual work, whether any of it reached a position, and which cut you would take back. The signal is your calibration, not your willingness to ship.
Follow-up
- A trade correction arrives for a fill booked three sessions ago. What does your compensating control actually do with it?
- The debt slipped twice. What changes on the second slip?
- How would you scope this if the date were immovable and the desk wanted twice the size?
Reverse a simulator fill model after live results diverged
Your backtest fills a passive order whenever the market trades at its price. Two strategies approved on that model reached production and filled at roughly a third of the simulated rate, turning a projected edge into a loss. Describe a decision you reversed. State what you originally believed and why it was reasonable, the evidence that changed your mind, what reversing cost — including work discarded and strategies withdrawn — and how you made the replacement trustworthy rather than merely newer. Say what you would have measured earlier to shorten the gap.
Approach
- State the original model's appeal honestly before demolishing it: it is cheap, it needs no order-level data, and it is close to right for aggressive orders. The reversal is about scope, not about anyone being foolish.
- Name the mechanism rather than the symptom. A passive order fills only once the quantity resting ahead of it at that price level has traded or cancelled. Touch-the-price assumes queue position zero — the most optimistic assumption available — and it is biased in exactly one direction, which is why the error never showed up as noise.
- Give the comparison you ran: the same recording through both models, reporting fill rate and per-fill P&L, and the ratio of simulated to live fills over the same period. One reproducible ratio on one recording is the evidence; the two withdrawn strategies are its consequence.
- Own the replacement's residual error. A queue model needs the quantity ahead, and at price-level depth you cannot observe cancellations ahead of you, so your estimate is itself wrong — state the direction of that error. A candidate who claims the new model is correct has repeated the original mistake in a more expensive form.
- Describe how you made the reversal stick: re-run the already-approved backlog through both models and publish the per-strategy delta, so the reversal arrives as a number every strategy owner can see rather than as a memo they can ignore.
- Close on the earlier measurement. The live-versus-simulated fill ratio is computable in the first days of any passive strategy; tracking it from day one turns a six-month surprise into a two-week correction.
Follow-up
- Price-level data hides cancellations ahead of you. How wrong can your queue estimate be, and in which direction?
- How do you stop the new model being tuned until it happens to reproduce the live result?
- What does the reversal imply for strategies that were rejected under the old model?
- 01
Tell me about a time you disagreed with a technical direction. How did you challenge it, what evidence did you bring, and how did you commit once the decision was made?
- 02
Describe a period when several projects competed for your time. What did you prioritize, what did you drop, and how did you tell the people affected?
- 03
Tell me about a performance problem you diagnosed in a running system: how you measured it, what the cause was, and how you confirmed the fix worked.
- 04
Walk through the test cases you write before submitting a coding solution, and describe a case that caught a real bug for you.
- 05
Describe a time you shipped with known debt: what you cut, what detector you put in its place, and what the debt cost before it was paid.
- 06
Describe a technical decision you reversed after evidence showed it was wrong, what reversing cost, and what you would have measured earlier.
Is this an official Hudson River Trading interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Hudson River Trading. Rounds and questions reflect what candidates have reported, not a process Hudson River Trading has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗What stages does the Hudson River Trading Software Engineer process have?
Candidates report three stages over about three to five weeks: online coding challenges, several live technical interviews, and an onsite or final-round interview. The source says the final round can cover system design, algorithm development and, depending on the team, probability or statistics. This is a candidate-reported process, not a published one.
PracHub Software Engineer practice ↗How much time should I spend preparing?
It depends on how current your systems knowledge is. Start before the online challenge, not after it, since the reported process runs about three to five weeks. Weight practice toward depth: implementing a deque or hashmap from scratch and defending its complexity teaches more than a batch of easy problems, and the reported low-level questions reward explaining mechanisms rather than recalling definitions. The seven-day plan on this page gives a compressed order to follow.
PracHub interview research ↗Do I need trading or low-latency experience?
The requirements in the source notes list low-latency or high-frequency trading exposure as nice-to-have, not must-have. The must-haves are proficiency in C++ or Python with a solid grasp of the standard library and memory model, strong data structures and algorithms, and experience with Linux and command-line tools. Without trading background, you can still prepare the reported scenario questions (latency spikes, timer scheduling, disk space) from operating-system and performance fundamentals.
PracHub interview research ↗Should I interview in C++ or Python?
The source names C++ or Python as the typical languages. Pick the one you can explain below the surface. Several reported questions (inline, segfaults, struct alignment, Small String Optimization) are specific to C++, so review them even if you code in Python. If you choose Python, also be ready for runtime questions such as reference counting, the GIL in standard CPython builds, and per-object memory overhead.
PracHub Software Engineer practice ↗Will I have to code without an IDE?
Possibly. The source says live technical interviews may use remote coding environments or traditional paper-and-pencil problem solving. Practise both: timed problems in a plain editor with autocomplete off, and full functions written by hand and traced on a small input with a table of variable values, including the empty and one-element cases.
PracHub Software Engineer practice ↗Will I be asked probability or statistics?
It depends on the team. The source says the final round can cover probability or statistics depending on the team's focus, and the question bank for this role includes a question on choosing a mean or median for trading profit metrics. Review expectation, variance, conditional probability and common distributions, and practise explaining which summary statistic fits heavy-tailed data.
PracHub Software Engineer practice ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-24