wrun vs Pine
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.
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).
| Pine | wrun | |
|---|---|---|
| Language | Pine Script, TradingView's own | TypeScript syntax, compiled to WebAssembly |
| Where it runs | TradingView's servers only | Your browser, sandboxed; OpenMarket's servers when the code is protected |
| Order book | Best bid and ask, on 1-tick charts only | Every level, up to 500 a side, on every bar |
| Trades | Sides estimated from price moves | The exchange's own buy and sell tags, by price and by trade size |
| Options | No chain, greeks or implied volatility | Every listed contract with greeks; implied volatility and skew by tenor |
| Drawing | Lines, labels and boxes (500 each), polylines (100), tables | All of that, plus pies, scatters, curves, heatmaps and tiles below the chart, profiles on the price axis, HUD and hover cards |
| History | Lookahead is a switch; TradingView estimates over 95% of indicators repaint | No lookahead to switch on: history shows what you would have seen live |
| Limits | Memory is limited; the number is not published | Every 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.
- Pine: two rows, the touch. The faint rest is the book it never reads.
- wrun: every row, bids and asks.
- 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);
}More: Order flow.
Who actually traded
Delta from the exchange's own tags, split by price and by trade size.
- Each bar, sliced by price into buckets with real bounds.
- Sellers, left of the bar's line.
- Buyers, right of it.
- 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.
- Price: the chart's candles, last close marked on the axis.
- Gamma by strike, docked on the price axis: shaded where net gamma is positive, open where it is negative.
- Call wall: the strike above price with the most call open interest.
- Gamma flip: where net gamma, summed up from the lowest strike, crosses zero.
- 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.
- Pie: shares of one whole, such as this bar's volume by trade size.
- Scatter: one dot per pair of values, x against y.
- Curve: a line over any axis, such as implied volatility by strike.
- Heatmap: a grid of cells, such as volume by hour and weekday.
- 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.
- The legend carries words as well as values, each word colored for its bar.
- A HUD card sits at one of nine anchors and holds typed tiles: a value with its change, a sparkline, a gauge, a meter.
- 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.
- One 4h candle spans four 1h bars.
- 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.
- 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.
//@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")//@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).