Charts

DepthChart

An order book's bids and asks as stepped areas of the size waiting at each price or better, around the mid price, with the spread.

#chart#data#trading#no-js

Demo

Preview
BTC-USD depth
  • Best bid 61,247.5
  • Best ask 61,252.5
  • Spread 5.0 (0.008%)
BTC-USD depth: mid 61,250.0, spread 5.0
SidePriceSizeTotal
Ask61,447.54.48 BTC82.39 BTC
Ask61,442.54.22 BTC77.91 BTC
Ask61,437.51.89 BTC73.68 BTC
Ask61,432.53.66 BTC71.8 BTC
Ask61,427.52.1 BTC68.14 BTC
Ask61,422.53.31 BTC66.04 BTC
Ask61,417.52.94 BTC62.74 BTC
Ask61,412.52.21 BTC59.8 BTC
Ask61,407.54.09 BTC57.59 BTC
Ask61,402.52.53 BTC53.5 BTC
Ask61,397.52.01 BTC50.97 BTC
Ask61,392.53.22 BTC48.95 BTC
Ask61,387.52.21 BTC45.74 BTC
Ask61,382.51.76 BTC43.53 BTC
Ask61,377.52.15 BTC41.77 BTC
Ask61,372.53.53 BTC39.62 BTC
Ask61,367.52.43 BTC36.09 BTC
Ask61,362.52.33 BTC33.66 BTC
Ask61,357.52.1 BTC31.34 BTC
Ask61,352.52.04 BTC29.24 BTC
Ask61,347.51.39 BTC27.19 BTC
Ask61,342.51.99 BTC25.8 BTC
Ask61,337.51.79 BTC23.81 BTC
Ask61,332.51.6 BTC22.02 BTC
Ask61,327.52.51 BTC20.42 BTC
Ask61,322.52.34 BTC17.92 BTC
Ask61,317.51.32 BTC15.57 BTC
Ask61,312.50.87 BTC14.25 BTC
Ask61,307.51.26 BTC13.38 BTC
Ask61,302.52.11 BTC12.12 BTC
Ask61,297.50.77 BTC10.01 BTC
Ask61,292.51.08 BTC9.24 BTC
Ask61,287.51.14 BTC8.16 BTC
Ask61,282.50.64 BTC7.02 BTC
Ask61,277.50.95 BTC6.38 BTC
Ask61,272.50.74 BTC5.44 BTC
Ask61,267.51.46 BTC4.7 BTC
Ask61,262.51.17 BTC3.24 BTC
Ask61,257.51.51 BTC2.07 BTC
Ask61,252.50.57 BTC0.57 BTC
Bid61,247.50.27 BTC0.27 BTC
Bid61,242.51.17 BTC1.44 BTC
Bid61,237.51.4 BTC2.84 BTC
Bid61,232.50.76 BTC3.59 BTC
Bid61,227.51.37 BTC4.96 BTC
Bid61,222.50.51 BTC5.47 BTC
Bid61,217.50.77 BTC6.24 BTC
Bid61,212.51.09 BTC7.33 BTC
Bid61,207.51.14 BTC8.47 BTC
Bid61,202.51.72 BTC10.19 BTC
Bid61,197.52.28 BTC12.47 BTC
Bid61,192.51.7 BTC14.17 BTC
Bid61,187.50.8 BTC14.97 BTC
Bid61,182.50.87 BTC15.84 BTC
Bid61,177.51.12 BTC16.95 BTC
Bid61,172.52.07 BTC19.02 BTC
Bid61,167.51.41 BTC20.43 BTC
Bid61,162.50.64 BTC21.07 BTC
Bid61,157.52.97 BTC24.04 BTC
Bid61,152.51.48 BTC25.51 BTC
Bid61,147.50.84 BTC26.35 BTC
Bid61,142.51.85 BTC28.2 BTC
Bid61,137.53.39 BTC31.58 BTC
Bid61,132.52.6 BTC34.18 BTC
Bid61,127.51.37 BTC35.55 BTC
Bid61,122.52.67 BTC38.22 BTC
Bid61,117.52.17 BTC40.4 BTC
Bid61,112.51.6 BTC42 BTC
Bid61,107.50.99 BTC42.99 BTC
Bid61,102.52.49 BTC45.48 BTC
Bid61,097.52.7 BTC48.18 BTC
Bid61,092.53.7 BTC51.87 BTC
Bid61,087.51.94 BTC53.81 BTC
Bid61,082.51.7 BTC55.51 BTC
Bid61,077.53.43 BTC58.93 BTC
Bid61,072.53.34 BTC62.27 BTC
Bid61,067.51.5 BTC63.78 BTC
Bid61,062.54.04 BTC67.82 BTC
Bid61,057.54.83 BTC72.65 BTC
Bid61,052.54.38 BTC77.03 BTC
ACME, within 0.5% of the mid
  • Best bid $182.38
  • Best ask $182.43
  • Spread $0.05 (0.027%)
ACME, within 0.5% of the mid: mid $182.41, spread $0.05
SidePriceSizeTotal
Ask$183.881,44425,960
Ask$183.831,68824,516
Ask$183.781,44722,828
Ask$183.731,50921,381
Ask$183.681,69719,872
Ask$183.6371818,175
Ask$183.58683.9517,457
Ask$183.531,38716,773
Ask$183.481,19915,386
Ask$183.43507.8614,187
Ask$183.38540.7513,679
Ask$183.33567.0313,139
Ask$183.28939.7712,572
Ask$183.23871.3911,632
Ask$183.181,15610,760
Ask$183.131,0649,604
Ask$183.08253.748,541
Ask$183.03853.68,287
Ask$182.98934.157,433
Ask$182.931,0566,499
Ask$182.88516.025,443
Ask$182.83917.264,927
Ask$182.78231.574,010
Ask$182.73613.813,778
Ask$182.68839.453,164
Ask$182.63727.222,325
Ask$182.58517.981,598
Ask$182.53228.131,080
Ask$182.48214.09851.45
Ask$182.43637.36637.36
Bid$182.38120.05120.05
Bid$182.33470.57590.62
Bid$182.28176.59767.21
Bid$182.23474.511,242
Bid$182.18421.841,664
Bid$182.13612.832,276
Bid$182.08593.152,870
Bid$182.03286.83,156
Bid$181.98303.213,460
Bid$181.93371.353,831
Bid$181.88731.414,562
Bid$181.83415.794,978
Bid$181.781,1426,120
Bid$181.73627.146,747
Bid$181.68881.847,629
Bid$181.63607.218,237
Bid$181.58294.248,531
Bid$181.53346.98,878
Bid$181.48949.639,827
Bid$181.431,21811,045
Bid$181.38276.1111,321
Bid$181.33576.5511,898
Bid$181.28366.3812,264
Bid$181.23505.8312,770
Bid$181.18503.2313,273
Bid$181.13752.414,026
Bid$181.08455.6914,482
Bid$181.031,55716,039
Bid$180.981,81017,849
Bid$180.93964.4818,814

Hover the depth for the size waiting up to that price and what it is worth.

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/depth_chart.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/depth_chart.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/depth_chart.qwe
// An order book from a seeded random walk (Park–Miller), the same on every render:// `count` levels a side, `tick` apart, around `mid`, sizes growing away from it.fn depth_chart_demo_book(seed: int, mid: num, tick: num, count: int, base: num) -> map {    let s = seed    let rnd = () => {        s = s * 16807 % 2147483647        s / 2147483647    }    let level = (k) => round(base * (0.3 + rnd() * 1.4) * (1 + k * 0.06) * 1000) / 1000    let bids = for k in range(count) => { price: round((mid - tick / 2 - k * tick) * 100) / 100, size: level(k) }    let asks = for k in range(count) => { price: round((mid + tick / 2 + k * tick) * 100) / 100, size: level(k) }    { bids, asks }}let depth_chart_demo_btc = depth_chart_demo_book(5, 61250, 5, 40, 0.9)let depth_chart_demo_acme = depth_chart_demo_book(12, 182.4, 0.05, 30, 400)view DepthChartDemo() =    <div.demo-stack>        <div id="depth_chart-demo-btc">            <DepthChart label="BTC-USD depth" show_label={true} bids={depth_chart_demo_btc.bids} asks={depth_chart_demo_btc.asks}                        unit=" BTC" decimals={1}/>        </div>        <div id="depth_chart-demo-acme" class="demo-narrow" style="max-width: 520px">            <DepthChart label="ACME, within 0.5% of the mid" show_label={true} bids={depth_chart_demo_acme.bids}                        asks={depth_chart_demo_acme.asks} prefix="$" spread={0.5} height={180}/>        </div>        <p.demo-note>Hover the depth for the size waiting up to that price and what it is worth.</p>    </div>

Source

ui/depth_chart.qwe
// DepthChart: an order book's depth: bids (to buy) and asks (to sell) as stepped// areas of the size waiting at each price or better, green below the mid price and// red above it, with the mid price and the spread marked. CSS only: hovering a level// shows its price, the size there and in total up to it, and what that total is// worth; screen readers get the levels as a table.////   <DepthChart label="BTC-USD depth" bids={book.bids} asks={book.asks} unit=" BTC" decimals={1}/>//   <DepthChart label="ACME depth" bids={bids} asks={asks} prefix="$" spread={2}/>//// bids and asks: lists of { price, size }, in any order. The x axis is price, centred// on the mid price (halfway between the best bid and the best ask) and as wide each// way as the farther side's last level; spread: shows only that many percent either// side of the mid instead. The y axis is the size waiting at that price or better.// prefix and suffix: around every price shown ("$"); decimals: digits after the point// of prices (2). unit: after every size (" BTC"); size_decimals: digits after the// point of sizes (up to 4, as needed). height: the plot's height in pixels (260).// label: what the chart shows; it names it for screen readers and captions their// table. show_label also writes it above the chart.import ui/theme.qwe use ui_clslet depth_chart_css = css`.ui-dc { display: grid; gap: 10px; min-width: 0; margin: 0; font: 12px/1.3 var(--ui-font); color: var(--ui-fg); }.ui-dc-title { margin: 0; font-size: 14px; font-weight: 600; }.ui-dc-bid { --k: var(--ui-success); } .ui-dc-ask { --k: var(--ui-danger); }.ui-dc-head { display: flex; flex-wrap: wrap; gap: 4px 18px; margin: 0; padding: 0; list-style: none; color: var(--ui-muted); font-size: 13px; }.ui-dc-head li { display: inline-flex; align-items: center; gap: 6px; }.ui-dc-head b { color: var(--ui-fg); font-weight: 600; font-variant-numeric: tabular-nums; }.ui-dc-key { width: 10px; height: 10px; border-radius: 3px; background: var(--k); }.ui-dc-frame { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 8px; padding-top: 20px; }.ui-dc-plot { position: relative; height: var(--h); border-bottom: 1px solid var(--ui-strong); }.ui-dc-grid { position: absolute; left: 0; right: 0; height: 1px; background: color-mix(in srgb, var(--ui-border) 70%, transparent); }.ui-dc-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }.ui-dc-area { fill: var(--k); opacity: .16; }.ui-dc-edge { fill: none; stroke: var(--k); stroke-width: 2; stroke-linejoin: round; }.ui-dc-mid { position: absolute; z-index: 1; top: 0; bottom: 0; border-left: 1px dashed var(--ui-strong); pointer-events: none; }.ui-dc-mid span { position: absolute; top: 0; left: 50%; transform: translate(-50%, -100%); padding: 1px 5px; border-radius: 3px;  background: var(--ui-subtle); color: var(--ui-fg); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }.ui-dc-zone { position: absolute; top: 0; bottom: 0; }.ui-dc-zone::before { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: var(--y); border-top: 2px solid var(--k);  background: color-mix(in srgb, var(--k) 22%, transparent); opacity: 0; }.ui-dc-zone:hover::before { opacity: 1; }.ui-dc-tip { position: absolute; z-index: 20; bottom: calc(var(--y) + 8px); display: none; min-width: 170px; padding: 7px 10px;  border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); background: var(--ui-surface);  box-shadow: var(--ui-shadow); color: var(--ui-fg); font-size: 12px; pointer-events: none; }.ui-dc-bid .ui-dc-tip { left: 0; } .ui-dc-ask .ui-dc-tip { right: 0; }.ui-dc-zone:hover .ui-dc-tip { display: block; }.ui-dc-tip strong { display: block; margin-bottom: 3px; font-weight: 600; white-space: nowrap; color: var(--k); }.ui-dc-tip p { display: flex; align-items: center; gap: 6px; margin: 2px 0 0; color: var(--ui-muted); white-space: nowrap; }.ui-dc-tip b { margin-left: auto; padding-left: 12px; color: var(--ui-fg); font-weight: 600; font-variant-numeric: tabular-nums; }.ui-dc-y { position: relative; height: var(--h); }.ui-dc-y span { position: absolute; left: 0; transform: translateY(50%); color: var(--ui-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }.ui-dc-x { position: relative; height: 16px; margin-top: 6px; }.ui-dc-x span { position: absolute; top: 0; transform: translateX(-50%); color: var(--ui-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }.ui-dc-x .ui-dc-first { transform: none; }.ui-dc-x .ui-dc-end { transform: translateX(-100%); }.ui-dc-empty { display: grid; place-items: center; height: var(--h); border: 1px dashed var(--ui-border); border-radius: var(--ui-radius-sm); color: var(--ui-muted); }`// n (a whole number, 0 or more) with at least `width` digits.fn depth_chart_pad(n: int, width: int) -> str {    let s = str(n)    len(s) < width ? repeat("0", width - len(s)) + s : s}// A whole number with commas: 1234567 is "1,234,567".fn depth_chart_group(n: int) -> str =    n < 1000 ? str(n) : depth_chart_group(floor(n / 1000)) + "," + depth_chart_pad(n % 1000, 3)// A value as it reads, with commas and `places` digits after the point (up to two as// needed when that is null): 48210.5 is "48,210.50" with 2. Worked out in whole// numbers, so the server and the browser write the same text.fn depth_chart_num(v: num, places: int? = null) -> str {    let digits = places == null ? 2 : (places < 0 ? 0 : (places > 6 ? 6 : places))    let scale = [1, 10, 100, 1000, 10000, 100000, 1000000][digits]    let units = round(abs(v) * scale)    let frac = units % scale    let tail = digits == 0 || (places == null && frac == 0) ? ""        : (places == null && frac % 10 == 0 ? `.${floor(frac / 10)}` : `.${depth_chart_pad(frac, digits)}`)    `${v < 0 && units > 0 ? "-" : ""}${depth_chart_group(floor(units / scale))}${tail}`}// A large number, short: 1500 is "1.5k", 2000000 is "2M", 0.25 is "0.25".fn depth_chart_short(v: num) -> str {    let a = abs(v)    let unit = a >= 1000000000 ? 1000000000 : (a >= 1000000 ? 1000000 : (a >= 1000 ? 1000 : 1))    let tenths = round(a * 10 / unit)    let mark = unit == 1000000000 ? "B" : (unit == 1000000 ? "M" : "k")    unit == 1 ? depth_chart_num(v)        : `${v < 0 ? "-" : ""}${depth_chart_group(floor(tenths / 10))}${tenths % 10 != 0 ? `.${tenths % 10}` : ""}${mark}`}// A change in percent, signed, to two decimals: "+1.25%", "-0.40%".fn depth_chart_signed(p: num) -> str = `${p > 0 ? "+" : (p < 0 && round(abs(p) * 100) > 0 ? "-" : "")}${depth_chart_num(abs(p), 2)}%`// A coordinate out of 100 (or a length), to two decimals.fn depth_chart_pct(n: num) -> str {    let r = round(n * 100) / 100    r == floor(r) ? str(floor(r)) : str(r)}// The axis from lo to hi, widened to round ticks: about `steps` steps of 1, 2, 2.5 or 5 x 10^n.fn depth_chart_scale(lo: num, hi: num, steps: int = 5) -> map {    let span = hi - lo == 0 ? (lo == 0 ? 1 : abs(lo) / 10) : hi - lo    let raw = span / steps    let mag = 1    while mag * 10 <= raw { mag = mag * 10 }    while mag > raw { mag = mag / 10 }    let r = raw / mag    let step = (r <= 1 ? 1 : (r <= 2 ? 2 : (r <= 2.5 ? 2.5 : (r <= 5 ? 5 : 10)))) * mag    let a = floor(lo / step + 0.000001)    let b0 = ceil(hi / step - 0.000001)    let b = b0 == a ? a + 1 : b0    { lo: a * step, hi: b * step, ticks: for i in range(b - a + 1) => (a + i) * step }}// One side of the book, best first (bids high to low, asks low to high), each level// with `total`: its size and every better one's, and `worth`: their price times size.fn depth_chart_side(levels: list, bids: bool) -> list {    let sorted = levels | filter((l) => l.size > 0) | sort_by((l) => bids ? 0 - l.price : l.price)    let total = 0    let worth = 0    let out = []    for l in sorted {        total += l.size        worth += l.size * l.price        out = out | push({ price: l.price, size: l.size, total, worth })    }    out}view DepthChart(bids: list, asks: list, spread: num? = null, height: int = 260, prefix: str = "", suffix: str = "",                decimals: int = 2, unit: str = "", size_decimals: int? = null, label: str = "Depth chart",                show_label: bool = false, class: str? = null) {    style global depth_chart_css    let all_bids = depth_chart_side(bids, true)    let all_asks = depth_chart_side(asks, false)    let ready = len(all_bids) > 0 && len(all_asks) > 0    let mid = ready ? (all_bids[0].price + all_asks[0].price) / 2 : 0    let reach = ready ? max([mid - all_bids[-1].price, all_asks[-1].price - mid]) : 1    let half0 = spread != null && spread > 0 ? mid * spread / 100 : reach * 1.04    let half = half0 > 0 ? half0 : 1    let lo = mid - half    let hi = mid + half    let side_b = all_bids | filter((l) => l.price >= lo)    let side_a = all_asks | filter((l) => l.price <= hi)    let top = max([0] + (side_b | map((l) => l.total)) + (side_a | map((l) => l.total)))    let ys = depth_chart_scale(0, top > 0 ? top : 1, 4)    let xs = depth_chart_scale(lo, hi, 6).ticks | filter((t) => t >= lo && t <= hi)    let px = (p) => (p - lo) * 100 / (hi - lo)    let py = (v) => (ys.hi - v) * 100 / ys.hi    let pct = (v) => depth_chart_pct(v)    let price = (v) => `${v < 0 ? "-" : ""}${prefix}${depth_chart_num(abs(v), decimals)}${suffix}`    let size = (v) => `${depth_chart_num(v, size_decimals ?? (v >= 1000 ? 0 : null))}${unit}`    let gap = ready ? all_asks[0].price - all_bids[0].price : 0    // A side's stepped edge, from the best level outward to the plot's end.    let edge = (levels: list, end: num) => len(levels) == 0 ? "" : "M" + join(range(len(levels)) | map((k) =>        `${pct(px(levels[k].price))},${pct(k == 0 ? 100 : py(levels[k - 1].total))} L${pct(px(levels[k].price))},${pct(py(levels[k].total))}`), " L")        + ` L${pct(end)},${pct(py(levels[-1].total))}`    let fill = (levels: list, end: num) => len(levels) == 0 ? "" : `${edge(levels, end)} L${pct(end)},100 Z`    // A level's hover zone: from its price out to the next level's (or the plot's end).    let zone = (levels: list, k: int, end: num) => {        let a = px(levels[k].price)        let b = k + 1 < len(levels) ? px(levels[k + 1].price) : end        { left: a < b ? a : b, width: abs(b - a) }    }    let axis_width = max([3] + (ys.ticks | map((t) => len(depth_chart_short(t)))))    <figure class={ui_cls(["ui-dc", class])} aria-label={label} style={`--h: ${height}px`}>        {show_label && <figcaption.ui-dc-title>{label}</figcaption>}        {ready && <ul.ui-dc-head>            <li class="ui-dc-bid"><span.ui-dc-key aria-hidden="true"></span>Best bid <b>{price(all_bids[0].price)}</b></li>            <li class="ui-dc-ask"><span.ui-dc-key aria-hidden="true"></span>Best ask <b>{price(all_asks[0].price)}</b></li>            <li>Spread <b>{price(gap)}</b> ({depth_chart_num(mid != 0 ? gap * 100 / mid : 0, 3)}%)</li>        </ul>}        {!ready ? <div.ui-dc-empty>No bids or no asks.</div> : <div.ui-dc-frame aria-hidden="true">            <div.ui-dc-plot>                {for t in ys.ticks => <span.ui-dc-grid style={`top: ${pct(py(t))}%`}></span>}                <svg.ui-dc-svg viewBox="0 0 100 100" preserveAspectRatio="none">                    <g class="ui-dc-bid">                        <path.ui-dc-area d={fill(side_b, 0)}/>                        <path.ui-dc-edge d={edge(side_b, 0)} vector-effect="non-scaling-stroke"/>                    </g>                    <g class="ui-dc-ask">                        <path.ui-dc-area d={fill(side_a, 100)}/>                        <path.ui-dc-edge d={edge(side_a, 100)} vector-effect="non-scaling-stroke"/>                    </g>                </svg>                <span.ui-dc-mid style={`left: ${pct(px(mid))}%`}><span>Mid {price(mid)}</span></span>                {for [k, l] in enumerate(side_b) => {                    let z = zone(side_b, k, 0)                    <div class="ui-dc-zone ui-dc-bid" style={`left: ${pct(z.left)}%; width: ${pct(z.width)}%; --y: ${pct(100 - py(l.total))}%`}>                        <span.ui-dc-tip>                            <strong>Bids at {price(l.price)} or more</strong>                            <p>Size here<b>{size(l.size)}</b></p>                            <p>Total<b>{size(l.total)}</b></p>                            <p>Worth<b>{prefix}{depth_chart_num(l.worth, 0)}</b></p>                            <p>From mid<b>{depth_chart_signed((l.price - mid) * 100 / mid)}</b></p>                        </span>                    </div>                }}                {for [k, l] in enumerate(side_a) => {                    let z = zone(side_a, k, 100)                    <div class="ui-dc-zone ui-dc-ask" style={`left: ${pct(z.left)}%; width: ${pct(z.width)}%; --y: ${pct(100 - py(l.total))}%`}>                        <span.ui-dc-tip>                            <strong>Asks at {price(l.price)} or less</strong>                            <p>Size here<b>{size(l.size)}</b></p>                            <p>Total<b>{size(l.total)}</b></p>                            <p>Worth<b>{prefix}{depth_chart_num(l.worth, 0)}</b></p>                            <p>From mid<b>{depth_chart_signed((l.price - mid) * 100 / mid)}</b></p>                        </span>                    </div>                }}            </div>            <div.ui-dc-y style={`width: ${axis_width}ch`}>                {for t in ys.ticks => <span style={`bottom: ${pct(100 - py(t))}%`}>{depth_chart_short(t)}</span>}            </div>            <div.ui-dc-x>                {for t in xs => <span class={ui_cls([px(t) < 5 && "ui-dc-first", px(t) > 95 && "ui-dc-end"])} style={`left: ${pct(px(t))}%`}>{prefix}{depth_chart_num(t)}</span>}            </div>            <span></span>        </div>}        <table.ui-sr>            <caption>{label}: mid {price(mid)}, spread {price(gap)}</caption>            <thead><tr><th scope="col">Side</th><th scope="col">Price</th><th scope="col">Size</th><th scope="col">Total</th></tr></thead>            <tbody>                {for l in reverse(all_asks) => <tr><td>Ask</td><th scope="row">{price(l.price)}</th><td>{size(l.size)}</td><td>{size(l.total)}</td></tr>}                {for l in all_bids => <tr><td>Bid</td><th scope="row">{price(l.price)}</th><td>{size(l.size)}</td><td>{size(l.total)}</td></tr>}            </tbody>        </table>    </figure>}
Thread
57Useful? Like it, or add to its thread below.

Thread 0 posts

On DepthChart: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.