millionchart
فارسی

Lesson 13: Finding mistakes with the log

When a script runs but draws the wrong thing. Stop it on purpose with a clear message, write log lines, and look at values with plots, marks and labels.

Lesson 13 of 18. The editor finds mistakes in the text before a script runs (Lesson 18 shows how to read them). This lesson is about the other kind: the script runs, but a number is wrong, a mark never appears, or a setting makes no sense. You will stop a script on purpose, write log lines, and put values where you can see them.

Stop on purpose when a setting makes no sense#

Cuprel
indicator "Two averages, checked"
    edition = 1
    display = price

input fast_length: int = 10, name = "Fast length", min = 1
input slow_length: int = 30, name = "Slow length", min = 2
if fast_length >= slow_length
    log.abort("The fast length must be shorter than the slow length")
plot(tech.ema(close, fast_length), name = "Fast", color = color.green)
plot(tech.ema(close, slow_length), name = "Slow", color = color.red)

log.abort(message) stops the run. The editor then shows Stopped on the chart with your message, so whoever changed the settings knows what to fix. Here the defaults are fine and the script runs. With defaults that break the rule, it stops on the first bar with the code E5637 and your text:

Cuprel · error E5637
indicator "Two averages, wrong defaults"
    edition = 1
    display = price

input fast_length: int = 30, name = "Fast length", min = 1
input slow_length: int = 10, name = "Slow length", min = 2
if fast_length >= slow_length
    log.abort("The fast length must be shorter than the slow length")
plot(tech.ema(close, fast_length), name = "Fast", color = color.green)

A clear stop tells the reader what is wrong; a chart that quietly draws nonsense does not.

Log lines#

Cuprel
indicator "Logged crossings, short"
    edition = 1
    display = price

let fast = tech.ema(close, 10)
let slow = tech.ema(close, 30)
let up = tech.crosses_above(fast, slow)
if up
    log.info("Up cross on bar {0} at {1}", bar.index, close)
if close <= 0
    log.error("A price of zero or less on bar {0}", bar.index)
plot(fast, name = "Fast", color = color.green)
plot(slow, name = "Slow", color = color.red)
  • log.info, log.warning and log.error write a line at three levels. {0}, {1} and so on in the text are filled with the values after it.
  • The engine keeps at most 10 lines for each bar and 5,000 for a run, each up to 500 characters; it counts the lines it had to drop (W-ENGINE-LOG-DROPPED).

Put the value on the chart#

The quickest way to look at a number is to draw it.

Cuprel
indicator "Look inside"
    edition = 1
    display = pane

input length: int = 14, name = "Length", min = 2, max = 200
let rsi = tech.rsi(close, length)
let range = tech.atr(length)
let up = tech.crosses_above(rsi, 50)
plot(rsi, name = "RSI", color = color.purple)
mark.char(up, name = "Crossed 50", char = "x", place = above, color = color.orange)
if bar.is_last
    label.new(
        bar.index,
        rsi,
        f"RSI {rsi:.1f}, ATR {range:price}, bar {bar.index}",
        style = label_down,
    )
  • A plot of the value shows it on every bar; the indicator's chip in the legend shows the value too (with Values switched on in the legend settings).
  • A mark.char or mark.shape on a condition shows exactly where the condition is true. If a signal never appears, mark each part of the condition on its own and see which part is never true.
  • A label.new on the last bar (bar.is_last) writes several values at once. f"..." puts values in the text; :.1f writes one decimal and :price uses the symbol's own decimals.

For many values at once, a small table works well: Lesson 9 shows one.

A checklist when the result looks wrong#

  • Is the value na? A value with too little history is na, and a plot draws nothing there. Test it with is_na(x), never with x == na.
  • Which bar am I on? The script runs once for each bar, oldest first. x[1] is the value one bar back, not the next one.
  • Does a call keep a memory? A tech.* call written inside a condition would skip bars; the editor refuses that (E3302), and the fix is to work it out on its own line first.
  • Is it a closed bar or a forming one? On the newest bar the values move until the bar closes. Lesson 16 explains what that changes.

Try changing#

  • In "Two averages, checked", set the fast length to 40 in the settings. The editor shows Stopped on the chart with your message.
  • In "Look inside", change 50 to 70 in the crossing. Fewer marks appear.
  • Add bar.index to the label in a different place, or add a second label on the first bar with bar.index == 0.
  • Write a condition that is never true (close < 0) and mark it with mark.char. No mark appears: that is how a condition looks when one part of it never holds.

Checkpoint#

  • You can stop a script on purpose with log.abort and a message that says what to fix.
  • You can write log lines and know that this release keeps them without showing them.
  • You can look at a value with a plot, a mark or a label on the last bar.
  • You can check the four usual causes of a wrong result: na, the bar, a call's memory, and the forming bar.

Next: Lesson 14: Your first strategy