openalgo-script 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +12 -0
- package/README.md +255 -0
- package/dist/core/ast/annotations.d.ts +56 -0
- package/dist/core/ast/annotations.d.ts.map +1 -0
- package/dist/core/ast/annotations.js +27 -0
- package/dist/core/ast/annotations.js.map +1 -0
- package/dist/core/ast/build.d.ts +25 -0
- package/dist/core/ast/build.d.ts.map +1 -0
- package/dist/core/ast/build.js +26 -0
- package/dist/core/ast/build.js.map +1 -0
- package/dist/core/ast/children.d.ts +15 -0
- package/dist/core/ast/children.d.ts.map +1 -0
- package/dist/core/ast/children.js +103 -0
- package/dist/core/ast/children.js.map +1 -0
- package/dist/core/ast/expressions.d.ts +178 -0
- package/dist/core/ast/expressions.d.ts.map +1 -0
- package/dist/core/ast/expressions.js +50 -0
- package/dist/core/ast/expressions.js.map +1 -0
- package/dist/core/ast/header.d.ts +50 -0
- package/dist/core/ast/header.d.ts.map +1 -0
- package/dist/core/ast/header.js +2 -0
- package/dist/core/ast/header.js.map +1 -0
- package/dist/core/ast/index.d.ts +30 -0
- package/dist/core/ast/index.d.ts.map +1 -0
- package/dist/core/ast/index.js +21 -0
- package/dist/core/ast/index.js.map +1 -0
- package/dist/core/ast/name.d.ts +24 -0
- package/dist/core/ast/name.d.ts.map +1 -0
- package/dist/core/ast/name.js +2 -0
- package/dist/core/ast/name.js.map +1 -0
- package/dist/core/ast/node.d.ts +34 -0
- package/dist/core/ast/node.d.ts.map +1 -0
- package/dist/core/ast/node.js +70 -0
- package/dist/core/ast/node.js.map +1 -0
- package/dist/core/ast/script.d.ts +57 -0
- package/dist/core/ast/script.d.ts.map +1 -0
- package/dist/core/ast/script.js +2 -0
- package/dist/core/ast/script.js.map +1 -0
- package/dist/core/ast/statements.d.ts +161 -0
- package/dist/core/ast/statements.d.ts.map +1 -0
- package/dist/core/ast/statements.js +10 -0
- package/dist/core/ast/statements.js.map +1 -0
- package/dist/core/ast/walk.d.ts +39 -0
- package/dist/core/ast/walk.d.ts.map +1 -0
- package/dist/core/ast/walk.js +54 -0
- package/dist/core/ast/walk.js.map +1 -0
- package/dist/core/catalogue/catalogue.generated.d.ts +1590 -0
- package/dist/core/catalogue/catalogue.generated.d.ts.map +1 -0
- package/dist/core/catalogue/catalogue.generated.js +162 -0
- package/dist/core/catalogue/catalogue.generated.js.map +1 -0
- package/dist/core/catalogue/index.d.ts +15 -0
- package/dist/core/catalogue/index.d.ts.map +1 -0
- package/dist/core/catalogue/index.js +4 -0
- package/dist/core/catalogue/index.js.map +1 -0
- package/dist/core/catalogue/lookup.d.ts +14 -0
- package/dist/core/catalogue/lookup.d.ts.map +1 -0
- package/dist/core/catalogue/lookup.js +19 -0
- package/dist/core/catalogue/lookup.js.map +1 -0
- package/dist/core/catalogue/template.d.ts +15 -0
- package/dist/core/catalogue/template.d.ts.map +1 -0
- package/dist/core/catalogue/template.js +33 -0
- package/dist/core/catalogue/template.js.map +1 -0
- package/dist/core/catalogue/types.d.ts +34 -0
- package/dist/core/catalogue/types.d.ts.map +1 -0
- package/dist/core/catalogue/types.js +6 -0
- package/dist/core/catalogue/types.js.map +1 -0
- package/dist/core/catalogue/values.generated.d.ts +565 -0
- package/dist/core/catalogue/values.generated.d.ts.map +1 -0
- package/dist/core/catalogue/values.generated.js +6 -0
- package/dist/core/catalogue/values.generated.js.map +1 -0
- package/dist/core/diagnostics/collector.d.ts +46 -0
- package/dist/core/diagnostics/collector.d.ts.map +1 -0
- package/dist/core/diagnostics/collector.js +55 -0
- package/dist/core/diagnostics/collector.js.map +1 -0
- package/dist/core/diagnostics/diagnostic.d.ts +40 -0
- package/dist/core/diagnostics/diagnostic.d.ts.map +1 -0
- package/dist/core/diagnostics/diagnostic.js +29 -0
- package/dist/core/diagnostics/diagnostic.js.map +1 -0
- package/dist/core/diagnostics/index.d.ts +12 -0
- package/dist/core/diagnostics/index.d.ts.map +1 -0
- package/dist/core/diagnostics/index.js +3 -0
- package/dist/core/diagnostics/index.js.map +1 -0
- package/dist/core/index.d.ts +28 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +22 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lex/blocks.d.ts +51 -0
- package/dist/core/lex/blocks.d.ts.map +1 -0
- package/dist/core/lex/blocks.js +56 -0
- package/dist/core/lex/blocks.js.map +1 -0
- package/dist/core/lex/characters.d.ts +38 -0
- package/dist/core/lex/characters.d.ts.map +1 -0
- package/dist/core/lex/characters.js +50 -0
- package/dist/core/lex/characters.js.map +1 -0
- package/dist/core/lex/comments.d.ts +50 -0
- package/dist/core/lex/comments.d.ts.map +1 -0
- package/dist/core/lex/comments.js +64 -0
- package/dist/core/lex/comments.js.map +1 -0
- package/dist/core/lex/index.d.ts +12 -0
- package/dist/core/lex/index.d.ts.map +1 -0
- package/dist/core/lex/index.js +12 -0
- package/dist/core/lex/index.js.map +1 -0
- package/dist/core/lex/layout.d.ts +40 -0
- package/dist/core/lex/layout.d.ts.map +1 -0
- package/dist/core/lex/layout.js +143 -0
- package/dist/core/lex/layout.js.map +1 -0
- package/dist/core/lex/lexer.d.ts +13 -0
- package/dist/core/lex/lexer.d.ts.map +1 -0
- package/dist/core/lex/lexer.js +419 -0
- package/dist/core/lex/lexer.js.map +1 -0
- package/dist/core/lex/numbers.d.ts +22 -0
- package/dist/core/lex/numbers.d.ts.map +1 -0
- package/dist/core/lex/numbers.js +58 -0
- package/dist/core/lex/numbers.js.map +1 -0
- package/dist/core/lex/statements.d.ts +24 -0
- package/dist/core/lex/statements.d.ts.map +1 -0
- package/dist/core/lex/statements.js +90 -0
- package/dist/core/lex/statements.js.map +1 -0
- package/dist/core/lex/strings.d.ts +27 -0
- package/dist/core/lex/strings.d.ts.map +1 -0
- package/dist/core/lex/strings.js +92 -0
- package/dist/core/lex/strings.js.map +1 -0
- package/dist/core/lex/unexpected.d.ts +31 -0
- package/dist/core/lex/unexpected.d.ts.map +1 -0
- package/dist/core/lex/unexpected.js +114 -0
- package/dist/core/lex/unexpected.js.map +1 -0
- package/dist/core/parse/annotations.d.ts +4 -0
- package/dist/core/parse/annotations.d.ts.map +1 -0
- package/dist/core/parse/annotations.js +49 -0
- package/dist/core/parse/annotations.js.map +1 -0
- package/dist/core/parse/blocks.d.ts +18 -0
- package/dist/core/parse/blocks.d.ts.map +1 -0
- package/dist/core/parse/blocks.js +47 -0
- package/dist/core/parse/blocks.js.map +1 -0
- package/dist/core/parse/branches.d.ts +17 -0
- package/dist/core/parse/branches.d.ts.map +1 -0
- package/dist/core/parse/branches.js +66 -0
- package/dist/core/parse/branches.js.map +1 -0
- package/dist/core/parse/cursor.d.ts +119 -0
- package/dist/core/parse/cursor.d.ts.map +1 -0
- package/dist/core/parse/cursor.js +305 -0
- package/dist/core/parse/cursor.js.map +1 -0
- package/dist/core/parse/expressions.d.ts +57 -0
- package/dist/core/parse/expressions.d.ts.map +1 -0
- package/dist/core/parse/expressions.js +373 -0
- package/dist/core/parse/expressions.js.map +1 -0
- package/dist/core/parse/functions.d.ts +16 -0
- package/dist/core/parse/functions.d.ts.map +1 -0
- package/dist/core/parse/functions.js +92 -0
- package/dist/core/parse/functions.js.map +1 -0
- package/dist/core/parse/index.d.ts +13 -0
- package/dist/core/parse/index.d.ts.map +1 -0
- package/dist/core/parse/index.js +13 -0
- package/dist/core/parse/index.js.map +1 -0
- package/dist/core/parse/loops.d.ts +13 -0
- package/dist/core/parse/loops.d.ts.map +1 -0
- package/dist/core/parse/loops.js +78 -0
- package/dist/core/parse/loops.js.map +1 -0
- package/dist/core/parse/names.d.ts +34 -0
- package/dist/core/parse/names.d.ts.map +1 -0
- package/dist/core/parse/names.js +122 -0
- package/dist/core/parse/names.js.map +1 -0
- package/dist/core/parse/script.d.ts +23 -0
- package/dist/core/parse/script.d.ts.map +1 -0
- package/dist/core/parse/script.js +55 -0
- package/dist/core/parse/script.js.map +1 -0
- package/dist/core/parse/statements.d.ts +14 -0
- package/dist/core/parse/statements.d.ts.map +1 -0
- package/dist/core/parse/statements.js +292 -0
- package/dist/core/parse/statements.js.map +1 -0
- package/dist/core/parse/switches.d.ts +16 -0
- package/dist/core/parse/switches.d.ts.map +1 -0
- package/dist/core/parse/switches.js +98 -0
- package/dist/core/parse/switches.js.map +1 -0
- package/dist/core/render/index.d.ts +10 -0
- package/dist/core/render/index.d.ts.map +1 -0
- package/dist/core/render/index.js +10 -0
- package/dist/core/render/index.js.map +1 -0
- package/dist/core/render/terminal.d.ts +11 -0
- package/dist/core/render/terminal.d.ts.map +1 -0
- package/dist/core/render/terminal.js +51 -0
- package/dist/core/render/terminal.js.map +1 -0
- package/dist/core/source/index.d.ts +7 -0
- package/dist/core/source/index.d.ts.map +1 -0
- package/dist/core/source/index.js +2 -0
- package/dist/core/source/index.js.map +1 -0
- package/dist/core/source/source.d.ts +41 -0
- package/dist/core/source/source.d.ts.map +1 -0
- package/dist/core/source/source.js +67 -0
- package/dist/core/source/source.js.map +1 -0
- package/dist/core/span/index.d.ts +3 -0
- package/dist/core/span/index.d.ts.map +1 -0
- package/dist/core/span/index.js +2 -0
- package/dist/core/span/index.js.map +1 -0
- package/dist/core/span/span.d.ts +67 -0
- package/dist/core/span/span.d.ts.map +1 -0
- package/dist/core/span/span.js +27 -0
- package/dist/core/span/span.js.map +1 -0
- package/dist/core/tokens/index.d.ts +8 -0
- package/dist/core/tokens/index.d.ts.map +1 -0
- package/dist/core/tokens/index.js +2 -0
- package/dist/core/tokens/index.js.map +1 -0
- package/dist/core/tokens/kind.d.ts +53 -0
- package/dist/core/tokens/kind.d.ts.map +1 -0
- package/dist/core/tokens/kind.js +94 -0
- package/dist/core/tokens/kind.js.map +1 -0
- package/dist/core/tokens/token.d.ts +36 -0
- package/dist/core/tokens/token.d.ts.map +1 -0
- package/dist/core/tokens/token.js +2 -0
- package/dist/core/tokens/token.js.map +1 -0
- package/package.json +52 -0
- package/spec/README.md +48 -0
- package/spec/errors.json +3204 -0
- package/src/core/ast/annotations.ts +68 -0
- package/src/core/ast/build.ts +35 -0
- package/src/core/ast/children.ts +110 -0
- package/src/core/ast/expressions.ts +254 -0
- package/src/core/ast/header.ts +53 -0
- package/src/core/ast/index.ts +102 -0
- package/src/core/ast/name.ts +24 -0
- package/src/core/ast/node.ts +120 -0
- package/src/core/ast/script.ts +60 -0
- package/src/core/ast/statements.ts +193 -0
- package/src/core/ast/walk.ts +67 -0
- package/src/core/catalogue/catalogue.generated.ts +171 -0
- package/src/core/catalogue/index.ts +20 -0
- package/src/core/catalogue/lookup.ts +23 -0
- package/src/core/catalogue/template.ts +38 -0
- package/src/core/catalogue/types.ts +37 -0
- package/src/core/catalogue/values.generated.ts +160 -0
- package/src/core/diagnostics/collector.ts +87 -0
- package/src/core/diagnostics/diagnostic.ts +72 -0
- package/src/core/diagnostics/index.ts +12 -0
- package/src/core/index.ts +146 -0
- package/src/core/lex/blocks.ts +92 -0
- package/src/core/lex/characters.ts +56 -0
- package/src/core/lex/comments.ts +75 -0
- package/src/core/lex/index.ts +11 -0
- package/src/core/lex/layout.ts +188 -0
- package/src/core/lex/lexer.ts +486 -0
- package/src/core/lex/numbers.ts +77 -0
- package/src/core/lex/statements.ts +94 -0
- package/src/core/lex/strings.ts +119 -0
- package/src/core/lex/unexpected.ts +125 -0
- package/src/core/parse/annotations.ts +60 -0
- package/src/core/parse/blocks.ts +52 -0
- package/src/core/parse/branches.ts +77 -0
- package/src/core/parse/cursor.ts +348 -0
- package/src/core/parse/expressions.ts +441 -0
- package/src/core/parse/functions.ts +109 -0
- package/src/core/parse/index.ts +12 -0
- package/src/core/parse/loops.ts +97 -0
- package/src/core/parse/names.ts +135 -0
- package/src/core/parse/script.ts +64 -0
- package/src/core/parse/statements.ts +329 -0
- package/src/core/parse/switches.ts +105 -0
- package/src/core/render/index.ts +9 -0
- package/src/core/render/terminal.ts +62 -0
- package/src/core/source/index.ts +6 -0
- package/src/core/source/source.ts +102 -0
- package/src/core/span/index.ts +2 -0
- package/src/core/span/span.ts +84 -0
- package/src/core/tokens/index.ts +15 -0
- package/src/core/tokens/kind.ts +123 -0
- package/src/core/tokens/token.ts +39 -0
package/spec/errors.json
ADDED
|
@@ -0,0 +1,3204 @@
|
|
|
1
|
+
{
|
|
2
|
+
"catalogue": "openscript-errors",
|
|
3
|
+
"schemaVersion": 1,
|
|
4
|
+
"languageVersion": 1,
|
|
5
|
+
"placeholderSyntax": "{name}",
|
|
6
|
+
"severities": {
|
|
7
|
+
"error": "Stops compilation, or stops the bar. Nothing is drawn from a bar that raised one.",
|
|
8
|
+
"warning": "Never stops anything. Reported on the line and in the editor's gutter."
|
|
9
|
+
},
|
|
10
|
+
"stages": {
|
|
11
|
+
"lex": "Reading characters into tokens.",
|
|
12
|
+
"parse": "Reading tokens into a tree.",
|
|
13
|
+
"check": "Names, types, scope and call sites, before any bar runs.",
|
|
14
|
+
"runtime": "Executing a bar.",
|
|
15
|
+
"host": "The host answering the engine: data, limits and order destinations."
|
|
16
|
+
},
|
|
17
|
+
"stageLabels": {
|
|
18
|
+
"lex": "lexer",
|
|
19
|
+
"parse": "parser",
|
|
20
|
+
"check": "checker",
|
|
21
|
+
"runtime": "engine",
|
|
22
|
+
"host": "host"
|
|
23
|
+
},
|
|
24
|
+
"ranges": [
|
|
25
|
+
{
|
|
26
|
+
"prefix": "OS1",
|
|
27
|
+
"kind": "Syntax",
|
|
28
|
+
"means": "The source text is not a program: characters, layout and grammar.",
|
|
29
|
+
"severity": "error"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"prefix": "OS2",
|
|
33
|
+
"kind": "Names and types",
|
|
34
|
+
"means": "The program parses, and a name or a type does not work out.",
|
|
35
|
+
"severity": "error"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"prefix": "OS3",
|
|
39
|
+
"kind": "Arguments",
|
|
40
|
+
"means": "A call or an option is wrong at the call site.",
|
|
41
|
+
"severity": "error"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"prefix": "OS4",
|
|
45
|
+
"kind": "Runtime",
|
|
46
|
+
"means": "A bar produced a value the engine cannot act on.",
|
|
47
|
+
"severity": "error"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"prefix": "OS5",
|
|
51
|
+
"kind": "Limits",
|
|
52
|
+
"means": "A budget was exhausted: loops, memory, size or time.",
|
|
53
|
+
"severity": "error"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"prefix": "OS6",
|
|
57
|
+
"kind": "Data",
|
|
58
|
+
"means": "Bars, instruments, timeframes and the host's answers to requests.",
|
|
59
|
+
"severity": "error"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"prefix": "OS7",
|
|
63
|
+
"kind": "Orders",
|
|
64
|
+
"means": "An order could not be placed as written.",
|
|
65
|
+
"severity": "error"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"prefix": "OS8",
|
|
69
|
+
"kind": "Warnings",
|
|
70
|
+
"means": "The script compiles and runs, and something in it is probably not meant.",
|
|
71
|
+
"severity": "warning"
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"entries": [
|
|
75
|
+
{
|
|
76
|
+
"code": "OS1001",
|
|
77
|
+
"title": "Unexpected character",
|
|
78
|
+
"severity": "error",
|
|
79
|
+
"stage": "lex",
|
|
80
|
+
"since": 1,
|
|
81
|
+
"message": "Unexpected character {char}. {suggestion}",
|
|
82
|
+
"placeholders": {
|
|
83
|
+
"char": "the offending character, quoted, with its Unicode name when it is not an ASCII graphic",
|
|
84
|
+
"suggestion": "the replacement sentence for this character, from the substitution table in section 7 of this document"
|
|
85
|
+
},
|
|
86
|
+
"cause": "Outside a string literal the language accepts ASCII letters, ASCII digits, space, newline and the punctuation list of language.md 3.12, and nothing else. A non-breaking space pasted from a web page, a typographic quotation mark pasted from a word processor, a non-ASCII letter in a name, and an operator the language does not have (!, &&, ||, ^, {, }) all arrive here. The character is rejected where it sits rather than three tokens later, because an invisible character produces a baffling parse error otherwise. Two runs whose every character is legal are not this code: a name written against a number is OS1029, and a colour literal with the wrong number of digits is OS1027. Naming one character of either and advising its deletion, which is what this fix says to do, would leave a program that still compiles and means something else.",
|
|
87
|
+
"fix": "Delete the character or replace it with the plain ASCII spelling the message names: a plain space for a non-breaking space, a straight quote for a typographic one, not for !, and for &&, or for ||, pow(a, b) for ^.",
|
|
88
|
+
"autofix": true,
|
|
89
|
+
"example": {
|
|
90
|
+
"before": "if !ready\n signal(\"BUY\")",
|
|
91
|
+
"after": "if not ready\n signal(\"BUY\")"
|
|
92
|
+
},
|
|
93
|
+
"spec": "language.md 3.1, 3.3, 3.12",
|
|
94
|
+
"refines": null,
|
|
95
|
+
"test": "tests/errors/OS1001"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"code": "OS1002",
|
|
99
|
+
"title": "Tab in indentation",
|
|
100
|
+
"severity": "error",
|
|
101
|
+
"stage": "lex",
|
|
102
|
+
"since": 1,
|
|
103
|
+
"message": "This line is indented with a tab.",
|
|
104
|
+
"placeholders": {},
|
|
105
|
+
"cause": "Leading whitespace is spaces only. A tab is rejected rather than expanded because the width of a tab is an editor setting, so a file whose block structure depends on it means something different when someone else opens it.",
|
|
106
|
+
"fix": "Replace the leading tabs with spaces. Four spaces per level is the convention and the formatter's output.",
|
|
107
|
+
"autofix": true,
|
|
108
|
+
"example": {
|
|
109
|
+
"before": "if close > open\n\tsignal(\"UP\")",
|
|
110
|
+
"after": "if close > open\n signal(\"UP\")"
|
|
111
|
+
},
|
|
112
|
+
"spec": "language.md 3.10",
|
|
113
|
+
"refines": null,
|
|
114
|
+
"test": "tests/errors/OS1002"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"code": "OS1003",
|
|
118
|
+
"title": "Indentation does not match this block",
|
|
119
|
+
"severity": "error",
|
|
120
|
+
"stage": "lex",
|
|
121
|
+
"since": 1,
|
|
122
|
+
"message": "This line is indented {found} spaces; the block opened at line {line} is indented {expected}.",
|
|
123
|
+
"placeholders": {
|
|
124
|
+
"found": "the leading space count on this line",
|
|
125
|
+
"expected": "the leading space count every line of this block carries",
|
|
126
|
+
"line": "the line that opened the block"
|
|
127
|
+
},
|
|
128
|
+
"cause": "Every line of one block carries exactly the same leading whitespace. A difference of one space is still a difference, because the alternative is a language where a block's extent depends on a tolerance nobody can see. A blank line and a comment-only line stand outside the rule: they carry no token and no indentation at all, so any leading whitespace is accepted on them and neither one is ever this error. A continuation line stands outside it for a different reason: it belongs to a statement that began above it and opens no block, so what its indentation has to clear is that statement rather than a block, and that is OS1028.",
|
|
129
|
+
"fix": "Indent this line to {expected} spaces to keep it in the block, or to {line}'s own indentation to end the block here.",
|
|
130
|
+
"autofix": true,
|
|
131
|
+
"example": {
|
|
132
|
+
"before": "if trending\n fast = ema(close, 9)\n slow = ema(close, 21)",
|
|
133
|
+
"after": "if trending\n fast = ema(close, 9)\n slow = ema(close, 21)"
|
|
134
|
+
},
|
|
135
|
+
"spec": "language.md 3.10",
|
|
136
|
+
"refines": null,
|
|
137
|
+
"test": "tests/errors/OS1003"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"code": "OS1004",
|
|
141
|
+
"title": "Unterminated string literal",
|
|
142
|
+
"severity": "error",
|
|
143
|
+
"stage": "lex",
|
|
144
|
+
"since": 1,
|
|
145
|
+
"message": "This string literal opens with {quote} and the line ends before a matching {quote}.",
|
|
146
|
+
"placeholders": {
|
|
147
|
+
"quote": "the opening delimiter, a double or a single quote"
|
|
148
|
+
},
|
|
149
|
+
"cause": "A string literal may not span a line, so the scanner reports the opening quote rather than swallowing the rest of the file and complaining about the last line.",
|
|
150
|
+
"fix": "Close the string with a matching {quote} before the end of the line, and join text across lines with + and a continuation.",
|
|
151
|
+
"autofix": false,
|
|
152
|
+
"example": {
|
|
153
|
+
"before": "signal(\"BUY)",
|
|
154
|
+
"after": "signal(\"BUY\")"
|
|
155
|
+
},
|
|
156
|
+
"spec": "language.md 3.6",
|
|
157
|
+
"refines": null,
|
|
158
|
+
"test": "tests/errors/OS1004"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"code": "OS1005",
|
|
162
|
+
"title": "Unknown escape sequence",
|
|
163
|
+
"severity": "error",
|
|
164
|
+
"stage": "lex",
|
|
165
|
+
"since": 1,
|
|
166
|
+
"message": "{sequence} is not an escape sequence.",
|
|
167
|
+
"placeholders": {
|
|
168
|
+
"sequence": "the backslash and the character that followed it"
|
|
169
|
+
},
|
|
170
|
+
"cause": "The escapes are a backslash followed by one of \\ \" ' n t r 0, and uXXXX with exactly four hexadecimal digits. Anything else is almost always a Windows path or a regular expression that was pasted without doubling its backslashes.",
|
|
171
|
+
"fix": "Double the backslash to write one literally, or use one of the escapes the language defines.",
|
|
172
|
+
"autofix": true,
|
|
173
|
+
"example": {
|
|
174
|
+
"before": "path = \"C:\\data\\bars\"",
|
|
175
|
+
"after": "path = \"C:\\\\data\\\\bars\""
|
|
176
|
+
},
|
|
177
|
+
"spec": "language.md 3.6",
|
|
178
|
+
"refines": "OS1004",
|
|
179
|
+
"test": "tests/errors/OS1005"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"code": "OS1006",
|
|
183
|
+
"title": "Assignment used as a condition",
|
|
184
|
+
"severity": "error",
|
|
185
|
+
"stage": "parse",
|
|
186
|
+
"since": 1,
|
|
187
|
+
"message": "= assigns a value, and a condition needs a comparison.",
|
|
188
|
+
"placeholders": {},
|
|
189
|
+
"cause": "Assignment is a statement and never an expression, so the classic typo cannot compile into a condition that is always true. There is no form of this language in which if x = 5 means anything.",
|
|
190
|
+
"fix": "Write == to compare, or move the assignment to its own line above the if.",
|
|
191
|
+
"autofix": true,
|
|
192
|
+
"example": {
|
|
193
|
+
"before": "if len = 14\n signal(\"DEFAULT\")",
|
|
194
|
+
"after": "if len == 14\n signal(\"DEFAULT\")"
|
|
195
|
+
},
|
|
196
|
+
"spec": "language.md 10.1",
|
|
197
|
+
"refines": null,
|
|
198
|
+
"test": "tests/errors/OS1006"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"code": "OS1007",
|
|
202
|
+
"title": "Semicolon",
|
|
203
|
+
"severity": "error",
|
|
204
|
+
"stage": "lex",
|
|
205
|
+
"since": 1,
|
|
206
|
+
"message": "; is not part of the language.",
|
|
207
|
+
"placeholders": {},
|
|
208
|
+
"cause": "A statement ends at the end of its line and a line holds at most one statement, so there is no separator to write.",
|
|
209
|
+
"fix": "Delete the ; and put the second statement on its own line.",
|
|
210
|
+
"autofix": true,
|
|
211
|
+
"example": {
|
|
212
|
+
"before": "fast = ema(close, 9); slow = ema(close, 21)",
|
|
213
|
+
"after": "fast = ema(close, 9)\nslow = ema(close, 21)"
|
|
214
|
+
},
|
|
215
|
+
"spec": "language.md 3.10",
|
|
216
|
+
"refines": null,
|
|
217
|
+
"test": "tests/errors/OS1007"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"code": "OS1008",
|
|
221
|
+
"title": "Chained comparison",
|
|
222
|
+
"severity": "error",
|
|
223
|
+
"stage": "parse",
|
|
224
|
+
"since": 1,
|
|
225
|
+
"message": "A comparison cannot be chained: {op1} is already applied before {op2}.",
|
|
226
|
+
"placeholders": {
|
|
227
|
+
"op1": "the first comparison operator on the line",
|
|
228
|
+
"op2": "the second comparison operator on the line"
|
|
229
|
+
},
|
|
230
|
+
"cause": "Two readings of a < b < c are plausible to a reader, the mathematical one and the C-family one, and a form with two plausible meanings has no place in a language that places orders. The grammar allows at most one comparison operator per expression.",
|
|
231
|
+
"fix": "Split it with and, naming the middle value twice: a < b and b < c.",
|
|
232
|
+
"autofix": false,
|
|
233
|
+
"example": {
|
|
234
|
+
"before": "if 30 < r < 70\n zone = \"mid\"",
|
|
235
|
+
"after": "if 30 < r and r < 70\n zone = \"mid\""
|
|
236
|
+
},
|
|
237
|
+
"spec": "language.md 9.3",
|
|
238
|
+
"refines": null,
|
|
239
|
+
"test": "tests/errors/OS1008"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"code": "OS1009",
|
|
243
|
+
"title": "break or continue outside a loop",
|
|
244
|
+
"severity": "error",
|
|
245
|
+
"stage": "parse",
|
|
246
|
+
"since": 1,
|
|
247
|
+
"message": "{word} is only valid inside a for or a while body.",
|
|
248
|
+
"placeholders": {
|
|
249
|
+
"word": "break or continue, whichever was written"
|
|
250
|
+
},
|
|
251
|
+
"cause": "break leaves the innermost loop and continue starts its next iteration, so neither has a meaning where there is no loop to act on.",
|
|
252
|
+
"fix": "Move it inside the loop body, or write return to leave a function early.",
|
|
253
|
+
"autofix": false,
|
|
254
|
+
"example": {
|
|
255
|
+
"before": "fn firstAbove(values, mark) =>\n if size(values) == 0\n break\n values[0]",
|
|
256
|
+
"after": "fn firstAbove(values, mark) =>\n if size(values) == 0\n return none\n values[0]"
|
|
257
|
+
},
|
|
258
|
+
"spec": "language.md 10.5",
|
|
259
|
+
"refines": null,
|
|
260
|
+
"test": "tests/errors/OS1009"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"code": "OS1010",
|
|
264
|
+
"title": "Block header with no body",
|
|
265
|
+
"severity": "error",
|
|
266
|
+
"stage": "parse",
|
|
267
|
+
"since": 1,
|
|
268
|
+
"message": "{header} opens a block, and the next line is not indented more deeply.",
|
|
269
|
+
"placeholders": {
|
|
270
|
+
"header": "the keyword that opened the block, such as if, else, for, while, case or default"
|
|
271
|
+
},
|
|
272
|
+
"cause": "A header line introduces a block that consists of the following lines indented more deeply than the header. With nothing indented under it the header has no body, which is almost always a line that lost its indentation in a paste. Blank and comment-only lines carry no token, so they never count as a body: a header followed only by those, and then by a line at or left of the header, is still this error.",
|
|
273
|
+
"fix": "Indent the body under the header, or delete the header line if the body is genuinely empty.",
|
|
274
|
+
"autofix": true,
|
|
275
|
+
"example": {
|
|
276
|
+
"before": "if crossUp(fast, slow)\nsignal(\"BUY\")",
|
|
277
|
+
"after": "if crossUp(fast, slow)\n signal(\"BUY\")"
|
|
278
|
+
},
|
|
279
|
+
"spec": "language.md 3.10",
|
|
280
|
+
"refines": null,
|
|
281
|
+
"test": "tests/errors/OS1010"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"code": "OS1011",
|
|
285
|
+
"title": "var with no initial value",
|
|
286
|
+
"severity": "error",
|
|
287
|
+
"stage": "parse",
|
|
288
|
+
"since": 1,
|
|
289
|
+
"message": "var {name} has no initial value.",
|
|
290
|
+
"placeholders": {
|
|
291
|
+
"name": "the name being declared"
|
|
292
|
+
},
|
|
293
|
+
"cause": "The declaration and the first assignment are one statement, so a persistent value always has something in it and no bar can read it before it exists.",
|
|
294
|
+
"fix": "Give it a starting value; var {name} = none is the empty start.",
|
|
295
|
+
"autofix": true,
|
|
296
|
+
"example": {
|
|
297
|
+
"before": "var runningHigh\nif high > orElse(runningHigh, high)\n runningHigh = high",
|
|
298
|
+
"after": "var runningHigh = none\nif isNone(runningHigh) or high > runningHigh\n runningHigh = high"
|
|
299
|
+
},
|
|
300
|
+
"spec": "language.md 8.2",
|
|
301
|
+
"refines": null,
|
|
302
|
+
"test": "tests/errors/OS1011"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"code": "OS1012",
|
|
306
|
+
"title": "Bracket is never closed",
|
|
307
|
+
"severity": "error",
|
|
308
|
+
"stage": "parse",
|
|
309
|
+
"since": 1,
|
|
310
|
+
"message": "The {bracket} opened at line {line} is never closed.",
|
|
311
|
+
"placeholders": {
|
|
312
|
+
"bracket": "the opening bracket character",
|
|
313
|
+
"line": "the line the bracket was opened on"
|
|
314
|
+
},
|
|
315
|
+
"cause": "An open ( or [ continues the statement onto the following lines, so a missing closer makes the scanner read the rest of the file as one statement. The error is reported at the opening bracket, which is where the mistake is.",
|
|
316
|
+
"fix": "Add the matching closer at the end of the argument list on line {line}.",
|
|
317
|
+
"autofix": false,
|
|
318
|
+
"example": {
|
|
319
|
+
"before": "plot(ema(close, 9), \"EMA\", aqua",
|
|
320
|
+
"after": "plot(ema(close, 9), \"EMA\", aqua)"
|
|
321
|
+
},
|
|
322
|
+
"spec": "language.md 3.11",
|
|
323
|
+
"refines": null,
|
|
324
|
+
"test": "tests/errors/OS1012"
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"code": "OS1013",
|
|
328
|
+
"title": "Mismatched closing bracket",
|
|
329
|
+
"severity": "error",
|
|
330
|
+
"stage": "parse",
|
|
331
|
+
"since": 1,
|
|
332
|
+
"message": "Found {found} where {expected} was expected, closing the {opener} opened at line {line}.",
|
|
333
|
+
"placeholders": {
|
|
334
|
+
"found": "the closing bracket that was written",
|
|
335
|
+
"expected": "the closing bracket the opener requires",
|
|
336
|
+
"opener": "the opening bracket character",
|
|
337
|
+
"line": "the line the bracket was opened on"
|
|
338
|
+
},
|
|
339
|
+
"cause": "Brackets nest, and a call closes with ) while an index or an array literal closes with ].",
|
|
340
|
+
"fix": "Change {found} to {expected}, which is what closes the {opener} opened at line {line}.",
|
|
341
|
+
"autofix": true,
|
|
342
|
+
"example": {
|
|
343
|
+
"before": "total = sum(closes]",
|
|
344
|
+
"after": "total = sum(closes)"
|
|
345
|
+
},
|
|
346
|
+
"spec": "language.md 19",
|
|
347
|
+
"refines": null,
|
|
348
|
+
"test": "tests/errors/OS1013"
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"code": "OS1014",
|
|
352
|
+
"title": "Missing comma between arguments",
|
|
353
|
+
"severity": "error",
|
|
354
|
+
"stage": "parse",
|
|
355
|
+
"since": 1,
|
|
356
|
+
"message": "Two arguments run together; a comma is missing before {token}.",
|
|
357
|
+
"placeholders": {
|
|
358
|
+
"token": "the first token of the second argument"
|
|
359
|
+
},
|
|
360
|
+
"cause": "Arguments are separated by commas. Two expressions side by side inside a call are not a form the language has, so the missing comma is reported rather than guessed at.",
|
|
361
|
+
"fix": "Put a comma between the two arguments.",
|
|
362
|
+
"autofix": true,
|
|
363
|
+
"example": {
|
|
364
|
+
"before": "plot(ema(close, 9) \"EMA\", aqua)",
|
|
365
|
+
"after": "plot(ema(close, 9), \"EMA\", aqua)"
|
|
366
|
+
},
|
|
367
|
+
"spec": "language.md 11.2",
|
|
368
|
+
"refines": null,
|
|
369
|
+
"test": "tests/errors/OS1014"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"code": "OS1015",
|
|
373
|
+
"title": "Ternary with no second arm",
|
|
374
|
+
"severity": "error",
|
|
375
|
+
"stage": "parse",
|
|
376
|
+
"since": 1,
|
|
377
|
+
"message": "This ? has no matching :",
|
|
378
|
+
"placeholders": {},
|
|
379
|
+
"cause": "The ternary always yields a value, so both arms are required. A ternary with one arm is usually a plot that wanted to draw nothing on some bars.",
|
|
380
|
+
"fix": "Give the ternary both arms, and use none for the arm that should draw nothing.",
|
|
381
|
+
"autofix": false,
|
|
382
|
+
"example": {
|
|
383
|
+
"before": "plot(ready ? value, \"Value\", aqua)",
|
|
384
|
+
"after": "plot(ready ? value : none, \"Value\", aqua)"
|
|
385
|
+
},
|
|
386
|
+
"spec": "language.md 9.5",
|
|
387
|
+
"refines": null,
|
|
388
|
+
"test": "tests/errors/OS1015"
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"code": "OS1016",
|
|
392
|
+
"title": "else does not follow an if",
|
|
393
|
+
"severity": "error",
|
|
394
|
+
"stage": "parse",
|
|
395
|
+
"since": 1,
|
|
396
|
+
"message": "This else is indented {found} spaces and the nearest if is indented {expected}.",
|
|
397
|
+
"placeholders": {
|
|
398
|
+
"found": "the leading space count on the else line",
|
|
399
|
+
"expected": "the leading space count of the if it should pair with"
|
|
400
|
+
},
|
|
401
|
+
"cause": "An else pairs with the if at its own indentation. At any other indentation there is no if for it to belong to, and guessing which one was meant would make the block structure unreadable.",
|
|
402
|
+
"fix": "Line the else up with its if, at {expected} spaces.",
|
|
403
|
+
"autofix": true,
|
|
404
|
+
"example": {
|
|
405
|
+
"before": "if trending\n signal(\"BUY\")\n else\n signal(\"WAIT\")",
|
|
406
|
+
"after": "if trending\n signal(\"BUY\")\nelse\n signal(\"WAIT\")"
|
|
407
|
+
},
|
|
408
|
+
"spec": "language.md 10.2",
|
|
409
|
+
"refines": null,
|
|
410
|
+
"test": "tests/errors/OS1016"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"code": "OS1017",
|
|
414
|
+
"title": "case or default in the wrong place",
|
|
415
|
+
"severity": "error",
|
|
416
|
+
"stage": "parse",
|
|
417
|
+
"since": 1,
|
|
418
|
+
"message": "{word} is only valid inside a switch, and default must be its last arm.",
|
|
419
|
+
"placeholders": {
|
|
420
|
+
"word": "case or default, whichever was written"
|
|
421
|
+
},
|
|
422
|
+
"cause": "The arms of a switch are its whole body. A default that is not last would leave the arms after it unreachable, which the language rejects rather than silently accepts.",
|
|
423
|
+
"fix": "Move the arm inside the switch block, and put default after every case.",
|
|
424
|
+
"autofix": false,
|
|
425
|
+
"example": {
|
|
426
|
+
"before": "switch method\n default\n len = 14\n case \"fast\"\n len = 9",
|
|
427
|
+
"after": "switch method\n case \"fast\"\n len = 9\n default\n len = 14"
|
|
428
|
+
},
|
|
429
|
+
"spec": "language.md 10.6",
|
|
430
|
+
"refines": null,
|
|
431
|
+
"test": "tests/errors/OS1017"
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
"code": "OS1018",
|
|
435
|
+
"title": "More than one statement on a line",
|
|
436
|
+
"severity": "error",
|
|
437
|
+
"stage": "parse",
|
|
438
|
+
"since": 1,
|
|
439
|
+
"message": "Unexpected {token} after the end of this statement.",
|
|
440
|
+
"placeholders": {
|
|
441
|
+
"token": "the first token that follows the complete statement"
|
|
442
|
+
},
|
|
443
|
+
"cause": "A line holds at most one statement, so anything after a statement ends is either a missing operator or a second statement that lost its newline. An assignment operator after an index or after a dotted name is not this error: the statement did not end there, and what is wrong is the target rather than anything following it. Those two are OS1024 and OS1025.",
|
|
444
|
+
"fix": "Put {token} and what follows it on its own line, or supply the operator that was meant to join them.",
|
|
445
|
+
"autofix": false,
|
|
446
|
+
"example": {
|
|
447
|
+
"before": "fast = ema(close, 9) slow = ema(close, 21)",
|
|
448
|
+
"after": "fast = ema(close, 9)\nslow = ema(close, 21)"
|
|
449
|
+
},
|
|
450
|
+
"spec": "language.md 3.10",
|
|
451
|
+
"refines": null,
|
|
452
|
+
"test": "tests/errors/OS1018"
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"code": "OS1019",
|
|
456
|
+
"title": "Reserved word used as a name",
|
|
457
|
+
"severity": "error",
|
|
458
|
+
"stage": "parse",
|
|
459
|
+
"since": 1,
|
|
460
|
+
"message": "{word} is a reserved word and cannot be used as a name.",
|
|
461
|
+
"placeholders": {
|
|
462
|
+
"word": "the reserved word that was used as a name",
|
|
463
|
+
"suggestion": "a near name that is not reserved, derived from the word"
|
|
464
|
+
},
|
|
465
|
+
"cause": "The reserved words of language.md 3.4 include five that version 1 does not implement (import, map, matrix, type, as). They are reserved now so that implementing them later cannot break a script that used one as a name. A named argument label is not a name: it is matched against the callee's parameter list and is never looked up in any scope, so plot(v, \"V\", color = aqua) is correct and is not this error. The rule stops at the label. A parameter of a user function is an ordinary identifier, because the body refers to it, so fn f(color = red) is this error.",
|
|
466
|
+
"fix": "Rename it; {suggestion} keeps the meaning and is not reserved.",
|
|
467
|
+
"autofix": false,
|
|
468
|
+
"example": {
|
|
469
|
+
"before": "type = input(\"fast\", \"Mode\", options = [\"fast\", \"slow\"])",
|
|
470
|
+
"after": "mode = input(\"fast\", \"Mode\", options = [\"fast\", \"slow\"])"
|
|
471
|
+
},
|
|
472
|
+
"spec": "language.md 3.4",
|
|
473
|
+
"refines": null,
|
|
474
|
+
"test": "tests/errors/OS1019"
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"code": "OS1020",
|
|
478
|
+
"title": "Incomplete for header",
|
|
479
|
+
"severity": "error",
|
|
480
|
+
"stage": "parse",
|
|
481
|
+
"since": 1,
|
|
482
|
+
"message": "A for header needs = start to end or in array; found {token}.",
|
|
483
|
+
"placeholders": {
|
|
484
|
+
"token": "the token that appeared where to or in was expected"
|
|
485
|
+
},
|
|
486
|
+
"cause": "There are two loop forms and no third. A counted loop names a start and an end, and a loop over an array names the array.",
|
|
487
|
+
"fix": "Write for i = 0 to size(values) - 1 for a counted loop, or for v in values to visit elements.",
|
|
488
|
+
"autofix": false,
|
|
489
|
+
"example": {
|
|
490
|
+
"before": "for i = 0, 9\n total += close[i]",
|
|
491
|
+
"after": "for i = 0 to 9\n total += close[i]"
|
|
492
|
+
},
|
|
493
|
+
"spec": "language.md 10.3",
|
|
494
|
+
"refines": null,
|
|
495
|
+
"test": "tests/errors/OS1020"
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"code": "OS1021",
|
|
499
|
+
"title": "The version declaration is not first",
|
|
500
|
+
"severity": "error",
|
|
501
|
+
"stage": "parse",
|
|
502
|
+
"since": 1,
|
|
503
|
+
"message": "version must be the first line that is not blank and not a comment; line {line} came before it.",
|
|
504
|
+
"placeholders": {
|
|
505
|
+
"line": "the line of the first statement that preceded the version declaration"
|
|
506
|
+
},
|
|
507
|
+
"cause": "A host reads the version with a one-line scan so it can pick a front end before any parsing happens. A declaration further down the file would be read by the wrong front end, which defeats the purpose of having one.",
|
|
508
|
+
"fix": "Move the version line to the top of the file, above the study or strategy declaration.",
|
|
509
|
+
"autofix": true,
|
|
510
|
+
"example": {
|
|
511
|
+
"before": "study(\"EMA cross\")\nversion 1",
|
|
512
|
+
"after": "version 1\n\nstudy(\"EMA cross\")"
|
|
513
|
+
},
|
|
514
|
+
"spec": "language.md 4",
|
|
515
|
+
"refines": null,
|
|
516
|
+
"test": "tests/errors/OS1021"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"code": "OS1022",
|
|
520
|
+
"title": "Expression expected",
|
|
521
|
+
"severity": "error",
|
|
522
|
+
"stage": "parse",
|
|
523
|
+
"since": 1,
|
|
524
|
+
"message": "An expression was expected after {token}.",
|
|
525
|
+
"placeholders": {
|
|
526
|
+
"token": "the operator or punctuation the statement ended on"
|
|
527
|
+
},
|
|
528
|
+
"cause": "An operator, a comma or an opening bracket at the end of a statement continues it onto the next line, so a statement that ends this way and is followed by nothing usable has lost its right-hand side.",
|
|
529
|
+
"fix": "Supply the missing operand, or delete the trailing {token}.",
|
|
530
|
+
"autofix": false,
|
|
531
|
+
"example": {
|
|
532
|
+
"before": "len = input(14, \"Length\") +",
|
|
533
|
+
"after": "len = input(14, \"Length\") + 1"
|
|
534
|
+
},
|
|
535
|
+
"spec": "language.md 3.11",
|
|
536
|
+
"refines": null,
|
|
537
|
+
"test": "tests/errors/OS1022"
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
"code": "OS1023",
|
|
541
|
+
"title": "A function declared inside a block",
|
|
542
|
+
"severity": "error",
|
|
543
|
+
"stage": "parse",
|
|
544
|
+
"since": 1,
|
|
545
|
+
"message": "fn {name} is declared inside a block, and a function is declared at the top level of the file.",
|
|
546
|
+
"placeholders": {
|
|
547
|
+
"name": "the function being declared"
|
|
548
|
+
},
|
|
549
|
+
"cause": "Functions may not be nested (language.md 11.1). A declaration under an if, a loop, a switch arm or another function's body sits in a scope that ends where the block ends, so the name would exist for part of a file and not the rest, and a reader would have to trace the block structure to know whether a call on the line in front of them was legal. The declaration is reported rather than dropped or lifted: dropping it leaves every call to it as OS2001 with no word about where the function went, and lifting it to the top level compiles a form the language does not have.",
|
|
550
|
+
"fix": "Move the whole declaration out to the top level of the file, and call {name} from inside the block.",
|
|
551
|
+
"autofix": false,
|
|
552
|
+
"example": {
|
|
553
|
+
"before": "if trending\n fn smoothed(src) => sma(src, 9)\n plot(smoothed(close), \"Smooth\", aqua)",
|
|
554
|
+
"after": "fn smoothed(src) => sma(src, 9)\n\nif trending\n plot(smoothed(close), \"Smooth\", aqua)"
|
|
555
|
+
},
|
|
556
|
+
"spec": "language.md 11.1",
|
|
557
|
+
"refines": null,
|
|
558
|
+
"test": "tests/errors/OS1023"
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
"code": "OS1024",
|
|
562
|
+
"title": "Assignment to an indexed element",
|
|
563
|
+
"severity": "error",
|
|
564
|
+
"stage": "parse",
|
|
565
|
+
"since": 1,
|
|
566
|
+
"message": "An assignment writes to a name, and this target is an index into {name}.",
|
|
567
|
+
"placeholders": {
|
|
568
|
+
"name": "the name that was indexed"
|
|
569
|
+
},
|
|
570
|
+
"cause": "The grammar of language.md 19 admits one assignment target, an identifier, and there is no indexed assignment anywhere in the language. The reason is that [] reads two different things (language.md 9.6): on an array it is an element, which section 14.1 writes with set(arr, i, v), and on a series it is a bar the engine has already computed, which nothing in a script may overwrite. One syntax covering both would read as though a past bar could be rewritten.",
|
|
571
|
+
"fix": "Change an array element with set({name}, i, v), or assign to a plain name: the past of a series is computed and never written.",
|
|
572
|
+
"autofix": false,
|
|
573
|
+
"example": {
|
|
574
|
+
"before": "var prices = [0.0]\nprices[0] = close",
|
|
575
|
+
"after": "var prices = [0.0]\nset(prices, 0, close)"
|
|
576
|
+
},
|
|
577
|
+
"spec": "language.md 9.6, 14.1, 19",
|
|
578
|
+
"refines": null,
|
|
579
|
+
"test": "tests/errors/OS1024"
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
"code": "OS1025",
|
|
583
|
+
"title": "Assignment to a member",
|
|
584
|
+
"severity": "error",
|
|
585
|
+
"stage": "parse",
|
|
586
|
+
"since": 1,
|
|
587
|
+
"message": "An assignment writes to a name, and this target is the member {member} of {name}.",
|
|
588
|
+
"placeholders": {
|
|
589
|
+
"name": "the name written before the dot",
|
|
590
|
+
"member": "the name written after the dot"
|
|
591
|
+
},
|
|
592
|
+
"cause": "The grammar of language.md 19 admits one assignment target, an identifier. A dotted name reads a member of one of the namespaces on the closed list in language.md 15.2, and every member on that list is a per-bar fact, an instrument fact, a position fact or a function, each of them produced by the engine or by the host. There is nothing behind a dot that a script owns. Version 1 has no user-declared types that could add one either: type is a reserved word and is not implemented (language.md 18). A dotted name whose first half is not a namespace is the same answer for a simpler reason: it has no members at all.",
|
|
593
|
+
"fix": "Assign to a plain name. Version 1 has no member assignment: a dot reads a member, and the members a script can reach are facts and functions the library and the host supply.",
|
|
594
|
+
"autofix": false,
|
|
595
|
+
"example": {
|
|
596
|
+
"before": "chart.tickStep = 0.05\nplot(close + chart.tickStep, \"Stepped\", aqua)",
|
|
597
|
+
"after": "tickStep = 0.05\nplot(close + tickStep, \"Stepped\", aqua)"
|
|
598
|
+
},
|
|
599
|
+
"spec": "language.md 15.2, 19",
|
|
600
|
+
"refines": null,
|
|
601
|
+
"test": "tests/errors/OS1025"
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
"code": "OS1026",
|
|
605
|
+
"title": "Block comment",
|
|
606
|
+
"severity": "error",
|
|
607
|
+
"stage": "lex",
|
|
608
|
+
"since": 1,
|
|
609
|
+
"message": "{marker} does not open or close a comment. A comment is written // and runs to the end of its line.",
|
|
610
|
+
"placeholders": {
|
|
611
|
+
"marker": "the two characters that were written, the opener or the closer"
|
|
612
|
+
},
|
|
613
|
+
"cause": "The language has line comments and no block form (language.md 3.2). The block form is left out on purpose: an unterminated one swallows the rest of a file and reports its error at the last line, which is the worst message a compiler can produce. Without the form there is nothing special about the two characters, so a divide and a multiply are what they are and the line parses as arithmetic with its operands missing. The diagnostics then land on the operators rather than on the comment that caused them, and a reader is told three things that are each true of a program they did not write. The marker is reported where it sits instead.",
|
|
614
|
+
"fix": "Write // instead, and give every line of a commented region its own //, which every editor does with one keystroke.",
|
|
615
|
+
"autofix": false,
|
|
616
|
+
"example": {
|
|
617
|
+
"before": "lookback = 14 /* bars */",
|
|
618
|
+
"after": "lookback = 14 // bars"
|
|
619
|
+
},
|
|
620
|
+
"spec": "language.md 3.2",
|
|
621
|
+
"refines": null,
|
|
622
|
+
"test": "tests/errors/OS1026"
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
"code": "OS1027",
|
|
626
|
+
"title": "Malformed colour literal",
|
|
627
|
+
"severity": "error",
|
|
628
|
+
"stage": "lex",
|
|
629
|
+
"since": 1,
|
|
630
|
+
"message": "{written} is not a colour: a colour literal is # and six or eight hexadecimal digits.",
|
|
631
|
+
"placeholders": {
|
|
632
|
+
"written": "the # and the run of characters written against it"
|
|
633
|
+
},
|
|
634
|
+
"cause": "A hexadecimal colour is # and six digits, two each for red, green and blue, or eight where the last two are the alpha byte (language.md 3.8). The three and four digit shorthands belong to other formats and this language does not have them, and a run of any other length, or one carrying a digit outside 0 to f, is a typing slip. It has a code of its own because every character in it is one the language accepts: the substitution row that gives OS1001 its sentence about # is written for a # outside a colour literal, and its advice, to delete the character or move the text into a string, would throw away a colour the writer had nearly right.",
|
|
635
|
+
"fix": "Give it six hexadecimal digits, or eight where the last two are the alpha byte. A named colour or rgb(r, g, b) says the same thing without the digits.",
|
|
636
|
+
"autofix": false,
|
|
637
|
+
"example": {
|
|
638
|
+
"before": "plot(close, \"Close\", #ff88)",
|
|
639
|
+
"after": "plot(close, \"Close\", #ff8800)"
|
|
640
|
+
},
|
|
641
|
+
"spec": "language.md 3.8",
|
|
642
|
+
"refines": "OS1001",
|
|
643
|
+
"test": "tests/errors/OS1027"
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
"code": "OS1028",
|
|
647
|
+
"title": "A continuation line is not indented past its statement",
|
|
648
|
+
"severity": "error",
|
|
649
|
+
"stage": "lex",
|
|
650
|
+
"since": 1,
|
|
651
|
+
"message": "A continuation line must be indented more deeply than the line its statement began on; line {line} is indented {statement} and this line is indented {found}.",
|
|
652
|
+
"placeholders": {
|
|
653
|
+
"found": "the leading space count on this line",
|
|
654
|
+
"statement": "the leading space count of the line the statement began on",
|
|
655
|
+
"line": "the line the statement began on"
|
|
656
|
+
},
|
|
657
|
+
"cause": "A statement continues onto the next line when a bracket is still open, when the line ends with a binary operator, a comma, a ?, a : or an =, or when it ends with a backslash (language.md 3.11). A continuation is required to sit further right than the line the statement began on, so that a reader can tell a continued statement from a new one without scanning back up the file for a trailing operator. This is not the block rule of OS1003, and it has a code of its own for that reason: a continuation opens no block, so OS1003, whose message names the indentation every line of a block carries and whose fix offers to end that block, would state a fact about a block that is not there.",
|
|
658
|
+
"fix": "Indent this line further than the {statement} spaces on line {line}. Four more is the convention and the formatter's output.",
|
|
659
|
+
"autofix": true,
|
|
660
|
+
"example": {
|
|
661
|
+
"before": "total = ema(close, 9) +\nema(close, 21)",
|
|
662
|
+
"after": "total = ema(close, 9) +\n ema(close, 21)"
|
|
663
|
+
},
|
|
664
|
+
"spec": "language.md 3.11",
|
|
665
|
+
"refines": "OS1003",
|
|
666
|
+
"test": "tests/errors/OS1028"
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
"code": "OS1029",
|
|
670
|
+
"title": "A name written against a number",
|
|
671
|
+
"severity": "error",
|
|
672
|
+
"stage": "lex",
|
|
673
|
+
"since": 1,
|
|
674
|
+
"message": "{written} is neither a number nor a name: the number literal ends at {number}, and a name cannot begin with a digit.",
|
|
675
|
+
"placeholders": {
|
|
676
|
+
"written": "the whole run, the number literal and the name characters against it",
|
|
677
|
+
"number": "the part of the run that is a number literal"
|
|
678
|
+
},
|
|
679
|
+
"cause": "A number literal is decimal, with an optional fractional part and an optional exponent, or 0x and hexadecimal digits. There is no octal form and no binary form, and an underscore belongs to a literal only where a digit follows it (language.md 3.5). A name cannot begin with a digit (language.md 3.3). A base prefix the language does not have, a unit written against a quantity, and a literal that ends in an underscore all produce this run. The whole run is reported rather than its first character, because every character in it is one the language accepts: naming the leading digit would name something legal, and deleting it, which is what OS1001 advises, leaves a valid name behind and a program that means something else.",
|
|
680
|
+
"fix": "Write the value in a form the language has: a decimal literal, or 0x and hexadecimal digits. There is no binary form and no octal form, and an underscore separates digit groups only where a digit follows it. Where a number and a name were meant as two things, put an operator between them.",
|
|
681
|
+
"autofix": false,
|
|
682
|
+
"example": {
|
|
683
|
+
"before": "mask = 0b1011",
|
|
684
|
+
"after": "mask = 0x0b"
|
|
685
|
+
},
|
|
686
|
+
"spec": "language.md 3.3, 3.5",
|
|
687
|
+
"refines": "OS1001",
|
|
688
|
+
"test": "tests/errors/OS1029"
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
"code": "OS2001",
|
|
692
|
+
"title": "Name is not defined here",
|
|
693
|
+
"severity": "error",
|
|
694
|
+
"stage": "check",
|
|
695
|
+
"since": 1,
|
|
696
|
+
"message": "{name} is not defined at this point in the file.",
|
|
697
|
+
"placeholders": {
|
|
698
|
+
"name": "the name that was read",
|
|
699
|
+
"suggestion": "the closest name that is in scope here, by edit distance"
|
|
700
|
+
},
|
|
701
|
+
"cause": "The file is the body of the per-bar loop and runs top to bottom, so a name must be assigned above the line that reads it. A name first assigned inside a block belongs to that block and is not visible outside it. A misspelling arrives here too.",
|
|
702
|
+
"fix": "Assign {name} above this line, move this line below its assignment, or correct the spelling to {suggestion}.",
|
|
703
|
+
"autofix": false,
|
|
704
|
+
"example": {
|
|
705
|
+
"before": "plot(spread, \"Spread\", aqua)\nspread = high - low",
|
|
706
|
+
"after": "spread = high - low\nplot(spread, \"Spread\", aqua)"
|
|
707
|
+
},
|
|
708
|
+
"spec": "language.md 12.2, 12.5",
|
|
709
|
+
"refines": null,
|
|
710
|
+
"test": "tests/errors/OS2001"
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
"code": "OS2002",
|
|
714
|
+
"title": "The name already exists in an enclosing scope",
|
|
715
|
+
"severity": "error",
|
|
716
|
+
"stage": "check",
|
|
717
|
+
"since": 1,
|
|
718
|
+
"message": "{name} is already declared at line {line}, so a second one cannot be declared here.",
|
|
719
|
+
"placeholders": {
|
|
720
|
+
"name": "the name being declared",
|
|
721
|
+
"line": "the line of the existing declaration, or the word built-in when it is a library name"
|
|
722
|
+
},
|
|
723
|
+
"cause": "There is no shadowing. An assignment to a name that exists in an enclosing scope updates that name, so declaring the same name inside is a request the language has no syntax for and is always a mistake. Built-in names such as close, ema, aqua and plot live in the global scope, so assigning to one lands here as well.",
|
|
724
|
+
"fix": "Rename this one, or drop the inner declaration and let the assignment update the {name} at line {line}.",
|
|
725
|
+
"autofix": false,
|
|
726
|
+
"example": {
|
|
727
|
+
"before": "len = 20\n\nfn smooth(src) =>\n len = 9\n sma(src, len)",
|
|
728
|
+
"after": "len = 20\n\nfn smooth(src) =>\n inner = 9\n sma(src, inner)"
|
|
729
|
+
},
|
|
730
|
+
"spec": "language.md 12.3, 12.4",
|
|
731
|
+
"refines": null,
|
|
732
|
+
"test": "tests/errors/OS2002"
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
"code": "OS2003",
|
|
736
|
+
"title": "Types do not match",
|
|
737
|
+
"severity": "error",
|
|
738
|
+
"stage": "check",
|
|
739
|
+
"since": 1,
|
|
740
|
+
"message": "{leftType} and {rightType} do not mix here.",
|
|
741
|
+
"placeholders": {
|
|
742
|
+
"leftType": "the type of the left operand or of the name's first assignment",
|
|
743
|
+
"rightType": "the type of the right operand or of the value being assigned"
|
|
744
|
+
},
|
|
745
|
+
"cause": "There is no implicit conversion anywhere in the language: 0 is not false, an empty string is not false, and a number is not a string. A name's type is fixed by its first assignment, so assigning a different type later arrives here too. A first assignment of none fixes no type, because none is a member of every type; the type comes from the first assignment that gives a definite one, and this code names the second definite type rather than the first. Every silent coercion rule is a source of bugs that survive review, and a trading script that quietly treats a zero as a false is a bug nobody finds until it costs money. A declaration handle arrives here as well: plot(), plotCandles(), fill() and level() return the compile-time half of a declaration (language.md 5.4), so a handle in a position that requires a value has nothing to give, and the message names its type as plot, fill or level.",
|
|
746
|
+
"fix": "Convert explicitly: text(x) for a string, number(s) for a number, bool(x) for a bool, or use a separate name for the second value. A plot, fill or level handle converts to nothing: leave it named at the top level, pass it to fill(), and use draw.line() or draw.box() where the script needs something it can keep.",
|
|
747
|
+
"autofix": false,
|
|
748
|
+
"example": {
|
|
749
|
+
"before": "s = \"count: \" + 5",
|
|
750
|
+
"after": "s = \"count: \" + text(5)"
|
|
751
|
+
},
|
|
752
|
+
"spec": "language.md 5.3, 5.4, 10.1",
|
|
753
|
+
"refines": null,
|
|
754
|
+
"test": "tests/errors/OS2003"
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
"code": "OS2004",
|
|
758
|
+
"title": "This value has no history",
|
|
759
|
+
"severity": "error",
|
|
760
|
+
"stage": "check",
|
|
761
|
+
"since": 1,
|
|
762
|
+
"message": "{expr} has no history, so [] cannot read a past value of it.",
|
|
763
|
+
"placeholders": {
|
|
764
|
+
"expr": "the expression the history operator was applied to"
|
|
765
|
+
},
|
|
766
|
+
"cause": "History is retained for four kinds of value: a built-in series, a name assigned at the top level of the file, a call to a function declared to return a series, and a series parameter of a user function. Retaining it for every temporary inside every block would cost memory per bar and would not run fifty thousand bars in a browser tab. A name bound to a declaration handle is none of the four, although it sits at the top level: it is a compile-time binding with no per-bar value at all. A runtime object carries no history either, so [] on a line, a label, a box, a polyline or a table is this error as well (language.md 5.4).",
|
|
767
|
+
"fix": "Assign the value to a name at the top level of the file, then read that name's history.",
|
|
768
|
+
"autofix": false,
|
|
769
|
+
"example": {
|
|
770
|
+
"before": "if trending\n body = close - open\n plot(body[1], \"Previous body\", aqua)",
|
|
771
|
+
"after": "body = close - open\nplot(trending ? body[1] : none, \"Previous body\", aqua)"
|
|
772
|
+
},
|
|
773
|
+
"spec": "language.md 5.2, 5.4",
|
|
774
|
+
"refines": null,
|
|
775
|
+
"test": "tests/errors/OS2004"
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
"code": "OS2005",
|
|
779
|
+
"title": "Recursive call",
|
|
780
|
+
"severity": "error",
|
|
781
|
+
"stage": "check",
|
|
782
|
+
"since": 1,
|
|
783
|
+
"message": "{name} calls itself: {cycle}.",
|
|
784
|
+
"placeholders": {
|
|
785
|
+
"name": "the function at the start of the cycle",
|
|
786
|
+
"cycle": "the call chain, from the function back to itself"
|
|
787
|
+
},
|
|
788
|
+
"cause": "State slots for var and for stateful library calls are allocated statically, one per call site. Recursion would need a dynamic stack of them, whose cost would be paid on every bar of every script to support a form a per-bar language almost never needs.",
|
|
789
|
+
"fix": "Rewrite it as a loop, or split the work into two functions that do not call each other.",
|
|
790
|
+
"autofix": false,
|
|
791
|
+
"example": {
|
|
792
|
+
"before": "fn total(n) =>\n if n <= 0\n return 0\n close[n] + total(n - 1)",
|
|
793
|
+
"after": "fn total(n) =>\n runningTotal = 0.0\n for i = 0 to n\n runningTotal += close[i]\n runningTotal"
|
|
794
|
+
},
|
|
795
|
+
"spec": "language.md 11.4",
|
|
796
|
+
"refines": null,
|
|
797
|
+
"test": "tests/errors/OS2005"
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
"code": "OS2006",
|
|
801
|
+
"title": "Assignment to a loop variable",
|
|
802
|
+
"severity": "error",
|
|
803
|
+
"stage": "check",
|
|
804
|
+
"since": 1,
|
|
805
|
+
"message": "{name} is this loop's variable and cannot be assigned inside the body.",
|
|
806
|
+
"placeholders": {
|
|
807
|
+
"name": "the loop variable"
|
|
808
|
+
},
|
|
809
|
+
"cause": "The loop controls its own variable, so a body that also writes to it produces a loop whose iteration count cannot be read from its header.",
|
|
810
|
+
"fix": "Use break to leave early, or keep a separate name for the value the body changes.",
|
|
811
|
+
"autofix": false,
|
|
812
|
+
"example": {
|
|
813
|
+
"before": "for i = 0 to 9\n if close[i] > hi\n i = 9",
|
|
814
|
+
"after": "for i = 0 to 9\n if close[i] > hi\n break"
|
|
815
|
+
},
|
|
816
|
+
"spec": "language.md 10.3",
|
|
817
|
+
"refines": null,
|
|
818
|
+
"test": "tests/errors/OS2006"
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
"code": "OS2007",
|
|
822
|
+
"title": "The file has no declaration",
|
|
823
|
+
"severity": "error",
|
|
824
|
+
"stage": "check",
|
|
825
|
+
"since": 1,
|
|
826
|
+
"message": "A file needs one study() or strategy() declaration before any other statement.",
|
|
827
|
+
"placeholders": {},
|
|
828
|
+
"cause": "The declaration supplies the title, the pane, the precision and the settings the host needs before the first bar runs. Without one there is nothing to put in a legend and nothing to name in a picker.",
|
|
829
|
+
"fix": "Add study(\"Name\") as the first statement, or strategy(\"Name\") if the file places orders.",
|
|
830
|
+
"autofix": false,
|
|
831
|
+
"example": {
|
|
832
|
+
"before": "fast = ema(close, 9)\nplot(fast, \"Fast\", aqua)",
|
|
833
|
+
"after": "study(\"EMA\", overlay = true)\n\nfast = ema(close, 9)\nplot(fast, \"Fast\", aqua)"
|
|
834
|
+
},
|
|
835
|
+
"spec": "language.md 13.1",
|
|
836
|
+
"refines": null,
|
|
837
|
+
"test": "tests/errors/OS2007"
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
"code": "OS2008",
|
|
841
|
+
"title": "More than one declaration",
|
|
842
|
+
"severity": "error",
|
|
843
|
+
"stage": "check",
|
|
844
|
+
"since": 1,
|
|
845
|
+
"message": "This file already declares {kind} at line {line}.",
|
|
846
|
+
"placeholders": {
|
|
847
|
+
"kind": "study or strategy, whichever came first",
|
|
848
|
+
"line": "the line of the first declaration"
|
|
849
|
+
},
|
|
850
|
+
"cause": "Two declarations would need a rule for whose overlay, precision and title win, and the answer to that question is always that there should have been one. A strategy accepts every study option, so a file that wants both is a strategy.",
|
|
851
|
+
"fix": "Delete this declaration and move the options you wanted onto the one at line {line}.",
|
|
852
|
+
"autofix": false,
|
|
853
|
+
"example": {
|
|
854
|
+
"before": "study(\"EMA cross\", overlay = true)\nstrategy(\"EMA cross\")",
|
|
855
|
+
"after": "strategy(\"EMA cross\", overlay = true)"
|
|
856
|
+
},
|
|
857
|
+
"spec": "language.md 13.1",
|
|
858
|
+
"refines": null,
|
|
859
|
+
"test": "tests/errors/OS2008"
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
"code": "OS2009",
|
|
863
|
+
"title": "Unknown member of a namespace",
|
|
864
|
+
"severity": "error",
|
|
865
|
+
"stage": "check",
|
|
866
|
+
"since": 1,
|
|
867
|
+
"message": "{namespace} has no member named {member}.",
|
|
868
|
+
"placeholders": {
|
|
869
|
+
"namespace": "the namespace that was read",
|
|
870
|
+
"member": "the member name that does not exist",
|
|
871
|
+
"suggestion": "the closest member of that namespace, by edit distance"
|
|
872
|
+
},
|
|
873
|
+
"cause": "Namespaces hold a fixed set of names, published in the library manifest the editor completes from. A member that does not exist is a typo or a name from a different namespace.",
|
|
874
|
+
"fix": "Use one of {namespace}'s members; {suggestion} is the closest match to what was written.",
|
|
875
|
+
"autofix": false,
|
|
876
|
+
"example": {
|
|
877
|
+
"before": "plot(bar.idx, \"Bar\", aqua)",
|
|
878
|
+
"after": "plot(bar.index, \"Bar\", aqua)"
|
|
879
|
+
},
|
|
880
|
+
"spec": "language.md 15.2",
|
|
881
|
+
"refines": null,
|
|
882
|
+
"test": "tests/errors/OS2009"
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
"code": "OS2010",
|
|
886
|
+
"title": "This name is not a function",
|
|
887
|
+
"severity": "error",
|
|
888
|
+
"stage": "check",
|
|
889
|
+
"since": 1,
|
|
890
|
+
"message": "{name} is {type}, not a function, so it cannot be called.",
|
|
891
|
+
"placeholders": {
|
|
892
|
+
"name": "the name that was called",
|
|
893
|
+
"type": "the type the name holds",
|
|
894
|
+
"suggestion": "the library function whose name is closest, with its first argument filled in"
|
|
895
|
+
},
|
|
896
|
+
"cause": "A built-in series and a user value are read bare. An argument list after one of them is usually a library function that was remembered with the wrong name.",
|
|
897
|
+
"fix": "Remove the argument list to read the value, or call the function that was meant: {suggestion}.",
|
|
898
|
+
"autofix": false,
|
|
899
|
+
"example": {
|
|
900
|
+
"before": "v = volume(20)",
|
|
901
|
+
"after": "v = sma(volume, 20)"
|
|
902
|
+
},
|
|
903
|
+
"spec": "language.md 15.1",
|
|
904
|
+
"refines": null,
|
|
905
|
+
"test": "tests/errors/OS2010"
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
"code": "OS2011",
|
|
909
|
+
"title": "A condition must be a bool",
|
|
910
|
+
"severity": "error",
|
|
911
|
+
"stage": "check",
|
|
912
|
+
"since": 1,
|
|
913
|
+
"message": "This condition is {type}; a condition must be bool or none.",
|
|
914
|
+
"placeholders": {
|
|
915
|
+
"type": "the type the condition expression produced",
|
|
916
|
+
"name": "the condition expression as written, or its name when it is one"
|
|
917
|
+
},
|
|
918
|
+
"cause": "There is no truthiness. A number is not a condition, a string is not a condition, and an absent value takes the false branch only after the condition has been written as a genuine test.",
|
|
919
|
+
"fix": "Write the test out: {name} > 0 for a count, isNone({name}) for absence, {name} != \"\" for a string.",
|
|
920
|
+
"autofix": false,
|
|
921
|
+
"example": {
|
|
922
|
+
"before": "if hitCount\n signal(\"SEEN\")",
|
|
923
|
+
"after": "if hitCount > 0\n signal(\"SEEN\")"
|
|
924
|
+
},
|
|
925
|
+
"spec": "language.md 5.3, 10.2",
|
|
926
|
+
"refines": "OS2003",
|
|
927
|
+
"test": "tests/errors/OS2011"
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
"code": "OS2012",
|
|
931
|
+
"title": "The two arms of the ternary have different types",
|
|
932
|
+
"severity": "error",
|
|
933
|
+
"stage": "check",
|
|
934
|
+
"since": 1,
|
|
935
|
+
"message": "The arms of this ? : are {leftType} and {rightType}.",
|
|
936
|
+
"placeholders": {
|
|
937
|
+
"leftType": "the type of the arm before the colon",
|
|
938
|
+
"rightType": "the type of the arm after the colon"
|
|
939
|
+
},
|
|
940
|
+
"cause": "The ternary yields one value, so both arms must agree on its type. The one exception is none, which is a member of every type and is how an arm says there is nothing here.",
|
|
941
|
+
"fix": "Make both arms the same type with text() or number(), or use none for the empty arm.",
|
|
942
|
+
"autofix": false,
|
|
943
|
+
"example": {
|
|
944
|
+
"before": "label = up ? 1 : \"down\"",
|
|
945
|
+
"after": "label = up ? \"up\" : \"down\""
|
|
946
|
+
},
|
|
947
|
+
"spec": "language.md 9.5",
|
|
948
|
+
"refines": "OS2003",
|
|
949
|
+
"test": "tests/errors/OS2012"
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
"code": "OS2013",
|
|
953
|
+
"title": "An array literal mixes types",
|
|
954
|
+
"severity": "error",
|
|
955
|
+
"stage": "check",
|
|
956
|
+
"since": 1,
|
|
957
|
+
"message": "This array holds {firstType} at index 0 and {otherType} at index {index}.",
|
|
958
|
+
"placeholders": {
|
|
959
|
+
"firstType": "the type of the first element",
|
|
960
|
+
"otherType": "the type of the element that disagrees",
|
|
961
|
+
"index": "the index of the element that disagrees"
|
|
962
|
+
},
|
|
963
|
+
"cause": "An array is homogeneous, which is what lets size, sum, avg and sort mean one thing. A mixed literal is usually two lists that belong side by side.",
|
|
964
|
+
"fix": "Make every element {firstType}, or keep two arrays and index them together.",
|
|
965
|
+
"autofix": false,
|
|
966
|
+
"example": {
|
|
967
|
+
"before": "rows = [\"RSI\", 14, \"EMA\", 9]",
|
|
968
|
+
"after": "names = [\"RSI\", \"EMA\"]\nlengths = [14, 9]"
|
|
969
|
+
},
|
|
970
|
+
"spec": "language.md 14.1",
|
|
971
|
+
"refines": "OS2003",
|
|
972
|
+
"test": "tests/errors/OS2013"
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
"code": "OS2014",
|
|
976
|
+
"title": "A function cannot be used as a value",
|
|
977
|
+
"severity": "error",
|
|
978
|
+
"stage": "check",
|
|
979
|
+
"since": 1,
|
|
980
|
+
"message": "{name} is a function, and version 1 has no function values.",
|
|
981
|
+
"placeholders": {
|
|
982
|
+
"name": "the function name that was used bare"
|
|
983
|
+
},
|
|
984
|
+
"cause": "Functions are not values in version 1: they cannot be assigned, stored in an array or passed as an argument. The feature is reserved rather than absent, and language.md 18 names it.",
|
|
985
|
+
"fix": "Call {name} with its arguments and use the value it returns.",
|
|
986
|
+
"autofix": false,
|
|
987
|
+
"example": {
|
|
988
|
+
"before": "src = ema\nplot(src, \"EMA\", aqua)",
|
|
989
|
+
"after": "src = ema(close, 9)\nplot(src, \"EMA\", aqua)"
|
|
990
|
+
},
|
|
991
|
+
"spec": "language.md 11.1, 18",
|
|
992
|
+
"refines": null,
|
|
993
|
+
"test": "tests/errors/OS2014"
|
|
994
|
+
},
|
|
995
|
+
{
|
|
996
|
+
"code": "OS2015",
|
|
997
|
+
"title": "The element type of this empty array is unknown",
|
|
998
|
+
"severity": "error",
|
|
999
|
+
"stage": "check",
|
|
1000
|
+
"since": 1,
|
|
1001
|
+
"message": "An empty array literal needs its element type from an annotation or from a first use.",
|
|
1002
|
+
"placeholders": {},
|
|
1003
|
+
"cause": "An array is homogeneous, so its element type has to be known before anything is pushed into it. An empty literal takes that type from an annotation when it has one, and otherwise from the first call in the file, in source order, that puts an element into it: push, unshift, insert or set. With neither an annotation nor such a call, nothing in the file says what the array holds and the language does not guess.",
|
|
1004
|
+
"fix": "Annotate the declaration, var hits: array<number> = [], or put the first element in with push(), unshift(), insert() or set() and let the type be read from that call.",
|
|
1005
|
+
"autofix": false,
|
|
1006
|
+
"example": {
|
|
1007
|
+
"before": "var hits = []\nplot(size(hits), \"Hits\", aqua)",
|
|
1008
|
+
"after": "var hits: array<number> = []\nplot(size(hits), \"Hits\", aqua)"
|
|
1009
|
+
},
|
|
1010
|
+
"spec": "language.md 14.1",
|
|
1011
|
+
"refines": null,
|
|
1012
|
+
"test": "tests/errors/OS2015"
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
"code": "OS2016",
|
|
1016
|
+
"title": "Unknown type in an annotation",
|
|
1017
|
+
"severity": "error",
|
|
1018
|
+
"stage": "check",
|
|
1019
|
+
"since": 1,
|
|
1020
|
+
"message": "{type} is not a type.",
|
|
1021
|
+
"placeholders": {
|
|
1022
|
+
"type": "the word that was written where a type was expected"
|
|
1023
|
+
},
|
|
1024
|
+
"cause": "The types are number, string, bool, color, none, array<T> and the five runtime object types line, label, box, polyline and table, with series in front where a per-bar value is meant. There is no integer type: a length, a bar count and a price are all number. The declaration handle types plot, fill and level are deliberately not in the grammar, so writing one in an annotation is this error as well: a handle can never be a var, a parameter, a return value or an array element, and there is nothing left for an annotation to describe.",
|
|
1025
|
+
"fix": "Use number, string, bool, color, array<T>, or an object type (line, label, box, polyline, table), with series in front for a per-bar value.",
|
|
1026
|
+
"autofix": false,
|
|
1027
|
+
"example": {
|
|
1028
|
+
"before": "fn band(src: series number, len: int = 20) =>\n sma(src, len)",
|
|
1029
|
+
"after": "fn band(src: series number, len: number = 20) =>\n sma(src, len)"
|
|
1030
|
+
},
|
|
1031
|
+
"spec": "language.md 5.1, 5.4, 19",
|
|
1032
|
+
"refines": null,
|
|
1033
|
+
"test": "tests/errors/OS2016"
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
"code": "OS2017",
|
|
1037
|
+
"title": "A function with this name is already declared",
|
|
1038
|
+
"severity": "error",
|
|
1039
|
+
"stage": "check",
|
|
1040
|
+
"since": 1,
|
|
1041
|
+
"message": "{name} is already declared as a function at line {line}.",
|
|
1042
|
+
"placeholders": {
|
|
1043
|
+
"name": "the function name",
|
|
1044
|
+
"line": "the line of the first declaration"
|
|
1045
|
+
},
|
|
1046
|
+
"cause": "A user function is not overloaded in version 1, so one name in a file is one function. Two declarations would make the call site's meaning depend on argument types, which is exactly the kind of resolution a reader cannot do in their head. The library's own overloads are a different thing: they are a fixed published set, round() and clear() among them, and a script cannot add to it.",
|
|
1047
|
+
"fix": "Rename one of them, or give the one function a default argument that covers both uses.",
|
|
1048
|
+
"autofix": false,
|
|
1049
|
+
"example": {
|
|
1050
|
+
"before": "fn band(src) => sma(src, 20)\nfn band(src, len) => sma(src, len)",
|
|
1051
|
+
"after": "fn band(src, len = 20) => sma(src, len)"
|
|
1052
|
+
},
|
|
1053
|
+
"spec": "language.md 11.1",
|
|
1054
|
+
"refines": null,
|
|
1055
|
+
"test": "tests/errors/OS2017"
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
"code": "OS2018",
|
|
1059
|
+
"title": "Duplicate parameter name",
|
|
1060
|
+
"severity": "error",
|
|
1061
|
+
"stage": "check",
|
|
1062
|
+
"since": 1,
|
|
1063
|
+
"message": "{name} appears twice in this parameter list.",
|
|
1064
|
+
"placeholders": {
|
|
1065
|
+
"name": "the repeated parameter name"
|
|
1066
|
+
},
|
|
1067
|
+
"cause": "Two parameters with one name make every use of that name inside the body ambiguous, and a named argument at the call site could not say which one it meant.",
|
|
1068
|
+
"fix": "Rename the second parameter, so every name in the list appears once.",
|
|
1069
|
+
"autofix": false,
|
|
1070
|
+
"example": {
|
|
1071
|
+
"before": "fn ratio(src, src) => src / src[1]",
|
|
1072
|
+
"after": "fn ratio(src, len) => src / src[len]"
|
|
1073
|
+
},
|
|
1074
|
+
"spec": "language.md 11.2",
|
|
1075
|
+
"refines": null,
|
|
1076
|
+
"test": "tests/errors/OS2018"
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
"code": "OS2019",
|
|
1080
|
+
"title": "This type cannot be an array element",
|
|
1081
|
+
"severity": "error",
|
|
1082
|
+
"stage": "check",
|
|
1083
|
+
"since": 1,
|
|
1084
|
+
"message": "array<{type}> is not a type: {type} cannot be an array element.",
|
|
1085
|
+
"placeholders": {
|
|
1086
|
+
"type": "the element type written inside the angle brackets"
|
|
1087
|
+
},
|
|
1088
|
+
"cause": "An array element is a number, a string, a bool, a color, or one of the runtime object types of language.md 5.4, and nothing else. A declaration handle is the compile-time half of a declaration and has no run-time value, so there would be nothing to store. A series is a per-bar history, and an array of them would be a second history the engine retained per element, as many as the script chose to push. An array of arrays is the matrix that language.md 14.2 reserves for a later version.",
|
|
1089
|
+
"fix": "Use an element type an array holds: number, string, bool, color, line, label, box, polyline or table. A plot cannot be kept anywhere, so declare each plot at the top level and give it its own name.",
|
|
1090
|
+
"autofix": false,
|
|
1091
|
+
"example": {
|
|
1092
|
+
"before": "var edges: array<plot> = []\npush(edges, plot(upper, \"Upper\", aqua))",
|
|
1093
|
+
"after": "upperEdge = plot(upper, \"Upper\", aqua)\nlowerEdge = plot(lower, \"Lower\", aqua)\nfill(upperEdge, lowerEdge, color = fade(aqua, 88))"
|
|
1094
|
+
},
|
|
1095
|
+
"spec": "language.md 5.4, 14.1, 19",
|
|
1096
|
+
"refines": "OS2016",
|
|
1097
|
+
"test": "tests/errors/OS2019"
|
|
1098
|
+
},
|
|
1099
|
+
{
|
|
1100
|
+
"code": "OS3001",
|
|
1101
|
+
"title": "Wrong number of arguments",
|
|
1102
|
+
"severity": "error",
|
|
1103
|
+
"stage": "check",
|
|
1104
|
+
"since": 1,
|
|
1105
|
+
"message": "{name} takes {expected} arguments and {found} were given.",
|
|
1106
|
+
"placeholders": {
|
|
1107
|
+
"name": "the function being called",
|
|
1108
|
+
"expected": "the number of parameters, or the range when some have defaults",
|
|
1109
|
+
"found": "the number of arguments at this call site",
|
|
1110
|
+
"signature": "the function's full signature, parameter names and defaults included"
|
|
1111
|
+
},
|
|
1112
|
+
"cause": "A call site is checked against the declared parameter list. Parameters with defaults may be left out, and every parameter without one must be supplied.",
|
|
1113
|
+
"fix": "Pass the arguments the signature names: {signature}.",
|
|
1114
|
+
"autofix": false,
|
|
1115
|
+
"example": {
|
|
1116
|
+
"before": "e = ema(close)",
|
|
1117
|
+
"after": "e = ema(close, 9)"
|
|
1118
|
+
},
|
|
1119
|
+
"spec": "language.md 11.2",
|
|
1120
|
+
"refines": null,
|
|
1121
|
+
"test": "tests/errors/OS3001"
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
"code": "OS3002",
|
|
1125
|
+
"title": "Unknown named argument",
|
|
1126
|
+
"severity": "error",
|
|
1127
|
+
"stage": "check",
|
|
1128
|
+
"since": 1,
|
|
1129
|
+
"message": "{name} has no argument called {argument}.",
|
|
1130
|
+
"placeholders": {
|
|
1131
|
+
"name": "the function being called",
|
|
1132
|
+
"argument": "the named argument that does not exist",
|
|
1133
|
+
"names": "every argument name the function accepts, in declaration order",
|
|
1134
|
+
"suggestion": "the closest accepted name, by edit distance"
|
|
1135
|
+
},
|
|
1136
|
+
"cause": "Named arguments are matched against the parameter list by exact name. A name that is not on the list is a spelling that the call site and the function disagree about, and ignoring it would leave the argument silently unapplied.",
|
|
1137
|
+
"fix": "Use one of {names}; {suggestion} is the closest to what was written.",
|
|
1138
|
+
"autofix": false,
|
|
1139
|
+
"example": {
|
|
1140
|
+
"before": "plot(v, \"V\", colour = aqua)",
|
|
1141
|
+
"after": "plot(v, \"V\", color = aqua)"
|
|
1142
|
+
},
|
|
1143
|
+
"spec": "language.md 11.2",
|
|
1144
|
+
"refines": null,
|
|
1145
|
+
"test": "tests/errors/OS3002"
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
"code": "OS3003",
|
|
1149
|
+
"title": "This option must be a constant",
|
|
1150
|
+
"severity": "error",
|
|
1151
|
+
"stage": "check",
|
|
1152
|
+
"since": 1,
|
|
1153
|
+
"message": "{option} is read once, before the first bar, so it cannot depend on bar data.",
|
|
1154
|
+
"placeholders": {
|
|
1155
|
+
"option": "the option that was given a bar-dependent value"
|
|
1156
|
+
},
|
|
1157
|
+
"cause": "The declaration builds the legend, the axis and the settings dialog before bar 0 runs, so its options must be literals, arithmetic over literals, or an input(). A value that changes per bar has no single answer at the moment the dialog is built. The rule covers more than the declaration's own options: every argument that lands in a declaration fixed before bar 0 arrives here too, signal's at, shape and color, table's position, rows and cols, and a plot's style arguments among them. An input() counts as a constant for this purpose, because the engine resolves inputs at load and substitutes the resolved value before bar 0 runs.",
|
|
1158
|
+
"fix": "Use a literal, or make it tunable with an input(): {option} = input(2, \"{option}\").",
|
|
1159
|
+
"autofix": false,
|
|
1160
|
+
"example": {
|
|
1161
|
+
"before": "study(\"Range\", precision = round(close / 1000))",
|
|
1162
|
+
"after": "study(\"Range\", precision = input(2, \"Precision\"))"
|
|
1163
|
+
},
|
|
1164
|
+
"spec": "language.md 13.2, 15.3",
|
|
1165
|
+
"refines": null,
|
|
1166
|
+
"test": "tests/errors/OS3003"
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
"code": "OS3004",
|
|
1170
|
+
"title": "Argument is not a valid whole number",
|
|
1171
|
+
"severity": "error",
|
|
1172
|
+
"stage": "check",
|
|
1173
|
+
"since": 1,
|
|
1174
|
+
"message": "{name}'s {argument} must be a whole number {range}; {found} was given.",
|
|
1175
|
+
"placeholders": {
|
|
1176
|
+
"name": "the function being called",
|
|
1177
|
+
"argument": "the parameter name",
|
|
1178
|
+
"range": "the accepted range, such as 1 or more",
|
|
1179
|
+
"found": "the literal value that was written"
|
|
1180
|
+
},
|
|
1181
|
+
"cause": "A lookback length, an index and a loop step are counts of bars or of elements. A fractional value is rejected rather than truncated, because a length of 14.5 is a bug in the script and rounding it hides the bug. A step of 0 is rejected because it is the one loop that cannot finish.",
|
|
1182
|
+
"fix": "Pass a whole number inside {range}, and wrap a computed value in floor() or round().",
|
|
1183
|
+
"autofix": false,
|
|
1184
|
+
"example": {
|
|
1185
|
+
"before": "s = sma(close, len / 2)",
|
|
1186
|
+
"after": "s = sma(close, floor(len / 2))"
|
|
1187
|
+
},
|
|
1188
|
+
"spec": "language.md 5.1, 10.3",
|
|
1189
|
+
"refines": null,
|
|
1190
|
+
"test": "tests/errors/OS3004"
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
"code": "OS3005",
|
|
1194
|
+
"title": "Positional argument after a named one",
|
|
1195
|
+
"severity": "error",
|
|
1196
|
+
"stage": "check",
|
|
1197
|
+
"since": 1,
|
|
1198
|
+
"message": "A positional argument cannot follow a named one.",
|
|
1199
|
+
"placeholders": {},
|
|
1200
|
+
"cause": "Once an argument is named, the position of the arguments after it no longer says which parameter they fill. The order is positional first, named afterwards, which keeps every call site readable without the signature in front of you.",
|
|
1201
|
+
"fix": "Name this argument too, or move it in front of the first named argument.",
|
|
1202
|
+
"autofix": false,
|
|
1203
|
+
"example": {
|
|
1204
|
+
"before": "b = band(close, len = 20, 3)",
|
|
1205
|
+
"after": "b = band(close, len = 20, mult = 3)"
|
|
1206
|
+
},
|
|
1207
|
+
"spec": "language.md 11.2",
|
|
1208
|
+
"refines": null,
|
|
1209
|
+
"test": "tests/errors/OS3005"
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
"code": "OS3006",
|
|
1213
|
+
"title": "This call must be at the top level",
|
|
1214
|
+
"severity": "error",
|
|
1215
|
+
"stage": "check",
|
|
1216
|
+
"since": 1,
|
|
1217
|
+
"message": "{name} defines part of the study's fixed shape and cannot appear inside {construct}.",
|
|
1218
|
+
"placeholders": {
|
|
1219
|
+
"name": "the call, one of plot, plotCandles, fill, level, table, leg.fixed or leg.relative",
|
|
1220
|
+
"construct": "the enclosing construct, such as an if block, a loop or a function body"
|
|
1221
|
+
},
|
|
1222
|
+
"cause": "The set of plotted columns, bands, levels and grids is fixed before bar 0 so the chart can build a legend, an axis and a settings dialog. A call inside a branch would add a column on some bars and not others, and there would be nothing stable to name. table() is on the list for the same reason, the grid's size and corner being part of the study's fixed shape, although what it returns is a runtime object that cell() writes to per bar (language.md 5.4). A leg declaration is on the list for the same reason: the set of contracts a strategy trades is fixed before bar 0, and a leg that existed on some bars and not others would leave the run's record with nothing to key on. A drawing or an alert call inside a request expression is the same error for a related reason: that expression is evaluated on another instrument's bars, so there is no bar of this chart for it to draw on.",
|
|
1223
|
+
"fix": "Move the call to the top level and hide it per bar by passing none: plot(cond ? value : none, ...). Inside a request expression, read the value first and draw with it afterwards. A leg is not hidden by passing none: declare it at the top level and decide per bar whether to send it an order.",
|
|
1224
|
+
"autofix": false,
|
|
1225
|
+
"example": {
|
|
1226
|
+
"before": "if trending\n plot(ema20, \"EMA 20\", aqua)",
|
|
1227
|
+
"after": "plot(trending ? ema20 : none, \"EMA 20\", aqua)"
|
|
1228
|
+
},
|
|
1229
|
+
"spec": "language.md 7.1, 15.3; stdlib.md 17.6",
|
|
1230
|
+
"refines": null,
|
|
1231
|
+
"test": "tests/errors/OS3006"
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
"code": "OS3007",
|
|
1235
|
+
"title": "input() must be at the top level",
|
|
1236
|
+
"severity": "error",
|
|
1237
|
+
"stage": "check",
|
|
1238
|
+
"since": 1,
|
|
1239
|
+
"message": "input() builds one row of the settings dialog, which is read once before the first bar.",
|
|
1240
|
+
"placeholders": {},
|
|
1241
|
+
"cause": "The dialog exists before any bar runs, so every input has to be reachable without executing the script. An input inside a block would appear or vanish depending on the data, and a saved setting would have nothing to attach to.",
|
|
1242
|
+
"fix": "Move the input() to the top level and use the name it assigns inside the block.",
|
|
1243
|
+
"autofix": false,
|
|
1244
|
+
"example": {
|
|
1245
|
+
"before": "if useBand\n len = input(20, \"Length\")",
|
|
1246
|
+
"after": "len = input(20, \"Length\")\nif useBand\n b = sma(close, len)"
|
|
1247
|
+
},
|
|
1248
|
+
"spec": "language.md 13.4",
|
|
1249
|
+
"refines": null,
|
|
1250
|
+
"test": "tests/errors/OS3007"
|
|
1251
|
+
},
|
|
1252
|
+
{
|
|
1253
|
+
"code": "OS3008",
|
|
1254
|
+
"title": "The value is not valid for this parameter",
|
|
1255
|
+
"severity": "error",
|
|
1256
|
+
"stage": "check",
|
|
1257
|
+
"since": 1,
|
|
1258
|
+
"message": "{argument} accepts {values}; {found} is not one of them.",
|
|
1259
|
+
"placeholders": {
|
|
1260
|
+
"argument": "the parameter, option or setting key",
|
|
1261
|
+
"values": "the accepted values or the accepted form, written out",
|
|
1262
|
+
"found": "the value that was written or that the host supplied",
|
|
1263
|
+
"suggestion": "the accepted value closest to what was written"
|
|
1264
|
+
},
|
|
1265
|
+
"cause": "Some parameters take a value out of a fixed set, or in a fixed written form, rather than free text, because each one selects a different rule in the engine or the report. Two cases arrive here: a name that is not in the set, and a structured string that does not parse, a session window for instance. An unknown name has no rule to select, and defaulting quietly would change what the script does without saying so. The same failure in a setting the host supplied is OS6019, because there the value came from outside the source.",
|
|
1266
|
+
"fix": "Use one of {values}; {suggestion} is the closest to what was written.",
|
|
1267
|
+
"autofix": false,
|
|
1268
|
+
"example": {
|
|
1269
|
+
"before": "strategy(\"Breakout\", qtyType = \"shares\")",
|
|
1270
|
+
"after": "strategy(\"Breakout\", qtyType = \"units\")"
|
|
1271
|
+
},
|
|
1272
|
+
"spec": "language.md 13.3",
|
|
1273
|
+
"refines": null,
|
|
1274
|
+
"test": "tests/errors/OS3008"
|
|
1275
|
+
},
|
|
1276
|
+
{
|
|
1277
|
+
"code": "OS3009",
|
|
1278
|
+
"title": "This option needs another option to be set",
|
|
1279
|
+
"severity": "error",
|
|
1280
|
+
"stage": "check",
|
|
1281
|
+
"since": 1,
|
|
1282
|
+
"message": "{option} = {value} requires {required}.",
|
|
1283
|
+
"placeholders": {
|
|
1284
|
+
"option": "the option that was set",
|
|
1285
|
+
"value": "the value it was set to",
|
|
1286
|
+
"required": "the option and value it depends on"
|
|
1287
|
+
},
|
|
1288
|
+
"cause": "A few options only mean something in combination. An alert that fires on every execution of a bar only has executions to fire on in a file that acts on unconfirmed bars. Accepting the option on its own would leave it doing nothing, silently, which is the worst of the three possible behaviours.",
|
|
1289
|
+
"fix": "Set {required} in the declaration, or choose a value of {option} that stands on its own.",
|
|
1290
|
+
"autofix": false,
|
|
1291
|
+
"example": {
|
|
1292
|
+
"before": "study(\"Ticks\")\n\nif close > open\n alert(\"Up\", frequency = \"everyUpdate\")",
|
|
1293
|
+
"after": "study(\"Ticks\", onUnconfirmed = true)\n\nif close > open\n alert(\"Up\", frequency = \"everyUpdate\")"
|
|
1294
|
+
},
|
|
1295
|
+
"spec": "language.md 7.5, 13.2",
|
|
1296
|
+
"refines": null,
|
|
1297
|
+
"test": "tests/errors/OS3009"
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
"code": "OS3010",
|
|
1301
|
+
"title": "Two arguments that cannot both be given",
|
|
1302
|
+
"severity": "error",
|
|
1303
|
+
"stage": "check",
|
|
1304
|
+
"since": 1,
|
|
1305
|
+
"message": "{first} and {second} set the same thing two ways.",
|
|
1306
|
+
"placeholders": {
|
|
1307
|
+
"first": "the first of the two arguments",
|
|
1308
|
+
"second": "the second of the two arguments"
|
|
1309
|
+
},
|
|
1310
|
+
"cause": "Two arguments that set one thing have to be reconciled, and every rule for reconciling them surprises somebody, so the call is refused and the script says which it meant. There are three pairs. An exit level can be written as an absolute price or as a distance from the entry, and the candidate rules (the nearer one, the later one, the absolute one) each surprise someone. A band takes color for both of its sides, or colorUp and colorDown for the leading and the lagging side, and color together with either of those leaves no answer for which side wins. The third is a relative leg described as a future and given a right or a strike offset, which are fields of an option.",
|
|
1311
|
+
"fix": "Keep one of the two: an absolute price or a distance from the entry, one colour for the whole band or a colour for each side, and on a leg described as a future neither right nor strikeOffset.",
|
|
1312
|
+
"autofix": false,
|
|
1313
|
+
"example": {
|
|
1314
|
+
"before": "exit(limit = 105, profit = 5)",
|
|
1315
|
+
"after": "exit(limit = 105)"
|
|
1316
|
+
},
|
|
1317
|
+
"spec": "language.md 13.3, 15.3",
|
|
1318
|
+
"refines": null,
|
|
1319
|
+
"test": "tests/errors/OS3010"
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
"code": "OS3011",
|
|
1323
|
+
"title": "Argument has the wrong type",
|
|
1324
|
+
"severity": "error",
|
|
1325
|
+
"stage": "check",
|
|
1326
|
+
"since": 1,
|
|
1327
|
+
"message": "{name}'s {argument} is {expected}; {found} was given.",
|
|
1328
|
+
"placeholders": {
|
|
1329
|
+
"name": "the function being called",
|
|
1330
|
+
"argument": "the parameter name",
|
|
1331
|
+
"expected": "the declared parameter type",
|
|
1332
|
+
"found": "the type of the expression at the call site"
|
|
1333
|
+
},
|
|
1334
|
+
"cause": "Arguments are checked against the declared parameter types, with broadcast as the only widening: a plain value may be passed where a series is expected, and it is read as that same value on every bar. Nothing else converts on its own. A declaration handle passed to an argument that does not take one arrives here too; where that argument wanted a runtime object the case has its own code, OS3019, and fill's two plot arguments have OS3020.",
|
|
1335
|
+
"fix": "Convert the value with text(), number() or bool(), or pass an expression of type {expected}.",
|
|
1336
|
+
"autofix": false,
|
|
1337
|
+
"example": {
|
|
1338
|
+
"before": "e = ema(close, \"9\")",
|
|
1339
|
+
"after": "e = ema(close, 9)"
|
|
1340
|
+
},
|
|
1341
|
+
"spec": "language.md 5.2, 5.3",
|
|
1342
|
+
"refines": null,
|
|
1343
|
+
"test": "tests/errors/OS3011"
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
"code": "OS3012",
|
|
1347
|
+
"title": "A required argument is missing",
|
|
1348
|
+
"severity": "error",
|
|
1349
|
+
"stage": "check",
|
|
1350
|
+
"since": 1,
|
|
1351
|
+
"message": "{name} requires {argument}, which has no default.",
|
|
1352
|
+
"placeholders": {
|
|
1353
|
+
"name": "the function being called",
|
|
1354
|
+
"argument": "the parameter that was left out",
|
|
1355
|
+
"example": "a filled-in call showing the argument in place"
|
|
1356
|
+
},
|
|
1357
|
+
"cause": "A parameter without a default has no value the engine could invent. The title of a study and the source of an indicator are the common cases, and an order's leg argument in a file that declares more than one leg is a third: with several contracts to choose from there is none the engine could pick.",
|
|
1358
|
+
"fix": "Pass {argument} at the call site, as the example shows: {example}.",
|
|
1359
|
+
"autofix": false,
|
|
1360
|
+
"example": {
|
|
1361
|
+
"before": "study(overlay = true)",
|
|
1362
|
+
"after": "study(\"Range breakout\", overlay = true)"
|
|
1363
|
+
},
|
|
1364
|
+
"spec": "language.md 11.2, 13.2",
|
|
1365
|
+
"refines": null,
|
|
1366
|
+
"test": "tests/errors/OS3012"
|
|
1367
|
+
},
|
|
1368
|
+
{
|
|
1369
|
+
"code": "OS3013",
|
|
1370
|
+
"title": "Argument given twice",
|
|
1371
|
+
"severity": "error",
|
|
1372
|
+
"stage": "check",
|
|
1373
|
+
"since": 1,
|
|
1374
|
+
"message": "{argument} is given twice at this call site.",
|
|
1375
|
+
"placeholders": {
|
|
1376
|
+
"argument": "the parameter that received two values"
|
|
1377
|
+
},
|
|
1378
|
+
"cause": "A named argument may not repeat one already filled by position, and may not appear twice. Either would leave the reader guessing which value wins.",
|
|
1379
|
+
"fix": "Delete one of the two, keeping the value that was meant.",
|
|
1380
|
+
"autofix": false,
|
|
1381
|
+
"example": {
|
|
1382
|
+
"before": "b = band(close, 50, len = 20)",
|
|
1383
|
+
"after": "b = band(close, len = 20)"
|
|
1384
|
+
},
|
|
1385
|
+
"spec": "language.md 11.2",
|
|
1386
|
+
"refines": null,
|
|
1387
|
+
"test": "tests/errors/OS3013"
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
"code": "OS3014",
|
|
1391
|
+
"title": "limits() is in the wrong place",
|
|
1392
|
+
"severity": "error",
|
|
1393
|
+
"stage": "check",
|
|
1394
|
+
"since": 1,
|
|
1395
|
+
"message": "limits() appears at most once, immediately after the declaration; this one is at line {line}.",
|
|
1396
|
+
"placeholders": {
|
|
1397
|
+
"line": "the line the limits() call was found on"
|
|
1398
|
+
},
|
|
1399
|
+
"cause": "The budgets apply to the whole run and are read before the first bar. One fixed position means a reader can see a script's budgets without searching the file, and two calls would need a rule for which one wins.",
|
|
1400
|
+
"fix": "Move the limits() line directly under study(...) or strategy(...), and merge two calls into one.",
|
|
1401
|
+
"autofix": false,
|
|
1402
|
+
"example": {
|
|
1403
|
+
"before": "study(\"Heavy\")\n\nlen = input(20, \"Length\")\nlimits(loops = 50_000_000)",
|
|
1404
|
+
"after": "study(\"Heavy\")\nlimits(loops = 50_000_000)\n\nlen = input(20, \"Length\")"
|
|
1405
|
+
},
|
|
1406
|
+
"spec": "language.md 10.7",
|
|
1407
|
+
"refines": null,
|
|
1408
|
+
"test": "tests/errors/OS3014"
|
|
1409
|
+
},
|
|
1410
|
+
{
|
|
1411
|
+
"code": "OS3015",
|
|
1412
|
+
"title": "limits() takes literal numbers",
|
|
1413
|
+
"severity": "error",
|
|
1414
|
+
"stage": "check",
|
|
1415
|
+
"since": 1,
|
|
1416
|
+
"message": "limits() is read before the first bar, so {option} must be a literal number.",
|
|
1417
|
+
"placeholders": {
|
|
1418
|
+
"option": "the limits option that was given a computed value"
|
|
1419
|
+
},
|
|
1420
|
+
"cause": "The engine allocates against these budgets before it runs anything, so there is no bar on which a computed budget could be evaluated. An input() is not accepted here either, because a budget that a settings dialog can change is a budget a reader cannot see.",
|
|
1421
|
+
"fix": "Write the number: limits(loops = 50_000_000).",
|
|
1422
|
+
"autofix": false,
|
|
1423
|
+
"example": {
|
|
1424
|
+
"before": "limits(loops = maxBars * 1000)",
|
|
1425
|
+
"after": "limits(loops = 50_000_000)"
|
|
1426
|
+
},
|
|
1427
|
+
"spec": "language.md 10.7",
|
|
1428
|
+
"refines": null,
|
|
1429
|
+
"test": "tests/errors/OS3015"
|
|
1430
|
+
},
|
|
1431
|
+
{
|
|
1432
|
+
"code": "OS3016",
|
|
1433
|
+
"title": "range must be a low and a high",
|
|
1434
|
+
"severity": "error",
|
|
1435
|
+
"stage": "check",
|
|
1436
|
+
"since": 1,
|
|
1437
|
+
"message": "range is [{low}, {high}]; it takes two numbers and the first must be below the second.",
|
|
1438
|
+
"placeholders": {
|
|
1439
|
+
"low": "the first element as written",
|
|
1440
|
+
"high": "the second element as written"
|
|
1441
|
+
},
|
|
1442
|
+
"cause": "The range option fixes the study pane's scale, so it is exactly two numbers in axis order. A reversed or single-element range has no scale to build.",
|
|
1443
|
+
"fix": "Write two numbers, lowest first: range = [0, 100].",
|
|
1444
|
+
"autofix": false,
|
|
1445
|
+
"example": {
|
|
1446
|
+
"before": "study(\"RSI\", range = [100, 0])",
|
|
1447
|
+
"after": "study(\"RSI\", range = [0, 100])"
|
|
1448
|
+
},
|
|
1449
|
+
"spec": "language.md 13.2",
|
|
1450
|
+
"refines": null,
|
|
1451
|
+
"test": "tests/errors/OS3016"
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
"code": "OS3017",
|
|
1455
|
+
"title": "Two of these share a name",
|
|
1456
|
+
"severity": "error",
|
|
1457
|
+
"stage": "check",
|
|
1458
|
+
"since": 1,
|
|
1459
|
+
"message": "{kind} names must be unique in a file; {name} is also used at line {line}.",
|
|
1460
|
+
"placeholders": {
|
|
1461
|
+
"kind": "the thing being named: plot, plotCandles, level, input, table or leg",
|
|
1462
|
+
"name": "the repeated name, quoted",
|
|
1463
|
+
"line": "the line of the first use of that name"
|
|
1464
|
+
},
|
|
1465
|
+
"cause": "The legend row, the settings dialog and the saved layout all key a column by its name, and an alert message names it. Two columns with one name would overwrite each other's saved settings. fill is not on the list: a band has no name of its own and is identified by the two plots it is drawn between. A leg's name is what every later call keys on, so two legs with one name leave every leg call with no answer.",
|
|
1466
|
+
"fix": "Rename one of them so each name appears once.",
|
|
1467
|
+
"autofix": false,
|
|
1468
|
+
"example": {
|
|
1469
|
+
"before": "plot(fast, \"EMA\", aqua)\nplot(slow, \"EMA\", orange)",
|
|
1470
|
+
"after": "plot(fast, \"EMA fast\", aqua)\nplot(slow, \"EMA slow\", orange)"
|
|
1471
|
+
},
|
|
1472
|
+
"spec": "language.md 15.3",
|
|
1473
|
+
"refines": null,
|
|
1474
|
+
"test": "tests/errors/OS3017"
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
"code": "OS3018",
|
|
1478
|
+
"title": "The default is not in the options list",
|
|
1479
|
+
"severity": "error",
|
|
1480
|
+
"stage": "check",
|
|
1481
|
+
"since": 1,
|
|
1482
|
+
"message": "This input's default {default} is not one of options {values}.",
|
|
1483
|
+
"placeholders": {
|
|
1484
|
+
"default": "the default value as written",
|
|
1485
|
+
"values": "the options list as written"
|
|
1486
|
+
},
|
|
1487
|
+
"cause": "An input with options is a dropdown, and its default is the row that is selected when the dialog opens. A default outside the list leaves the dialog with nothing selected and the script with a value the user cannot reproduce.",
|
|
1488
|
+
"fix": "Add {default} to options, or make the default one of the listed values.",
|
|
1489
|
+
"autofix": false,
|
|
1490
|
+
"example": {
|
|
1491
|
+
"before": "mode = input(\"medium\", \"Mode\", options = [\"fast\", \"slow\"])",
|
|
1492
|
+
"after": "mode = input(\"fast\", \"Mode\", options = [\"fast\", \"slow\"])"
|
|
1493
|
+
},
|
|
1494
|
+
"spec": "language.md 13.4",
|
|
1495
|
+
"refines": null,
|
|
1496
|
+
"test": "tests/errors/OS3018"
|
|
1497
|
+
},
|
|
1498
|
+
{
|
|
1499
|
+
"code": "OS3019",
|
|
1500
|
+
"title": "A declaration handle in an object argument",
|
|
1501
|
+
"severity": "error",
|
|
1502
|
+
"stage": "check",
|
|
1503
|
+
"since": 1,
|
|
1504
|
+
"message": "{name}'s {argument} is {expected}; {found} is a declaration handle, which has no value at run time.",
|
|
1505
|
+
"placeholders": {
|
|
1506
|
+
"name": "the function being called",
|
|
1507
|
+
"argument": "the parameter name",
|
|
1508
|
+
"expected": "the object type the parameter takes",
|
|
1509
|
+
"found": "the handle type at the call site: plot, fill or level"
|
|
1510
|
+
},
|
|
1511
|
+
"cause": "plot(), plotCandles(), fill() and level() return a declaration handle: the compile-time half of a declaration, an entry in the program's outputs, with no run-time representation at all. The draw setters, cell() and clear() take a runtime object, which is a value the script made on a bar and can keep, move and delete. The two kinds are separated in the type system for exactly this reason, and fill() is the one call in version 1 that takes a handle.",
|
|
1512
|
+
"fix": "Pass an object the script created with draw.line(), draw.box() or draw.label(), and change a plot's own appearance through the arguments of the plot call instead.",
|
|
1513
|
+
"autofix": false,
|
|
1514
|
+
"example": {
|
|
1515
|
+
"before": "upper = plot(basis + dev, \"Upper\", aqua)\ndraw.setColor(upper, red)",
|
|
1516
|
+
"after": "plot(basis + dev, \"Upper\", color = close > basis ? lime : red)"
|
|
1517
|
+
},
|
|
1518
|
+
"spec": "language.md 5.4, 15.3",
|
|
1519
|
+
"refines": "OS3011",
|
|
1520
|
+
"test": "tests/errors/OS3019"
|
|
1521
|
+
},
|
|
1522
|
+
{
|
|
1523
|
+
"code": "OS3020",
|
|
1524
|
+
"title": "fill needs two declared plots",
|
|
1525
|
+
"severity": "error",
|
|
1526
|
+
"stage": "check",
|
|
1527
|
+
"since": 1,
|
|
1528
|
+
"message": "fill's {argument} is {found}; it takes a plot declared by plot() or plotCandles().",
|
|
1529
|
+
"placeholders": {
|
|
1530
|
+
"argument": "the parameter that was given the wrong thing, plotA or plotB",
|
|
1531
|
+
"found": "the type of the expression at the call site"
|
|
1532
|
+
},
|
|
1533
|
+
"cause": "A band is one entry of the chart contract's shaded bands, and that entry holds the two plot keys the band is drawn between (compiled-program.md 2.8). A bare expression has no key, because no column was ever declared for it, so there is nothing in the contract for it to compile into. Naming the two edges is what makes a band readable as well: it is drawn between two columns the chart already shows, rather than instead of them.",
|
|
1534
|
+
"fix": "Plot both edges at the top level, name each one, and pass the two names to fill().",
|
|
1535
|
+
"autofix": false,
|
|
1536
|
+
"example": {
|
|
1537
|
+
"before": "fill(basis + dev, basis - dev, color = fade(aqua, 88))",
|
|
1538
|
+
"after": "upper = plot(basis + dev, \"Upper\", aqua)\nlower = plot(basis - dev, \"Lower\", aqua)\nfill(upper, lower, color = fade(aqua, 88))"
|
|
1539
|
+
},
|
|
1540
|
+
"spec": "language.md 5.4, 15.3",
|
|
1541
|
+
"refines": "OS3011",
|
|
1542
|
+
"test": "tests/errors/OS3020"
|
|
1543
|
+
},
|
|
1544
|
+
{
|
|
1545
|
+
"code": "OS4001",
|
|
1546
|
+
"title": "History index is not usable",
|
|
1547
|
+
"severity": "error",
|
|
1548
|
+
"stage": "runtime",
|
|
1549
|
+
"since": 1,
|
|
1550
|
+
"message": "[{index}] is not a whole number of bars at or above zero.",
|
|
1551
|
+
"placeholders": {
|
|
1552
|
+
"index": "the value the index expression produced on this bar"
|
|
1553
|
+
},
|
|
1554
|
+
"cause": "A history index counts bars back, so it is a whole number and it is never negative: reading the future is not available at any price. A negative literal is caught at compile time as OS3004; this is the computed case.",
|
|
1555
|
+
"fix": "Wrap the index in floor() or round(), and clamp a computed index with max(0, n).",
|
|
1556
|
+
"autofix": false,
|
|
1557
|
+
"example": {
|
|
1558
|
+
"before": "prev = close[len / 2]",
|
|
1559
|
+
"after": "prev = close[floor(len / 2)]"
|
|
1560
|
+
},
|
|
1561
|
+
"spec": "language.md 7.4",
|
|
1562
|
+
"refines": null,
|
|
1563
|
+
"test": "tests/errors/OS4001"
|
|
1564
|
+
},
|
|
1565
|
+
{
|
|
1566
|
+
"code": "OS4002",
|
|
1567
|
+
"title": "History index is deeper than the retained depth",
|
|
1568
|
+
"severity": "error",
|
|
1569
|
+
"stage": "runtime",
|
|
1570
|
+
"since": 1,
|
|
1571
|
+
"message": "[{index}] reaches past the retained depth of {depth} bars.",
|
|
1572
|
+
"placeholders": {
|
|
1573
|
+
"index": "the index that was requested",
|
|
1574
|
+
"depth": "the depth the host or the script set",
|
|
1575
|
+
"suggested": "an adequate depth for this script, being the deepest index it uses"
|
|
1576
|
+
},
|
|
1577
|
+
"cause": "The value existed and the engine discarded it, which is a different situation from a value that never existed. Conflating the two would hide a real bug behind a plausible gap, so this is an error while a read past the start of history is simply absent.",
|
|
1578
|
+
"fix": "Raise the depth in one place: limits(history = {suggested}).",
|
|
1579
|
+
"autofix": false,
|
|
1580
|
+
"example": {
|
|
1581
|
+
"before": "study(\"Long lookback\")\n\nold = close[5000]",
|
|
1582
|
+
"after": "study(\"Long lookback\")\nlimits(history = 5001)\n\nold = close[5000]"
|
|
1583
|
+
},
|
|
1584
|
+
"spec": "language.md 7.4, 10.7",
|
|
1585
|
+
"refines": null,
|
|
1586
|
+
"test": "tests/errors/OS4002"
|
|
1587
|
+
},
|
|
1588
|
+
{
|
|
1589
|
+
"code": "OS4003",
|
|
1590
|
+
"title": "A whole number was required here",
|
|
1591
|
+
"severity": "error",
|
|
1592
|
+
"stage": "runtime",
|
|
1593
|
+
"since": 1,
|
|
1594
|
+
"message": "{name}'s {argument} was {found} on this bar; a whole number was required.",
|
|
1595
|
+
"placeholders": {
|
|
1596
|
+
"name": "the function being called",
|
|
1597
|
+
"argument": "the parameter name",
|
|
1598
|
+
"found": "the value that reached the call"
|
|
1599
|
+
},
|
|
1600
|
+
"cause": "An index, a digit count and a repeat count are whole numbers. Where the value is a literal the checker catches it as OS3004; where it is computed it arrives here, on the bar that produced it.",
|
|
1601
|
+
"fix": "Round the value before passing it: floor() towards zero, round() to nearest.",
|
|
1602
|
+
"autofix": false,
|
|
1603
|
+
"example": {
|
|
1604
|
+
"before": "mid = element(values, size(values) / 2)",
|
|
1605
|
+
"after": "mid = element(values, floor(size(values) / 2))"
|
|
1606
|
+
},
|
|
1607
|
+
"spec": "language.md 14.1",
|
|
1608
|
+
"refines": null,
|
|
1609
|
+
"test": "tests/errors/OS4003"
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
"code": "OS4004",
|
|
1613
|
+
"title": "Array index out of range",
|
|
1614
|
+
"severity": "error",
|
|
1615
|
+
"stage": "runtime",
|
|
1616
|
+
"since": 1,
|
|
1617
|
+
"message": "Index {index} is outside {name}, which holds {size} elements.",
|
|
1618
|
+
"placeholders": {
|
|
1619
|
+
"index": "the index that was requested",
|
|
1620
|
+
"name": "the array's name",
|
|
1621
|
+
"size": "the array's element count on this bar"
|
|
1622
|
+
},
|
|
1623
|
+
"cause": "An array has an extent the script chose, so an index outside it is a mistake rather than a missing measurement. This is deliberately the opposite of the history operator, where reading past the start of history is absence.",
|
|
1624
|
+
"fix": "Guard the read with size({name}), and index from size({name}) - 1 for the last element.",
|
|
1625
|
+
"autofix": false,
|
|
1626
|
+
"example": {
|
|
1627
|
+
"before": "last = values[10]",
|
|
1628
|
+
"after": "last = size(values) > 10 ? values[10] : none"
|
|
1629
|
+
},
|
|
1630
|
+
"spec": "language.md 14.1",
|
|
1631
|
+
"refines": null,
|
|
1632
|
+
"test": "tests/errors/OS4004"
|
|
1633
|
+
},
|
|
1634
|
+
{
|
|
1635
|
+
"code": "OS4005",
|
|
1636
|
+
"title": "The drawing object no longer exists",
|
|
1637
|
+
"severity": "error",
|
|
1638
|
+
"stage": "runtime",
|
|
1639
|
+
"since": 1,
|
|
1640
|
+
"message": "This {kind} was deleted on bar {bar} and cannot be changed.",
|
|
1641
|
+
"placeholders": {
|
|
1642
|
+
"kind": "the object kind: line, label, box or polyline",
|
|
1643
|
+
"bar": "the bar index the object was deleted on"
|
|
1644
|
+
},
|
|
1645
|
+
"cause": "An object lives from the bar that created it until the bar that deletes it, and nothing else ends its life: dropping the last name that refers to one leaves it on the chart, because there is no collection of unreachable objects. This error is the other side of that rule. A name kept in a var outlives the object it was given, so a setter reaching a deleted object is a script that has lost track of its own state and will go on doing so. A table never arrives here, because a table is never deleted: clear(t) empties it and the grid lives as long as the study. OS8019 warns about the shape that leads here, at compile time.",
|
|
1646
|
+
"fix": "Assign none to the name on the same path as the delete, and test isNone() on it before changing the object.",
|
|
1647
|
+
"autofix": false,
|
|
1648
|
+
"example": {
|
|
1649
|
+
"before": "var top = none\nif isNone(top)\n top = draw.line(time, low, time, high)\nif close < open\n draw.delete(top)\ndraw.setTo(top, time, high)",
|
|
1650
|
+
"after": "var top = none\nif isNone(top)\n top = draw.line(time, low, time, high)\nif close < open\n draw.delete(top)\n top = none\nif not isNone(top)\n draw.setTo(top, time, high)"
|
|
1651
|
+
},
|
|
1652
|
+
"spec": "language.md 5.4",
|
|
1653
|
+
"refines": null,
|
|
1654
|
+
"test": "tests/errors/OS4005"
|
|
1655
|
+
},
|
|
1656
|
+
{
|
|
1657
|
+
"code": "OS4006",
|
|
1658
|
+
"title": "The array is empty",
|
|
1659
|
+
"severity": "error",
|
|
1660
|
+
"stage": "runtime",
|
|
1661
|
+
"since": 1,
|
|
1662
|
+
"message": "{name} cannot take an element from an empty array.",
|
|
1663
|
+
"placeholders": {
|
|
1664
|
+
"name": "the call, one of pop, shift, min, max or avg"
|
|
1665
|
+
},
|
|
1666
|
+
"cause": "Removing or summarising an element of an empty array has no answer. Returning absence would let a script drain an array without noticing, which is the bug this catches.",
|
|
1667
|
+
"fix": "Test size(arr) > 0 before the call.",
|
|
1668
|
+
"autofix": false,
|
|
1669
|
+
"example": {
|
|
1670
|
+
"before": "oldest = shift(window)",
|
|
1671
|
+
"after": "oldest = size(window) > 0 ? shift(window) : none"
|
|
1672
|
+
},
|
|
1673
|
+
"spec": "language.md 14.1",
|
|
1674
|
+
"refines": null,
|
|
1675
|
+
"test": "tests/errors/OS4006"
|
|
1676
|
+
},
|
|
1677
|
+
{
|
|
1678
|
+
"code": "OS4007",
|
|
1679
|
+
"title": "Slice range is invalid",
|
|
1680
|
+
"severity": "error",
|
|
1681
|
+
"stage": "runtime",
|
|
1682
|
+
"since": 1,
|
|
1683
|
+
"message": "slice({from}, {to}) is not a range inside an array of {size} elements.",
|
|
1684
|
+
"placeholders": {
|
|
1685
|
+
"from": "the start index, inclusive",
|
|
1686
|
+
"to": "the end index, exclusive",
|
|
1687
|
+
"size": "the array's element count on this bar"
|
|
1688
|
+
},
|
|
1689
|
+
"cause": "A slice takes from inclusive and to exclusive, so a valid range satisfies 0 <= from <= to <= size. A reversed range is not read backwards, because a slice that silently reverses is a slice nobody can read.",
|
|
1690
|
+
"fix": "Clamp the bounds: from = max(0, from) and to = min(size(arr), to), with from at or below to.",
|
|
1691
|
+
"autofix": false,
|
|
1692
|
+
"example": {
|
|
1693
|
+
"before": "tail = slice(values, size(values), 0)",
|
|
1694
|
+
"after": "tail = slice(values, max(0, size(values) - 10), size(values))"
|
|
1695
|
+
},
|
|
1696
|
+
"spec": "language.md 14.1",
|
|
1697
|
+
"refines": null,
|
|
1698
|
+
"test": "tests/errors/OS4007"
|
|
1699
|
+
},
|
|
1700
|
+
{
|
|
1701
|
+
"code": "OS4008",
|
|
1702
|
+
"title": "Table cell is outside the table",
|
|
1703
|
+
"severity": "error",
|
|
1704
|
+
"stage": "runtime",
|
|
1705
|
+
"since": 1,
|
|
1706
|
+
"message": "Cell ({row}, {column}) is outside a table of {rows} rows and {columns} columns.",
|
|
1707
|
+
"placeholders": {
|
|
1708
|
+
"row": "the row index that was written to",
|
|
1709
|
+
"column": "the column index that was written to",
|
|
1710
|
+
"rows": "the table's declared row count",
|
|
1711
|
+
"columns": "the table's declared column count"
|
|
1712
|
+
},
|
|
1713
|
+
"cause": "A table's shape is declared once, with the study's fixed surface, so a write outside it has no cell to land in. Growing the table on demand would change the layout between bars and would make a saved layout meaningless.",
|
|
1714
|
+
"fix": "Declare the table with the shape the script writes: table(\"Summary\", {rows}, {columns}).",
|
|
1715
|
+
"autofix": false,
|
|
1716
|
+
"example": {
|
|
1717
|
+
"before": "t = table(\"Summary\", 2, 2)\ncell(t, 2, 0, \"Total\")",
|
|
1718
|
+
"after": "t = table(\"Summary\", 3, 2)\ncell(t, 2, 0, \"Total\")"
|
|
1719
|
+
},
|
|
1720
|
+
"spec": "language.md 15.3",
|
|
1721
|
+
"refines": null,
|
|
1722
|
+
"test": "tests/errors/OS4008"
|
|
1723
|
+
},
|
|
1724
|
+
{
|
|
1725
|
+
"code": "OS4009",
|
|
1726
|
+
"title": "Colour channel is out of range",
|
|
1727
|
+
"severity": "error",
|
|
1728
|
+
"stage": "runtime",
|
|
1729
|
+
"since": 1,
|
|
1730
|
+
"message": "{name}'s {argument} is {found}; channels run 0 to 255 and alpha runs 0 to 1.",
|
|
1731
|
+
"placeholders": {
|
|
1732
|
+
"name": "the colour function: rgb, rgba or fade",
|
|
1733
|
+
"argument": "the channel that was out of range",
|
|
1734
|
+
"found": "the value that reached the call"
|
|
1735
|
+
},
|
|
1736
|
+
"cause": "A colour channel outside its range has no rendering, and clamping silently would make a study whose colours are a computation look right at the edges and wrong in between.",
|
|
1737
|
+
"fix": "Clamp the value where it is computed: rgb(min(255, max(0, r)), g, b).",
|
|
1738
|
+
"autofix": false,
|
|
1739
|
+
"example": {
|
|
1740
|
+
"before": "tint = rgb(255 * strength, 0, 0)",
|
|
1741
|
+
"after": "tint = rgb(min(255, max(0, 255 * strength)), 0, 0)"
|
|
1742
|
+
},
|
|
1743
|
+
"spec": "language.md 3.8",
|
|
1744
|
+
"refines": null,
|
|
1745
|
+
"test": "tests/errors/OS4009"
|
|
1746
|
+
},
|
|
1747
|
+
{
|
|
1748
|
+
"code": "OS4010",
|
|
1749
|
+
"title": "Calendar field is out of range",
|
|
1750
|
+
"severity": "error",
|
|
1751
|
+
"stage": "runtime",
|
|
1752
|
+
"since": 1,
|
|
1753
|
+
"message": "{field} is {found}; it runs {range}.",
|
|
1754
|
+
"placeholders": {
|
|
1755
|
+
"field": "the calendar field: year, month, day, hour, minute or second",
|
|
1756
|
+
"found": "the value that reached the call",
|
|
1757
|
+
"range": "the accepted range for that field"
|
|
1758
|
+
},
|
|
1759
|
+
"cause": "A date is built from fields that each have a range, and a month of 13 is a script bug rather than a date. Rolling over into the next year would turn an off-by-one into a silently wrong timestamp, which in a session test is a whole day of wrong signals.",
|
|
1760
|
+
"fix": "Pass a value inside {range}, carrying the overflow into the field above it as the example does.",
|
|
1761
|
+
"autofix": false,
|
|
1762
|
+
"example": {
|
|
1763
|
+
"before": "t = date.from(2026, month + 1, 1)",
|
|
1764
|
+
"after": "t = date.from(2026 + floor(month / 12), mod(month, 12) + 1, 1)"
|
|
1765
|
+
},
|
|
1766
|
+
"spec": "language.md 15.2",
|
|
1767
|
+
"refines": null,
|
|
1768
|
+
"test": "tests/errors/OS4010"
|
|
1769
|
+
},
|
|
1770
|
+
{
|
|
1771
|
+
"code": "OS4011",
|
|
1772
|
+
"title": "String position is outside the string",
|
|
1773
|
+
"severity": "error",
|
|
1774
|
+
"stage": "runtime",
|
|
1775
|
+
"since": 1,
|
|
1776
|
+
"message": "Position {index} is outside a string of {length} characters.",
|
|
1777
|
+
"placeholders": {
|
|
1778
|
+
"index": "the position that was requested",
|
|
1779
|
+
"length": "the string's length in characters"
|
|
1780
|
+
},
|
|
1781
|
+
"cause": "A string position addresses an existing character, counted in Unicode code points. Returning an empty string instead would let a parsing loop run off the end and produce a plausible empty result.",
|
|
1782
|
+
"fix": "Guard with str.length(s), or clamp the position with min() before the call.",
|
|
1783
|
+
"autofix": false,
|
|
1784
|
+
"example": {
|
|
1785
|
+
"before": "c = str.substring(sym, 10, 11)",
|
|
1786
|
+
"after": "c = str.length(sym) > 10 ? str.substring(sym, 10, 11) : \"\""
|
|
1787
|
+
},
|
|
1788
|
+
"spec": "language.md 15.2",
|
|
1789
|
+
"refines": null,
|
|
1790
|
+
"test": "tests/errors/OS4011"
|
|
1791
|
+
},
|
|
1792
|
+
{
|
|
1793
|
+
"code": "OS4012",
|
|
1794
|
+
"title": "That value is not one of the accepted names",
|
|
1795
|
+
"severity": "error",
|
|
1796
|
+
"stage": "runtime",
|
|
1797
|
+
"since": 1,
|
|
1798
|
+
"message": "{argument} accepts {values}; {found} was computed on this bar.",
|
|
1799
|
+
"placeholders": {
|
|
1800
|
+
"argument": "the parameter name",
|
|
1801
|
+
"values": "every accepted value, quoted and comma separated",
|
|
1802
|
+
"found": "the value that reached the call"
|
|
1803
|
+
},
|
|
1804
|
+
"cause": "The compile-time form of this is OS3008. Where the value is computed, the check happens on the bar that produced it, because a name that selects an engine rule cannot be guessed at and cannot be defaulted without changing what the script does.",
|
|
1805
|
+
"fix": "Produce the value from an input() with an options list, so only accepted names can reach the call.",
|
|
1806
|
+
"autofix": false,
|
|
1807
|
+
"example": {
|
|
1808
|
+
"before": "sortOrder = up ? \"ascending\" : \"desc\"\nsort(values, sortOrder)",
|
|
1809
|
+
"after": "sortOrder = up ? \"asc\" : \"desc\"\nsort(values, sortOrder)"
|
|
1810
|
+
},
|
|
1811
|
+
"spec": "language.md 14.1",
|
|
1812
|
+
"refines": null,
|
|
1813
|
+
"test": "tests/errors/OS4012"
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
"code": "OS4013",
|
|
1817
|
+
"title": "A loop bound is absent",
|
|
1818
|
+
"severity": "error",
|
|
1819
|
+
"stage": "runtime",
|
|
1820
|
+
"since": 1,
|
|
1821
|
+
"message": "This loop's {bound} is absent on this bar.",
|
|
1822
|
+
"placeholders": {
|
|
1823
|
+
"bound": "the bound that was absent: start, limit or step"
|
|
1824
|
+
},
|
|
1825
|
+
"cause": "A counted loop needs three numbers before it can begin. Absence propagates through arithmetic and comparison, but a loop cannot propagate it: it either runs or it does not. Treating an absent bound as zero iterations would skip work the script asked for and leave a plot that looks computed, so the bar stops instead.",
|
|
1826
|
+
"fix": "Give the bound a value with orElse(), or guard the loop with isNone() so a warmup bar skips it deliberately.",
|
|
1827
|
+
"autofix": false,
|
|
1828
|
+
"example": {
|
|
1829
|
+
"before": "for i = 0 to lookback\n total += close[i]",
|
|
1830
|
+
"after": "if not isNone(lookback)\n for i = 0 to lookback\n total += close[i]"
|
|
1831
|
+
},
|
|
1832
|
+
"spec": "language.md 10.3",
|
|
1833
|
+
"refines": null,
|
|
1834
|
+
"test": "tests/errors/OS4013"
|
|
1835
|
+
},
|
|
1836
|
+
{
|
|
1837
|
+
"code": "OS5001",
|
|
1838
|
+
"title": "Loop budget exhausted",
|
|
1839
|
+
"severity": "error",
|
|
1840
|
+
"stage": "runtime",
|
|
1841
|
+
"since": 1,
|
|
1842
|
+
"message": "This bar used its {budget} loop iterations, and the loop at line {line} was still running.",
|
|
1843
|
+
"placeholders": {
|
|
1844
|
+
"budget": "the per-bar iteration budget in force",
|
|
1845
|
+
"line": "the line of the loop that was running when the budget ran out",
|
|
1846
|
+
"suggested": "a budget that would have completed this bar, rounded up"
|
|
1847
|
+
},
|
|
1848
|
+
"cause": "Every iteration of every loop counts against one per-bar budget, so a script with one nested loop is treated like a script with ten sequential ones, and the budget resets each bar so a long dataset is not itself a reason to fail. The bar stops rather than breaking out of the loop, because a loop that ran two million times and then stopped produces a plausible wrong number, which is worse than no number.",
|
|
1849
|
+
"fix": "Fix the exit condition, or raise the budget in one line: limits(loops = {suggested}).",
|
|
1850
|
+
"autofix": false,
|
|
1851
|
+
"example": {
|
|
1852
|
+
"before": "while close[i] > close[i + 1]\n total += close[i]",
|
|
1853
|
+
"after": "while i < 500 and close[i] > close[i + 1]\n total += close[i]\n i += 1"
|
|
1854
|
+
},
|
|
1855
|
+
"spec": "language.md 10.7",
|
|
1856
|
+
"refines": null,
|
|
1857
|
+
"test": "tests/errors/OS5001"
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
"code": "OS5002",
|
|
1861
|
+
"title": "The array is too large",
|
|
1862
|
+
"severity": "error",
|
|
1863
|
+
"stage": "runtime",
|
|
1864
|
+
"since": 1,
|
|
1865
|
+
"message": "An array holds at most {max} elements; {name} reached {size}.",
|
|
1866
|
+
"placeholders": {
|
|
1867
|
+
"max": "the element ceiling",
|
|
1868
|
+
"name": "the array's name",
|
|
1869
|
+
"size": "the size the array reached"
|
|
1870
|
+
},
|
|
1871
|
+
"cause": "The ceiling exists so that one script cannot exhaust a browser tab's memory and take the chart with it. It is not raised by limits() in version 1, because an array that large is almost always a window that is never trimmed.",
|
|
1872
|
+
"fix": "Drop the oldest element as you append: if size(arr) > 500, shift(arr).",
|
|
1873
|
+
"autofix": false,
|
|
1874
|
+
"example": {
|
|
1875
|
+
"before": "var window = [0.0]\npush(window, close)",
|
|
1876
|
+
"after": "var window = [0.0]\npush(window, close)\nif size(window) > 500\n shift(window)"
|
|
1877
|
+
},
|
|
1878
|
+
"spec": "language.md 14.1",
|
|
1879
|
+
"refines": null,
|
|
1880
|
+
"test": "tests/errors/OS5002"
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
"code": "OS5003",
|
|
1884
|
+
"title": "The host refused this limits() value",
|
|
1885
|
+
"severity": "error",
|
|
1886
|
+
"stage": "host",
|
|
1887
|
+
"since": 1,
|
|
1888
|
+
"message": "This host allows {option} up to {max}; the file asks for {found}.",
|
|
1889
|
+
"placeholders": {
|
|
1890
|
+
"option": "the limits option",
|
|
1891
|
+
"max": "the host's ceiling",
|
|
1892
|
+
"found": "the value the file asked for"
|
|
1893
|
+
},
|
|
1894
|
+
"cause": "A host that will not run a budget says so instead of quietly capping it, because a script that silently ran under a smaller budget would produce numbers its author never asked for and could not reproduce.",
|
|
1895
|
+
"fix": "Lower {option} to {max} or below, or run the file on a host that allows more.",
|
|
1896
|
+
"autofix": false,
|
|
1897
|
+
"example": {
|
|
1898
|
+
"before": "limits(loops = 500_000_000)",
|
|
1899
|
+
"after": "limits(loops = 50_000_000)"
|
|
1900
|
+
},
|
|
1901
|
+
"spec": "language.md 10.7; host-interface.md 10.1",
|
|
1902
|
+
"refines": null,
|
|
1903
|
+
"test": "tests/errors/OS5003"
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
"code": "OS5004",
|
|
1907
|
+
"title": "The program needs more state regions than the engine allows",
|
|
1908
|
+
"severity": "error",
|
|
1909
|
+
"stage": "check",
|
|
1910
|
+
"since": 1,
|
|
1911
|
+
"message": "This program needs {found} state regions and the engine allows {max}; {first} calls {second} on several paths.",
|
|
1912
|
+
"placeholders": {
|
|
1913
|
+
"found": "the number of state regions the compiler would allocate",
|
|
1914
|
+
"max": "the engine's declared region count",
|
|
1915
|
+
"first": "the outer function in the nesting that caused it",
|
|
1916
|
+
"second": "the function it calls more than once"
|
|
1917
|
+
},
|
|
1918
|
+
"cause": "State is allocated per call path, which is what lets one function body serve many independent pieces of state. The number of paths grows multiplicatively when several functions each call the next more than once. The compiler reports it rather than emitting a program no engine will load.",
|
|
1919
|
+
"fix": "Call the inner function once at the top level, give its result a name, and pass that name down.",
|
|
1920
|
+
"autofix": false,
|
|
1921
|
+
"example": {
|
|
1922
|
+
"before": "fn inner(src) => ema(src, 20)\nfn outer(src) => inner(src) - inner(src[1])\nv = outer(close) + outer(hlc3)",
|
|
1923
|
+
"after": "fn inner(src) => ema(src, 20)\nbase = inner(close)\nv = base - base[1]"
|
|
1924
|
+
},
|
|
1925
|
+
"spec": "compiled-program.md, state regions",
|
|
1926
|
+
"refines": null,
|
|
1927
|
+
"test": "tests/errors/OS5004"
|
|
1928
|
+
},
|
|
1929
|
+
{
|
|
1930
|
+
"code": "OS5005",
|
|
1931
|
+
"title": "Nesting is too deep",
|
|
1932
|
+
"severity": "error",
|
|
1933
|
+
"stage": "parse",
|
|
1934
|
+
"since": 1,
|
|
1935
|
+
"message": "{construct} is nested {found} deep and the ceiling is {max}.",
|
|
1936
|
+
"placeholders": {
|
|
1937
|
+
"construct": "the construct that nested too deeply: an expression, a block or a call",
|
|
1938
|
+
"found": "the nesting depth reached",
|
|
1939
|
+
"max": "the nesting ceiling"
|
|
1940
|
+
},
|
|
1941
|
+
"cause": "The ceiling keeps the parser, the checker and the engine inside a bounded stack, so no input can stop a tab. It applies to source nesting at compile time and to call depth at run time, and it is far above anything a person writes by hand: generated source is the usual way to reach it.",
|
|
1942
|
+
"fix": "Flatten it: give the inner expression a name at the top level and use the name.",
|
|
1943
|
+
"autofix": false,
|
|
1944
|
+
"example": {
|
|
1945
|
+
"before": "z = a ? b ? c ? d ? 1 : 2 : 3 : 4 : 5",
|
|
1946
|
+
"after": "inner = c ? (d ? 1 : 2) : 3\nz = a ? (b ? inner : 4) : 5"
|
|
1947
|
+
},
|
|
1948
|
+
"spec": "language.md 19",
|
|
1949
|
+
"refines": null,
|
|
1950
|
+
"test": "tests/errors/OS5005"
|
|
1951
|
+
},
|
|
1952
|
+
{
|
|
1953
|
+
"code": "OS5006",
|
|
1954
|
+
"title": "Too many outstanding data requests",
|
|
1955
|
+
"severity": "error",
|
|
1956
|
+
"stage": "host",
|
|
1957
|
+
"since": 1,
|
|
1958
|
+
"message": "This file makes {found} data requests and the host allows {max}.",
|
|
1959
|
+
"placeholders": {
|
|
1960
|
+
"found": "the number of distinct requests in the file",
|
|
1961
|
+
"max": "the host's ceiling on concurrent requests"
|
|
1962
|
+
},
|
|
1963
|
+
"cause": "Each higher timeframe or other-instrument read is a separate series the host fetches and keeps in step with the chart. The ceiling is the host's, and it is reported rather than silently dropping the requests past it, because a dropped request is a plot that quietly turns absent.",
|
|
1964
|
+
"fix": "Keep one request per symbol and timeframe, reuse the name it assigns, and delete the requests whose results are unused.",
|
|
1965
|
+
"autofix": false,
|
|
1966
|
+
"example": {
|
|
1967
|
+
"before": "dayHigh = req.timeframe(\"1D\", high)\nprevHigh = req.timeframe(\"1D\", high)[1]\nweekHigh = req.timeframe(\"1W\", high)",
|
|
1968
|
+
"after": "dayHigh = req.timeframe(\"1D\", high)\nprevHigh = dayHigh[1]\nweekHigh = req.timeframe(\"1W\", high)"
|
|
1969
|
+
},
|
|
1970
|
+
"spec": "language.md 15.2",
|
|
1971
|
+
"refines": null,
|
|
1972
|
+
"test": "tests/errors/OS5006"
|
|
1973
|
+
},
|
|
1974
|
+
{
|
|
1975
|
+
"code": "OS5007",
|
|
1976
|
+
"title": "The bar took too long",
|
|
1977
|
+
"severity": "error",
|
|
1978
|
+
"stage": "host",
|
|
1979
|
+
"since": 1,
|
|
1980
|
+
"message": "Bar {bar} ran for {ms} ms and the host allows {max} ms.",
|
|
1981
|
+
"placeholders": {
|
|
1982
|
+
"bar": "the bar index that exceeded the budget",
|
|
1983
|
+
"ms": "the time the bar took",
|
|
1984
|
+
"max": "the host's per-bar time budget"
|
|
1985
|
+
},
|
|
1986
|
+
"cause": "A server running many strategies gives each bar a wall-clock budget, so one script cannot starve the rest. Work that does not change from bar to bar is the usual cause: recomputing over the whole history on every bar turns a linear study into a quadratic one.",
|
|
1987
|
+
"fix": "Keep a running value in a var and update it per bar instead of recomputing over the whole history.",
|
|
1988
|
+
"autofix": false,
|
|
1989
|
+
"example": {
|
|
1990
|
+
"before": "total = 0.0\nfor i = 0 to bar.index\n total += close[i]",
|
|
1991
|
+
"after": "var total = 0.0\ntotal += close"
|
|
1992
|
+
},
|
|
1993
|
+
"spec": "language.md 7.1",
|
|
1994
|
+
"refines": null,
|
|
1995
|
+
"test": "tests/errors/OS5007"
|
|
1996
|
+
},
|
|
1997
|
+
{
|
|
1998
|
+
"code": "OS5008",
|
|
1999
|
+
"title": "The string is too long",
|
|
2000
|
+
"severity": "error",
|
|
2001
|
+
"stage": "runtime",
|
|
2002
|
+
"since": 1,
|
|
2003
|
+
"message": "A string holds at most {max} characters; this one reached {found}.",
|
|
2004
|
+
"placeholders": {
|
|
2005
|
+
"max": "the character ceiling",
|
|
2006
|
+
"found": "the length that was reached"
|
|
2007
|
+
},
|
|
2008
|
+
"cause": "The ceiling catches the one shape that grows without bound by accident: text appended to a persistent string on every bar, which is a log that nothing ever trims.",
|
|
2009
|
+
"fix": "Keep the pieces in an array, trim it to the rows you display, and join only those.",
|
|
2010
|
+
"autofix": false,
|
|
2011
|
+
"example": {
|
|
2012
|
+
"before": "var logLines = \"\"\nlogLines += text(close) + \"\\n\"",
|
|
2013
|
+
"after": "var logLines: array<string> = []\npush(logLines, text(close))\nif size(logLines) > 50\n shift(logLines)"
|
|
2014
|
+
},
|
|
2015
|
+
"spec": "language.md 5.1",
|
|
2016
|
+
"refines": null,
|
|
2017
|
+
"test": "tests/errors/OS5008"
|
|
2018
|
+
},
|
|
2019
|
+
{
|
|
2020
|
+
"code": "OS5009",
|
|
2021
|
+
"title": "The program is too large",
|
|
2022
|
+
"severity": "error",
|
|
2023
|
+
"stage": "check",
|
|
2024
|
+
"since": 1,
|
|
2025
|
+
"message": "This file compiles to {found} instructions and the ceiling is {max}.",
|
|
2026
|
+
"placeholders": {
|
|
2027
|
+
"found": "the instruction count the compiler produced",
|
|
2028
|
+
"max": "the instruction ceiling"
|
|
2029
|
+
},
|
|
2030
|
+
"cause": "The compiled program is held in memory per chart and per running strategy, so it has a size the host is willing to hold. A file at this size is nearly always repeated blocks that a function would collapse.",
|
|
2031
|
+
"fix": "Move the repeated block into a fn and call it, and delete branches the script no longer uses.",
|
|
2032
|
+
"autofix": false,
|
|
2033
|
+
"example": {
|
|
2034
|
+
"before": "a1 = sma(close, 10)\na2 = sma(close, 20)\na3 = sma(close, 30)",
|
|
2035
|
+
"after": "fn avgOf(len) => sma(close, len)\na1 = avgOf(10)\na2 = avgOf(20)\na3 = avgOf(30)"
|
|
2036
|
+
},
|
|
2037
|
+
"spec": "language.md 10.7",
|
|
2038
|
+
"refines": null,
|
|
2039
|
+
"test": "tests/errors/OS5009"
|
|
2040
|
+
},
|
|
2041
|
+
{
|
|
2042
|
+
"code": "OS6001",
|
|
2043
|
+
"title": "Unknown timeframe",
|
|
2044
|
+
"severity": "error",
|
|
2045
|
+
"stage": "check",
|
|
2046
|
+
"since": 1,
|
|
2047
|
+
"message": "{value} is not a timeframe.",
|
|
2048
|
+
"placeholders": {
|
|
2049
|
+
"value": "the string that was passed as a timeframe"
|
|
2050
|
+
},
|
|
2051
|
+
"cause": "A timeframe is a count and a unit, written as a string: m for minutes, h for hours, D for days, W for weeks and M for months. The unit letters are case sensitive, so \"1M\" is one month and \"1m\" is one minute. A bare number is read as minutes, because that is the form an interval input supplies, so \"60\" and \"1h\" are the same timeframe.",
|
|
2052
|
+
"fix": "Write a count and a unit, or a bare number of minutes: \"5m\", \"1h\", \"1D\", \"1W\", \"60\".",
|
|
2053
|
+
"autofix": false,
|
|
2054
|
+
"example": {
|
|
2055
|
+
"before": "d = req.timeframe(\"hourly\", high)",
|
|
2056
|
+
"after": "d = req.timeframe(\"1h\", high)"
|
|
2057
|
+
},
|
|
2058
|
+
"spec": "language.md 15.2",
|
|
2059
|
+
"refines": null,
|
|
2060
|
+
"test": "tests/errors/OS6001"
|
|
2061
|
+
},
|
|
2062
|
+
{
|
|
2063
|
+
"code": "OS6002",
|
|
2064
|
+
"title": "The requested timeframe is lower than the chart's",
|
|
2065
|
+
"severity": "error",
|
|
2066
|
+
"stage": "host",
|
|
2067
|
+
"since": 1,
|
|
2068
|
+
"message": "The chart is {chart} and the request asks for {requested}.",
|
|
2069
|
+
"placeholders": {
|
|
2070
|
+
"chart": "the chart's interval",
|
|
2071
|
+
"requested": "the interval that was requested"
|
|
2072
|
+
},
|
|
2073
|
+
"cause": "Folding a lower timeframe into a higher bar needs data from inside the bar, which the chart was not given. Inventing it is the definition of a repainting study, so the request is refused rather than approximated.",
|
|
2074
|
+
"fix": "Request {chart} or higher, or change the chart's interval to the lower one and fold upwards instead.",
|
|
2075
|
+
"autofix": false,
|
|
2076
|
+
"example": {
|
|
2077
|
+
"before": "m5 = req.timeframe(\"5\", close)",
|
|
2078
|
+
"after": "h1 = req.timeframe(\"60\", close)"
|
|
2079
|
+
},
|
|
2080
|
+
"spec": "language.md 15.2",
|
|
2081
|
+
"refines": null,
|
|
2082
|
+
"test": "tests/errors/OS6002"
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
"code": "OS6003",
|
|
2086
|
+
"title": "A per-bar name inside a request expression",
|
|
2087
|
+
"severity": "error",
|
|
2088
|
+
"stage": "check",
|
|
2089
|
+
"since": 1,
|
|
2090
|
+
"message": "{name} is computed on this chart's bars, so it has no meaning on the requested ones.",
|
|
2091
|
+
"placeholders": {
|
|
2092
|
+
"name": "the file-scope name that was read inside the expression"
|
|
2093
|
+
},
|
|
2094
|
+
"cause": "The expression passed to a request is evaluated on the requested instrument's bars, in its own time. A name from this file may be read there only when it is a compile-time constant: a literal, arithmetic over literals, or an input(). A value computed on this chart's bars has no counterpart on the requested ones, and there is no honest answer for what it would mean there.",
|
|
2095
|
+
"fix": "Move the calculation inside the request expression, or pass a constant: a literal or an input().",
|
|
2096
|
+
"autofix": false,
|
|
2097
|
+
"example": {
|
|
2098
|
+
"before": "m = sma(close, 20)\nd = req.timeframe(\"1D\", close > m)",
|
|
2099
|
+
"after": "d = req.timeframe(\"1D\", close > sma(close, 20))"
|
|
2100
|
+
},
|
|
2101
|
+
"spec": "language.md 15.2",
|
|
2102
|
+
"refines": null,
|
|
2103
|
+
"test": "tests/errors/OS6003"
|
|
2104
|
+
},
|
|
2105
|
+
{
|
|
2106
|
+
"code": "OS6004",
|
|
2107
|
+
"title": "The library manifest disagrees with the program",
|
|
2108
|
+
"severity": "error",
|
|
2109
|
+
"stage": "host",
|
|
2110
|
+
"since": 1,
|
|
2111
|
+
"message": "Entry {index} of the program names {name} with {arity} arguments; this engine's manifest has {manifest}.",
|
|
2112
|
+
"placeholders": {
|
|
2113
|
+
"index": "the entry's index in the program's function table",
|
|
2114
|
+
"name": "the function name the program carries",
|
|
2115
|
+
"arity": "the argument count the program carries",
|
|
2116
|
+
"manifest": "what the engine's manifest says about that name"
|
|
2117
|
+
},
|
|
2118
|
+
"cause": "A compiled program carries its own function table, and the engine checks every entry against its manifest at load: the name must exist, the argument count must match, and the state and effect facts must agree. The entries carry facts the engine already knows precisely so that they can be disagreed with, which catches a program compiled against a different library before it computes a single wrong number.",
|
|
2119
|
+
"fix": "Recompile the script against this engine's library, or run the program on an engine with the library version it was compiled against.",
|
|
2120
|
+
"autofix": false,
|
|
2121
|
+
"example": {
|
|
2122
|
+
"before": "program: library 5, entry 12 name \"stdev\" arity 3\nengine: library 4, entry \"stdev\" arity 2",
|
|
2123
|
+
"after": "program: library 4, entry 12 name \"stdev\" arity 2\nengine: library 4, entry \"stdev\" arity 2"
|
|
2124
|
+
},
|
|
2125
|
+
"spec": "compiled-program.md, the function table",
|
|
2126
|
+
"refines": null,
|
|
2127
|
+
"test": "tests/errors/OS6004"
|
|
2128
|
+
},
|
|
2129
|
+
{
|
|
2130
|
+
"code": "OS6005",
|
|
2131
|
+
"title": "Unknown timezone",
|
|
2132
|
+
"severity": "error",
|
|
2133
|
+
"stage": "host",
|
|
2134
|
+
"since": 1,
|
|
2135
|
+
"message": "{value} is not a timezone this host knows.",
|
|
2136
|
+
"placeholders": {
|
|
2137
|
+
"value": "the timezone name that was given"
|
|
2138
|
+
},
|
|
2139
|
+
"cause": "Timezone names come from the host's timezone database, and an abbreviation is not one of them: several abbreviations mean two different offsets in different parts of the world, which is the kind of ambiguity a session test cannot carry.",
|
|
2140
|
+
"fix": "Use a full area and location name from the host's list, or leave the argument out to use the chart's own timezone.",
|
|
2141
|
+
"autofix": false,
|
|
2142
|
+
"example": {
|
|
2143
|
+
"before": "h = date.hour(time, \"IST\")",
|
|
2144
|
+
"after": "h = date.hour(time, \"Asia/Kolkata\")"
|
|
2145
|
+
},
|
|
2146
|
+
"spec": "language.md 15.2",
|
|
2147
|
+
"refines": null,
|
|
2148
|
+
"test": "tests/errors/OS6005"
|
|
2149
|
+
},
|
|
2150
|
+
{
|
|
2151
|
+
"code": "OS6006",
|
|
2152
|
+
"title": "The engine lacks a capability the program requires",
|
|
2153
|
+
"severity": "error",
|
|
2154
|
+
"stage": "host",
|
|
2155
|
+
"since": 1,
|
|
2156
|
+
"message": "This program requires {tag} and this engine does not have it.",
|
|
2157
|
+
"placeholders": {
|
|
2158
|
+
"tag": "the first required capability tag the engine does not have"
|
|
2159
|
+
},
|
|
2160
|
+
"cause": "A compiled program lists the capability tags it needs, and the engine compares the list against its own at load. Tags are the real compatibility mechanism and the format version is the coarse one. Refusing at load is better than meeting an instruction the engine cannot execute halfway through a bar, with half a chart already drawn.",
|
|
2161
|
+
"fix": "Run the program on an engine that has {tag}, or remove the feature that needs it: the tag names it.",
|
|
2162
|
+
"autofix": false,
|
|
2163
|
+
"example": {
|
|
2164
|
+
"before": "program requires: core.1, arrays, orders\nengine has: core.1, arrays",
|
|
2165
|
+
"after": "program requires: core.1, arrays\nengine has: core.1, arrays"
|
|
2166
|
+
},
|
|
2167
|
+
"spec": "compiled-program.md, capability tags",
|
|
2168
|
+
"refines": null,
|
|
2169
|
+
"test": "tests/errors/OS6006"
|
|
2170
|
+
},
|
|
2171
|
+
{
|
|
2172
|
+
"code": "OS6007",
|
|
2173
|
+
"title": "Unknown symbol or exchange",
|
|
2174
|
+
"severity": "error",
|
|
2175
|
+
"stage": "host",
|
|
2176
|
+
"since": 1,
|
|
2177
|
+
"message": "The host does not know {symbol} on {exchange}.",
|
|
2178
|
+
"placeholders": {
|
|
2179
|
+
"symbol": "the symbol that was requested",
|
|
2180
|
+
"exchange": "the exchange that was requested, or the chart's when none was given"
|
|
2181
|
+
},
|
|
2182
|
+
"cause": "A symbol is resolved by the host against the instruments it can serve. An unknown one is reported rather than returning an empty series, because an empty series looks exactly like an instrument that did not trade.",
|
|
2183
|
+
"fix": "Correct the symbol, and name the exchange it trades on when it is not the chart's: req.symbol(\"SYMBOL\", \"1D\", close, exchange = \"EXCHANGE\").",
|
|
2184
|
+
"autofix": false,
|
|
2185
|
+
"example": {
|
|
2186
|
+
"before": "other = req.symbol(\"SYMBL\", \"1D\", close)",
|
|
2187
|
+
"after": "other = req.symbol(\"SYMBOL\", \"1D\", close, exchange = \"EXCHANGE\")"
|
|
2188
|
+
},
|
|
2189
|
+
"spec": "language.md 15.2",
|
|
2190
|
+
"refines": null,
|
|
2191
|
+
"test": "tests/errors/OS6007"
|
|
2192
|
+
},
|
|
2193
|
+
{
|
|
2194
|
+
"code": "OS6008",
|
|
2195
|
+
"title": "The request returned no bars",
|
|
2196
|
+
"severity": "error",
|
|
2197
|
+
"stage": "host",
|
|
2198
|
+
"since": 1,
|
|
2199
|
+
"message": "{symbol} at {timeframe} returned no bars over the range this chart covers.",
|
|
2200
|
+
"placeholders": {
|
|
2201
|
+
"symbol": "the symbol that was requested",
|
|
2202
|
+
"timeframe": "the interval that was requested"
|
|
2203
|
+
},
|
|
2204
|
+
"cause": "The host resolved the instrument and had nothing to send for the range. An instrument that had not listed yet, a contract that has expired and a range before the stored history begins all arrive here.",
|
|
2205
|
+
"fix": "Move the chart's range into the period the instrument traded, or request a symbol that covers it.",
|
|
2206
|
+
"autofix": false,
|
|
2207
|
+
"example": {
|
|
2208
|
+
"before": "fut = req.symbol(\"EXPIRED_CONTRACT\", \"1D\", close)",
|
|
2209
|
+
"after": "fut = req.symbol(\"CURRENT_CONTRACT\", \"1D\", close)"
|
|
2210
|
+
},
|
|
2211
|
+
"spec": "language.md 15.2",
|
|
2212
|
+
"refines": null,
|
|
2213
|
+
"test": "tests/errors/OS6008"
|
|
2214
|
+
},
|
|
2215
|
+
{
|
|
2216
|
+
"code": "OS6009",
|
|
2217
|
+
"title": "The request failed",
|
|
2218
|
+
"severity": "error",
|
|
2219
|
+
"stage": "host",
|
|
2220
|
+
"since": 1,
|
|
2221
|
+
"message": "The host could not fetch {symbol} at {timeframe}: {reason}.",
|
|
2222
|
+
"placeholders": {
|
|
2223
|
+
"symbol": "the symbol that was requested",
|
|
2224
|
+
"timeframe": "the interval that was requested",
|
|
2225
|
+
"reason": "the host's own description of the failure"
|
|
2226
|
+
},
|
|
2227
|
+
"cause": "The host reached its data source and the source refused or did not answer. A connection that is down, a subscription that does not cover the instrument and a rate limit all arrive here, carrying the reason the host gave.",
|
|
2228
|
+
"fix": "Act on {reason} in the host: it is a connection, permission or quota problem. Where the value can be derived from the chart's own bars, derive it and drop the request.",
|
|
2229
|
+
"autofix": false,
|
|
2230
|
+
"example": {
|
|
2231
|
+
"before": "dayHigh = req.timeframe(\"1D\", high)",
|
|
2232
|
+
"after": "var dayHigh = none\nif session.isFirstBar\n dayHigh = high\nelse\n dayHigh = max(dayHigh, high)"
|
|
2233
|
+
},
|
|
2234
|
+
"spec": "language.md 15.2",
|
|
2235
|
+
"refines": null,
|
|
2236
|
+
"test": "tests/errors/OS6009"
|
|
2237
|
+
},
|
|
2238
|
+
{
|
|
2239
|
+
"code": "OS6010",
|
|
2240
|
+
"title": "The engine was given no bars",
|
|
2241
|
+
"severity": "error",
|
|
2242
|
+
"stage": "host",
|
|
2243
|
+
"since": 1,
|
|
2244
|
+
"message": "There are no bars for {symbol} at {timeframe}, so the script cannot run.",
|
|
2245
|
+
"placeholders": {
|
|
2246
|
+
"symbol": "the chart's symbol",
|
|
2247
|
+
"timeframe": "the chart's interval"
|
|
2248
|
+
},
|
|
2249
|
+
"cause": "The script is the body of the per-bar loop, and a loop over zero bars produces nothing to plot and nothing to report. Saying so is better than an empty pane, which looks like a script that computed nothing.",
|
|
2250
|
+
"fix": "Choose an instrument and interval that have history, or widen the chart's date range until bars exist.",
|
|
2251
|
+
"autofix": false,
|
|
2252
|
+
"example": {
|
|
2253
|
+
"before": "host input: symbol SYMBOL, interval 5, bars 0",
|
|
2254
|
+
"after": "host input: symbol SYMBOL, interval 5, bars 1240"
|
|
2255
|
+
},
|
|
2256
|
+
"spec": "language.md 7.1",
|
|
2257
|
+
"refines": null,
|
|
2258
|
+
"test": "tests/errors/OS6010"
|
|
2259
|
+
},
|
|
2260
|
+
{
|
|
2261
|
+
"code": "OS6011",
|
|
2262
|
+
"title": "The bars are not in order",
|
|
2263
|
+
"severity": "error",
|
|
2264
|
+
"stage": "host",
|
|
2265
|
+
"since": 1,
|
|
2266
|
+
"message": "Bar {index} is dated {time}, which is not after bar {previous}.",
|
|
2267
|
+
"placeholders": {
|
|
2268
|
+
"index": "the index of the offending bar",
|
|
2269
|
+
"time": "that bar's timestamp",
|
|
2270
|
+
"previous": "the index of the bar before it"
|
|
2271
|
+
},
|
|
2272
|
+
"cause": "Every engine assumes strictly increasing bar times: the history operator, warmup and every session test are defined against that order. Running on duplicated or reversed bars would make every one of those meaningless, quietly.",
|
|
2273
|
+
"fix": "Reload the history; if the same bar repeats, the feed is sending duplicates and the host has to sort and deduplicate the bars before the engine runs.",
|
|
2274
|
+
"autofix": false,
|
|
2275
|
+
"example": {
|
|
2276
|
+
"before": "host input:\n bar 41 09:15\n bar 42 09:15\n bar 43 09:10",
|
|
2277
|
+
"after": "host input:\n bar 41 09:10\n bar 42 09:15\n bar 43 09:20"
|
|
2278
|
+
},
|
|
2279
|
+
"spec": "language.md 7.1",
|
|
2280
|
+
"refines": null,
|
|
2281
|
+
"test": "tests/errors/OS6011"
|
|
2282
|
+
},
|
|
2283
|
+
{
|
|
2284
|
+
"code": "OS6012",
|
|
2285
|
+
"title": "An instrument fact is not known",
|
|
2286
|
+
"severity": "error",
|
|
2287
|
+
"stage": "host",
|
|
2288
|
+
"since": 1,
|
|
2289
|
+
"message": "The host did not supply {fact} for {symbol}.",
|
|
2290
|
+
"placeholders": {
|
|
2291
|
+
"fact": "the missing fact: tick size, lot size, session or timezone",
|
|
2292
|
+
"symbol": "the instrument it is missing for"
|
|
2293
|
+
},
|
|
2294
|
+
"cause": "Tick size, lot size, the session and the timezone come from the instrument record (host-interface.md 4.1), not from the bars. A script that rounds to a tick or sizes in lots cannot invent them, and guessing would produce orders the exchange rejects.",
|
|
2295
|
+
"fix": "Supply {fact} in the host's instrument record, or stop depending on it: round with a number the script chooses rather than chart.tickSize.",
|
|
2296
|
+
"autofix": false,
|
|
2297
|
+
"example": {
|
|
2298
|
+
"before": "qty = lots * chart.lotSize",
|
|
2299
|
+
"after": "lotSize = input(1, \"Lot size\", min = 1)\nqty = lots * lotSize"
|
|
2300
|
+
},
|
|
2301
|
+
"spec": "language.md 15.2",
|
|
2302
|
+
"refines": null,
|
|
2303
|
+
"test": "tests/errors/OS6012"
|
|
2304
|
+
},
|
|
2305
|
+
{
|
|
2306
|
+
"code": "OS6013",
|
|
2307
|
+
"title": "The request changed after the first bar",
|
|
2308
|
+
"severity": "error",
|
|
2309
|
+
"stage": "runtime",
|
|
2310
|
+
"since": 1,
|
|
2311
|
+
"message": "This request asked for {first} on bar 0 and for {found} on bar {bar}.",
|
|
2312
|
+
"placeholders": {
|
|
2313
|
+
"first": "the symbol and timeframe requested on the first bar",
|
|
2314
|
+
"found": "the symbol and timeframe requested on this bar",
|
|
2315
|
+
"bar": "the bar index where it changed"
|
|
2316
|
+
},
|
|
2317
|
+
"cause": "The host fetches a requested series once, keyed by symbol and timeframe, and keeps it in step with the chart. A request whose identity changes mid-run would need a second fetch on a bar that has already been drawn, so the identity is fixed before the run.",
|
|
2318
|
+
"fix": "Compute the symbol and the timeframe from literals or inputs, not from bar data.",
|
|
2319
|
+
"autofix": false,
|
|
2320
|
+
"example": {
|
|
2321
|
+
"before": "tf = close > open ? \"60\" : \"1D\"\nh = req.timeframe(tf, high)",
|
|
2322
|
+
"after": "tf = input(\"60\", \"Higher timeframe\", kind = \"interval\")\nh = req.timeframe(tf, high)"
|
|
2323
|
+
},
|
|
2324
|
+
"spec": "language.md 15.2",
|
|
2325
|
+
"refines": null,
|
|
2326
|
+
"test": "tests/errors/OS6013"
|
|
2327
|
+
},
|
|
2328
|
+
{
|
|
2329
|
+
"code": "OS6014",
|
|
2330
|
+
"title": "The feed does not offer this timeframe",
|
|
2331
|
+
"severity": "error",
|
|
2332
|
+
"stage": "host",
|
|
2333
|
+
"since": 1,
|
|
2334
|
+
"message": "The host has no {timeframe} data for {symbol}; it offers {available}.",
|
|
2335
|
+
"placeholders": {
|
|
2336
|
+
"timeframe": "the interval that was requested",
|
|
2337
|
+
"symbol": "the instrument it was requested for",
|
|
2338
|
+
"available": "the intervals the host can serve for it"
|
|
2339
|
+
},
|
|
2340
|
+
"cause": "A timeframe can be well formed and still be one the feed does not store for this instrument. Building it from a lower one is the host's decision to offer or not, and the script is told which intervals exist rather than receiving a silently empty series.",
|
|
2341
|
+
"fix": "Request one of {available}, or derive the interval you want from a lower one the host does serve.",
|
|
2342
|
+
"autofix": false,
|
|
2343
|
+
"example": {
|
|
2344
|
+
"before": "r = req.timeframe(\"3\", close)",
|
|
2345
|
+
"after": "r = req.timeframe(\"5\", close)"
|
|
2346
|
+
},
|
|
2347
|
+
"spec": "language.md 15.2",
|
|
2348
|
+
"refines": null,
|
|
2349
|
+
"test": "tests/errors/OS6014"
|
|
2350
|
+
},
|
|
2351
|
+
{
|
|
2352
|
+
"code": "OS6015",
|
|
2353
|
+
"title": "The requested timeframe does not fold into the chart's",
|
|
2354
|
+
"severity": "error",
|
|
2355
|
+
"stage": "host",
|
|
2356
|
+
"since": 1,
|
|
2357
|
+
"message": "{requested} is not a whole multiple of {chart}.",
|
|
2358
|
+
"placeholders": {
|
|
2359
|
+
"requested": "the interval that was requested",
|
|
2360
|
+
"chart": "the chart's interval",
|
|
2361
|
+
"suggestion": "the nearest interval above the requested one that is a whole multiple of the chart's"
|
|
2362
|
+
},
|
|
2363
|
+
"cause": "An intraday request is folded by counting chart bars, so its interval must be a whole multiple of the chart's. Day, week and month requests are folded by the calendar and the session instead, so they are exempt from this rule.",
|
|
2364
|
+
"fix": "Request a multiple of {chart}, for example {suggestion}.",
|
|
2365
|
+
"autofix": false,
|
|
2366
|
+
"example": {
|
|
2367
|
+
"before": "r = req.timeframe(\"45\", close)",
|
|
2368
|
+
"after": "r = req.timeframe(\"60\", close)"
|
|
2369
|
+
},
|
|
2370
|
+
"spec": "language.md 15.2",
|
|
2371
|
+
"refines": null,
|
|
2372
|
+
"test": "tests/errors/OS6015"
|
|
2373
|
+
},
|
|
2374
|
+
{
|
|
2375
|
+
"code": "OS6016",
|
|
2376
|
+
"title": "The compiled format version is not one this engine implements",
|
|
2377
|
+
"severity": "error",
|
|
2378
|
+
"stage": "host",
|
|
2379
|
+
"since": 1,
|
|
2380
|
+
"message": "This program is in compiled format {found} and this engine implements up to {max}.",
|
|
2381
|
+
"placeholders": {
|
|
2382
|
+
"found": "the format version the program declares",
|
|
2383
|
+
"max": "the highest format version the engine implements"
|
|
2384
|
+
},
|
|
2385
|
+
"cause": "The compiled program is a versioned data format, and an engine refuses a format it cannot read rather than guessing at instructions it does not know. Refusing at load, with both numbers named, is the whole reason the version is carried.",
|
|
2386
|
+
"fix": "Run the program on an engine that implements format {found}, or recompile the source with a compiler that emits format {max}.",
|
|
2387
|
+
"autofix": false,
|
|
2388
|
+
"example": {
|
|
2389
|
+
"before": "program: compiled format 3\nengine: compiled format 2",
|
|
2390
|
+
"after": "program: compiled format 2\nengine: compiled format 2"
|
|
2391
|
+
},
|
|
2392
|
+
"spec": "compiled-program.md, loading a program",
|
|
2393
|
+
"refines": null,
|
|
2394
|
+
"test": "tests/errors/OS6016"
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
"code": "OS6017",
|
|
2398
|
+
"title": "The program's language version is not one this engine implements",
|
|
2399
|
+
"severity": "error",
|
|
2400
|
+
"stage": "host",
|
|
2401
|
+
"since": 1,
|
|
2402
|
+
"message": "This program was compiled from language version {found}, and this engine implements {versions}.",
|
|
2403
|
+
"placeholders": {
|
|
2404
|
+
"found": "the language version the program declares",
|
|
2405
|
+
"versions": "the language versions the engine implements"
|
|
2406
|
+
},
|
|
2407
|
+
"cause": "Every past front end is kept, so a program may name a language version older than the engine and still run exactly as it did. A version the engine does not have is one it cannot run correctly, and running it approximately would break the promise that a saved script keeps producing the same numbers.",
|
|
2408
|
+
"fix": "Upgrade the engine to one that implements language version {found}, or recompile the source against a version it has.",
|
|
2409
|
+
"autofix": false,
|
|
2410
|
+
"example": {
|
|
2411
|
+
"before": "program: language version 2\nengine: language version 1",
|
|
2412
|
+
"after": "program: language version 1\nengine: language version 1"
|
|
2413
|
+
},
|
|
2414
|
+
"spec": "language.md 4.1",
|
|
2415
|
+
"refines": null,
|
|
2416
|
+
"test": "tests/errors/OS6017"
|
|
2417
|
+
},
|
|
2418
|
+
{
|
|
2419
|
+
"code": "OS6018",
|
|
2420
|
+
"title": "The compiled program is malformed",
|
|
2421
|
+
"severity": "error",
|
|
2422
|
+
"stage": "host",
|
|
2423
|
+
"since": 1,
|
|
2424
|
+
"message": "The program failed verification at {location}: {reason}.",
|
|
2425
|
+
"placeholders": {
|
|
2426
|
+
"location": "where the failure is: the word instruction followed by the index of the first instruction that failed, or, when the failure is not in an instruction list, the path of the field that failed, such as outputs[3].color",
|
|
2427
|
+
"reason": "what the verifier found wrong there"
|
|
2428
|
+
},
|
|
2429
|
+
"cause": "An engine verifies a program before it runs it: the encoding parses, every jump lands inside the program, every slot is in range, and every instruction's operands are the shape the format requires. A program that fails is refused whole, because one that is verified as it goes can fail halfway through a bar with half a chart already drawn. Not every check is about an instruction: the structure checks read the program's own fields, and the input-reference check fails on a declaration field that names an input the program never declares, so a failure is located either at the first instruction that failed or at the field holding it, and the message carries whichever of the two applies.",
|
|
2430
|
+
"fix": "Recompile the script from its source; a program that fails verification came from a broken compiler or was edited after it was written, and neither is repairable by hand.",
|
|
2431
|
+
"autofix": false,
|
|
2432
|
+
"example": {
|
|
2433
|
+
"before": "instruction 402: JUMP target 9911, program holds 812 instructions",
|
|
2434
|
+
"after": "instruction 402: JUMP target 411, program holds 812 instructions"
|
|
2435
|
+
},
|
|
2436
|
+
"spec": "compiled-program.md 3.5, 9.4",
|
|
2437
|
+
"refines": null,
|
|
2438
|
+
"test": "tests/errors/OS6018"
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
"code": "OS6019",
|
|
2442
|
+
"title": "A host setting fails the input's validation",
|
|
2443
|
+
"severity": "error",
|
|
2444
|
+
"stage": "host",
|
|
2445
|
+
"since": 1,
|
|
2446
|
+
"message": "The host supplied {value} for {key}, and {validation}.",
|
|
2447
|
+
"placeholders": {
|
|
2448
|
+
"value": "the value the host supplied",
|
|
2449
|
+
"key": "the input's settings key",
|
|
2450
|
+
"validation": "the rule it broke, such as the minimum being 1"
|
|
2451
|
+
},
|
|
2452
|
+
"cause": "An input validates exactly what the host supplies: the type, a number's min and max, and membership of an options list. A value that fails is refused and the program does not run, rather than falling back to the default, because a settings dialog that silently ignores what a user typed is worse than one that says the value is out of range. A declaration field that the compiled program carries as a reference to an input arrives here on the same ground: the engine resolves inputs at load and substitutes the value before it builds the descriptor, so a resolved value the field refuses is a setting from outside the source that the program cannot run with.",
|
|
2453
|
+
"fix": "Correct the value in the settings dialog, or widen the input's own min, max or options so the value is allowed.",
|
|
2454
|
+
"autofix": false,
|
|
2455
|
+
"example": {
|
|
2456
|
+
"before": "input len: min 1, max 500\nhost setting: len = 0",
|
|
2457
|
+
"after": "input len: min 1, max 500\nhost setting: len = 14"
|
|
2458
|
+
},
|
|
2459
|
+
"spec": "language.md 13.4",
|
|
2460
|
+
"refines": null,
|
|
2461
|
+
"test": "tests/errors/OS6019"
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
"code": "OS7001",
|
|
2465
|
+
"title": "Only a strategy can do that",
|
|
2466
|
+
"severity": "error",
|
|
2467
|
+
"stage": "check",
|
|
2468
|
+
"since": 1,
|
|
2469
|
+
"message": "{name} is available only in a file declared with strategy().",
|
|
2470
|
+
"placeholders": {
|
|
2471
|
+
"name": "the order function or position name that was used",
|
|
2472
|
+
"line": "the line of the study declaration"
|
|
2473
|
+
},
|
|
2474
|
+
"cause": "Order functions and the pos namespace need a position to act on and a report to write to, both of which a study does not have. The check is at compile time so a study can never place an order at all.",
|
|
2475
|
+
"fix": "Change study(...) on line {line} to strategy(...), or replace {name} with signal(\"...\") to mark the bar without trading.",
|
|
2476
|
+
"autofix": false,
|
|
2477
|
+
"example": {
|
|
2478
|
+
"before": "study(\"EMA cross\", overlay = true)\n\nif crossUp(fast, slow)\n buy(qty = 1)",
|
|
2479
|
+
"after": "strategy(\"EMA cross\", overlay = true)\n\nif crossUp(fast, slow)\n buy(qty = 1)"
|
|
2480
|
+
},
|
|
2481
|
+
"spec": "language.md 13.1, 13.3",
|
|
2482
|
+
"refines": null,
|
|
2483
|
+
"test": "tests/errors/OS7001"
|
|
2484
|
+
},
|
|
2485
|
+
{
|
|
2486
|
+
"code": "OS7002",
|
|
2487
|
+
"title": "An order argument is absent",
|
|
2488
|
+
"severity": "error",
|
|
2489
|
+
"stage": "runtime",
|
|
2490
|
+
"since": 1,
|
|
2491
|
+
"message": "{name}'s {argument} is absent on this bar.",
|
|
2492
|
+
"placeholders": {
|
|
2493
|
+
"name": "the order function that was called",
|
|
2494
|
+
"argument": "the argument that was absent"
|
|
2495
|
+
},
|
|
2496
|
+
"cause": "An order is the one place in the language where doing nothing quietly is worse than stopping loudly, so an absent price or quantity is refused rather than defaulted. Warmup is the usual source: a stop computed from a window that has not filled yet.",
|
|
2497
|
+
"fix": "Guard the call with isNone({argument}), or supply a fallback with orElse() where one is genuinely correct.",
|
|
2498
|
+
"autofix": false,
|
|
2499
|
+
"example": {
|
|
2500
|
+
"before": "buy(qty = 1, stop = lowest(low, 20))",
|
|
2501
|
+
"after": "s = lowest(low, 20)\nif not isNone(s)\n buy(qty = 1, stop = s)"
|
|
2502
|
+
},
|
|
2503
|
+
"spec": "language.md 6.8",
|
|
2504
|
+
"refines": null,
|
|
2505
|
+
"test": "tests/errors/OS7002"
|
|
2506
|
+
},
|
|
2507
|
+
{
|
|
2508
|
+
"code": "OS7003",
|
|
2509
|
+
"title": "An order function inside a request expression",
|
|
2510
|
+
"severity": "error",
|
|
2511
|
+
"stage": "check",
|
|
2512
|
+
"since": 1,
|
|
2513
|
+
"message": "{name} inside a request expression would place an order from another instrument's bars.",
|
|
2514
|
+
"placeholders": {
|
|
2515
|
+
"name": "the order function that was called inside the expression"
|
|
2516
|
+
},
|
|
2517
|
+
"cause": "A request expression is evaluated on the requested instrument's bars, in that instrument's time. An order placed there has no defined instrument, no defined moment and no defined price, and it would fire once per bar of a series the chart never shows.",
|
|
2518
|
+
"fix": "Read the value with the request, and place the order at the top level from the result.",
|
|
2519
|
+
"autofix": false,
|
|
2520
|
+
"example": {
|
|
2521
|
+
"before": "d = req.timeframe(\"1D\", buy(qty = 1))",
|
|
2522
|
+
"after": "up = req.timeframe(\"1D\", close > ema(close, 20))\nif up\n buy(qty = 1)"
|
|
2523
|
+
},
|
|
2524
|
+
"spec": "language.md 15.2",
|
|
2525
|
+
"refines": null,
|
|
2526
|
+
"test": "tests/errors/OS7003"
|
|
2527
|
+
},
|
|
2528
|
+
{
|
|
2529
|
+
"code": "OS7004",
|
|
2530
|
+
"title": "Order quantity is zero or negative",
|
|
2531
|
+
"severity": "error",
|
|
2532
|
+
"stage": "runtime",
|
|
2533
|
+
"since": 1,
|
|
2534
|
+
"message": "{name} was given a quantity of {qty}.",
|
|
2535
|
+
"placeholders": {
|
|
2536
|
+
"name": "the order function that was called",
|
|
2537
|
+
"qty": "the quantity that reached it"
|
|
2538
|
+
},
|
|
2539
|
+
"cause": "Direction is chosen by the function, not by the sign of the quantity, so a negative quantity is a calculation that went the wrong way rather than an order in the other direction. A quantity of zero is never what a script means.",
|
|
2540
|
+
"fix": "Pass a positive quantity, guard the call with a size test, and use sell() to go the other way.",
|
|
2541
|
+
"autofix": false,
|
|
2542
|
+
"example": {
|
|
2543
|
+
"before": "buy(qty = target - pos.size)",
|
|
2544
|
+
"after": "delta = target - pos.size\nif delta > 0\n buy(qty = delta)"
|
|
2545
|
+
},
|
|
2546
|
+
"spec": "language.md 13.3",
|
|
2547
|
+
"refines": null,
|
|
2548
|
+
"test": "tests/errors/OS7004"
|
|
2549
|
+
},
|
|
2550
|
+
{
|
|
2551
|
+
"code": "OS7005",
|
|
2552
|
+
"title": "Quantity is not a multiple of the lot size",
|
|
2553
|
+
"severity": "error",
|
|
2554
|
+
"stage": "runtime",
|
|
2555
|
+
"since": 1,
|
|
2556
|
+
"message": "{symbol} trades in lots of {lot}, and {qty} is not a multiple of it.",
|
|
2557
|
+
"placeholders": {
|
|
2558
|
+
"symbol": "the instrument being traded",
|
|
2559
|
+
"lot": "its lot size",
|
|
2560
|
+
"qty": "the quantity that was passed"
|
|
2561
|
+
},
|
|
2562
|
+
"cause": "An exchange that trades in lots rejects anything else, so a backtest that filled such an order would report a trade that could not have happened. The engine refuses it for the same reason the exchange would.",
|
|
2563
|
+
"fix": "Size in lots: declare qtyType = \"lots\" and pass the lot count, or round a computed size with order.roundToLot().",
|
|
2564
|
+
"autofix": false,
|
|
2565
|
+
"example": {
|
|
2566
|
+
"before": "buy(qty = 100)",
|
|
2567
|
+
"after": "strategy(\"Lots\", qtyType = \"lots\")\nbuy(qty = 2)"
|
|
2568
|
+
},
|
|
2569
|
+
"spec": "language.md 13.3",
|
|
2570
|
+
"refines": null,
|
|
2571
|
+
"test": "tests/errors/OS7005"
|
|
2572
|
+
},
|
|
2573
|
+
{
|
|
2574
|
+
"code": "OS7006",
|
|
2575
|
+
"title": "Price is not on a tick",
|
|
2576
|
+
"severity": "error",
|
|
2577
|
+
"stage": "runtime",
|
|
2578
|
+
"since": 1,
|
|
2579
|
+
"message": "{symbol} ticks at {tick}, and {price} does not fall on one.",
|
|
2580
|
+
"placeholders": {
|
|
2581
|
+
"symbol": "the instrument being traded",
|
|
2582
|
+
"tick": "its tick size",
|
|
2583
|
+
"price": "the price that was passed"
|
|
2584
|
+
},
|
|
2585
|
+
"cause": "A limit or stop price between two ticks cannot exist at the exchange. Rounding it silently would move the order away from the level the script computed, and in a backtest that difference is free money or a free stop.",
|
|
2586
|
+
"fix": "Round to the tick before passing the price: round(price / chart.tickSize) * chart.tickSize.",
|
|
2587
|
+
"autofix": false,
|
|
2588
|
+
"example": {
|
|
2589
|
+
"before": "sell(qty = 1, limit = close * 1.013)",
|
|
2590
|
+
"after": "target = round(close * 1.013 / chart.tickSize) * chart.tickSize\nsell(qty = 1, limit = target)"
|
|
2591
|
+
},
|
|
2592
|
+
"spec": "language.md 13.3",
|
|
2593
|
+
"refines": null,
|
|
2594
|
+
"test": "tests/errors/OS7006"
|
|
2595
|
+
},
|
|
2596
|
+
{
|
|
2597
|
+
"code": "OS7007",
|
|
2598
|
+
"title": "A resting order has no price",
|
|
2599
|
+
"severity": "error",
|
|
2600
|
+
"stage": "runtime",
|
|
2601
|
+
"since": 1,
|
|
2602
|
+
"message": "A {type} order needs {argument}, and none was given.",
|
|
2603
|
+
"placeholders": {
|
|
2604
|
+
"type": "the order type that was named",
|
|
2605
|
+
"argument": "the price argument that is missing"
|
|
2606
|
+
},
|
|
2607
|
+
"cause": "An order that rests in the book needs the level it rests at. Filling it in from the bar's close would make the order a market order wearing another name, and the report would show a fill the script never asked for.",
|
|
2608
|
+
"fix": "Pass {argument}, or leave the type as market and let the order fill at the next price.",
|
|
2609
|
+
"autofix": false,
|
|
2610
|
+
"example": {
|
|
2611
|
+
"before": "order.place(\"buy\", 1, type = \"limit\")",
|
|
2612
|
+
"after": "order.place(\"buy\", 1, type = \"limit\", price = close - chart.tickSize)"
|
|
2613
|
+
},
|
|
2614
|
+
"spec": "language.md 13.3",
|
|
2615
|
+
"refines": null,
|
|
2616
|
+
"test": "tests/errors/OS7007"
|
|
2617
|
+
},
|
|
2618
|
+
{
|
|
2619
|
+
"code": "OS7008",
|
|
2620
|
+
"title": "The entry was refused by pyramiding",
|
|
2621
|
+
"severity": "error",
|
|
2622
|
+
"stage": "runtime",
|
|
2623
|
+
"since": 1,
|
|
2624
|
+
"message": "This strategy allows {max} entries in one direction and already holds {found}.",
|
|
2625
|
+
"placeholders": {
|
|
2626
|
+
"max": "the pyramiding option in force",
|
|
2627
|
+
"found": "the number of entries already open in that direction"
|
|
2628
|
+
},
|
|
2629
|
+
"cause": "Refusing rather than silently adding keeps a backtest from building a position the declaration forbade, which would report a return the stated rules never earned.",
|
|
2630
|
+
"fix": "Raise pyramiding in the declaration, or test pos.size before entering again.",
|
|
2631
|
+
"autofix": false,
|
|
2632
|
+
"example": {
|
|
2633
|
+
"before": "strategy(\"Add\", pyramiding = 1)\nif signalUp\n buy(qty = 1)",
|
|
2634
|
+
"after": "strategy(\"Add\", pyramiding = 1)\nif signalUp and pos.size == 0\n buy(qty = 1)"
|
|
2635
|
+
},
|
|
2636
|
+
"spec": "language.md 13.3",
|
|
2637
|
+
"refines": null,
|
|
2638
|
+
"test": "tests/errors/OS7008"
|
|
2639
|
+
},
|
|
2640
|
+
{
|
|
2641
|
+
"code": "OS7009",
|
|
2642
|
+
"title": "Unknown order tag",
|
|
2643
|
+
"severity": "error",
|
|
2644
|
+
"stage": "runtime",
|
|
2645
|
+
"since": 1,
|
|
2646
|
+
"message": "There is no working order tagged {tag}.",
|
|
2647
|
+
"placeholders": {
|
|
2648
|
+
"tag": "the tag that was passed"
|
|
2649
|
+
},
|
|
2650
|
+
"cause": "A tag names an order from the moment it is placed. Acting on a tag that names nothing is a script that has lost track of its own orders, and ignoring the call would leave it believing an order exists that does not. This code is for a call that acts on an order. The reading calls of stdlib.md section 17.3 read the ledger, which keeps a row after the order finishes, so a tag that names no row reads as the entry's documented empty value rather than raising.",
|
|
2651
|
+
"fix": "Use the tag the order was placed with, and test order.working({tag}) before acting on it.",
|
|
2652
|
+
"autofix": false,
|
|
2653
|
+
"example": {
|
|
2654
|
+
"before": "buy(qty = 1, tag = \"entry\")\norder.bracket(tag = \"entries\", loss = 10)",
|
|
2655
|
+
"after": "buy(qty = 1, tag = \"entry\")\nif order.working(\"entry\")\n order.bracket(tag = \"entry\", loss = 10)"
|
|
2656
|
+
},
|
|
2657
|
+
"spec": "language.md 15.2; stdlib.md 17.3",
|
|
2658
|
+
"refines": null,
|
|
2659
|
+
"test": "tests/errors/OS7009"
|
|
2660
|
+
},
|
|
2661
|
+
{
|
|
2662
|
+
"code": "OS7010",
|
|
2663
|
+
"title": "A bracket price is on the wrong side of the entry",
|
|
2664
|
+
"severity": "error",
|
|
2665
|
+
"stage": "runtime",
|
|
2666
|
+
"since": 1,
|
|
2667
|
+
"message": "A {side} entry at {entry} cannot take a {leg} at {price}.",
|
|
2668
|
+
"placeholders": {
|
|
2669
|
+
"side": "long or short",
|
|
2670
|
+
"entry": "the position's average entry price",
|
|
2671
|
+
"leg": "the bracket leg: stop or limit",
|
|
2672
|
+
"price": "the price that was passed"
|
|
2673
|
+
},
|
|
2674
|
+
"cause": "A stop protects and a limit takes profit, so their sides are fixed by the direction of the position. A stop on the wrong side fills immediately, which in a backtest turns every trade into an instant loss that looks like a strategy result.",
|
|
2675
|
+
"fix": "Put the stop below a long entry and the limit above it, and swap the two for a short.",
|
|
2676
|
+
"autofix": false,
|
|
2677
|
+
"example": {
|
|
2678
|
+
"before": "buy(qty = 1)\nexit(limit = pos.avgPrice - atrValue, stop = pos.avgPrice + atrValue)",
|
|
2679
|
+
"after": "buy(qty = 1)\nexit(limit = pos.avgPrice + atrValue, stop = pos.avgPrice - atrValue)"
|
|
2680
|
+
},
|
|
2681
|
+
"spec": "language.md 13.3",
|
|
2682
|
+
"refines": null,
|
|
2683
|
+
"test": "tests/errors/OS7010"
|
|
2684
|
+
},
|
|
2685
|
+
{
|
|
2686
|
+
"code": "OS7011",
|
|
2687
|
+
"title": "The order needs more capital than the strategy has",
|
|
2688
|
+
"severity": "error",
|
|
2689
|
+
"stage": "runtime",
|
|
2690
|
+
"since": 1,
|
|
2691
|
+
"message": "This order needs {required} and the strategy has {available}.",
|
|
2692
|
+
"placeholders": {
|
|
2693
|
+
"required": "the capital the order would consume",
|
|
2694
|
+
"available": "the capital the strategy has left"
|
|
2695
|
+
},
|
|
2696
|
+
"cause": "A backtest that can spend money it does not have reports a return nobody could have earned. The order is refused rather than filled on credit, and the report records the refusal so the equity curve stays honest.",
|
|
2697
|
+
"fix": "Size from equity with qtyType = \"equityPercent\", or test pos.equity before entering.",
|
|
2698
|
+
"autofix": false,
|
|
2699
|
+
"example": {
|
|
2700
|
+
"before": "buy(qty = 100)",
|
|
2701
|
+
"after": "strategy(\"Sized\", qtyType = \"equityPercent\")\nbuy(qty = 10)"
|
|
2702
|
+
},
|
|
2703
|
+
"spec": "language.md 13.3",
|
|
2704
|
+
"refines": null,
|
|
2705
|
+
"test": "tests/errors/OS7011"
|
|
2706
|
+
},
|
|
2707
|
+
{
|
|
2708
|
+
"code": "OS7012",
|
|
2709
|
+
"title": "The instrument is outside its session",
|
|
2710
|
+
"severity": "error",
|
|
2711
|
+
"stage": "runtime",
|
|
2712
|
+
"since": 1,
|
|
2713
|
+
"message": "{symbol} is outside its trading session at {time}.",
|
|
2714
|
+
"placeholders": {
|
|
2715
|
+
"symbol": "the instrument being traded",
|
|
2716
|
+
"time": "the bar's timestamp"
|
|
2717
|
+
},
|
|
2718
|
+
"cause": "An order placed outside the session cannot be worked by the exchange. Holding it until the open would fill it at a price the script never saw, so it is refused and the script decides what to do at the open instead.",
|
|
2719
|
+
"fix": "Guard entries with session.isOpen, and set closeOnSessionEnd = true to flatten at the close.",
|
|
2720
|
+
"autofix": false,
|
|
2721
|
+
"example": {
|
|
2722
|
+
"before": "if crossUp(fast, slow)\n buy(qty = 1)",
|
|
2723
|
+
"after": "if crossUp(fast, slow) and session.isOpen\n buy(qty = 1)"
|
|
2724
|
+
},
|
|
2725
|
+
"spec": "language.md 15.2",
|
|
2726
|
+
"refines": null,
|
|
2727
|
+
"test": "tests/errors/OS7012"
|
|
2728
|
+
},
|
|
2729
|
+
{
|
|
2730
|
+
"code": "OS7013",
|
|
2731
|
+
"title": "Two opposite orders on one bar",
|
|
2732
|
+
"severity": "error",
|
|
2733
|
+
"stage": "runtime",
|
|
2734
|
+
"since": 1,
|
|
2735
|
+
"message": "{first} and {second} were both placed on bar {bar}.",
|
|
2736
|
+
"placeholders": {
|
|
2737
|
+
"first": "the first order call, with its line",
|
|
2738
|
+
"second": "the second order call, with its line",
|
|
2739
|
+
"bar": "the bar index"
|
|
2740
|
+
},
|
|
2741
|
+
"cause": "Which of the two the engine should honour has no defensible answer: source order is an accident of layout, and the last one wins is a rule that silently changes when someone reorders two blocks. Neither is placed, and the bar stops.",
|
|
2742
|
+
"fix": "Make the conditions exclusive with else if, or place the exit on this bar and the entry on the next.",
|
|
2743
|
+
"autofix": false,
|
|
2744
|
+
"example": {
|
|
2745
|
+
"before": "if crossUp(fast, slow)\n buy(qty = 1)\nif rsi(close, 14) > 70\n sell(qty = 1)",
|
|
2746
|
+
"after": "if crossUp(fast, slow)\n buy(qty = 1)\nelse if rsi(close, 14) > 70\n sell(qty = 1)"
|
|
2747
|
+
},
|
|
2748
|
+
"spec": "language.md 13.3",
|
|
2749
|
+
"refines": null,
|
|
2750
|
+
"test": "tests/errors/OS7013"
|
|
2751
|
+
},
|
|
2752
|
+
{
|
|
2753
|
+
"code": "OS7014",
|
|
2754
|
+
"title": "The destination rejected the order",
|
|
2755
|
+
"severity": "error",
|
|
2756
|
+
"stage": "host",
|
|
2757
|
+
"since": 1,
|
|
2758
|
+
"message": "The order destination rejected {name}: {reason}.",
|
|
2759
|
+
"placeholders": {
|
|
2760
|
+
"name": "the order function that was called",
|
|
2761
|
+
"reason": "the destination's own rejection text"
|
|
2762
|
+
},
|
|
2763
|
+
"cause": "The order left the engine well formed and the destination refused it. A product the account cannot trade, a margin shortfall and a symbol the account has no permission for all arrive here, carrying the destination's reason.",
|
|
2764
|
+
"fix": "Act on {reason}: it comes from the destination, not from the script, and the same order will be rejected again until the account or the order changes.",
|
|
2765
|
+
"autofix": false,
|
|
2766
|
+
"example": {
|
|
2767
|
+
"before": "strategy(\"Swing\", product = \"overnight\")\nbuy(qty = 1)",
|
|
2768
|
+
"after": "strategy(\"Swing\", product = \"intraday\")\nbuy(qty = 1)"
|
|
2769
|
+
},
|
|
2770
|
+
"spec": "language.md 13.3",
|
|
2771
|
+
"refines": null,
|
|
2772
|
+
"test": "tests/errors/OS7014"
|
|
2773
|
+
},
|
|
2774
|
+
{
|
|
2775
|
+
"code": "OS7015",
|
|
2776
|
+
"title": "The strategy has no order destination",
|
|
2777
|
+
"severity": "error",
|
|
2778
|
+
"stage": "host",
|
|
2779
|
+
"since": 1,
|
|
2780
|
+
"message": "This strategy placed an order and the host supplied no destination.",
|
|
2781
|
+
"placeholders": {},
|
|
2782
|
+
"cause": "A strategy needs somewhere for orders to go: a paper engine, a backtest simulator or a broker connection. Running one with no destination would compute a position nothing ever took.",
|
|
2783
|
+
"fix": "Connect a destination in the host, or run the file as a study(): replace buy() with signal(\"BUY\").",
|
|
2784
|
+
"autofix": false,
|
|
2785
|
+
"example": {
|
|
2786
|
+
"before": "strategy(\"EMA cross\")\nif crossUp(fast, slow)\n buy(qty = 1)",
|
|
2787
|
+
"after": "study(\"EMA cross\")\nif crossUp(fast, slow)\n signal(\"BUY\")"
|
|
2788
|
+
},
|
|
2789
|
+
"spec": "language.md 13.3",
|
|
2790
|
+
"refines": null,
|
|
2791
|
+
"test": "tests/errors/OS7015"
|
|
2792
|
+
},
|
|
2793
|
+
{
|
|
2794
|
+
"code": "OS8001",
|
|
2795
|
+
"title": "A stateful call inside a branch",
|
|
2796
|
+
"severity": "warning",
|
|
2797
|
+
"stage": "check",
|
|
2798
|
+
"since": 1,
|
|
2799
|
+
"message": "{name} advances only on the bars where this branch runs, and is absent on the rest.",
|
|
2800
|
+
"placeholders": {
|
|
2801
|
+
"name": "the stateful call, such as ema, rma or a user function holding var"
|
|
2802
|
+
},
|
|
2803
|
+
"cause": "A call site that does not execute on a bar leaves its series absent for that bar and its state untouched. The alternatives are worse: forcing the call would execute code the script said to skip, and carrying the previous value forward would draw a flat line that looks like data. This is almost always a mistake rather than an intent.",
|
|
2804
|
+
"fix": "Compute it unconditionally at the top level and use the result inside the branch.",
|
|
2805
|
+
"autofix": false,
|
|
2806
|
+
"example": {
|
|
2807
|
+
"before": "if trending\n e = ema(close, 20)\n plot(e, \"EMA\", aqua)",
|
|
2808
|
+
"after": "e = ema(close, 20)\nplot(trending ? e : none, \"EMA\", aqua)"
|
|
2809
|
+
},
|
|
2810
|
+
"spec": "language.md 11.4",
|
|
2811
|
+
"refines": null,
|
|
2812
|
+
"test": "tests/errors/OS8001"
|
|
2813
|
+
},
|
|
2814
|
+
{
|
|
2815
|
+
"code": "OS8002",
|
|
2816
|
+
"title": "A higher timeframe read with onUnconfirmed",
|
|
2817
|
+
"severity": "warning",
|
|
2818
|
+
"stage": "check",
|
|
2819
|
+
"since": 1,
|
|
2820
|
+
"message": "This file sets onUnconfirmed = true and reads {timeframe}; together they repaint.",
|
|
2821
|
+
"placeholders": {
|
|
2822
|
+
"timeframe": "the requested interval"
|
|
2823
|
+
},
|
|
2824
|
+
"cause": "A higher timeframe bar is incomplete until it closes, and acting on an unconfirmed bar acts on that incomplete value. The chart then redraws when the higher bar closes, and the backtest and the live run disagree on the same data.",
|
|
2825
|
+
"fix": "Drop onUnconfirmed = true, or guard every use of the read with bar.isConfirmed.",
|
|
2826
|
+
"autofix": false,
|
|
2827
|
+
"example": {
|
|
2828
|
+
"before": "strategy(\"HTF\", onUnconfirmed = true)\nd = req.timeframe(\"1D\", high)\nif close > d\n buy(qty = 1)",
|
|
2829
|
+
"after": "strategy(\"HTF\")\nd = req.timeframe(\"1D\", high)\nif close > d\n buy(qty = 1)"
|
|
2830
|
+
},
|
|
2831
|
+
"spec": "language.md 7.5",
|
|
2832
|
+
"refines": null,
|
|
2833
|
+
"test": "tests/errors/OS8002"
|
|
2834
|
+
},
|
|
2835
|
+
{
|
|
2836
|
+
"code": "OS8003",
|
|
2837
|
+
"title": "No version declaration",
|
|
2838
|
+
"severity": "warning",
|
|
2839
|
+
"stage": "check",
|
|
2840
|
+
"since": 1,
|
|
2841
|
+
"message": "This file declares no language version; it was compiled as version {version}.",
|
|
2842
|
+
"placeholders": {
|
|
2843
|
+
"version": "the newest language version the compiler implements"
|
|
2844
|
+
},
|
|
2845
|
+
"cause": "A file that names its version is parsed by that version's front end for ever, and every past front end is kept. A file without one is parsed by the newest, which is the one thing that can change under it.",
|
|
2846
|
+
"fix": "Add version {version} as the first line of the file.",
|
|
2847
|
+
"autofix": true,
|
|
2848
|
+
"example": {
|
|
2849
|
+
"before": "study(\"EMA cross\")",
|
|
2850
|
+
"after": "version 1\n\nstudy(\"EMA cross\")"
|
|
2851
|
+
},
|
|
2852
|
+
"spec": "language.md 4",
|
|
2853
|
+
"refines": null,
|
|
2854
|
+
"test": "tests/errors/OS8003"
|
|
2855
|
+
},
|
|
2856
|
+
{
|
|
2857
|
+
"code": "OS8004",
|
|
2858
|
+
"title": "A branch on an absent condition changes a value used later",
|
|
2859
|
+
"severity": "warning",
|
|
2860
|
+
"stage": "check",
|
|
2861
|
+
"since": 1,
|
|
2862
|
+
"message": "{condition} can be absent, and this block assigns {name}, which is read at line {line}.",
|
|
2863
|
+
"placeholders": {
|
|
2864
|
+
"condition": "the condition expression as written",
|
|
2865
|
+
"name": "the name the block assigns",
|
|
2866
|
+
"line": "the line outside the block that reads it"
|
|
2867
|
+
},
|
|
2868
|
+
"cause": "An absent condition takes the false branch, so during warmup the block does not run and the name keeps whatever it held before. Warmup bars sit off the left edge of the screen, which is why this shape silently changes an answer and nobody notices.",
|
|
2869
|
+
"fix": "Decide what warmup means: test isNone({condition}) explicitly, or give {name} a starting value above the if.",
|
|
2870
|
+
"autofix": false,
|
|
2871
|
+
"example": {
|
|
2872
|
+
"before": "if rsi(close, 14) > 70\n zone = \"high\"\nplot(zone == \"high\" ? 1 : 0, \"Zone\", aqua)",
|
|
2873
|
+
"after": "zone = \"mid\"\nif rsi(close, 14) > 70\n zone = \"high\"\nplot(zone == \"high\" ? 1 : 0, \"Zone\", aqua)"
|
|
2874
|
+
},
|
|
2875
|
+
"spec": "language.md 6.6",
|
|
2876
|
+
"refines": null,
|
|
2877
|
+
"test": "tests/errors/OS8004"
|
|
2878
|
+
},
|
|
2879
|
+
{
|
|
2880
|
+
"code": "OS8005",
|
|
2881
|
+
"title": "A lookahead read",
|
|
2882
|
+
"severity": "warning",
|
|
2883
|
+
"stage": "check",
|
|
2884
|
+
"since": 1,
|
|
2885
|
+
"message": "This read uses {mode}, so the study shows values the bar it is drawn on could not have known.",
|
|
2886
|
+
"placeholders": {
|
|
2887
|
+
"mode": "the mode the read was given"
|
|
2888
|
+
},
|
|
2889
|
+
"cause": "A lookahead read takes a higher timeframe bar's final value on chart bars that fall before that bar closed. It is the strongest form of repainting there is, which is why it has to be written out in a word a reviewer sees on the line that causes it. The compiled study is also marked as repainting, so the host can say so.",
|
|
2890
|
+
"fix": "Drop the mode to take the default, which never repaints, unless the study is deliberately a study of what the higher bar went on to do.",
|
|
2891
|
+
"autofix": false,
|
|
2892
|
+
"example": {
|
|
2893
|
+
"before": "d = req.timeframe(\"1D\", high, mode = \"lookahead\")",
|
|
2894
|
+
"after": "d = req.timeframe(\"1D\", high)"
|
|
2895
|
+
},
|
|
2896
|
+
"spec": "language.md 7.5",
|
|
2897
|
+
"refines": null,
|
|
2898
|
+
"test": "tests/errors/OS8005"
|
|
2899
|
+
},
|
|
2900
|
+
{
|
|
2901
|
+
"code": "OS8006",
|
|
2902
|
+
"title": "A session average on a session-length bar",
|
|
2903
|
+
"severity": "warning",
|
|
2904
|
+
"stage": "check",
|
|
2905
|
+
"since": 1,
|
|
2906
|
+
"message": "A session anchored average resets each session, and each bar of {interval} is a whole session, so it equals its source.",
|
|
2907
|
+
"placeholders": {
|
|
2908
|
+
"interval": "the chart's interval"
|
|
2909
|
+
},
|
|
2910
|
+
"cause": "The average is a within-session measure: it accumulates from the session's first bar. On a daily or longer interval each bar is its own session, so the accumulation is one bar long and the result is that bar's own price. The plot carries no information and the legend suggests it does.",
|
|
2911
|
+
"fix": "Use an intraday interval for a session anchored average, or plot the source directly and delete the call.",
|
|
2912
|
+
"autofix": false,
|
|
2913
|
+
"example": {
|
|
2914
|
+
"before": "plot(vwap(), \"Session average\", aqua)",
|
|
2915
|
+
"after": "plot(hlc3, \"Typical price\", aqua)"
|
|
2916
|
+
},
|
|
2917
|
+
"spec": "language.md 15.2",
|
|
2918
|
+
"refines": null,
|
|
2919
|
+
"test": "tests/errors/OS8006"
|
|
2920
|
+
},
|
|
2921
|
+
{
|
|
2922
|
+
"code": "OS8007",
|
|
2923
|
+
"title": "A plot sets the price pane's own formatting",
|
|
2924
|
+
"severity": "warning",
|
|
2925
|
+
"stage": "check",
|
|
2926
|
+
"since": 1,
|
|
2927
|
+
"message": "{title} sets {option} while drawing over the price pane, which reformats the instrument's own axis.",
|
|
2928
|
+
"placeholders": {
|
|
2929
|
+
"title": "the plot's title, quoted",
|
|
2930
|
+
"option": "the option that was set, precision or format"
|
|
2931
|
+
},
|
|
2932
|
+
"cause": "precision and format on a plot apply to the price scale that plot maps to. Over the price pane that scale is the instrument's own, so the setting changes the axis every other series on that pane is read against, which is almost never what was meant.",
|
|
2933
|
+
"fix": "Set precision and format in the declaration, for the whole study, or drop them from a plot drawn over the price pane.",
|
|
2934
|
+
"autofix": false,
|
|
2935
|
+
"example": {
|
|
2936
|
+
"before": "study(\"Bands\", overlay = true)\nplot(upper, \"Upper\", aqua, precision = 0)",
|
|
2937
|
+
"after": "study(\"Bands\", overlay = true, precision = 0)\nplot(upper, \"Upper\", aqua)"
|
|
2938
|
+
},
|
|
2939
|
+
"spec": "language.md 13.2, 15.3",
|
|
2940
|
+
"refines": null,
|
|
2941
|
+
"test": "tests/errors/OS8007"
|
|
2942
|
+
},
|
|
2943
|
+
{
|
|
2944
|
+
"code": "OS8008",
|
|
2945
|
+
"title": "An alert with no id",
|
|
2946
|
+
"severity": "warning",
|
|
2947
|
+
"stage": "check",
|
|
2948
|
+
"since": 1,
|
|
2949
|
+
"message": "This alert has no id, so its identity is derived from its position at line {line}.",
|
|
2950
|
+
"placeholders": {
|
|
2951
|
+
"line": "the line the alert is declared on"
|
|
2952
|
+
},
|
|
2953
|
+
"cause": "A user's alert subscription is keyed by the alert's id, and an id derived from a call's position changes the moment a line is inserted above it. The subscription then belongs to an alert that no longer exists, and it stops firing without telling anybody.",
|
|
2954
|
+
"fix": "Give the alert a stable id of your own: alert(\"text\", id = \"emaCross\").",
|
|
2955
|
+
"autofix": false,
|
|
2956
|
+
"example": {
|
|
2957
|
+
"before": "if crossUp(fast, slow)\n alert(\"EMA cross\")",
|
|
2958
|
+
"after": "if crossUp(fast, slow)\n alert(\"EMA cross\", id = \"emaCross\")"
|
|
2959
|
+
},
|
|
2960
|
+
"spec": "language.md 15.3",
|
|
2961
|
+
"refines": null,
|
|
2962
|
+
"test": "tests/errors/OS8008"
|
|
2963
|
+
},
|
|
2964
|
+
{
|
|
2965
|
+
"code": "OS8009",
|
|
2966
|
+
"title": "This plot can never draw",
|
|
2967
|
+
"severity": "warning",
|
|
2968
|
+
"stage": "check",
|
|
2969
|
+
"since": 1,
|
|
2970
|
+
"message": "{title} plots a value that is absent on every bar.",
|
|
2971
|
+
"placeholders": {
|
|
2972
|
+
"title": "the plot's title, quoted"
|
|
2973
|
+
},
|
|
2974
|
+
"cause": "A plot whose value is constantly none occupies a legend row, a settings group and an axis, and draws nothing. It is usually a placeholder that was left behind, or a name that was never assigned the value it was meant to hold.",
|
|
2975
|
+
"fix": "Plot the value that was meant, or delete the plot and its settings row.",
|
|
2976
|
+
"autofix": false,
|
|
2977
|
+
"example": {
|
|
2978
|
+
"before": "plot(none, \"Reserved\", aqua)",
|
|
2979
|
+
"after": "plot(ema(close, 20), \"EMA 20\", aqua)"
|
|
2980
|
+
},
|
|
2981
|
+
"spec": "language.md 6.7",
|
|
2982
|
+
"refines": null,
|
|
2983
|
+
"test": "tests/errors/OS8009"
|
|
2984
|
+
},
|
|
2985
|
+
{
|
|
2986
|
+
"code": "OS8010",
|
|
2987
|
+
"title": "A name is never read",
|
|
2988
|
+
"severity": "warning",
|
|
2989
|
+
"stage": "check",
|
|
2990
|
+
"since": 1,
|
|
2991
|
+
"message": "{name} is assigned at line {line} and never read.",
|
|
2992
|
+
"placeholders": {
|
|
2993
|
+
"name": "the name that is never read",
|
|
2994
|
+
"line": "the line of its assignment"
|
|
2995
|
+
},
|
|
2996
|
+
"cause": "The statement still runs on every bar, so an unread name costs time on fifty thousand bars and tells the next reader that something depends on it. It is usually the remains of a calculation that was replaced. A name bound to a declaration handle is exempt: it is a compile-time binding with nothing left in the bar loop, and naming a fill or a level result and never reading it is legal (language.md 5.4).",
|
|
2997
|
+
"fix": "Use the value, or delete the line.",
|
|
2998
|
+
"autofix": false,
|
|
2999
|
+
"example": {
|
|
3000
|
+
"before": "slow = ema(close, 21)\nplot(ema(close, 9), \"Fast\", aqua)",
|
|
3001
|
+
"after": "plot(ema(close, 9), \"Fast\", aqua)"
|
|
3002
|
+
},
|
|
3003
|
+
"spec": "language.md 12.2",
|
|
3004
|
+
"refines": null,
|
|
3005
|
+
"test": "tests/errors/OS8010"
|
|
3006
|
+
},
|
|
3007
|
+
{
|
|
3008
|
+
"code": "OS8011",
|
|
3009
|
+
"title": "live var makes live and backtest differ",
|
|
3010
|
+
"severity": "warning",
|
|
3011
|
+
"stage": "check",
|
|
3012
|
+
"since": 1,
|
|
3013
|
+
"message": "{name} is a live var, so it keeps its value across the updates of the moving bar.",
|
|
3014
|
+
"placeholders": {
|
|
3015
|
+
"name": "the name declared with live var"
|
|
3016
|
+
},
|
|
3017
|
+
"cause": "An ordinary var rolls back before each re-execution of the newest bar, which is what makes a live chart and a backtest of the same data agree. A live var opts out of that, so a script using one reports different numbers in the two places by design.",
|
|
3018
|
+
"fix": "Use var unless counting intrabar updates is the actual intent; keep live var only for that.",
|
|
3019
|
+
"autofix": false,
|
|
3020
|
+
"example": {
|
|
3021
|
+
"before": "live var barCount = 0\nbarCount = barCount + 1\nplot(barCount, \"Bars\", aqua)",
|
|
3022
|
+
"after": "var barCount = 0\nbarCount = barCount + 1\nplot(barCount, \"Bars\", aqua)"
|
|
3023
|
+
},
|
|
3024
|
+
"spec": "language.md 7.5, 8.2",
|
|
3025
|
+
"refines": null,
|
|
3026
|
+
"test": "tests/errors/OS8011"
|
|
3027
|
+
},
|
|
3028
|
+
{
|
|
3029
|
+
"code": "OS8012",
|
|
3030
|
+
"title": "An ordered comparison against none is always absent",
|
|
3031
|
+
"severity": "warning",
|
|
3032
|
+
"stage": "check",
|
|
3033
|
+
"since": 1,
|
|
3034
|
+
"message": "This {op} has none on one side, so it is absent on every bar.",
|
|
3035
|
+
"placeholders": {
|
|
3036
|
+
"op": "the comparison operator that was written"
|
|
3037
|
+
},
|
|
3038
|
+
"cause": "Ordered comparison propagates absence, so a comparison against none can only ever be none, and a condition of none takes the false branch. The branch is therefore dead. Equality is the operator that answers this question.",
|
|
3039
|
+
"fix": "Test absence with isNone(x), or with x == none, both of which are always true or false.",
|
|
3040
|
+
"autofix": false,
|
|
3041
|
+
"example": {
|
|
3042
|
+
"before": "if value > none\n signal(\"READY\")",
|
|
3043
|
+
"after": "if not isNone(value)\n signal(\"READY\")"
|
|
3044
|
+
},
|
|
3045
|
+
"spec": "language.md 6.4, 6.5",
|
|
3046
|
+
"refines": null,
|
|
3047
|
+
"test": "tests/errors/OS8012"
|
|
3048
|
+
},
|
|
3049
|
+
{
|
|
3050
|
+
"code": "OS8013",
|
|
3051
|
+
"title": "Deprecated",
|
|
3052
|
+
"severity": "warning",
|
|
3053
|
+
"stage": "check",
|
|
3054
|
+
"since": 1,
|
|
3055
|
+
"message": "{name} is deprecated since language version {version}; {replacement} does the same thing.",
|
|
3056
|
+
"placeholders": {
|
|
3057
|
+
"name": "the deprecated function or option",
|
|
3058
|
+
"version": "the language version that deprecated it",
|
|
3059
|
+
"replacement": "the name or option that replaces it"
|
|
3060
|
+
},
|
|
3061
|
+
"cause": "A construct that turns out to be a mistake is never removed and never changes meaning, because a saved script must keep working. It warns instead, and it will still be there in version 9.",
|
|
3062
|
+
"fix": "Replace {name} with {replacement}; the two compute the same values.",
|
|
3063
|
+
"autofix": true,
|
|
3064
|
+
"example": {
|
|
3065
|
+
"before": "plot(oldName(close, 14), \"Value\", aqua)",
|
|
3066
|
+
"after": "plot(newName(close, 14), \"Value\", aqua)"
|
|
3067
|
+
},
|
|
3068
|
+
"spec": "language.md 4.1",
|
|
3069
|
+
"refines": null,
|
|
3070
|
+
"test": "tests/errors/OS8013"
|
|
3071
|
+
},
|
|
3072
|
+
{
|
|
3073
|
+
"code": "OS8014",
|
|
3074
|
+
"title": "A persistent value holds a bar index",
|
|
3075
|
+
"severity": "warning",
|
|
3076
|
+
"stage": "check",
|
|
3077
|
+
"since": 1,
|
|
3078
|
+
"message": "{name} keeps a bar index across bars, and every index shifts when more history loads.",
|
|
3079
|
+
"placeholders": {
|
|
3080
|
+
"name": "the persistent name that holds bar.index"
|
|
3081
|
+
},
|
|
3082
|
+
"cause": "bar.index is a position in the data the engine was given, not an address. Loading more history renumbers every bar, so a stored index compared later is being compared against something that moved underneath it.",
|
|
3083
|
+
"fix": "Store time instead and compare timestamps; the bar's time does not move.",
|
|
3084
|
+
"autofix": false,
|
|
3085
|
+
"example": {
|
|
3086
|
+
"before": "var entryBar = none\nif enter\n entryBar = bar.index",
|
|
3087
|
+
"after": "var entryTime = none\nif enter\n entryTime = time"
|
|
3088
|
+
},
|
|
3089
|
+
"spec": "language.md 7.2",
|
|
3090
|
+
"refines": null,
|
|
3091
|
+
"test": "tests/errors/OS8014"
|
|
3092
|
+
},
|
|
3093
|
+
{
|
|
3094
|
+
"code": "OS8015",
|
|
3095
|
+
"title": "This loop never runs",
|
|
3096
|
+
"severity": "warning",
|
|
3097
|
+
"stage": "check",
|
|
3098
|
+
"since": 1,
|
|
3099
|
+
"message": "The loop starts at {start}, ends at {end} and steps {step}, so the body never runs.",
|
|
3100
|
+
"placeholders": {
|
|
3101
|
+
"start": "the start value",
|
|
3102
|
+
"end": "the end value",
|
|
3103
|
+
"step": "the step value"
|
|
3104
|
+
},
|
|
3105
|
+
"cause": "A descending range with a positive step is empty, and so is an ascending range with a negative step: the loop does not silently reverse in either direction, because a form that reverses itself is the only shape of for loop that can spin for ever by accident.",
|
|
3106
|
+
"fix": "Add step -1 to count down, or swap the bounds to count up.",
|
|
3107
|
+
"autofix": false,
|
|
3108
|
+
"example": {
|
|
3109
|
+
"before": "for i = 9 to 0\n total += close[i]",
|
|
3110
|
+
"after": "for i = 9 to 0 step -1\n total += close[i]"
|
|
3111
|
+
},
|
|
3112
|
+
"spec": "language.md 10.3",
|
|
3113
|
+
"refines": null,
|
|
3114
|
+
"test": "tests/errors/OS8015"
|
|
3115
|
+
},
|
|
3116
|
+
{
|
|
3117
|
+
"code": "OS8016",
|
|
3118
|
+
"title": "Unreachable code",
|
|
3119
|
+
"severity": "warning",
|
|
3120
|
+
"stage": "check",
|
|
3121
|
+
"since": 1,
|
|
3122
|
+
"message": "Line {line} follows a return that always runs, so it never executes.",
|
|
3123
|
+
"placeholders": {
|
|
3124
|
+
"line": "the first unreachable line"
|
|
3125
|
+
},
|
|
3126
|
+
"cause": "A return exits the function immediately. Statements after one that always runs are dead, and the usual cause is an early exit that lost its if.",
|
|
3127
|
+
"fix": "Delete the unreachable lines, or move them above the return.",
|
|
3128
|
+
"autofix": false,
|
|
3129
|
+
"example": {
|
|
3130
|
+
"before": "fn pick(x) =>\n return x\n x * 2",
|
|
3131
|
+
"after": "fn pick(x) =>\n x"
|
|
3132
|
+
},
|
|
3133
|
+
"spec": "language.md 11.3",
|
|
3134
|
+
"refines": null,
|
|
3135
|
+
"test": "tests/errors/OS8016"
|
|
3136
|
+
},
|
|
3137
|
+
{
|
|
3138
|
+
"code": "OS8017",
|
|
3139
|
+
"title": "The condition is constant",
|
|
3140
|
+
"severity": "warning",
|
|
3141
|
+
"stage": "check",
|
|
3142
|
+
"since": 1,
|
|
3143
|
+
"message": "This condition is {value} on every bar.",
|
|
3144
|
+
"placeholders": {
|
|
3145
|
+
"value": "true or false, whichever it folds to"
|
|
3146
|
+
},
|
|
3147
|
+
"cause": "A condition built only from literals and constant options has one answer for the whole run, so one branch is dead. It is usually a test that was pinned during debugging and left behind.",
|
|
3148
|
+
"fix": "Restore the test that was meant, or delete the branch that never runs.",
|
|
3149
|
+
"autofix": false,
|
|
3150
|
+
"example": {
|
|
3151
|
+
"before": "if true\n signal(\"BUY\")",
|
|
3152
|
+
"after": "if crossUp(fast, slow)\n signal(\"BUY\")"
|
|
3153
|
+
},
|
|
3154
|
+
"spec": "language.md 10.2",
|
|
3155
|
+
"refines": null,
|
|
3156
|
+
"test": "tests/errors/OS8017"
|
|
3157
|
+
},
|
|
3158
|
+
{
|
|
3159
|
+
"code": "OS8018",
|
|
3160
|
+
"title": "An input is never used",
|
|
3161
|
+
"severity": "warning",
|
|
3162
|
+
"stage": "check",
|
|
3163
|
+
"since": 1,
|
|
3164
|
+
"message": "The input {title} is declared at line {line} and never read.",
|
|
3165
|
+
"placeholders": {
|
|
3166
|
+
"title": "the input's title, quoted",
|
|
3167
|
+
"line": "the line it is declared on"
|
|
3168
|
+
},
|
|
3169
|
+
"cause": "The input still builds a row in the settings dialog, so a reader can change it and nothing happens, which is worse than the setting not being there at all.",
|
|
3170
|
+
"fix": "Use the name the input assigns, or delete the input() and its dialog row.",
|
|
3171
|
+
"autofix": false,
|
|
3172
|
+
"example": {
|
|
3173
|
+
"before": "len = input(20, \"Length\")\nplot(ema(close, 9), \"EMA\", aqua)",
|
|
3174
|
+
"after": "len = input(20, \"Length\")\nplot(ema(close, len), \"EMA\", aqua)"
|
|
3175
|
+
},
|
|
3176
|
+
"spec": "language.md 13.4",
|
|
3177
|
+
"refines": null,
|
|
3178
|
+
"test": "tests/errors/OS8018"
|
|
3179
|
+
},
|
|
3180
|
+
{
|
|
3181
|
+
"code": "OS8019",
|
|
3182
|
+
"title": "A deleted object is still held",
|
|
3183
|
+
"severity": "warning",
|
|
3184
|
+
"stage": "check",
|
|
3185
|
+
"since": 1,
|
|
3186
|
+
"message": "{name} still holds the {kind} deleted at line {line}.",
|
|
3187
|
+
"placeholders": {
|
|
3188
|
+
"name": "the name or the array that refers to the deleted object",
|
|
3189
|
+
"kind": "the object kind: line, label, box or polyline",
|
|
3190
|
+
"line": "the line the delete is written on"
|
|
3191
|
+
},
|
|
3192
|
+
"cause": "An object lives until the script deletes it, and deleting it touches neither the name nor the array element that refers to it: there is no collection of unreachable objects, because unreachable and no longer wanted are different facts and only the script knows the second. What is left is stale rather than absent, so the next setter that reaches it stops the bar with OS4005, usually many bars after the line that caused it.",
|
|
3193
|
+
"fix": "Assign none to the name on the same path as the delete, and where the object came out of an array, remove the element as well as deleting the object.",
|
|
3194
|
+
"autofix": false,
|
|
3195
|
+
"example": {
|
|
3196
|
+
"before": "var zones: array<box> = []\npush(zones, draw.box(time, low, time, high))\nif size(zones) > 20\n draw.delete(element(zones, 0))",
|
|
3197
|
+
"after": "var zones: array<box> = []\npush(zones, draw.box(time, low, time, high))\nif size(zones) > 20\n draw.delete(element(zones, 0))\n shift(zones)"
|
|
3198
|
+
},
|
|
3199
|
+
"spec": "language.md 5.4",
|
|
3200
|
+
"refines": null,
|
|
3201
|
+
"test": "tests/errors/OS8019"
|
|
3202
|
+
}
|
|
3203
|
+
]
|
|
3204
|
+
}
|