Skip to content

Submit a native iceberg order

A native iceberg order is a venue primitive: trader sends a single order with a visible slice size and the total quantity. The exchange exposes only the visible slice on the book and auto-refreshes the next slice from the hidden remainder as the visible portion fills. Other participants see a sequence of small fills and level refills, not one big resting order.

lrvx ships IcebergExecutor for client-side slicing (each leg pays a round-trip latency). The simulator also supports the venue's native iceberg primitive, with no per-refresh latency unless you configure one.

When to use which

Scenario Pick
Venue offers iceberg natively (CME, Eurex) submit_iceberg
Client-controlled slicing on a venue without IcebergExecutor
Hybrid (some venues, some not, one strategy) submit_iceberg then fall back to executor algo on rejection

Native iceberg is preferred when available: the venue handles the refresh atomically, no client-side network round-trip per slice.

Configure

A worked example (Python):

"""Submit a native iceberg order and watch its hidden remainder drain."""
import lrvx

exec = lrvx.SimulatedExecutor()

# Instant refresh is the default; set non-zero for venues with refresh delay.
exec.set_iceberg_refresh_latency(0)

# 10 BTC total, 2 BTC visible per slice.
exec.submit_iceberg(order_id=1, side="buy", price=100.0,
                     total_quantity=10.0, visible_quantity=2.0)

hidden = exec.iceberg_hidden_remaining_raw(1)
print(f"after submit: hidden remainder raw = {hidden}")
assert hidden > 0

# Cancel cleans up both visible and hidden portions in one call.
exec.cancel_order(1)
print(f"after cancel: hidden remainder = {exec.iceberg_hidden_remaining_raw(1)}")
import lrvx

exec = lrvx.SimulatedExecutor()
# 1ms refresh delay (most venues are instant; leave at 0 for those).
exec.set_iceberg_refresh_latency(1_000_000)
exec.submit_iceberg(order_id=1, side="buy", price=50000.0,
                     total_quantity=20.0, visible_quantity=2.0)
import { SimulatedExecutor } from "@lrvx/lrvx";

const exec = new SimulatedExecutor();
exec.setIcebergRefreshLatency(1_000_000);
exec.submitIceberg(1, "buy", 50000.0, 20.0, 2.0);
const exec = new SimulatedExecutor();
exec.setIcebergRefreshLatency(1_000_000);
exec.submitIceberg(1, "buy", 50000.0, 20.0, 2.0);
from lrvx.backtest import SimulatedExecutor

exec = SimulatedExecutor()
exec.set_iceberg_refresh_latency(1_000_000)
exec.submit_iceberg(1, "buy", 50000.0, 20.0, 2.0)
LrvxSimulatedExecutorHandle exec = lrvx_simulated_executor_create();
lrvx_simulated_executor_set_iceberg_refresh_latency(exec, 1000000);
lrvx_simulated_executor_submit_iceberg(exec, /*id=*/1, /*side=*/0,
                                       /*price=*/50000.0,
                                       /*total=*/20.0, /*visible=*/2.0,
                                       /*symbol=*/1);

Behaviour the simulator guarantees

  • Only visible_quantity is added to the book queue at submit.
  • When the visible tranche fully fills, the simulator atomically refreshes another slice of min(visible_quantity, hidden_remainder) unless a refresh latency is configured, in which case the next slice is exposed at fill_time + latency instead.
  • The refreshed slice goes to the back of the queue at that price level by default (override via set_iceberg_priority_mode): whatever is still resting at the level sits in front of it, and it waits its turn like any newly posted order. Under retain the slice inherits the queue position the consumed one held and trades on the next print.
  • Cancel cancels both the visible portion and the hidden remainder in one call.

Refresh variants

Real venues differ on two iceberg knobs that materially change the fill profile for queue-position strategies:

Knob Default Other modes Models
Size randomisation 0% (off) up to ±100% uniform per refresh Anti-detection ±10% on liquid spot
Refresh queue priority back retain CME options + some Eurex contracts
# Anti-detection: jitter each refreshed slice ±10% around visible.
exec.set_iceberg_size_randomisation_pct(0.10)
exec.set_iceberg_jitter_seed(42)  # reproducible draws

# CME-style: refreshed slice keeps the same queue position.
exec.set_iceberg_priority_mode("retain")
exec.setIcebergSizeRandomisationPct(0.10);
exec.setIcebergJitterSeed(42);
exec.setIcebergPriorityMode("retain");

Inspect remaining hidden quantity

For diagnostics, the executor exposes the raw fixed-point hidden remainder per order ID:

rem_raw = exec.iceberg_hidden_remaining_raw(order_id=1)
rem = rem_raw / 1e8  # Quantity is fixed-point with scale 1e-8
const remRaw = exec.icebergHiddenRemainingRaw(1);

The value drops by visible_quantity on each refresh and reaches 0 once the order is fully filled.

Notes

  • visible_quantity must be strictly less than total_quantity — otherwise the order behaves as a regular limit (no hidden state created).
  • Refresh latency is a single venue-wide setting, not per-order. Most venues are instant; some inject 0.5-2ms between refresh and next-slice exposure.
  • Self-trade prevention and rate-limit policy apply to native iceberg the same way they apply to limit orders.