According to the source notes, a Software Engineer at IMC designs, develops and optimizes software that supports trading strategies and trading systems. The listed responsibilities are concrete: building software to meet the needs of traders and researchers, defining requirements with cross-functional teams, keeping trading systems reliable and scalable through testing and optimization, taking part in code reviews, and analysing system performance to recommend improvements.
The engineering constraints named for the role are high throughput, low latency and real-time processing of large data volumes. That shapes what the reported questions look like. Coding questions range from a balanced-binary-tree check to implementing a hash table, and language questions such as comparing memory management in C++ and Python sit next to them. Design questions include processing millions of transactions per second, a real-time market data feed, and a fault-tolerant trading platform. Problem-solving questions cover optimizing a trading algorithm, choosing performance metrics, debugging an intermittently failing system, and securing a trading application.
The source notes list proficiency in C++ or Python with a solid grasp of object-oriented programming, plus algorithms and data structures, as must-have skills. Experience with real-time systems or trading platforms, knowledge of financial markets, and exposure to cloud and distributed systems are listed as nice-to-have. If your background is outside finance, prepare to reason from latency, ordering and failure rather than from market terminology.
Online Assessment
reportedCandidates report that the process opens with an online assessment testing coding skills and logical reasoning, and that candidates who do well there are invited to the later interviews. Treat it as an execution test. On coding problems, the approach is rarely what costs you. The time goes to a half-remembered library call, an off-by-one in a loop bound, and debugging by random edits until something passes. When output is wrong, take the smallest input that reproduces it and walk the state by hand before you change a line. For logical-reasoning items, write down what the question actually guarantees before you infer anything from it.
What to demonstrate
- Coding skill, which the source notes name for this stage: whether you reach a correct structure and can write it from memory rather than only recall that one exists
- Logical reasoning, which the source notes list next to coding for this stage
- Whether overflow and recursion depth are handled on large inputs: a signed 32-bit int wraps in Java and is undefined behaviour in C++ past 2,147,483,647, and CPython's default recursion limit is 1000 frames
- Whether a failing case is isolated and explained before any edit is made to the code
How to prepare
- From an empty file and with no references open, implement a heap push and pop, an iterative DFS with an explicit stack, and a binary search whose midpoint is lo + (hi - lo) / 2
- For every recursive solution you practise, write an iterative version as well and state the time complexity and the maximum stack depth of each before you run it
- Drill the library calls you look up most (custom-comparator sort, string split and join, ordered-map lower bound) in the language you will use until you no longer need to look them up
- Take a broken solution and, before touching it, write one sentence naming the input, the expected value and the actual value
Technical Discussions
reportedThe source notes say candidates who perform well in the online assessment move on to technical discussions. They do not say which reported questions come up in this stage. The reported technical and coding category mixes implementation with explanation: check whether a binary tree is balanced, implement a hash table, compare memory management in C++ and Python, give quicksort's time complexity against mergesort's, and describe a time you optimized existing code. Prepare each one as a conversation that can go two follow-ups deep. A slogan such as 'Python has a garbage collector' or 'quicksort is n log n' is where the follow-up starts, not where the answer ends. Also read the input bounds of any coding prompt, because they tell you which complexity class you can use.
What to demonstrate
- Whether a language answer goes past slogans: stack versus heap, RAII and unique_ptr/shared_ptr ownership in C++, against CPython's reference counting plus a cycle-detecting collector
- Whether a complexity claim includes the worst case and what triggers it, such as quicksort's O(n^2) on a bad pivot sequence against mergesort's guaranteed O(n log n) and O(n) extra space for arrays
- Whether an implement-this answer covers the design choices: collision strategy, load factor, resize cost, and the worst case when many keys collide
- Whether the approach is justified by the stated input size and you can name the bottleneck in your own solution
How to prepare
- Write one page comparing C++ and Python memory management: who frees an object and when, what a shared_ptr cycle does (it leaks unless one edge is a weak_ptr), and why a CPython reference cycle is reclaimed only by the cycle collector
- Implement a hash table twice, with separate chaining and with open addressing plus linear probing. Include resizing, and explain why insert is amortized O(1) but O(n) in the worst case
- Build a quicksort-versus-mergesort table: average and worst time, extra space, stability, and when each is the better choice (mergesort for linked lists and external sorting, randomized or median-of-three pivots for quicksort)
- Prepare the code-optimization story with a before and after measurement, the profiler or evidence that located the cost, and what you left unoptimized and why
Behavioral Interviews
reportedThe source notes describe behavioral interviews that assess cultural fit and soft skills. The reported behavioral prompts are: a conflict with a coworker and how you resolved it, how you prioritize across multiple deadlines, a time you took initiative on a project, what motivates you to work in technology and finance, and how you handle feedback and criticism. Each needs a specific story with a decision point, not a description of your general character. Start from the moment you had to choose. Say what the options were, what you did not know, and what you did. Close with what changed as a result, including in your own behaviour. For the motivation question, answer from your own experience with performance-sensitive or data-heavy work. Do not make claims about the firm that you cannot back up.
What to demonstrate
- Soft skills and team fit, which the source notes name as the purpose of this stage
- Whether a conflict or feedback story shows what you changed, rather than only what the other person got wrong
- Whether a prioritization answer names what you dropped or delayed, and who you told
- Whether the technology-and-finance motivation answer is specific to you rather than a line that fits any candidate
How to prepare
- Map one real story to each of the five reported prompts and check that no single project carries more than two of them
- Rehearse each story from the decision point forward and stop before the outcome, then have someone ask what you would do next
- Write the motivation answer as two concrete experiences that drew you to latency, throughput or data-heavy problems, and cut any sentence that could appear in another candidate's answer
- Practise explaining one technical project to a non-technical listener, since a question on this appears in the PracHub bank for the role
System Design
reportedThe source notes describe system design interviews that evaluate architectural skills. The reported design questions are listed as a category: design a system that processes millions of transactions per second, architect a real-time market data feed, weigh microservices against a monolith, reason about data consistency in a distributed system, and design a fault-tolerant trading platform. Some of them start from a throughput or latency target, such as the million-transaction system and the real-time feed. For those, turn the headline number into a per-component budget before you draw a box. Put sequence numbers on every stream so gaps can be detected, and say how a consumer recovers from a gap. For anything that sends orders, decide what happens when a request times out before you discuss scaling. Prepare every design for failure as well as for the happy path.
What to demonstrate
- Whether a throughput target becomes per-component budgets and a partitioning scheme: one million messages a second on a single thread leaves about one microsecond each
- Whether failure is designed in: sequence-gap detection, snapshot-plus-replay recovery, failover, and the fate of in-flight requests
- Whether consistency is chosen per data path (order state and positions versus dashboards) and the cost of each choice is named
- Whether monolith versus microservices is argued as a trade-off in network hops, deployment independence and operational cost, not as a preference
How to prepare
- For the market data feed, write the recovery sequence: detect a gap from sequence numbers, fetch a snapshot with its sequence, buffer live increments meanwhile, discard those at or below the snapshot sequence, apply the rest. The resumable-position-stream exercise drills the same contract
- For the million-transaction system, do the arithmetic aloud: per-message budget, how many partitions, what the partition key is, and which query it makes expensive
- For the fault-tolerant platform, specify an idempotency key per order that is persisted before the send, and recovery by querying order status instead of resending after a timeout
- For distributed consistency, list each piece of state in your design with the consistency it needs and what a stale read would cost
13 candidate reports. Individual accounts describe a particular role and hiring cycle.
IMC Software Engineer interview with no update after round two
After I got through the early stages, the process seemed to be moving normally. I completed what I believed was round two and waited for the next step. The silence afterward threw me off. I never received an official message saying that I hadn’t made it to round three. Instead, I was left to infer what had happened, with no real closure. The lack of communication made the experience drag on even…
Read full experienceIMC Software Engineer home assignment and code review
I started with a home assignment that was split into two parts. They estimated about six hours of work and gave me three days to complete it, so it felt manageable at first. The first part involved implementing something straightforward, along with smaller requirements such as writing a README and adding unit tests. Some of the solution requirements felt subjective and depended on the approach, w…
Read full experienceIMC Software Engineer interview: seven three-minute reasoning sessions
I applied through a school career fair and online, and then received an online logical reasoning test. It was divided into seven sessions. Each session lasted three minutes and had four questions, which made the test feel rushed even though the topics were the kind of things you can practice. After I finished it, I received a second online test with three questions. This one was more technical, a…
Read full experienceIMC Network Engineer interview: modern C++ and multicast protocol questions
My process began with an asynchronous online interview and a technical test. The main technical portion started with a deeper discussion focused heavily on C++. It felt intense from the beginning. The interviewers were welcoming enough, but the questions went well beyond surface-level knowledge and dug into modern C++. I went through additional rounds, and the later stages became even more specia…
Read full experienceIMC Software Engineer interview with coding, system design, and team fit
The process was structured and felt comprehensive. After applying, I completed an online activity and then had an initial screening call. The technical portion moved into coding interviews where I could choose the programming language. The work combined problem-solving discussions with system design questions. There were also team fit and culture checks, along with a feedback step where they revi…
Read full experiencePracHub editorial advice for the preparation topics above.
Answering the C++ versus Python memory-management question with 'C++ is manual, Python has garbage collection' and stopping there.
Prepare the mechanism, not the slogan. In C++, name automatic storage versus the heap, RAII as the reason manual new/delete is rare in modern code, unique_ptr for sole ownership, and shared_ptr's reference count, including the cycle it leaks unless one edge is a weak_ptr. In CPython, name reference counting as the primary mechanism, which frees most objects as soon as their last reference goes away, plus the cycle collector that reclaims reference cycles. Then say what each costs on a latency-sensitive path: allocation and deallocation you do not control, and collector pauses. Aim to be able to say exactly when an object is freed in each language.
Stating quicksort is O(n log n) without its worst case, then being unable to say when mergesort is the better choice.
Give both cases: average O(n log n), worst O(n^2) when pivots repeatedly split badly, as a first-element pivot does on already-sorted input. Name the fixes (random or median-of-three pivots, recursing on the smaller side to bound stack depth at O(log n)). Then compare. Mergesort guarantees O(n log n) and is stable, but needs O(n) extra space on arrays. It is the natural choice for linked lists and for data that does not fit in memory. Quicksort sorts in place and is usually faster on arrays in memory because its access pattern is cache-friendly.
In a fault-tolerant trading platform design, retrying an order send after a timeout on the assumption that the send failed.
A timeout only tells you the reply did not arrive in time. The order may have been received, matched and acknowledged. Retrying turns that unknown into a duplicate: two live orders and twice the intended position. Treat the client order id as an idempotency key, persist it before the send, and on recovery query the order's state instead of resending. Raise this before you discuss replicas and failover, because it is the failure that redundancy alone does not fix.
Answering the 'which metrics would you analyze to improve system performance' question with average latency and CPU usage.
Report latency as a distribution and lead with the tail (p99 and p99.9), because a stall that meets a burst shows up there and disappears into an average. Pair it with throughput, queue depth and error or reject rates, so a latency improvement that comes from shedding load is visible as such. If you mention benchmarking, explain coordinated omission. A closed-loop harness that waits for each reply before sending the next never samples a stall at the rate real traffic would have hit it. Use an open-loop generator and measure from the intended send time.
Telling the coworker-conflict or feedback story as an account of what the other person got wrong.
The reported prompts ask how you resolved the conflict and how you handle criticism, so the answer has to contain your own change. State the disagreement in a sentence or two. Say what evidence you gathered or what you asked the other person, and what you did differently afterwards. Give one concrete result, such as a decision reversed, a process changed or a review habit adopted. If the story ends with the other person admitting they were wrong, pick a different story.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Given a binary tree, write a function to check if it is balanced.
Given a binary tree, write a function to check if it is balanced.
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.
- Choose the data structure from the access pattern, not from familiarity.
Follow-up
- How does this change if the input no longer fits in memory?
- Which test case would catch an off-by-one here?
Explain the differences between C++ and Python in terms of memory mana…
Explain the differences between C++ and Python in terms of memory management.
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
- How does this change if the input no longer fits in memory?
- Which test case would catch an off-by-one here?
What is the time complexity of quicksort? How does it compare to merge…
What is the time complexity of quicksort? How does it compare to mergesort?
Approach
- State the target complexity and say which constraint rules the naive version out.
- Choose the data structure from the access pattern, not from familiarity.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- Which test case would catch an off-by-one here?
- What is the worst case, and how likely is it on real data?
Attribute every order to the limit version in force
You have 10^7 order_event rows of type submit sorted by sent_ts, and 10^5 risk_limit versions carrying (limit_id, limit_version, scope_type, scope_id, limit_type, limit_value, effective_from, effective_to nullable). For every order and every scope that applies to it — firm, desk, account, strategy, instrument, asset_class — produce the (limit_id, limit_version) that was in force at sent_ts. Windows are half-open. Write the offline attribution job, state its complexity against a per-order binary search, and define exactly what happens to an order sent at the microsecond a new version becomes effective, and to one whose scope has no version at all.
Approach
- Group the version table by (scope_type, scope_id, limit_type) and sort each group by effective_from. Assert non-overlap inside each group first; the whole job assumes at most one version is in force per group per instant, and an unchecked assumption there produces a plausible answer rather than an error.
- Partition the orders by the same key. Orders already arrive sorted by sent_ts, so each group is a sorted merge: advance the version pointer while the next version's effective_from is at or before the order's sent_ts, then emit the current one. O(n + v) after partitioning, against O(n log v) for a binary search per order.
- Be honest about why the merge wins. At v = 10^5, a binary search is about 17 probes into an array that fits in L2, so the log factor is not the problem; the merge wins because it is a single sequential pass over both inputs with no random access, which at 10^7 orders is a memory-bandwidth story rather than a complexity one.
- Pin the boundary in code and in a test: the window is [effective_from, effective_to), so a version effective at 10:00:00.000000 does not bind an order sent at 09:59:59.999999 and does bind one sent at 10:00:00.000000. The columns are microsecond-precision, so exact ties are common enough to hit in production, not a theoretical edge.
- Use sent_ts rather than decision_ts or the ack's received_ts, because the property being audited is that the order was evaluated against the limits in force when it went out. Record which timestamp you used in the output schema so a later reader cannot silently reinterpret it.
- Emit an explicit no-limit-in-force marker with a reason code when a group has no covering version, rather than omitting the row. An absent row reads as unlimited, and unlimited is the one answer a risk report must never produce by accident.
Worked solution 25 min
- Build one scope group with v1 = [08:00:00.000000, 10:00:00.000000) and v2 = [10:00:00.000000, NULL), plus a second scope with no versions at all.
- Build four orders at 07:59:59.999999, 09:59:59.999999, 10:00:00.000000 and 10:00:00.000001, each touching both scopes.
- Run the two-pointer merge and record the attributed version for each of the eight (order, scope) pairs.
- Re-run with the closed upper bound and diff the output to see which rows change.
- Cross-check the full result against a per-order binary search implementation on a 10^6-row fixture.
Follow-up
- Move this into the live pre-trade path. How does the check read a versioned snapshot without a lock, and how does it record the version it evaluated against?
- A limit tightens at 10:00:00 while three orders are in flight. What should happen to them, and which component decides?
- Scopes nest — a firm limit and an account limit both apply. Do you attribute all of them or only the binding one, and what does the answer cost you at 10^7 rows?
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.
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?
Current order state from an append-only event log
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.
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?
How would you architect a real-time market data feed?
How would you architect a real-time market data feed?
Approach
- State the consistency you need, and where you are willing to be stale.
- 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.
Follow-up
- How does this behave when that dependency is down for an hour?
- What would you drop to keep the system up under load?
How would you approach designing a fault-tolerant trading platform?
How would you approach designing a fault-tolerant trading platform?
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
- What would you drop to keep the system up under load?
- What breaks first when traffic grows ten times?
What considerations would you make for data consistency in a distribut…
What considerations would you make for data consistency in a distributed 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.
- Name the read and write paths separately; they rarely have the same bottleneck.
Follow-up
- What would you drop to keep the system up under load?
- What breaks first when traffic grows ten times?
How would you approach debugging a system that is intermittently faili…
How would you approach debugging a system that is intermittently failing?
Approach
- Clarify what is being asked and what a complete answer contains.
- State your assumptions explicitly before working the problem.
- Say what you would check first and why it is the highest-information step.
Follow-up
- How would you know your answer was wrong?
- What assumption would you test first?
Resumable position stream with a replayable watermark
Risk dashboards and an external allocator subscribe to intraday position updates over long-lived connections. position_snapshot is keyed by (account_id, instrument_id, business_date, snapshot_kind) and carries net_qty, avg_cost_px, mark_px, mark_source ENUM('last_trade','mid','settlement','vendor','stale') and fills_applied_through_exec_id as the watermark. Design the subscribe and resume contract: how a client disconnected for 90 seconds catches up without replaying the day, what happens when its resume point has aged out of retention, and how a consumer that must not double-count handles at-least-once delivery.
Approach
- Assign a server-side monotonic sequence per partition (account_id is the natural partition, since positions are folded per account) that is independent of any wall clock, and make the resume token (partition, last_seq, epoch). The epoch changes whenever the sequence is reset or the partition is rebuilt, so a stale token from a previous session cannot alias a new sequence and quietly skip a day.
- Define resume as 'send from last_seq+1, at-least-once, possibly redelivered', and publish the retention floor as part of the contract so a client knows how long it may be away.
- Make an expired resume point a typed error, not a silent restart from the head: the client must then do snapshot-then-increments, exactly as a market data book resynchronises. It fetches the snapshot with its sequence, buffers live increments arriving meanwhile, discards those at or below the snapshot's sequence, and applies the rest. Skipping the buffering step is what produces a book, or a position, that is wrong and well-formed.
- Prefer state-carrying messages over deltas: a message carrying net_qty, avg_cost_px and fills_applied_through_exec_id is idempotent under redelivery, whereas one carrying '+100' is not. Where a delta is unavoidable, carry the resulting sequence so the consumer can order and deduplicate.
- Put exactly-once at the sink, not on the wire: the consumer persists last_applied_seq per partition in the same transaction as the effect it applies, so a redelivered message is a no-op on restart. No transport gives exactly-once, and a design that assumes one has simply moved the bug.
- Carry staleness to the wire: mark_source='stale' must reach the client rather than being smoothed into a last known mark, so a dashboard can show a stale mark instead of an old number that looks current.
Worked solution 40 min
- Write the subscribe request, the resume request and the message envelope, with the sequence and epoch fields explicit.
- Trace a 90-second disconnect inside retention: list the messages sent on resume and the consumer's state before and after.
- Trace the same disconnect past the retention floor: write the error response and the full snapshot-then-increments sequence, including the buffer and the discard rule.
- Write the consumer's apply transaction showing last_applied_seq and the effect committed together.
- Write what the dashboard renders when mark_source is 'stale' and confirm nothing upstream can overwrite it with a fresher-looking value.
Follow-up
- A slow consumer falls behind the retention floor while still connected. Do you disconnect it, drop messages, or buffer, and what does each choice cost the risk dashboard?
- The allocator needs a consistent cross-instrument view for one account at a point in time. Does per-instrument sequencing give it that, and what would?
- How do you prove after the fact that a client saw a given position value, given that the stream is ephemeral?
Position fold consumer restarts on the same message
At 10:14 the position fold stopped advancing. The consumer crashes and restarts every 30 seconds on an identical stack trace, lag grows, and intraday positions are stale on every desk. The message at the head is an execution_report with exec_type='trade_correct' whose corrects_exec_id refers to an execution id the consumer has never seen. The consumer commits its offset only after a successful apply. Give an ordered checklist to confirm the diagnosis and a fix that neither drops the correction nor blocks the partition.
Approach
- Identify the exact message before theorising. Read the stuck offset and the message itself; a restart loop with an unchanging stack is the cheapest bug in the system to pin down and the one most often guessed at instead.
- Classify it. Malformed and never-processable is one class; well-formed but not yet resolvable is another. A trade_correct whose referent is absent is usually the second: the original execution may have arrived only on the drop copy, may belong to an earlier business date, or may simply be behind this message in another partition.
- Confirm by looking for the referent. Query execution_report on (venue_mic, venue_exec_id) for the corrects_exec_id across all three source values and the adjacent business dates. If it exists somewhere, the defect is a visibility or ordering assumption in the consumer, not the message.
- Clear the head-of-line block without losing the record. Park the message on a retry queue with bounded attempts and increasing delay, and on exhaustion move it to a dead letter queue that raises an alert - never a silent drop, because a correction is the venue restating a trade already booked into a position.
- Make redelivery cheap rather than rare. Apply is idempotent on (venue_mic, venue_exec_id) and the offset commits after apply, so reprocessing costs nothing; a correction is applied as a negating entry against the original rather than an edit, which is what the retained corrects_exec_id is for.
- Alert on the shape, not the symptom. Consumer lag alone says slow; lag with a flat committed offset and a rising restart count says poisoned, and those two want different responses from whoever is paged.
Follow-up
- The referent genuinely does not exist because the pipeline lost the original fill. What now, and what does the position row look like in the meantime?
- How many retries before the dead letter queue, and what makes that number defensible rather than arbitrary?
- Why is applying a correction by updating the original row the wrong shape for this table?
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.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Coding baseline: trees and traversal
- Solve the reported balanced-binary-tree check cold with a top-down height comparison, then rewrite it as a single postorder pass that returns -1 on imbalance. Write one line each on why the top-down version is O(n log n) on a balanced tree and the postorder pass is O(n) because it visits each node once
- Implement an iterative DFS with an explicit stack and a heap push and pop from an empty file, with no references open
- For every bug you hit, write the input, the expected value and the actual value before you edit anything
Deliverable: Two tree solutions with their complexities, a from-blank DFS and heap, and a short list of the bugs you hit with their failing inputs.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Data structures for the technical discussions
- Implement a hash table with separate chaining and again with open addressing and linear probing, including resize, and state amortized versus worst-case insert cost
- Write a quicksort-versus-mergesort table covering average and worst time, extra space, stability and the input that breaks a naive quicksort pivot
- Answer when to use a dictionary versus an array, a question in the PracHub bank for the role, in terms of access pattern, ordering and memory
Deliverable: Two working hash tables, a sorting comparison table, and a one-paragraph dictionary-versus-array answer you can say aloud.
Practice prompt ↗Practice prompt ↗03Language fundamentals: C++ and Python
- Write one page on C++ versus Python memory management: stack and heap, RAII, unique_ptr and shared_ptr, weak_ptr for cycles, CPython reference counting and the cycle collector
- Prepare an answer on what C++ templates are useful for and what they cost in compile time and binary size, a language-features topic listed in the source notes
- Work through time complexity and threading trade-offs, a topic in the PracHub bank: when adding threads helps, and what a lock or shared cache line costs
Deliverable: A memory-management comparison and two short spoken answers on templates and threading, each tested aloud once.
Practice prompt ↗Practice prompt ↗04Worked coding and SQL practice
- Work the limit-attribution exercise (drill-coding-3) end to end, including the half-open boundary cases and the no-limit-in-force marker
- Work the current-order-state SQL exercise (drill-sql-2) and explain why ordering by received_ts instead of seq_no returns the wrong row
- Prepare the reported 'describe a situation where you optimized existing code' story with a before-and-after measurement
Deliverable: Both exercises completed against their checks, plus one optimization story with a measured result.
Practice prompt ↗Practice prompt ↗Worked solution ↗05System design: throughput, feeds and failure
- Sketch the reported million-transactions-per-second system: per-message budget, partitioning key, and what the key makes expensive
- Design the reported real-time market data feed with sequence numbers, gap detection, and snapshot-plus-buffered-increments recovery
- Work the resumable position stream exercise (drill-design-4) and compare its resume contract with your feed design
Deliverable: Two design sketches with written failure and recovery paths, and the completed position-stream exercise.
Practice prompt ↗Practice prompt ↗06Problem-solving and fault tolerance
- Answer the reported fault-tolerant trading platform question with idempotency keys, a policy for requests that time out, and failover, then the distributed-consistency question per data path
- Work the poison-message debugging drill (drill-debugging-5) as practice for the reported intermittent-failure question, and write your bisection procedure
- Prepare the reported metrics, trading-app security and monolith-versus-microservices questions as short structured answers
Deliverable: A written debugging procedure, a consistency table for your design, and three short answers you have said aloud.
Practice prompt ↗Practice prompt ↗07Behavioral stories and a light review
- Map one real story to each reported behavioral prompt: coworker conflict, competing deadlines, taking initiative, motivation for technology and finance, and handling feedback
- Rehearse each story from the decision point forward, and practise explaining one project to a non-technical listener
- Re-read only your notes from days 1-6 and write down the logistics for each stage, including the language you will use for the online assessment
Deliverable: A one-page story map covering all five behavioral prompts and a card with your notes and logistics for the four stages.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
The source notes say the behavioral interviews assess cultural fit and soft skills. The first five prompts below are the reported behavioral questions; the sixth is listed among the reported problem-solving questions but is answered the same way. Answer each with one specific situation where the decision was yours. State the situation briefly, then spend most of the answer on what you knew, what you chose and what changed afterwards, including in your own behaviour. For the technology-and-finance prompt, use your own experience rather than general statements about the industry.
Describe a conflict you had with a coworker and how you resolved it.
Describe a conflict you had with a coworker and how you resolved it.
Approach
- Close with what you would do differently, concretely.
- Give the blast radius: what could have broken, and what you measured.
- Pick a story where you made the decision, not one where you watched it.
Follow-up
- What would you do differently if you ran that again?
- How did you know your change caused the improvement?
How do you handle feedback and criticism?
How do you handle feedback and criticism?
Approach
- Name the disagreement and how you resolved it with evidence.
- State the situation in two sentences and spend the rest on the reasoning.
- Give the blast radius: what could have broken, and what you measured.
Follow-up
- What would you do differently if you ran that again?
- How did you know your change caused the improvement?
Estimate a kernel-bypass migration you have never attempted
Leadership asks how long it would take to move the order path off the conventional kernel networking stack, where it sits in the tens of microseconds, onto a kernel-bypass stack targeting single-digit microseconds. You have never done this migration. Give an estimate you are prepared to defend: the decomposition, the range and what drives its width, the spike that would narrow it most, and the conditions under which you would recommend not doing it at all. State what you would refuse to put a number on until the spike is finished.
Approach
- Refuse the single number first and say what you are giving instead: a range with the driver of its width named. The width is the information the requester actually needs, and handing over a point estimate destroys it.
- Decompose by uncertainty, not by component. The transport swap is well-bounded. What is not is everything currently leaning on kernel facilities: packet capture feeding the recording, session recovery on reconnect, failover between redundant lines, the timestamping source, and core pinning and isolation. Mark each item done-before or never-done and estimate only the first group directly.
- Attack the premise before estimating the work. Profile where the tens of microseconds actually sit; if a meaningful share is allocation, a lock, a synchronous log write or a page fault, the migration buys much less than the headline and the cheaper work comes first. State that as a decision gate with a threshold, not as a caveat at the end.
- Specify the spike as the narrowest experiment that collapses the widest uncertainty: one instrument, one venue session, send and receive only, tick-to-trade p99.9 measured against the current path on the same open-loop harness, with a fixed time box and a written question it must answer.
- State the costs that persist after delivery, because that is the part usually missing. Taking the network out of the kernel also takes it out of the kernel's tooling, so capture, counters and the existing packet recording need replacements, and a core is now permanently dedicated. Those are recurring costs against a one-time latency gain.
- Name the conditions for not doing it and what you will not estimate yet: if the strategies are not latency-sensitive at the margin, or the venue queue rather than your stack is the binding constraint, the answer is no — and the recovery and capture rework stays unestimated until the spike says what the new stack actually provides.
Follow-up
- Give me one number anyway, with the probability you attach to it.
- The spike shows 4 microseconds where you projected 9. What do you do with the estimate?
- What would you cut to get half the benefit in a quarter of the time?
- 01
Describe a conflict you had with a coworker and how you resolved it.
- 02
How do you prioritize tasks when you have multiple deadlines?
- 03
Can you provide an example of when you took the initiative on a project?
- 04
What motivates you to work in technology and finance?
- 05
How do you handle feedback and criticism?
- 06
Describe a time when you had to learn a new technology quickly to complete a project.
Is this an official IMC interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at IMC. Rounds and questions reflect what candidates have reported, not a process IMC has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How difficult are the IMC Software Engineer interviews?
The source notes describe them as challenging, with a focus on technical proficiency and problem-solving, and advise thorough preparation, especially in coding and algorithms. In practice, preparation should go deep rather than wide. Be able to implement a hash table or a balanced-tree check from a blank file. Explain complexity including worst cases, and follow a language question such as C++ versus Python memory management through two or three follow-ups.
PracHub interview research ↗How should I shape my answers in this loop?
State your reasoning aloud as you go. Name the worst case and the failure mode before you are asked, and back any claimed improvement with a measurement. On the reported design questions, put sequence numbers, gap recovery and timeout handling into your first pass rather than waiting for a prompt. On behavioral questions, spend most of the answer on what you decided and what changed afterwards.
PracHub interview research ↗How long does the process take and what are the stages?
Candidates report four stages over roughly three to five weeks: an online assessment, technical discussions, behavioral interviews and system design. The online assessment covers coding and logical reasoning, and candidates who do well there are invited to the later interviews. Timing depends on interviewer and candidate availability, so ask your recruiter for the current schedule.
PracHub interview research ↗Which programming language should I use?
The source notes list proficiency in C++ or Python as a must-have and mention that Java may also come up. Use the language you are fastest and most accurate in for coding problems. Whichever you choose, prepare to discuss both C++ and Python at the level of memory management and language features, because a C++ versus Python memory-management question is reported and the source notes also list a question on C++ templates.
PracHub Software Engineer practice ↗Are the SQL, debugging and estimation drills in this guide reported IMC questions?
No. Questions marked as reported come from candidate reports; the drill questions (the execution-report index query, the event-log current-state query, the limit attribution job, the position stream, the poison-message consumer and the kernel-bypass estimate) are PracHub's own practice material. They are there to train the reasoning the reported design and problem-solving questions call for: ordering, idempotency, recovery and measurement.
PracHub Software Engineer practice ↗Is remote work an option for this role?
The sources do not settle this; the notes say policies may vary by team and location. Ask your recruiter directly, early in the process, rather than inferring it from the job posting.
PracHub interview research ↗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