millionchart
فارسی

Lesson 12: Modules you reuse

Move functions you use in several scripts into a module, save it in My scripts, and import it into your other scripts by its name and version.

Lesson 12 of 18. When the same function turns up in three of your scripts, a fix has to be made three times. A module keeps it in one place: it is a script of your own that draws nothing and only offers functions and types to your other scripts.

Start with a function in the script#

Write the function in the script that needs it first, and make sure it works:

Cuprel
indicator "Range in percent"
    edition = 1
    display = pane

fn range_percent(top: float, bottom: float): float = 100 * (top - bottom) / bottom
plot(range_percent(high, low), name = "Bar range %", style = columns, color = color.teal)
plot(tech.sma(range_percent(high, low), 20), name = "Average", color = color.orange)

fn name(parameters): type = expression declares a function (Lesson 6 covers functions). Once a function works and you want it in other scripts too, move it into a module.

Write the module#

A module starts with module Name in place of indicator "Name". Only what is marked export can be used by other scripts; everything else stays private to the module. This module keeps a running mean in a record (a type with fields) and offers two methods on it:

Cuprel
// A module: a running mean kept in a record. Other scripts import it by name and version.
module Stats
    edition = 1

export type Stats
    n: int = 0
    total: float = 0

export fn Stats.add(self: Stats, x: float)
    self.n += 1
    self.total += x

export fn Stats.mean(self: Stats): float = if self.n > 0 then self.total / self.n else na

To keep it:

  1. Open the editor, choose New script, and replace the example text with the module.
  2. Press Save. The status beside the name says Library — ready to import when the module compiles.
  3. A module draws nothing, so there is nothing to add to the chart. It stays in My scripts like your other scripts, and it counts toward the number of scripts your plan keeps.

The other scripts find the module by the name written after module (here Stats), not by the name it is saved under in the list, and upper and lower case do not matter.

Import it#

Cuprel
// Using the module: the running mean of every close so far, drawn next to the close.
indicator "Running mean"
    edition = 1
    display = price

import Me/Stats@1 as mu
state stats = mu.Stats(n = 0)
stats.add(close)
plot(stats.mean(), name = "Mean of every close so far", color = color.orange)
plot(close, name = "Close", color = color.gray)
  • import Me/Stats@1 as mu brings the module in. Me says it is your own module; only your own modules can be imported in this release. @1 is the version, and it is required. mu is the short name you use for what the module exports.
  • mu.Stats(n = 0) makes a record of the module's type; state keeps it from bar to bar.
  • stats.add(close) and stats.mean() call the module's methods.
  • Every script that imports a module gets its own memory: two scripts that use Stats do not share their running means.

The version cannot be left out:

Cuprel · error E2070
indicator "An import with no version"
    edition = 1
import Me/Stats as mu
plot(close)

A name the module does not export is E3172, and a module that imports itself, directly or through other modules, is E-IMPORT-CYCLE. A module that you do not have gives an error that names it. Modules and libraries has all the rules and limits.

Try changing#

  • Add a third method to the module, export fn Stats.count(self: Stats): int = self.n, save it, and plot stats.count() from the importing script.
  • Remove the word export in front of fn Stats.mean and save the module. The importing script now shows a problem, because mean is no longer offered.
  • Change @1 to nothing (import Me/Stats as mu) and read the error.
  • Move range_percent from the first program into a module of your own called Ranges, and import it with import Me/Ranges@1 as r.

Checkpoint#

  • You can write a module with module Name and mark what it offers with export.
  • You can save it and recognise Library — ready to import.
  • You can import it with import Me/Name@1 as alias and call what it exports.
  • You know that a module is found by the name after module, and that only your own modules can be imported.

Next: Lesson 13: Finding mistakes with the log