A Software Engineer at Wolverine Trading plays a pivotal role in designing, developing, and maintaining high-throughput, low-latency trading infrastructure and analytical tools. Based primarily out of the firm's headquarters in Chicago, software engineers build the core backbone that powers automated market making, options pricing, order routing, and real-time risk management.
In this role, your technical contributions directly impact the firm's trading capabilities across worldwide exchanges. Engineering teams at Wolverine Trading closely collaborate with quantitative traders, risk managers, and system administrators. You will be tasked with solving complex problems where microsecond delays, thread-safety, and data structure efficiency make a direct difference in daily financial performance.
Wolverine Trading heavily emphasizes core object-oriented programming (OOP), multi-threading, and low-level system design. Candidates should expect a heavy focus on modern C++, C#, or Java throughout the technical evaluation process.
Application Submission
reportedWhen a round has no standard shape, it is often there because something is still open: an area no earlier conversation reached, a round where the signal came out mixed, or a decision someone is not ready to make alone. Work out which by going back over what each earlier round actually covered rather than how it felt, and arrive able to give evidence on that point without being asked twice. Weak answers replay the loop's earlier material at the same depth. Strong ones go a level deeper and stay consistent with what you already said.
What to demonstrate
- Whether your account of a project matches the one you gave earlier in the loop, since what you said before may be available to whoever runs this round
- Whether you can go a level deeper on something already covered, reaching the decision and its alternatives rather than repeating the summary
- Whether you state your own uncertainty accurately, including parts of a system you did not build and decisions you inherited, instead of claiming even ownership across all of it
- Whether you can answer a question you handled poorly earlier by naming what you missed, rather than delivering a polished second version as if the first had not happened
How to prepare
- Reconstruct the loop on one page: for each round, the questions you were asked and the answer you actually gave, not the better one you thought of afterwards. The gaps on that page are your best available guess at why this round exists.
- Take the two claims you made earlier that carry the most weight and assemble the backing for each: the measurement, the date, what broke, the decision you would make differently now.
- Write down the three facts about your work that must not drift between tellings, such as team size, timeline and your own role, and check your stories against that list rather than trusting recall under pressure
Online Assessment
reportedWhat this round decides is narrow: whether you can produce code that runs and is correct on inputs nobody showed you. An elegant solution that does not compile scores below a plain one that does, so write a correct brute force first, say out loud that you know its cost, and improve it with the working version still on screen. What separates strong answers is who finds the broken case. Trace your own code against an empty input, a single element, and duplicate keys before you say you are finished, because being told is far more expensive than noticing.
What to demonstrate
- Whether degenerate inputs get checked without being asked for: an empty collection, one element, every element equal, and the extreme value the input type allows
- Whether the complexity you state matches the code you actually wrote, including a sort or a copy sitting inside a loop
- Whether the finished answer is verified against the worked examples before you call it done, rather than assumed correct because the code reads correctly
How to prepare
- Take five problems you have already solved and, without running anything, write down what each returns for empty input, a single element, and all-duplicates. Then run them and count how many you predicted wrong.
- Drill the brute force as its own skill: on ten problems, write only the obviously-correct slow version and time how long it takes to get it passing. If that is more than a few minutes, that is what to practise, not the optimal version.
- Add a fixed last step before you submit anything, reading only the loop bounds and the initial value of each accumulator, which is where most off-by-one errors live
Technical Screens
reportedInput bounds are the part of the prompt most often skimmed, and they usually contain the answer. They tell you which complexity class is admissible, which narrows the search before you have thought about the problem itself. As a rough planning figure, a compiled language does on the order of 10^8 simple operations per second and an interpreted one roughly an order of magnitude less. So n up to about twenty admits enumerating subsets, a few thousand admits a quadratic pass, and a million admits neither: you need near-linear, or linear with a log factor. If the bounds are missing, ask for them.
What to demonstrate
- Whether the approach is justified by the stated input size rather than by whichever pattern you recognised first
- Whether you ask about the properties that change the algorithm: whether the input arrives sorted, whether duplicates occur, whether values are bounded integers, whether it all fits in memory
- Whether you can name the bottleneck in your own solution and what would remove it, even when you deliberately leave it in place
- Whether a claimed speedup is real, since memoising a recursion only helps when subproblems genuinely overlap and the state can be keyed cheaply
How to prepare
- For each algorithm you rely on, write down the largest n it handles in roughly a second, then check two of those figures by timing them in the language you will actually type in
- For two weeks, write one line naming your target complexity and the bound that justifies it before you write any code, then compare that line with what you ended up submitting
- Practise the conversion backwards: given a required O(n log n), list the mechanisms that get you there (sorting, a heap, an ordered map, divide and conquer) and choose by what the problem needs to query, not by what you used last
Superday
reportedCoding rounds mostly set a floor. They decide whether you clear the bar, not where you land on the ladder. Level tends to come out of the design discussion and the ownership stories, so the question worth auditing beforehand is whether the scope you describe matches the scope of the job. Work that stops at your own service, or a story whose hard part was writing the code rather than getting several people to agree on an interface, reads a level below where you think you are interviewing, and that gap is usually resolved downwards.
What to demonstrate
- Whether the largest thing you describe owning ran end to end — the decision, the migration path, the rollout, and what you did when it went wrong — or stopped at the change you merged
- Whether design answers include what you would not build, what you would defer, and what you would measure before committing, rather than only what the boxes are
- Whether a disagreement in a story was settled with something checkable — a benchmark, a prototype, a written proposal — instead of by seniority or by waiting it out
- Whether you can say which calls you made alone and which you escalated, and why the line sat where it did
How to prepare
- Write your largest piece of owned work as a timeline of decisions — who decided what, when, and what you did when the plan broke — then delete every sentence whose subject is "we" and see how much survives
- Take one system you know well and drill the migration answer: how old and new paths run side by side under live traffic, how you compare their outputs, what the rollback is once writes are going to both, and which step you would not automate
- Map each line of the ladder in the job posting to a specific thing you have done, find the line you cannot support, and prepare the closest evidence you have plus an honest account of the gap
3 candidate reports. Individual accounts describe a particular role and hiring cycle.
Wolverine Trading Software Engineer interview: CoderByte trading assessment
An early conversation led quickly to a CoderByte assessment built around trading concepts. The coding was lighter than the systems-style prompts I had seen elsewhere, but it depended on using the right trading vocabulary and mapping it to simple logic. Timing and execution made the difference, and the platform's format pushed me to be precise. The live coding interview gave me more room to explai…
Read full experienceWolverine Trading Software Engineer interview experience
After applying, I was sent to an online OA on CoderByte. It involved object-oriented coding and trading-flavored logic, including registering and monitoring stock pairs and reporting when their price relationship moved beyond a tolerance. The specification was long, so careful reading mattered more than finding a trick algorithm. I spent most of my time translating requirements into code. The nex…
Read full experienceWolverine Trading Software Engineer interview: OOP clarification with a helpful interviewer
My process felt straightforward end to end. After an initial recruiter-style conversation, I had a quick early exchange where I introduced myself and answered an OOP-oriented question. The process felt professional. The interviewer was clear about the specification and helped me work through what they expected, so it felt more like collaborative clarification than a gotcha. It was what I expected…
Read full experiencePracHub editorial advice for the preparation topics above.
Reading the wall clock inside strategy or matching logic instead of taking time from the event stream.
It breaks replay outright - the same inputs stop producing the same outputs - and it adds a subtler failure. CLOCK_REALTIME is adjustable, so an NTP correction can step it backward and produce a negative interval or a timer that fires twice. CLOCK_MONOTONIC never steps backward but is not comparable across machines and, on Linux, does not advance while the machine is suspended, so it is the right clock for measuring a duration and the wrong one for stamping an event. Event time has to flow through the system as data, with the wall clock consulted only at the ingestion boundary where a receipt timestamp is taken once and then carried.
Retrying an order send after a timeout, on the assumption that the send failed.
A timeout says nothing about whether the venue received, matched and acknowledged the order - only that a reply did not arrive in time. Retrying turns an unknown into a duplicate: two live orders, twice the intended position, and a hedge computed against a position record that is now wrong. The correct move is to treat the client order id as an idempotency key, persist it before the send, and on recovery query state (order status request, or the drop-copy stream) rather than resend. It is the same shape as a double-captured payment, except the second order can move the price against you while you work out what happened.
Abandoning working code to chase the optimal solution
Get the straightforward version correct, state its complexity, and only then optimise, keeping the working version until the faster one passes the same cases. A correct quadratic solution with a stated path to linear beats a half-written optimal one that never ran.
Sorting when the problem never required a total order
Match the algorithm to the guarantee actually needed: the top k comes from a size-k heap in O(n log k) time and O(k) space, distinctness needs a set rather than an ordering, and a small bounded integer key range admits a linear counting pass. A full O(n log n) sort is the right default only when you genuinely need everything in order.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Find if a pair of integers in a sorted array sums up to a target integ…
Find if a pair of integers in a sorted array sums up to a target integer within a given threshold.
Approach
- 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.
- 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?
- How does this change if the input no longer fits in memory?
Write a function to find the largest palindromic number that is the pr…
Write a function to find the largest palindromic number that is the product of two $N$-digit numbers.
Approach
- Name the brute-force solution and its complexity before improving on it.
- Restate the input: its shape, its size, and what is guaranteed about it.
- 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?
Given an array of stock prices throughout a trading day, find the maxi…
Given an array of stock prices throughout a trading day, find the maximum profit achievable with buying and selling rules.
Approach
- Choose the data structure from the access pattern, not from familiarity.
- Walk one small example through your approach before writing the whole thing.
- 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?
- What is the worst case, and how likely is it on real data?
Given an unsorted array or stream of market ticks, output or track the…
Given an unsorted array or stream of market ticks, output or track the running median efficiently.
Approach
- Choose the data structure from the access pattern, not from familiarity.
- 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?
Explain the mechanics of making a hash map thread-safe for multi-threa…
Explain the mechanics of making a hash map thread-safe for multi-threaded readers and writers.
Approach
- Name what is shared across threads and what owns each piece of state.
- 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?
- What happens if two callers reach this at the same time?
Compare memory models, lookup times, and runtime complexity across `st…
Compare memory models, lookup times, and runtime complexity across std::vector, std::list, std::unordered_map, and tree structures.
Approach
- Distinguish a value from a reference to it, and say which one you handed out.
- Say what the runtime actually does before reasoning about the code.
- Identify the window where an invariant is briefly untrue.
Follow-up
- Where could this allocate more than you expect?
- What happens if two callers reach this at the same time?
Cap outbound message rate over a rolling one-second window
Outbound order messages carry int64 nanosecond send timestamps, non-decreasing, up to 10^7 per session. A venue caps you at M admitted messages in any rolling one-second window, and exceeding it risks a session-level throttle or a disconnect. Implement admit(t) on the order path: it must not allocate, must not read a clock of its own, and must run in constant time. Return the number of denials and the timestamp of the first one. State your memory footprint at M = 100 and your window boundary convention.
Approach
- Observe that only admitted messages enter the window, because the cap is on what the venue received. That single decision collapses the general sliding-window-with-eviction problem into a fixed-size one: at most M timestamps can ever be in the window, so the structure is a ring buffer of M int64s plus a monotone head counter, never a deque that grows.
- admit(t) compares t against the timestamp M positions back: if head < M, admit; otherwise admit iff t - ring[head mod M] is at least 1e9. On admit, write t at head mod M and increment head. That is two loads, one compare, one store, no branch on data size, O(1) time and 8M bytes of state — 800 bytes at M = 100, about thirteen cache lines, allocated once at startup.
- Fix the boundary convention and write it down: treat the window as (t - 1e9, t], so a message exactly 1,000,000,000 ns old has left it. The opposite convention is equally defensible but must be the same one the venue uses, and the off-by-one only ever shows up as an unexplained session throttle under burst.
- Take t from the event rather than from a clock call. A CLOCK_REALTIME read on this path is both a syscall risk and a determinism break: a replay of the same recording must make the same admit decisions, which it cannot do if the limiter samples wall time.
- Note the property this does not give you: strict rolling-window counting is bursty by construction — M messages can land in the first microsecond of the window and then nothing for a second. A token bucket smooths that but no longer implements the venue's stated rule, so it is a different contract, not an optimisation.
Worked solution 15 min
- Set M = 3 and work in milliseconds for legibility, with the window as (t - 1000, t].
- Feed the sequence 0, 10, 20, 500, 1005, 1010 through admit(t) by hand, writing down the ring contents and head after each call.
- At t = 500, compare against ring[(3-3) mod 3] = 0: the gap is 500 ms, so deny and leave the ring untouched.
- At t = 1005 compare against ring[0] = 0 (gap 1005, admit, overwrite ring[0]); at t = 1010 compare against ring[1] = 10 (gap exactly 1000, admit under the half-open convention).
- Re-run with the window defined as [t - 1000, t] instead and note which decision flips.
Follow-up
- The venue publishes two caps, M per second and N per ten seconds. How does the structure change, and what is the memory cost?
- You must also cap by scope — per session, per account, per instrument. Where does that stop being O(1)?
- A denied message still has to go somewhere. What do you do with it, and what does the strategy see?
Denormalising instrument attributes onto the position snapshot
A risk dashboard reads every open position for a desk once a second, roughly 50,000 rows, and each row needs price_scale, contract_multiplier and currency from instrument_version resolved as of now, plus a mark price. Today that is a range-predicate join per row against an effective-dated table. Propose a model that serves this read, state exactly which columns you copy and what makes the copy safe, and say what happens to yesterday's snapshot rows when a vendor correction changes an instrument version that was already in effect.
Approach
- Characterise the read before choosing a shape: 50,000 rows a second, attributes immutable within a version, versions changing at most daily for almost every instrument. Be accurate about why the as-of join costs what it does, because the tempting explanation is wrong. The join carries instrument_id equality alongside the effective-dating predicate, so the planner hashes or merges on that key and applies effective_from <= now() AND (effective_to IS NULL OR effective_to > now()) as a filter qual; a range predicate does not prevent a hash join. The real costs are that the scanned side is the whole version history rather than the single row per instrument actually in effect, that two correlated inequalities are a selectivity the planner estimates badly, and that the answer is recomputed every second while being constant for the entire business date. On 50,000 positions against 150,000 stored versions the hash join discards two rows for every one it keeps, and the per-row nested loop a bad estimate would choose instead costs 50,000 index descents and roughly three times the runtime.
- Prefer the middle option to either extreme. Materialize an as-of dimension once per business date, one row per (instrument_id, business_date) resolved at session start, and make the dashboard an equality join on that pair. The range predicate disappears without duplicating attributes onto every position row.
- If you do copy onto position_snapshot, copy price_scale, contract_multiplier and currency together with their provenance: the effective_from (or a version surrogate) of the instrument_version they came from. A denormalised copy without provenance cannot be audited, cannot be reconciled and cannot be replayed.
- Handle the intraday hazard explicitly instead of hoping: a corporate action effective mid-session changes contract_multiplier within a business date, so a per-date dimension is wrong for that instrument that day. Either key the dimension at a finer grain for affected instruments or fall back to the as-of join for them, and know which instruments those are from the corporate-action feed rather than from a support ticket.
- Answer the correction question the books-and-records way: a derived snapshot records what the system believed when it computed, so a later vendor correction does not rewrite yesterday's rows. It produces a new snapshot row or a break, which is the same principle that keeps eod_derived and eod_reconciled as separate rows rather than one mutable number.
- State the cost you accepted: writes amplify, the dimension build becomes a session-start dependency, and a missed rebuild serves yesterday's attributes silently, so the dimension needs a freshness assertion that fails the dashboard rather than a scheduled job and optimism.
Follow-up
- The dashboard now wants 30 days of the same view. Does the per-date dimension still work, and what does the join key become?
- Where does mark_px live in your model, and what stops a stale mark from being indistinguishable from a fresh one?
- How large is the dimension, and does it fit in memory on the service that serves the dashboard?
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?
Design a dynamic order book tracker that keeps track of the lowest ask…
Design a dynamic order book tracker that keeps track of the lowest ask or cheapest bid for a stock across a continuous stream of quote updates.
Approach
- Clarify what is being asked and what a complete answer contains.
- Work from the requirement backwards to the design.
- Say what you would check first and why it is the highest-information step.
Follow-up
- What assumption would you test first?
- How would you know your answer was wrong?
Implement an infrastructure component that handles deduplication of re…
Implement an infrastructure component that handles deduplication of redundant incoming stock pair registrations while managing complex bidirectional mappings.
Approach
- Say what you would check first and why it is the highest-information step.
- Clarify what is being asked and what a complete answer contains.
- Work from the requirement backwards to the design.
Follow-up
- How would you know your answer was wrong?
- What assumption would you test first?
Shard order sessions and route execution reports home
One gateway process cannot hold every venue session, so N processes split them, each carrying 100 to 10,000 outbound messages/second per session. client_order_id is venue-unique, at most 40 characters, and must be allocated and persisted before the send. Execution reports arrive on the owning session and also on an independent drop-copy stream that is not partitioned the same way. Design the sharding and routing: the shard key, how an id is allocated without a cross-shard round trip, how a drop-copy report reaches the process that owns the order, and what happens during a failover.
Approach
- Shard on the venue session, because the session sequence and the order state machine cannot be separated. Sharding by account or instrument forces two processes onto one session and therefore onto one sequence number space, which is the worst available coupling. Shard by (venue_mic, session) and assign accounts to sessions for capacity.
- Encode the shard in the id so allocation needs no coordination: shard id, session date and a per-process monotonic counter fits inside 40 characters and is venue-unique by construction. A central allocator would put a network round trip in the one place a round trip cannot be afforded.
- Reuse that encoding as the routing table. A drop-copy report carries the client order id, so the router parses the shard from it instead of consulting an ownership service. Reports carrying only venue_order_id, such as a restatement or a late reject, fall back to a lookup on order_event by (venue_mic, venue_order_id), which needs its own index and is measurably slower, which is why it is the fallback.
- Make ownership exclusive with a lease and a fencing token, because two processes on one session duplicate orders. The new owner reconciles before sending anything, by order status request or drop copy, rather than resending; the old owner's writes are rejected on the stale token, which is what a process paused past its lease expiry needs to hit when it resumes.
- Make the counter crash-safe without writing per order: reserve a block of 100,000 ids, persist the high-water mark once, hand them out from memory. A restart skips ids, which is harmless, where a per-id durable write puts a disk latency in the order path and a lost write reissues an id, which is not.
Worked solution 30 min
- Implement id allocation with a reserved block and a persisted high-water mark, then kill and restart the process mid-block twenty times.
- Feed a mixed report stream in which 10 percent of reports carry only venue_order_id, and route every report to an owner.
- Simulate a lease handover with the old owner paused for five seconds and then resumed, attempting a send.
- Measure allocation cost per id with the reservation block and with a durable write per id.
Follow-up
- A restarted process finds twelve orders in pending_new with no ack. What does it do with each?
- The drop copy shows a live order whose client order id this shard never allocated. Enumerate the possible explanations.
- Volume doubles on one account. What do you move, and what breaks if you move it mid-session?
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 done01Fix the scope and take a cold baseline
- Read the role description and write the three things the loop will almost certainly test, then write an explicit not-doing list and keep it visible all week.
- Take one twenty-five-minute coding problem and one fifteen-minute design prompt cold, and write the single sentence naming what blocked each, because those two sentences decide where the remaining evenings go.
- Set the week's rule: one thing finished every night, including the night you only have forty minutes.
Deliverable: A one-page scope with a not-doing list and two cold attempts, each carrying one sentence on what blocked it.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02One pattern, written three times from blank
- Choose the single pattern most likely to appear in your loop and write it three times from an empty file rather than editing the previous attempt.
- On the third pass, write the invariant as a comment before the loop body and the complexity before the first line of code.
- Stop at ninety minutes even if the third version is imperfect, and write the one thing you would fix given another hour.
Deliverable: Three independent implementations of the same pattern plus a note on what changed between them.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03One design, only to the depth you can defend
- Take one system shape and go only as far as requirements, interface and data model, refusing to draw a box you could not survive a follow-up about.
- Attach one number to each non-functional requirement, deriving it rather than asserting it, and write the assumption the number rests on.
- Write the one tradeoff you are choosing against and the observation that would make you reverse it.
Deliverable: One design at interface-and-schema depth with derived numbers and one written reversible tradeoff.
Practice prompt ↗Practice prompt ↗04Only the fundamentals you will have to defend
- Write, in under two hundred words each, the answers to the two questions that follow almost any implementation: why this structure and not the obvious alternative, and what happens to this code at a hundred times the input.
- Write what an index actually costs: faster lookups on the indexed columns against a write that now maintains a second structure, plus the cases where the planner declines to use it anyway, low selectivity, or a predicate wrapping the column in a function.
- Delete any answer you cannot deliver aloud in under a minute, since an answer that needs reading is not an answer you have.
Deliverable: Three written answers, each under two hundred words and each timed aloud.
Practice prompt ↗Practice prompt ↗Worked solution ↗05Your own work, timed
- Write a ninety-second and a four-minute version of your main project and time both aloud rather than reading them.
- Prepare the two follow-ups that always come: what you would do differently, and how you knew it worked.
- Put one number in the first sentence and be ready to say exactly where it came from and what it excludes.
Deliverable: Two timed narratives with one defensible number in the opening line.
Practice prompt ↗Practice prompt ↗06The one full rehearsal, in the weekend block
- Run a sixty-minute mock covering a coding round and a design round in one sitting with no break, because sustained attention is the thing evenings have not trained.
- Immediately afterwards, and before hearing any feedback, write the three moments you lost the thread.
- Spend the rest of the block only on those three moments, and on nothing you merely feel shaky about.
Deliverable: Mock notes naming three failure moments with a specific fix written under each.
Practice prompt ↗Practice prompt ↗07Taper
- Write the twenty-minute warm-up you will actually do on the morning: one problem you can already solve from a blank file, one design you can narrate, and nothing you have never seen.
- Re-read only your own notes from this week and open no new material.
- Write the logistics down: the editor or shared document you will be working in, whether execution and lookups are permitted, and the sentence you will use when you do not know something.
Deliverable: A one-page card holding the design structure, the project numbers, and the logistics.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
Team size, service count and tickets closed say very little. Seniority shows in the decision you owned: what you chose not to build, which constraint you traded away, whose objection you had to resolve before anything could move. A large project where you executed someone else's plan is a small story.
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?
Own the postmortem for a stale-book quoting incident
A sequence gap hit both the A and B lines of one venue channel at 09:31. Recovery was requested but never completed, the book stayed marked ok, and a strategy quoted against phantom levels for 14 minutes. Take the on-call role. Describe an incident you owned of comparable blast radius: how it was detected, how you bounded which instruments and orders were affected, what you stopped first, and what the loss was. Give a wall-clock timeline, the query that sized it, and the one change that would have caught it sooner.
Approach
- Open with the invariant that broke rather than the symptom: no order is priced off a book with a known, unrecovered gap. The invariant tells the listener what to count; 'we got bad fills' does not.
- Bound the population with a stated query, not an adjective. From feed_channel_session, the rows for that (venue_mic, channel_id, session_date) on both lines where unrecovered_msg_count > 0 give the window; join order_event on instrument_id where sent_ts falls between the first gap and the recovery. The finding that matters is that book_state read 'ok' throughout — the gap was the event, the unchanged state flag was the bug.
- Separate mitigation from fix and say which came first. Mitigation is blunt and needs no deploy: force the channel's book_state to stale, pull quotes, and cancel resting orders — resting orders keep trading while you are deciding, which is the part people forget. Making detection structural per line is not an incident-window change.
- Size the loss with a method attached: replay the capture, rebuild the book with the gap recovered, and remark the fills against that. State what you cannot attribute — some of the adverse selection in those 14 minutes would have happened anyway — because a loss number that claims everything is not believed.
- Name one detector with a threshold and a cost, not five. max_interarrival_us exceeding the venue's heartbeat interval catches a silent line; per-line sequence continuity catches a lossy one. Say which you shipped and its expected false pages per week.
- Name your own error inside the response window — the wrong hypothesis you held for 20 minutes, or the mitigation that made it worse. That is the part candidates rehearse away and interviewers weight most.
Follow-up
- Resting orders kept trading while you were deciding. What is the policy: cancel on stale, or leave them and stop adding?
- Recovery completed but the book could still be wrong. How do you prove a reconstructed book is correct before you quote off it again?
- What do you tell risk about the portion of the loss you cannot separate from ordinary adverse selection?
Unblock an engineer chasing a nondeterministic replay divergence
An engineer has spent two days on a replay that produces a different outbound order sequence on every run of the same build over the same recording. Their plan is to add a tolerance to the diff so the comparison passes. You have 30 minutes. Describe a time you unblocked someone. State what you asked before offering anything, the method you handed over rather than the answer itself, what they found, and how you knew they could do it without you next time. Include the case where your first hypothesis was wrong.
Approach
- Reject the tolerance quickly and with a reason: it does not bound the divergence, it hides it, and once it exists every future divergence is invisible behind it. Then immediately offer something better, because a refusal with no replacement is not unblocking.
- Hand over the bisect. Diff the two outbound streams to the first differing message, then walk back to the last identical input event. The interval between those two points is where the nondeterminism lives, and it is usually a few hundred microseconds of a session rather than a whole day.
- Give the checklist in the order that pays: a wall-clock read inside the strategy boundary; iteration over a hash container whose order depends on a per-process seed or on identity hash codes; more than one thread consuming the input stream; floating-point reassociation or a multiply-add contracted into a single FMA under fast-math latitude.
- Make each item an experiment rather than a theory — pin the hash seed and re-run, run single-threaded and re-run, disable fast-math and re-run. Whichever run comes back identical names the cause in one experiment, which is the part they can reuse without you.
- Verify the transfer rather than assuming it: ask them to write the rule into the codebase — nothing inside the strategy boundary reads a clock or iterates an unordered container — and to add the byte-for-byte diff to CI so the next divergence lands on a pull request instead of in a month.
- Mention your own wrong hypothesis and its cost. It is usually threading, the glamorous answer, when it was a clock read.
Follow-up
- The divergence appears on one machine only. What does that narrow it to?
- CI is byte-for-byte green but the strategy still behaves differently live. What else differs between the two?
- How do you write the determinism rule so it is enforceable in review rather than advisory?
- 01
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.
- 02
A sequence gap hit both the A and B lines of one venue channel at 09:31. Recovery was requested but never completed, the book stayed marked ok, and a strategy quoted against phantom levels for 14 minutes. Take the on-call role. Describe an incident you owned of comparable blast radius: how it was detected, how you bounded which instruments and orders were affected, what you stopped first, and what the loss was. Give a wall-clock timeline, the query that sized it, and the one change that would have caught it sooner.
- 03
An engineer has spent two days on a replay that produces a different outbound order sequence on every run of the same build over the same recording. Their plan is to add a tolerance to the diff so the comparison passes. You have 30 minutes. Describe a time you unblocked someone. State what you asked before offering anything, the method you handed over rather than the answer itself, what they found, and how you knew they could do it without you next time. Include the case where your first hypothesis was wrong.
Is this an official Wolverine Trading interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Wolverine Trading. Rounds and questions reflect what candidates have reported, not a process Wolverine Trading has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗Which programming languages are supported or preferred during the interview process?
While initial Online Assessments restrict language choices primarily to C++, C#, or Java (with Python strictly excluded on specific technical tests), live technical rounds allow candidates to select their primary production language. Demonstrating deep, idiomatically sound language mechanics in your chosen language is critical.
PracHub interview research ↗How difficult is the Wolverine Trading technical interview compared to standard tech companies?
The overall difficulty is average to difficult, with a strong focus on clean code design and practical performance rather than hyper-abstract algorithmic riddles. The primary challenge stems from tight time constraints and strict expectations around code efficiency, edge-case coverage, and OOP structure.
PracHub interview research ↗What is the company culture like for engineering teams?
Wolverine Trading maintains a focused, collaborative, and fast-paced trading environment in Chicago. The engineering culture values direct problem-solving, pragmatic code quality, technical ownership, and efficient execution without excessive bureaucratic overhead.
PracHub interview research ↗How fast is the typical interview process from application to offer?
The interview process generally moves quickly, often completing within 2 to 4 weeks. After passing the Online Assessment, phone screens and virtual superdays are typically scheduled in close succession.
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-22 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-22 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-22