wrun vs Pine

View as MarkdownOpen the editor

Pine works from candles: it runs on TradingView's servers and draws lines, labels and boxes on the chart.

A wrun indicator gets the market under the candles. Every level of the order book, buy and sell volume as the exchange tagged each trade, and the live options chain with its greeks arrive as numbers your code can do arithmetic on. It compiles to WebAssembly, runs in your browser, and draws its answer in whatever form fits: a line, a profile on the price axis, a heatmap below the chart, a card in the corner.

How deep each one reads, one row per kind of market data: Pine reads candles, other markets and liquidations, funding and open interest in full, buy and sell volume by price and the order book only with limits, and stops before the options chain; wrun reads every row, and the options chain, implied volatility and skew, prediction odds and ETF flows only wrun reads.Pinewruncandles and volumeother markets, higher timeframesliquidations, funding, open interestbuy and sell volume by pricethe order book, level by leveloptions chain with greeksimplied volatility and skewprediction odds, ETF flowsPremium plans, sides estimatedbest bid and ask, 1-tick chartsstops hereonly wrun reads these

Indicators you can build in wrun and not in Pine:

  • Real delta. Buy minus sell volume from the exchange's own trade tags (Order flow HUD). Pine's footprint infers the side from price moves, on Premium plans and up, one per script.
  • Book walls. The biggest resting bid and ask near price on every bar, and how the pressure between the two sides shifts (Order book HUD).
  • A live gamma map. Gamma exposure at every strike, the zero-gamma flip and the call and put walls, drawn against price from the options chain (Gamma map).
  • Who is trading. Each bar's volume split by trade size, from trades under $1K to trades of $10M and up, buyers and sellers apart.
  • Pictures, not just lines. A volume profile docked on the price axis, an hour-by-weekday heatmap, a HUD of sparklines and gauges (Volume heat hours, Market HUD).
Pinewrun
LanguagePine Script, TradingView's ownTypeScript syntax, compiled to WebAssembly
Where it runsTradingView's servers onlyYour browser, sandboxed; OpenMarket's servers when the code is protected
Order bookBest bid and ask, on 1-tick charts onlyEvery level, up to 500 a side, on every bar
TradesSides estimated from price movesThe exchange's own buy and sell tags, by price and by trade size
OptionsNo chain, greeks or implied volatilityEvery listed contract with greeks; implied volatility and skew by tenor
DrawingLines, labels and boxes (500 each), polylines (100), tablesAll of that, plus pies, scatters, curves, heatmaps and tiles below the chart, profiles on the price axis, HUD and hover cards
HistoryLookahead is a switch; TradingView estimates over 95% of indicators repaintNo lookahead to switch on: history shows what you would have seen live
LimitsMemory is limited; the number is not publishedEvery limit is published, and a run that hits one stops and says which

Already have Pine scripts? Any AI ports them, and the compiler checks every line before anything runs.

The whole order book

Walls, gaps and the pressure between bids and asks, on every bar.

The order book at one moment: Pine reads one bid and one ask, the best of each, on 1-tick charts only, while the rest of the book stays faint; wrun reads every level on both sides, up to 500 a side on every bar, so the wall of bids six levels down is in view.Pinewrunasksbidsthe wallthe best bid and ask1-tick charts onlyevery levelup to 500 a side, every bar
  1. Pine: two rows, the touch. The faint rest is the book it never reads.
  2. wrun: every row, bids and asks.
  3. The wall: six levels down, a size your code can find and draw.

Crypto charts, spot and perpetual. Imbalance over the nearest levels, plus the biggest bid and ask within 1% of price:

param.int("levels", 20, { min: 1, max: 500, label: "Levels per side" });
input("close", ohlcv.close); // the chart's candles
input("book", book.cells, { max_cells: 1000, block_size: 10 }); // the whole book
output("imbalance", line, lower, { color: "#64748b", label: "Imbalance", format: "0.00" });
output("bid_wall", line, overlay, { color: "#16a34a", label: "Bid wall", format: "price" });
output("ask_wall", line, overlay, { color: "#dc2626", label: "Ask wall", format: "price" });

let depth = new BookImbalance(20);

function onStart(): void {
  depth = new BookImbalance(i32(p_levels()));
}

function onBar(): void {
  const n = in_book_cells();
  if (n <= 0) return; // no book on this bar
  const cells = in_book_view(); // [price, size, side] per level
  const close = bar.close();
  let bidWall = NaN; let bidMax = 0.0;
  let askWall = NaN; let askMax = 0.0;
  depth.begin();
  for (let i = 0; i + 2 < n; i += 3) {
    const price = cells[i];
    const qty = cells[i + 1];
    const side = i32(cells[i + 2]); // +1 a bid, -1 an ask
    depth.add(price, qty, side);
    if (Math.abs(price - close) > close * 0.01) continue;
    if (side > 0 && qty > bidMax) { bidMax = qty; bidWall = price; }
    if (side < 0 && qty > askMax) { askMax = qty; askWall = price; }
  }
  depth.end();
  out_imbalance(depth.imbalance()); // (bids - asks) / (bids + asks)
  out_bid_wall(bidWall);
  out_ask_wall(askWall);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 10 to Aug 18, 2026Real output from OpenMarket's engine

More: Order flow.

Who actually traded

Delta from the exchange's own tags, split by price and by trade size.

A footprint, three bars wide: each bar is sliced by price, sell volume drawn to the left of the bar's line and buy volume to the right, each side as the exchange recorded it, with each bar's delta, buy minus sell, written underneath.+60-58+6sellersbuyersdeltaeach side as the exchange recorded it
  1. Each bar, sliced by price into buckets with real bounds.
  2. Sellers, left of the bar's line.
  3. Buyers, right of it.
  4. Delta, under each bar: buy minus sell. Summed bar after bar, it is CVD.

trades.volume, volume_profile and trade_volume_by_size; sizes run in seven buckets, from under 1K to 10M and up, in USD. Big trades against small ones, as two running sums of buy minus sell split at 100K:

input("close", ohlcv.close); // the chart's candles
input("sizes", trade_volume_by_size.cells, { max_cells: 7 });
output("large", line, lower, { color: "#c2410c", width: 2, label: "100K and up", format: "si" });
output("small", line, lower, { color: "#94a3b8", label: "Under 100K", format: "si" });

let large = 0.0; // buy minus sell in USD, trades of 100K and up
let small = 0.0; // the same for smaller trades

function onBar(): void {
  const n = in_sizes_cells();
  const cells = in_sizes_view();
  // [bucket, buy_usd, sell_usd, buy_count, sell_count] per bucket
  for (let i = 0; i + 4 < n; i += 5) {
    const net = cells[i + 1] - cells[i + 2]; // buy minus sell
    if (cells[i] >= 4.0) large += net; // bucket 4 starts at 100K
    else small += net;
  }
  out_large(large);
  out_small(small);
}

When the lines part, the big trades and the small ones are on opposite sides. More: Order flow.

The options chain

Gamma by strike, the flip and the walls, from the live chain, in one call.

The options chain beside price: gamma by strike docked on the price axis, shaded where net gamma is positive and open where it is negative; the call wall above price, the put wall below it, and the gamma flip, dashed, where net gamma crosses zero.gamma by strikecall wallpricegamma flipput wall
  1. Price: the chart's candles, last close marked on the axis.
  2. Gamma by strike, docked on the price axis: shaded where net gamma is positive, open where it is negative.
  3. Call wall: the strike above price with the most call open interest.
  4. Gamma flip: where net gamma, summed up from the lowest strike, crosses zero.
  5. Put wall: the strike below price with the most put open interest.

Any coin an options venue lists. Implied volatility and skew by tenor, one week to six months, are inputs on every bar of BTC and ETH charts.

The levels and max pain, on a card:

input("close", ohlcv.close); // spot is the chart's close
input("chain", options_chain.cells, { max_cells: 4000 });
output("flip", none, overlay, { label: "Gamma flip" });
output("call_wall", none, overlay, { label: "Call wall" });
output("put_wall", none, overlay, { label: "Put wall" });
output("max_pain", none, overlay, { label: "Max pain" });
render.hud("chain_card", {
  position: "top_right", title: "Options chain", columns: 2,
  tiles: [
    tile.value("Call wall", "call_wall", { format: "price" }),
    tile.value("Gamma flip", "flip", { format: "price" }),
    tile.value("Put wall", "put_wall", { format: "price" }),
    tile.value("Max pain", "max_pain", { format: "price" }),
  ],
});

let chain = new OptionsChain(1);

function onStart(): void { chain = new OptionsChain(512); }

function onBar(): void {
  if (bar.isLast()) { // the chain arrives on the live bar
    const n = in_chain_cells();
    const now = bar.time() * 1000.0; // the clock in milliseconds
    if (n > 0) chain.load(in_chain_view(), n, now, bar.close());
  }
  out_flip(chain.zeroGamma()); // net gamma crosses zero here
  out_call_wall(chain.callWall()); // most call open interest above
  out_put_wall(chain.putWall()); // most put open interest below
  out_max_pain(chain.maxPain()); // pays option holders the least
}

The Gamma map recipe docks the profile as drawn. More: Options kit.

Off the time axis

Some answers are not lines. A pie, a curve over strikes or a heatmap gets its own pane.

Five panel kinds, each in a pane of its own below the chart: a pie of the bar's volume by order size, a scatter of one value against another, a curve such as implied volatility by strike, a heatmap such as volume by hour and weekday, and tiles of big numbers with sparklines.12.4%64KPievolume by sizeScatterx against yCurvevolatility by strikeHeatmaphour by weekdayTilesbig numbers
  1. Pie: shares of one whole, such as this bar's volume by trade size.
  2. Scatter: one dot per pair of values, x against y.
  3. Curve: a line over any axis, such as implied volatility by strike.
  4. Heatmap: a grid of cells, such as volume by hour and weekday.
  5. Tiles: big numbers, each with a caption and a sparkline.

This bar's volume by trade size as a pie, rebuilt on the live bar. The share of trades of 100K and up runs as a line through history:

input("close", ohlcv.close);
input("sizes", trade_volume_by_size.cells, { max_cells: 7 });
output("share", line, lower, { color: "#c2410c", label: "100K and up", format: "%" });
const mix = frame("mix", { max_bytes: 1024 }); // the pie's slices
panel.pie({ name: "size_mix", title: "This bar by trade size",
  x: "category", place: "below", frame: mix, hole: 0.5 });

const NAMES: StaticArray<string> = ["Under 1K", "1K to 10K",
  "10K to 100K", "100K to 500K", "500K to 1M",
  "1M to 10M", "10M and up"];
const COLORS: StaticArray<string> = ["#e2e8f0", "#cbd5e1",
  "#94a3b8", "#fdba74", "#fb923c", "#ea580c", "#9a3412"];

function onBar(): void {
  const n = in_sizes_cells();
  const cells = in_sizes_view();
  const live = bar.isLast();
  let total = 0.0; let large = 0.0;
  if (live) { fb_clear(); fb_text('{"rows":['); }
  for (let i = 0; i + 4 < n; i += 5) {
    const usd = cells[i + 1] + cells[i + 2];
    const k = i32(cells[i]) - 1; // buckets run 1 to 7
    total += usd;
    if (k >= 3) large += usd;
    if (live) { // one slice per bucket: [name, value, color]
      if (i > 0) fb_text(",");
      fb_text("["); fb_str(NAMES[k]); fb_text(",");
      fb_f64(usd, 0); fb_text(","); fb_str(COLORS[k]); fb_text("]");
    }
  }
  if (live) { fb_text("]}"); writeFrameBuffer(FRAME_MIX); }
  out_share(total > 0.0 ? (100.0 * large) / total : NaN);
}

fb_* builds the frame without allocating: the sandbox refuses memory growth once the bars start. More: Cards, frames and panels, Volume profile and value area, Volatility term structure.

Cards, not tables

Your code writes numbers and words. The chart draws the card.

Three answers on one chart: the legend names the basis line, its value and a word colored for this bar; a HUD card in the corner holds four typed tiles, a value with its change, a sparkline, a gauge and a meter; and a hover card opens where the cursor rests on the line.Basis64,120.5overSignalBasis64,120+1.2%RSIRSI levelVolumeBasis64,120.5RSI 61.3the legendthe HUD cardthe hover card
  1. The legend carries words as well as values, each word colored for its bar.
  2. A HUD card sits at one of nine anchors and holds typed tiles: a value with its change, a sparkline, a gauge, a meter.
  3. A hover card opens where the cursor rests on a line, a legend entry or a tile.

The options card above is one; Market HUD builds a full board. Every block kind: What the chart shows.

Settings

Declare the kind. The dialog draws the control.

  • Presets: named sets of values; one click fills every setting they name (Presets).
  • Ranges, multi-selects, lists: a slider with two handles, a pick of weekdays, an editable list of lookbacks (Setting kinds).
  • Units: an offset in percent of price or in ATRs, picked beside the number (Sessions and units).
  • Pages: settings on pages and sections, with a filter box past twelve (Pages, sections, dividers, notes).

No lookahead

onBar() gets one bar. There is no next bar to peek at.

Higher timeframes, bar by bar: a 4h candle spans four 1h bars; with lookahead on, a Pine script on history sees the new 4h close from the first of those bars, while in wrun every 1h bar sees the previous close and the new one arrives on the bar after the 4h candle closes.one 4h candle4h closePine, lookahead onwrunsees it four bars earlyarrives after the closethe new 4h closethe previous one
  1. One 4h candle spans four 1h bars.
  2. Pine, lookahead on: history sees the new 4h close from the first of those bars, before it happened. It is off by default; an author turns it on.
  3. wrun: no switch. Every 1h bar sees the last closed 4h candle; the new close arrives on the bar after.

The forming bar replays from the last closed bar on every update. No varip, no barstate.isrealtime (Repainting).

Bring your Pine scripts

Hover a Pine line. See the wrun line that replaces it.

textPine
//@version=6
indicator("MA Cross", overlay=true)
fastLen = input.int(9, "Fast")
slowLen = input.int(21, "Slow")
fast = ta.ema(close, fastLen)
slow = ta.sma(close, slowLen)
plot(fast, color=color.orange)
plot(slow, color=color.blue)
up = ta.crossover(fast, slow)
plotshape(up, shape.triangleup,
  location.belowbar)
alertcondition(up, "Golden cross")
wrun
//@lang=wrun-ts
param.int("fast_len", 9, { min: 1 });
param.int("slow_len", 21, { min: 1 });
output("fast", line, overlay);
output("slow", line, overlay);
render.shape("cross_up_mark", {
  output: "low", shape: "triangle_up",
  where: "crossed_up" });
alert("golden_cross", {
  when: crossedUp });
…
fast = new Ema(i32(p_fast_len()));
slow = new Sma(i32(p_slow_len()));
const crossed = cross.update(
  fastValue, slowValue);

In the editor, Copy for LLM hands any AI your code, the build's rules, the kit's API and every compiler error (Write it with AI).