MobChart
EN / DE / FR / RU / UA
What do we do?
Workspaces
Chart
Screeners
API for Custom Indicators
Scripting๐Ÿค– AI for generation of Custom Indicators๐Ÿ”’ Security Principles and Current Limitationsโš’๏ธ Indicator Development๐Ÿงญ Creating an Indicatorโš™๏ธ Input Parameters๐Ÿงฎ Data Sources๐ŸŒ Global Variables๐Ÿงพ Logging๐Ÿ“ˆ Visualization๐Ÿ“Š Technical Analysis๐Ÿ”” Alerts๐Ÿง  Examples of indicators

๐Ÿ“˜ API for Custom Indicators

๐Ÿค– AI for generation of Custom Indicators

Use the following document with ChatGPT, Claude, Gemini or other LLMs:

๐Ÿ‘‰ AI.txt

Example Prompt:

Read the attached [AI.txt] https://mobchart.com/docs/public/AI.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.

๐Ÿ”’ Security Principles and Current Limitations

  • The script execution environment is based on a secure JavaScript sandbox.
  • Your script has an execution time limit of 1500ms.
  • The length of the returned data array must not exceed 1500. Therefore, the indicator cannot extend deeper than 1500 candles.
  • You can make up to 8 data source requests within a single indicator.

โš’๏ธ Indicator Development

Indicator development is done within a separate widget, which can be placed next to the chart where the indicator will be enabled.


๐Ÿงญ Creating an Indicator

define(title, description, type, isPublic, isPublicCode)

Registers the indicator in the system.

ParameterTypeDescription
titlestringIndicator name
descriptionstringShort description
type'onchart' | 'offchart'Indicator placement (on chart or in a separate panel)
isPublictrue | falseWhether the indicator is public
isPublicCodetrue | falseWhether the source code is public

Example:

define("My Indicator", "Shows EMA line", "onchart", true, false);

Indicator placement for onchart and offchart:

โš™๏ธ Input Parameters

The input and style classes allow you to create user-configurable settings (similar to input() in PineScript).

MethodDescription
number(title, defaultValue, min, max, step)Numeric input
range(title, defaultValue, min, max, step)Range input
select(title, defaultValue, values)Dropdown selection
color(title, defaultValue)Color picker
boolean(title, defaultValue)Boolean input

Example (input) for main parameters:

const length = input.number("Period", 14, 1, 100, 1);
const type = input.select("Type", 'sma', ['sma', 'ema']);
const color = input.color("Line Color", "#FF0000");

Or (style) for visual parameters:

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");

๐Ÿงฎ Data Sources

The source class is used to fetch market data from the server side.

We do not always guarantee that the returned data exactly matches the candles visible on the chart.

For example, during script execution, ohlcv() might return 400 candles, while depths() might return only 300 limit orders (for the heatmap).
Use additional checks for the startTime parameter in your code.

Currently, the length of the returned data array must not exceed 1500.
Therefore, the indicator cannot extend deeper than 1500 candles.

Currently, 8 data source requests are allowed within a single indicator.

The exchange, ticker, and timeframe parameters are by default tied to the current chart where the indicator is opened.
The lookback parameter (history of bars from the current date, but no more than 1500) can be set at your discretion.

"ohlcv" MethodDescription
ohlcv(exchange, ticker, timeframe, lookback?)Candle data
[
{
"open": 115886.17,
"high": 115911.96,
"low": 115764.27,
"close": 115815.16,
"volume": 228.41528,
"startTime": 1757699100000
},
{...},
...
]
"depths" MethodDescription
depths(exchange, ticker, timeframe, lookback?)Limit orders
[
{
"data": {
"asks": {
"109425": 5.467,
"109495": 3.564,
...
},
"bids": {
"33000": 3.746,
"35000": 16.49,
...
}
},
"startTime": 1757699100000
},
{...},
...
]
"clusters" MethodDescription
clusters(exchange, ticker, timeframe, lookback?)Cluster data
[
{
"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" MethodDescription
liquidations(exchange, ticker, timeframe, lookback?)Liquidations data
[
{
"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" MethodDescription
moreData(exchange, ticker, timeframe, lookback?)Some additional data
[
{
"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
},
{...},
...
]

Example:

const candles = source.ohlcv(); // The exchange, ticker, and timeframe parameters default to the current chart.
const closes = candles.map(c => c.close);

๐ŸŒ Global Variables

VariableDescriptionExample
currentExchangeCurrent exchange on the chartbinance, binanceFutures, coinbase, ...
currentTickerCurrent ticker on the chart (format depends on the exchange)BTCUSDT, ADA-USD, BTC/USDC, ...
currentTimeframeCurrent timeframe on the chart1m, 5m, 15m, ...

Example:

const candles = source.ohlcv(currentExchange, currentTicker, currentTimeframe);
const closes = candles.map(c => c.close);

๐Ÿงพ Logging

The logger class is used for outputting information and debugging.

MethodDescription
log(...args)Adds an entry to the log (with limits on length and quantity)

Example:

const candles = source.ohlcv();
logger.log("First candle:", candles?.[0]);

๐Ÿ“ˆ Visualization

The plot class is responsible for drawing graphical elements and data series.

๐Ÿ”น Geometric Shapes

MethodDescription
box(title, date1, price1, date2, price2, fill, opacity, stroke, strokeOpacity, strokeWidth)Rectangle
square(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth)Square
diamond(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth)Diamond
triangle(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth)Triangle
circle(title, date1, price1, radius = 10, fill, opacity, stroke, strokeOpacity, strokeWidth)Circle
text(title, value, date1, price1, fontSize, fill, opacity)Text on chart
straightLine(title, date1, price1, date2, price2, stroke, strokeOpacity, strokeWidth, strokeDasharray)Straight line
ParameterData TypeDescription
titlestringElement name (unique identifier on the chart)
date1number (timestamp, ms)Starting timestamp
price1numberStarting price (Y-axis coordinate)
date2number (timestamp, ms)Ending timestamp (for lines and rectangles)
price2numberEnding price (for lines and rectangles)
sizenumberShape size (in pixels)
radiusnumberCircle radius (default 10)
fillstring (hex, e.g., "#FF0000")Fill color of the shape
opacitynumber (0โ€“1)Fill opacity
strokestring (hex, e.g., "#000000")Stroke color
strokeOpacitynumber (0โ€“1)Stroke opacity
strokeWidthnumberStroke width (in pixels)
strokeDasharrayarray (e.g., [5,5])Dashed line style
fontSizenumberText size (in pixels)
valuestringText value (for text method)

Example:

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);

No more than 3000 shapes can be displayed on the chart.


๐Ÿ”น Data Series

MethodDescription
lineSeries(title, values, stroke, strokeOpacity, strokeWidth, strokeDasharray)Line chart
barSeries(title, values, fill, opacity)Bar chart
candleSeries(title, openValues, closeValues, highValues, lowValues, fill, opacity)Candles

Example:

plot.lineSeries("EMA", ta.ema(closes, 20), "#00C8FF");
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
}
);

No more than 10 data series can be displayed on the chart.


๐Ÿ“Š Technical Analysis

The ta class includes a set of functions similar to PineScript.

๐Ÿ”น Helper Functions

MethodDescription
highest(values, period)Highest value over a period
lowest(values, period)Lowest value over a period
variance(values, period)Variance
stdev(values, period)Standard deviation
crossover(series1, series2)Checks for upward crossover
crossunder(series1, series2)Checks for downward crossover
cross(series1, series2)Any crossover

๐Ÿ”น Moving Averages

MethodDescription
sma(values, period)Simple moving average
ema(values, period)Exponential moving average
wma(values, period)Weighted moving average

๐Ÿ”น Indicators

MethodDescription
rsi(values, period)Relative Strength Index
macd(values, fast, slow, signalPeriod)MACD
bollinger(values, period, mult)Bollinger Bands
stoch(highs, lows, closes, period, smoothK, smoothD)Stochastic Oscillator
atr(highs, lows, closes, period)Average True Range
adx(highs, lows, closes, period)Average Directional Index
cci(highs, lows, closes, period)Commodity Channel Index
vwap(highs, lows, closes, volumes)Volume Weighted Average Price

Example:

const candles = source.ohlcv();
const closes = candles.map(c => c.close);
const rsiValues = ta.rsi(closes, 14);
plot.lineSeries("RSI", rsiValues, "#00C8FF");

๐Ÿ”” Alerts

Users can create alerts for your indicator from the Alerts tab in the indicator settings on the chart. Alerts are checked on the server side every 30 seconds, notifications are sent to Telegram and/or a user defined Webhook URL.

โš™๏ธ Alert Settings โ€” alertInput

The alertInput class declares alert settings. It works exactly like input, but the fields are shown in the Alerts tab and every user configures their own values for their alert.

Scripts without alertInput declarations can not be used for alerts.

MethodDescription
number(title, defaultValue, min, max, step)Numeric input
range(title, defaultValue, min, max, step)Range input
select(title, defaultValue, values)Dropdown selection
color(title, defaultValue)Color picker
boolean(title, defaultValue)Boolean input

Example:

const alertVolumeThreshold = alertInput.number("Volume threshold", 1000, 0, 10000000, 1);

๐Ÿšจ Firing Alerts โ€” alert.fire

MethodDescription
fire(title, message, date)Fires an alert notification
  • All three parameters are required โ€” a call with a missing title, message or date is skipped (a warning is written to the log).
  • Keep the alert title constant, do not generate unique titles. Each title fires only once per run (max 20 titles per run).
  • date is the candle startTime the signal belongs to: the same title notifies again only when it fires for a newer candle. Signals from candles older than the moment the user created their alert are ignored (anti-spam).

Example:

if (candle.volume > alertVolumeThreshold) {
alert.fire("High volume", `vol ${candle.volume}`, candle.startTime);
}

๐Ÿ“ Shape Alerts โ€” plot.<shape>(...).alert(...)

All geometric shapes (box, square, diamond, triangle, circle, straightLine, text) return a handle with an .alert() method. The alert fires with the shape's date1 automatically, so the anti-spam logic works without extra code โ€” this is the recommended way for per-candle signals.

Two forms:

// simple: fires every time the shape is plotted for a new date
plot.box(...).alert("High volume", `vol ${candle.volume}`);
// callback: return nothing to skip, [title, message] to fire
plot.box(...).alert((shape) => {
if (candle.volume > alertVolumeThreshold)
alert.fire("High volume", `vol ${candle.volume}`, candle.startTime);
});

The callback receives the shape data (date1, price1, date2, price2, ...).

๐Ÿ“ˆ Series Alerts โ€” plot.<series>(...).alert(...)

Data series (lineSeries, barSeries, candleSeries) also return the .alert() handle. The callback receives the latest data point and the latest candle date, the alert fires with that date.

  • lineSeries / barSeries โ€” the callback receives the last value (a number)
  • candleSeries โ€” the callback receives the last candle {open, close, high, low}
plot.lineSeries("RSI", rsiValues, color).alert((rsi, date) => {
if (rsi > alertRsiThreshold) alert.fire("RSI high", `RSI ${rsi.toFixed(1)}`, latestCandle.startTime);
});

A condition that stays true notifies once per new candle. For one-time signals use crossover-style conditions (ta.crossover).

๐Ÿงฉ Full Example

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 close
plot.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 threshold
plot.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);
});
}
}

๐Ÿง  Examples of indicators

const title = "Your first indicator";
const description = "Shows closes lines on the chart";
const indicatorType = "onchart";
define(title, description, type, 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 candles
const candles = source.ohlcv();
const colorUp = style.color("Color 1", "#2BB462"); // Greenish color for rising prices
const colorDown = style.color("Color 2", "#FB4C51"); // Reddish color for falling prices
const 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 candles
const cbCandles = source.ohlcv('coinbase', 'BTC-USD');
const binanceCandles = source.ohlcv('binance', 'BTCUSDT');
// Extract closes
const 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 index
plot.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
}
);