(window.webpackJsonp=window.webpackJsonp||[]).push([[53],{tDBA:function(e,t,n){"use strict";n.r(t),n.d(t,"_frontmatter",(function(){return o})),n.d(t,"default",(function(){return p}));var a=n("Fcif"),l=n("dV/x"),r=(n("mXGw"),n("/FXl")),i=n("TjRS"),b=n("wCAC");n("l1C2");const c=["components"],o={};void 0!==o&&o&&o===Object(o)&&Object.isExtensible(o)&&!Object.prototype.hasOwnProperty.call(o,"__filemeta")&&Object.defineProperty(o,"__filemeta",{configurable:!0,value:{name:"_frontmatter",filename:"src/components/fr/Scripting.mdx"}});const d={_frontmatter:o},s=i.a;function p(e){let{components:t}=e,n=Object(l.a)(e,c);return Object(r.b)(s,Object(a.a)({},d,n,{components:t,mdxType:"MDXLayout"}),Object(r.b)("h1",{id:"api-dindicateurs-personnalisés"},"API d'indicateurs personnalisés"),Object(r.b)(b.e,{mdxType:"Lead"},"Écrivez vos propres indicateurs en JavaScript, exécutez-les dans un bac à sable sécurisé à côté du graphique et partagez-les avec les autres utilisateurs. L'API vous donne accès aux données de marché (chandeliers, carnets d'ordres, clusters, liquidations, données des dérivés), à des primitives de dessin, à des fonctions d'analyse technique et aux alertes."),Object(r.b)("h2",{id:"générer-des-indicateurs-avec-lia"},"Générer des indicateurs avec l'IA"),Object(r.b)("p",null,"L'ensemble de l'API est également disponible sous forme d'un seul fichier texte pour les modèles de langage : ",Object(r.b)("a",{parentName:"p",href:"../../public/AI-scripting.txt"},"AI-scripting.txt"),". Joignez-le à ChatGPT, Claude, Gemini ou tout autre LLM et décrivez l'indicateur que vous souhaitez."),Object(r.b)("p",null,"Exemple de requête :"),Object(r.b)("pre",null,Object(r.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(r.b)("h2",{id:"bac-à-sable-et-limites"},"Bac à sable et limites"),Object(r.b)(b.b,{type:"warning",title:"Limites actuelles",mdxType:"Callout"},Object(r.b)("ul",null,Object(r.b)("li",{parentName:"ul"},"Les scripts s'exécutent dans un bac à sable JavaScript sécurisé."),Object(r.b)("li",{parentName:"ul"},"Un script a une limite de temps d'exécution de ",Object(r.b)("inlineCode",{parentName:"li"},"1500ms"),"."),Object(r.b)("li",{parentName:"ul"},"Les tableaux de données retournés sont plafonnés à ",Object(r.b)("strong",{parentName:"li"},"5000")," éléments, un indicateur ne peut donc pas remonter plus loin que ",Object(r.b)("strong",{parentName:"li"},"5000 chandeliers"),"."),Object(r.b)("li",{parentName:"ul"},"Jusqu'à ",Object(r.b)("strong",{parentName:"li"},"8 requêtes de sources de données")," sont autorisées dans un même indicateur. Une requête au-delà de la limite lève une erreur et l'exécution s'arrête avec le problème \"too much sources\"."),Object(r.b)("li",{parentName:"ul"},"Jusqu'à ",Object(r.b)("strong",{parentName:"li"},"3 requêtes HTTP")," (",Object(r.b)("inlineCode",{parentName:"li"},"http.get")," / ",Object(r.b)("inlineCode",{parentName:"li"},"http.post"),") sont autorisées dans une même exécution, voir ",Object(r.b)("strong",{parentName:"li"},"Requêtes HTTP"),"."))),Object(r.b)("h2",{id:"développement-dindicateurs"},"Développement d'indicateurs"),Object(r.b)("p",null,"Le développement d'indicateurs se fait dans un widget séparé, qui peut être placé à côté du graphique sur lequel l'indicateur sera activé."),Object(r.b)(b.f,{src:"../../public/chart_code.png",mdxType:"Screenshot"}),Object(r.b)("h2",{id:"créer-un-indicateur"},"Créer un indicateur"),Object(r.b)("h3",{id:"definetitle-description-type-ispublic-ispubliccode-options"},Object(r.b)("inlineCode",{parentName:"h3"},"define(title, description, type, isPublic, isPublicCode, options)")),Object(r.b)("p",null,"Enregistre l'indicateur dans le système."),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Paramètre"),Object(r.b)("th",{parentName:"tr",align:null},"Type"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"title")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"string")),Object(r.b)("td",{parentName:"tr",align:null},"Nom de l'indicateur")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"description")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"string")),Object(r.b)("td",{parentName:"tr",align:null},"Courte description")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"type")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"'onchart'")," ","|"," ",Object(r.b)("inlineCode",{parentName:"td"},"'offchart'")),Object(r.b)("td",{parentName:"tr",align:null},"Emplacement de l'indicateur (sur le graphique ou dans un panneau séparé)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"isPublic")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"true")," ","|"," ",Object(r.b)("inlineCode",{parentName:"td"},"false")),Object(r.b)("td",{parentName:"tr",align:null},"Si l'indicateur est public")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"isPublicCode")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"true")," ","|"," ",Object(r.b)("inlineCode",{parentName:"td"},"false")),Object(r.b)("td",{parentName:"tr",align:null},"Si le code source est public")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"options")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"object")," (optionnel)"),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"{ fullRange, warmup }"),", voir ci-dessous")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.b)("code",{parentName:"pre",className:"language-js"},'define("My Indicator", "Shows EMA line", "onchart", true, false);\n')),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Plage de calcul.")," Par défaut un script ne calcule que la partie du graphique qui en a besoin : les chandeliers chargés en remontant dans l'historique, ou les derniers chandeliers lors d'un rafraîchissement, plus ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"warmup"))," chandeliers d'historique avant eux (500 par défaut), nécessaires aux calculs mais non retournés. Déclarez ",Object(r.b)("inlineCode",{parentName:"p"},"warmup")," si votre indicateur a besoin de plus ou moins d'historique, par ex. ",Object(r.b)("inlineCode",{parentName:"p"},"{ warmup: 200 }")," pour une EMA(200). Un script dont les valeurs ou les formes dépendent de ",Object(r.b)("strong",{parentName:"p"},"toute la plage visible")," (VWAP depuis le premier chandelier visible, delta cumulé, un libellé au maximum de la fenêtre, des niveaux tracés sur tout le graphique) doit déclarer ",Object(r.b)("inlineCode",{parentName:"p"},"{ fullRange: true }"),", sinon ses résultats sont faux après le chargement d'historique. Le mode est lu dans ",Object(r.b)("inlineCode",{parentName:"p"},"define()")," à chaque enregistrement : un script enregistré sans ",Object(r.b)("inlineCode",{parentName:"p"},"fullRange")," est calculé partiellement, ajoutez donc le drapeau aux scripts cumulatifs existants lorsque vous les modifiez. Les scripts partiels sont aussi chargés par morceaux : quand vous en activez un sur un long historique, le graphique demande d'abord les 500 derniers chandeliers (ou ",Object(r.b)("inlineCode",{parentName:"p"},"warmup")," chandeliers si c'est plus), puis les parties plus anciennes une requête à la fois, de sorte que la partie visible apparaît immédiatement. Les scripts avec ",Object(r.b)("inlineCode",{parentName:"p"},"fullRange")," sont toujours calculés en une seule requête sur toute la plage."),Object(r.b)("pre",null,Object(r.b)("code",{parentName:"pre",className:"language-js"},'define("Window max", "Label at the highest visible high", "onchart", true, true, { fullRange: true });\n')),Object(r.b)("p",null,"Emplacement de l'indicateur pour ",Object(r.b)("strong",{parentName:"p"},"onchart")," et ",Object(r.b)("strong",{parentName:"p"},"offchart")," :"),Object(r.b)(b.f,{src:"../../public/onchart_offchart.png",mdxType:"Screenshot"}),Object(r.b)("h2",{id:"paramètres-dentrée"},"Paramètres d'entrée"),Object(r.b)("p",null,"Les classes ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"input"))," et ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"style"))," permettent de créer des paramètres configurables par l'utilisateur (similaires à ",Object(r.b)("inlineCode",{parentName:"p"},"input()")," dans PineScript)."),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number(title, defaultValue, min, max, step)")),Object(r.b)("td",{parentName:"tr",align:null},"Entrée numérique")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"range(title, defaultValue, min, max, step)")),Object(r.b)("td",{parentName:"tr",align:null},"Entrée de type plage")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"select(title, defaultValue, values)")),Object(r.b)("td",{parentName:"tr",align:null},"Sélection dans une liste déroulante")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"color(title, defaultValue)")),Object(r.b)("td",{parentName:"tr",align:null},"Sélecteur de couleur")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"boolean(title, defaultValue)")),Object(r.b)("td",{parentName:"tr",align:null},"Entrée booléenne")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"tickers(title, defaultValue, exchange)")),Object(r.b)("td",{parentName:"tr",align:null},"Sélection multiple des tickers d'une plateforme (par défaut celle du graphique) ; renvoie les tickers choisis sous forme de tableau")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple (input) pour les paramètres principaux :")),Object(r.b)("pre",null,Object(r.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// paires à inclure / exclure, choisies parmi les tickers de la plateforme du graphique\nconst include = input.tickers("Include pairs", []);\nconst exclude = input.tickers("Exclude pairs", []);\n')),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Ou (style) pour les paramètres visuels :")),Object(r.b)("pre",null,Object(r.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(r.b)(b.f,{src:"../../public/input_style.png",mdxType:"Screenshot"}),Object(r.b)("h2",{id:"sources-de-données"},"Sources de données"),Object(r.b)("p",null,"La classe ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"source"))," récupère les données de marché depuis le serveur. Les méthodes basées sur les chandeliers prennent les mêmes paramètres ; les deux instantanés en direct diffèrent : ",Object(r.b)("inlineCode",{parentName:"p"},"orderbook()")," prend un exchange et un ticker, ",Object(r.b)("inlineCode",{parentName:"p"},"levels()")," un exchange et un objet de réglages."),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Paramètre"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"exchange"),", ",Object(r.b)("inlineCode",{parentName:"td"},"ticker"),", ",Object(r.b)("inlineCode",{parentName:"td"},"timeframe")),Object(r.b)("td",{parentName:"tr",align:null},"Par défaut, ceux du graphique sur lequel l'indicateur est ouvert. Passez d'autres valeurs pour récupérer les données d'un autre marché.")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"lookback")),Object(r.b)("td",{parentName:"tr",align:null},"Nombre de barres d'historique à partir de la date actuelle, ",Object(r.b)("strong",{parentName:"td"},"pas plus de 5000"),".")))),Object(r.b)(b.b,{type:"note",title:"Les données sont alignées sur les chandeliers du graphique",mdxType:"Callout"},Object(r.b)("p",null,Object(r.b)("inlineCode",{parentName:"p"},"ohlcv()"),", ",Object(r.b)("inlineCode",{parentName:"p"},"depths()"),", ",Object(r.b)("inlineCode",{parentName:"p"},"clusters()"),", ",Object(r.b)("inlineCode",{parentName:"p"},"liquidations()")," et ",Object(r.b)("inlineCode",{parentName:"p"},"moreData()")," de l'unité de temps du graphique retournent ",Object(r.b)("strong",{parentName:"p"},"une entrée par chandelier du graphique")," : ",Object(r.b)("inlineCode",{parentName:"p"},"depths()[i]")," est le carnet d'ordres de ",Object(r.b)("inlineCode",{parentName:"p"},"ohlcv()[i]"),", ",Object(r.b)("inlineCode",{parentName:"p"},"moreData()[i]")," contient ses données de dérivés, et ainsi de suite. Lorsqu'une source n'a pas d'enregistrement pour un chandelier (par exemple si l'historique du carnet d'ordres est plus court que celui des chandeliers), l'entrée vaut ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"null"))," : vérifiez-le toujours. Les données demandées avec ",Object(r.b)("inlineCode",{parentName:"p"},"lookback")," ou pour une autre unité de temps sont retournées telles quelles et doivent être associées par ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"startTime")),". ",Object(r.b)("inlineCode",{parentName:"p"},"ohlcv()")," du marché du graphique ne contient que les chandeliers déjà chargés par le serveur, jamais ",Object(r.b)("inlineCode",{parentName:"p"},"null")," ; tant qu'un historique plus ancien se charge, l'indicateur est recalculé automatiquement.")),Object(r.b)("h3",{id:"ohlcvexchange-ticker-timeframe-lookback"},Object(r.b)("inlineCode",{parentName:"h3"},"ohlcv(exchange, ticker, timeframe, lookback?)")),Object(r.b)("p",null,"Données des chandeliers."),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"depthsexchange-ticker-timeframe-lookback"},Object(r.b)("inlineCode",{parentName:"h3"},"depths(exchange, ticker, timeframe, lookback?)")),Object(r.b)("p",null,"Instantanés historiques du carnet d'ordres, un par chandelier. Seuls les plus gros ordres limites sont conservés (les données de la heatmap)."),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"clustersexchange-ticker-timeframe-lookback"},Object(r.b)("inlineCode",{parentName:"h3"},"clusters(exchange, ticker, timeframe, lookback?)")),Object(r.b)("p",null,"Données des clusters (footprints) : volume acheteur et vendeur à chaque niveau de prix du chandelier."),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"liquidationsexchange-ticker-timeframe-lookback"},Object(r.b)("inlineCode",{parentName:"h3"},"liquidations(exchange, ticker, timeframe, lookback?)")),Object(r.b)("p",null,"Volume liquidé à chaque niveau de prix du chandelier (marchés futures uniquement). Même format que ",Object(r.b)("inlineCode",{parentName:"p"},"clusters()"),"."),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"moredataexchange-ticker-timeframe-lookback"},Object(r.b)("inlineCode",{parentName:"h3"},"moreData(exchange, ticker, timeframe, lookback?)")),Object(r.b)("p",null,"Données dérivées par chandelier : taux de financement, chandelier d'open interest, ratios long/short (marchés futures uniquement)."),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"orderbookexchange-ticker"},Object(r.b)("inlineCode",{parentName:"h3"},"orderbook(exchange, ticker)")),Object(r.b)("p",null,"Instantané du carnet d'ordres en direct : le carnet complet actuel, non lié aux chandeliers."),Object(r.b)("pre",null,Object(r.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(r.b)("p",null,"Les niveaux ne sont pas triés, utilisez les fonctions de carnet d'ordres de ",Object(r.b)("inlineCode",{parentName:"p"},"ta")," (",Object(r.b)("inlineCode",{parentName:"p"},"ta.bestBid"),", ",Object(r.b)("inlineCode",{parentName:"p"},"ta.sumBids"),", ...) pour travailler avec le carnet.\nLes carnets historiques des chandeliers proviennent de ",Object(r.b)("inlineCode",{parentName:"p"},"depths()")," : ",Object(r.b)("inlineCode",{parentName:"p"},"depths()[i].data")," a le même format, mais seuls les plus grands ordres limites y sont stockés."),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"levelsexchange-settings"},Object(r.b)("inlineCode",{parentName:"h3"},"levels(exchange, settings?)")),Object(r.b)("p",null,"Les niveaux du ",Object(r.b)("strong",{parentName:"p"},"screener de liquidité")," : les gros ordres limite au repos d'un exchange, filtrés avec les mêmes réglages que la page du screener. Données en direct, non liées aux chandeliers. Sans plan, la proximité est limitée à 1 %, la durée de vie à 60 minutes et le résultat à 200 niveaux ; avec un plan jusqu'à 2000 niveaux. Les plus gros ordres viennent en premier. Compte comme une requête de source de données."),Object(r.b)("pre",null,Object(r.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(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Réglages")," (tous optionnels) :"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Réglage"),Object(r.b)("th",{parentName:"tr",align:null},"Défaut"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"tickers")),Object(r.b)("td",{parentName:"tr",align:null},"tous les tickers"),Object(r.b)("td",{parentName:"tr",align:null},"seulement ces tickers de l'exchange")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"exclude")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"[]")),Object(r.b)("td",{parentName:"tr",align:null},"tickers à ignorer")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"type")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'"all"')),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'"all"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"bids"')," ou ",Object(r.b)("inlineCode",{parentName:"td"},'"asks"'))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"nearness")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"5")),Object(r.b)("td",{parentName:"tr",align:null},"distance max au prix courant, en pourcent")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"size")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"100000")),Object(r.b)("td",{parentName:"tr",align:null},"taille min de l'ordre dans la devise de cotation")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"lifespan")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"0")),Object(r.b)("td",{parentName:"tr",align:null},"minutes min pendant lesquelles l'ordre est resté")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"touches"),", ",Object(r.b)("inlineCode",{parentName:"td"},"currentTouches")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"0")),Object(r.b)("td",{parentName:"tr",align:null},"touches min du niveau / de l'ordre courant")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"filled")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"0")),Object(r.b)("td",{parentName:"tr",align:null},"part min exécutée de l'ordre, en pourcent")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"strength"),", ",Object(r.b)("inlineCode",{parentName:"td"},"diversity")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"0")),Object(r.b)("td",{parentName:"tr",align:null},"force (pourcent) et diversité min, comme sur le screener")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"continuousOnly")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"false")),Object(r.b)("td",{parentName:"tr",align:null},"ignorer les ordres qui ont disparu puis sont revenus")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"volume1h")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"0")),Object(r.b)("td",{parentName:"tr",align:null},"volume 1h min du ticker dans la devise de cotation")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"spotAndFutures")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"false")),Object(r.b)("td",{parentName:"tr",align:null},"seulement les tickers présents sur le spot et les futures")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"limit")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"500")),Object(r.b)("td",{parentName:"tr",align:null},"nombre max de niveaux, les plus gros en premier (max 2000)")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"keylevelsexchange-settings"},Object(r.b)("inlineCode",{parentName:"h3"},"keyLevels(exchange, settings?)")),Object(r.b)("p",null,"Les niveaux du ",Object(r.b)("strong",{parentName:"p"},"screener Key levels")," : open, high et low du jour, de la semaine, du mois et de l'année en cours et précédents pour chaque ticker d'un échange, avec la distance de chaque niveau au prix actuel. Données en direct, non liées aux bougies. Sans abonnement, le résultat ne contient que les niveaux à ±1 % du prix (la plage fixe de la page du screener) et au plus 100 tickers ; avec un abonnement, n'importe quelle plage de distance et jusqu'à 1000 tickers. Les tickers dont le niveau est le plus proche viennent en premier. Compte comme une requête de source de données."),Object(r.b)("pre",null,Object(r.b)("code",{parentName:"pre",className:"language-js"},'{\n "BTCUSDT": {\n "price": 79776.57, // prix actuel\n "updatedAt": 1788707880000,\n "bootstrapped": true, // false tant que l\'historique du ticker est encore collecté\n "nearest": 0.08, // distance du niveau le plus proche, en pourcent\n "matches": 5, // niveaux dans la plage de distance\n "daily": { "start": 1788652800000, "open": 79700, "high": 79985, "low": 79710, "openApprox": false,\n "distance": { "open": -0.1, "high": 0.26, "low": -0.08 } }, // pourcent du prix, signé\n "prevDay": { ... }, "weekly": { ... }, "prevWeek": { ... },\n "monthly": { ... }, "prevMonth": { ... }, "yearly": { ... }, "prevYear": null // null : pas encore de données pour la période\n },\n ...\n}\n')),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Paramètres")," (tous optionnels) :"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Paramètre"),Object(r.b)("th",{parentName:"tr",align:null},"Défaut"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"tickers")),Object(r.b)("td",{parentName:"tr",align:null},"tous les tickers"),Object(r.b)("td",{parentName:"tr",align:null},"seulement ces tickers de l'échange")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"exclude")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"[]")),Object(r.b)("td",{parentName:"tr",align:null},"tickers à ignorer")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"periods")),Object(r.b)("td",{parentName:"tr",align:null},"toutes"),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'"daily"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"prevDay"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"weekly"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"prevWeek"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"monthly"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"prevMonth"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"yearly"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"prevYear"'))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"fields")),Object(r.b)("td",{parentName:"tr",align:null},"tous"),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'"open"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"high"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"low"')," : les niveaux à renvoyer")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"distanceFrom"),", ",Object(r.b)("inlineCode",{parentName:"td"},"distanceTo")),Object(r.b)("td",{parentName:"tr",align:null},"aucune"),Object(r.b)("td",{parentName:"tr",align:null},"plage de distance signée au prix, en pourcent (p. ex. ",Object(r.b)("inlineCode",{parentName:"td"},"-2")," et ",Object(r.b)("inlineCode",{parentName:"td"},"2"),") ; seuls les tickers ayant un niveau dans la plage sont renvoyés. Sans abonnement la plage est fixée à ±1 %")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"limit")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"200")),Object(r.b)("td",{parentName:"tr",align:null},"nombre max de tickers, le niveau le plus proche en premier (max 1000 ; 100 sans abonnement)")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"levelsmetricsexchange-settings"},Object(r.b)("inlineCode",{parentName:"h3"},"levelsMetrics(exchange, settings?)")),Object(r.b)("p",null,"L'historique des ",Object(r.b)("strong",{parentName:"p"},"niveaux de liquidité forts")," collecté pour la page ",Object(r.b)("strong",{parentName:"p"},"Métriques")," : toutes les 5 minutes, le serveur prend un instantané des niveaux du cribleur de liquidité de chaque plateforme ayant une durée d'au moins 30 minutes et une force d'au moins 80 %, et en conserve une semaine. La source filtre les instantanés d'une période comme la page et renvoie, par instantané, le nombre et la taille en $ des niveaux correspondants, les agrégats par ticker et le cycle de vie de chaque niveau. Données collectées, non liées aux bougies : faites correspondre ",Object(r.b)("inlineCode",{parentName:"p"},"series[].at")," aux dates des bougies. Sans abonnement, la période est limitée à 24 heures et la liste à 100 niveaux ; avec un abonnement, jusqu'à 7 jours et 1000 niveaux. Compte pour une requête de source de données."),Object(r.b)("pre",null,Object(r.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(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Paramètres")," (tous facultatifs) :"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Paramètre"),Object(r.b)("th",{parentName:"tr",align:null},"Par défaut"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"tickers")),Object(r.b)("td",{parentName:"tr",align:null},"tous les tickers"),Object(r.b)("td",{parentName:"tr",align:null},"seulement ces tickers de la plateforme")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"exclude")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"[]")),Object(r.b)("td",{parentName:"tr",align:null},"tickers à ignorer")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"hours")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"24")),Object(r.b)("td",{parentName:"tr",align:null},"période en heures (max 168 ; 24 sans abonnement)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"minDuration")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"30")),Object(r.b)("td",{parentName:"tr",align:null},"durée minimale de l'ordre en minutes : ",Object(r.b)("inlineCode",{parentName:"td"},"30"),", ",Object(r.b)("inlineCode",{parentName:"td"},"60"),", ",Object(r.b)("inlineCode",{parentName:"td"},"120")," ou ",Object(r.b)("inlineCode",{parentName:"td"},"180"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"minStrength")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"80")),Object(r.b)("td",{parentName:"tr",align:null},"force minimale de l'ordre en pourcentage : ",Object(r.b)("inlineCode",{parentName:"td"},"80"),", ",Object(r.b)("inlineCode",{parentName:"td"},"90")," ou ",Object(r.b)("inlineCode",{parentName:"td"},"100"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"maxDistance")),Object(r.b)("td",{parentName:"tr",align:null},"aucune"),Object(r.b)("td",{parentName:"tr",align:null},"distance maximale de l'ordre au prix actuel en pourcentage, vérifiée dans chaque instantané")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"stableOnly")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"false")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"true")," : seulement les ordres dont la quantité n'a jamais sauté (« exclure les ordres aux quantités instables »)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"limit")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"200")),Object(r.b)("td",{parentName:"tr",align:null},"nombre maximal de niveaux dans ",Object(r.b)("inlineCode",{parentName:"td"},"levels")," (max 1000 ; 100 sans abonnement)")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.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(r.b)("h2",{id:"requêtes-http"},"Requêtes HTTP"),Object(r.b)("p",null,"La classe ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"http"))," envoie des requêtes vers des API HTTP externes et retourne la réponse, ce qui permet à un indicateur d'utiliser des données absentes de ",Object(r.b)("inlineCode",{parentName:"p"},"source")," : votre propre serveur, une API REST publique, un fournisseur de métriques on-chain, ...\nComme pour ",Object(r.b)("inlineCode",{parentName:"p"},"source.*"),", les appels s'écrivent comme des fonctions synchrones, aucun ",Object(r.b)("inlineCode",{parentName:"p"},"await")," n'est nécessaire."),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"get(url, options?)")),Object(r.b)("td",{parentName:"tr",align:null},"Requête GET")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"post(url, body?, options?)")),Object(r.b)("td",{parentName:"tr",align:null},"Requête POST. Un objet ou un tableau ",Object(r.b)("inlineCode",{parentName:"td"},"body")," est envoyé en JSON (",Object(r.b)("inlineCode",{parentName:"td"},"Content-Type: application/json"),"), une chaîne est envoyée telle quelle")))),Object(r.b)("p",null,Object(r.b)("inlineCode",{parentName:"p"},"options")," est ",Object(r.b)("inlineCode",{parentName:"p"},"{ headers: { name: value } }")," — jusqu'à ",Object(r.b)("strong",{parentName:"p"},"10")," en-têtes personnalisés (par exemple ",Object(r.b)("inlineCode",{parentName:"p"},"Authorization")," ou ",Object(r.b)("inlineCode",{parentName:"p"},"x-api-key"),")."),Object(r.b)("p",null,"Les deux méthodes retournent le même objet de réponse et ",Object(r.b)("strong",{parentName:"p"},"ne lèvent jamais d'exception")," :"),Object(r.b)("pre",null,Object(r.b)("code",{parentName:"pre",className:"language-js"},'{\n ok: true, // statut 200–299\n status: 200, // statut HTTP, 0 si la requête n\'a pas été envoyée\n headers: { "content-type": "application/json", ... },\n data: { ... }, // corps de la réponse analysé en JSON, null si le corps n\'est pas du JSON\n text: "...", // corps brut de la réponse\n error: null // raison si la requête a échoué : "timeout", "url not allowed", "too many requests", ...\n}\n')),Object(r.b)(b.b,{type:"warning",title:"Limites et sécurité",mdxType:"Callout"},Object(r.b)("ul",null,Object(r.b)("li",{parentName:"ul"},"Jusqu'à ",Object(r.b)("strong",{parentName:"li"},"3 requêtes")," par exécution du script et ",Object(r.b)("strong",{parentName:"li"},"60 requêtes par minute")," par utilisateur."),Object(r.b)("li",{parentName:"ul"},"Uniquement des URL publiques ",Object(r.b)("inlineCode",{parentName:"li"},"http://")," / ",Object(r.b)("inlineCode",{parentName:"li"},"https://"),". Les requêtes vers ",Object(r.b)("inlineCode",{parentName:"li"},"localhost"),", les réseaux privés et les adresses de métadonnées cloud sont rejetées, les redirections ",Object(r.b)("strong",{parentName:"li"},"ne sont pas suivies")," (une réponse ",Object(r.b)("inlineCode",{parentName:"li"},"3xx")," est retournée telle quelle)."),Object(r.b)("li",{parentName:"ul"},"Les requêtes sont envoyées depuis les serveurs proxy de MobChart : l'API cible voit l'adresse du proxy et jamais votre IP."),Object(r.b)("li",{parentName:"ul"},"Délai d'attente ",Object(r.b)("strong",{parentName:"li"},"5 secondes"),", corps de la requête jusqu'à ",Object(r.b)("strong",{parentName:"li"},"64 Ko"),", réponse jusqu'à ",Object(r.b)("strong",{parentName:"li"},"512 Ko")," (les réponses plus grandes échouent avec ",Object(r.b)("inlineCode",{parentName:"li"},"response too large"),")."),Object(r.b)("li",{parentName:"ul"},"Les en-têtes ",Object(r.b)("inlineCode",{parentName:"li"},"Host"),", ",Object(r.b)("inlineCode",{parentName:"li"},"Cookie"),", ",Object(r.b)("inlineCode",{parentName:"li"},"Proxy-*"),", ",Object(r.b)("inlineCode",{parentName:"li"},"X-Forwarded-*")," et autres en-têtes de connexion ne peuvent pas être définis, ",Object(r.b)("inlineCode",{parentName:"li"},"Set-Cookie")," n'est jamais retourné."),Object(r.b)("li",{parentName:"ul"},"La vérification des alertes exécute votre script sur le serveur régulièrement : l'API est appelée à chaque vérification, tenez compte des limites de l'API cible."),Object(r.b)("li",{parentName:"ul"},"Le code d'un indicateur public (",Object(r.b)("inlineCode",{parentName:"li"},"isPublicCode = true"),") est visible par tous — ne mettez jamais de clés API dans du code public."))),Object(r.b)("p",null,"Valeurs possibles de ",Object(r.b)("inlineCode",{parentName:"p"},"error")," : ",Object(r.b)("inlineCode",{parentName:"p"},"invalid url"),", ",Object(r.b)("inlineCode",{parentName:"p"},"url not allowed"),", ",Object(r.b)("inlineCode",{parentName:"p"},"invalid headers"),", ",Object(r.b)("inlineCode",{parentName:"p"},"invalid body"),", ",Object(r.b)("inlineCode",{parentName:"p"},"body too large"),", ",Object(r.b)("inlineCode",{parentName:"p"},"response too large"),", ",Object(r.b)("inlineCode",{parentName:"p"},"timeout"),", ",Object(r.b)("inlineCode",{parentName:"p"},"too many requests"),", ",Object(r.b)("inlineCode",{parentName:"p"},"rate limit"),", ",Object(r.b)("inlineCode",{parentName:"p"},"busy"),", ",Object(r.b)("inlineCode",{parentName:"p"},"proxy unavailable"),", ",Object(r.b)("inlineCode",{parentName:"p"},"network error"),"."),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple (GET) :")),Object(r.b)("pre",null,Object(r.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(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple (POST) :")),Object(r.b)("pre",null,Object(r.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(r.b)("h2",{id:"variables-globales"},"Variables globales"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Variable"),Object(r.b)("th",{parentName:"tr",align:null},"Description"),Object(r.b)("th",{parentName:"tr",align:null},"Exemple"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"currentExchange")),Object(r.b)("td",{parentName:"tr",align:null},"Échange actuel sur le graphique"),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"binance"),", ",Object(r.b)("inlineCode",{parentName:"td"},"binanceFutures"),", ",Object(r.b)("inlineCode",{parentName:"td"},"bybit"),", ",Object(r.b)("inlineCode",{parentName:"td"},"bybitFutures"),", ",Object(r.b)("inlineCode",{parentName:"td"},"okx"),", ",Object(r.b)("inlineCode",{parentName:"td"},"okxFutures"),", ",Object(r.b)("inlineCode",{parentName:"td"},"coinbase"),", ",Object(r.b)("inlineCode",{parentName:"td"},"bitfinex"),", ",Object(r.b)("inlineCode",{parentName:"td"},"bitfinexFutures"),", ",Object(r.b)("inlineCode",{parentName:"td"},"hyperliquid"),", ",Object(r.b)("inlineCode",{parentName:"td"},"hyperliquidFutures"),", ",Object(r.b)("inlineCode",{parentName:"td"},"asterFutures"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"currentTicker")),Object(r.b)("td",{parentName:"tr",align:null},"Ticker actuel sur le graphique (le format dépend de l'échange)"),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"BTCUSDT"),", ",Object(r.b)("inlineCode",{parentName:"td"},"ADA-USD"),", ",Object(r.b)("inlineCode",{parentName:"td"},"BTC/USDC"),", ...")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"currentTimeframe")),Object(r.b)("td",{parentName:"tr",align:null},"Période actuelle sur le graphique"),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"1m"),", ",Object(r.b)("inlineCode",{parentName:"td"},"5m"),", ",Object(r.b)("inlineCode",{parentName:"td"},"15m"),", ...")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"exchanges")),Object(r.b)("td",{parentName:"tr",align:null},"Noms de toutes les plateformes (tableau figé)"),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'["binance", "binanceFutures", ...]'))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"tickers")),Object(r.b)("td",{parentName:"tr",align:null},"Tickers de toutes les plateformes : ",Object(r.b)("inlineCode",{parentName:"td"},"tickers[currentExchange]")," est la liste de la plateforme du graphique (figé)"),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"tickers.binance")," = ",Object(r.b)("inlineCode",{parentName:"td"},'["BTCUSDT", "ETHUSDT", ...]'))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"heatmapStep")),Object(r.b)("td",{parentName:"tr",align:null},"Pas de prix de la heatmap du carnet d'ordres et des clusters au prix actuel (la précision à laquelle le serveur arrondit les prix : 5 au-dessus de 5000, 1 pour 1000-5000, 0.1 pour 100-1000, ...), pris sur la dernière clôture du graphique ; ",Object(r.b)("inlineCode",{parentName:"td"},"null")," tant que le prix est inconnu (connu après le premier ",Object(r.b)("inlineCode",{parentName:"td"},"source.ohlcv()")," du marché du graphique)"),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"5"),", ",Object(r.b)("inlineCode",{parentName:"td"},"1"),", ",Object(r.b)("inlineCode",{parentName:"td"},"0.1"),", ",Object(r.b)("inlineCode",{parentName:"td"},"0.001"),", ...")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.b)("code",{parentName:"pre",className:"language-js"},"const candles = source.ohlcv(currentExchange, currentTicker, currentTimeframe);\nconst closes = candles.map(c => c.close);\n")),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Formats de ticker.")," Chaque méthode ",Object(r.b)("inlineCode",{parentName:"p"},"source")," qui prend un échange et un ticker fait passer le ticker par ",Object(r.b)("inlineCode",{parentName:"p"},"resolveTicker")," : la monnaie de base est extraite du format fourni (",Object(r.b)("inlineCode",{parentName:"p"},"ETHUSDT"),", ",Object(r.b)("inlineCode",{parentName:"p"},"ETH-USD"),", ",Object(r.b)("inlineCode",{parentName:"p"},"ETH/USDC"),", ",Object(r.b)("inlineCode",{parentName:"p"},"ETH:USD"),", ",Object(r.b)("inlineCode",{parentName:"p"},"ETHF0:USTF0"),", ...) et le ticker est reconstruit au format de l'échange demandé. ",Object(r.b)("inlineCode",{parentName:"p"},"ETH-USD")," fonctionne donc pour Binance, et le ",Object(r.b)("inlineCode",{parentName:"p"},"currentTicker")," d'un graphique Binance fonctionne pour Coinbase. Seule la monnaie est conservée : la paire standard de l'échange est utilisée (USDT sur Binance, USD sur Coinbase, USDC sur Hyperliquid, ...), et une monnaie que l'échange ne liste pas donne un résultat vide avec une ligne dans les logs."),Object(r.b)("pre",null,Object(r.b)("code",{parentName:"pre",className:"language-js"},'// un ticker au format Coinbase sur Binance Futures : résolu en ETHUSDT\nconst depths = source.depths("binanceFutures", "ETH-USD");\n// le ticker d\'un graphique Binance sur Coinbase : ETHUSDT devient ETH-USD\nconst spot = source.ohlcv("coinbase", currentTicker, currentTimeframe);\n')),Object(r.b)("h2",{id:"journalisation"},"Journalisation"),Object(r.b)("p",null,"La classe ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"logger"))," sert à afficher des informations et à déboguer."),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"log(...args)")),Object(r.b)("td",{parentName:"tr",align:null},"Ajoute une entrée au journal (avec des limites de longueur et de quantité)")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.b)("code",{parentName:"pre",className:"language-js"},'const candles = source.ohlcv();\nlogger.log("First candle:", candles?.[0]);\n')),Object(r.b)(b.f,{src:"../../public/code_editor_log.png",mdxType:"Screenshot"}),Object(r.b)("h2",{id:"visualisation"},"Visualisation"),Object(r.b)("p",null,"La classe ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"plot"))," est responsable du dessin des éléments graphiques et des séries de données."),Object(r.b)("h3",{id:"formes-géométriques"},"Formes géométriques"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"box(title, date1, price1, date2, price2, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(r.b)("td",{parentName:"tr",align:null},"Rectangle")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"square(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(r.b)("td",{parentName:"tr",align:null},"Carré")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"diamond(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(r.b)("td",{parentName:"tr",align:null},"Losange")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"triangle(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(r.b)("td",{parentName:"tr",align:null},"Triangle")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"circle(title, date1, price1, radius = 10, fill, opacity, stroke, strokeOpacity, strokeWidth)")),Object(r.b)("td",{parentName:"tr",align:null},"Cercle")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"text(title, value, date1, price1, fontSize, fill, opacity)")),Object(r.b)("td",{parentName:"tr",align:null},"Texte sur le graphique")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"table(title, rows, options)")),Object(r.b)("td",{parentName:"tr",align:null},"Tableau épinglé dans un coin du graphique, voir ",Object(r.b)("a",{parentName:"td",href:"#tableaux"},"Tableaux"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"straightLine(title, date1, price1, date2, price2, stroke, strokeOpacity, strokeWidth, strokeDasharray)")),Object(r.b)("td",{parentName:"tr",align:null},"Ligne droite")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"horizontalLine(title, price1, stroke, strokeOpacity, strokeWidth, strokeDasharray)")),Object(r.b)("td",{parentName:"tr",align:null},"Ligne horizontale sur toute la largeur du graphique")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"verticalLine(title, date1, stroke, strokeOpacity, strokeWidth, strokeDasharray)")),Object(r.b)("td",{parentName:"tr",align:null},"Ligne verticale sur toute la hauteur du graphique")))),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Paramètre"),Object(r.b)("th",{parentName:"tr",align:null},"Type de donnée"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"title")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"string")),Object(r.b)("td",{parentName:"tr",align:null},"Nom de l'élément (identifiant unique sur le graphique)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"date1")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")," (timestamp, ms)"),Object(r.b)("td",{parentName:"tr",align:null},"Horodatage de départ")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"price1")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")),Object(r.b)("td",{parentName:"tr",align:null},"Prix de départ (coordonnée sur l'axe Y)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"date2")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")," (timestamp, ms)"),Object(r.b)("td",{parentName:"tr",align:null},"Horodatage de fin (pour les lignes et les rectangles)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"price2")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")),Object(r.b)("td",{parentName:"tr",align:null},"Prix de fin (pour les lignes et les rectangles)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"size")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")),Object(r.b)("td",{parentName:"tr",align:null},"Taille de la forme (en pixels)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"radius")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")),Object(r.b)("td",{parentName:"tr",align:null},"Rayon du cercle (10 par défaut)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"fill")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"string")," (hex, ex. ",Object(r.b)("inlineCode",{parentName:"td"},'"#FF0000"'),")"),Object(r.b)("td",{parentName:"tr",align:null},"Couleur de remplissage de la forme")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"opacity")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")," (0–1)"),Object(r.b)("td",{parentName:"tr",align:null},"Opacité du remplissage")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"stroke")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"string")," (hex, ex. ",Object(r.b)("inlineCode",{parentName:"td"},'"#000000"'),")"),Object(r.b)("td",{parentName:"tr",align:null},"Couleur du contour")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"strokeOpacity")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")," (0–1)"),Object(r.b)("td",{parentName:"tr",align:null},"Opacité du contour")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"strokeWidth")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")),Object(r.b)("td",{parentName:"tr",align:null},"Épaisseur du contour (en pixels)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"strokeDasharray")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"array")," (ex. ",Object(r.b)("inlineCode",{parentName:"td"},"[5,5]"),")"),Object(r.b)("td",{parentName:"tr",align:null},"Style de ligne pointillée")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"fontSize")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")),Object(r.b)("td",{parentName:"tr",align:null},"Taille du texte (en pixels)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"value")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"string")),Object(r.b)("td",{parentName:"tr",align:null},"Valeur du texte (pour la méthode ",Object(r.b)("inlineCode",{parentName:"td"},"text"),")")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.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(r.b)("p",null,"Pas plus de ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"3000"))," formes ne peuvent être affichées sur le graphique."),Object(r.b)("h3",{id:"séries-de-données"},"Séries de données"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"lineSeries(title, values, stroke, strokeOpacity, strokeWidth, strokeDasharray)")),Object(r.b)("td",{parentName:"tr",align:null},"Graphique en ligne")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"barSeries(title, values, fill, opacity)")),Object(r.b)("td",{parentName:"tr",align:null},"Graphique en barres")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"candleSeries(title, openValues, closeValues, highValues, lowValues, fill, opacity)")),Object(r.b)("td",{parentName:"tr",align:null},"Chandeliers")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"areaSeries(title, values, fill, opacity, stroke, strokeOpacity, strokeWidth, base)")),Object(r.b)("td",{parentName:"tr",align:null},"Zone remplie entre la ligne et ",Object(r.b)("inlineCode",{parentName:"td"},"base")," (par défaut : le bas du graphique)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"bandSeries(title, upperValues, lowerValues, fill, opacity, stroke, strokeOpacity, strokeWidth, step)")),Object(r.b)("td",{parentName:"tr",align:null},"Bande remplie entre deux séries, chaque chandelier conserve sa propre couleur et opacité")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"profile(title, values, options)")),Object(r.b)("td",{parentName:"tr",align:null},"Histogramme par prix des chandeliers visibles, comme le profil de volume, voir ",Object(r.b)("a",{parentName:"td",href:"#profils"},"Profils"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"histogram(title, rows, options)")),Object(r.b)("td",{parentName:"tr",align:null},"Une carte de prix déjà calculée, dessinée comme un profil, voir ",Object(r.b)("a",{parentName:"td",href:"#histogrammes"},"Histogrammes"))))),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Paramètre"),Object(r.b)("th",{parentName:"tr",align:null},"Type de donnée"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"values"),", ",Object(r.b)("inlineCode",{parentName:"td"},"upperValues"),", ",Object(r.b)("inlineCode",{parentName:"td"},"lowerValues")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"(number \\| null)[]")),Object(r.b)("td",{parentName:"tr",align:null},"Une valeur par chandelier, ",Object(r.b)("inlineCode",{parentName:"td"},"null")," masque le chandelier")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"fill"),", ",Object(r.b)("inlineCode",{parentName:"td"},"stroke")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"string")," (hex) ou ",Object(r.b)("inlineCode",{parentName:"td"},"function")),Object(r.b)("td",{parentName:"tr",align:null},"Couleur, ou fonction retournant la couleur du chandelier : ",Object(r.b)("inlineCode",{parentName:"td"},"(value, i)")," pour ",Object(r.b)("inlineCode",{parentName:"td"},"lineSeries"),", ",Object(r.b)("inlineCode",{parentName:"td"},"barSeries"),", ",Object(r.b)("inlineCode",{parentName:"td"},"candleSeries"),", ",Object(r.b)("inlineCode",{parentName:"td"},"areaSeries"),", ",Object(r.b)("inlineCode",{parentName:"td"},"(upper, lower, i)")," pour ",Object(r.b)("inlineCode",{parentName:"td"},"bandSeries"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"opacity")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")," (0–1) ou ",Object(r.b)("inlineCode",{parentName:"td"},"function")),Object(r.b)("td",{parentName:"tr",align:null},"Opacité du remplissage, une fonction ",Object(r.b)("inlineCode",{parentName:"td"},"(value, i)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"(upper, lower, i)")," pour ",Object(r.b)("inlineCode",{parentName:"td"},"areaSeries")," / ",Object(r.b)("inlineCode",{parentName:"td"},"bandSeries"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"base")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number")," ","|"," ",Object(r.b)("inlineCode",{parentName:"td"},"null")),Object(r.b)("td",{parentName:"tr",align:null},"Niveau de base de ",Object(r.b)("inlineCode",{parentName:"td"},"areaSeries"),", ",Object(r.b)("inlineCode",{parentName:"td"},"null")," = le bas du graphique")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"step")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"boolean")),Object(r.b)("td",{parentName:"tr",align:null},"Style de ",Object(r.b)("inlineCode",{parentName:"td"},"bandSeries")," : ",Object(r.b)("inlineCode",{parentName:"td"},"true")," = une colonne par chandelier (par défaut), ",Object(r.b)("inlineCode",{parentName:"td"},"false")," = un polygone continu")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.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(r.b)("p",null,"Pas plus de ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"20"))," séries de données ne peuvent être affichées sur le graphique."),Object(r.b)("h3",{id:"profils"},"Profils"),Object(r.b)("p",null,Object(r.b)("inlineCode",{parentName:"p"},"plot.profile(title, values, options?)")," dessine un histogramme par prix, comme le profil de volume intégré. ",Object(r.b)("inlineCode",{parentName:"p"},"values[i]")," est la carte des prix du chandelier ",Object(r.b)("inlineCode",{parentName:"p"},"i")," de ",Object(r.b)("inlineCode",{parentName:"p"},"ohlcv()")," : ",Object(r.b)("inlineCode",{parentName:"p"},"{ price: value }")," ou ",Object(r.b)("inlineCode",{parentName:"p"},"{ price: { buy, sell } }")," (le ",Object(r.b)("inlineCode",{parentName:"p"},"data")," d'un enregistrement ",Object(r.b)("inlineCode",{parentName:"p"},"clusters()")," ou ",Object(r.b)("inlineCode",{parentName:"p"},"liquidations()")," peut être passé tel quel), ou ",Object(r.b)("inlineCode",{parentName:"p"},"null")," pour un chandelier sans lignes. Les lignes des chandeliers ",Object(r.b)("strong",{parentName:"p"},"visibles sur le graphique")," sont additionnées par pas de prix dans le graphique lui-même : le profil suit le déplacement, le zoom et le chargement de l'historique sans nouvelle exécution. Les valeurs simples sont dessinées d'une seule couleur (une valeur négative va dans ",Object(r.b)("inlineCode",{parentName:"p"},"fill2"),"), les paires divisent la barre en parties achat et vente et permettent le mode delta. ",Object(r.b)("inlineCode",{parentName:"p"},"profile()")," ne renvoie pas de poignée d'alerte."),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Option"),Object(r.b)("th",{parentName:"tr",align:null},"Défaut"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"step")),Object(r.b)("td",{parentName:"tr",align:null},"tick du graphique"),Object(r.b)("td",{parentName:"tr",align:null},"hauteur de ligne en unités de prix, les prix sont arrondis vers le bas")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"orient")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'"right"')),Object(r.b)("td",{parentName:"tr",align:null},"bord d'où partent les barres, ",Object(r.b)("inlineCode",{parentName:"td"},'"right"')," ou ",Object(r.b)("inlineCode",{parentName:"td"},'"left"'))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"width")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"30")),Object(r.b)("td",{parentName:"tr",align:null},"ligne la plus large en pourcentage de la largeur du graphique")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"fill")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'"#26a69a"')),Object(r.b)("td",{parentName:"tr",align:null},"couleur de la valeur, ou de la partie achat")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"fill2")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'"#ef5350"')),Object(r.b)("td",{parentName:"tr",align:null},"couleur de la partie vente")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"opacity")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"0.6")),Object(r.b)("td",{parentName:"tr",align:null},"opacité des barres")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"delta")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"false")),Object(r.b)("td",{parentName:"tr",align:null},"dessiner ",Object(r.b)("inlineCode",{parentName:"td"},"buy - sell")," au lieu de la somme, paires seulement")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"poc")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"true")),Object(r.b)("td",{parentName:"tr",align:null},"ligne sur la ligne de plus grande valeur")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"valueArea")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"70")),Object(r.b)("td",{parentName:"tr",align:null},"pourcentage du total autour du POC dessiné en pleine opacité, ",Object(r.b)("inlineCode",{parentName:"td"},"false")," pour désactiver")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"valueAreaOpacity")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"0.3")),Object(r.b)("td",{parentName:"tr",align:null},"opacité des lignes hors de la value area")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"labels")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"true")),Object(r.b)("td",{parentName:"tr",align:null},"totaux des lignes sur les barres quand il y a de la place")))),Object(r.b)("p",null,"Limites : ",Object(r.b)("strong",{parentName:"p"},"3")," profils par exécution, ",Object(r.b)("strong",{parentName:"p"},"300")," lignes par chandelier, ",Object(r.b)("strong",{parentName:"p"},"200000")," lignes par profil. Les lignes au-delà sont ignorées et l'exécution l'indique dans le journal."),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"histogrammes"},"Histogrammes"),Object(r.b)("p",null,Object(r.b)("inlineCode",{parentName:"p"},"plot.histogram(title, rows, options?)")," dessine un histogramme que vous avez déjà construit : ",Object(r.b)("strong",{parentName:"p"},"une seule carte de prix pour tout le graphique"),", rien n'est additionné depuis les chandeliers. Utilisez ",Object(r.b)("inlineCode",{parentName:"p"},"profile()")," quand les lignes appartiennent aux chandeliers et que l'image doit suivre la plage visible, et ",Object(r.b)("inlineCode",{parentName:"p"},"histogram()")," quand les lignes sont définitives : un instantané du carnet d'ordres, des niveaux calculés par le script, des données d'un autre marché."),Object(r.b)("p",null,Object(r.b)("inlineCode",{parentName:"p"},"rows")," a les mêmes formats qu'une entrée de chandelier de ",Object(r.b)("inlineCode",{parentName:"p"},"profile()")," : ",Object(r.b)("inlineCode",{parentName:"p"},"{ price: value }"),", ",Object(r.b)("inlineCode",{parentName:"p"},"{ price: { buy, sell } }")," ou une liste de lignes ",Object(r.b)("inlineCode",{parentName:"p"},"[price, buy, sell]"),". Une valeur simple est dessinée d'une seule couleur (une valeur négative va dans ",Object(r.b)("inlineCode",{parentName:"p"},"fill2"),"), les paires divisent la barre en parties achat et vente et permettent le mode delta. Les options sont celles de ",Object(r.b)("inlineCode",{parentName:"p"},"profile()"),", voir le tableau ci-dessus."),Object(r.b)("p",null,"L'histogramme porte la date du dernier chandelier de l'exécution et le graphique garde l'histogramme ",Object(r.b)("strong",{parentName:"p"},"le plus récent")," d'un titre, donc les exécutions partielles sur des chandeliers plus anciens (chargement de l'historique) ne remplacent jamais l'actuel. Il est redessiné à chaque rafraîchissement du script, un histogramme du carnet suit donc le carnet en direct. ",Object(r.b)("inlineCode",{parentName:"p"},"histogram()")," ne renvoie pas de poignée d'alerte."),Object(r.b)("p",null,"Limites : ",Object(r.b)("strong",{parentName:"p"},"3")," histogrammes par exécution, ",Object(r.b)("strong",{parentName:"p"},"5000")," lignes. Les lignes au-delà sont ignorées et l'exécution l'indique dans le journal."),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple, le carnet d'ordres :")),Object(r.b)("pre",null,Object(r.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(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple, vos propres niveaux :")),Object(r.b)("pre",null,Object(r.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(r.b)("h3",{id:"tableaux"},"Tableaux"),Object(r.b)("p",null,Object(r.b)("inlineCode",{parentName:"p"},"plot.table(title, rows, options?)")," dessine une petite grille épinglée dans un coin du panneau : résumés multi-unités de temps, statistiques de la plage chargée, tableaux de signaux. ",Object(r.b)("inlineCode",{parentName:"p"},"rows")," est un tableau de lignes, une ligne un tableau de cellules, une cellule une chaîne, un nombre ou ",Object(r.b)("inlineCode",{parentName:"p"},"{ text, color, bg, align, bold }"),". Les tableaux d'un même coin s'empilent dans l'ordre. Un tableau porte la date du dernier chandelier de l'exécution et le graphique garde le tableau ",Object(r.b)("strong",{parentName:"p"},"le plus récent")," d'un titre, donc les exécutions partielles sur des chandeliers plus anciens (chargement de l'historique) ne remplacent jamais le tableau courant. Les tableaux ne sont pas dessinés sur les graphiques de moins de 500 px de large. Un tableau peut être déplacé à la souris : la position est enregistrée avec l'espace de travail et réinitialisée quand le script change son option ",Object(r.b)("inlineCode",{parentName:"p"},"position"),". ",Object(r.b)("inlineCode",{parentName:"p"},"table()")," ne renvoie pas de poignée d'alerte."),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Option"),Object(r.b)("th",{parentName:"tr",align:null},"Défaut"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"position")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'"top-right"')),Object(r.b)("td",{parentName:"tr",align:null},"coin du panneau : ",Object(r.b)("inlineCode",{parentName:"td"},'"top-right"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"top-left"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"bottom-right"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"bottom-left"'))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"header")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"false")),Object(r.b)("td",{parentName:"tr",align:null},"première ligne en gras sur un bandeau ombré")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"fontSize")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"11")),Object(r.b)("td",{parentName:"tr",align:null},"taille du texte en px, de 8 à 16")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"color"),", ",Object(r.b)("inlineCode",{parentName:"td"},"bg"),", ",Object(r.b)("inlineCode",{parentName:"td"},"border")),Object(r.b)("td",{parentName:"tr",align:null},"selon le thème"),Object(r.b)("td",{parentName:"tr",align:null},"couleurs par défaut du texte, du panneau et de la grille ; une cellule peut redéfinir ",Object(r.b)("inlineCode",{parentName:"td"},"color")," et ",Object(r.b)("inlineCode",{parentName:"td"},"bg"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"opacity")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"0.85")),Object(r.b)("td",{parentName:"tr",align:null},"opacité du panneau")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"align")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},'"left"')),Object(r.b)("td",{parentName:"tr",align:null},"alignement par défaut des cellules, ",Object(r.b)("inlineCode",{parentName:"td"},'"left"'),", ",Object(r.b)("inlineCode",{parentName:"td"},'"center"')," ou ",Object(r.b)("inlineCode",{parentName:"td"},'"right"'))))),Object(r.b)("p",null,"Limites : ",Object(r.b)("strong",{parentName:"p"},"3")," tableaux par exécution, ",Object(r.b)("strong",{parentName:"p"},"50")," lignes, ",Object(r.b)("strong",{parentName:"p"},"10")," colonnes, ",Object(r.b)("strong",{parentName:"p"},"100")," caractères par cellule. Un texte plus long est coupé avec des points de suspension, une colonne occupe au plus 30 % de la largeur du graphique."),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.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(r.b)("h2",{id:"analyse-technique"},"Analyse technique"),Object(r.b)("p",null,"La classe ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"ta"))," implémente les fonctions ",Object(r.b)("inlineCode",{parentName:"p"},"ta.*")," de PineScript v5. Pine calcule barre par barre, ici chaque fonction prend des séries entières (tableaux, une valeur par bougie, de la plus ancienne à la plus récente) et renvoie une série de même longueur, directement traçable. Les valeurs sont ",Object(r.b)("inlineCode",{parentName:"p"},"null")," tant que la période n'est pas remplie, et les entrées ",Object(r.b)("inlineCode",{parentName:"p"},"null")," se propagent comme le ",Object(r.b)("inlineCode",{parentName:"p"},"na")," de Pine. Les fonctions Pine qui lisent implicitement ",Object(r.b)("inlineCode",{parentName:"p"},"high"),", ",Object(r.b)("inlineCode",{parentName:"p"},"low"),", ",Object(r.b)("inlineCode",{parentName:"p"},"close")," ou ",Object(r.b)("inlineCode",{parentName:"p"},"volume")," reçoivent ces tableaux en arguments."),Object(r.b)("p",null,"Les fonctions qui renvoient plusieurs séries (",Object(r.b)("inlineCode",{parentName:"p"},"bb"),", ",Object(r.b)("inlineCode",{parentName:"p"},"kc"),", ",Object(r.b)("inlineCode",{parentName:"p"},"macd"),", ",Object(r.b)("inlineCode",{parentName:"p"},"stoch"),", ",Object(r.b)("inlineCode",{parentName:"p"},"dmi"),", ",Object(r.b)("inlineCode",{parentName:"p"},"supertrend"),") renvoient un tableau doté de propriétés nommées : ",Object(r.b)("inlineCode",{parentName:"p"},"const [middle, upper, lower] = ta.bb(closes, 20, 2)")," ou ",Object(r.b)("inlineCode",{parentName:"p"},"ta.bb(closes, 20, 2).upper"),"."),Object(r.b)("h3",{id:"fonctions-utilitaires"},"Fonctions utilitaires"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"highest(values, length)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"lowest(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Valeur la plus haute / basse sur ",Object(r.b)("inlineCode",{parentName:"td"},"length")," barres")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"highestbars(values, length)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"lowestbars(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Décalage (0 ou négatif) vers la barre la plus haute / basse")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"max(values)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"min(values)")),Object(r.b)("td",{parentName:"tr",align:null},"Plus haut / bas historique jusqu'à chaque barre")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"range(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"highest - lowest"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"change(values, length = 1)")),Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"values[i] - values[i - length]"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"mom(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Momentum")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"roc(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Taux de variation, %")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"cum(values)")),Object(r.b)("td",{parentName:"tr",align:null},"Somme cumulée")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"rising(values, length)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"falling(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Valeur au-dessus / en dessous de chacune des ",Object(r.b)("inlineCode",{parentName:"td"},"length")," valeurs précédentes")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"variance(values, length, biased = true)")),Object(r.b)("td",{parentName:"tr",align:null},"Variance")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"stdev(values, length, biased = true)")),Object(r.b)("td",{parentName:"tr",align:null},"Écart type")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"dev(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Écart absolu moyen")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"median(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Médiane")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"mode(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Valeur la plus fréquente (la plus petite en cas d'égalité)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"percentile_linear_interpolation(values, length, percentage)")),Object(r.b)("td",{parentName:"tr",align:null},"Percentile, interpolation linéaire entre les deux rangs les plus proches")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"percentile_nearest_rank(values, length, percentage)")),Object(r.b)("td",{parentName:"tr",align:null},"Percentile, méthode du rang le plus proche")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"percentrank(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"% des ",Object(r.b)("inlineCode",{parentName:"td"},"length")," valeurs précédentes inférieures ou égales à la valeur courante")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"correlation(series1, series2, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Coefficient de corrélation")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"crossover(series1, series2)")),Object(r.b)("td",{parentName:"tr",align:null},"Croisement à la hausse (chaque argument peut être un nombre : ",Object(r.b)("inlineCode",{parentName:"td"},"ta.crossover(rsi, 70)"),")")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"crossunder(series1, series2)")),Object(r.b)("td",{parentName:"tr",align:null},"Croisement à la baisse")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"cross(series1, series2)")),Object(r.b)("td",{parentName:"tr",align:null},"Tout croisement")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"barssince(condition)")),Object(r.b)("td",{parentName:"tr",align:null},"Barres depuis la dernière fois où ",Object(r.b)("inlineCode",{parentName:"td"},"condition")," (tableau de booléens) était vraie")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"valuewhen(condition, values, occurrence = 0)")),Object(r.b)("td",{parentName:"tr",align:null},"Valeur de ",Object(r.b)("inlineCode",{parentName:"td"},"values")," quand ",Object(r.b)("inlineCode",{parentName:"td"},"condition")," était vraie il y a ",Object(r.b)("inlineCode",{parentName:"td"},"occurrence")," occurrences")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"pivothigh(values, leftbars, rightbars)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"pivotlow(...)")),Object(r.b)("td",{parentName:"tr",align:null},"Valeur du pivot sur la barre de confirmation, le pivot lui-même est ",Object(r.b)("inlineCode",{parentName:"td"},"rightbars")," barres plus tôt")))),Object(r.b)("h3",{id:"moyennes-mobiles"},"Moyennes mobiles"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"sma(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Moyenne mobile simple")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"ema(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Moyenne mobile exponentielle")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"rma(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Moyenne mobile de Wilder (utilisée par RSI / ATR)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"wma(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Moyenne mobile pondérée")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"hma(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Moyenne mobile de Hull")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"alma(values, length, offset = 0.85, sigma = 6, floor = false)")),Object(r.b)("td",{parentName:"tr",align:null},"Moyenne mobile d'Arnaud Legoux")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"swma(values)")),Object(r.b)("td",{parentName:"tr",align:null},"Moyenne mobile à pondération symétrique sur 4 barres")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"vwma(values, volumes, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Moyenne mobile pondérée par le volume")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"linreg(values, length, offset = 0)")),Object(r.b)("td",{parentName:"tr",align:null},"Courbe de régression linéaire")))),Object(r.b)("h3",{id:"oscillateurs"},"Oscillateurs"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"rsi(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Indice de force relative")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"cmo(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Chande Momentum Oscillator")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"cog(values, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Centre de gravité")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"mfi(values, volumes, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Money Flow Index (",Object(r.b)("inlineCode",{parentName:"td"},"values")," est généralement hlc3)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"tsi(values, shortLength, longLength)")),Object(r.b)("td",{parentName:"tr",align:null},"True Strength Index, -1..1")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"wpr(highs, lows, closes, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Williams %R")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"stoch(highs, lows, closes, period = 14, smoothK = 3, smoothD = 3)")),Object(r.b)("td",{parentName:"tr",align:null},"Stochastique ",Object(r.b)("inlineCode",{parentName:"td"},"[k, d]"),", ",Object(r.b)("inlineCode",{parentName:"td"},"smoothK = 1")," donne la valeur brute")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"cci(values, length)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"cci(highs, lows, closes, length)")),Object(r.b)("td",{parentName:"tr",align:null},"Commodity Channel Index (du prix typique pour la seconde forme)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"macd(values, fast = 12, slow = 26, signalPeriod = 9)")),Object(r.b)("td",{parentName:"tr",align:null},"MACD ",Object(r.b)("inlineCode",{parentName:"td"},"[macd, signal, histogram]"))))),Object(r.b)("h3",{id:"bandes-et-canaux"},"Bandes et canaux"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"bb(values, length = 20, mult = 2)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"bollinger(...)")),Object(r.b)("td",{parentName:"tr",align:null},"Bandes de Bollinger ",Object(r.b)("inlineCode",{parentName:"td"},"[middle, upper, lower]"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"bbw(values, length, mult)")),Object(r.b)("td",{parentName:"tr",align:null},"Largeur des bandes de Bollinger ",Object(r.b)("inlineCode",{parentName:"td"},"(upper - lower) / middle"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"kc(values, highs, lows, closes, length = 20, mult = 2, useTrueRange = true)")),Object(r.b)("td",{parentName:"tr",align:null},"Canaux de Keltner ",Object(r.b)("inlineCode",{parentName:"td"},"[middle, upper, lower]"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"kcw(values, highs, lows, closes, length, mult, useTrueRange)")),Object(r.b)("td",{parentName:"tr",align:null},"Largeur des canaux de Keltner ",Object(r.b)("inlineCode",{parentName:"td"},"(upper - lower) / middle"))))),Object(r.b)("h3",{id:"tendance-et-volatilité"},"Tendance et volatilité"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"tr(highs, lows, closes, handleNa = false)")),Object(r.b)("td",{parentName:"tr",align:null},"True range (",Object(r.b)("inlineCode",{parentName:"td"},"handleNa = true")," donne ",Object(r.b)("inlineCode",{parentName:"td"},"high - low")," sur la première barre)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"atr(highs, lows, closes, period = 14)")),Object(r.b)("td",{parentName:"tr",align:null},"Average True Range")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"dmi(highs, lows, closes, diLength = 14, adxSmoothing = 14)")),Object(r.b)("td",{parentName:"tr",align:null},"Mouvement directionnel ",Object(r.b)("inlineCode",{parentName:"td"},"[plus, minus, adx]"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"adx(highs, lows, closes, period = 14)")),Object(r.b)("td",{parentName:"tr",align:null},"Average Directional Index")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"sar(highs, lows, closes, start = 0.02, inc = 0.02, max = 0.2)")),Object(r.b)("td",{parentName:"tr",align:null},"SAR parabolique")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"supertrend(highs, lows, closes, factor = 3, atrPeriod = 10)")),Object(r.b)("td",{parentName:"tr",align:null},"Supertrend ",Object(r.b)("inlineCode",{parentName:"td"},"[supertrend, direction]"),", direction ",Object(r.b)("inlineCode",{parentName:"td"},"-1")," = tendance haussière, ",Object(r.b)("inlineCode",{parentName:"td"},"1")," = baissière")))),Object(r.b)("h3",{id:"volume"},"Volume"),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"vwap(values, volumes, anchor?)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"vwap(highs, lows, closes, volumes, anchor?)")),Object(r.b)("td",{parentName:"tr",align:null},"VWAP cumulatif (du prix typique pour la seconde forme), redémarre sur les barres où ",Object(r.b)("inlineCode",{parentName:"td"},"anchor")," (tableau de booléens) est vrai")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"obv(closes, volumes)")),Object(r.b)("td",{parentName:"tr",align:null},"On Balance Volume")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"accdist(highs, lows, closes, volumes)")),Object(r.b)("td",{parentName:"tr",align:null},"Accumulation / Distribution")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"iii(highs, lows, closes, volumes)")),Object(r.b)("td",{parentName:"tr",align:null},"Intraday Intensity Index")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"pvt(closes, volumes)")),Object(r.b)("td",{parentName:"tr",align:null},"Price-Volume Trend")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"nvi(closes, volumes)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"pvi(closes, volumes)")),Object(r.b)("td",{parentName:"tr",align:null},"Negative / Positive Volume Index")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"wad(highs, lows, closes)")),Object(r.b)("td",{parentName:"tr",align:null},"Williams Accumulation / Distribution")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"wvad(opens, highs, lows, closes, volumes)")),Object(r.b)("td",{parentName:"tr",align:null},"Williams Variable Accumulation / Distribution")))),Object(r.b)("h3",{id:"fonctions-de-carnet-dordres"},"Fonctions de carnet d'ordres"),Object(r.b)("p",null,Object(r.b)("inlineCode",{parentName:"p"},"orderbook")," est ",Object(r.b)("inlineCode",{parentName:"p"},"source.orderbook()")," ou ",Object(r.b)("inlineCode",{parentName:"p"},"source.depths()[i].data")," : ",Object(r.b)("inlineCode",{parentName:"p"},"{asks: {price: quantity}, bids: {price: quantity}}"),"."),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"orderbookSide(orderbook, side)")),Object(r.b)("td",{parentName:"tr",align:null},"Un côté (",Object(r.b)("inlineCode",{parentName:"td"},"'bids'")," / ",Object(r.b)("inlineCode",{parentName:"td"},"'asks'"),") sous forme de ",Object(r.b)("inlineCode",{parentName:"td"},"[[price, quantity], ...]")," trié du meilleur prix au moins bon")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"bestBid(orderbook)")," / ",Object(r.b)("inlineCode",{parentName:"td"},"bestAsk(orderbook)")),Object(r.b)("td",{parentName:"tr",align:null},"Meilleur prix bid / ask (",Object(r.b)("inlineCode",{parentName:"td"},"null")," pour un carnet vide)")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"sumBids(orderbook, percent, price, inQuote)")),Object(r.b)("td",{parentName:"tr",align:null},"Quantité cumulée des bids entre ",Object(r.b)("inlineCode",{parentName:"td"},"price")," (par défaut : meilleur bid) et ",Object(r.b)("inlineCode",{parentName:"td"},"price * (1 - percent / 100)"),", ",Object(r.b)("inlineCode",{parentName:"td"},"inQuote = true")," additionne ",Object(r.b)("inlineCode",{parentName:"td"},"price * quantity"))),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"sumAsks(orderbook, percent, price, inQuote)")),Object(r.b)("td",{parentName:"tr",align:null},"Quantité cumulée des asks entre ",Object(r.b)("inlineCode",{parentName:"td"},"price")," (par défaut : meilleur ask) et ",Object(r.b)("inlineCode",{parentName:"td"},"price * (1 + percent / 100)"))))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.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(r.b)("h2",{id:"alertes"},"Alertes"),Object(r.b)("p",null,"Les utilisateurs peuvent créer des alertes pour votre indicateur depuis l'onglet ",Object(r.b)("strong",{parentName:"p"},"Alertes")," des paramètres de l'indicateur sur le graphique.\nLes alertes sont vérifiées côté serveur régulièrement, les notifications sont envoyées vers ",Object(r.b)("strong",{parentName:"p"},"Telegram")," et/ou une ",Object(r.b)("strong",{parentName:"p"},"URL de webhook")," définie par l'utilisateur."),Object(r.b)("h3",{id:"paramètres-dalerte--alertinput"},"Paramètres d'alerte — ",Object(r.b)("inlineCode",{parentName:"h3"},"alertInput")),Object(r.b)("p",null,"La classe ",Object(r.b)("strong",{parentName:"p"},Object(r.b)("inlineCode",{parentName:"strong"},"alertInput"))," déclare les paramètres d'alerte. Elle fonctionne exactement comme ",Object(r.b)("inlineCode",{parentName:"p"},"input"),", mais les champs sont affichés dans l'onglet ",Object(r.b)("strong",{parentName:"p"},"Alertes")," et chaque utilisateur configure ses propres valeurs pour son alerte."),Object(r.b)("p",null,"Les scripts sans déclarations ",Object(r.b)("inlineCode",{parentName:"p"},"alertInput")," ne peuvent pas être utilisés pour les alertes."),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"number(title, defaultValue, min, max, step)")),Object(r.b)("td",{parentName:"tr",align:null},"Entrée numérique")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"range(title, defaultValue, min, max, step)")),Object(r.b)("td",{parentName:"tr",align:null},"Entrée de type plage")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"select(title, defaultValue, values)")),Object(r.b)("td",{parentName:"tr",align:null},"Sélection dans une liste déroulante")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"color(title, defaultValue)")),Object(r.b)("td",{parentName:"tr",align:null},"Sélecteur de couleur")),Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"boolean(title, defaultValue)")),Object(r.b)("td",{parentName:"tr",align:null},"Entrée booléenne")))),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.b)("code",{parentName:"pre",className:"language-js"},'const alertVolumeThreshold = alertInput.number("Volume threshold", 1000, 0, 10000000, 1);\n')),Object(r.b)("h3",{id:"déclencher-des-alertes--alertfire"},"Déclencher des alertes — ",Object(r.b)("inlineCode",{parentName:"h3"},"alert.fire")),Object(r.b)("table",null,Object(r.b)("thead",{parentName:"table"},Object(r.b)("tr",{parentName:"thead"},Object(r.b)("th",{parentName:"tr",align:null},"Méthode"),Object(r.b)("th",{parentName:"tr",align:null},"Description"))),Object(r.b)("tbody",{parentName:"table"},Object(r.b)("tr",{parentName:"tbody"},Object(r.b)("td",{parentName:"tr",align:null},Object(r.b)("inlineCode",{parentName:"td"},"fire(title, message, date)")),Object(r.b)("td",{parentName:"tr",align:null},"Déclenche une notification d'alerte")))),Object(r.b)("ul",null,Object(r.b)("li",{parentName:"ul"},"Les trois paramètres sont ",Object(r.b)("strong",{parentName:"li"},"obligatoires")," — un appel sans ",Object(r.b)("inlineCode",{parentName:"li"},"title"),", ",Object(r.b)("inlineCode",{parentName:"li"},"message")," ou ",Object(r.b)("inlineCode",{parentName:"li"},"date")," est ignoré (un avertissement est écrit dans le journal)."),Object(r.b)("li",{parentName:"ul"},"Gardez le ",Object(r.b)("strong",{parentName:"li"},"titre de l'alerte constant"),", ne générez pas de titres uniques. Chaque titre ne se déclenche qu'une fois par exécution (",Object(r.b)("strong",{parentName:"li"},"20")," titres maximum par exécution)."),Object(r.b)("li",{parentName:"ul"},Object(r.b)("inlineCode",{parentName:"li"},"date")," est le ",Object(r.b)("inlineCode",{parentName:"li"},"startTime")," du chandelier auquel le signal appartient : le même titre ne notifie à nouveau que lorsqu'il se déclenche pour un chandelier ",Object(r.b)("strong",{parentName:"li"},"plus récent"),". Les signaux provenant de chandeliers antérieurs au moment où l'utilisateur a créé son alerte sont ignorés (anti-spam).")),Object(r.b)("p",null,Object(r.b)("strong",{parentName:"p"},"Exemple :")),Object(r.b)("pre",null,Object(r.b)("code",{parentName:"pre",className:"language-js"},'if (candle.volume > alertVolumeThreshold) {\n alert.fire("High volume", `vol ${candle.volume}`, candle.startTime);\n}\n')),Object(r.b)("h3",{id:"alertes-sur-les-formes--plotshapealert"},"Alertes sur les formes — ",Object(r.b)("inlineCode",{parentName:"h3"},"plot.(...).alert(...)")),Object(r.b)("p",null,"Toutes les formes géométriques (",Object(r.b)("inlineCode",{parentName:"p"},"box"),", ",Object(r.b)("inlineCode",{parentName:"p"},"square"),", ",Object(r.b)("inlineCode",{parentName:"p"},"diamond"),", ",Object(r.b)("inlineCode",{parentName:"p"},"triangle"),", ",Object(r.b)("inlineCode",{parentName:"p"},"circle"),", ",Object(r.b)("inlineCode",{parentName:"p"},"straightLine"),", ",Object(r.b)("inlineCode",{parentName:"p"},"horizontalLine"),", ",Object(r.b)("inlineCode",{parentName:"p"},"verticalLine"),", ",Object(r.b)("inlineCode",{parentName:"p"},"text"),") retournent un handle doté d'une méthode ",Object(r.b)("inlineCode",{parentName:"p"},".alert()"),".\nL'alerte se déclenche automatiquement avec le ",Object(r.b)("inlineCode",{parentName:"p"},"date1")," de la forme, la logique anti-spam fonctionne donc sans code supplémentaire — c'est la méthode recommandée pour les signaux par chandelier."),Object(r.b)("p",null,"Deux formes :"),Object(r.b)("pre",null,Object(r.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(r.b)("p",null,"Le callback reçoit les données de la forme (",Object(r.b)("inlineCode",{parentName:"p"},"date1"),", ",Object(r.b)("inlineCode",{parentName:"p"},"price1"),", ",Object(r.b)("inlineCode",{parentName:"p"},"date2"),", ",Object(r.b)("inlineCode",{parentName:"p"},"price2"),", ...)."),Object(r.b)("h3",{id:"alertes-sur-les-séries--plotseriesalert"},"Alertes sur les séries — ",Object(r.b)("inlineCode",{parentName:"h3"},"plot.(...).alert(...)")),Object(r.b)("p",null,"Les séries de données (",Object(r.b)("inlineCode",{parentName:"p"},"lineSeries"),", ",Object(r.b)("inlineCode",{parentName:"p"},"barSeries"),", ",Object(r.b)("inlineCode",{parentName:"p"},"candleSeries"),", ",Object(r.b)("inlineCode",{parentName:"p"},"areaSeries"),", ",Object(r.b)("inlineCode",{parentName:"p"},"bandSeries"),") retournent également le handle ",Object(r.b)("inlineCode",{parentName:"p"},".alert()"),".\nLe callback reçoit le ",Object(r.b)("strong",{parentName:"p"},"dernier point de données")," et la date du dernier chandelier, l'alerte se déclenche avec cette date."),Object(r.b)("ul",null,Object(r.b)("li",{parentName:"ul"},Object(r.b)("inlineCode",{parentName:"li"},"lineSeries")," / ",Object(r.b)("inlineCode",{parentName:"li"},"barSeries")," / ",Object(r.b)("inlineCode",{parentName:"li"},"areaSeries")," — le callback reçoit la dernière ",Object(r.b)("strong",{parentName:"li"},"valeur")," (un nombre)"),Object(r.b)("li",{parentName:"ul"},Object(r.b)("inlineCode",{parentName:"li"},"candleSeries")," — le callback reçoit le dernier chandelier ",Object(r.b)("inlineCode",{parentName:"li"},"{open, close, high, low}")),Object(r.b)("li",{parentName:"ul"},Object(r.b)("inlineCode",{parentName:"li"},"bandSeries")," — le callback reçoit le dernier ",Object(r.b)("inlineCode",{parentName:"li"},"{upper, lower}"))),Object(r.b)("pre",null,Object(r.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(r.b)("p",null,"Une condition qui reste vraie notifie une fois par ",Object(r.b)("strong",{parentName:"p"},"nouveau chandelier"),". Pour des signaux ponctuels, utilisez des conditions de type croisement (",Object(r.b)("inlineCode",{parentName:"p"},"ta.crossover"),")."),Object(r.b)("h3",{id:"exemple-complet"},"Exemple complet"),Object(r.b)("pre",null,Object(r.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(r.b)("h2",{id:"exemples-dindicateurs"},"Exemples d'indicateurs"),Object(r.b)("pre",null,Object(r.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(r.b)("pre",null,Object(r.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(r.b)("pre",null,Object(r.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(r.b)("pre",null,Object(r.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(r.b)("pre",null,Object(r.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(r.b)("p",null,"Order Book Pressure (bandes de déséquilibre de profondeur bid / ask autour du prix, comme les superpositions « Order Book Pressure » des terminaux de trading) :"),Object(r.b)("pre",null,Object(r.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/fr/Scripting.mdx"}}),p.isMDXComponent=!0}}]); //# sourceMappingURL=component---src-components-fr-scripting-mdx-a289ff05a1a60cce191f.js.map