Market participants rarely place a trade and get filled at the same instant. The path prices take between the time an order is placed and when it executes determines whether an order fires immediately, sits patiently, or never fills. This page demonstrates how five common order types behave as the market unfolds.
Order types at a glance
Market order : executes right away at the best price available.
Limit order : waits for a specific price or better.
Stop order : becomes a market order once price moves to a less favorable level.
Stop-limit order : arms at the stop price, then becomes a limit order with a constrained fill price.
Market-if-touched order : becomes a market order once price moves to a more favorable level.
Interactive order execution lab
Configure orders, then reveal the price path one day at a time to see when each order activates and how it fills. The price path follows a volatile geometric Brownian motion (GBM), so you can explore different price relationships.
How to experiment
Choose the order type and side (buy or sell).
Enter the price levels relevant for the order (limit price, stop price, etc.).
Click Queue order to stage as many orders as you like.
Drag the day slider to reveal the path and watch each order trigger, fill, or expire.
Remove or clear orders at any point to try a new scenario.
formatCurrency = (value, digits = 2 ) =>
new Intl . NumberFormat ("en-US" , {
style : "currency" ,
currency : "USD" ,
minimumFractionDigits : digits,
maximumFractionDigits : digits
}). format (value)
formatNumber = (value, digits = 2 ) =>
Number (value). toLocaleString ("en-US" , {
minimumFractionDigits : digits,
maximumFractionDigits : digits
})
mulberry32 = seed => {
return function () {
let t = seed += 0x6D2B79F5
t = Math . imul (t ^ t >>> 15 , t | 1 )
t ^= t + Math . imul (t ^ t >>> 7 , t | 61 )
return ((t ^ t >>> 14 ) >>> 0 ) / 4294967296
}
}
randomNormal = rng => {
let u = 0 , v = 0
while (u === 0 ) u = rng ()
while (v === 0 ) v = rng ()
return Math . sqrt (- 2.0 * Math . log (u)) * Math . cos (2.0 * Math . PI * v)
}
mutable randomSeed = 202410
startPrice = 48
simulationSteps = 180
rng = mulberry32 (randomSeed)
mu = 0.06
sigma = 0.32
timeUnit = 1 / simulationSteps
pricePath = {
const path = Array . from ({ length : simulationSteps + 1 }, (_, step) => ({ step, price : null , time : step }))
path[0 ]. price = startPrice
for (let step = 1 ; step <= simulationSteps; step++ ) {
const prev = path[step - 1 ]. price
const drift = (mu - 0.5 * sigma ** 2 ) * timeUnit
const diffusion = sigma * Math . sqrt (timeUnit) * randomNormal (rng)
const nextPrice = prev * Math . exp (drift + diffusion)
path[step]. price = Number (nextPrice. toFixed (2 ))
}
return path
}
priceDomain = [
Math . min (... pricePath. map (d => d. price )) - 2 ,
Math . max (... pricePath. map (d => d. price )) + 2
]
mutable orders = []
mutable orderCounter = 1
orderFieldConfig = new Map ([
["market" , []],
["limit" , [
{ key : "limitPrice" , label : "Limit price" , help : "Executes at this price or better." }
]],
["stop" , [
{ key : "stopPrice" , label : "Stop price" , help : "Activates once price reaches this level." }
]],
["stop-limit" , [
{ key : "stopPrice" , label : "Stop price" , help : "Arms the order when touched." },
{ key : "limitPrice" , label : "Limit price" , help : "Fill price once armed (may be skipped)." }
]],
["mit" , [
{ key : "touchPrice" , label : "Touch price" , help : "Becomes a market order when reached." }
]]
])
defaultFieldValues = (orderType, orderSide, start) => {
// Standard adjustment: Buy lower (-), Sell higher (+)
const standardAdjust = (up, down) => (orderSide === "Buy" ? down : up)
// Stop adjustment: Buy higher (+), Sell lower (-)
const stopAdjust = (up, down) => (orderSide === "Buy" ? up : down)
switch (orderType) {
case "market" :
return {}
case "limit" :
return { limitPrice : Number ((start + standardAdjust (2 , - 2 )). toFixed (2 )) }
case "stop" :
return { stopPrice : Number ((start + stopAdjust (2 , - 2 )). toFixed (2 )) }
case "stop-limit" : {
const stopPrice = Number ((start + stopAdjust (2 , - 2 )). toFixed (2 ))
// If Buy Stop (higher), Limit usually slightly higher or same.
// If Sell Stop (lower), Limit usually slightly lower or same.
const limitOffset = orderSide === "Buy" ? 0.5 : - 0.5
return {
stopPrice,
limitPrice : Number ((stopPrice + limitOffset). toFixed (2 ))
}
}
case "mit" :
return { touchPrice : Number ((start + standardAdjust (2 , - 2 )). toFixed (2 )) }
default :
return {}
}
}
appendOrder = order => {
const nextId = orderCounter ?? 1
mutable orderCounter = nextId + 1
const newOrder = { ... order, id : nextId }
mutable orders = [... (orders ?? []), newOrder]
return newOrder
}
removeOrder = id => {
mutable orders = (orders ?? []). filter (order => order. id !== id)
}
clearOrders = () => {
mutable orders = []
mutable orderCounter = 1
}
orderSummary = order => {
switch (order. type ) {
case "market" :
return ` ${ order. side } market`
case "limit" :
return ` ${ order. side } limit ${ formatCurrency (order. limitPrice )} `
case "stop" :
return ` ${ order. side } stop ${ formatCurrency (order. stopPrice )} `
case "stop-limit" :
return ` ${ order. side } stop-limit (stop ${ formatCurrency (order. stopPrice )} , limit ${ formatCurrency (order. limitPrice )} )`
case "mit" :
return ` ${ order. side } MIT ${ formatCurrency (order. touchPrice )} `
default :
return ` ${ order. side } ${ order. type } `
}
}
viewof orderBuilder = {
const currentPrice = currentPoint. price
const currentDay = simulationStep
const fields = orderFieldConfig. get (orderType) ?? []
const defaults = defaultFieldValues (orderType, orderSide, currentPrice)
const state = { ... defaults }
const container = html `<div class="order-builder" style="margin: 1rem 0; padding: 1.1rem 1.5rem; border: 1px solid #d0d7de; border-radius: 14px; background: #f8f9fb; display: flex; flex-direction: column; gap: 0.9rem;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div style="font-weight: 600;">Configure order at day ${ currentDay} </div>
<span style="background: linear-gradient(135deg, rgba(47,113,213,0.18), rgba(47,113,213,0.32)); color: #0b3d91; border-radius: 999px; padding: 0.25rem 0.75rem; font-weight: 700; font-size: 0.85rem; letter-spacing: 0.02em; text-transform: uppercase;">Current ${ formatCurrency (currentPrice)} </span>
</div>
</div>`
const fieldBlock = html `<div class="order-builder__fields" style="display: grid; gap: 0.75rem;"></div>`
container. append (fieldBlock)
if (fields. length === 0 ) {
fieldBlock. append (html `<div style="font-size: 0.92rem; color: #495057;">Market orders use the current price and execute immediately.</div>` )
} else {
fields. forEach (field => {
const wrapper = html `<label style="display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.92rem;">
<span style="font-weight: 600;"> ${ field. label } </span>
<input type="number" step="0.25" value=" ${ state[field. key ]} " style="padding: 0.4rem 0.6rem; border-radius: 8px; border: 1px solid #c4cdd5; font-size: 0.95rem;" />
${ field. help ? html `<span style="font-size: 0.8rem; color: #6c757d;"> ${ field. help } </span>` : null }
</label>`
const input = wrapper. querySelector ("input" )
input. addEventListener ("input" , () => {
const parsed = Number (input. value )
state[field. key ] = Number . isFinite (parsed) ? parsed : null
})
fieldBlock. append (wrapper)
})
}
const actionRow = html `<div style="display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center;"></div>`
const addButton = html `<button style="background: #2f71d5; color: white; border: none; border-radius: 10px; padding: 0.55rem 1.3rem; font-weight: 600; cursor: pointer;">Queue order at day ${ currentDay} </button>`
const newPathButton = html `<button style="background: white; color: #495057; border: 1px solid #adb5bd; border-radius: 10px; padding: 0.5rem 1rem; font-weight: 600; cursor: pointer;">New price path</button>`
const clearButton = html `<button style="background: white; color: #d00000; border: 1px solid #d00000; border-radius: 10px; padding: 0.5rem 1rem; font-weight: 600; cursor: pointer;">Clear all</button>`
actionRow. append (addButton, newPathButton, clearButton)
container. append (actionRow)
const statusBox = html `<div style="font-size: 0.9rem; color: #495057;">Set the price levels, then add the order to the queue.</div>`
container. append (statusBox)
const orderCount = orders. length
if (orderCount > 0 ) {
const countBadge = html `<div style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: #1b4332; background: #d2f4ea; padding: 0.3rem 0.7rem; border-radius: 8px; width: fit-content; margin-top: -0.4rem; font-weight: 500;">
<span style="display: inline-block; width: 8px; height: 8px; background: #1b4332; border-radius: 50%;"></span>
Currently ${ orderCount} order ${ orderCount === 1 ? "" : "s" } in queue
</div>`
container. append (countBadge)
}
newPathButton. onclick = () => {
mutable randomSeed = Math . floor (Math . random () * 1000000 )
statusBox. textContent = "New random price path generated."
statusBox. style . color = "#495057"
}
clearButton. onclick = () => {
clearOrders ()
statusBox. textContent = "All queued orders removed."
statusBox. style . color = "#6c757d"
}
addButton. onclick = () => {
if (fields. some (field => state[field. key ] == null || ! Number . isFinite (Number (state[field. key ])))) {
statusBox. textContent = "Enter a valid price for each field before queueing the order."
statusBox. style . color = "#d00000"
return
}
let validationError = null
switch (orderType) {
case "limit" :
if (orderSide === "Buy" && state. limitPrice >= currentPrice) {
validationError = "Buy Limit price must be below the current market price."
} else if (orderSide === "Sell" && state. limitPrice <= currentPrice) {
validationError = "Sell Limit price must be above the current market price."
}
break
case "stop" :
case "stop-limit" :
if (orderSide === "Buy" && state. stopPrice <= currentPrice) {
validationError = "Buy Stop price must be above the current market price."
} else if (orderSide === "Sell" && state. stopPrice >= currentPrice) {
validationError = "Sell Stop price must be below the current market price."
}
break
case "mit" :
if (orderSide === "Buy" && state. touchPrice >= currentPrice) {
validationError = "Buy MIT price must be below the current market price."
} else if (orderSide === "Sell" && state. touchPrice <= currentPrice) {
validationError = "Sell MIT price must be above the current market price."
}
break
}
if (validationError) {
statusBox. textContent = validationError
statusBox. style . color = "#d00000"
return
}
const orderDetails = (() => {
switch (orderType) {
case "market" :
return { triggerPrice : currentPrice }
case "limit" :
return {
limitPrice : Number (state. limitPrice ),
triggerPrice : Number (state. limitPrice )
}
case "stop" :
return {
stopPrice : Number (state. stopPrice ),
triggerPrice : Number (state. stopPrice )
}
case "stop-limit" :
return {
stopPrice : Number (state. stopPrice ),
limitPrice : Number (state. limitPrice ),
triggerPrice : Number (state. stopPrice )
}
case "mit" :
return {
touchPrice : Number (state. touchPrice ),
triggerPrice : Number (state. touchPrice )
}
default :
return {}
}
})()
const newOrder = {
type : orderType,
side : orderSide,
placedAt : currentDay,
createdAt : Date . now (),
... orderDetails
}
const queued = appendOrder ({ ... newOrder, description : orderSummary (newOrder) })
statusBox. textContent = `Order # ${ queued. id } added at day ${ currentDay} : ${ orderSummary (newOrder)} .`
statusBox. style . color = "#1b4332"
}
container. value = null
return container
}
viewof simulationStep = {
randomSeed; // Depend on randomSeed to reset
return Inputs. range ([0 , pricePath. length - 1 ], {
step : 1 ,
value : 0 ,
label : "Simulation day" ,
play : true ,
speed : 0.4 ,
loop : false
});
}
currentPoint = pricePath[simulationStep]
visiblePath = pricePath. slice (0 , simulationStep + 1 )
orderColors = new Map ([
["market" , "#2f71d5" ],
["limit" , "#2b8a3e" ],
["stop" , "#f08c00" ],
["stop-limit" , "#d00000" ],
["mit" , "#845ef7" ]
])
evaluateOrder = (order, path, step) => {
if (! order) return null
const currentStep = Math . min (step, path. length - 1 )
if (currentStep < order. placedAt ) {
return { ... order, status : "Queued" , triggeredAt : null , executedAt : null , executedPrice : null }
}
const side = order. side
const priceAt = s => path[Math . min (s, path. length - 1 )]. price
let triggeredAt = null
let executedAt = null
let executedPrice = null
let status = "Waiting"
const start = order. placedAt
const meetsTrigger = price => {
switch (order. type ) {
case "stop" :
case "stop-limit" :
return side === "Buy" ? price >= order. stopPrice : price <= order. stopPrice
case "mit" :
return side === "Buy" ? price <= order. touchPrice : price >= order. touchPrice
default :
return true
}
}
const meetsLimit = price => {
if (order. type === "limit" ) {
return side === "Buy" ? price <= order. limitPrice : price >= order. limitPrice
}
if (order. type === "stop-limit" ) {
if (order. limitPrice == null ) return false
return side === "Buy" ? price <= order. limitPrice : price >= order. limitPrice
}
return true
}
if (order. type === "market" ) {
executedAt = start
executedPrice = priceAt (start)
status = currentStep >= start ? "Filled" : "Queued"
return { ... order, status, triggeredAt, executedAt, executedPrice }
}
let armed = false
for (let s = start; s <= currentStep; s++ ) {
const price = priceAt (s)
if (order. type === "limit" ) {
if (meetsLimit (price)) {
executedAt = s
executedPrice = side === "Buy" ? Math . min (order. limitPrice , price) : Math . max (order. limitPrice , price)
status = "Filled"
break
}
continue
}
if (order. type === "stop" ) {
if (meetsTrigger (price)) {
triggeredAt = s
executedAt = s
executedPrice = price
status = "Filled"
break
}
continue
}
if (order. type === "stop-limit" ) {
if (! armed && meetsTrigger (price)) {
armed = true
triggeredAt = s
}
if (armed && meetsLimit (price)) {
executedAt = s
executedPrice = side === "Buy" ? Math . min (order. limitPrice , price) : Math . max (order. limitPrice , price)
status = "Filled"
break
}
continue
}
if (order. type === "mit" ) {
if (meetsTrigger (price)) {
triggeredAt = s
executedAt = s
executedPrice = price
status = "Filled"
break
}
}
}
if (executedAt === null ) {
if (order. type === "stop-limit" && triggeredAt !== null ) {
status = "Armed (limit waiting)"
} else {
status = "Waiting"
}
}
if (currentStep === path. length - 1 && executedAt === null ) {
status = status === "Armed (limit waiting)" ? "Expired unfilled" : "Unfilled"
}
return { ... order, status, triggeredAt, executedAt, executedPrice }
}
ordersWithState = (Array . isArray (orders) ? orders : []). map (order =>
evaluateOrder (order, pricePath, simulationStep)
)
orderGuides = ordersWithState. flatMap (order => {
if (! order) return []
const color = orderColors. get (order. type ) ?? "#495057"
const guides = []
const addGuide = (price, kind) => {
if (price == null ) return
guides. push ({ price, color, kind, id : order. id })
}
switch (order. type ) {
case "limit" :
addGuide (order. limitPrice , "limit" )
break
case "stop" :
addGuide (order. stopPrice , "stop" )
break
case "stop-limit" :
addGuide (order. stopPrice , "stop" )
addGuide (order. limitPrice , "limit" )
break
case "mit" :
addGuide (order. touchPrice , "mit" )
break
default :
break
}
return guides
})
executionMarkers = ordersWithState
. filter (order => order && order. executedAt !== null && order. executedAt <= simulationStep)
. map (order => ({
id : order. id ,
step : order. executedAt ,
price : order. executedPrice ,
color : orderColors. get (order. type ) ?? "#495057"
}))
html `<div style="display: flex; justify-content: center; align-items: center; gap: 0.8rem; margin-top: 0.5rem; margin-bottom: 1rem; font-family: sans-serif;">
<div style="font-size: 0.85rem; color: #495057;">Spot price @ day ${ simulationStep} :</div>
<div style="font-weight: 700; font-size: 1.0rem; color: #0b3d91; background: rgba(47,113,213,0.18); border-radius: 999px; padding: 0.25rem 0.8rem;"> ${ formatCurrency (currentPoint. price )} </div>
</div>`
viewof priceChart = Plot. plot ({
height : 360 ,
marginLeft : 70 ,
grid : true ,
style : { background : "linear-gradient(180deg, rgba(47,113,213,0.08), rgba(47,113,213,0))" },
x : {
label : "Days" ,
domain : [0 , pricePath. length - 1 ]
},
y : {
label : "Price ($)" ,
domain : priceDomain,
grid : true
},
marks : [
Plot. line (visiblePath, { x : "step" , y : "price" , stroke : "#2f71d5" , strokeWidth : 1.6 }),
Plot. dot (visiblePath, { x : "step" , y : "price" , r : 2.6 , fill : "#2f71d5" }),
Plot. ruleX ([simulationStep], { stroke : "#343a40" , strokeDasharray : "4,4" }),
Plot. dot ([currentPoint], { x : "step" , y : "price" , r : 6 , fill : "#1b1b1b" , stroke : "white" , strokeWidth : 1.5 }),
orderGuides. length
? Plot. ruleY (orderGuides, {
y : "price" ,
stroke : d => d. color ,
strokeDasharray : d => (d. kind === "stop" ? "6,4" : d. kind === "mit" ? "2,6" : null ),
strokeWidth : 1.6 ,
opacity : 0.85
})
: null ,
executionMarkers. length
? Plot. dot (executionMarkers, {
x : "step" ,
y : "price" ,
fill : d => d. color ,
stroke : "white" ,
strokeWidth : 1.5 ,
r : 6
})
: null
]. filter (Boolean )
})
Order log
orderLegend = html `<div class="order-legend" style="display: flex; gap: 1.5rem; flex-wrap: wrap; margin: 0.75rem 0;">
${ orderTypeOptions. map (o => {
const swatch = html `<span style="display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.9rem;">
<span style="display: inline-block; width: 14px; height: 14px; border-radius: 4px; background: ${ orderColors. get (o. value )} ;"></span>
${ o. label }
</span>`
return swatch
})}
</div>`
orderTableRows = ordersWithState. map (order => {
if (! order) return null
const prices = (() => {
switch (order. type ) {
case "limit" :
return `Limit ${ formatCurrency (order. limitPrice )} `
case "stop" :
return `Stop ${ formatCurrency (order. stopPrice )} `
case "stop-limit" :
return `Stop ${ formatCurrency (order. stopPrice )} / Limit ${ formatCurrency (order. limitPrice )} `
case "mit" :
return `Touch ${ formatCurrency (order. touchPrice )} `
default :
return `—`
}
})()
return {
id : order. id ,
typeLabel : orderTypeOptions. find (o => o. value === order. type )?. label ?? order. type ,
side : order. side ,
placedAt : order. placedAt ,
prices,
status : order. status ,
triggeredAt : order. triggeredAt ?? "—" ,
executedAt : order. executedAt ?? "—" ,
executedPrice : order. executedPrice != null ? formatCurrency (order. executedPrice ) : "—" ,
color : orderColors. get (order. type ) ?? "#495057"
}
}). filter (Boolean )
removeOrderButton = id => {
const btn = html `<button style="background: none; border: 1px solid #adb5bd; border-radius: 8px; padding: 0.3rem 0.7rem; font-size: 0.8rem; cursor: pointer;">Remove</button>`
btn. onclick = () => removeOrder (id)
return btn
}
html `<table class="order-table" style="width: 100%; border-collapse: collapse; font-size: 0.92rem;">
<thead>
<tr style="text-align: left; border-bottom: 2px solid #adb5bd;">
<th style="padding: 0.5rem;">#</th>
<th style="padding: 0.5rem;">Type</th>
<th style="padding: 0.5rem;">Side</th>
<th style="padding: 0.5rem;">Placed day</th>
<th style="padding: 0.5rem;">Price levels</th>
<th style="padding: 0.5rem;">Status @ day ${ simulationStep} </th>
<th style="padding: 0.5rem;">Trigger day</th>
<th style="padding: 0.5rem;">Fill day</th>
<th style="padding: 0.5rem;">Fill price</th>
<th style="padding: 0.5rem;">Actions</th>
</tr>
</thead>
<tbody>
${ orderTableRows. length === 0
? html `<tr><td colspan="10" style="padding: 0.75rem; text-align: center; color: #6c757d;">No queued orders yet. Configure an order above to get started.</td></tr>`
: orderTableRows. map (row => {
const removeBtn = removeOrderButton (row. id )
return html `<tr style="border-bottom: 1px solid #e5e7eb;">
<td style="padding: 0.5rem; font-weight: 600; color: ${ row. color } ;"> ${ row. id } </td>
<td style="padding: 0.5rem;"> ${ row. typeLabel } </td>
<td style="padding: 0.5rem;"> ${ row. side } </td>
<td style="padding: 0.5rem;"> ${ row. placedAt } </td>
<td style="padding: 0.5rem;"> ${ row. prices } </td>
<td style="padding: 0.5rem; font-weight: 600;"> ${ row. status } </td>
<td style="padding: 0.5rem;"> ${ row. triggeredAt } </td>
<td style="padding: 0.5rem;"> ${ row. executedAt } </td>
<td style="padding: 0.5rem;"> ${ row. executedPrice } </td>
<td style="padding: 0.5rem;"> ${ removeBtn} </td>
</tr>`
})}
</tbody>
</table>`
What to observe
Market orders fill instantly upon placement—use them as a benchmark against conditional orders.
Limit orders wait for the market to reach their price; if the revealed path never touches, they expire unfilled.
Stop orders convert into market orders at the stop price, highlighting how protection can trigger during adverse moves.
Stop-limit orders show gap risk: once armed, the limit may never trade, leaving the order resting indefinitely.
Market-if-touched orders flip the logic of stops—use them to explore profit-taking levels that execute with market liquidity.
Takeaways
Placing orders while gradually revealing the price path underscores the difference between activation price and execution price . Market orders fill at the opening quote, limit orders demand favorable levels, stop-family orders protect against unfavorable moves (but can slip), and market-if-touched orders secure profits once a target is hit. Experiment with multiple orders at once—such as pairing a protective stop-limit with a profit-taking MIT—to see how combined strategies respond to the same price path.
Caveats and Simplifications
This simulation makes several simplifying assumptions for educational clarity:
Daily Prices: The simulation uses a single price point for each day (akin to a closing price). It does not account for intraday price fluctuations (highs and lows), which in a real market could trigger an order.
Guaranteed Fills: Limit orders are assumed to execute as soon as the market price meets the limit condition. In reality, execution is not guaranteed and depends on factors like order book depth and queue priority.
No Slippage: Market orders, including those triggered by stop and MIT orders, are assumed to execute at the exact price of that day’s time step. In live markets, especially fast-moving ones, they can experience slippage —the difference between the price when the order is submitted to the market and the final execution price.
Exchange-Specific Rules: The order types and validation logic shown here (such as the relationship between stop and limit prices) are common but not universal. Different exchanges and brokers may support different sets of orders or enforce their own specific constraints.