Skip to content
ChartDeck ChartScript docs Account Open the terminal

Libraries

Share functions, types, enums and constants between your scripts with library(), export and import, and learn how versions and updates work.

Once you have a few scripts, the same helpers start to appear in each of them: a band calculation, a level type, a color scheme. A library is a script that holds such code once. Other scripts import it and use what it exports, so a fix in the library reaches every script that uses it.

#Writing a library

A library starts like any script, with library(...) in place of indicator(...). Mark what other scripts may use with export in front of the declaration:

ChartScriptOpen in editor
//@chartscript 1
library("BandTools", { overlay: true })

//@type A price band with an upper and a lower edge.
export type Band {
    upper: float
    lower: float
}

//@function The middle of a band.
export method Band.mid() => (this.upper + this.lower) / 2

//@function The width of a band as a percentage of its middle.
export method Band.widthPct() => (this.upper - this.lower) / this.mid() * 100

export enum Side {
    above = "Above the band",
    inside = "Inside the band",
    below = "Below the band"
}

export const WIDE: float = 8.0

//@function Bollinger-style band: average plus and minus a number of standard deviations.
//@param src The series to measure.
//@param len Number of bars.
//@param mult Width in standard deviations.
export function stdevBand(src: float, len: int, mult: float = 2.0) {
    const centre = ta.sma(src, len)
    const dev = ta.stdev(src, len)
    return Band.new(centre + mult * dev, centre - mult * dev)
}

//@function Where a price is relative to a band.
export function sideOf(b: Band, price: float) {
    return price > b.upper ? Side.above : price < b.lower ? Side.below : Side.inside
}

const clamp = (x: float, lo: float, hi: float) => math.max(lo, math.min(hi, x))  // not exported: internal helper

// a preview for the editor; importing scripts do not get this plot
const b = stdevBand(close, 20)
plot(b.mid(), "Middle", color.muted)

What can be exported:

KindExample
functionsexport function stdevBand(src: float, len: int, mult: float = 2.0) { … }
methodsexport method Band.mid() => …
typesexport type Band { … }
enumsexport enum Side { … }
constantsexport const WIDE: float = 8.0

Everything else stays private to the library: clamp above cannot be called from outside. Variables cannot be exported, only constants whose value is known when the script is compiled (CS2405 otherwise). An arrow function is exported the same way: export const half = (x: float) => x / 2.

A library can have plots, so you can preview it on a chart while you write it. Those plots are not part of what other scripts import.

Save the library under the name you gave in library("…"). Its saved version number (1 for the first save, then 2, 3 …) is the version other scripts import.

#Importing a library

Add an import line near the top of a script, then use the library's exports through the name you gave it:

ChartScriptOpen in editor
//@chartscript 1
indicator("Band position", { overlay: true })
import * as BT from "@user/BandTools@1"

const len = input.int(20, "Length", { minval: 2 })
const mult = input.float(2.0, "Width (std devs)", { minval: 0.5, step: 0.5 })

const band = BT.stdevBand(close, len, mult)
const side = BT.sideOf(band, close)
const squeeze = band.widthPct() < BT.WIDE / 2

const pu = plot(band.upper, "Upper", color.muted)
const pl = plot(band.lower, "Lower", color.muted)
fill(pu, pl, squeeze ? color.new(color.orange, 85) : color.new(color.accent, 92), "Band")
plot(band.mid(), "Middle", side == BT.Side.above ? color.up : side == BT.Side.below ? color.down : color.muted, 2)

import * as BT from "@user/BandTools@1" reads: import everything the library exports, under the name BT. The path in quotes has three parts:

  • @user is the author's handle. Today a script can import only your own libraries, so this part is not checked yet.
  • BandTools is the library's name, as written in its library("…") line.
  • @1 is the library version to use. It must be an exact saved version.

Exported types and enums are used through that name too: BT.Band, BT.Side.above. Exported methods work with a dot on the values they belong to, as band.widthPct() shows. import lines may come before or after the indicator(...) line.

#Importing single names

To use a few exports without a prefix, list them in braces. as gives one a different name in your script:

ChartScriptOpen in editor
//@chartscript 1
indicator("Band middle", { overlay: true })
import { stdevBand, WIDE as wideBand } from "@user/BandTools@1"

const band = stdevBand(close, 20)
plot(band.mid(), "Middle", band.widthPct() > wideBand ? color.orange : color.muted)

Both forms import the same library; the names in braces are only shortcuts for BandTools.stdevBand and BandTools.WIDE. An imported name cannot be declared again in your script (CS2002).

#Versions and updates

Libraries are copied into the importing script when it is compiled, at the exact version you named. This has two consequences:

  • A script never changes behind your back. Saving version 2 of a library does not change scripts that import version 1.
  • To pick up a new version, change the number and save the script again. The script then compiles with the new library code.

The editor checks an import line against your saved libraries as you type. If it underlines the line as not found (CS4001), the library is not saved under exactly that name, or that version does not exist: save the library first, and use one of its version numbers.

After the name you gave the import (BT. above) the editor lists what the library exports, with each function's signature and its //@function description, and hovering an imported name shows the same with the library, version and line it comes from.

#Rules and restrictions

  • export is only allowed in a library (CS4005).
  • A script can only use what the library exports (CS4006).
  • The import path must name a library, not an indicator (CS4007), and one that exists at that version (CS4001).
  • Functions cannot create inputs: input.* is only allowed at the top level (CS3007). Settings belong to the script that uses the library; pass the values in as parameters, as len and mult above.
  • Libraries may import other libraries, but not in a circle (CS4002).
  • An error inside a library is reported once, on the import line of the script that uses it, with the library's line number (CS4004).
  • Exported functions may use request.*. Each such call counts toward the importing script's limit of other timeframes and markets.
  • A script may import up to 32 libraries, and the code of the libraries it imports counts toward its own size limit.
  • A library that your own scripts import cannot be deleted or renamed, and the version they import cannot be deleted. Remove the import first.

#Sharing a library in the Community

A library can be published in the Community like a script, with three differences:

  • Open source and public only. A library is compiled into every script that imports it, in the reader's browser, so its code cannot be hidden. It cannot be protected or invite-only.
  • A one-word name. Other scripts write the name into their import line, so it is letters, digits and _ only.
  • It is imported, not added to a chart. Its Community card shows a Copy import line button in place of Add.

Another member imports it by your handle, its name and an exact version:

Code
import * as Tools from "@yourhandle/Tools@1"
  • The version must be one you have published. Publishing version 3 makes versions 1 to 3 importable, and those versions can then no longer be deleted.
  • Publishing a newer version changes nothing for scripts that import an older one. They move on only when their author edits the import line.
  • A script that imports libraries can be published too, open or protected, free or paid. Every library it names must be a published Community library, so publish your own library first. Its listing credits each library: "Uses: Tools@1 by @yourhandle".
  • While other members' scripts import your library you cannot delete or rename it. You can unpublish it: scripts that already import it keep working, and nobody new can import it.
  • If a moderator removes a library, scripts that import it stop with "The library ... was not found".

Calling something the library keeps private:

ChartScriptError: CS4006
//@chartscript 1
indicator("Private helper")
import * as BT from "@user/BandTools@1"
plot(BT.clamp(close, 0, 100))

Exporting from an ordinary script:

ChartScriptError: CS4005
//@chartscript 1
indicator("Not a library")
export const double = (x: float) => x * 2
plot(double(close))

#Good to know

  • Give exported functions typed parameters (src: float, len: int). It documents the library and gives callers clear errors at the call.
  • //@function, //@param, //@returns, //@type and //@field comments above exports feed the editor's hover cards, so a library documents itself.
  • Stateful functions in a library (anything using ta.*, history or state) keep separate state per call site in the importing script, exactly like your own functions.
  • Keep libraries focused. A library is compiled into every script that imports it, so each of those scripts carries what it uses from the library.

The ChartScript documentation is in English for now; translations will follow.