Skip to main content

Iceberg Detection Study Guide

This study guide covers the mechanics, detection, and implementation of iceberg order detection using MBO data on CME ES futures via the Bookmap platform.

Prerequisite Knowledge: Basic understanding of futures markets, limit orders, and the concept of bid/ask. Study Method: Read each section, then answer the verification questions before proceeding. Answers are provided at the end.


Part 1: The Matching Engine — Where Everything Begins

1.1 The Core Axiom

Every price movement in a market originates from one source: trader orders. There is no other mechanism. When someone says "the Fed moved the market," what actually happened is: the Fed made an announcement, traders changed their minds about where price should be, those traders sent orders to the exchange, and those orders changed the order book. The Fed announcement itself never touched the matching engine. Only orders touch the matching engine.

The matching engine is a deterministic computer program running at the exchange. It receives three types of actions from traders:

  • Send: a new order enters the book
  • Cancel: an existing order is removed
  • Modify: an existing order's price or size is changed

That's it. Three actions. Everything observable in market data — every price tick, every volume bar, every candlestick — is derived from the matching engine processing these three actions.

1.2 The Order Book

The order book has two sides. The bid book contains resting buy limit orders, sorted highest price first. The ask book contains resting sell limit orders, sorted lowest price first. The highest bid and lowest ask are called the "best bid" and "best ask" — together they form the "inside market" or BBO (Best Bid and Offer). The gap between them is the spread.

Orders rest in the book when they can't be immediately executed. A buy limit order at 5840.00 rests if the current best ask is 5841.00 — the buyer isn't willing to pay 5841, so they wait. Their order joins the queue of other buyers at 5840.00.

1.3 FIFO Matching (Price-Time Priority)

CME uses FIFO (First-In, First-Out) matching for ES futures. When an aggressive order arrives and needs to be matched against resting orders, the rules are:

  1. Match against the best-priced resting orders first (price priority)
  2. At the same price, match against the order that arrived earliest (time priority)
  3. Continue until the aggressive order is fully filled or no more resting orders are at acceptable prices

This means queue position matters enormously. If you placed a sell order at 5850.00 an hour ago, you get filled before someone who placed a sell at 5850.00 one minute ago, even if their order is larger.

1.4 Atomic Processing

This is the most critical concept for understanding iceberg detection.

When an aggressive order arrives at CME, the matching engine processes it as one uninterruptible operation. From the moment the engine picks up the aggressive order to the moment it finishes processing it, nothing else can happen. No other orders enter. No cancellations are processed. No modifications. The engine is exclusively dedicated to this one aggressive order until it's done.

Think of it like a bank teller serving one customer. While the teller is counting out your cash, they don't stop midway to serve someone else, then come back to you. They finish your transaction completely, then call the next person.

In exchange terminology, this is called "atomic" processing — the aggressive order is an "atom," an indivisible unit that cannot be split or interrupted.

Why does this matter? Because during this uninterruptible window, if something appears that shouldn't be there — like a new order from the opposite side — there's only one possible explanation. No external trader could have submitted it. The matching engine wasn't accepting external input. The only entity capable of creating that order during the atomic window is the exchange itself.


Verification Questions — Part 1

Q1.1: If a war breaks out and ES drops 50 points, what actually caused the 
price change at the matching engine level?

Q1.2: A trader places a sell limit order at 5850.00 when the best bid is
5848.00. Does this order execute immediately or rest in the book? Why?

Q1.3: Two sell orders are resting at 5850.00 — Order A arrived at 10:00:01
and Order B arrived at 10:00:02. A buy order arrives that can fill
one of them. Which gets filled first under FIFO? Why?

Q1.4: During atomic processing of a buy market order, can another trader's
cancel message be processed? Why or why not?

Q1.5: During atomic processing, a new sell order appears at the same price
where the buy order is executing. What are the possible sources of
this new sell order?

Part 2: Market Data — How the Outside World Sees the Engine

2.1 The Information Hierarchy

The matching engine generates market data as it processes orders. This data is distributed to traders through data vendors. But not all market data is equal:

MBO (Market By Order) shows every individual order with a unique order ID, its side, price, and size. When an order is modified, you see the same ID with new parameters. When it's cancelled, you see that ID disappear. This is the highest-fidelity data available — you can reconstruct the exact state of the order book and track individual order lifecycles.

MBP (Market By Price) shows the total aggregate size at each price level. You see "350 contracts bid at 5850.00" but you don't know if that's one order of 350 or thirty-five orders of 10. Individual order behavior is invisible.

Times & Sales shows only executed trades — no book state at all. You know trades happened but not the context they happened in.

Candlesticks compress everything into four numbers per time period: open, high, low, close. Roughly 1-2% of available information survives this compression.

For iceberg detection, MBO data is essential. You need to see individual order IDs to identify new orders appearing during atomic processing. With MBP, you can only see aggregate size changes, which makes detection less precise.

2.2 The Data Chain for Your System

The data flows through a specific chain to reach your add-on:

CME matching engine generates MBO events. Rithmic, as a direct CME market data recipient, receives these events with full order IDs preserved. Bookmap connects to Rithmic and passes the MBO events to your add-on through the MarketByOrderDepthDataListener interface — send(orderId, isBid, price, size), replace(orderId, price, size), and cancel(orderId). Simultaneously, trade executions arrive through TradeDataListener.onTrade(price, size, tradeInfo).

The critical quality factor: this chain preserves the event sequence. The order in which events arrive at your add-on matches the order in which they occurred at the exchange. This preserved sequencing is what makes atomic processing detection possible.

2.3 Recorded Data (.bmf Files)

Bookmap can record the complete data stream into .bmf files. These files preserve everything: MBO order IDs, event sequence, nanosecond timestamps. When you replay a .bmf file, your add-on receives exactly the same events in exactly the same order as the live session. This means:

  • Detection results on replay are identical to what they would have been live
  • The same .bmf file replayed twice produces the same detections (deterministic)
  • CME order IDs in the replay are the real exchange-assigned IDs from the original session

For a retail trader at your scale (2 ES / 10 MES maximum), the recorded data is effectively ground truth. Your orders would not have changed the market data — your 2 lots are invisible in ES liquidity of millions of contracts per day.


Verification Questions — Part 2

Q2.1: You're watching a candlestick chart and see a long lower wick on a 
5-minute candle. A friend watching MBO data on Bookmap tells you a
200-lot iceberg buy order absorbed all the selling at the low of that
candle. Could you have detected this from the candlestick alone?

Q2.2: With MBP data, you see the size at 5850.00 ask go from 200 to 200
after a trade executes 20 contracts at that price. What might explain
the size staying at 200?

Q2.3: With MBO data at the same scenario, you see orderId=ABC (sell 20 at
5850) get executed, then immediately orderId=XYZ (sell 20 at 5850)
appears as a new send. What additional information does MBO give you
that MBP didn't?

Q2.4: You replay a .bmf file recorded from a live Rithmic session. Your
iceberg detector finds 15 detections. You replay it again the next
day. How many detections should it find? Why?

Q2.5: If you traded 2 ES contracts during the original live session, would
the .bmf replay be an accurate representation of what the market did?
Why or why not?

Part 3: Iceberg Orders — The Hidden Liquidity

3.1 What Is an Iceberg Order

An iceberg order is a limit order with one additional parameter: maximum displayed size. The trader specifies their total desired quantity and how much the exchange should show at any time.

Example: A sell iceberg at 5850.00, total size 500 contracts, max displayed size 20.

The exchange places 20 contracts visible in the ask book at 5850.00. The remaining 480 contracts are held in a hidden reserve — invisible to all market participants, invisible in both MBO and MBP data, invisible in any market data feed.

To every other participant watching the order book, this looks like an ordinary 20-lot sell order. Nothing distinguishes it from any other 20-lot sell.

3.2 Why Institutions Use Icebergs

Large traders face a fundamental problem: orders reveal intentions. If other participants see a 500-lot sell order at 5850.00, they infer that a large, presumably informed, trader wants to sell. Other traders then:

  • Sell ahead of the large order (front-running), pushing price down before the large trader gets filled
  • Cancel their own buy orders below 5850, removing liquidity
  • Place their own sells at lower prices to get ahead in queue

The result: the large trader's remaining fills are at significantly worse prices. The market moved against them specifically because their intention was visible.

By showing only 20 of the 500, the iceberg trader appears small. Other participants don't panic. The iceberg trader gets early fills at favorable prices. As each 20-lot slice executes, the exchange quietly refills from the hidden reserve, and the process repeats.

3.3 The Refill Lifecycle

Here's the step-by-step mechanics of what happens inside the exchange:

Step 1: The iceberg order enters. The exchange shows 20 contracts at 5850.00 ask. Hidden reserve: 480.

Step 2: The displayed 20 joins the end of the queue at 5850.00, like any new order. It has no special priority.

Step 3: As other orders ahead of it fill or cancel, the displayed 20 advances in the queue.

Step 4: An aggressive buy arrives and executes against the displayed 20. It's fully consumed.

Step 5: The exchange internally refills. It takes 20 from the hidden reserve (now 460) and creates a new displayed order of 20 at 5850.00. In the MBO data, this appears as a brand new order with a new order ID. It is not the same order being "restored" — it is a fresh order placed at the end of the queue.

Step 6: The cycle repeats. Each refill gets a new order ID and goes to the back of the queue.

Step 7: When the hidden reserve falls below the max displayed size — say 10 remain — the final refill is only 10 contracts instead of 20. This smaller final refill can be an additional detection signal.

3.4 What Can and Cannot Be Detected

AspectDetectable?How
Presence of icebergYes, after first executionRefill pattern during atomic processing
Displayed size parameterYes, estimatedSize of refill orders (consistent across refills)
Hidden remaining sizeNeverFundamentally unknowable from market data
Original placement timePartially, MBO onlyCan infer from first detected order ID
Total size when fully exhaustedApproximatelySum of all observed refill sizes + final partial
CancellationMBO onlyOrder ID disappears without a trade
Presence before first executionNeverIceberg is invisible until the displayed part fills

The hidden size is the critical limitation. When your detector finds an iceberg, it can tell you "there's an iceberg here with a displayed size of 20 and I've seen 4 refills so far" — but it cannot tell you whether there are 5 more refills coming or 500. The iceberg reveals itself progressively, never completely.


Verification Questions — Part 3

Q3.1: An institution wants to buy 1000 contracts of ES. They submit an 
iceberg with max displayed size of 25. How many contracts are visible
in the order book? How many are hidden?

Q3.2: After the first 25-lot displayed portion fills, where in the queue
does the refill go — front, middle, or end? Why does this matter?

Q3.3: The iceberg has 30 contracts remaining in its hidden reserve and the
max displayed size is 25. What size will the next refill be? What
about the refill after that?

Q3.4: Can you determine the total size of an active iceberg from market
data? Why or why not?

Q3.5: A trader looking at MBP data sees the ask size at 5850.00 stay at
approximately 200 despite heavy buying at that level. A trader
looking at MBO data sees orderId=100 (sell 25) get executed, then
orderId=101 (sell 25) appear, get executed, then orderId=102
(sell 25) appear. Which trader has more confidence that this is an
iceberg, and why?

Part 4: The Native Detection Algorithm

4.1 The Core Insight

The Native algorithm exploits the atomic processing constraint. During the uninterruptible processing of one aggressive order, the matching engine does not accept external actions. Therefore, if a new order appears on the opposite side during this window, it must have been created by the exchange's internal iceberg refill mechanism. There is no other explanation.

This is not a heuristic or a probability estimate. It is a logical deduction from a known mechanical constraint. If you accept that processing is atomic (which CME confirms in their documentation), then interleaved opposite-side orders during processing are definitively iceberg refills.

4.2 What "Normal" Looks Like

When a buy aggressive order sweeps through sell orders at 5850.00, the MBO event sequence looks like:

Execute Sell orderId=100 (20 contracts at 5850.00)
Execute Sell orderId=101 (10 contracts at 5850.00)
Execute Sell orderId=102 (15 contracts at 5850.00)
→ Level cleared. Aggressive buy remainder (if any) moves to next price.

This is clean. The aggressive buy consumed three resting sell orders at the price level. No new orders appeared because no new orders can arrive during atomic processing.

4.3 What an Iceberg Looks Like

Same scenario, but orderId=101 was an iceberg with displayed size 10:

Execute Sell orderId=100 (20 contracts at 5850.00)
Execute Sell orderId=101 (10 contracts at 5850.00) ← Iceberg's displayed portion
NEW Sell orderId=103 (10 contracts at 5850.00) ← REFILL! New ID, same size
Execute Sell orderId=102 (15 contracts at 5850.00)
Execute Sell orderId=103 (10 contracts at 5850.00) ← Refill portion executes
NEW Sell orderId=104 (10 contracts at 5850.00) ← Another refill!
Execute Sell orderId=104 (10 contracts at 5850.00)
NEW Sell orderId=105 (10 contracts at 5850.00) ← And another

The "smoking gun" is those NEW Sell Order lines appearing mid-sequence. During atomic processing of a buy order, no external sell orders can arrive. The only source is the exchange replenishing the iceberg.

4.4 How Your Add-on Implements This

Your add-on uses two data streams simultaneously:

TradeDataListener provides onTrade(price, size, tradeInfo) where tradeInfo contains:

  • isExecutionStart: true on the first trade of an atomic chain
  • isExecutionEnd: true on the last trade of an atomic chain
  • isBidAggressor: true if the aggressive order is a buy

MarketByOrderDepthDataListener provides send(orderId, isBid, price, size) for every new order entering the book.

These two streams are interleaved on the same processing thread. During one aggressive order's atomic processing, your add-on receives trade events and MBO events in the exact sequence they occurred at the exchange.

The detection state machine works as follows:

  1. IDLE state: Waiting for a trade with isExecutionStart=true
  2. Transition to IN_CHAIN: Record the aggressor side from isBidAggressor. Start collecting trades and MBO sends.
  3. IN_CHAIN state: Every onTrade is accumulated. Every MBO send that arrives is added to the interleaved sends list.
  4. Chain ends: When a trade arrives with isExecutionEnd=true, evaluate the collected data.
  5. Evaluation: For each interleaved MBO send, check three criteria:
    • Is the send on the opposite side from the aggressor? (Buy aggressor → send must be a sell)
    • Is the send at a price where executions occurred in this chain?
    • Is the send genuinely new? (Not a modification of a pre-existing order)
  6. If qualifying sends are found, emit an iceberg detection event.

4.5 Side Logic — Getting the Direction Right

This is where confusion often arises. The aggressor side and the iceberg side are opposites:

Buy aggressor sweeping asks → sell iceberg detected The aggressive buy is consuming sell orders. The refill appears as a new sell order. The iceberg is on the sell/ask side — it's a sell iceberg providing resistance/ceiling.

Sell aggressor sweeping bids → buy iceberg detected The aggressive sell is consuming buy orders. The refill appears as a new buy order. The iceberg is on the buy/bid side — it's a buy iceberg providing support/floor.

The iceberg is always on the passive side — the side being consumed. The aggressor is the one revealing the iceberg by triggering executions that force refills.

4.6 Order ID Behavior Across Refills

Each refill creates a new, unique order ID assigned by CME. The exchange does not reuse or modify the original order ID. From the MBO data perspective, each refill looks like a completely independent new order.

This means you cannot track an iceberg across refill cycles by following one order ID. The linkage between refills is established by the detection pattern (same price, same side, same size, during atomic processing) — not by order ID continuity.

However, each individual refill's order ID is valuable for validation. If orderId=103 appeared during an atomic chain and you can verify it didn't exist in the book before the chain started, that confirms it was genuinely created during atomic processing — it's a real refill, not a pre-existing order.


Verification Questions — Part 4

Q4.1: During atomic processing of a sell market order, your detector sees 
a new buy order appear at the same price. Is this an iceberg refill?
What side is the iceberg?

Q4.2: Your detector sees: Chain START aggressor=BUY, Execute Sell @5850,
New Sell @5850 (orderId=200), Execute Sell @5850, New Sell @5851
(orderId=201), Chain END. OrderId=200 qualifies as a refill. Does
orderId=201 qualify? Why or why not?

Q4.3: A chain has aggressor=BUY. During the chain, a new BUY order appears
at the execution price. Is this an iceberg refill? Why or why not?

Q4.4: Your detector reports: "SELL_ICEBERG price=5850 displayedSize=20
refills=3." What does this tell you? What doesn't it tell you?

Q4.5: An iceberg's first refill has orderId=500, second refill has
orderId=501, third has orderId=502. Are these the same order being
modified, or three different orders? How do you know?

Q4.6: Why can't the Native algorithm detect HFT-mimicked icebergs
(where a co-located firm manually replaces filled orders)?

Q4.7: Your detector shows a chain with 2 trades and 23 interleaved MBO
sends, but 0 qualified as refills. What likely happened?

Part 5: Absorption — The Trading Meaning of Icebergs

5.1 What Absorption Means

Detection tells you an iceberg exists. Absorption tells you what it's doing to price.

Absorption occurs when aggressive orders hitting a price level are neutralized by continuously replenishing passive liquidity. The aggressive side is spending its force, but price doesn't move because the passive side keeps refilling. It's like pushing against a wall that rebuilds itself as fast as you tear it down.

Icebergs are the most granular form of absorption. But absorption exists on a spectrum:

  • Exchange iceberg: Zero-latency refill, mechanically perfect pattern. Detected by the Native algorithm.
  • HFT-mimicked iceberg: Sub-millisecond refill from a co-located firm sending regular limit orders. Appears almost identical but has a tiny gap between fill and refill.
  • Institutional manual absorption: A human trader or slow algorithm adding liquidity over seconds or minutes at a level. Same market effect, but much slower refill cycle.

All three produce the same outcome: price stalls or reverses at the level despite persistent aggressive flow.

5.2 Absorption Success vs Failure

Detecting an iceberg is not automatically a trading signal. The critical question is whether the absorption will hold.

Absorption succeeds: The iceberg absorbs all aggressive flow. Aggressive traders exhaust themselves. Price reverses away from the level. The iceberg holder wins. Traders who identified the iceberg and traded in its direction also win.

Absorption fails: An overwhelming opposing force — a larger institution, a cascade of triggered stops, a news event — generates more aggressive flow than the iceberg can absorb. The iceberg fills completely, and price blows through the level. The iceberg holder loses.

The outcome depends on the relative size of two forces: the iceberg's hidden reserve versus the total aggressive flow directed at the level. Since the hidden reserve is unknowable, predicting the outcome requires assessing the aggressive side — which is where CVD, delta analysis, and broader context become essential.

5.3 CVD as Independent Confirmation

Cumulative Volume Delta (CVD) measures the net aggressive flow direction. It sums trade volume where bid-aggressive trades (sells) are negative and ask-aggressive trades (buys) are positive.

When a sell iceberg is absorbing buying pressure:

  • CVD rises (aggressive buying is occurring)
  • Price doesn't advance (the iceberg is absorbing the buying)
  • This CVD-price divergence independently confirms the absorption

When a buy iceberg is absorbing selling pressure:

  • CVD falls (aggressive selling is occurring)
  • Price doesn't decline (the iceberg is absorbing the selling)

CVD and the iceberg detector use completely separate data paths — CVD uses tradeInfo.isBidAggressor while the detector uses MBO send events. When both independently indicate absorption at the same level, confidence is much higher than either signal alone.

5.4 Structural Context

Institutional traders don't place icebergs randomly. They place them at levels where they want to accumulate or distribute positions — levels that have structural significance:

  • Prior day's high, low, and settlement
  • Overnight session boundaries
  • Volume Profile POC, VAH, VAL
  • Round numbers with psychological significance
  • Levels where previous icebergs or large absorption events occurred

If your detector's markers cluster at these levels, it's behavioral confirmation that the detections are capturing real institutional positioning.


Verification Questions — Part 5

Q5.1: Your detector shows a sell iceberg at 5850.00. Price has been 
pushing up toward 5850 with strong buying. What are the two possible
outcomes? What determines which one occurs?

Q5.2: You see a sell iceberg detection at 5850.00. CVD is rising sharply
but price is flat at 5850. What does this combination tell you?

Q5.3: You see a sell iceberg detection at 5850.00. CVD is falling and
price is also falling. Does this make sense? What might explain it?

Q5.4: Your detector shows 12 detections during an RTH session. 8 of them
are within 2 ticks of the prior day's high. Is this pattern
significant? What might it indicate?

Q5.5: A buy iceberg is detected at 5820.00 with 15 refills. Price
eventually breaks down through 5820. Was the detection wrong?
Explain your reasoning.

Q5.6: Why is iceberg detection a higher-confidence signal of genuine
institutional intent than a large visible resting order?

Part 6: Your Implementation Architecture

6.1 The Simplified API Choice

Your add-on uses Bookmap's Simplified API (VERSION1) rather than the Advanced API (VERSION2). This means:

  • The main class implements CustomModule with initialize() and stop() lifecycle methods
  • Data listeners are interfaces: TradeDataListener, MarketByOrderDepthDataListener, TimeListener
  • Indicators are registered via api.registerIndicator() and updated via indicator.addIcon()
  • Settings are persisted via api.getSettings() / api.setSettings()

The trade-off: no historical scrollback for markers (they appear from subscription start only), but simpler code and direct access to all needed data streams.

6.2 Component Responsibilities

IcebergDetectorAddon: The entry point. Receives all Bookmap callbacks, forwards MBO events to MboBookTracker, forwards trades to ExecutionChainTracker, and renders detections as chart icons.

MboBookTracker: Maintains a HashMap of all known MBO orders by orderId. When a new send arrives, it stores the order and notifies the ExecutionChainTracker. On replace, it updates the existing entry. On cancel, it removes it.

ExecutionChainTracker: The detection engine. Manages the IDLE/IN_CHAIN state machine. Collects trades and interleaved MBO sends during a chain. At chain end, evaluates sends against refill criteria and emits IcebergEvent records.

IcebergEvent: An immutable Java record holding all detection data — timestamp, price, side, estimated displayed size, refill count, first refill order ID.

IcebergIconRenderer: Creates and caches icon images at construction time. Returns the appropriate cached icon based on iceberg side and user-selected shape.

IcebergSettings: Persisted configuration — minimum refills, minimum displayed size, icon shape, logging toggle.

6.3 Threading Model

All data callbacks arrive on the same Bookmap processing thread. This means onTrade, MBO send/replace/cancel, and onTimestamp are never concurrent with each other. No synchronization is needed between them. The settings panel runs on Swing's Event Dispatch Thread, so settings reads from the processing thread should be tolerant of slight staleness (which is fine — filter values don't need nanosecond precision).

6.4 Data Quality Dependencies

The detection only works with Rithmic MBO data. dxFeed cloud backfill provides aggregated historical data without individual order IDs — the MBO callbacks won't fire, and no detection is possible. This means:

  • Live sessions with Rithmic: full detection
  • Recorded .bmf replays from Rithmic sessions: full detection (preserved MBO events)
  • dxFeed backfill: no detection (no MBO data available)

Verification Questions — Part 6

Q6.1: Why does the add-on use the Simplified API instead of the Advanced 
API? What feature does it lose by this choice?

Q6.2: MboBookTracker receives a send() call with orderId="ABC", then
later a replace() call with orderId="ABC" at a different price.
What does MboBookTracker do with this? Does the replace trigger
a "new send" notification to ExecutionChainTracker?

Q6.3: Can onTrade and MBO send callbacks arrive simultaneously on
different threads? Why does this matter for the detection logic?

Q6.4: You load the add-on on a chart using dxFeed as the data provider
instead of Rithmic. What happens? Will you see false detections?

Q6.5: The user changes Min Displayed Size from 1 to 10 in the settings
panel while the add-on is processing live data. Could this cause a
crash? Why or why not?

Answers

Part 1 Answers

A1.1: Traders reacting to the war sent sell orders (and cancelled buy orders) at the exchange. Those orders changed the order book, which changed the price. The war itself never touched the matching engine — only orders touch the matching engine.

A1.2: The order rests in the book. The sell limit at 5850.00 means "don't execute at worse than 5850.00." Since the best bid is 5848.00 (below the sell price), there's no counterparty willing to pay the seller's minimum price. The order waits in the ask book at 5850.00.

A1.3: Order A gets filled first. Under FIFO (price-time priority), at the same price level, the order that arrived earlier has priority. Order A arrived at 10:00:01, before Order B at 10:00:02.

A1.4: No. During atomic processing, the engine is exclusively dedicated to the current aggressive order. All other actions (cancels, new orders, modifications from external participants) wait in a queue until the current atomic operation completes.

A1.5: The only possible source is the exchange's internal iceberg refill mechanism. No external participant can submit orders during atomic processing. The exchange itself is the only entity that can create new orders mid-processing — and it only does this when refilling an iceberg's displayed portion from its hidden reserve.

Part 2 Answers

A2.1: No. The candlestick shows a lower wick (price went down then recovered) but gives zero information about why. You can't see individual orders, absorption patterns, or iceberg activity from OHLC data. The 200-lot iceberg buy order is completely invisible in the candlestick.

A2.2: Several possibilities: an iceberg at that level is refilling (maintaining the size despite fills), or new unrelated orders coincidentally arrived to replace the filled ones, or the MBP data aggregation timing made it appear constant. Without MBO data, you can't distinguish between these explanations.

A2.3: MBO shows you the specific order IDs involved. You can see that orderId=ABC was consumed (executed and removed) and orderId=XYZ is a completely new, different order that appeared immediately after. This confirms it's not the same order surviving — it's a new order replacing the consumed one, which is the iceberg refill pattern. MBP only showed the aggregate size staying at 200, without revealing this order-level lifecycle.

A2.4: Exactly 15 detections. The .bmf file contains the identical event stream with the same MBO order IDs in the same sequence. The detection algorithm is deterministic — same input always produces same output. This is the basis of the Repeatability Protocol in the validation framework.

A2.5: Yes, it would be accurate. At 2 ES contracts, your market impact is zero. ES trades roughly 1.5-2 million contracts per day. Your 2-lot order would not have changed any other participant's behavior or any price. The recorded market data would have been identical whether you traded or not.

Part 3 Answers

A3.1: 25 contracts are visible in the order book. 975 contracts are hidden (1000 - 25 = 975). Other participants see only the 25-lot order and have no way to know about the remaining 975.

A3.2: End of the queue. Each iceberg refill is treated as a brand new order and goes to the back of the line at that price level. This means the iceberg trades slower than a single visible 1000-lot order would (which would have priority for its entire size). The tradeoff is concealment for slower execution.

A3.3: The next refill will be 25 (the max displayed size, and there's enough hidden reserve). After that fills, only 5 remain in the reserve (30 - 25 = 5), so the final refill will be 5 contracts — a smaller partial refill that signals the iceberg is nearly exhausted.

A3.4: No. The hidden reserve is fundamentally unknowable from market data. You can count observed refills (e.g., "I've seen 4 refills of 25 = 100 contracts so far") but you have no way to know how many more refills remain. The iceberg reveals itself progressively, never completely, until it's fully exhausted or cancelled.

A3.5: The MBO trader has much more confidence. They can see distinct order IDs being consumed and replaced — a definitive refill pattern. The MBP trader can only see the aggregate size staying constant, which could also be explained by coincidental new orders from different participants, or timing artifacts in the data feed.

Part 4 Answers

A4.1: Yes, it's an iceberg refill. The aggressive order is a sell (sweeping bids), and the new buy order appearing during atomic processing must be an iceberg refill. The iceberg is on the buy/bid side — it's a buy iceberg providing support.

A4.2: OrderId=200 qualifies because it's on the opposite side (sell) at the execution price (5850). OrderId=201 does NOT qualify because it's at a different price (5851) than where executions are occurring (5850). Iceberg refills appear at the same price level where the iceberg's displayed portion was consumed.

A4.3: No. The aggressor is a BUY, so the refill must be on the opposite side — a SELL. A new BUY order appearing is on the same side as the aggressor. This could be the unfilled remainder of the aggressive order being placed in the book, or some other exchange-internal event, but it's not an iceberg refill (icebergs refill on the passive side).

A4.4: It tells you: a sell iceberg was detected at price 5850, each refill showed approximately 20 contracts (the estimated displayed size parameter), and 3 refill events were observed during atomic processing. It does NOT tell you: the total iceberg size, how many refills remain, when the iceberg was originally placed, or whether the absorption will hold.

A4.5: These are three different orders, each with a unique CME-assigned order ID. The exchange creates a new order for each refill — it does not modify or reuse the previous order ID. From the MBO data perspective, they are independent orders that happen to share the same price, side, and size.

A4.6: HFT-mimicked icebergs use regular limit orders from an external co-located firm. After a fill, the firm sends a new replacement order — but this happens between atomic chains, not during one. The firm's replacement order arrives as a new action after the current aggressive order's atomic processing completes. So it doesn't appear interleaved within a chain — it appears between chains, which the Native algorithm doesn't flag.

A4.7: The 23 MBO sends arrived during the atomic chain but none met all three refill criteria. They may have been on the same side as the aggressor (not opposite), at different price levels than where executions occurred, or were modifications/updates rather than genuinely new orders. The "0 qualified" diagnostic tells you the detector examined them and correctly rejected them.

Part 5 Answers

A5.1: Two outcomes: (1) Absorption succeeds — the sell iceberg absorbs all the aggressive buying, buyers exhaust themselves, and price reverses downward. (2) Absorption fails — the buying pressure overwhelms the iceberg's hidden reserve, the iceberg fills completely, and price breaks through 5850.00 to the upside. The outcome depends on whether the total aggressive buy volume exceeds the iceberg's total remaining hidden reserve — which is unknowable in advance.

A5.2: This is a classic absorption-in-progress signal. Rising CVD means aggressive buying is occurring. Flat price at 5850 means that buying isn't moving price. The sell iceberg detection explains why — the iceberg is absorbing the buy aggression. Two independent signals (CVD divergence + iceberg detection) both point to absorption at 5850.

A5.3: This doesn't make typical sense for active absorption. If CVD is falling, aggressive selling dominates — but a sell iceberg absorbs buying, not selling. One explanation: the iceberg was placed earlier when buying was active, absorbed some buying, but now the buying has stopped and new selling pressure is pushing price down independently. The iceberg detection was correct at its moment, but the market context shifted.

A5.4: Very significant. 8 of 12 detections (67%) clustering within 2 ticks of a key structural level suggests institutional participants were actively defending or positioning at the prior day's high. This structural concentration is one of the strongest validation signals — icebergs placed at meaningful levels indicate genuine institutional intent.

A5.5: The detection was NOT wrong. The iceberg genuinely existed at 5820.00 — the refill pattern confirmed it. But absorption failed — the selling pressure overwhelmed the iceberg's hidden reserve. A correct detection of an iceberg that ultimately fails is still a correct detection. Detection accuracy and trade outcome are separate things.

A5.6: A large visible resting order might be spoofing — a trader places a large order they intend to cancel before it fills, creating a false impression of supply or demand. An iceberg, by contrast, is deliberately hidden. Why would you hide a fake order? The entire point of spoofing is visibility — making other participants react to what they see. An iceberg is designed to NOT be seen. This makes detected icebergs a higher-confidence signal of genuine intent: the trader is real, they're hiding their size, and they're committed to the level.

Part 6 Answers

A6.1: The Simplified API provides direct access to both TradeDataListener and MarketByOrderDepthDataListener with simpler lifecycle management. The trade-off is no historical scrollback support for chart markers — icons only appear from the moment the add-on subscribes, not when scrolling back in history. For a real-time iceberg detector, this is acceptable.

A6.2: MboBookTracker updates the existing entry for orderId="ABC" with the new price. It does NOT fire a "new send" notification — replace is a modification of an existing order, not a new order. Only genuine send() calls (new orders) trigger the notification to ExecutionChainTracker. This distinction is critical: existing orders being modified are not iceberg refills.

A6.3: No, they cannot arrive simultaneously. All data callbacks arrive on the same Bookmap processing thread, sequentially. This is critical because it means the interleaving of onTrade and MBO send events reflects the actual exchange event sequence — the order in which your add-on receives them is the order in which they occurred. If they arrived on different threads, the interleaving would be non-deterministic and detection would be unreliable.

A6.4: Nothing visible happens. The MarketByOrderDepthDataListener callbacks (send/replace/cancel) simply won't fire because dxFeed doesn't provide MBO data. No MBO events means no interleaved sends during chains, which means no detections. You won't see false detections — you'll see no detections at all.

A6.5: No crash. The settings panel runs on Swing's EDT (Event Dispatch Thread) while data processing runs on Bookmap's processing thread. The settings field is a simple int that the processing thread reads when evaluating a detection. In the worst case, a detection that fires at the exact moment of the settings change might use the old value — which is harmless. There's no concurrent modification of complex data structures, just a simple field read.


Review any sections where verification questions were challenging before proceeding to hands-on validation work.

See Also