Text and number formatting
Write text with quotes, build messages with formatted text, control decimals, signs, percentages and times, and know which braces belong to the alert server.
Text appears in labels, tables, alert messages and plot titles. This page shows how to write it, how to put numbers in it, and how to make the numbers look right.
Quotes and escapes#
A text is written in double or single quotes. Either kind may hold the other. A backslash starts an escape:
| Write | Means |
|---|---|
\n | a new line |
\t | a tab |
\" \' \\ | the quote or the backslash itself |
\u{1F600} | the character with this code (one to six hex digits) |
Triple quotes hold several lines and anything inside them:
indicator "Text in labels"
edition = 1
display = price
if bar.is_last
label.new(
bar.index,
high,
"""Last bar
Quotes "like this" and 'this' are fine""",
style = label_down,
)
plot(close)Join texts with +. A number joined to a text becomes its plain text ("a" + 1 is a1); to choose the decimals or the sign, use a formatted text.
Formatted text#
f"..." is a text with expressions inside braces. After a colon you say how to write the value.
indicator "Formatted text"
edition = 1
display = price
let change = close - close[1]
let share = close / close[1] - 1
if bar.is_last
let t = table.new(top_right, 2, 4)
table.cell(t, 0, 0, "Close")
table.cell(t, 1, 0, f"{close:price}")
table.cell(t, 0, 1, "Change")
table.cell(t, 1, 1, f"{change:+.2f}")
table.cell(t, 0, 2, "Change %")
table.cell(t, 1, 2, f"{share:+.2%}")
table.cell(t, 0, 3, "Bar")
table.cell(t, 1, 3, f"{bar.index:06}")
plot(close)| Format | Value | Result |
|---|---|---|
{x:.2f} | 3.14159 | 3.14 |
{x:.0f} | 4318.08 | 4318 |
{x:,.2f} | 1234567.891 | 1,234,567.89 |
{x:+.1f} | 0.5 | +0.5 |
{x:.1%} | 0.256 | 25.6% |
{x:03} | 7 | 007 |
{x:price} | a price | the price with the symbol's own number of decimals |
{x} | any value | the value as plain text (true, 2999) |
A missing number (na) is written na. Give a fallback after a question mark:
| Format | Value | Result |
|---|---|---|
{x:.1f} | na | na |
{x:.1f?'n/a'} | na | n/a |
A time is written with the format time(...), which takes a pattern and a time zone:
indicator "Time in text"
edition = 1
display = price
input zone: text = "Asia/Tehran", name = "Clock zone"
if bar.is_last
label.new(
bar.index,
high,
f"{bar.time:time('HH:mm', zone)} {bar.time:time('yyyy-MM-dd', 'UTC')}",
)
plot(close)An unknown format is an error and names the format:
indicator "Unknown format"
edition = 1
if bar.is_last
label.new(bar.index, high, f"{close:10.2f}")
plot(close)Braces for the character itself are doubled: f"{{x}}" is the text {x}. A brace that is neither closed nor doubled is E1010.
A text in single or double quotes must be closed on the same line (E1001); only a triple-quoted text may span lines. A quote of the other kind may nest inside the braces: f"{'a'}".
Alert placeholders are not f-text#
The alert server fills words in double braces, {{ticker}} and the like, in a plain text. (Alerts on your own scripts are switched off in this release, but the rule holds for the text you write.) Inside an f-text the double brace is the character itself, so f"{{ticker}}" is {ticker}. The checker warns about it:
indicator "Placeholder in an f-text"
edition = 1
display = price
alert.when(close > open, "Up", message = f"{{ticker}} closed up")
plot(close)Write the placeholder as a plain text: "{{ticker}} closed up". Alert keys and options lists the placeholders.
Text functions#
The text group has the usual helpers. Most can also be written with a dot.
indicator "Text functions"
edition = 1
display = price
let clock = "10:30"
let parts = clock.split(":")
let hours = text.to_number(parts.at(0))
let minutes = text.to_number(parts.at(1))
plot(hours * 60 + minutes, name = "Minutes since midnight")
if bar.is_last
label.new(bar.index, high, text.upper("done") + " " + text.of(close, tick))| Function | Gives |
|---|---|
text.length(s) | the number of characters |
text.upper(s) text.lower(s) | the text in capitals or lower case |
text.contains(s, part) | true if part is in s |
text.replace(s, from, to) | s with the first from replaced |
text.slice(s, start, end) | the characters from start up to, not including, end |
text.split(s, separator) | an array of the pieces |
text.match(s, pattern) | the first part of s that fits the pattern, or an empty text (see Patterns, below) |
text.to_number(s) | the number written in s, or na |
text.fmt(x, format) | a number written with a format such as ".2f" or ",.2f"; an unknown format is E-FMT-SPEC |
text.of(x, format) | a number as text, with a pattern such as "#.##" or tick |
A text one call builds may hold at most 1,000,000 characters.
Patterns#
text.match(s, pattern) looks in s for the first part that fits a pattern and gives that part as a text. When nothing fits, it gives an empty text. A pattern that is only a word, such as "USD", is a plain search.
indicator "A pattern"
edition = 1
let digits = text.match("order-2048-x", "[0-9]+")
plot(text.to_number(digits), name = "The number in the text")The pattern reader of the chart knows:
- ordinary characters, and
.for any character except the end of a line; - classes such as
[a-z]and[^0-9], and the shorthands\d,\w,\sand their opposites\D,\W,\S; ^and$for the start and the end of the text, and\band\Bfor a word boundary;- groups
( )and(?: ), which only group: the call always gives the whole match; |for a choice, and the repeats*,+,?,{n},{n,}and{n,m}, each with a?after it to take as little as possible.
It does not read back-references, look-ahead, look-behind, named groups, \p{…} or \cX; a pattern that uses one is E-REGEX-FEATURE. A pattern past the limits on Limits and budgets is E-LIMIT-REGEX. A pattern that is only malformed, an open bracket for example, gives an empty text. Every step of the reader counts towards the steps of the bar, so a pattern on a very long text ends in E-LIMIT-OPS and can never keep a script busy beyond its budget.
See also: Alert keys and options, Tables, Units and literals.