(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{"TE/p":function(e,t,n){"use strict";n.r(t),n.d(t,"_frontmatter",(function(){return c})),n.d(t,"default",(function(){return p}));var a=n("Fcif"),r=n("dV/x"),l=(n("mXGw"),n("/FXl")),b=n("TjRS"),i=n("wCAC");n("l1C2");const o=["components"],c={};void 0!==c&&c&&c===Object(c)&&Object.isExtensible(c)&&!Object.prototype.hasOwnProperty.call(c,"__filemeta")&&Object.defineProperty(c,"__filemeta",{configurable:!0,value:{name:"_frontmatter",filename:"src/components/en/Scripting.mdx"}});const d={_frontmatter:c},s=b.a;function p(e){let{components:t}=e,n=Object(r.a)(e,o);return Object(l.b)(s,Object(a.a)({},d,n,{components:t,mdxType:"MDXLayout"}),Object(l.b)("h1",{id:"api-for-custom-indicators"},"API for custom indicators"),Object(l.b)(i.e,{mdxType:"Lead"},"Write your own indicators in JavaScript, run them in a secure sandbox next to the chart and share them with other users. The API gives you market data (candles, order books, clusters, liquidations, derivatives data), plotting primitives, technical analysis helpers and alerts."),Object(l.b)("h2",{id:"generate-indicators-with-ai"},"Generate indicators with AI"),Object(l.b)("p",null,"The whole API is also available as a single text file for language models: ",Object(l.b)("a",{parentName:"p",href:"../../public/AI-scripting.txt"},"AI-scripting.txt"),". Attach it to ChatGPT, Claude, Gemini or any other LLM and describe the indicator you want."),Object(l.b)("p",null,"Example prompt:"),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-text"},"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.\n\nCreate 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.\n\nUse only the documented API. Do not invent functions. Return the complete indicator code.\n")),Object(l.b)("h2",{id:"sandbox-and-limits"},"Sandbox and limits"),Object(l.b)(i.b,{type:"warning",title:"Current limits",mdxType:"Callout"},Object(l.b)("ul",null,Object(l.b)("li",{parentName:"ul"},"Scripts run in a secure JavaScript sandbox."),Object(l.b)("li",{parentName:"ul"},"A script has an execution time limit of ",Object(l.b)("inlineCode",{parentName:"li"},"1500ms"),"."),Object(l.b)("li",{parentName:"ul"},"The returned data arrays are capped at ",Object(l.b)("strong",{parentName:"li"},"5000")," elements, so an indicator cannot look deeper than ",Object(l.b)("strong",{parentName:"li"},"5000 candles"),"."),Object(l.b)("li",{parentName:"ul"},"Up to ",Object(l.b)("strong",{parentName:"li"},"8 data source requests"),' are allowed within a single indicator. A request over the limit throws an error and the run stops with the issue "too much sources".'),Object(l.b)("li",{parentName:"ul"},"Up to ",Object(l.b)("strong",{parentName:"li"},"3 HTTP requests")," (",Object(l.b)("inlineCode",{parentName:"li"},"http.get")," / ",Object(l.b)("inlineCode",{parentName:"li"},"http.post"),") are allowed within a single run, see ",Object(l.b)("strong",{parentName:"li"},"HTTP Requests"),"."))),Object(l.b)("h2",{id:"indicator-development"},"Indicator Development"),Object(l.b)("p",null,"Indicator development is done within a separate widget, which can be placed next to the chart where the indicator will be enabled."),Object(l.b)(i.f,{src:"../../public/chart_code.png",mdxType:"Screenshot"}),Object(l.b)("h2",{id:"creating-an-indicator"},"Creating an Indicator"),Object(l.b)("h3",{id:"definetitle-description-type-ispublic-ispubliccode-options"},Object(l.b)("inlineCode",{parentName:"h3"},"define(title, description, type, isPublic, isPublicCode, options)")),Object(l.b)("p",null,"Registers the indicator in the system."),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Parameter"),Object(l.b)("th",{parentName:"tr",align:null},"Type"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"title")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"string")),Object(l.b)("td",{parentName:"tr",align:null},"Indicator name")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"description")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"string")),Object(l.b)("td",{parentName:"tr",align:null},"Short description")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"type")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"'onchart'")," ","|"," ",Object(l.b)("inlineCode",{parentName:"td"},"'offchart'")),Object(l.b)("td",{parentName:"tr",align:null},"Indicator placement (on chart or in a separate panel)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"isPublic")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"true")," ","|"," ",Object(l.b)("inlineCode",{parentName:"td"},"false")),Object(l.b)("td",{parentName:"tr",align:null},"Whether the indicator is public")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"isPublicCode")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"true")," ","|"," ",Object(l.b)("inlineCode",{parentName:"td"},"false")),Object(l.b)("td",{parentName:"tr",align:null},"Whether the source code is public")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"options")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"object")," (optional)"),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"{ fullRange, warmup }"),", see below")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'define("My Indicator", "Shows EMA line", "onchart", true, false);\n')),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Calculation range.")," By default a script computes only the part of the chart that needs it: the bars loaded by scrolling back, or the latest bars on a refresh, plus ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"warmup"))," bars of history before them (default 500) that are needed by the calculations but not returned. Declare ",Object(l.b)("inlineCode",{parentName:"p"},"warmup")," when your indicator needs more or less history, e.g. ",Object(l.b)("inlineCode",{parentName:"p"},"{ warmup: 200 }")," for an EMA(200). A script whose values or shapes depend on the ",Object(l.b)("strong",{parentName:"p"},"whole visible range")," (VWAP from the first visible candle, cumulative delta, a label at the window maximum, levels drawn across the chart) must declare ",Object(l.b)("inlineCode",{parentName:"p"},"{ fullRange: true }"),", otherwise its results are wrong after loading more history. The mode is read from ",Object(l.b)("inlineCode",{parentName:"p"},"define()")," every time the script is saved: a script saved without ",Object(l.b)("inlineCode",{parentName:"p"},"fullRange")," is calculated partially, so add the flag to existing cumulative scripts when you edit them. Partial scripts are also loaded in pieces: when you switch one on over a long history the chart requests the newest 500 candles first (or ",Object(l.b)("inlineCode",{parentName:"p"},"warmup")," candles when that is more) and the older parts follow one request at a time, so the visible part appears at once. Scripts with ",Object(l.b)("inlineCode",{parentName:"p"},"fullRange")," are always computed in a single request over the whole range."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'define("Window max", "Label at the highest visible high", "onchart", true, true, { fullRange: true });\n')),Object(l.b)("p",null,"Indicator placement for ",Object(l.b)("strong",{parentName:"p"},"onchart")," and ",Object(l.b)("strong",{parentName:"p"},"offchart"),":"),Object(l.b)(i.f,{src:"../../public/onchart_offchart.png",mdxType:"Screenshot"}),Object(l.b)("h2",{id:"input-parameters"},"Input Parameters"),Object(l.b)("p",null,"The ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"input"))," and ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"style"))," classes allow you to create user-configurable settings (similar to ",Object(l.b)("inlineCode",{parentName:"p"},"input()")," in PineScript)."),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number(title, defaultValue, min, max, step)")),Object(l.b)("td",{parentName:"tr",align:null},"Numeric input")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"range(title, defaultValue, min, max, step)")),Object(l.b)("td",{parentName:"tr",align:null},"Range input")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"select(title, defaultValue, values)")),Object(l.b)("td",{parentName:"tr",align:null},"Dropdown selection")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"color(title, defaultValue)")),Object(l.b)("td",{parentName:"tr",align:null},"Color picker")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"boolean(title, defaultValue)")),Object(l.b)("td",{parentName:"tr",align:null},"Boolean input")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"tickers(title, defaultValue, exchange)")),Object(l.b)("td",{parentName:"tr",align:null},"Multi select of the tickers of an exchange (default: the chart exchange); returns the chosen tickers as an array")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example (input) for main parameters:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const length = input.number("Period", 14, 1, 100, 1);\nconst type = input.select("Type", \'sma\', [\'sma\', \'ema\']);\nconst color = input.color("Line Color", "#FF0000");\n// pairs to include / exclude, picked from the tickers of the chart exchange\nconst include = input.tickers("Include pairs", []);\nconst exclude = input.tickers("Exclude pairs", []);\n')),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Or (style) for visual parameters:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const length = style.number("Size", 14, 1, 100, 1);\nconst color = style.select("Background Color", \'#FF0000\', ["#FF0000", "#AA0000", "#BB0000"]);\nconst color = style.color("Background Color", "#FF0000");\n')),Object(l.b)(i.f,{src:"../../public/input_style.png",mdxType:"Screenshot"}),Object(l.b)("h2",{id:"data-sources"},"Data Sources"),Object(l.b)("p",null,"The ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"source"))," class fetches market data from the server. The candle-based methods take the same parameters; the live snapshots differ: ",Object(l.b)("inlineCode",{parentName:"p"},"orderbook()")," takes an exchange and a ticker, ",Object(l.b)("inlineCode",{parentName:"p"},"levels()")," and ",Object(l.b)("inlineCode",{parentName:"p"},"keyLevels()")," an exchange and a settings object."),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Parameter"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"exchange"),", ",Object(l.b)("inlineCode",{parentName:"td"},"ticker"),", ",Object(l.b)("inlineCode",{parentName:"td"},"timeframe")),Object(l.b)("td",{parentName:"tr",align:null},"Default to the chart the indicator is opened on. Pass other values to fetch data of another market.")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"lookback")),Object(l.b)("td",{parentName:"tr",align:null},"Number of bars of history from the current date, ",Object(l.b)("strong",{parentName:"td"},"no more than 5000"),".")))),Object(l.b)(i.b,{type:"note",title:"Data is aligned to the chart candles",mdxType:"Callout"},Object(l.b)("p",null,Object(l.b)("inlineCode",{parentName:"p"},"ohlcv()"),", ",Object(l.b)("inlineCode",{parentName:"p"},"depths()"),", ",Object(l.b)("inlineCode",{parentName:"p"},"clusters()"),", ",Object(l.b)("inlineCode",{parentName:"p"},"liquidations()")," and ",Object(l.b)("inlineCode",{parentName:"p"},"moreData()")," of the chart timeframe return ",Object(l.b)("strong",{parentName:"p"},"one entry per chart candle"),": ",Object(l.b)("inlineCode",{parentName:"p"},"depths()[i]")," is the order book of ",Object(l.b)("inlineCode",{parentName:"p"},"ohlcv()[i]"),", ",Object(l.b)("inlineCode",{parentName:"p"},"moreData()[i]")," holds its derivatives data and so on. When a source has no record for a candle (for example the order book history is shorter than the candle history) the entry is ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"null")),", so always check for it. Data requested with ",Object(l.b)("inlineCode",{parentName:"p"},"lookback")," or for another timeframe is returned as is and has to be matched by ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"startTime")),". ",Object(l.b)("inlineCode",{parentName:"p"},"ohlcv()")," of the chart market contains only the candles the server has already loaded, never ",Object(l.b)("inlineCode",{parentName:"p"},"null"),"; while older history is still loading the indicator is recalculated automatically.")),Object(l.b)("h3",{id:"ohlcvexchange-ticker-timeframe-lookback"},Object(l.b)("inlineCode",{parentName:"h3"},"ohlcv(exchange, ticker, timeframe, lookback?)")),Object(l.b)("p",null,"Candle data."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'[\n {\n "open": 115886.17,\n "high": 115911.96,\n "low": 115764.27,\n "close": 115815.16,\n "volume": 228.41528,\n "startTime": 1757699100000\n },\n {...},\n ...\n] \n')),Object(l.b)("h3",{id:"depthsexchange-ticker-timeframe-lookback"},Object(l.b)("inlineCode",{parentName:"h3"},"depths(exchange, ticker, timeframe, lookback?)")),Object(l.b)("p",null,"Historical order book snapshots, one per candle. Only the biggest limit orders are stored (the data behind the heatmap)."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'[\n {\n "data": {\n "asks": {\n "109425": 5.467,\n "109495": 3.564,\n ...\n },\n "bids": {\n "33000": 3.746,\n "35000": 16.49,\n ...\n }\n },\n "startTime": 1757699100000\n },\n {...},\n ...\n]\n\n')),Object(l.b)("h3",{id:"clustersexchange-ticker-timeframe-lookback"},Object(l.b)("inlineCode",{parentName:"h3"},"clusters(exchange, ticker, timeframe, lookback?)")),Object(l.b)("p",null,"Cluster (footprint) data: buy and sell volume at every price level of the candle."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'[\n {\n "data": {\n "totalBuy": 16053385,\n "totalSell": 20490989,\n "0.8654": { "buy": 286845, "sell": 336689 },\n "0.8653": { "buy": 271848, "sell": 259167 },\n "0.8652": { "buy": 318814, "sell": 373497 },\n ...\n },\n "startTime": 1757699100000\n },\n {...},\n ...\n]\n\n')),Object(l.b)("h3",{id:"liquidationsexchange-ticker-timeframe-lookback"},Object(l.b)("inlineCode",{parentName:"h3"},"liquidations(exchange, ticker, timeframe, lookback?)")),Object(l.b)("p",null,"Liquidated volume at every price level of the candle (futures markets only). Same format as ",Object(l.b)("inlineCode",{parentName:"p"},"clusters()"),"."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'[\n {\n "data": {\n "totalBuy": 16053385,\n "totalSell": 20490989,\n "0.8654": { "buy": 286845, "sell": 336689 },\n "0.8653": { "buy": 271848, "sell": 259167 },\n "0.8652": { "buy": 318814, "sell": 373497 },\n ...\n },\n "startTime": 1757699100000\n },\n {...},\n ...\n]\n\n')),Object(l.b)("h3",{id:"moredataexchange-ticker-timeframe-lookback"},Object(l.b)("inlineCode",{parentName:"h3"},"moreData(exchange, ticker, timeframe, lookback?)")),Object(l.b)("p",null,"Derivatives data per candle: funding rate, open interest candle, long/short ratios (futures markets only)."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'[\n { \n "data": {\n "FR": 0.00003777, // Funding Rate\n "OI": { // Open Interest Candle\n "open": 85645.708,\n "close": 83496.225,\n "high": 85801.04,\n "low": 83397.918\n },\n "LS": { "l": 0.5426, "s": 0.4574 }, // Long/Short Ratio\n "taLS": { "l": 0.552, "s": 0.448 }, // Top Traders Account Ratio\n "tpLS": { "l": 0.5996, "s": 0.4004 } // Top Traders Position Ratio\n },\n "startTime": 1757699100000\n },\n {...},\n ...\n]\n\n')),Object(l.b)("h3",{id:"orderbookexchange-ticker"},Object(l.b)("inlineCode",{parentName:"h3"},"orderbook(exchange, ticker)")),Object(l.b)("p",null,"Live order book snapshot: the current full book, not tied to candles."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'{\n "asks": {\n "109425": 5.467,\n "109495": 3.564,\n ...\n },\n "bids": {\n "109400": 1.2,\n "109390": 0.8,\n ...\n }\n}\n\n')),Object(l.b)("p",null,"The levels are not sorted, use the ",Object(l.b)("inlineCode",{parentName:"p"},"ta")," orderbook helpers (",Object(l.b)("inlineCode",{parentName:"p"},"ta.bestBid"),", ",Object(l.b)("inlineCode",{parentName:"p"},"ta.sumBids"),", ...) to work with the book.\nThe historical books of the candles come from ",Object(l.b)("inlineCode",{parentName:"p"},"depths()"),": ",Object(l.b)("inlineCode",{parentName:"p"},"depths()[i].data")," has the same format, but only the biggest limit orders are stored there."),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},"const candles = source.ohlcv(); // The exchange, ticker, and timeframe parameters default to the current chart.\nconst closes = candles.map(c => c.close);\n")),Object(l.b)("h3",{id:"levelsexchange-settings"},Object(l.b)("inlineCode",{parentName:"h3"},"levels(exchange, settings?)")),Object(l.b)("p",null,"The levels of the ",Object(l.b)("strong",{parentName:"p"},"Liquidity screener"),": the big resting limit orders of an exchange, filtered with the same settings as the screener page. Live data, not tied to candles. Without a plan the nearness is limited to 1%, the lifespan to 60 minutes and the result to 200 levels; with a plan up to 2000 levels. The largest orders come first. Counts as one data source request."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'{\n "BTCUSDT": {\n "asks": {\n "109500": {\n "price": 109500,\n "quantity": 42.5, // size in the base asset\n "quantityUSDT": 4653750, // size in the quote currency\n "distance": 0.62, // from the current price, percent\n "duration": 184, // minutes the order has been standing\n "touches": 2,\n "currentTouches": 1,\n "filledPercent": 12.4,\n "strength": 95.5, // percent, see the screener\n "diversity": 11,\n "isContinuous": true,\n "date": "2026-10-10T06:12:03.000Z"\n },\n ...\n },\n "bids": { ... },\n "volume1hTotal": 183000000\n },\n ...\n}\n')),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Settings")," (all optional):"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Setting"),Object(l.b)("th",{parentName:"tr",align:null},"Default"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"tickers")),Object(l.b)("td",{parentName:"tr",align:null},"all tickers"),Object(l.b)("td",{parentName:"tr",align:null},"only these tickers of the exchange")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"exclude")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"[]")),Object(l.b)("td",{parentName:"tr",align:null},"tickers to skip")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"type")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'"all"')),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'"all"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"bids"')," or ",Object(l.b)("inlineCode",{parentName:"td"},'"asks"'))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"nearness")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"5")),Object(l.b)("td",{parentName:"tr",align:null},"max distance from the current price, percent")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"size")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"100000")),Object(l.b)("td",{parentName:"tr",align:null},"min order size in the quote currency")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"lifespan")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"0")),Object(l.b)("td",{parentName:"tr",align:null},"min minutes the order has been standing")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"touches"),", ",Object(l.b)("inlineCode",{parentName:"td"},"currentTouches")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"0")),Object(l.b)("td",{parentName:"tr",align:null},"min touches of the level / of the current order")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"filled")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"0")),Object(l.b)("td",{parentName:"tr",align:null},"min filled part of the order, percent")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"strength"),", ",Object(l.b)("inlineCode",{parentName:"td"},"diversity")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"0")),Object(l.b)("td",{parentName:"tr",align:null},"min strength (percent) and diversity, as on the screener")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"continuousOnly")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"false")),Object(l.b)("td",{parentName:"tr",align:null},"skip orders that disappeared and came back")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"volume1h")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"0")),Object(l.b)("td",{parentName:"tr",align:null},"min 1h volume of the ticker in the quote currency")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"spotAndFutures")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"false")),Object(l.b)("td",{parentName:"tr",align:null},"only tickers that exist on both the spot and the futures market")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"limit")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"500")),Object(l.b)("td",{parentName:"tr",align:null},"max number of levels, the largest first (max 2000)")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'// the big orders around the price of the chart market as horizontal lines, labelled with the size and the lifespan\nconst candles = await source.ohlcv();\nconst lastDate = candles.at(-1).startTime;\nconst levels = await source.levels(currentExchange, { tickers: [currentTicker], nearness: 3, size: 300000, lifespan: 30 });\nconst own = levels[currentTicker] || { asks: {}, bids: {} };\nfor (const [type, color] of [["asks", "#ef5350"], ["bids", "#26a69a"]]) {\n for (const [price, level] of Object.entries(own[type])) {\n plot.horizontalLine(`${type} ${price}`, +price, color, 0.8, Math.min(4, 1 + level.quantityUSDT / 1e6));\n plot.text(`${type} ${price} label`, `${(level.quantityUSDT / 1e6).toFixed(2)}M · ${level.duration}m`, lastDate, +price, 10, color);\n }\n}\n')),Object(l.b)("h3",{id:"keylevelsexchange-settings"},Object(l.b)("inlineCode",{parentName:"h3"},"keyLevels(exchange, settings?)")),Object(l.b)("p",null,"The levels of the ",Object(l.b)("strong",{parentName:"p"},"Key levels screener"),": open, high and low of the current and the previous day, week, month and year of every ticker of an exchange, with the distance of each level from the current price. Live data, not tied to candles. Without a plan the result holds only the levels within ±1% of the price (the fixed range of the screener page) and at most 100 tickers; with a plan any distance range and up to 1000 tickers. The tickers with the nearest level come first. Counts as one data source request."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'{\n "BTCUSDT": {\n "price": 79776.57, // current price\n "updatedAt": 1788707880000,\n "bootstrapped": true, // false while the history of the ticker is still being collected\n "nearest": 0.08, // distance of the nearest level, percent\n "matches": 5, // levels inside the distance range\n "daily": { "start": 1788652800000, "open": 79700, "high": 79985, "low": 79710, "openApprox": false,\n "distance": { "open": -0.1, "high": 0.26, "low": -0.08 } }, // percent from the price, signed\n "prevDay": { ... }, "weekly": { ... }, "prevWeek": { ... },\n "monthly": { ... }, "prevMonth": { ... }, "yearly": { ... }, "prevYear": null // null: no data for the period yet\n },\n ...\n}\n')),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Settings")," (all optional):"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Setting"),Object(l.b)("th",{parentName:"tr",align:null},"Default"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"tickers")),Object(l.b)("td",{parentName:"tr",align:null},"all tickers"),Object(l.b)("td",{parentName:"tr",align:null},"only these tickers of the exchange")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"exclude")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"[]")),Object(l.b)("td",{parentName:"tr",align:null},"tickers to skip")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"periods")),Object(l.b)("td",{parentName:"tr",align:null},"all"),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'"daily"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"prevDay"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"weekly"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"prevWeek"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"monthly"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"prevMonth"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"yearly"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"prevYear"'))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"fields")),Object(l.b)("td",{parentName:"tr",align:null},"all"),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'"open"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"high"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"low"'),": the levels to return")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"distanceFrom"),", ",Object(l.b)("inlineCode",{parentName:"td"},"distanceTo")),Object(l.b)("td",{parentName:"tr",align:null},"none"),Object(l.b)("td",{parentName:"tr",align:null},"signed distance range from the price, percent (e.g. ",Object(l.b)("inlineCode",{parentName:"td"},"-2")," and ",Object(l.b)("inlineCode",{parentName:"td"},"2"),"); only tickers with a level inside the range are returned. Without a plan the range is fixed at ±1%")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"limit")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"200")),Object(l.b)("td",{parentName:"tr",align:null},"max number of tickers, the nearest level first (max 1000; 100 without a plan)")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'// the key levels of the chart market as horizontal lines\nconst candles = await source.ohlcv();\nconst lastDate = candles.at(-1).startTime;\nconst rows = await source.keyLevels(currentExchange, { tickers: [currentTicker], periods: ["daily", "prevDay", "weekly"] });\nconst own = rows[currentTicker];\nif (own) {\n for (const period of ["daily", "prevDay", "weekly"]) {\n const level = own[period];\n if (!level) continue;\n for (const [field, color] of [["high", "#ef5350"], ["low", "#26a69a"], ["open", "#90a4ae"]]) {\n if (level[field] == null) continue;\n plot.horizontalLine(`${period} ${field}`, level[field], color, 0.8, 1);\n plot.text(`${period} ${field} label`, `${period} ${field.toUpperCase()}`, lastDate, level[field], 10, color);\n }\n }\n}\n')),Object(l.b)("h3",{id:"levelsmetricsexchange-settings"},Object(l.b)("inlineCode",{parentName:"h3"},"levelsMetrics(exchange, settings?)")),Object(l.b)("p",null,"The history of the ",Object(l.b)("strong",{parentName:"p"},"strong liquidity levels")," collected for the ",Object(l.b)("strong",{parentName:"p"},"Metrics")," page: every 5 minutes the server snapshots the liquidity screener levels of every exchange with a duration of at least 30 minutes and a strength of at least 80% and keeps a week of them. The source filters the snapshots of a period like the page does and returns, per snapshot, the number and the $ size of the matching levels, the aggregates per ticker and the lifecycle of every level. Collected data, not tied to candles: match ",Object(l.b)("inlineCode",{parentName:"p"},"series[].at")," with the candle dates. Without a plan the period is at most 24 hours and the list holds at most 100 levels; with a plan up to 7 days and 1000 levels. Counts as one data source request."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'{\n "exchange": "binanceFutures",\n "hours": 24,\n "snapshotMinutes": 5,\n "latestAt": 1792102098685,\n "summary": { "snapshots": 287, "uniqueLevels": 217, "uniqueTickers": 118, "activeNow": 180, "bidsNow": 102, "asksNow": 78, "usdNow": 119294436 },\n "series": [ // one point per snapshot, oldest first\n { "at": 1792015822555, "bids": 24, "asks": 21, "bidsUsd": 1851345, "asksUsd": 1228883, "tickers": 20 },\n ...\n ],\n "tickers": { // per ticker, over the period\n "BTCUSDT": { "now": 3, "nowUsd": 95710480, "snapshots": 63, "bids": 3, "asks": 0, "levels": 3,\n "maxUsd": 42347880, "maxDuration": 8220, "maxStrength": 100 },\n ...\n },\n "levels": [ // the lifecycle of every level, active ones first\n { "ticker": "BTCUSDT", "side": "bids", "price": 52050, "usd": 42347880, "quantity": 813.6, "distance": 19.8,\n "duration": 8220, "strength": 100, "continuous": 1, "firstSeen": 1792015822555, "lastSeen": 1792102098685,\n "snapshots": 63, "maxDuration": 8220, "maxStrength": 100, "active": true },\n ...\n ],\n "levelsTotal": 217\n}\n')),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Settings")," (all optional):"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Setting"),Object(l.b)("th",{parentName:"tr",align:null},"Default"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"tickers")),Object(l.b)("td",{parentName:"tr",align:null},"all tickers"),Object(l.b)("td",{parentName:"tr",align:null},"only these tickers of the exchange")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"exclude")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"[]")),Object(l.b)("td",{parentName:"tr",align:null},"tickers to skip")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"hours")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"24")),Object(l.b)("td",{parentName:"tr",align:null},"period, hours (max 168; 24 without a plan)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"minDuration")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"30")),Object(l.b)("td",{parentName:"tr",align:null},"min order duration, minutes: ",Object(l.b)("inlineCode",{parentName:"td"},"30"),", ",Object(l.b)("inlineCode",{parentName:"td"},"60"),", ",Object(l.b)("inlineCode",{parentName:"td"},"120")," or ",Object(l.b)("inlineCode",{parentName:"td"},"180"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"minStrength")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"80")),Object(l.b)("td",{parentName:"tr",align:null},"min order strength, percent: ",Object(l.b)("inlineCode",{parentName:"td"},"80"),", ",Object(l.b)("inlineCode",{parentName:"td"},"90")," or ",Object(l.b)("inlineCode",{parentName:"td"},"100"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"maxDistance")),Object(l.b)("td",{parentName:"tr",align:null},"none"),Object(l.b)("td",{parentName:"tr",align:null},"max distance of the order to the current price, percent, checked in every snapshot")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"stableOnly")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"false")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"true"),': only orders whose quantity never jumped ("exclude orders with unstable quantities")')),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"limit")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"200")),Object(l.b)("td",{parentName:"tr",align:null},"max number of levels in ",Object(l.b)("inlineCode",{parentName:"td"},"levels")," (max 1000; 100 without a plan)")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'// $ size of the strong levels of the chart market per candle\nconst candles = await source.ohlcv();\nconst data = await source.levelsMetrics(currentExchange, { tickers: [currentTicker], hours: 24, minDuration: 60, minStrength: 90, stableOnly: true });\nconst series = data.series; // oldest first\nlet j = 0;\nconst buy = [], sell = [];\nfor (let i = 0; i < candles.length; i++) {\n const end = candles[i + 1] ? candles[i + 1].startTime : candles[i].startTime + (candles[1].startTime - candles[0].startTime);\n while (j + 1 < series.length && series[j + 1].at < end) j++; // the last snapshot inside the candle\n const point = series[j] && series[j].at < end ? series[j] : null;\n buy.push(point ? point.bidsUsd : null);\n sell.push(point ? point.asksUsd : null);\n}\nplot.areaSeries("Buy levels ($)", buy, "#26a69a", 0.3, "#26a69a", 1, 1, 0);\nplot.areaSeries("Sell levels ($)", sell, "#ef5350", 0.3, "#ef5350", 1, 1, 0);\n')),Object(l.b)("h2",{id:"http-requests"},"HTTP Requests"),Object(l.b)("p",null,"The ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"http"))," class sends requests to external HTTP APIs and returns the response, so an indicator can use data that is not available in ",Object(l.b)("inlineCode",{parentName:"p"},"source"),": your own server, a public REST API, an on-chain metrics provider, ...\nLike ",Object(l.b)("inlineCode",{parentName:"p"},"source.*"),", the calls are written as synchronous functions, no ",Object(l.b)("inlineCode",{parentName:"p"},"await")," is needed."),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"get(url, options?)")),Object(l.b)("td",{parentName:"tr",align:null},"GET request")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"post(url, body?, options?)")),Object(l.b)("td",{parentName:"tr",align:null},"POST request. An object or array ",Object(l.b)("inlineCode",{parentName:"td"},"body")," is sent as JSON (",Object(l.b)("inlineCode",{parentName:"td"},"Content-Type: application/json"),"), a string is sent as is")))),Object(l.b)("p",null,Object(l.b)("inlineCode",{parentName:"p"},"options")," is ",Object(l.b)("inlineCode",{parentName:"p"},"{ headers: { name: value } }")," — up to ",Object(l.b)("strong",{parentName:"p"},"10")," custom headers (for example ",Object(l.b)("inlineCode",{parentName:"p"},"Authorization")," or ",Object(l.b)("inlineCode",{parentName:"p"},"x-api-key"),")."),Object(l.b)("p",null,"Both methods return the same response object and ",Object(l.b)("strong",{parentName:"p"},"never throw"),":"),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'{\n ok: true, // status 200–299\n status: 200, // HTTP status, 0 when the request was not sent\n headers: { "content-type": "application/json", ... },\n data: { ... }, // response body parsed as JSON, null when the body is not JSON\n text: "...", // raw response body\n error: null // reason when the request failed: "timeout", "url not allowed", "too many requests", ...\n}\n')),Object(l.b)(i.b,{type:"warning",title:"Limits and safety",mdxType:"Callout"},Object(l.b)("ul",null,Object(l.b)("li",{parentName:"ul"},"Up to ",Object(l.b)("strong",{parentName:"li"},"3 requests")," per script run and ",Object(l.b)("strong",{parentName:"li"},"60 requests per minute")," per user."),Object(l.b)("li",{parentName:"ul"},"Only public ",Object(l.b)("inlineCode",{parentName:"li"},"http://")," / ",Object(l.b)("inlineCode",{parentName:"li"},"https://")," URLs. Requests to ",Object(l.b)("inlineCode",{parentName:"li"},"localhost"),", private networks and cloud metadata addresses are rejected, redirects are ",Object(l.b)("strong",{parentName:"li"},"not followed")," (a ",Object(l.b)("inlineCode",{parentName:"li"},"3xx")," response is returned as is)."),Object(l.b)("li",{parentName:"ul"},"Requests are sent from the MobChart proxy servers: the target API sees the proxy address and never your IP."),Object(l.b)("li",{parentName:"ul"},"Timeout ",Object(l.b)("strong",{parentName:"li"},"5 seconds"),", request body up to ",Object(l.b)("strong",{parentName:"li"},"64 KB"),", response up to ",Object(l.b)("strong",{parentName:"li"},"512 KB")," (bigger responses fail with ",Object(l.b)("inlineCode",{parentName:"li"},"response too large"),")."),Object(l.b)("li",{parentName:"ul"},Object(l.b)("inlineCode",{parentName:"li"},"Host"),", ",Object(l.b)("inlineCode",{parentName:"li"},"Cookie"),", ",Object(l.b)("inlineCode",{parentName:"li"},"Proxy-*"),", ",Object(l.b)("inlineCode",{parentName:"li"},"X-Forwarded-*")," and other connection headers can not be set, ",Object(l.b)("inlineCode",{parentName:"li"},"Set-Cookie")," is never returned."),Object(l.b)("li",{parentName:"ul"},"Alert checks run your script on the server regularly: the API is called on every check, keep the rate limits of the target API in mind."),Object(l.b)("li",{parentName:"ul"},"The code of a public indicator (",Object(l.b)("inlineCode",{parentName:"li"},"isPublicCode = true"),") is visible to everyone — never put API keys into public code."))),Object(l.b)("p",null,"Possible ",Object(l.b)("inlineCode",{parentName:"p"},"error")," values: ",Object(l.b)("inlineCode",{parentName:"p"},"invalid url"),", ",Object(l.b)("inlineCode",{parentName:"p"},"url not allowed"),", ",Object(l.b)("inlineCode",{parentName:"p"},"invalid headers"),", ",Object(l.b)("inlineCode",{parentName:"p"},"invalid body"),", ",Object(l.b)("inlineCode",{parentName:"p"},"body too large"),", ",Object(l.b)("inlineCode",{parentName:"p"},"response too large"),", ",Object(l.b)("inlineCode",{parentName:"p"},"timeout"),", ",Object(l.b)("inlineCode",{parentName:"p"},"too many requests"),", ",Object(l.b)("inlineCode",{parentName:"p"},"rate limit"),", ",Object(l.b)("inlineCode",{parentName:"p"},"busy"),", ",Object(l.b)("inlineCode",{parentName:"p"},"proxy unavailable"),", ",Object(l.b)("inlineCode",{parentName:"p"},"network error"),"."),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example (GET):")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'define("Fear & Greed", "Crypto Fear & Greed index", "offchart", true, true);\n\nconst res = http.get("https://api.alternative.me/fng/?limit=30");\nif (!res.ok) logger.log("request failed:", res.status, res.error);\n\n// res.data = { data: [{ value: "71", timestamp: "1757635200" }, ...] }\nconst byDay = {};\n(res.data?.data || []).forEach((row) => { byDay[Number(row.timestamp) * 1000] = Number(row.value); });\n\nconst candles = source.ohlcv();\nconst values = candles.map((c) => c ? byDay[c.startTime - (c.startTime % 86400000)] ?? null : null);\nplot.lineSeries("Fear & Greed", values, "#00C8FF");\n')),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example (POST):")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const candles = source.ohlcv();\nconst last = candles.at(-1);\n\nconst res = http.post(\n "https://example.com/api/signals",\n { exchange: currentExchange, ticker: currentTicker, close: last.close },\n { headers: { "x-api-key": "YOUR_KEY" } } // private code only\n);\nlogger.log("signal sent:", res.ok, res.status, res.data);\n')),Object(l.b)("h2",{id:"global-variables"},"Global Variables"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Variable"),Object(l.b)("th",{parentName:"tr",align:null},"Description"),Object(l.b)("th",{parentName:"tr",align:null},"Example"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"currentExchange")),Object(l.b)("td",{parentName:"tr",align:null},"Current exchange on the chart"),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"binance"),", ",Object(l.b)("inlineCode",{parentName:"td"},"binanceFutures"),", ",Object(l.b)("inlineCode",{parentName:"td"},"bybit"),", ",Object(l.b)("inlineCode",{parentName:"td"},"bybitFutures"),", ",Object(l.b)("inlineCode",{parentName:"td"},"okx"),", ",Object(l.b)("inlineCode",{parentName:"td"},"okxFutures"),", ",Object(l.b)("inlineCode",{parentName:"td"},"coinbase"),", ",Object(l.b)("inlineCode",{parentName:"td"},"bitfinex"),", ",Object(l.b)("inlineCode",{parentName:"td"},"bitfinexFutures"),", ",Object(l.b)("inlineCode",{parentName:"td"},"hyperliquid"),", ",Object(l.b)("inlineCode",{parentName:"td"},"hyperliquidFutures"),", ",Object(l.b)("inlineCode",{parentName:"td"},"asterFutures"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"currentTicker")),Object(l.b)("td",{parentName:"tr",align:null},"Current ticker on the chart (format depends on the exchange)"),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"BTCUSDT"),", ",Object(l.b)("inlineCode",{parentName:"td"},"ADA-USD"),", ",Object(l.b)("inlineCode",{parentName:"td"},"BTC/USDC"),", ...")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"currentTimeframe")),Object(l.b)("td",{parentName:"tr",align:null},"Current timeframe on the chart"),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"1m"),", ",Object(l.b)("inlineCode",{parentName:"td"},"5m"),", ",Object(l.b)("inlineCode",{parentName:"td"},"15m"),", ...")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"exchanges")),Object(l.b)("td",{parentName:"tr",align:null},"Names of every exchange (frozen array)"),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'["binance", "binanceFutures", ...]'))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"tickers")),Object(l.b)("td",{parentName:"tr",align:null},"Tickers of every exchange: ",Object(l.b)("inlineCode",{parentName:"td"},"tickers[currentExchange]")," is the list of the chart exchange (frozen)"),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"tickers.binance")," = ",Object(l.b)("inlineCode",{parentName:"td"},'["BTCUSDT", "ETHUSDT", ...]'))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"heatmapStep")),Object(l.b)("td",{parentName:"tr",align:null},"Price step of the order book heatmap and clusters at the current price (the precision the server rounds prices to: 5 above 5000, 1 for 1000-5000, 0.1 for 100-1000, ...), taken from the latest close of the chart; ",Object(l.b)("inlineCode",{parentName:"td"},"null")," when the price is not known yet (known after the first ",Object(l.b)("inlineCode",{parentName:"td"},"source.ohlcv()")," of the chart market)"),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"5"),", ",Object(l.b)("inlineCode",{parentName:"td"},"1"),", ",Object(l.b)("inlineCode",{parentName:"td"},"0.1"),", ",Object(l.b)("inlineCode",{parentName:"td"},"0.001"),", ...")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},"const candles = source.ohlcv(currentExchange, currentTicker, currentTimeframe);\nconst closes = candles.map(c => c.close);\n")),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Ticker formats.")," Every ",Object(l.b)("inlineCode",{parentName:"p"},"source")," method that takes an exchange and a ticker runs the ticker through ",Object(l.b)("inlineCode",{parentName:"p"},"resolveTicker"),": the base coin is taken from whatever format you pass (",Object(l.b)("inlineCode",{parentName:"p"},"ETHUSDT"),", ",Object(l.b)("inlineCode",{parentName:"p"},"ETH-USD"),", ",Object(l.b)("inlineCode",{parentName:"p"},"ETH/USDC"),", ",Object(l.b)("inlineCode",{parentName:"p"},"ETH:USD"),", ",Object(l.b)("inlineCode",{parentName:"p"},"ETHF0:USTF0"),", ...) and the ticker is rebuilt in the format of the exchange you ask for. So ",Object(l.b)("inlineCode",{parentName:"p"},"ETH-USD")," works for Binance and ",Object(l.b)("inlineCode",{parentName:"p"},"currentTicker")," of a Binance chart works for Coinbase. Only the coin is kept: the exchange's standard quote pair is used (USDT on Binance, USD on Coinbase, USDC on Hyperliquid, ...), and a coin the exchange does not list gives an empty result with a line in the logs."),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'// a Coinbase-style ticker on Binance Futures: resolved to ETHUSDT\nconst depths = source.depths("binanceFutures", "ETH-USD");\n// the ticker of a Binance chart on Coinbase: ETHUSDT becomes ETH-USD\nconst spot = source.ohlcv("coinbase", currentTicker, currentTimeframe);\n')),Object(l.b)("h2",{id:"logging"},"Logging"),Object(l.b)("p",null,"The ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"logger"))," class is used for outputting information and debugging."),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"log(...args)")),Object(l.b)("td",{parentName:"tr",align:null},"Adds an entry to the log (with limits on length and quantity)")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const candles = source.ohlcv();\nlogger.log("First candle:", candles?.[0]);\n')),Object(l.b)(i.f,{src:"../../public/code_editor_log.png",mdxType:"Screenshot"}),Object(l.b)("h2",{id:"visualization"},"Visualization"),Object(l.b)("p",null,"The ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"plot"))," class is responsible for drawing graphical elements and data series."),Object(l.b)("h3",{id:"geometric-shapes"},"Geometric Shapes"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"box(title, date1, price1, date2, price2, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(l.b)("td",{parentName:"tr",align:null},"Rectangle")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"square(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(l.b)("td",{parentName:"tr",align:null},"Square")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"diamond(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(l.b)("td",{parentName:"tr",align:null},"Diamond")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"triangle(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(l.b)("td",{parentName:"tr",align:null},"Triangle")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"circle(title, date1, price1, radius = 10, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(l.b)("td",{parentName:"tr",align:null},"Circle")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"text(title, value, date1, price1, fontSize, fill, opacity)")),Object(l.b)("td",{parentName:"tr",align:null},"Text on chart")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"table(title, rows, options)")),Object(l.b)("td",{parentName:"tr",align:null},"Table pinned to a corner of the chart, see ",Object(l.b)("a",{parentName:"td",href:"#tables"},"Tables"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"straightLine(title, date1, price1, date2, price2, stroke, strokeOpacity, strokeWidth, strokeDasharray)")),Object(l.b)("td",{parentName:"tr",align:null},"Straight line")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"horizontalLine(title, price1, stroke, strokeOpacity, strokeWidth, strokeDasharray)")),Object(l.b)("td",{parentName:"tr",align:null},"Horizontal line across the whole chart")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"verticalLine(title, date1, stroke, strokeOpacity, strokeWidth, strokeDasharray)")),Object(l.b)("td",{parentName:"tr",align:null},"Vertical line across the whole chart")))),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Parameter"),Object(l.b)("th",{parentName:"tr",align:null},"Data Type"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"title")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"string")),Object(l.b)("td",{parentName:"tr",align:null},"Element name (unique identifier on the chart)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"date1")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")," (timestamp, ms)"),Object(l.b)("td",{parentName:"tr",align:null},"Starting timestamp")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"price1")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")),Object(l.b)("td",{parentName:"tr",align:null},"Starting price (Y-axis coordinate)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"date2")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")," (timestamp, ms)"),Object(l.b)("td",{parentName:"tr",align:null},"Ending timestamp (for lines and rectangles)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"price2")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")),Object(l.b)("td",{parentName:"tr",align:null},"Ending price (for lines and rectangles)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"size")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")),Object(l.b)("td",{parentName:"tr",align:null},"Shape size (in pixels)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"radius")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")),Object(l.b)("td",{parentName:"tr",align:null},"Circle radius (default 10)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"fill")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"string")," (hex, e.g., ",Object(l.b)("inlineCode",{parentName:"td"},'"#FF0000"'),")"),Object(l.b)("td",{parentName:"tr",align:null},"Fill color of the shape")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"opacity")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")," (0–1)"),Object(l.b)("td",{parentName:"tr",align:null},"Fill opacity")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"stroke")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"string")," (hex, e.g., ",Object(l.b)("inlineCode",{parentName:"td"},'"#000000"'),")"),Object(l.b)("td",{parentName:"tr",align:null},"Stroke color")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"strokeOpacity")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")," (0–1)"),Object(l.b)("td",{parentName:"tr",align:null},"Stroke opacity")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"strokeWidth")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")),Object(l.b)("td",{parentName:"tr",align:null},"Stroke width (in pixels)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"strokeDasharray")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"array")," (e.g., ",Object(l.b)("inlineCode",{parentName:"td"},"[5,5]"),")"),Object(l.b)("td",{parentName:"tr",align:null},"Dashed line style")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"fontSize")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")),Object(l.b)("td",{parentName:"tr",align:null},"Text size (in pixels)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"value")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"string")),Object(l.b)("td",{parentName:"tr",align:null},"Text value (for ",Object(l.b)("inlineCode",{parentName:"td"},"text")," method)")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'plot.text("Text object", "Hello world!", 1757699100000, 115000, 10, "#00C8FF", 0.5);\nplot.straightLine(`${namePrefix} Open`, level.timeStart, level.open, level.timeEnd, level.open, color);\nplot.circle(`${namePrefix} High`, level.timeStart, level.high, 3, color);\n')),Object(l.b)("p",null,"No more than ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"3000"))," shapes can be displayed on the chart."),Object(l.b)("h3",{id:"data-series"},"Data Series"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"lineSeries(title, values, stroke, strokeOpacity, strokeWidth, strokeDasharray)")),Object(l.b)("td",{parentName:"tr",align:null},"Line chart")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"barSeries(title, values, fill, opacity)")),Object(l.b)("td",{parentName:"tr",align:null},"Bar chart")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"candleSeries(title, openValues, closeValues, highValues, lowValues, fill, opacity)")),Object(l.b)("td",{parentName:"tr",align:null},"Candles")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"areaSeries(title, values, fill, opacity, stroke, strokeOpacity, strokeWidth, base)")),Object(l.b)("td",{parentName:"tr",align:null},"Filled area between the line and ",Object(l.b)("inlineCode",{parentName:"td"},"base")," (default: the bottom of the chart)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"bandSeries(title, upperValues, lowerValues, fill, opacity, stroke, strokeOpacity, strokeWidth, step)")),Object(l.b)("td",{parentName:"tr",align:null},"Filled band between two series, every candle keeps its own color and opacity")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"profile(title, values, options)")),Object(l.b)("td",{parentName:"tr",align:null},"Volume-profile style histogram of the visible candles, see ",Object(l.b)("a",{parentName:"td",href:"#profiles"},"Profiles"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"histogram(title, rows, options)")),Object(l.b)("td",{parentName:"tr",align:null},"A finished price map drawn as a profile, see ",Object(l.b)("a",{parentName:"td",href:"#histograms"},"Histograms"))))),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Parameter"),Object(l.b)("th",{parentName:"tr",align:null},"Data Type"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"values"),", ",Object(l.b)("inlineCode",{parentName:"td"},"upperValues"),", ",Object(l.b)("inlineCode",{parentName:"td"},"lowerValues")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"(number \\| null)[]")),Object(l.b)("td",{parentName:"tr",align:null},"One value per candle, ",Object(l.b)("inlineCode",{parentName:"td"},"null")," hides the candle")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"fill"),", ",Object(l.b)("inlineCode",{parentName:"td"},"stroke")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"string")," (hex) or ",Object(l.b)("inlineCode",{parentName:"td"},"function")),Object(l.b)("td",{parentName:"tr",align:null},"Color, or a function returning the color of the candle: ",Object(l.b)("inlineCode",{parentName:"td"},"(value, i)")," for ",Object(l.b)("inlineCode",{parentName:"td"},"lineSeries"),", ",Object(l.b)("inlineCode",{parentName:"td"},"barSeries"),", ",Object(l.b)("inlineCode",{parentName:"td"},"candleSeries")," and ",Object(l.b)("inlineCode",{parentName:"td"},"areaSeries"),", ",Object(l.b)("inlineCode",{parentName:"td"},"(upper, lower, i)")," for ",Object(l.b)("inlineCode",{parentName:"td"},"bandSeries"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"opacity")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")," (0–1) or ",Object(l.b)("inlineCode",{parentName:"td"},"function")),Object(l.b)("td",{parentName:"tr",align:null},"Fill opacity, a function ",Object(l.b)("inlineCode",{parentName:"td"},"(value, i)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"(upper, lower, i)")," for ",Object(l.b)("inlineCode",{parentName:"td"},"areaSeries")," / ",Object(l.b)("inlineCode",{parentName:"td"},"bandSeries"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"base")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number")," ","|"," ",Object(l.b)("inlineCode",{parentName:"td"},"null")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"areaSeries")," base level, ",Object(l.b)("inlineCode",{parentName:"td"},"null")," = the bottom of the chart")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"step")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"boolean")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"bandSeries")," style: ",Object(l.b)("inlineCode",{parentName:"td"},"true")," = one column per candle (default), ",Object(l.b)("inlineCode",{parentName:"td"},"false")," = a continuous polygon")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'plot.lineSeries("EMA", ta.ema(closes, 20), "#00C8FF");\n// every candle keeps its own color: green where the close is above the EMA, red below it\nplot.lineSeries("Close", closes, (v, i) => v > ema[i] ? colorBuy : colorSell);\nplot.barSeries("Premium %", premiumPercents, ((d) => d > 0 ? color1 : color2), 1);\nplot.candleSeries(\n "Candles",\n opens, closes, highs, lows,\n (v, i) => {\n return opens?.[i] > closes?.[i] ? colorBuy : colorSell\n }\n);\nplot.areaSeries("Volume", volumes, "#00C8FF", 0.3);\n// area colored per candle, more opaque on the bigger candles\nplot.areaSeries("Delta", deltas, (v, i) => v >= 0 ? colorBuy : colorSell, (v, i) => Math.min(1, Math.abs(v) / maxDelta));\n\n// pressure band between the close and 1% below it, colored and shaded by the imbalance ratio of the candle\nplot.bandSeries(\n "Bid pressure",\n closes,\n closes.map(c => c * 0.99),\n (upper, lower, i) => ratios[i] > 0 ? bidColor : askColor,\n (upper, lower, i) => Math.abs(ratios[i]) / 100\n);\n')),Object(l.b)("p",null,"No more than ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"20"))," data series can be displayed on the chart."),Object(l.b)("h3",{id:"profiles"},"Profiles"),Object(l.b)("p",null,Object(l.b)("inlineCode",{parentName:"p"},"plot.profile(title, values, options?)")," draws a histogram by price, like the built-in volume profile. ",Object(l.b)("inlineCode",{parentName:"p"},"values[i]")," is the price map of candle ",Object(l.b)("inlineCode",{parentName:"p"},"i")," of ",Object(l.b)("inlineCode",{parentName:"p"},"ohlcv()"),": ",Object(l.b)("inlineCode",{parentName:"p"},"{ price: value }")," or ",Object(l.b)("inlineCode",{parentName:"p"},"{ price: { buy, sell } }")," (the ",Object(l.b)("inlineCode",{parentName:"p"},"data")," of a ",Object(l.b)("inlineCode",{parentName:"p"},"clusters()")," or ",Object(l.b)("inlineCode",{parentName:"p"},"liquidations()")," record can be passed as is), or ",Object(l.b)("inlineCode",{parentName:"p"},"null")," for a candle without rows. The rows of the candles ",Object(l.b)("strong",{parentName:"p"},"visible on the chart")," are summed per price step on the chart itself, so the profile follows pan, zoom and load more without a new run. Single values are drawn in one colour (a negative value goes to ",Object(l.b)("inlineCode",{parentName:"p"},"fill2"),"), pairs split the bar into buy and sell parts and allow delta mode. ",Object(l.b)("inlineCode",{parentName:"p"},"profile()")," returns no alert handle."),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Option"),Object(l.b)("th",{parentName:"tr",align:null},"Default"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"step")),Object(l.b)("td",{parentName:"tr",align:null},"chart tick size"),Object(l.b)("td",{parentName:"tr",align:null},"row height in price units, prices are snapped down to it")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"orient")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'"right"')),Object(l.b)("td",{parentName:"tr",align:null},"edge the bars grow from, ",Object(l.b)("inlineCode",{parentName:"td"},'"right"')," or ",Object(l.b)("inlineCode",{parentName:"td"},'"left"'))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"width")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"30")),Object(l.b)("td",{parentName:"tr",align:null},"widest row as a percent of the chart width")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"fill")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'"#26a69a"')),Object(l.b)("td",{parentName:"tr",align:null},"colour of the value, or of the buy part")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"fill2")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'"#ef5350"')),Object(l.b)("td",{parentName:"tr",align:null},"colour of the sell part")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"opacity")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"0.6")),Object(l.b)("td",{parentName:"tr",align:null},"bar opacity")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"delta")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"false")),Object(l.b)("td",{parentName:"tr",align:null},"draw ",Object(l.b)("inlineCode",{parentName:"td"},"buy - sell")," instead of the sum, pairs only")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"poc")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"true")),Object(l.b)("td",{parentName:"tr",align:null},"line at the row with the largest value")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"valueArea")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"70")),Object(l.b)("td",{parentName:"tr",align:null},"percent of the total around the POC drawn at full opacity, ",Object(l.b)("inlineCode",{parentName:"td"},"false")," to disable")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"valueAreaOpacity")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"0.3")),Object(l.b)("td",{parentName:"tr",align:null},"opacity of the rows outside the value area")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"labels")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"true")),Object(l.b)("td",{parentName:"tr",align:null},"row totals on the bars when there is room")))),Object(l.b)("p",null,"Limits: ",Object(l.b)("strong",{parentName:"p"},"3")," profiles per run, ",Object(l.b)("strong",{parentName:"p"},"300")," rows per candle, ",Object(l.b)("strong",{parentName:"p"},"200000")," rows per profile. Rows beyond the limits are dropped and the run logs a note."),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'// volume profile of the chart, the same picture as the built-in tool\nconst rows = await source.clusters();\nplot.profile("Volume", rows.map((r) => r?.data ?? null), { step: 5 });\n\n// only large trades, delta coloured, on the left edge\nconst minSize = input.number("Min trade size", 50, 0, 100000, 1);\nconst big = rows.map((r) => {\n if (!r?.data) return null;\n const out = {};\n for (const [price, c] of Object.entries(r.data)) {\n if (price === "totalBuy" || price === "totalSell") continue;\n if (c.buy + c.sell >= minSize) out[price] = { buy: c.buy, sell: c.sell };\n }\n return out;\n});\nplot.profile("Large trades", big, { step: 5, delta: true, orient: "left" });\n')),Object(l.b)("h3",{id:"histograms"},"Histograms"),Object(l.b)("p",null,Object(l.b)("inlineCode",{parentName:"p"},"plot.histogram(title, rows, options?)")," draws a histogram you have already built: ",Object(l.b)("strong",{parentName:"p"},"one price map for the whole chart"),", nothing is summed from the candles. Use ",Object(l.b)("inlineCode",{parentName:"p"},"profile()")," when the rows belong to candles and the picture should follow the visible range, and ",Object(l.b)("inlineCode",{parentName:"p"},"histogram()")," when the rows are final: an order book snapshot, levels the script computed, data of another market."),Object(l.b)("p",null,Object(l.b)("inlineCode",{parentName:"p"},"rows")," has the same formats as a candle entry of ",Object(l.b)("inlineCode",{parentName:"p"},"profile()"),": ",Object(l.b)("inlineCode",{parentName:"p"},"{ price: value }"),", ",Object(l.b)("inlineCode",{parentName:"p"},"{ price: { buy, sell } }")," or a list of ",Object(l.b)("inlineCode",{parentName:"p"},"[price, buy, sell]")," rows. A single value is drawn in one colour (a negative value goes to ",Object(l.b)("inlineCode",{parentName:"p"},"fill2"),"), pairs split the bar into buy and sell parts and allow delta mode. The options are the same as for ",Object(l.b)("inlineCode",{parentName:"p"},"profile()"),", see the table above."),Object(l.b)("p",null,"The histogram is dated with the last candle of the run and the chart keeps the ",Object(l.b)("strong",{parentName:"p"},"newest")," histogram of a title, so partial runs over older candles (loading history) never replace the current one. It is redrawn with every refresh of the script, so an order book histogram follows the live book. ",Object(l.b)("inlineCode",{parentName:"p"},"histogram()")," returns no alert handle."),Object(l.b)("p",null,"Limits: ",Object(l.b)("strong",{parentName:"p"},"3")," histograms per run, ",Object(l.b)("strong",{parentName:"p"},"5000")," rows. Rows beyond the limit are dropped and the run logs a note."),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example, the order book:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'// the live order book as a profile on the right edge: bids in the buy colour, asks in the sell colour\nconst book = await source.orderbook();\nconst rows = {};\nfor (const [price, qty] of Object.entries(book.bids)) rows[price] = { buy: qty };\nfor (const [price, qty] of Object.entries(book.asks)) rows[price] = { sell: qty };\nplot.histogram("Order book", rows, { step: 5, width: 25, valueArea: false, poc: false });\n')),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example, your own levels:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'// your own histogram: cluster volume of the last 100 candles, recent candles weigh more\nconst rows = await source.clusters();\nconst weighted = {};\nrows.slice(-100).forEach((r, i, arr) => {\n if (!r?.data) return;\n const weight = (i + 1) / arr.length;\n for (const [price, c] of Object.entries(r.data)) {\n if (price === "totalBuy" || price === "totalSell") continue;\n const cell = weighted[price] || (weighted[price] = { buy: 0, sell: 0 });\n cell.buy += (c.buy || 0) * weight;\n cell.sell += (c.sell || 0) * weight;\n }\n});\nplot.histogram("Weighted volume", weighted, { step: 5, orient: "left", delta: true });\n')),Object(l.b)("h3",{id:"tables"},"Tables"),Object(l.b)("p",null,Object(l.b)("inlineCode",{parentName:"p"},"plot.table(title, rows, options?)")," draws a small grid pinned to a corner of the chart pane: multi-timeframe summaries, statistics of the loaded range, signal dashboards. ",Object(l.b)("inlineCode",{parentName:"p"},"rows")," is an array of rows, a row is an array of cells, and a cell is a string, a number or ",Object(l.b)("inlineCode",{parentName:"p"},"{ text, color, bg, align, bold }"),". Tables of the same corner stack in order. A table is dated with the last candle of the run and the chart keeps the ",Object(l.b)("strong",{parentName:"p"},"newest")," table of a title, so partial runs over older candles (loading history) never replace the current one. Tables are not drawn on charts narrower than 500 px. A table can be dragged with the mouse: the position is saved with the workspace and reset when the script changes its ",Object(l.b)("inlineCode",{parentName:"p"},"position")," option. ",Object(l.b)("inlineCode",{parentName:"p"},"table()")," returns no alert handle."),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Option"),Object(l.b)("th",{parentName:"tr",align:null},"Default"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"position")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'"top-right"')),Object(l.b)("td",{parentName:"tr",align:null},"corner of the pane: ",Object(l.b)("inlineCode",{parentName:"td"},'"top-right"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"top-left"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"bottom-right"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"bottom-left"'))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"header")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"false")),Object(l.b)("td",{parentName:"tr",align:null},"first row drawn bold on a shaded band")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"fontSize")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"11")),Object(l.b)("td",{parentName:"tr",align:null},"text size in px, 8 to 16")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"color"),", ",Object(l.b)("inlineCode",{parentName:"td"},"bg"),", ",Object(l.b)("inlineCode",{parentName:"td"},"border")),Object(l.b)("td",{parentName:"tr",align:null},"by theme"),Object(l.b)("td",{parentName:"tr",align:null},"default text, panel and grid colours; a cell can override ",Object(l.b)("inlineCode",{parentName:"td"},"color")," and ",Object(l.b)("inlineCode",{parentName:"td"},"bg"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"opacity")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"0.85")),Object(l.b)("td",{parentName:"tr",align:null},"panel opacity")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"align")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},'"left"')),Object(l.b)("td",{parentName:"tr",align:null},"default cell alignment, ",Object(l.b)("inlineCode",{parentName:"td"},'"left"'),", ",Object(l.b)("inlineCode",{parentName:"td"},'"center"')," or ",Object(l.b)("inlineCode",{parentName:"td"},'"right"'))))),Object(l.b)("p",null,"Limits: ",Object(l.b)("strong",{parentName:"p"},"3")," tables per run, ",Object(l.b)("strong",{parentName:"p"},"50")," rows, ",Object(l.b)("strong",{parentName:"p"},"10")," columns, ",Object(l.b)("strong",{parentName:"p"},"100")," characters per cell. Longer text is cut with an ellipsis, a column takes at most 30% of the chart width."),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const candles = await source.ohlcv();\nconst closes = candles.map((c) => c.close);\nconst rsi = ta.rsi(closes, 14);\nconst last = rsi.at(-1);\nconst change = (closes.at(-1) / closes.at(-25) - 1) * 100;\n\nplot.table("Stats", [\n ["Metric", "Value"],\n ["RSI 14", { text: last.toFixed(1), color: last > 70 ? "#ef5350" : last < 30 ? "#26a69a" : undefined }],\n ["Change 24 bars", { text: change.toFixed(2) + "%", align: "right" }],\n ["Volume", candles.slice(-24).reduce((s, c) => s + c.volume, 0).toFixed(0)],\n], { position: "top-right", header: true });\n')),Object(l.b)("h2",{id:"technical-analysis"},"Technical Analysis"),Object(l.b)("p",null,"The ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"ta"))," class implements the PineScript v5 ",Object(l.b)("inlineCode",{parentName:"p"},"ta.*")," functions. Pine works bar by bar, here every function takes whole series (arrays, one value per candle, oldest first) and returns a series of the same length that can be plotted directly. Values are ",Object(l.b)("inlineCode",{parentName:"p"},"null")," while the period is not filled yet, and ",Object(l.b)("inlineCode",{parentName:"p"},"null")," inputs propagate like Pine's ",Object(l.b)("inlineCode",{parentName:"p"},"na"),". Pine functions that read ",Object(l.b)("inlineCode",{parentName:"p"},"high"),", ",Object(l.b)("inlineCode",{parentName:"p"},"low"),", ",Object(l.b)("inlineCode",{parentName:"p"},"close")," or ",Object(l.b)("inlineCode",{parentName:"p"},"volume")," implicitly take those arrays as arguments."),Object(l.b)("p",null,"Functions returning several series (",Object(l.b)("inlineCode",{parentName:"p"},"bb"),", ",Object(l.b)("inlineCode",{parentName:"p"},"kc"),", ",Object(l.b)("inlineCode",{parentName:"p"},"macd"),", ",Object(l.b)("inlineCode",{parentName:"p"},"stoch"),", ",Object(l.b)("inlineCode",{parentName:"p"},"dmi"),", ",Object(l.b)("inlineCode",{parentName:"p"},"supertrend"),") return an array that also has named properties: ",Object(l.b)("inlineCode",{parentName:"p"},"const [middle, upper, lower] = ta.bb(closes, 20, 2)")," or ",Object(l.b)("inlineCode",{parentName:"p"},"ta.bb(closes, 20, 2).upper"),"."),Object(l.b)("h3",{id:"helper-functions"},"Helper Functions"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"highest(values, length)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"lowest(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Highest / lowest value over ",Object(l.b)("inlineCode",{parentName:"td"},"length")," bars")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"highestbars(values, length)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"lowestbars(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Offset (0 or negative) to the highest / lowest bar")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"max(values)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"min(values)")),Object(l.b)("td",{parentName:"tr",align:null},"All-time high / low up to each bar")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"range(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"highest - lowest"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"change(values, length = 1)")),Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"values[i] - values[i - length]"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"mom(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Momentum")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"roc(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Rate of change, %")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"cum(values)")),Object(l.b)("td",{parentName:"tr",align:null},"Running total")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"rising(values, length)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"falling(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Value is above / below each of the previous ",Object(l.b)("inlineCode",{parentName:"td"},"length")," values")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"variance(values, length, biased = true)")),Object(l.b)("td",{parentName:"tr",align:null},"Variance")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"stdev(values, length, biased = true)")),Object(l.b)("td",{parentName:"tr",align:null},"Standard deviation")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"dev(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Mean absolute deviation")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"median(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Median")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"mode(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Most frequent value (the smallest one on a tie)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"percentile_linear_interpolation(values, length, percentage)")),Object(l.b)("td",{parentName:"tr",align:null},"Percentile, linear interpolation between the two nearest ranks")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"percentile_nearest_rank(values, length, percentage)")),Object(l.b)("td",{parentName:"tr",align:null},"Percentile, nearest rank method")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"percentrank(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"% of the previous ",Object(l.b)("inlineCode",{parentName:"td"},"length")," values less than or equal to the current one")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"correlation(series1, series2, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Correlation coefficient")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"crossover(series1, series2)")),Object(l.b)("td",{parentName:"tr",align:null},"Upward crossover (either argument can be a number: ",Object(l.b)("inlineCode",{parentName:"td"},"ta.crossover(rsi, 70)"),")")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"crossunder(series1, series2)")),Object(l.b)("td",{parentName:"tr",align:null},"Downward crossover")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"cross(series1, series2)")),Object(l.b)("td",{parentName:"tr",align:null},"Any crossover")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"barssince(condition)")),Object(l.b)("td",{parentName:"tr",align:null},"Bars since ",Object(l.b)("inlineCode",{parentName:"td"},"condition")," (array of booleans) was last true")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"valuewhen(condition, values, occurrence = 0)")),Object(l.b)("td",{parentName:"tr",align:null},"Value of ",Object(l.b)("inlineCode",{parentName:"td"},"values")," when ",Object(l.b)("inlineCode",{parentName:"td"},"condition")," was true ",Object(l.b)("inlineCode",{parentName:"td"},"occurrence")," times ago")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"pivothigh(values, leftbars, rightbars)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"pivotlow(...)")),Object(l.b)("td",{parentName:"tr",align:null},"Pivot value on the confirming bar, the pivot itself is ",Object(l.b)("inlineCode",{parentName:"td"},"rightbars")," bars earlier")))),Object(l.b)("h3",{id:"moving-averages"},"Moving Averages"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"sma(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Simple moving average")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"ema(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Exponential moving average")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"rma(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Wilder's moving average (used by RSI / ATR)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"wma(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Weighted moving average")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"hma(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Hull moving average")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"alma(values, length, offset = 0.85, sigma = 6, floor = false)")),Object(l.b)("td",{parentName:"tr",align:null},"Arnaud Legoux moving average")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"swma(values)")),Object(l.b)("td",{parentName:"tr",align:null},"Symmetrically weighted moving average over 4 bars")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"vwma(values, volumes, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Volume weighted moving average")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"linreg(values, length, offset = 0)")),Object(l.b)("td",{parentName:"tr",align:null},"Linear regression curve")))),Object(l.b)("h3",{id:"oscillators"},"Oscillators"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"rsi(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Relative Strength Index")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"cmo(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Chande Momentum Oscillator")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"cog(values, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Center of Gravity")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"mfi(values, volumes, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Money Flow Index (",Object(l.b)("inlineCode",{parentName:"td"},"values")," is usually hlc3)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"tsi(values, shortLength, longLength)")),Object(l.b)("td",{parentName:"tr",align:null},"True Strength Index, -1..1")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"wpr(highs, lows, closes, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Williams %R")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"stoch(highs, lows, closes, period = 14, smoothK = 3, smoothD = 3)")),Object(l.b)("td",{parentName:"tr",align:null},"Stochastic ",Object(l.b)("inlineCode",{parentName:"td"},"[k, d]"),", ",Object(l.b)("inlineCode",{parentName:"td"},"smoothK = 1")," gives the raw value")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"cci(values, length)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"cci(highs, lows, closes, length)")),Object(l.b)("td",{parentName:"tr",align:null},"Commodity Channel Index (of the typical price for the second form)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"macd(values, fast = 12, slow = 26, signalPeriod = 9)")),Object(l.b)("td",{parentName:"tr",align:null},"MACD ",Object(l.b)("inlineCode",{parentName:"td"},"[macd, signal, histogram]"))))),Object(l.b)("h3",{id:"bands-and-channels"},"Bands and Channels"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"bb(values, length = 20, mult = 2)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"bollinger(...)")),Object(l.b)("td",{parentName:"tr",align:null},"Bollinger Bands ",Object(l.b)("inlineCode",{parentName:"td"},"[middle, upper, lower]"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"bbw(values, length, mult)")),Object(l.b)("td",{parentName:"tr",align:null},"Bollinger Bands width ",Object(l.b)("inlineCode",{parentName:"td"},"(upper - lower) / middle"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"kc(values, highs, lows, closes, length = 20, mult = 2, useTrueRange = true)")),Object(l.b)("td",{parentName:"tr",align:null},"Keltner Channels ",Object(l.b)("inlineCode",{parentName:"td"},"[middle, upper, lower]"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"kcw(values, highs, lows, closes, length, mult, useTrueRange)")),Object(l.b)("td",{parentName:"tr",align:null},"Keltner Channels width ",Object(l.b)("inlineCode",{parentName:"td"},"(upper - lower) / middle"))))),Object(l.b)("h3",{id:"trend-and-volatility"},"Trend and Volatility"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"tr(highs, lows, closes, handleNa = false)")),Object(l.b)("td",{parentName:"tr",align:null},"True range (",Object(l.b)("inlineCode",{parentName:"td"},"handleNa = true")," gives ",Object(l.b)("inlineCode",{parentName:"td"},"high - low")," on the first bar)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"atr(highs, lows, closes, period = 14)")),Object(l.b)("td",{parentName:"tr",align:null},"Average True Range")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"dmi(highs, lows, closes, diLength = 14, adxSmoothing = 14)")),Object(l.b)("td",{parentName:"tr",align:null},"Directional Movement ",Object(l.b)("inlineCode",{parentName:"td"},"[plus, minus, adx]"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"adx(highs, lows, closes, period = 14)")),Object(l.b)("td",{parentName:"tr",align:null},"Average Directional Index")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"sar(highs, lows, closes, start = 0.02, inc = 0.02, max = 0.2)")),Object(l.b)("td",{parentName:"tr",align:null},"Parabolic SAR")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"supertrend(highs, lows, closes, factor = 3, atrPeriod = 10)")),Object(l.b)("td",{parentName:"tr",align:null},"Supertrend ",Object(l.b)("inlineCode",{parentName:"td"},"[supertrend, direction]"),", direction ",Object(l.b)("inlineCode",{parentName:"td"},"-1")," = up trend, ",Object(l.b)("inlineCode",{parentName:"td"},"1")," = down trend")))),Object(l.b)("h3",{id:"volume"},"Volume"),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"vwap(values, volumes, anchor?)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"vwap(highs, lows, closes, volumes, anchor?)")),Object(l.b)("td",{parentName:"tr",align:null},"Cumulative VWAP (of the typical price for the second form), restarts on bars where ",Object(l.b)("inlineCode",{parentName:"td"},"anchor")," (array of booleans) is true")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"obv(closes, volumes)")),Object(l.b)("td",{parentName:"tr",align:null},"On Balance Volume")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"accdist(highs, lows, closes, volumes)")),Object(l.b)("td",{parentName:"tr",align:null},"Accumulation / Distribution")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"iii(highs, lows, closes, volumes)")),Object(l.b)("td",{parentName:"tr",align:null},"Intraday Intensity Index")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"pvt(closes, volumes)")),Object(l.b)("td",{parentName:"tr",align:null},"Price-Volume Trend")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"nvi(closes, volumes)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"pvi(closes, volumes)")),Object(l.b)("td",{parentName:"tr",align:null},"Negative / Positive Volume Index")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"wad(highs, lows, closes)")),Object(l.b)("td",{parentName:"tr",align:null},"Williams Accumulation / Distribution")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"wvad(opens, highs, lows, closes, volumes)")),Object(l.b)("td",{parentName:"tr",align:null},"Williams Variable Accumulation / Distribution")))),Object(l.b)("h3",{id:"orderbook-helpers"},"Orderbook Helpers"),Object(l.b)("p",null,Object(l.b)("inlineCode",{parentName:"p"},"orderbook")," is ",Object(l.b)("inlineCode",{parentName:"p"},"source.orderbook()")," or ",Object(l.b)("inlineCode",{parentName:"p"},"source.depths()[i].data"),": ",Object(l.b)("inlineCode",{parentName:"p"},"{asks: {price: quantity}, bids: {price: quantity}}"),"."),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"orderbookSide(orderbook, side)")),Object(l.b)("td",{parentName:"tr",align:null},"One side (",Object(l.b)("inlineCode",{parentName:"td"},"'bids'")," / ",Object(l.b)("inlineCode",{parentName:"td"},"'asks'"),") as ",Object(l.b)("inlineCode",{parentName:"td"},"[[price, quantity], ...]")," sorted best price first")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"bestBid(orderbook)")," / ",Object(l.b)("inlineCode",{parentName:"td"},"bestAsk(orderbook)")),Object(l.b)("td",{parentName:"tr",align:null},"Best bid / ask price (",Object(l.b)("inlineCode",{parentName:"td"},"null")," for an empty book)")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"sumBids(orderbook, percent, price, inQuote)")),Object(l.b)("td",{parentName:"tr",align:null},"Cumulative bid quantity between ",Object(l.b)("inlineCode",{parentName:"td"},"price")," (default: best bid) and ",Object(l.b)("inlineCode",{parentName:"td"},"price * (1 - percent / 100)"),", ",Object(l.b)("inlineCode",{parentName:"td"},"inQuote = true")," sums ",Object(l.b)("inlineCode",{parentName:"td"},"price * quantity"))),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"sumAsks(orderbook, percent, price, inQuote)")),Object(l.b)("td",{parentName:"tr",align:null},"Cumulative ask quantity between ",Object(l.b)("inlineCode",{parentName:"td"},"price")," (default: best ask) and ",Object(l.b)("inlineCode",{parentName:"td"},"price * (1 + percent / 100)"))))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const candles = source.ohlcv();\nconst closes = candles.map(c => c.close);\nconst rsiValues = ta.rsi(closes, 14);\nplot.lineSeries("RSI", rsiValues, "#00C8FF");\n')),Object(l.b)("h2",{id:"alerts"},"Alerts"),Object(l.b)("p",null,"Users can create alerts for your indicator from the ",Object(l.b)("strong",{parentName:"p"},"Alerts")," tab in the indicator settings on the chart.\nAlerts are checked on the server side regularly, notifications are sent to ",Object(l.b)("strong",{parentName:"p"},"Telegram")," and/or a user defined ",Object(l.b)("strong",{parentName:"p"},"Webhook URL"),"."),Object(l.b)("h3",{id:"alert-settings--alertinput"},"Alert Settings — ",Object(l.b)("inlineCode",{parentName:"h3"},"alertInput")),Object(l.b)("p",null,"The ",Object(l.b)("strong",{parentName:"p"},Object(l.b)("inlineCode",{parentName:"strong"},"alertInput"))," class declares alert settings. It works exactly like ",Object(l.b)("inlineCode",{parentName:"p"},"input"),", but the fields are shown in the ",Object(l.b)("strong",{parentName:"p"},"Alerts")," tab and every user configures their own values for their alert."),Object(l.b)("p",null,"Scripts without ",Object(l.b)("inlineCode",{parentName:"p"},"alertInput")," declarations can not be used for alerts."),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"number(title, defaultValue, min, max, step)")),Object(l.b)("td",{parentName:"tr",align:null},"Numeric input")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"range(title, defaultValue, min, max, step)")),Object(l.b)("td",{parentName:"tr",align:null},"Range input")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"select(title, defaultValue, values)")),Object(l.b)("td",{parentName:"tr",align:null},"Dropdown selection")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"color(title, defaultValue)")),Object(l.b)("td",{parentName:"tr",align:null},"Color picker")),Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"boolean(title, defaultValue)")),Object(l.b)("td",{parentName:"tr",align:null},"Boolean input")))),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const alertVolumeThreshold = alertInput.number("Volume threshold", 1000, 0, 10000000, 1);\n')),Object(l.b)("h3",{id:"firing-alerts--alertfire"},"Firing Alerts — ",Object(l.b)("inlineCode",{parentName:"h3"},"alert.fire")),Object(l.b)("table",null,Object(l.b)("thead",{parentName:"table"},Object(l.b)("tr",{parentName:"thead"},Object(l.b)("th",{parentName:"tr",align:null},"Method"),Object(l.b)("th",{parentName:"tr",align:null},"Description"))),Object(l.b)("tbody",{parentName:"table"},Object(l.b)("tr",{parentName:"tbody"},Object(l.b)("td",{parentName:"tr",align:null},Object(l.b)("inlineCode",{parentName:"td"},"fire(title, message, date)")),Object(l.b)("td",{parentName:"tr",align:null},"Fires an alert notification")))),Object(l.b)("ul",null,Object(l.b)("li",{parentName:"ul"},"All three parameters are ",Object(l.b)("strong",{parentName:"li"},"required")," — a call with a missing ",Object(l.b)("inlineCode",{parentName:"li"},"title"),", ",Object(l.b)("inlineCode",{parentName:"li"},"message")," or ",Object(l.b)("inlineCode",{parentName:"li"},"date")," is skipped (a warning is written to the log)."),Object(l.b)("li",{parentName:"ul"},"Keep the alert ",Object(l.b)("strong",{parentName:"li"},"title constant"),", do not generate unique titles. Each title fires only once per run (max ",Object(l.b)("strong",{parentName:"li"},"20")," titles per run)."),Object(l.b)("li",{parentName:"ul"},Object(l.b)("inlineCode",{parentName:"li"},"date")," is the candle ",Object(l.b)("inlineCode",{parentName:"li"},"startTime")," the signal belongs to: the same title notifies again only when it fires for a ",Object(l.b)("strong",{parentName:"li"},"newer")," candle. Signals from candles older than the moment the user created their alert are ignored (anti-spam).")),Object(l.b)("p",null,Object(l.b)("strong",{parentName:"p"},"Example:")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'if (candle.volume > alertVolumeThreshold) {\n alert.fire("High volume", `vol ${candle.volume}`, candle.startTime);\n}\n')),Object(l.b)("h3",{id:"shape-alerts--plotshapealert"},"Shape Alerts — ",Object(l.b)("inlineCode",{parentName:"h3"},"plot.(...).alert(...)")),Object(l.b)("p",null,"All geometric shapes (",Object(l.b)("inlineCode",{parentName:"p"},"box"),", ",Object(l.b)("inlineCode",{parentName:"p"},"square"),", ",Object(l.b)("inlineCode",{parentName:"p"},"diamond"),", ",Object(l.b)("inlineCode",{parentName:"p"},"triangle"),", ",Object(l.b)("inlineCode",{parentName:"p"},"circle"),", ",Object(l.b)("inlineCode",{parentName:"p"},"straightLine"),", ",Object(l.b)("inlineCode",{parentName:"p"},"horizontalLine"),", ",Object(l.b)("inlineCode",{parentName:"p"},"verticalLine"),", ",Object(l.b)("inlineCode",{parentName:"p"},"text"),") return a handle with an ",Object(l.b)("inlineCode",{parentName:"p"},".alert()")," method.\nThe alert fires with the shape's ",Object(l.b)("inlineCode",{parentName:"p"},"date1")," automatically, so the anti-spam logic works without extra code — this is the recommended way for per-candle signals."),Object(l.b)("p",null,"Two forms:"),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'// simple: fires every time the shape is plotted for a new date\nplot.box(...).alert("High volume", `vol ${candle.volume}`);\n\n// callback: return nothing to skip, [title, message] to fire\nplot.box(...).alert((shape) => {\n if (candle.volume > alertVolumeThreshold)\n alert.fire("High volume", `vol ${candle.volume}`, candle.startTime);\n});\n')),Object(l.b)("p",null,"The callback receives the shape data (",Object(l.b)("inlineCode",{parentName:"p"},"date1"),", ",Object(l.b)("inlineCode",{parentName:"p"},"price1"),", ",Object(l.b)("inlineCode",{parentName:"p"},"date2"),", ",Object(l.b)("inlineCode",{parentName:"p"},"price2"),", ...)."),Object(l.b)("h3",{id:"series-alerts--plotseriesalert"},"Series Alerts — ",Object(l.b)("inlineCode",{parentName:"h3"},"plot.(...).alert(...)")),Object(l.b)("p",null,"Data series (",Object(l.b)("inlineCode",{parentName:"p"},"lineSeries"),", ",Object(l.b)("inlineCode",{parentName:"p"},"barSeries"),", ",Object(l.b)("inlineCode",{parentName:"p"},"candleSeries"),", ",Object(l.b)("inlineCode",{parentName:"p"},"areaSeries"),", ",Object(l.b)("inlineCode",{parentName:"p"},"bandSeries"),") also return the ",Object(l.b)("inlineCode",{parentName:"p"},".alert()")," handle.\nThe callback receives the ",Object(l.b)("strong",{parentName:"p"},"latest data point")," and the latest candle date, the alert fires with that date."),Object(l.b)("ul",null,Object(l.b)("li",{parentName:"ul"},Object(l.b)("inlineCode",{parentName:"li"},"lineSeries")," / ",Object(l.b)("inlineCode",{parentName:"li"},"barSeries")," / ",Object(l.b)("inlineCode",{parentName:"li"},"areaSeries")," — the callback receives the last ",Object(l.b)("strong",{parentName:"li"},"value")," (a number)"),Object(l.b)("li",{parentName:"ul"},Object(l.b)("inlineCode",{parentName:"li"},"candleSeries")," — the callback receives the last candle ",Object(l.b)("inlineCode",{parentName:"li"},"{open, close, high, low}")),Object(l.b)("li",{parentName:"ul"},Object(l.b)("inlineCode",{parentName:"li"},"bandSeries")," — the callback receives the last ",Object(l.b)("inlineCode",{parentName:"li"},"{upper, lower}"))),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'plot.lineSeries("RSI", rsiValues, color).alert((rsi, date) => {\n if (rsi > alertRsiThreshold) alert.fire("RSI high", `RSI ${rsi.toFixed(1)}`, latestCandle.startTime);\n});\n')),Object(l.b)("p",null,"A condition that stays true notifies once per ",Object(l.b)("strong",{parentName:"p"},"new candle"),". For one-time signals use crossover-style conditions (",Object(l.b)("inlineCode",{parentName:"p"},"ta.crossover"),")."),Object(l.b)("h3",{id:"full-example"},"Full Example"),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const title = "Volume alerts";\nconst description = "Shows boxes on high-volume candles";\ndefine(title, description, "onchart", true, true);\n\nconst color = style.color("Color", "#00C8FF");\nconst volumeThreshold = input.number("Volume threshold", 1000, 0, 10000000, 1);\nconst alertVolumeThreshold = alertInput.number("Alert volume threshold", 5000, 0, 10000000, 1);\nconst alertCloseThreshold = alertInput.number("Alert close threshold", 100000, 0, 10000000, 1);\n\nconst candles = source.ohlcv();\nconst latestCandle = candles.at(-1);\nconst closes = candles.map(c => c.close);\n\n// series alert: checks the latest close\nplot.lineSeries("Closes", closes, color).alert((close, date) => {\n if (close > alertCloseThreshold) alert.fire("Close above threshold", `close ${close}`, latestCandle.startTime);\n});\n\nfor (let i = 0; i < candles.length; i++) {\n const candle = candles[i];\n\n if (candle.volume > volumeThreshold) {\n const right = i < candles.length - 1 ? candles[i + 1].startTime : candle.startTime + 60000;\n\n // shape alert: checked against the user\'s alert threshold\n plot.box(`Volume ${i}`, candle.startTime, candle.high, right, candle.low, "#00C8FF", 0.3)\n .alert(() => {\n if (candle.volume > alertVolumeThreshold)\n alert.fire("High volume", `vol ${candle.volume}`, candle.startTime);\n });\n }\n}\n')),Object(l.b)("h2",{id:"examples-of-indicators"},"Examples of indicators"),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const title = "Your first indicator";\nconst description = "Shows closes lines on the chart";\nconst indicatorType = "onchart";\n\ndefine(title, description, indicatorType, true, true);\n\nconst color = input.color("Line Color", "#00C8FF");\nconst candles = source.ohlcv();\nconst closes = candles.map(c => c.close);\nplot.lineSeries("Closes", closes, color);\n\n')),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'define("Example Indicator", "Demonstrates basic plotting", "onchart", true, true);\n\nconst color = input.color("Line Color", "#00FF99");\nconst candles = source.ohlcv();\nconst closes = candles.map(c => c.close);\n\nconst ema20 = ta.ema(closes, 20);\nplot.lineSeries("EMA 20", ema20, color);\n\n')),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const title = "Colored Volume Bars"; \nconst description = "Volumes colored based on candle direction."; \nconst indicatorType = "onchart"; \nconst isPublic = true; \nconst isPublicCode = true; \n\ndefine(title, description, indicatorType, isPublic, isPublicCode); \n\n// Get OHLC candles\nconst candles = source.ohlcv(); \n\nconst colorUp = style.color("Color 1", "#2BB462"); // Greenish color for rising prices\nconst colorDown = style.color("Color 2", "#FB4C51"); // Reddish color for falling prices\nconst volumes = candles.map(candle => candle.volume); \n\nplot.barSeries("Volume", volumes, (value, index) => {\n const currentCandle = candles[index]; \n return currentCandle.close >= currentCandle.open ? colorUp : colorDown; \n});\n\n')),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},"define('Coinbase Premium Index', 'Coinbase Premium Index', 'offchart', true, false);\n\nconst color1 = style.color(\"Color 1\", \"#2BB462\");\nconst color2 = style.color(\"Color 2\", \"#FB4C51\");\n\n// Get OHLC candles\nconst cbCandles = source.ohlcv('coinbase', 'BTC-USD');\nconst binanceCandles = source.ohlcv('binance', 'BTCUSDT');\n\n// Extract closes\nconst cbCloses = cbCandles.map(c => c.close);\nconst binanceCloses = binanceCandles.map(c => c.close);\n\n// --- Coinbase Premium Index calculation ---\nconst premiumPercent = cbCloses.map((cb, i) => {\n const b = binanceCloses[i];\n return ((cb - b) / b) * 100;\n});\n\n// Plot premium index\nplot.barSeries(\"Premium %\", premiumPercent, ((d) => d > 0 ? color1 : color2)) // Percentage difference\n\n")),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const title = "Candles";\nconst description = "Simple candles draw";\nconst indicatorType = "offchart";\nconst isPublic = true;\nconst isPublicCode = true;\ndefine(title, description, indicatorType, isPublic, isPublicCode);\n\nconst colorBuy = style.color("Buy Color", "#2BB462");\nconst colorSell = style.color("Sell Color", "#7a3d3f");\nconst candles = source.ohlcv();\nconst closes = candles.map(c => c.close);\nconst opens = candles.map(c => c.open);\nconst highs = candles.map(c => c.high);\nconst lows = candles.map(c => c.low);\nplot.candleSeries(\n "Candles",\n opens, closes, highs, lows,\n (v, i) => {\n return opens?.[i] > closes?.[i] ? colorBuy : colorSell\n }\n);\n\n')),Object(l.b)("p",null,'Order Book Pressure (bid / ask depth imbalance bands around the price, like the "Order Book Pressure" overlays of trading terminals):'),Object(l.b)("pre",null,Object(l.b)("code",{parentName:"pre",className:"language-js"},'const title = "Order Book Pressure";\nconst description = "Bid / ask depth imbalance bands around the price";\ndefine(title, description, "onchart", true, true);\n\nconst minBidRatio = input.number("Min bid ratio %", 20, 0, 100, 1);\nconst minAskRatio = input.number("Min ask ratio %", 20, 0, 100, 1);\nconst depthFrom = input.select("Depth from", "0%", ["0%", "1%", "2.5%", "5%"]);\nconst depthTo = input.select("Depth to", "5%", ["1%", "2.5%", "5%", "10%"]);\n\nconst bidColor = style.color("Bid fill color", "#a5d6a7");\nconst askColor = style.color("Ask fill color", "#f1627e");\nconst extremeColor = style.color("Extreme color", "#ffff00");\nconst highlight = style.boolean("Highlight extremes", true);\nconst showRef = style.boolean("Show reference lines", true);\n\nconst alertMinRatio = alertInput.number("Alert min |ratio| %", 40, 0, 100, 1);\n\nconst bounds = [1, 2.5, 5, 10]; // depth bands in % from the close\nconst from = parseFloat(depthFrom);\nconst to = parseFloat(depthTo);\n\nconst candles = source.ohlcv();\nconst depths = source.depths();\nconst liveBook = source.orderbook();\n\n// orderbook of every candle: depths()[i] is the snapshot of candles[i] (the biggest limit orders), the latest candle uses the live book\nconst lastIndex = candles.length - 1;\nconst books = candles.map((c, i) => {\n if (!c) return null;\n if (i === lastIndex && ta.bestBid(liveBook)) return liveBook;\n return depths[i]?.data || null;\n});\nconst closes = candles.map(c => c ? c.close : null);\n\n// imbalance ratio of every band in % (positive = bids dominate, negative = asks dominate)\nconst ratios = bounds.map((upper, b) => {\n const lower = b === 0 ? 0 : bounds[b - 1];\n return candles.map((c, i) => {\n const book = books[i];\n if (!c || !book) return null;\n const bid = ta.sumBids(book, upper, c.close) - ta.sumBids(book, lower, c.close);\n const ask = ta.sumAsks(book, upper, c.close) - ta.sumAsks(book, lower, c.close);\n const total = bid + ask;\n return total > 0 ? ((bid - ask) / total) * 100 : 0;\n });\n});\n\n// a stronger ratio -> a more opaque band, extreme ratios get the extreme color\nconst bandOpacity = (ratio) => Math.min(0.95, Math.abs(ratio || 0) / 70 * 0.95);\nconst bandColor = (ratio) => highlight && Math.abs(ratio || 0) >= 63 ? extremeColor : (ratio >= 0 ? bidColor : askColor);\n\nbounds.forEach((upper, b) => {\n const lower = b === 0 ? 0 : bounds[b - 1];\n if (from > lower || upper > to) return; // the band is outside of the selected depth range\n\n const bidUpper = closes.map(c => c != null ? c * (1 - lower / 100) : null);\n const bidLower = closes.map(c => c != null ? c * (1 - upper / 100) : null);\n const askLower = closes.map(c => c != null ? c * (1 + lower / 100) : null);\n const askUpper = closes.map(c => c != null ? c * (1 + upper / 100) : null);\n\n if (showRef) {\n plot.lineSeries(`Bid ${upper}%`, bidLower, bidColor, 0.4);\n plot.lineSeries(`Ask ${upper}%`, askUpper, askColor, 0.4);\n }\n\n // bid pressure: the band is filled only on the candles where bids dominate by more than the min ratio\n plot.bandSeries(\n `Bid pressure ${lower}-${upper}%`,\n bidUpper.map((v, i) => ratios[b][i] >= minBidRatio ? v : null),\n bidLower,\n (upper, lower, i) => bandColor(ratios[b][i]),\n (upper, lower, i) => bandOpacity(ratios[b][i])\n );\n\n // ask pressure\n plot.bandSeries(\n `Ask pressure ${lower}-${upper}%`,\n askUpper.map((v, i) => ratios[b][i] <= -minAskRatio ? v : null),\n askLower,\n (upper, lower, i) => bandColor(ratios[b][i]),\n (upper, lower, i) => bandOpacity(ratios[b][i])\n );\n\n // alert on the latest candle\n const latestRatio = ratios[b][lastIndex];\n if (latestRatio != null && Math.abs(latestRatio) >= alertMinRatio) {\n alert.fire(`Pressure ${lower}-${upper}%`, `ratio ${latestRatio.toFixed(1)}%`, candles[lastIndex].startTime);\n }\n});\n\n')))}void 0!==p&&p&&p===Object(p)&&Object.isExtensible(p)&&!Object.prototype.hasOwnProperty.call(p,"__filemeta")&&Object.defineProperty(p,"__filemeta",{configurable:!0,value:{name:"MDXContent",filename:"src/components/en/Scripting.mdx"}}),p.isMDXComponent=!0}}]); //# sourceMappingURL=component---src-components-en-scripting-mdx-fd4d27433aca5367118c.js.map