Interview guides · Point72
Point72 Software Engineer Interview Guide
Point72 Software Engineer interview guide: OA, phone screen and final stage, reported coding, SQL and design questions, plus a 7-day prep plan.
The 60-second brief
Point72 is a global multi-strategy hedge fund. Candidates and role descriptions indicate that its Software Engineers build data pipelines, low-latency execution engines, risk blotters, real-time trading UIs and developer platforms, working with quantitative researchers, portfolio managers, compliance officers and infrastructure engineers. Candidates are reportedly recruited into specific technology groups, including Execution Services, Data Infrastructure, AI & Data Technology, SDLC Engineering and Cubist Systematic Strategies, the firm's quantitative trading affiliate.
This guide covers the three stages candidates report for the Point72 Software Engineer role: an online assessment, a technical phone screen, and a final stage run as a Superday or as back-to-back virtual rounds. It also covers the reported coding, language-internals, SQL, design and behavioral questions. The worked exercises are on order-event SQL, deduplicating an execution stream and order-entry error handling, and the seven-day plan maps each day to a stage.
Focus areas
- Reconcile derived positions against clearing every day
- Detect sequence gaps before pricing off a book
- Fold execution reports idempotently on venue exec id
Overview
The work described for the role differs by group. In Execution Services and Data Infrastructure it covers microservices and user interfaces for front-office and back-office operations. That includes real-time React and TypeScript UIs for order entry, blotters and risk dashboards, and backend services in Java or Python. In Cubist the work is ETL pipelines, high-throughput market data feeds, and research tools for quantitative developers and researchers. In platform groups such as SDLC Engineering and DevSecOps it means CI/CD pipelines on GitHub Actions, containerized workloads on AWS ECS and EKS, developer tooling, and security and compliance automation.
For preparation, this range has one practical effect. Reported questions mix standard data structures and algorithms with language internals (Java garbage collection, Python copies and dictionaries, the JavaScript event loop), SQL, and design problems framed around trades and market data. Ask your recruiter which group the role sits in and weight your preparation toward that group's stack. Keep DSA and SQL sharp either way, because candidates report both in the online assessment.
The Point72 interview process
Point72 candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.
Online Assessment
Candidates describe an online assessment on a platform such as HackerRank. It typically tests data structures and algorithms, math or logic puzzles, or domain skills such as SQL and Python. An automated grader gives no partial credit for intent and nobody is there to answer questions, so the worked examples in each prompt are the full specification. Until the recruiter confirms the section mix, assume it is unknown and prepare coding, SQL and a few logic problems, not algorithms alone.
What to demonstrate
- Whether your solutions pass cases the examples do not show, such as empty input, a single element, duplicates, or cycles in a graph
- Whether you choose an approach that fits the input, for example Dijkstra for non-negative edge weights and Bellman-Ford when weights can be negative
- Whether you can write correct SQL with joins, aggregation and window functions when the assessment includes a SQL section
How to prepare
- Do timed sets in a plain browser editor with autocomplete off. Mix a graph or tree problem, an array or string problem, and a SQL query.
- Before you submit, run the given examples plus an empty case and a single-element case, and compare expected output against actual
- Work through this guide's order_event SQL exercise and the bank question on clustered versus non-clustered indexes
- Add a set of logic and probability puzzles, especially if the role sits in Cubist or another quantitative team
Technical Phone Screen
Candidates describe a technical phone screen with an engineering team member or the hiring manager. It combines a walkthrough of your resume with live coding or an object-oriented design question, and live SQL queries are also reported at this stage. Someone is watching, so your reasoning is visible. State your approach and its complexity before you type, and be ready to explain why you chose a particular structure or pattern.
What to demonstrate
- Whether you can explain the architecture, the trade-offs and your own contribution for each project on your resume
- Whether your live code is correct and your narration matches what you are writing
- Whether an object-oriented design keeps interfaces separate and coupling low
- Whether you adapt when the interviewer adds a constraint partway through a problem
How to prepare
- Write a short and a long version of your main project walkthrough, each naming one trade-off and one production issue
- Practise object-oriented design on the reported order-entry question: classes for the order, validation, the risk check and the venue gateway, plus the interface between each
- Run mock coding sessions in which a partner changes one constraint halfway through, and practise restating the new constraint before you refactor
- Rehearse language-internals answers for your primary language: memory management, hashing and concurrency
Final Stage
Candidates describe the final stage as a Superday or a series of back-to-back virtual rounds. It reportedly includes several technical sessions on live coding, system design, architecture or a debrief of a take-home case study, plus a behavioral and leadership interview with the hiring manager or quantitative leads. For the design and architecture sessions, practise on the reported design questions, which use market data and trading scenarios, and start every answer from requirements rather than components.
What to demonstrate
- Whether a design starts from requirements and states throughput, latency and consistency needs before it picks components
- Whether you handle duplicates, gaps and replays in a data pipeline, and can justify your concurrency and isolation choices
- Whether you can defend a take-home or a past design under follow-up questions
- Whether your behavioral answers show ownership and give a clear, concise account of your decisions
How to prepare
- Work this guide's order-entry error-classification and execution-stream exercises, then answer the reported ingestion-pipeline and message-transport design questions aloud
- Run the cache-stampede debugging drill as an ordered checklist, and say aloud how you would tell real demand growth from a stampede
- If you submitted a take-home, reread it and list its weakest decisions and what you would change
- Prepare stories for an outage, a disagreement with a senior engineer, a mid-project requirement change, and why Point72
Where candidates lose points
Preparing only algorithms for the online assessment and then meeting a SQL or logic section cold.
Candidates describe the assessment as covering data structures and algorithms, math or logic puzzles, or domain skills such as SQL and Python, so split your practice across all three. For SQL, drill joins, GROUP BY with HAVING, and ROW_NUMBER() and RANK() windows until you can write them without looking up the syntax. Check every query for join fan-out before you trust a SUM, as the desk-report drill shows.
Being unable to defend a technology or design choice from your resume during the phone-screen walkthrough.
The phone screen reportedly combines a resume walkthrough with live coding or object-oriented design. Prepare every project you list so you can discuss your own contribution, the trade-offs and the lessons from production issues. For each one, write down what you built yourself, the alternative you rejected and why, and one production issue with its root cause. Anything on the resume you cannot discuss at that depth is a liability.
Losing the thread when the interviewer changes a constraint partway through a live coding problem.
Candidates report requirements changing mid-problem, for example a new memory limit or a switch to streaming input. Structure your code so one change touches one function. When a constraint changes, restate it aloud, say which part of your approach it breaks and what fixing that costs, then refactor only that part. To practise, have a partner change one constraint halfway through each mock problem.
Answering a language-internals question as if it were an algorithms question.
Reported questions ask how Java garbage collection and collection classes affect performance, how Python deep and shallow copies and dictionary lookups work, and how the JavaScript event loop handles asynchronous events. For your primary language, prepare a short explanation of the memory model, of hashing and collision handling, and of the concurrency primitives. Tie each one to a concrete performance effect you could point to in code.
Naming components in a design question without saying what each one gives up.
For reported design prompts such as the real-time ingestion pipeline or REST versus WebSockets versus message queues, start from the property the system needs: ordering, replay, fan-out to many consumers, or idempotent backfill. Then choose components, say what each choice sacrifices, and explain how the system behaves when a feed duplicates or drops events. The execution-fold and error-taxonomy exercises in this guide give you concrete vocabulary for this.
Questions & practice
Given a weighted directed graph, find the shortest path between nodes while handling potential cycles.
Medium · data structures and algorithms
Approach
- Choose the data structure from the access pattern, not from familiarity.
- Name the brute-force solution and its complexity before improving on it.
- State the target complexity and say which constraint rules the naive version out.
Follow-up
- How does this change if the input no longer fits in memory?
- What is the worst case, and how likely is it on real data?
Demonstrate how to perform string parsing and manipulation efficiently without excessive memory allocations.
Medium · data structures and algorithms
Approach
- Restate the input: its shape, its size, and what is guaranteed about it.
- Name the brute-force solution and its complexity before improving on it.
- State the target complexity and say which constraint rules the naive version out.
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?
Describe how to design an order-entry system using object-oriented principles, ensuring low coupling and clean interface separation.
Medium · languages, concurrency and fundamentals
Approach
- Name what is shared across threads and what owns each piece of state.
- Say what the runtime actually does before reasoning about the code.
- Distinguish a value from a reference to it, and say which one you handed out.
Follow-up
- How would you prove the race exists rather than suspect it?
- What happens if two callers reach this at the same time?
Explain the difference between deep and shallow copies in Python and how memory is allocated for core data structures like dictionaries and lists.
Medium · languages, concurrency and fundamentals
Approach
- Distinguish a value from a reference to it, and say which one you handed out.
- Reach for the cheapest primitive that closes the race, not the broadest lock.
- Say what the runtime actually does before reasoning about the code.
Follow-up
- Where could this allocate more than you expect?
- How would you prove the race exists rather than suspect it?
Fold a duplicated execution stream into per-order state
Easy · idempotency · hashing · aggregation
A day's execution reports arrive as one unordered array of tuples (venue_mic, venue_exec_id, order_id, exec_type, last_qty, cum_qty, leaves_qty). The same execution appears up to three times, once from the trading session, once from the drop copy, once from the clearing file. Assume 10^7 rows over 10^6 orders, and ignore trade_correct and trade_cancel for now. Given a map order_id to order_qty, return each order's final cum_qty and leaves_qty, plus the list of orders whose final cum_qty exceeds order_qty. One pass, O(n) expected time; justify your space and say what you would give up to shrink it.
Approach
- Notice what the deliverable needs before reaching for a dedupe set: final cum_qty and leaves_qty are a max-fold over the venue's own cumulative field, and max is idempotent, so a resent report changes nothing. That reduces state to one record per order_id in an open-addressed hash map keyed by a 64-bit id, roughly 24 bytes each, about 24 MB at 10^6 orders.
- Fold under an explicit total order rather than arrival order: keep the report with the greatest cum_qty, breaking ties toward the smaller leaves_qty. The tie-break is what lets a canceled report (same cum_qty, leaves_qty 0) displace the partial fill that preceded it without consulting any clock, which matters because the three sources arrive interleaved.
- Reintroduce deduplication only for the fields that are sums rather than maxima: commission_amt, fee_amt, rebate_amt, and any fill count. Those need a set on (venue_mic, venue_exec_id), and at 10^7 executions with roughly 50-byte keys that is about a gigabyte of state. Hashing the key to 64 bits shrinks it to around 256 MB but accepts a collision probability near n^2/2^65, about 3e-6 per run, where a collision silently drops a real fill.
- Emit the violation list in the same pass: any order whose winning cum_qty exceeds its order_qty. The fold exists to protect that invariant, so the check belongs inside the job rather than in a dashboard that reads its output later.
- State the bound: O(n) expected time, O(orders) space, and the job is I/O bound on reading 10^7 rows rather than bound by the fold itself.
Worked solution (20 min)
- Build the fixture: order 1001 with order_qty 1000 has E1 (partial_fill, last 300, cum 300, leaves 700) and E2 (fill, last 700, cum 1000, leaves 0); order 1002 with order_qty 500 has E3 (partial_fill, last 200, cum 200, leaves 300) and E4 (canceled, last 0, cum 200, leaves 0).
- Duplicate E2 twice and E3 once, so the array holds 7 rows for 4 distinct executions, then shuffle it with a fixed seed.
- Run the max-fold with the (cum_qty desc, leaves_qty asc) tie-break and record both per-order outputs and the violation list.
- Add a corrupt row E5 for order 1001 with cum 1300, leaves 0, rerun, and confirm 1001 now appears in the violation list rather than being silently accepted.
- Swap the max-fold for a sum of last_qty on the same shuffled array and record what order 1001 reports.
Expected result: Order 1001 finishes cum_qty 1000 / leaves_qty 0; order 1002 finishes cum_qty 200 / leaves_qty 0 (the cancel wins the tie against the partial fill at cum 200); the violation list is empty until E5 is added, then contains only 1001. The sum-of-last_qty variant reports 2400 for order 1001 (300 + 700 x 3) and 400 for order 1002.
Verify it yourself
- Re-shuffling the input array with a different seed produces byte-identical output; if it does not, the fold still depends on arrival order somewhere.
- Tripling every row in the array changes nothing, which is the operational definition of idempotent here.
- Order 1002 ends with leaves_qty 0, not 300 — if it ends at 300 the tie-break is missing and a canceled order still looks live.
Follow-up
- Now admit trade_correct and trade_cancel, which arrive later and reference an earlier venue_exec_id via corrects_exec_id. What breaks in the max-fold, and what replaces it?
- The clearing file disagrees with the session on cum_qty for 40 orders. Which side do you write into position_snapshot, and what do you do with the difference?
- How would you make this fold restartable partway through a 10^7-row file without recomputing from the beginning?
A desk report that silently multiplies filled quantity
Medium · join-fanout · aggregation · query-rewrite
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?
Current order state from an append-only event log
Easy · window-functions · indexing · event-sourcing
order_event is append-only: event_id BIGINT PRIMARY KEY, order_id BIGINT, strategy_run_id UUID, seq_no INT (1-based and contiguous per order, UNIQUE (order_id, seq_no)), status_after, cum_qty, leaves_qty, received_ts TIMESTAMPTZ(6). The table holds 10^8 rows across 10^7 orders. Write the query returning current status_after, cum_qty and leaves_qty for every order in a supplied list of order_ids, then the variant returning current state for all orders of one strategy_run_id. State the index each needs, and why ordering by received_ts instead of seq_no is wrong.
Approach
- For the id list use SELECT DISTINCT ON (order_id) ... ORDER BY order_id, seq_no DESC, which takes the first row of each group and stops. The portable equivalent, ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY seq_no DESC) = 1, ranks every row of every group before discarding all but one.
- Index (order_id, seq_no DESC). A btree can be scanned forwards or backwards as a whole, not per key, so the mixed-direction ORDER BY (order_id ASC, seq_no DESC) is not satisfied by (order_id, seq_no) and the planner inserts a sort.
- For the strategy_run_id variant the leading column changes to (strategy_run_id, order_id, seq_no DESC); otherwise the run's orders are found by a scan and grouped afterwards, which is the difference between 10^4 index descents and 10^8 row reads.
- Justify seq_no over received_ts concretely: received_ts is neither unique nor monotonic across reports. Two reports can share a microsecond, and a resend after a reconnect can arrive after a report it precedes, so MAX(received_ts) picks a non-deterministic row and can return an older state than the one the system acted on.
- Name the cost that motivates a maintained order_current table: this query is one index descent per order in scope, fine for a list and unacceptable for a dashboard refreshing every live order in a session. The trade-off is a second write on the order path and a crash window between the append and the update.
Worked solution (20 min)
- Seed one order with six events, giving two of them the same received_ts and inserting the final event with a lower event_id than its predecessor.
- Run the DISTINCT ON query and a MAX(received_ts) variant against that order and diff the two rows.
- EXPLAIN the query under both index shapes, (order_id, seq_no) and (order_id, seq_no DESC), and look for the sort node.
- Repeat for the strategy_run_id variant over a run holding 10^4 orders.
Expected result: One row per order_id carrying the highest-seq_no state. The MAX(received_ts) variant returns a different, older row for the order with the tied timestamp, and the DESC index removes the sort node from the plan.
Verify it yourself
- The result row count equals the number of distinct order_ids requested.
- For every returned row, seq_no equals MAX(seq_no) for that order.
- With the DESC index in place the plan shows no sort node above the index scan.
Follow-up
- How do you assert seq_no is contiguous per order, and what should the system do when it is not?
- Write the query listing orders whose latest row violates cum_qty + leaves_qty = order_qty while the order is live.
- How would you keep order_current correct if the writer can crash between appending the event and updating the summary row?
Explain the difference between REST, WebSockets, and messaging queues (such as Kafka or RabbitMQ) when architecting real-time event-driven systems.
Medium · system design and architecture
Approach
- Fix the scope first: who calls this, how often, and what they do when it fails.
- Name the read and write paths separately; they rarely have the same bottleneck.
- State the consistency you need, and where you are willing to be stale.
Follow-up
- What would you drop to keep the system up under load?
- What breaks first when traffic grows ten times?
Design a real-time data ingestion pipeline that ingests data from external APIs, transforms it, and persists it for quantitative analysis.
Medium · system design and architecture
Approach
- Name the read and write paths separately; they rarely have the same bottleneck.
- State the consistency you need, and where you are willing to be stale.
- Choose a partition key and say what query it makes expensive.
Follow-up
- What breaks first when traffic grows ten times?
- What would you drop to keep the system up under load?
Classify order-entry errors as retryable, terminal, or unknown
Easy · error-taxonomy · retries · partial-failure
Your order API surfaces failures from four layers: transport (connection reset, write timeout), your own gateway (body validation, throttling, a full send queue), pre-trade risk (a risk_limit row with enforcement='block' and breach_action='reject_order'), and the venue (a reject carrying reject_reason_code). The caller is an automated strategy with no human in the loop. Define the error response body and a taxonomy it can branch on without parsing prose, then map at least six concrete failures to a class and a caller action. State clearly which failures are unknown rather than failed, and which are final even though nothing ever left your process.
Approach
- Fix the body first: a stable machine-readable code (closed string enum), a class field, a human message that carries no contract, the caller's client_order_id echoed back, and retry_after_ms where the class is retryable. Callers must never regex the message.
- Classify on two independent facts and only then collapse them into one class. Fact one: do you know for certain that no order from this request is live at the venue, either because nothing was sent or because the venue explicitly rejected it? That decides whether resending the same client_order_id is SAFE. Fact two: is the cause transient, so that the identical bytes could be accepted later? That decides whether resending can SUCCEED. A taxonomy that answers only the first question livelocks: the caller resends a deterministic rejection until its retry budget is gone, every attempt safe and every attempt identical.
- TERMINAL: the outcome is known and final, do not resend. This covers every deterministic rejection, wherever it was raised: a pre-trade risk block on max_order_notional, a 400 from body validation, a venue reject of an unknown symbol. Nothing about the order, the limit or the symbol changes by asking again with the same bytes, so a retry only burns rate budget and delays the strategy's own decision to do something different.
- RETRYABLE: no order exists at the venue AND the cause is transient, so the identical request can be accepted on a later attempt. That is a short list: 429 from your own throttle, a local send queue at capacity, connection refused before any byte was written, an explicit venue 'system busy' or 'session not ready' reject. Only this class carries retry_after_ms, and only it may be resent under the same client_order_id.
- UNKNOWN: you cannot rule out a live order, so the outcome is not knowable locally: write timeout, connection reset mid-write, no response at all, a 5xx your gateway raised after the venue write. Resolve by order status query or drop copy, and send nothing.
- Derive the class from what you know about venue state and whether the cause is deterministic, never from the HTTP status. A 500 from your own gateway raised after the venue write is UNKNOWN even though it is your error; a 400 from body validation never left your process and is still TERMINAL, because the same bytes fail the same check every time. For risk rejects, carry limit_id and limit_version in the payload so a rejection can be explained months later against the exact value in force, per risk_limit's PRIMARY KEY (limit_id, limit_version).
- Write the caller-side branch: TERMINAL records the code, updates local state and stops, and any follow-up (a smaller quantity after a notional block) is a new order with a new client_order_id, not a retry of this one; RETRYABLE resends the same key after retry_after_ms with jitter and a bounded attempt count; UNKNOWN issues an order status request or waits for drop copy, and never sends anything.
Worked solution (20 min)
- Write the error body as a concrete JSON object with every field named and typed.
- List seven failures (write timeout, connection reset after the venue write, connection refused before any byte was written, a 400 from body validation, a max_order_notional risk block, a venue reject of 'unknown symbol', 429 from your own throttle) and assign each a class.
- For each, write two sentences: whether the venue can now be holding an order from it, and what would have to change for an identical resend to be accepted. The pair, not either one alone, determines the class.
- Write the caller's three-branch handler and check by inspection that neither the UNKNOWN branch nor the TERMINAL branch contains a send call.
Expected result: A three-class taxonomy with a machine code per failure: every failure that can leave an order live at the venue is UNKNOWN and its only permitted action is a state query; every deterministic rejection is TERMINAL whether the venue, pre-trade risk or your own validator produced it; and RETRYABLE is reserved for failures that both stayed inside your process and can be accepted on a later identical attempt, with risk blocks carrying the limit version they were evaluated against.
Verify it yourself
- For every entry classed RETRYABLE, state both why no order from that request can be live at the venue and what would have to change for an identical resend to be accepted; if you cannot state the first it is UNKNOWN, and if you cannot state the second it is TERMINAL.
- Drive a deterministic failure in a loop (the same breached limit, the same malformed field): the caller must stop after the first response rather than resend, and no branch may resend after a timeout.
- Every error code appears in a closed enum that a client can pin, and the human message appears nowhere in the caller's logic.
Follow-up
- A venue reject arrives for an order your gateway already marked rejected locally on a risk block. Which one wins in the local state machine, and what does the API return to a status query in between?
- How do you add a new reject_reason_code without breaking callers that switch exhaustively on it?
Instrument cache flush degrades every book at once
Medium · cache stampede · retry amplification · as-of queries
At 13:05 a vendor correction reaches the reference service. Within two seconds its request rate rises forty-fold, p99 goes from 3 ms to 2 s, callers time out and retry, and the market data gateway marks books degraded because it cannot resolve symbology. Every service caches instrument_version with a five-minute TTL and flushes the entire cache on a correction notice. Give an ordered checklist that distinguishes a capacity problem from a stampede, and a fix that preserves as-of resolution for replay.
Approach
- Decide whether demand rose or supply fell. Compare distinct keys requested against total requests through the spike window. Forty times the requests over roughly the same key set is a stampede compounded by retries; forty times the distinct keys is genuine new demand and wants a different fix entirely.
- Check whether misses are correlated across processes. A global flush makes every instance miss the same key in the same instant, so the service receives one request per instance per key rather than one request per key. A uniform TTL does the same thing more slowly by aligning expiry across instances that started together.
- Quantify what the callers add before touching the server. A client that retries a timeout without backoff and without jitter multiplies an overload it is already inside; count retries separately from first attempts and state the amplification factor.
- Narrow the invalidation. A correction affects specific instrument_ids, and flushing discards 10^4 to 10^6 unrelated entries to fix a handful. Invalidate by key, which requires the notice to carry the affected instrument_id list rather than being a bare signal.
- Coalesce and stagger. One in-flight fetch per key per process with concurrent callers awaiting that single fetch bounds fan-out to the process count; TTL jitter stops survivors re-aligning; a bounded retry budget with backoff stops the client side recreating the burst.
- Handle the degraded case honestly. Serving a stale definition is only acceptable where the caller records which version it served, because a replay must resolve symbology exactly as the live session did; where it cannot, the correct behaviour is the one the gateway already took - mark the book degraded rather than price off a guess.
Follow-up
- Stale-while-revalidate would have kept the gateway quoting through this. When is that the right call, and when does it become a correctness bug?
- The correction notice is a topic every service subscribes to. What would you change about the notice itself?
- What signal would have made this visible as a design flaw before it was an incident?
Suggested practice checklist
Roughly ninety minutes on weeknights with one longer weekend block. The plan cuts scope rather than compressing everything, on the assumption that one thing finished per night beats four half-started.
Day 1 — Online assessment baseline: algorithms
- Solve the reported shortest-path question on a weighted directed graph cold in a plain editor. State whether the weights can be negative, and choose Dijkstra or Bellman-Ford to match.
- Solve one tree traversal problem and one BFS problem, then rewrite the traversal to use less extra space and state the new space bound.
- For every problem, run the examples plus empty, single-element and cyclic inputs before calling it done, and log each failure you catch.
Deliverable: Three solved problems, each with its complexity written above the code, plus a list of the edge cases your first attempts missed.
Day 2 — Online assessment and phone screen: SQL
- Work the 'Current order state from an append-only event log' exercise, including the EXPLAIN comparison of the two index shapes.
- Attempt the desk-report join fan-out drill: find both fan-outs, prove each with a cardinality probe, then rewrite the query.
- From memory, write queries that use GROUP BY with HAVING, ROW_NUMBER() and RANK(), and an outer join. Then explain clustered versus non-clustered indexes aloud.
Deliverable: A page of working queries and one paragraph explaining when an index removes a sort and when a join multiplies a SUM.
Day 3 — Language internals and object-oriented design
- For your primary language, write short answers on memory management and garbage collection, on hashing and collision handling in the built-in map, and on the concurrency model.
- Answer the reported Python question on deep versus shallow copies with a small example in which a nested list shared by two copies gets mutated.
- Design the order-entry system from the reported OOD question. Name the classes, their interfaces, and what each class must not know about the others.
- Give each answer aloud once without notes.
Deliverable: Internals notes for one language and a class diagram for order entry with each interface listed.
Day 4 — Phone screen rehearsal
- Prepare a short and a long walkthrough of your main project. Each should name your own contribution, one rejected alternative and one production issue.
- Work the 'Fold a duplicated execution stream into per-order state' exercise live with a partner. Partway through, the partner adds one of its follow-up constraints, such as admitting trade corrections.
- Solve the reported string-parsing question while narrating your memory allocations, and point out where the solution copies data it could slice or stream instead.
Deliverable: Two rehearsed project walkthroughs and notes from one live coding session, including how you handled the mid-problem change.
Day 5 — Final stage: system design
- Work the order-entry error taxonomy exercise, and check that no UNKNOWN or TERMINAL branch sends anything.
- Design the reported real-time pipeline that takes data from external APIs to storage for quantitative analysis. Cover duplicate and late data, backfill, and idempotent writes.
- Compare REST, WebSockets and message queues for one real-time use case, stating what each gives up.
- For a high-throughput writer, go through database locks and transaction isolation levels: which anomalies each level allows and what preventing them costs.
Deliverable: One pipeline design with its failure handling written out, plus a one-page comparison of the three transports.
Day 6 — Final stage: debugging, behavioral and puzzles
- Run the instrument cache-flush debugging drill as an ordered checklist, and write the fix that keeps as-of resolution intact for replay.
- Prepare stories for the reported behavioral prompts: an outage under pressure, a disagreement with a senior engineer, a requirement change midway through a project, and why Point72.
- Use the simulator fill-model reversal prompt as a template for structuring a story about a decision you reversed.
- If the role sits in Cubist or another quantitative team, work a set of logic and probability puzzles aloud.
Deliverable: A written debugging checklist and four or five behavioral stories, each stating your contribution, the trade-off and the result.
Day 7 — Final stage mock and taper
- Run back-to-back mocks in one sitting, covering a coding problem, a design question and a behavioral set, to match the Superday or consecutive virtual rounds candidates describe.
- Write down the moments where you lost the thread, and fix only those.
- Confirm logistics with the recruiter: your language choice, which group the role sits in, and whether the final stage includes a take-home debrief.
- Reread your notes from the week and open no new material.
Deliverable: Mock notes listing each failure moment with its fix, plus a one-page card with your project numbers, design structure and logistics.
Risky changes and the reversal path
Candidates report resume deep dives in the phone screen and a behavioral and leadership interview with the hiring manager or quantitative leads in the final stage. For each story, prepare your own contribution, the trade-off you made, and a result whose measurement you can explain. Keep answers direct: state the decision, then the trade-off, then offer to go deeper.
- Walk me through a complex technical project on your resume: What were the major architecture choices, trade-offs, and your specific contributions?
- Tell me about a time when an interviewer or stakeholder shifted requirements unexpectedly midway through a project. How did you adapt?
- Why do you want to work at Point72, and how does your technical background align with financial market technology?
- Tell me about a time a stakeholder challenged one of your architectural decisions. How did you respond, and what changed?
Describe a situation where you had to debug a critical production outage under tight deadline pressure. How did you identify the root cause?
Medium · behavioural and engineering judgement
Approach
- Give the blast radius: what could have broken, and what you measured.
- State the situation in two sentences and spend the rest on the reasoning.
- Name the disagreement and how you resolved it with evidence.
Follow-up
- What did you decide not to do, and why?
- How did you know your change caused the improvement?
Give an example of a time you disagreed with a senior engineer or team lead on a technical approach. How did you resolve it?
Medium · behavioural and engineering judgement
Approach
- Pick a story where you made the decision, not one where you watched it.
- Name the disagreement and how you resolved it with evidence.
- Close with what you would do differently, concretely.
Follow-up
- How did you know your change caused the improvement?
- What did you decide not to do, and why?
Reverse a simulator fill model after live results diverged
Medium · simulation fidelity · queue position · reversal
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?