CoinScriptLanguage

Language

The CoinScript reference: data series from candles through order flow and company figures to on-chain and macro, other symbols, functions, plot, signal, hline, alertcondition, input, your own calculations and what a script may not do.

CoinScript is JavaScript with a fixed library. Most of what you know from JavaScript works: const and let, functions, arrow functions, if, loops, Math. What is blocked is listed below under “What a script may not do”. On top of that come the chart's data series and the functions on this page. In the editor you can find the same overview in short form on the Reference tab below the code.

How a script calculates

A script runs once over all the candles the chart has loaded, not candle by candle.

  • Each data series is a list with exactly one value per candle. Index 0 is the oldest candle, the last index bars - 1 the newest, meaning the running one.
  • Missing values are NaN.
  • The functions take whole series and return whole series. ema(close, 21) is a list with the EMA for each candle.
  • Instead of a series you can also pass a number anywhere, for example cross(close, 85000).
CoinScript
// The last and the second-to-last close
const last = close[bars - 1]
const prev = close[bars - 2]
console.log('Letzter Schluss', last, 'davor', prev)
plot(close, { title: 'Close', overlay: true })

The running candle changes until it closes. What a script calculates for the last candle can still change until then, and a signal can disappear again.

If the chart loads older candles as you scroll back, the list gets longer and the indices shift. So always calculate relatively, for example with i - 1 or bars - 1, never with fixed indices.

Data series

NameContent
open, high, low, closeOpen, high, low and close of the chart's candles
timeEach candle's open time in milliseconds since 1970 (UTC), for weekdays, sessions and fixed cycles
volumeVolume of the chart's candles, summed across all the chart's exchanges
buyVolTaker buy volume per candle
sellVolTaker sell volume per candle
deltaTaker buy minus taker sell volume per candle
cvdRunning sum of delta
oiOpen interest in coin, sum of the exchanges
fundingFunding rate in % per 8 hours, average of the exchanges
liqLong, liqShortLiquidated longs and shorts per candle in USD, sum of the exchanges
tradesNumber of trades per candle
buyTrades, sellTradesNumber of buys and sells per candle (by taker side)
bidDepth, askDepthOrder book: buy and sell orders within 1% around the middle of the book in USD
bidWall, askWallOrder book: price of the largest buy wall below and the largest sell wall above the middle
bidWallSize, askWallSizeOrder book: amount of these walls in USD
revenue, netIncome, epsStocks: revenue, earnings and earnings per share of the most recently reported quarter
revenueTtm, netIncomeTtm, epsTtmStocks: the same figures over the last twelve months
peStocks: P/E, meaning close divided by epsTtm
revenueEst, epsEstStocks: revenue and earnings per share that analysts had expected for the most recently reported quarter
margin, marginTtmStocks: net margin in %, per quarter and over twelve months
cashStocks: cash and equivalents at the last quarter end
interest, interestTtmStocks: interest plus dividends on preferred stock, per quarter and over twelve months
holdings, mnavCrypto companies: holdings of the largest coin and mNAV
barsNumber of candles (a number, not a series)

All series have the same length as the chart's candles and apply to its symbol and timeframe. You read further data series from the terminal, such as on-chain values, yields, central banks, ETF flows or COT, with feed(), and the candles of other symbols with symbol(). A script does not know other timeframes or the time of day of the candles.

If a series does not exist for the symbol, such as company figures for Bitcoin or order flow for a stock, it contains only NaN, and the console says so.

Order flow series

buyVol, sellVol, delta and cvd are based on taker volume, meaning whether a trade went into the market as a buy or a sell.

buyVol + sellVol does not always equal volume, which can include several exchanges. A candle without a taker figure is NaN. Symbols without order flow, such as stocks or forex, have only NaN in these series, and the console says so.

cvd starts with the first loaded candle that has a taker figure. Its level therefore depends on how far back the chart reaches. Compare the course, not the absolute level.

Open interest and funding

oi sums the open interest of the exchanges, funding averages their funding rates. Each candle carries the last known value, so funding moves in steps. Before the first known value it is NaN.

oi and funding go back about 1,000 candles of the timeframe, but at least one day. If the chart has loaded more candles, they are NaN for the older ones. Both exist only for crypto perpetuals.

Note. In a script, oi is in coin, not in dollars. In alerts, on the other hand, open interest is in $M.

Liquidations and trades

liqLong and liqShort are the liquidated long and short positions per candle in US dollars, summed across all exchanges, as in the Liquidations pane with the source “Aggregated”. trades counts the trades per candle, buyTrades and sellTrades split them by whether the taker bought or sold.

A candle without a liquidation has the value 0. NaN means there is no figure for this candle. The series exist for crypto symbols, from the minute timeframe up.

CoinScript
// Liquidations: longs up, shorts down, in $M
plot(liqLong.map(v => v / 1e6), { title: 'Longs', style: 'hist', color: '#FF1919' })
plot(liqShort.map(v => -v / 1e6), { title: 'Shorts', style: 'hist', colorDn: '#00FFDA' })
hline(0)

Order book

The order book series are what the order book heatmap and order book top levels show, as numbers per candle:

NameContent
bidDepthBuy orders within 1% around the middle of the book in USD, averaged over the candle
askDepthSell orders within 1% around the middle of the book in USD, averaged over the candle
bidWallPrice of the largest buy wall below the middle, up to 5% away
askWallPrice of the largest sell wall above the middle, up to 5% away
bidWallSize, askWallSizeAmount of these walls in USD
  • Middle of the book: the price between the candle's buy and sell orders.
  • Wall: a price level of 0.05% of the price that holds at least twice as much as the levels around it. The one that stands out most is chosen, as with order book top levels. Without a wall, the value is NaN.
  • Period: from the recorded order book, the running candle from the live book of the open chart. Before recording began it is NaN. Minute candles go back three days, 5-minute candles 30 days and hourly candles one year.
  • Like the heatmap, the order book is part of Plus and available only for cryptocurrencies.
CoinScript
// Book imbalance: above 0 more buy orders, below 0 more sell orders
const imbalance = bidDepth.map((b, i) => (b - askDepth[i]) / (b + askDepth[i]))
plot(imbalance, { title: 'Ungleichgewicht', style: 'hist', color: '#00FFDA', colorDn: '#FF1919' })
hline(0)

// The largest walls as lines on the price chart
plot(bidWall, { title: 'Kaufwand', color: '#00FFDA', overlay: true })
plot(askWall, { title: 'Verkaufswand', color: '#FF1919', overlay: true })

// Alert when the price comes within 0.3% of the buy wall
alertcondition(close.map((c, i) => (c - bidWall[i]) / c < 0.003), { title: 'Kurs an der Kaufwand' })
Note. mantacharts checks script alerts on order book series only while the terminal is open.

Company figures

On stocks, a script reads the figures the indicators under Companies also show:

NameContent
revenueRevenue of the most recently reported quarter
netIncomeEarnings (net income) of the most recently reported quarter
epsEarnings per share (diluted) of the most recently reported quarter
revenueTtm, netIncomeTtm, epsTtmthe same figures over the last twelve months
peP/E: close divided by epsTtm
revenueEstRevenue that analysts had expected for the most recently reported quarter
epsEstEarnings per share that analysts had expected for the most recently reported quarter
margin, marginTtmNet margin in % (netIncome divided by revenue), per quarter and over twelve months; na where earnings exceed revenue or there is hardly any revenue
cashCash and equivalents: cash and short-term investments at the last quarter end, for Strategy the weekly reported USD reserve
interest, interestTtmInterest plus dividends on preferred stock, per quarter and over twelve months
  • From the report day: Each figure stands from the day the company published it and stays until the next report. The series moves in steps. Before that it is NaN.
  • A new report is a candle where the value changes from the candle before, for example revenue[i] !== revenue[i - 1].
  • Unit: revenue and netIncome in the company's reporting currency, usually US dollars. For billions, divide by 1e9.
  • Twelve months: The values with Ttm go back further than the quarterly values and smooth seasonal swings.
  • P/E: pe divides each close by the most recently reported earnings per share over twelve months. Without positive earnings it is NaN.
  • Expectations: revenueEst and epsEst are the expectations the market went into the respective report with. epsEst is usually estimated on an adjusted basis, eps is earnings under accounting rules.
  • For crypto, funds, indices and forex, these series are NaN.
CoinScript
// Twelve-month revenue in billions in its own pane
plot(revenueTtm.map(v => v / 1e9), { title: 'Umsatz 12 M (Mrd.)', color: '#00FFDA' })

A complete example that marks reports with rising revenue and a falling price is under Examples.

Crypto companies

NameContent
holdingsHoldings of the largest coin in units (BTC for Strategy and Metaplanet, ETH for BitMine), from each buy or sale
mnavEnterprise value (market cap plus debt and preferred stock, minus cash and equivalents) divided by the value of the coins per candle, as Strategy reports it; 1 means exactly the value of the coins

For companies without coins on their balance sheet, both are NaN. For miners, you read the network data with feed():

CoinScript
// Hashprice in USD per PH/s per day
const einnahmen = feed('btc-miner-revenue')
const hashrate = feed('btc-hashrate')
plot(einnahmen.map((v, i) => v / (hashrate[i] / 1000)), { title: 'Hashprice', color: '#00FFDA' })

More data series with feed

feed('name') returns another data series from the terminal as a series with one value per candle, for example feed('btc-sopr') or feed('us10y').

  • Alignment: Each candle carries the last known value. Daily, weekly and monthly values stand from the day they apply to. Before the first value it is NaN.
  • Stale values: If no new value follows for a long time, NaN appears instead of the old value: for yields and other trading-day values after five days, for the crypto market's daily values after two to three days, for weekly values after two weeks, for monthly values after about three months. An ETF net inflow applies only to its day. On-chain values and policy rates apply until the next value.
  • Independent of the chart: The series are the same no matter which symbol the script runs on.
CoinScript
// SOPR with 1 as the boundary and an alert condition
const sopr = feed('btc-sopr')
plot(sopr, { title: 'SOPR', color: '#FFEB3B' })
hline(1)
alertcondition(sopr.map((v, i) => i > 0 && sopr[i - 1] < 1 && v >= 1), 'SOPR über 1')
Tip. Monthly values such as us-cpi or us-m2 only appear weeks after the month they apply to. In the backtest they still stand from the first day of their month. Keep this in mind if a strategy relies on them.

These names exist:

On-chain

NameContentUnit
btc-mvrv-zBitcoin MVRV z-score–
eth-mvrv-zEthereum MVRV z-score–
btc-soprBitcoin SOPR–
btc-netflowBitcoin exchange netflow per dayUSD
eth-netflowEthereum exchange netflow per dayUSD
btc-exchange-supplyBitcoin on exchangesBTC
eth-exchange-supplyEther on exchangesETH
btc-active-addressesBitcoin active addressesaddresses
eth-active-addressesEthereum active addressesaddresses
btc-hashrateBitcoin hashrateTH/s
btc-difficultyBitcoin mining difficulty–
btc-supplyBitcoin in circulationBTC
btc-tx-feesBitcoin transaction fees per dayUSD
btc-miner-revenueBitcoin miner revenue per dayUSD

Crypto market

NameContentUnit
btc-marketcapBitcoin market capUSD
usdt-marketcapUSDT market capUSD
stablecoin-capStablecoin market capUSD
btc-dominanceBitcoin dominance%
usdt-dominanceUSDT dominance%
fear-greedFear & Greed Index0–100

ETF flows

NameContentUnit
btc-etf-flowBitcoin ETFs net inflow per dayUSD
eth-etf-flowEther ETFs net inflow per dayUSD
btc-etf-assetsBitcoin ETFs assets under managementUSD

Rates and bonds (US)

NameContentUnit
us1mUS 1-month yield%
us3mUS 3-month yield%
us6mUS 6-month yield%
us1yUS 1-year yield%
us2yUS 2-year yield%
us3yUS 3-year yield%
us5yUS 5-year yield%
us7yUS 7-year yield%
us10yUS 10-year yield%
us20yUS 20-year yield%
us30yUS 30-year yield%
us10y2yYield curve 10 years minus 2 years%
us10y-inflation10-year inflation expectation%
us10y-real10-year real yield%
hy-spreadHigh-yield spread%
baa-spreadBaa spread over 10 years%

Money and central banks

NameContentUnit
us-m2US money supply M2B USD
fed-balanceFed balance sheet totalM USD
tgaTreasury General AccountM USD
rrpOvernight reverse repoB USD
ecb-balanceECB balance sheet totalM EUR
boj-balanceBoJ balance sheet total100M JPY
fed-fundsFed funds rate, monthly average%
fed-target-upperFed target range, upper bound%
fed-target-lowerFed target range, lower bound%
policy-rate-usPolicy rate US%
policy-rate-eurPolicy rate eurozone%
policy-rate-jpPolicy rate Japan%
policy-rate-auPolicy rate Australia%

Economy (US)

NameContentUnit
us-cpiUS consumer price indexindex
us-core-cpiUS core consumer price index, not seasonally adjustedindex
us-unemploymentUS unemployment rate%
us-industrialUS industrial productionindex
us-dollar-broadUS dollar index, broadindex

COT positioning

cot-<markt>-<gruppe> is the net position of a trader group (long minus short) in contracts, from the weekly COT report, for example feed('cot-btc-lev'). Each value stands from the report's reference date (Tuesdays), and it is published the following Friday.

MarketsGroups
btc, mbt, eth, met, sol, xrp, es, nq, zn, zt, zq, eur, jpy, gbp, dxydealer Dealers / intermediaries, am Asset managers, lev Leveraged funds, oth Other reportables, nr Non-reportables
gc Gold, si Silver, hg Copper, cl WTI crude oil, ng Natural gaspm Producers / merchants, sd Swap dealers, mm Managed money, oth Other reportables, nr Non-reportables

The markets are named as in the COT widget: btc CME Bitcoin, mbt CME Micro Bitcoin, eth CME Ether, met CME Micro Ether, sol CME Solana, xrp CME XRP, es S&P 500 E-mini, nq Nasdaq-100 E-mini, zn 10Y T-Note, zt 2Y T-Note, zq Fed Funds, eur Euro FX, jpy Japanese yen, gbp British pound, dxy US dollar index.

Other symbols with symbol

symbol('name') returns the candles of another symbol in the chart's timeframe as an object with the series open, high, low, close and volume.

CoinScript
// Ether against Bitcoin: the price of ETH divided by that of the chart
const eth = symbol('ETH')
plot(eth.close.map((c, i) => c / close[i]), { title: 'ETH / Chart', color: '#B28CFF' })
  • Which names: any symbol from the symbol search, just as you type it there: crypto ('ETH', 'SOL'), stocks ('AAPL'), indices ('SPX'), forex ('EURUSD'), commodities ('XAU'), bonds ('US10Y'), economic data ('USIRYY'), companies before their IPO ('OPENAI', 'ANTHROPIC': their valuation in billions of US dollars, per day; after the IPO, the stock price), ratios ('ETH/BTC') and spreads ('BTC-ETH').
  • Alignment by time: Each candle of the chart carries the symbol's last candle that began at the same time or earlier. If the symbol is not trading, such as a stock on the weekend, it carries the values of its last candle before that, including volume. Before its first candle it is NaN.
  • How far back: as far as the chart, as long as the symbol has candles going back that far.

What feed and symbol need

A script runs without a network. The terminal therefore loads the data for feed() and symbol() before the run, and for that the name must be in the code:

  • Write the name as text in quotation marks directly in the call: feed('btc-sopr'), not feed(name). A calculated name is an error.
  • A script reads at most 8 different names in feed() and symbol() combined.
  • An unknown name is an error with the line. The message suggests similar names.
  • Store the result in a constant, for example const eth = symbol('ETH'), instead of calling the function in a loop.
Note. If one of your own variables or functions at the top of the script has the same name as a data series or function, for example const eps = 1e-9, yours applies in the whole script. Give it a different name if you need the series.

Functions

FunctionResult
sma(serie, länge)Simple moving average
ema(serie, länge)Exponential moving average
rsi(serie, länge)Relative Strength Index after Wilder, länge defaults to 14
stdev(serie, länge)Standard deviation over the window
zscore(serie, länge)Distance from the average in standard deviations
highest(serie, länge)Highest value of the last länge candles
lowest(serie, länge)Lowest value of the last länge candles
change(serie, n)Difference from the value n candles earlier, n defaults to 1
cross(a, b)true at every candle where a and b cross
feed('name')Another data series from the terminal, see above
symbol('name')Candles of another symbol, see above

In more detail:

  • sma is NaN until there are enough values. If the window contains a gap (NaN), the average is NaN too, and the next full window returns a number again.
  • ema starts with the first value of the series and smooths with the factor 2 / (länge + 1). The first values are therefore still imprecise. The EMA skips gaps.
  • rsi returns the first value after länge price changes. A gap counts as an unchanged value.
  • stdev divides by länge (population standard deviation). zscore is (wert − sma) / stdev over the same window.
  • highest and lowest include the current candle and skip NaN. At the start of the series, they take the candles that already exist.
  • change is NaN for the first n candles. n must be a whole number.
  • cross detects crossovers in both directions and returns a list of true and false. If the two are exactly level at a candle, that does not count as a crossover.

For one direction, compare yourself:

CoinScript
// Crossover upward only or downward only
const fast = ema(close, 9)
const slow = ema(close, 21)
const up = fast.map((f, i) => i > 0 && fast[i - 1] <= slow[i - 1] && f > slow[i])
const down = fast.map((f, i) => i > 0 && fast[i - 1] >= slow[i - 1] && f < slow[i])
signal(up, { title: 'Nach oben', color: '#00FFDA', pos: 'below' })
signal(down, { title: 'Nach unten', color: '#FF1919', pos: 'above' })

Drawing and signals

plot

plot(serie, { title, color, colorDn, overlay, style }) draws a series.

OptionMeaningDefault
titleName in the legend, up to 60 characters“Plot 1”, “Plot 2” …
colorColor of the line or the positive bars'#FFFCF0'
colorDnColor of the negative bars in the histogram'#FF1919'
overlaytrue draws on the price chartown pane
style'hist' draws a histogramline
  • All plots without overlay: true share one pane below the chart. Its scale adjusts to the visible values.
  • On the price chart, plots are always lines and use the price scale. The scale still follows the candles: a series in a different unit, such as cvd, then lies outside the visible range. Put such series in their own pane.
  • The first plot's color appears in the legend. Script alerts that compare with a value also use the first plot.

You specify colors as hex values. The terminal's house colors are #00FFDA (buy, rising), #FF1919 (sell, falling), #FFEB3B (note) and #FFFCF0 (neutral).

hline

hline(wert, { color }) draws a dashed horizontal line in the script's pane, for example at 0, 30 or 70. It does not appear on the price chart, so it needs at least one plot without overlay.

signal

signal(bedingung, { title, color, pos }) places a triangle at every candle where bedingung is true.

OptionMeaningDefault
titleName of the signal, also for alerts'Signal'
colorColor of the triangle'#FFEB3B'
pos'below' under the low, 'above' over the high'below'

In the backtest, signals with pos: 'below' are long entries and signals with pos: 'above' are short entries.

alertcondition

alertcondition(bedingung, { title }) draws nothing. It sets a condition you can put a script alert on in the alert dialog. Instead of the object, the title alone also works: alertcondition(bedingung, 'Ausbruch'). Without a title, it is called “Condition 1”, “Condition 2” …

Any list of true and false or 1 and 0 works as a condition, such as the result of cross() or of close.map(…).

Adjustable values with input

input(name, standard) makes a number adjustable and returns its current value.

CoinScript
const len = input('Länge', 21)
const k = input('Faktor', 2.5)
const mid = ema(close, len)
const sd = stdev(close, len)
plot(mid, { title: 'EMA', overlay: true })
plot(mid.map((v, i) => v + k * sd[i]), { title: 'Oben', overlay: true })
Tip. Calculate series like sd once before the map, not inside it. A stdev() inside map would recalculate the whole series for every candle and blow the time limit.
  • On the chart, the values appear in the indicator's settings under Inputs. You set whole numbers in steps of one, numbers with decimals in the precision of the default (for 2.5, in steps of 0.1).
  • The name is also the label, up to 40 characters. Each name counts once.
  • Only numbers become settings. Any other default, such as a text, comes back unchanged.
  • Script alerts always calculate with the default. The optimization varies the first two inputs.
Tip. Write the default as a simple number directly in the call, for example input('Länge', 21). Only then can the optimization write the best value into your code with Apply.

Output to the console

console.log(…) writes to the editor's console, console.warn(…) in yellow, console.error(…) in red. A series appears with its last five values and its length, for example [… 125, 126, 127, 128, 129] (30). The console shows up to 50 outputs of 300 characters each per run.

The output only appears with Run. When the script runs on the chart, it writes nothing to the console.

Calculating your own series

Arithmetic operators do not work on whole series: close - open does not give a list, but NaN. For your own series, go over each candle with map. The second parameter is the index:

CoinScript
// Candle body in percent: (close − open) / open
const body = close.map((c, i) => (c - open[i]) / open[i] * 100)
plot(body, { title: 'Körper %', style: 'hist', color: '#00FFDA', colorDn: '#FF1919' })
hline(0)

map on a data series returns a list of numbers again, with true and false becoming 1 and 0. This is how conditions for signal() and alertcondition() are made. If you access close[i - 1] at the first candle, you get undefined, and the calculation gives NaN. That is fine, the candle then shows nothing.

For calculations that need the value of the previous candle, a loop works well:

CoinScript
// Average True Range, simple average
const tr = new Array(bars).fill(NaN)
for (let i = 1; i < bars; i++) {
  tr[i] = Math.max(high[i] - low[i], Math.abs(high[i] - close[i - 1]), Math.abs(low[i] - close[i - 1]))
}
plot(sma(tr, input('Länge', 14)), { title: 'ATR', color: '#FFEB3B' })
Warning. Always give plot(), signal() and alertcondition() a list with one value per candle. A shorter list, for example from close.slice(-50), starts at the oldest candle and then no longer lines up with the candles.

You write your own helper functions with function or as arrow functions. Also available are Math, Number, String, Boolean, Object, Array, JSON, Date, Map, Set and the typed arrays such as Float64Array.

What a script may not do

Scripts run isolated in the browser. The terminal loads the data on this page itself before the run. The editor rejects:

  • import and export,
  • its own network and its own storage: fetch, XMLHttpRequest, WebSocket, localStorage and the like do not exist in a script,
  • timers and concurrency: setTimeout, setInterval, Promise, async functions and generators,
  • this outside a function, with, debugger and classes,
  • the names globalThis, self, window, eval, Function, constructor, prototype and __proto__, also as a property or in a text.

Common messages

MessageMeaning
“name” is not definedA typo, or the name is not part of CoinScript
Syntax error: Unexpected characterA bracket, comma or quotation mark is missing or there is one too many
“window” is not allowed in CoinScriptA blocked name, see above
fetch is not a functionThere is no network access of your own in CoinScript. You read data with the series above, feed() and symbol()
Unknown data feed “…” in feed()A typo in the name. The message names similar names, and all of them are listed above
Unknown symbol “…” in symbol()The symbol does not exist in the symbol search. The message names similar ones
feed() needs the name as text in quotation marksThe name is calculated, for example feed(name). Write it in directly: feed('btc-sopr')
At most 8 different names in feed() and symbol() per scriptSplit up the script
No data for revenue on BTC: the values stay empty.The series does not exist for this symbol, or no value came for these candles. The script still runs
Assignment to a constantA const was given a new value, use let
Too many plot() calls (at most 24)Usually a plot() in a loop
Memory limit: at most 5,000,000 values per arrayAn array has become too large
Script aborted: time limit exceededThe script calculates for longer than 250 ms, for example because of an endless loop
CoinScript could not be startedThe calculation environment did not load. Reload the page

Errors in the code name the line and column. Clicking the message in the console puts the cursor at that spot.