API für benutzerdefinierte Indikatoren
Indikatoren mit KI generieren
Die gesamte API ist auch als einzelne Textdatei für Sprachmodelle verfügbar: AI-scripting.txt. Hängen Sie sie an ChatGPT, Claude, Gemini oder ein anderes LLM an und beschreiben Sie den gewünschten Indikator.
Beispielanfrage:
Read the attached [AI-scripting.txt] https://mobchart.com/docs/public/AI-scripting.txt file and use it as the only reference for the MobChart scripting API.Create a custom indicator that plots a 20-period EMA and a 50-period EMA on the chart. Color the candles green when EMA20 is above EMA50 and red otherwise.Use only the documented API. Do not invent functions. Return the complete indicator code.
Sandbox und Limits
Indikator-Entwicklung
Die Entwicklung von Indikatoren erfolgt in einem eigenen Widget, das neben dem Chart platziert werden kann, in dem der Indikator aktiviert wird.

Einen Indikator erstellen
define(title, description, type, isPublic, isPublicCode, options)
Registriert den Indikator im System.
| Parameter | Typ | Beschreibung |
|---|---|---|
title | string | Name des Indikators |
description | string | Kurze Beschreibung |
type | 'onchart' | 'offchart' | Platzierung des Indikators (im Chart oder in einem eigenen Panel) |
isPublic | true | false | Ob der Indikator öffentlich ist |
isPublicCode | true | false | Ob der Quellcode öffentlich ist |
options | object (optional) | { fullRange, warmup }, siehe unten |
Beispiel:
define("My Indicator", "Shows EMA line", "onchart", true, false);
Berechnungsbereich. Standardmäßig berechnet ein Skript nur den Teil des Charts, der es braucht: die beim Zurückscrollen geladenen Kerzen oder die neuesten Kerzen bei einer Aktualisierung, plus warmup Kerzen Historie davor (Standard 500), die für die Berechnung nötig sind, aber nicht zurückgegeben werden. Deklarieren Sie warmup, wenn Ihr Indikator mehr oder weniger Historie braucht, z. B. { warmup: 200 } für einen EMA(200). Ein Skript, dessen Werte oder Formen vom gesamten sichtbaren Bereich abhängen (VWAP ab der ersten sichtbaren Kerze, kumuliertes Delta, ein Label am Maximum des Fensters, Levels über den ganzen Chart), muss { fullRange: true } deklarieren, sonst sind seine Ergebnisse nach dem Nachladen von Historie falsch. Der Modus wird bei jedem Speichern aus define() gelesen: ein ohne fullRange gespeichertes Skript wird teilweise berechnet. Ergänzen Sie das Flag daher bei bestehenden kumulativen Skripten, wenn Sie sie bearbeiten. Teilweise berechnete Skripte werden auch stückweise geladen: Wird eines über eine lange Historie eingeschaltet, fordert der Chart zuerst die neuesten 500 Kerzen an (oder warmup Kerzen, wenn das mehr ist), die älteren Teile folgen mit je einer Anfrage, sodass der sichtbare Teil sofort erscheint. Skripte mit fullRange werden immer in einer einzigen Anfrage über den gesamten Bereich berechnet.
define("Window max", "Label at the highest visible high", "onchart", true, true, { fullRange: true });
Platzierung des Indikators für onchart und offchart:

Eingabeparameter
Die Klassen input und style ermöglichen es Ihnen, vom Nutzer konfigurierbare Einstellungen zu erstellen (ähnlich wie input() in PineScript).
| Methode | Beschreibung |
|---|---|
number(title, defaultValue, min, max, step) | Numerische Eingabe |
range(title, defaultValue, min, max, step) | Bereichseingabe |
select(title, defaultValue, values) | Dropdown-Auswahl |
color(title, defaultValue) | Farbauswahl |
boolean(title, defaultValue) | Boolesche Eingabe |
tickers(title, defaultValue, exchange) | Mehrfachauswahl der Ticker einer Börse (Standard: die Börse des Charts); liefert die gewählten Ticker als Array |
Beispiel (input) für Hauptparameter:
const length = input.number("Period", 14, 1, 100, 1);const type = input.select("Type", 'sma', ['sma', 'ema']);const color = input.color("Line Color", "#FF0000");// Paare zum Ein- / Ausschließen, aus den Tickern der Chart-Börse gewähltconst include = input.tickers("Include pairs", []);const exclude = input.tickers("Exclude pairs", []);
Oder (style) für visuelle Parameter:
const length = style.number("Size", 14, 1, 100, 1);const color = style.select("Background Color", '#FF0000', ["#FF0000", "#AA0000", "#BB0000"]);const color = style.color("Background Color", "#FF0000");

Datenquellen
Die Klasse source ruft Marktdaten vom Server ab. Die kerzenbasierten Methoden nehmen dieselben Parameter entgegen; die beiden Live-Schnappschüsse weichen ab: orderbook() nimmt Börse und Ticker, levels() eine Börse und ein Einstellungsobjekt.
| Parameter | Beschreibung |
|---|---|
exchange, ticker, timeframe | Standardmäßig das Chart, in dem der Indikator geöffnet ist. Übergeben Sie andere Werte, um Daten eines anderen Marktes abzurufen. |
lookback | Anzahl der Bars an Historie ab dem aktuellen Datum, nicht mehr als 5000. |
ohlcv(exchange, ticker, timeframe, lookback?)
Kerzendaten.
[{"open": 115886.17,"high": 115911.96,"low": 115764.27,"close": 115815.16,"volume": 228.41528,"startTime": 1757699100000},{...},...]
depths(exchange, ticker, timeframe, lookback?)
Historische Orderbuch-Snapshots, einer pro Kerze. Nur die größten Limit-Orders werden gespeichert (die Daten hinter der Heatmap).
[{"data": {"asks": {"109425": 5.467,"109495": 3.564,...},"bids": {"33000": 3.746,"35000": 16.49,...}},"startTime": 1757699100000},{...},...]
clusters(exchange, ticker, timeframe, lookback?)
Cluster-Daten (Footprints): Kauf- und Verkaufsvolumen auf jedem Preisniveau der Kerze.
[{"data": {"totalBuy": 16053385,"totalSell": 20490989,"0.8654": { "buy": 286845, "sell": 336689 },"0.8653": { "buy": 271848, "sell": 259167 },"0.8652": { "buy": 318814, "sell": 373497 },...},"startTime": 1757699100000},{...},...]
liquidations(exchange, ticker, timeframe, lookback?)
Liquidiertes Volumen auf jedem Preisniveau der Kerze (nur Futures-Märkte). Dasselbe Format wie clusters().
[{"data": {"totalBuy": 16053385,"totalSell": 20490989,"0.8654": { "buy": 286845, "sell": 336689 },"0.8653": { "buy": 271848, "sell": 259167 },"0.8652": { "buy": 318814, "sell": 373497 },...},"startTime": 1757699100000},{...},...]
moreData(exchange, ticker, timeframe, lookback?)
Derivatedaten pro Kerze: Funding Rate, Open-Interest-Kerze, Long/Short-Verhältnisse (nur Futures-Märkte).
[{"data": {"FR": 0.00003777, // Funding Rate"OI": { // Open Interest Candle"open": 85645.708,"close": 83496.225,"high": 85801.04,"low": 83397.918},"LS": { "l": 0.5426, "s": 0.4574 }, // Long/Short Ratio"taLS": { "l": 0.552, "s": 0.448 }, // Top Traders Account Ratio"tpLS": { "l": 0.5996, "s": 0.4004 } // Top Traders Position Ratio},"startTime": 1757699100000},{...},...]
orderbook(exchange, ticker)
Live-Snapshot des Orderbuchs: das aktuelle vollständige Buch, nicht an Kerzen gebunden.
{"asks": {"109425": 5.467,"109495": 3.564,...},"bids": {"109400": 1.2,"109390": 0.8,...}}
Die Niveaus sind nicht sortiert; verwenden Sie die Orderbuch-Hilfsfunktionen der Klasse ta (ta.bestBid, ta.sumBids, ...), um mit dem Buch zu arbeiten.
Die historischen Bücher der Kerzen stammen aus depths(): depths()[i].data hat dasselbe Format, dort werden jedoch nur die größten Limit-Orders gespeichert.
Beispiel:
const candles = source.ohlcv(); // The exchange, ticker, and timeframe parameters default to the current chart.const closes = candles.map(c => c.close);
levels(exchange, settings?)
Die Level des Liquiditäts-Screeners: die großen ruhenden Limit-Orders einer Börse, gefiltert mit denselben Einstellungen wie die Screener-Seite. Live-Daten, nicht an Kerzen gebunden. Ohne Plan sind die Nähe auf 1 %, die Lebensdauer auf 60 Minuten und das Ergebnis auf 200 Level begrenzt; mit Plan bis zu 2000 Level. Die größten Orders kommen zuerst. Zählt als eine Datenquellen-Anfrage.
{"BTCUSDT": {"asks": {"109500": {"price": 109500,"quantity": 42.5, // size in the base asset"quantityUSDT": 4653750, // size in the quote currency"distance": 0.62, // from the current price, percent"duration": 184, // minutes the order has been standing"touches": 2,"currentTouches": 1,"filledPercent": 12.4,"strength": 95.5, // percent, see the screener"diversity": 11,"isContinuous": true,"date": "2026-10-10T06:12:03.000Z"},...},"bids": { ... },"volume1hTotal": 183000000},...}
Einstellungen (alle optional):
| Einstellung | Standard | Beschreibung |
|---|---|---|
tickers | alle Ticker | nur diese Ticker der Börse |
exclude | [] | zu überspringende Ticker |
type | "all" | "all", "bids" oder "asks" |
nearness | 5 | max. Abstand zum aktuellen Preis in Prozent |
size | 100000 | min. Ordergröße in der Notierungswährung |
lifespan | 0 | min. Minuten, die die Order steht |
touches, currentTouches | 0 | min. Berührungen des Levels / der aktuellen Order |
filled | 0 | min. ausgeführter Teil der Order in Prozent |
strength, diversity | 0 | min. Stärke (Prozent) und Diversität wie im Screener |
continuousOnly | false | Orders überspringen, die verschwanden und zurückkamen |
volume1h | 0 | min. 1h-Volumen des Tickers in der Notierungswährung |
spotAndFutures | false | nur Ticker, die es am Spot- und am Futures-Markt gibt |
limit | 500 | max. Anzahl Level, die größten zuerst (max. 2000) |
Beispiel:
// the big orders around the price of the chart market as horizontal lines, labelled with the size and the lifespanconst candles = await source.ohlcv();const lastDate = candles.at(-1).startTime;const levels = await source.levels(currentExchange, { tickers: [currentTicker], nearness: 3, size: 300000, lifespan: 30 });const own = levels[currentTicker] || { asks: {}, bids: {} };for (const [type, color] of [["asks", "#ef5350"], ["bids", "#26a69a"]]) {for (const [price, level] of Object.entries(own[type])) {plot.horizontalLine(`${type} ${price}`, +price, color, 0.8, Math.min(4, 1 + level.quantityUSDT / 1e6));plot.text(`${type} ${price} label`, `${(level.quantityUSDT / 1e6).toFixed(2)}M · ${level.duration}m`, lastDate, +price, 10, color);}}
keyLevels(exchange, settings?)
Die Levels des Key-Levels-Screeners: Open, High und Low des aktuellen und des vorherigen Tages, der Woche, des Monats und des Jahres für jeden Ticker einer Börse, mit dem Abstand jedes Levels zum aktuellen Preis. Live-Daten, nicht an Kerzen gebunden. Ohne Plan enthält das Ergebnis nur die Levels innerhalb von ±1 % vom Preis (der feste Bereich der Screener-Seite) und höchstens 100 Ticker; mit Plan ein beliebiger Abstandsbereich und bis zu 1000 Ticker. Die Ticker mit dem nächstgelegenen Level kommen zuerst. Zählt als eine Datenquellen-Anfrage.
{"BTCUSDT": {"price": 79776.57, // aktueller Preis"updatedAt": 1788707880000,"bootstrapped": true, // false, solange die Historie des Tickers noch gesammelt wird"nearest": 0.08, // Abstand des nächsten Levels in Prozent"matches": 5, // Levels innerhalb des Abstandsbereichs"daily": { "start": 1788652800000, "open": 79700, "high": 79985, "low": 79710, "openApprox": false,"distance": { "open": -0.1, "high": 0.26, "low": -0.08 } }, // Prozent vom Preis, mit Vorzeichen"prevDay": { ... }, "weekly": { ... }, "prevWeek": { ... },"monthly": { ... }, "prevMonth": { ... }, "yearly": { ... }, "prevYear": null // null: noch keine Daten für die Periode},...}
Einstellungen (alle optional):
| Einstellung | Standard | Beschreibung |
|---|---|---|
tickers | alle Ticker | nur diese Ticker der Börse |
exclude | [] | zu überspringende Ticker |
periods | alle | "daily", "prevDay", "weekly", "prevWeek", "monthly", "prevMonth", "yearly", "prevYear" |
fields | alle | "open", "high", "low": die zurückzugebenden Levels |
distanceFrom, distanceTo | keine | Abstandsbereich vom Preis in Prozent mit Vorzeichen (z. B. -2 und 2); nur Ticker mit einem Level innerhalb des Bereichs werden zurückgegeben. Ohne Plan ist der Bereich fest ±1 % |
limit | 200 | maximale Anzahl Ticker, das nächstgelegene Level zuerst (max. 1000; 100 ohne Plan) |
Beispiel:
// the key levels of the chart market as horizontal linesconst candles = await source.ohlcv();const lastDate = candles.at(-1).startTime;const rows = await source.keyLevels(currentExchange, { tickers: [currentTicker], periods: ["daily", "prevDay", "weekly"] });const own = rows[currentTicker];if (own) {for (const period of ["daily", "prevDay", "weekly"]) {const level = own[period];if (!level) continue;for (const [field, color] of [["high", "#ef5350"], ["low", "#26a69a"], ["open", "#90a4ae"]]) {if (level[field] == null) continue;plot.horizontalLine(`${period} ${field}`, level[field], color, 0.8, 1);plot.text(`${period} ${field} label`, `${period} ${field.toUpperCase()}`, lastDate, level[field], 10, color);}}}
levelsMetrics(exchange, settings?)
Die Historie der starken Liquiditätslevel, die für die Seite Metriken gesammelt wird: alle 5 Minuten nimmt der Server einen Schnappschuss der Liquiditäts-Screener-Level jeder Börse mit einer Dauer von mindestens 30 Minuten und einer Stärke von mindestens 80 % und bewahrt eine Woche davon auf. Die Quelle filtert die Schnappschüsse eines Zeitraums wie die Seite und liefert pro Schnappschuss die Anzahl und die $-Größe der passenden Level, die Aggregate je Ticker und den Lebenszyklus jedes Levels. Gesammelte Daten, nicht an Kerzen gebunden: series[].at muss mit den Kerzendaten abgeglichen werden. Ohne Abonnement beträgt der Zeitraum höchstens 24 Stunden und die Liste höchstens 100 Level; mit Abonnement bis zu 7 Tage und 1000 Level. Zählt als eine Datenquellen-Anfrage.
{"exchange": "binanceFutures","hours": 24,"snapshotMinutes": 5,"latestAt": 1792102098685,"summary": { "snapshots": 287, "uniqueLevels": 217, "uniqueTickers": 118, "activeNow": 180, "bidsNow": 102, "asksNow": 78, "usdNow": 119294436 },"series": [ // one point per snapshot, oldest first{ "at": 1792015822555, "bids": 24, "asks": 21, "bidsUsd": 1851345, "asksUsd": 1228883, "tickers": 20 },...],"tickers": { // per ticker, over the period"BTCUSDT": { "now": 3, "nowUsd": 95710480, "snapshots": 63, "bids": 3, "asks": 0, "levels": 3,"maxUsd": 42347880, "maxDuration": 8220, "maxStrength": 100 },...},"levels": [ // the lifecycle of every level, active ones first{ "ticker": "BTCUSDT", "side": "bids", "price": 52050, "usd": 42347880, "quantity": 813.6, "distance": 19.8,"duration": 8220, "strength": 100, "continuous": 1, "firstSeen": 1792015822555, "lastSeen": 1792102098685,"snapshots": 63, "maxDuration": 8220, "maxStrength": 100, "active": true },...],"levelsTotal": 217}
Einstellungen (alle optional):
| Einstellung | Standard | Beschreibung |
|---|---|---|
tickers | alle Ticker | nur diese Ticker der Börse |
exclude | [] | zu überspringende Ticker |
hours | 24 | Zeitraum in Stunden (max. 168; 24 ohne Abonnement) |
minDuration | 30 | minimale Orderdauer in Minuten: 30, 60, 120 oder 180 |
minStrength | 80 | minimale Orderstärke in Prozent: 80, 90 oder 100 |
maxDistance | keine | maximale Entfernung der Order zum aktuellen Preis in Prozent, in jedem Schnappschuss geprüft |
stableOnly | false | true: nur Orders, deren Menge nie gesprungen ist („Orders mit instabilen Mengen ausschließen“) |
limit | 200 | maximale Anzahl Level in levels (max. 1000; 100 ohne Abonnement) |
Beispiel:
// $ size of the strong levels of the chart market per candleconst candles = await source.ohlcv();const data = await source.levelsMetrics(currentExchange, { tickers: [currentTicker], hours: 24, minDuration: 60, minStrength: 90, stableOnly: true });const series = data.series; // oldest firstlet j = 0;const buy = [], sell = [];for (let i = 0; i < candles.length; i++) {const end = candles[i + 1] ? candles[i + 1].startTime : candles[i].startTime + (candles[1].startTime - candles[0].startTime);while (j + 1 < series.length && series[j + 1].at < end) j++; // the last snapshot inside the candleconst point = series[j] && series[j].at < end ? series[j] : null;buy.push(point ? point.bidsUsd : null);sell.push(point ? point.asksUsd : null);}plot.areaSeries("Buy levels ($)", buy, "#26a69a", 0.3, "#26a69a", 1, 1, 0);plot.areaSeries("Sell levels ($)", sell, "#ef5350", 0.3, "#ef5350", 1, 1, 0);
HTTP-Anfragen
Die Klasse http sendet Anfragen an externe HTTP-APIs und gibt die Antwort zurück, sodass ein Indikator Daten nutzen kann, die in source nicht verfügbar sind: Ihr eigener Server, eine öffentliche REST-API, ein Anbieter von On-Chain-Metriken, ...
Wie bei source.* werden die Aufrufe als synchrone Funktionen geschrieben, ein await ist nicht nötig.
| Methode | Beschreibung |
|---|---|
get(url, options?) | GET-Anfrage |
post(url, body?, options?) | POST-Anfrage. Ein Objekt oder Array als body wird als JSON gesendet (Content-Type: application/json), ein String wird unverändert gesendet |
options ist { headers: { name: value } } — bis zu 10 eigene Header (zum Beispiel Authorization oder x-api-key).
Beide Methoden geben dasselbe Antwortobjekt zurück und werfen nie eine Exception:
{ok: true, // Status 200–299status: 200, // HTTP-Status, 0 wenn die Anfrage nicht gesendet wurdeheaders: { "content-type": "application/json", ... },data: { ... }, // Antwortkörper als JSON geparst, null wenn der Körper kein JSON isttext: "...", // roher Antwortkörpererror: null // Grund, wenn die Anfrage fehlgeschlagen ist: "timeout", "url not allowed", "too many requests", ...}
Mögliche Werte von error: invalid url, url not allowed, invalid headers, invalid body, body too large, response too large, timeout, too many requests, rate limit, busy, proxy unavailable, network error.
Beispiel (GET):
define("Fear & Greed", "Crypto Fear & Greed index", "offchart", true, true);const res = http.get("https://api.alternative.me/fng/?limit=30");if (!res.ok) logger.log("request failed:", res.status, res.error);// res.data = { data: [{ value: "71", timestamp: "1757635200" }, ...] }const byDay = {};(res.data?.data || []).forEach((row) => { byDay[Number(row.timestamp) * 1000] = Number(row.value); });const candles = source.ohlcv();const values = candles.map((c) => c ? byDay[c.startTime - (c.startTime % 86400000)] ?? null : null);plot.lineSeries("Fear & Greed", values, "#00C8FF");
Beispiel (POST):
const candles = source.ohlcv();const last = candles.at(-1);const res = http.post("https://example.com/api/signals",{ exchange: currentExchange, ticker: currentTicker, close: last.close },{ headers: { "x-api-key": "YOUR_KEY" } } // private code only);logger.log("signal sent:", res.ok, res.status, res.data);
Globale Variablen
| Variable | Beschreibung | Beispiel |
|---|---|---|
currentExchange | Aktuelle Börse im Chart | binance, binanceFutures, bybit, bybitFutures, okx, okxFutures, coinbase, bitfinex, bitfinexFutures, hyperliquid, hyperliquidFutures, asterFutures |
currentTicker | Aktueller Ticker im Chart (Format hängt von der Börse ab) | BTCUSDT, ADA-USD, BTC/USDC, ... |
currentTimeframe | Aktueller Zeitrahmen im Chart | 1m, 5m, 15m, ... |
exchanges | Namen aller Börsen (eingefrorenes Array) | ["binance", "binanceFutures", ...] |
tickers | Ticker aller Börsen: tickers[currentExchange] ist die Liste der Chart-Börse (eingefroren) | tickers.binance = ["BTCUSDT", "ETHUSDT", ...] |
heatmapStep | Preisschritt der Orderbuch-Heatmap und der Cluster beim aktuellen Preis (die Genauigkeit, auf die der Server Preise rundet: 5 über 5000, 1 bei 1000-5000, 0.1 bei 100-1000, ...), aus dem letzten Schlusskurs des Charts; null, solange der Preis unbekannt ist (bekannt nach dem ersten source.ohlcv() des Chart-Markts) | 5, 1, 0.1, 0.001, ... |
Beispiel:
const candles = source.ohlcv(currentExchange, currentTicker, currentTimeframe);const closes = candles.map(c => c.close);
Ticker-Formate. Jede source-Methode mit Börse und Ticker schickt den Ticker durch resolveTicker: aus dem übergebenen Format (ETHUSDT, ETH-USD, ETH/USDC, ETH:USD, ETHF0:USTF0, ...) wird die Basiswährung gelesen und der Ticker im Format der angefragten Börse neu gebildet. ETH-USD funktioniert also für Binance, und currentTicker eines Binance-Charts funktioniert für Coinbase. Nur der Coin bleibt erhalten: es wird das Standard-Paar der Börse verwendet (USDT bei Binance, USD bei Coinbase, USDC bei Hyperliquid, ...), ein Coin, den die Börse nicht listet, liefert ein leeres Ergebnis mit einer Zeile im Log.
// ein Coinbase-Ticker auf Binance Futures: wird zu ETHUSDT aufgelöstconst depths = source.depths("binanceFutures", "ETH-USD");// der Ticker eines Binance-Charts auf Coinbase: aus ETHUSDT wird ETH-USDconst spot = source.ohlcv("coinbase", currentTicker, currentTimeframe);
Logging
Die Klasse logger dient zur Ausgabe von Informationen und zum Debuggen.
| Methode | Beschreibung |
|---|---|
log(...args) | Fügt dem Log einen Eintrag hinzu (mit Begrenzungen für Länge und Anzahl) |
Beispiel:
const candles = source.ohlcv();logger.log("First candle:", candles?.[0]);

Visualisierung
Die Klasse plot ist für das Zeichnen grafischer Elemente und Datenreihen zuständig.
Geometrische Formen
| Methode | Beschreibung |
|---|---|
box(title, date1, price1, date2, price2, fill, opacity, stroke, strokeOpacity, strokeWidth) | Rechteck |
square(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth) | Quadrat |
diamond(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth) | Raute |
triangle(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth) | Dreieck |
circle(title, date1, price1, radius = 10, fill, opacity, stroke, strokeOpacity, strokeWidth) | Kreis |
text(title, value, date1, price1, fontSize, fill, opacity) | Text im Chart |
table(title, rows, options) | Tabelle in einer Ecke des Charts, siehe Tabellen |
straightLine(title, date1, price1, date2, price2, stroke, strokeOpacity, strokeWidth, strokeDasharray) | Gerade Linie |
horizontalLine(title, price1, stroke, strokeOpacity, strokeWidth, strokeDasharray) | Horizontale Linie über das gesamte Chart |
verticalLine(title, date1, stroke, strokeOpacity, strokeWidth, strokeDasharray) | Vertikale Linie über das gesamte Chart |
| Parameter | Datentyp | Beschreibung |
|---|---|---|
title | string | Name des Elements (eindeutige Kennung im Chart) |
date1 | number (Zeitstempel, ms) | Start-Zeitstempel |
price1 | number | Startpreis (Koordinate der Y-Achse) |
date2 | number (Zeitstempel, ms) | End-Zeitstempel (für Linien und Rechtecke) |
price2 | number | Endpreis (für Linien und Rechtecke) |
size | number | Größe der Form (in Pixeln) |
radius | number | Kreisradius (Standard 10) |
fill | string (hex, z. B. "#FF0000") | Füllfarbe der Form |
opacity | number (0–1) | Deckkraft der Füllung |
stroke | string (hex, z. B. "#000000") | Linienfarbe |
strokeOpacity | number (0–1) | Deckkraft der Linie |
strokeWidth | number | Linienbreite (in Pixeln) |
strokeDasharray | array (z. B. [5,5]) | Stil der gestrichelten Linie |
fontSize | number | Textgröße (in Pixeln) |
value | string | Textwert (für die Methode text) |
Beispiel:
plot.text("Text object", "Hello world!", 1757699100000, 115000, 10, "#00C8FF", 0.5);plot.straightLine(`${namePrefix} Open`, level.timeStart, level.open, level.timeEnd, level.open, color);plot.circle(`${namePrefix} High`, level.timeStart, level.high, 3, color);
Es können nicht mehr als 3000 Formen im Chart angezeigt werden.
Datenreihen
| Methode | Beschreibung |
|---|---|
lineSeries(title, values, stroke, strokeOpacity, strokeWidth, strokeDasharray) | Linienchart |
barSeries(title, values, fill, opacity) | Balkenchart |
candleSeries(title, openValues, closeValues, highValues, lowValues, fill, opacity) | Kerzen |
areaSeries(title, values, fill, opacity, stroke, strokeOpacity, strokeWidth, base) | Gefüllte Fläche zwischen der Linie und base (Standard: der untere Rand des Charts) |
bandSeries(title, upperValues, lowerValues, fill, opacity, stroke, strokeOpacity, strokeWidth, step) | Gefülltes Band zwischen zwei Reihen, jede Kerze behält ihre eigene Farbe und Deckkraft |
profile(title, values, options) | Histogramm nach Preis über die sichtbaren Kerzen, wie das Volumenprofil, siehe Profile |
histogram(title, rows, options) | Fertiges Preisprofil, als Profil gezeichnet, siehe Histogramme |
| Parameter | Datentyp | Beschreibung |
|---|---|---|
values, upperValues, lowerValues | (number \| null)[] | Ein Wert pro Kerze, null blendet die Kerze aus |
fill, stroke | string (hex) oder function | Farbe oder eine Funktion, die die Farbe der Kerze zurückgibt: (value, i) für lineSeries, barSeries, candleSeries, areaSeries, (upper, lower, i) für bandSeries |
opacity | number (0–1) oder function | Deckkraft der Füllung, eine Funktion (value, i) / (upper, lower, i) für areaSeries / bandSeries |
base | number | null | Basisniveau von areaSeries, null = der untere Rand des Charts |
step | boolean | Stil von bandSeries: true = eine Spalte pro Kerze (Standard), false = ein durchgehendes Polygon |
Beispiel:
plot.lineSeries("EMA", ta.ema(closes, 20), "#00C8FF");// every candle keeps its own color: green where the close is above the EMA, red below itplot.lineSeries("Close", closes, (v, i) => v > ema[i] ? colorBuy : colorSell);plot.barSeries("Premium %", premiumPercents, ((d) => d > 0 ? color1 : color2), 1);plot.candleSeries("Candles",opens, closes, highs, lows,(v, i) => {return opens?.[i] > closes?.[i] ? colorBuy : colorSell});plot.areaSeries("Volume", volumes, "#00C8FF", 0.3);// area colored per candle, more opaque on the bigger candlesplot.areaSeries("Delta", deltas, (v, i) => v >= 0 ? colorBuy : colorSell, (v, i) => Math.min(1, Math.abs(v) / maxDelta));// pressure band between the close and 1% below it, colored and shaded by the imbalance ratio of the candleplot.bandSeries("Bid pressure",closes,closes.map(c => c * 0.99),(upper, lower, i) => ratios[i] > 0 ? bidColor : askColor,(upper, lower, i) => Math.abs(ratios[i]) / 100);
Es können nicht mehr als 20 Datenreihen im Chart angezeigt werden.
Profile
plot.profile(title, values, options?) zeichnet ein Histogramm nach Preis, wie das eingebaute Volumenprofil. values[i] ist die Preiskarte der Kerze i von ohlcv(): { price: value } oder { price: { buy, sell } } (das data eines clusters()- oder liquidations()-Datensatzes kann unverändert übergeben werden) oder null für eine Kerze ohne Zeilen. Die Zeilen der im Chart sichtbaren Kerzen werden direkt im Chart je Preisschritt summiert, daher folgt das Profil dem Verschieben, Zoomen und Nachladen ohne neuen Lauf. Einzelwerte werden in einer Farbe gezeichnet (ein negativer Wert geht an fill2), Paare teilen den Balken in Kauf- und Verkaufsteil und erlauben den Delta-Modus. profile() gibt keinen Alert-Handle zurück.
| Option | Standard | Beschreibung |
|---|---|---|
step | Tickgröße des Charts | Zeilenhöhe in Preiseinheiten, Preise werden darauf abgerundet |
orient | "right" | Kante, von der die Balken wachsen, "right" oder "left" |
width | 30 | breiteste Zeile in Prozent der Chartbreite |
fill | "#26a69a" | Farbe des Werts bzw. des Kaufteils |
fill2 | "#ef5350" | Farbe des Verkaufsteils |
opacity | 0.6 | Deckkraft der Balken |
delta | false | buy - sell statt der Summe zeichnen, nur bei Paaren |
poc | true | Linie an der Zeile mit dem größten Wert |
valueArea | 70 | Prozent der Summe um den POC mit voller Deckkraft, false zum Abschalten |
valueAreaOpacity | 0.3 | Deckkraft der Zeilen außerhalb der Value Area |
labels | true | Zeilensummen auf den Balken, wenn Platz ist |
Grenzen: 3 Profile pro Lauf, 300 Zeilen pro Kerze, 200000 Zeilen pro Profil. Zeilen darüber hinaus werden verworfen, der Lauf schreibt eine Notiz ins Log.
Beispiel:
// volume profile of the chart, the same picture as the built-in toolconst rows = await source.clusters();plot.profile("Volume", rows.map((r) => r?.data ?? null), { step: 5 });// only large trades, delta coloured, on the left edgeconst minSize = input.number("Min trade size", 50, 0, 100000, 1);const big = rows.map((r) => {if (!r?.data) return null;const out = {};for (const [price, c] of Object.entries(r.data)) {if (price === "totalBuy" || price === "totalSell") continue;if (c.buy + c.sell >= minSize) out[price] = { buy: c.buy, sell: c.sell };}return out;});plot.profile("Large trades", big, { step: 5, delta: true, orient: "left" });
Histogramme
plot.histogram(title, rows, options?) zeichnet ein Histogramm, das Sie bereits gebaut haben: eine Preiskarte für den ganzen Chart, aus den Kerzen wird nichts summiert. Verwenden Sie profile(), wenn die Zeilen zu Kerzen gehören und das Bild dem sichtbaren Bereich folgen soll, und histogram(), wenn die Zeilen fertig sind: ein Orderbuch-Schnappschuss, vom Skript berechnete Level, Daten eines anderen Marktes.
rows hat dieselben Formate wie ein Kerzeneintrag von profile(): { price: value }, { price: { buy, sell } } oder eine Liste von [price, buy, sell]-Zeilen. Ein Einzelwert wird in einer Farbe gezeichnet (ein negativer Wert geht an fill2), Paare teilen den Balken in Kauf- und Verkaufsteil und erlauben den Delta-Modus. Die Optionen sind dieselben wie bei profile(), siehe die Tabelle oben.
Das Histogramm trägt das Datum der letzten Kerze des Laufs, und der Chart behält das neueste Histogramm eines Titels, sodass Teilberechnungen über ältere Kerzen (Nachladen der Historie) das aktuelle nie ersetzen. Es wird bei jeder Aktualisierung des Skripts neu gezeichnet, ein Orderbuch-Histogramm folgt also dem Live-Buch. histogram() gibt keinen Alert-Handle zurück.
Grenzen: 3 Histogramme pro Lauf, 5000 Zeilen. Zeilen darüber hinaus werden verworfen, der Lauf schreibt eine Notiz ins Log.
Beispiel, das Orderbuch:
// the live order book as a profile on the right edge: bids in the buy colour, asks in the sell colourconst book = await source.orderbook();const rows = {};for (const [price, qty] of Object.entries(book.bids)) rows[price] = { buy: qty };for (const [price, qty] of Object.entries(book.asks)) rows[price] = { sell: qty };plot.histogram("Order book", rows, { step: 5, width: 25, valueArea: false, poc: false });
Beispiel, eigene Level:
// your own histogram: cluster volume of the last 100 candles, recent candles weigh moreconst rows = await source.clusters();const weighted = {};rows.slice(-100).forEach((r, i, arr) => {if (!r?.data) return;const weight = (i + 1) / arr.length;for (const [price, c] of Object.entries(r.data)) {if (price === "totalBuy" || price === "totalSell") continue;const cell = weighted[price] || (weighted[price] = { buy: 0, sell: 0 });cell.buy += (c.buy || 0) * weight;cell.sell += (c.sell || 0) * weight;}});plot.histogram("Weighted volume", weighted, { step: 5, orient: "left", delta: true });
Tabellen
plot.table(title, rows, options?) zeichnet ein kleines Raster in einer Ecke des Chartbereichs: Zusammenfassungen über mehrere Zeitrahmen, Statistiken des geladenen Bereichs, Signalübersichten. rows ist ein Array von Zeilen, eine Zeile ein Array von Zellen, eine Zelle ein String, eine Zahl oder { text, color, bg, align, bold }. Tabellen derselben Ecke werden der Reihe nach gestapelt. Eine Tabelle trägt das Datum der letzten Kerze des Laufs, und der Chart behält die neueste Tabelle eines Titels, sodass Teilberechnungen über ältere Kerzen (Nachladen der Historie) die aktuelle nie ersetzen. Auf Charts schmaler als 500 px werden Tabellen nicht gezeichnet. Eine Tabelle kann mit der Maus verschoben werden: Die Position wird mit dem Workspace gespeichert und zurückgesetzt, wenn das Skript seine Option position ändert. table() gibt keinen Alert-Handle zurück.
| Option | Standard | Beschreibung |
|---|---|---|
position | "top-right" | Ecke des Bereichs: "top-right", "top-left", "bottom-right", "bottom-left" |
header | false | erste Zeile fett auf einem schattierten Band |
fontSize | 11 | Textgröße in px, 8 bis 16 |
color, bg, border | nach Thema | Standardfarben für Text, Fläche und Raster; eine Zelle kann color und bg überschreiben |
opacity | 0.85 | Deckkraft der Fläche |
align | "left" | Standardausrichtung der Zellen, "left", "center" oder "right" |
Grenzen: 3 Tabellen pro Lauf, 50 Zeilen, 10 Spalten, 100 Zeichen pro Zelle. Längerer Text wird mit Auslassungspunkten gekürzt, eine Spalte nimmt höchstens 30 % der Chartbreite ein.
Beispiel:
const candles = await source.ohlcv();const closes = candles.map((c) => c.close);const rsi = ta.rsi(closes, 14);const last = rsi.at(-1);const change = (closes.at(-1) / closes.at(-25) - 1) * 100;plot.table("Stats", [["Metric", "Value"],["RSI 14", { text: last.toFixed(1), color: last > 70 ? "#ef5350" : last < 30 ? "#26a69a" : undefined }],["Change 24 bars", { text: change.toFixed(2) + "%", align: "right" }],["Volume", candles.slice(-24).reduce((s, c) => s + c.volume, 0).toFixed(0)],], { position: "top-right", header: true });
Technische Analyse
Die Klasse ta implementiert die ta.*-Funktionen von PineScript v5. Pine rechnet Bar für Bar, hier nimmt jede Funktion ganze Serien (Arrays, ein Wert pro Kerze, älteste zuerst) und gibt eine Serie gleicher Länge zurück, die direkt geplottet werden kann. Solange die Periode nicht gefüllt ist, sind die Werte null, und null-Eingaben werden wie Pines na weitergereicht. Pine-Funktionen, die implizit high, low, close oder volume lesen, erhalten diese Arrays als Argumente.
Funktionen mit mehreren Serien (bb, kc, macd, stoch, dmi, supertrend) geben ein Array mit benannten Eigenschaften zurück: const [middle, upper, lower] = ta.bb(closes, 20, 2) oder ta.bb(closes, 20, 2).upper.
Hilfsfunktionen
| Methode | Beschreibung |
|---|---|
highest(values, length) / lowest(values, length) | Höchster / niedrigster Wert über length Bars |
highestbars(values, length) / lowestbars(values, length) | Versatz (0 oder negativ) zum höchsten / niedrigsten Bar |
max(values) / min(values) | Allzeithoch / -tief bis zu jedem Bar |
range(values, length) | highest - lowest |
change(values, length = 1) | values[i] - values[i - length] |
mom(values, length) | Momentum |
roc(values, length) | Änderungsrate, % |
cum(values) | Laufende Summe |
rising(values, length) / falling(values, length) | Wert liegt über / unter jedem der vorherigen length Werte |
variance(values, length, biased = true) | Varianz |
stdev(values, length, biased = true) | Standardabweichung |
dev(values, length) | Mittlere absolute Abweichung |
median(values, length) | Median |
mode(values, length) | Häufigster Wert (bei Gleichstand der kleinste) |
percentile_linear_interpolation(values, length, percentage) | Perzentil, lineare Interpolation zwischen den zwei nächsten Rängen |
percentile_nearest_rank(values, length, percentage) | Perzentil, Nearest-Rank-Methode |
percentrank(values, length) | % der vorherigen length Werte, die kleiner oder gleich dem aktuellen sind |
correlation(series1, series2, length) | Korrelationskoeffizient |
crossover(series1, series2) | Kreuzung nach oben (jedes Argument kann eine Zahl sein: ta.crossover(rsi, 70)) |
crossunder(series1, series2) | Kreuzung nach unten |
cross(series1, series2) | Beliebige Kreuzung |
barssince(condition) | Bars seit condition (Array von Booleans) zuletzt true war |
valuewhen(condition, values, occurrence = 0) | Wert von values, als condition vor occurrence Vorkommen true war |
pivothigh(values, leftbars, rightbars) / pivotlow(...) | Pivot-Wert auf dem bestätigenden Bar, der Pivot selbst liegt rightbars Bars früher |
Gleitende Durchschnitte
| Methode | Beschreibung |
|---|---|
sma(values, length) | Einfacher gleitender Durchschnitt |
ema(values, length) | Exponentieller gleitender Durchschnitt |
rma(values, length) | Wilders gleitender Durchschnitt (für RSI / ATR) |
wma(values, length) | Gewichteter gleitender Durchschnitt |
hma(values, length) | Hull Moving Average |
alma(values, length, offset = 0.85, sigma = 6, floor = false) | Arnaud Legoux Moving Average |
swma(values) | Symmetrisch gewichteter gleitender Durchschnitt über 4 Bars |
vwma(values, volumes, length) | Volumengewichteter gleitender Durchschnitt |
linreg(values, length, offset = 0) | Lineare Regressionskurve |
Oszillatoren
| Methode | Beschreibung |
|---|---|
rsi(values, length) | Relative Strength Index |
cmo(values, length) | Chande Momentum Oscillator |
cog(values, length) | Center of Gravity |
mfi(values, volumes, length) | Money Flow Index (values meist hlc3) |
tsi(values, shortLength, longLength) | True Strength Index, -1..1 |
wpr(highs, lows, closes, length) | Williams %R |
stoch(highs, lows, closes, period = 14, smoothK = 3, smoothD = 3) | Stochastik [k, d], smoothK = 1 liefert den Rohwert |
cci(values, length) / cci(highs, lows, closes, length) | Commodity Channel Index (in der zweiten Form vom typischen Preis) |
macd(values, fast = 12, slow = 26, signalPeriod = 9) | MACD [macd, signal, histogram] |
Bänder und Kanäle
| Methode | Beschreibung |
|---|---|
bb(values, length = 20, mult = 2) / bollinger(...) | Bollinger-Bänder [middle, upper, lower] |
bbw(values, length, mult) | Breite der Bollinger-Bänder (upper - lower) / middle |
kc(values, highs, lows, closes, length = 20, mult = 2, useTrueRange = true) | Keltner-Kanäle [middle, upper, lower] |
kcw(values, highs, lows, closes, length, mult, useTrueRange) | Breite der Keltner-Kanäle (upper - lower) / middle |
Trend und Volatilität
| Methode | Beschreibung |
|---|---|
tr(highs, lows, closes, handleNa = false) | True Range (handleNa = true liefert high - low auf dem ersten Bar) |
atr(highs, lows, closes, period = 14) | Average True Range |
dmi(highs, lows, closes, diLength = 14, adxSmoothing = 14) | Directional Movement [plus, minus, adx] |
adx(highs, lows, closes, period = 14) | Average Directional Index |
sar(highs, lows, closes, start = 0.02, inc = 0.02, max = 0.2) | Parabolic SAR |
supertrend(highs, lows, closes, factor = 3, atrPeriod = 10) | Supertrend [supertrend, direction], direction -1 = Aufwärtstrend, 1 = Abwärtstrend |
Volumen
| Methode | Beschreibung |
|---|---|
vwap(values, volumes, anchor?) / vwap(highs, lows, closes, volumes, anchor?) | Kumulativer VWAP (in der zweiten Form vom typischen Preis), startet neu auf Bars, auf denen anchor (Array von Booleans) true ist |
obv(closes, volumes) | On Balance Volume |
accdist(highs, lows, closes, volumes) | Accumulation / Distribution |
iii(highs, lows, closes, volumes) | Intraday Intensity Index |
pvt(closes, volumes) | Price-Volume Trend |
nvi(closes, volumes) / pvi(closes, volumes) | Negative / Positive Volume Index |
wad(highs, lows, closes) | Williams Accumulation / Distribution |
wvad(opens, highs, lows, closes, volumes) | Williams Variable Accumulation / Distribution |
Orderbuch-Hilfsfunktionen
orderbook ist source.orderbook() oder source.depths()[i].data: {asks: {price: quantity}, bids: {price: quantity}}.
| Methode | Beschreibung |
|---|---|
orderbookSide(orderbook, side) | Eine Seite ('bids' / 'asks') als [[price, quantity], ...], sortiert mit dem besten Preis zuerst |
bestBid(orderbook) / bestAsk(orderbook) | Bester Bid- / Ask-Preis (null bei leerem Buch) |
sumBids(orderbook, percent, price, inQuote) | Kumulierte Bid-Menge zwischen price (Standard: bester Bid) und price * (1 - percent / 100), inQuote = true summiert price * quantity |
sumAsks(orderbook, percent, price, inQuote) | Kumulierte Ask-Menge zwischen price (Standard: bester Ask) und price * (1 + percent / 100) |
Beispiel:
const candles = source.ohlcv();const closes = candles.map(c => c.close);const rsiValues = ta.rsi(closes, 14);plot.lineSeries("RSI", rsiValues, "#00C8FF");
Alarme
Nutzer können Alarme für Ihren Indikator über den Reiter Alarme in den Indikator-Einstellungen im Chart erstellen. Alarme werden serverseitig regelmäßig geprüft, Benachrichtigungen werden an Telegram und/oder eine vom Nutzer definierte Webhook-URL gesendet.
Alarm-Einstellungen — alertInput
Die Klasse alertInput deklariert Alarm-Einstellungen. Sie funktioniert genau wie input, aber die Felder werden im Reiter Alarme angezeigt, und jeder Nutzer konfiguriert seine eigenen Werte für seinen Alarm.
Skripte ohne alertInput-Deklarationen können nicht für Alarme verwendet werden.
| Methode | Beschreibung |
|---|---|
number(title, defaultValue, min, max, step) | Numerische Eingabe |
range(title, defaultValue, min, max, step) | Bereichseingabe |
select(title, defaultValue, values) | Dropdown-Auswahl |
color(title, defaultValue) | Farbauswahl |
boolean(title, defaultValue) | Boolesche Eingabe |
Beispiel:
const alertVolumeThreshold = alertInput.number("Volume threshold", 1000, 0, 10000000, 1);
Alarme auslösen — alert.fire
| Methode | Beschreibung |
|---|---|
fire(title, message, date) | Löst eine Alarm-Benachrichtigung aus |
- Alle drei Parameter sind erforderlich — ein Aufruf mit fehlendem
title,messageoderdatewird übersprungen (eine Warnung wird ins Log geschrieben). - Halten Sie den Titel des Alarms konstant, generieren Sie keine eindeutigen Titel. Jeder Titel löst nur einmal pro Durchlauf aus (maximal 20 Titel pro Durchlauf).
dateist diestartTimeder Kerze, zu der das Signal gehört: Derselbe Titel benachrichtigt erst wieder, wenn er für eine neuere Kerze auslöst. Signale von Kerzen, die älter sind als der Zeitpunkt, zu dem der Nutzer seinen Alarm erstellt hat, werden ignoriert (Anti-Spam).
Beispiel:
if (candle.volume > alertVolumeThreshold) {alert.fire("High volume", `vol ${candle.volume}`, candle.startTime);}
Form-Alarme — plot.<shape>(...).alert(...)
Alle geometrischen Formen (box, square, diamond, triangle, circle, straightLine, horizontalLine, verticalLine, text) geben ein Handle mit einer Methode .alert() zurück.
Der Alarm wird automatisch mit dem date1 der Form ausgelöst, sodass die Anti-Spam-Logik ohne zusätzlichen Code funktioniert — dies ist der empfohlene Weg für Signale pro Kerze.
Zwei Formen:
// simple: fires every time the shape is plotted for a new dateplot.box(...).alert("High volume", `vol ${candle.volume}`);// callback: return nothing to skip, [title, message] to fireplot.box(...).alert((shape) => {if (candle.volume > alertVolumeThreshold)alert.fire("High volume", `vol ${candle.volume}`, candle.startTime);});
Der Callback erhält die Daten der Form (date1, price1, date2, price2, ...).
Reihen-Alarme — plot.<series>(...).alert(...)
Datenreihen (lineSeries, barSeries, candleSeries, areaSeries, bandSeries) geben ebenfalls das Handle .alert() zurück.
Der Callback erhält den letzten Datenpunkt und das Datum der letzten Kerze; der Alarm wird mit diesem Datum ausgelöst.
lineSeries/barSeries/areaSeries— der Callback erhält den letzten Wert (eine Zahl)candleSeries— der Callback erhält die letzte Kerze{open, close, high, low}bandSeries— der Callback erhält das letzte{upper, lower}
plot.lineSeries("RSI", rsiValues, color).alert((rsi, date) => {if (rsi > alertRsiThreshold) alert.fire("RSI high", `RSI ${rsi.toFixed(1)}`, latestCandle.startTime);});
Eine Bedingung, die wahr bleibt, benachrichtigt einmal pro neuer Kerze. Für einmalige Signale verwenden Sie Kreuzungsbedingungen (ta.crossover).
Vollständiges Beispiel
const title = "Volume alerts";const description = "Shows boxes on high-volume candles";define(title, description, "onchart", true, true);const color = style.color("Color", "#00C8FF");const volumeThreshold = input.number("Volume threshold", 1000, 0, 10000000, 1);const alertVolumeThreshold = alertInput.number("Alert volume threshold", 5000, 0, 10000000, 1);const alertCloseThreshold = alertInput.number("Alert close threshold", 100000, 0, 10000000, 1);const candles = source.ohlcv();const latestCandle = candles.at(-1);const closes = candles.map(c => c.close);// series alert: checks the latest closeplot.lineSeries("Closes", closes, color).alert((close, date) => {if (close > alertCloseThreshold) alert.fire("Close above threshold", `close ${close}`, latestCandle.startTime);});for (let i = 0; i < candles.length; i++) {const candle = candles[i];if (candle.volume > volumeThreshold) {const right = i < candles.length - 1 ? candles[i + 1].startTime : candle.startTime + 60000;// shape alert: checked against the user's alert thresholdplot.box(`Volume ${i}`, candle.startTime, candle.high, right, candle.low, "#00C8FF", 0.3).alert(() => {if (candle.volume > alertVolumeThreshold)alert.fire("High volume", `vol ${candle.volume}`, candle.startTime);});}}
Beispiele für Indikatoren
const title = "Your first indicator";const description = "Shows closes lines on the chart";const indicatorType = "onchart";define(title, description, indicatorType, true, true);const color = input.color("Line Color", "#00C8FF");const candles = source.ohlcv();const closes = candles.map(c => c.close);plot.lineSeries("Closes", closes, color);
define("Example Indicator", "Demonstrates basic plotting", "onchart", true, true);const color = input.color("Line Color", "#00FF99");const candles = source.ohlcv();const closes = candles.map(c => c.close);const ema20 = ta.ema(closes, 20);plot.lineSeries("EMA 20", ema20, color);
const title = "Colored Volume Bars";const description = "Volumes colored based on candle direction.";const indicatorType = "onchart";const isPublic = true;const isPublicCode = true;define(title, description, indicatorType, isPublic, isPublicCode);// Get OHLC candlesconst candles = source.ohlcv();const colorUp = style.color("Color 1", "#2BB462"); // Greenish color for rising pricesconst colorDown = style.color("Color 2", "#FB4C51"); // Reddish color for falling pricesconst volumes = candles.map(candle => candle.volume);plot.barSeries("Volume", volumes, (value, index) => {const currentCandle = candles[index];return currentCandle.close >= currentCandle.open ? colorUp : colorDown;});
define('Coinbase Premium Index', 'Coinbase Premium Index', 'offchart', true, false);const color1 = style.color("Color 1", "#2BB462");const color2 = style.color("Color 2", "#FB4C51");// Get OHLC candlesconst cbCandles = source.ohlcv('coinbase', 'BTC-USD');const binanceCandles = source.ohlcv('binance', 'BTCUSDT');// Extract closesconst cbCloses = cbCandles.map(c => c.close);const binanceCloses = binanceCandles.map(c => c.close);// --- Coinbase Premium Index calculation ---const premiumPercent = cbCloses.map((cb, i) => {const b = binanceCloses[i];return ((cb - b) / b) * 100;});// Plot premium indexplot.barSeries("Premium %", premiumPercent, ((d) => d > 0 ? color1 : color2)) // Percentage difference
const title = "Candles";const description = "Simple candles draw";const indicatorType = "offchart";const isPublic = true;const isPublicCode = true;define(title, description, indicatorType, isPublic, isPublicCode);const colorBuy = style.color("Buy Color", "#2BB462");const colorSell = style.color("Sell Color", "#7a3d3f");const candles = source.ohlcv();const closes = candles.map(c => c.close);const opens = candles.map(c => c.open);const highs = candles.map(c => c.high);const lows = candles.map(c => c.low);plot.candleSeries("Candles",opens, closes, highs, lows,(v, i) => {return opens?.[i] > closes?.[i] ? colorBuy : colorSell});
Order Book Pressure (Bänder des Bid-/Ask-Tiefen-Ungleichgewichts um den Preis, wie die "Order Book Pressure"-Overlays von Trading-Terminals):
const title = "Order Book Pressure";const description = "Bid / ask depth imbalance bands around the price";define(title, description, "onchart", true, true);const minBidRatio = input.number("Min bid ratio %", 20, 0, 100, 1);const minAskRatio = input.number("Min ask ratio %", 20, 0, 100, 1);const depthFrom = input.select("Depth from", "0%", ["0%", "1%", "2.5%", "5%"]);const depthTo = input.select("Depth to", "5%", ["1%", "2.5%", "5%", "10%"]);const bidColor = style.color("Bid fill color", "#a5d6a7");const askColor = style.color("Ask fill color", "#f1627e");const extremeColor = style.color("Extreme color", "#ffff00");const highlight = style.boolean("Highlight extremes", true);const showRef = style.boolean("Show reference lines", true);const alertMinRatio = alertInput.number("Alert min |ratio| %", 40, 0, 100, 1);const bounds = [1, 2.5, 5, 10]; // depth bands in % from the closeconst from = parseFloat(depthFrom);const to = parseFloat(depthTo);const candles = source.ohlcv();const depths = source.depths();const liveBook = source.orderbook();// orderbook of every candle: depths()[i] is the snapshot of candles[i] (the biggest limit orders), the latest candle uses the live bookconst lastIndex = candles.length - 1;const books = candles.map((c, i) => {if (!c) return null;if (i === lastIndex && ta.bestBid(liveBook)) return liveBook;return depths[i]?.data || null;});const closes = candles.map(c => c ? c.close : null);// imbalance ratio of every band in % (positive = bids dominate, negative = asks dominate)const ratios = bounds.map((upper, b) => {const lower = b === 0 ? 0 : bounds[b - 1];return candles.map((c, i) => {const book = books[i];if (!c || !book) return null;const bid = ta.sumBids(book, upper, c.close) - ta.sumBids(book, lower, c.close);const ask = ta.sumAsks(book, upper, c.close) - ta.sumAsks(book, lower, c.close);const total = bid + ask;return total > 0 ? ((bid - ask) / total) * 100 : 0;});});// a stronger ratio -> a more opaque band, extreme ratios get the extreme colorconst bandOpacity = (ratio) => Math.min(0.95, Math.abs(ratio || 0) / 70 * 0.95);const bandColor = (ratio) => highlight && Math.abs(ratio || 0) >= 63 ? extremeColor : (ratio >= 0 ? bidColor : askColor);bounds.forEach((upper, b) => {const lower = b === 0 ? 0 : bounds[b - 1];if (from > lower || upper > to) return; // the band is outside of the selected depth rangeconst bidUpper = closes.map(c => c != null ? c * (1 - lower / 100) : null);const bidLower = closes.map(c => c != null ? c * (1 - upper / 100) : null);const askLower = closes.map(c => c != null ? c * (1 + lower / 100) : null);const askUpper = closes.map(c => c != null ? c * (1 + upper / 100) : null);if (showRef) {plot.lineSeries(`Bid ${upper}%`, bidLower, bidColor, 0.4);plot.lineSeries(`Ask ${upper}%`, askUpper, askColor, 0.4);}// bid pressure: the band is filled only on the candles where bids dominate by more than the min ratioplot.bandSeries(`Bid pressure ${lower}-${upper}%`,bidUpper.map((v, i) => ratios[b][i] >= minBidRatio ? v : null),bidLower,(upper, lower, i) => bandColor(ratios[b][i]),(upper, lower, i) => bandOpacity(ratios[b][i]));// ask pressureplot.bandSeries(`Ask pressure ${lower}-${upper}%`,askUpper.map((v, i) => ratios[b][i] <= -minAskRatio ? v : null),askLower,(upper, lower, i) => bandColor(ratios[b][i]),(upper, lower, i) => bandOpacity(ratios[b][i]));// alert on the latest candleconst latestRatio = ratios[b][lastIndex];if (latestRatio != null && Math.abs(latestRatio) >= alertMinRatio) {alert.fire(`Pressure ${lower}-${upper}%`, `ratio ${latestRatio.toFixed(1)}%`, candles[lastIndex].startTime);}});