Lỗi thường gặp
- 🇻🇳 Tiếng Việt
- 🇬🇧 English
- 🇰🇷 한국어
- 🇨🇳 中文
Mỗi mục dưới ghi đúng chữ FireAnt Terminal báo, vì sao xảy ra và cách sửa. Đoạn mã có dấu ✗ là cách viết sai (để bạn nhận ra), ngay sau là cách viết đúng.
Lỗi hiện ở đâu
| Nơi | Khi nào | Ví dụ |
|---|---|---|
| Gạch đỏ trong trình soạn | Ngay khi gõ — kiểm cú pháp và kiểu theo đặc tả ngôn ngữ. Rê chuột lên chỗ gạch để đọc | Không có tên 'len' (gõ sai, hay quên khai báo?) |
| Dải đỏ trên trình soạn | Khi chạy (Thêm vào biểu đồ / Ch ạy lại), kèm số dòng, tô dòng lỗi và nút Sửa giúp tôi (nhờ Copilot sửa — Copilot) | Lỗi cú pháp: …, Lỗi: …, Vượt ngân sách: … |
| Thẻ Nhật ký | Lỗi khi chạy, kèm số dòng; và dòng bạn ghi bằng ctx.log | |
| Cảnh báo vàng | Script chạy nhưng có vấn đề | Script không vẽ gì: … |
Gặp gạch đỏ thì sửa trước khi chạy: có những lỗi không làm script dừng nhưng cho kết quả sai (mục Dùng >, && với chuỗi).
Dùng >, <, &&, || với chuỗi
Trình soạn báo: Không dùng toán tử '>' cho kiểu 'Series' và 'number' — với chuỗi dùng .add() / .sub() / .gt()…
Vì sao: chuỗi là cả cột số; toán tử của JavaScript so sánh đối tượng, không so từng nến. Script vẫn chạy nhưng sai: r > 70 luôn là false (dấu không bao giờ hiện), a && b trả về b (bỏ mất điều kiện a).
// ✗ sai — chạy được nhưng dấu không bao giờ hiện
indicator({
name: "RSI trên 70 (sai)",
plot: (d) => {
const r = ta.rsi(d.close, 14);
return { RSI: line(r), "Trên 70": shape(r > 70, { at: "top" }) };
},
});
indicator({
name: "RSI trên 70 (đúng)",
plot: (d) => {
const r = ta.rsi(d.close, 14);
return { RSI: line(r), "Trên 70": shape(r.gt(70), { at: "top" }) };
},
});
Bảng đổi: > → .gt(), >= → .gte(), < → .lt(), <= → .lte(), == → .eq(), != → .neq(), && → .and(), || → .or(), ! → .not(). Trong onBar thì ngược lại: giá trị là số, dùng toán tử bình thường.
A[0]: cần chuỗi hoặc số
Vì sao: cộng / trừ chuỗi bằng +, - — JavaScript biến chuỗi thành chữ ("[object Object]1"), đầu ra nhận một thứ không phải chuỗi.
// ✗ lỗi: param — dùng + với chuỗi
indicator({
name: "Giá cộng 1 (sai)",
plot: (d) => ({ A: line(d.close + 1) }),
});
indicator({
name: "Giá cộng 1 (đúng)",
plot: (d) => ({ A: line(d.close.add(1)) }),
});
shift(n) với n âm dùng dữ liệu tương lai — không được phép
Vì sao: shift(-1) đọc nến sau — tín hiệu sẽ "biết trước". Tương tự: ctx.bar: barsAgo phải ≥ 0 (không đọc được nến sau), ctx.prev: barsAgo phải ≥ 0 …, ctx.track(...).at(k): k phải ≥ 0 ….
// ✗ lỗi: lookahead — so với giá ngày mai
indicator({
name: "Ngày mai tăng (sai)",
pane: "price",
plot: (d) => ({ "Tăng": shape(d.close.shift(-1).gt(d.close), { at: "below" }) }),
});
Sửa: chỉ so với quá khứ — "hôm nay tăng so với hôm qua" là d.close.gt(d.close.shift(1)). Muốn biết tín hiệu sau đó giá đi thế nào, dùng bảng Kiểm thử chiến lược — không đưa vào chỉ báo.
Không có tên 'x' (gõ sai, hay quên khai báo?)
Khi chạy: Lỗi: len is not defined. Vì sao: dùng một tên chưa khai báo — thường là quên p. trước tên tham số, gõ sai chữ hoa / thường, hoặc dùng biến khai báo trong hàm khác.
// ✗ lỗi: runtime — quên p. trước tham số
indicator({
name: "Quên p. (sai)",
params: { len: param.int(14) },
plot: (d, p) => ({ RSI: line(ta.rsi(d.close, len)) }),
});
indicator({
name: "Có p. (đúng)",
params: { len: param.int(14) },
plot: (d, p) => ({ RSI: line(ta.rsi(d.close, p.len)) }),
});
ta.rsii is not a function — Kiểu '…' không có thuộc tính '…'
Vì sao: gõ sai tên hàm / thuộc tính. Trình soạn gợi ý: Kiểu 'typeof ta' không có thuộc tính 'rsii' — có phải 'rsi'?. Gõ ta. rồi chờ danh sách gợi ý để chọn đúng tên; tra cứu ở Tham chiếu.
// ✗ lỗi: runtime — gõ sai tên hàm
indicator({ name: "Sai tên hàm", plot: (d) => ({ RSI: line(ta.rsii(d.close, 14)) }) });
Lỗi cú pháp: Unexpected token …
Vì sao: thiếu / thừa dấu ngoặc, dấu phẩy, dấu nháy. Dải đỏ chỉ đúng dòng, cột. Hay gặp:
- quên dấu phẩy giữa hai mục của
{ … }; plot: (d) => { A: line(…) }— hàm trả đối tượng phải bọc ngoặc tròn:(d) => ({ A: line(…) });- thừa dấu chấm (
d.close.), ngoặc không đóng.
// ✗ lỗi: syntax — thừa dấu chấm
indicator({ name: "Thừa dấu chấm", plot: (d) => ({ A: line(d.close.) }) });
Script cần gọi một trong indicator(...), strategy(...), screen(...), research(...)
Vì sao: script không gọi indicator({ … }) hay strategy({ … }) — có thể đang chỉ có vài dòng tính toán, hoặc gõ sai tên (Indicator).
// ✗ lỗi: no-declaration
const r = 1;
Script gọi 2 lần indicator / strategy / screen / research — mỗi script chỉ một: mỗi script đúng một khai báo. Muốn hai chỉ báo, viết hai script (một biểu đồ gắn được 5 script) hoặc gộp đầu ra vào một plot.
// ✗ lỗi: multiple-declarations
indicator({ name: "Một", plot: (d) => ({ A: line(d.close) }) });
indicator({ name: "Hai", plot: (d) => ({ B: line(d.open) }) });
plot: "A" phải là line(…), hist(…), band(…)…
Vì sao: giá trị trong plot là một chuỗi chứ không phải đầu ra. Bọc bằng line(…), hist(…)…
// ✗ lỗi: runtime
indicator({ name: "Thiếu line", plot: (d) => ({ A: ta.sma(d.close, 20) }) });
Script không vẽ gì
Cảnh báo: Script không vẽ gì: giá trị onBar trả về chỉ hiện khi plot dùng tham số thứ ba o, vd onBar trả { "Tên": v } thì plot: (d, p, o) => ({ "Tên": line(o["Tên"]) }).
Vì sao: onBar trả giá trị nhưng plot không dùng chúng.
// ✗ sai — onBar tính nhưng không vẽ
indicator({
name: "Quên vẽ (sai)",
state: () => ({ n: 0 }),
onBar: (ctx, s) => {
s.n = ctx.close > ctx.open ? s.n + 1 : 0;
return { n: s.n };
},
plot: () => ({}),
});
indicator({
name: "Có vẽ (đúng)",
state: () => ({ n: 0 }),
onBar: (ctx, s) => {
s.n = ctx.close > ctx.open ? s.n + 1 : 0;
return { n: s.n };
},
plot: (d, p, o) => ({ "Nến tăng liên tiếp": hist(o.n) }),
});
Biểu đồ trống còn có thể vì: điều kiện không bao giờ đúng (kiểm bằng cột Tại nến), chu kỳ quá dài so với số nến đang có (SMA 500 trên biểu đồ có 300 nến), hoặc dùng dữ liệu chưa có trên biểu đồ (d.fin, khối ngoại… — Bài 8).
o.x: script không có onBar
Vì sao: dùng đối số o trong plot / alerts khi script không có onBar — o chỉ chứa đầu ra của onBar.
// ✗ lỗi: runtime
indicator({ name: "o không có onBar", plot: (d, p, o) => ({ A: line(o.x) }) });
ctx.ta: lần gọi thứ … trước là …, nến này là …
Thông báo đầy đủ: ctx.ta: lần gọi thứ 0 trước là sma:5, nến này là ema:10 — gọi ctx.ta ở mọi nến theo cùng thứ tự, hoặc truyền key khi gọi trong nhánh.
Vì sao: gọi ctx.ta.* trong if / else nên thứ tự gọi khác nhau giữa các nến.
// ✗ lỗi: ta-order
indicator({
name: "ctx.ta trong nhánh (sai)",
onBar: (ctx) => {
const v = ctx.close > ctx.open ? ctx.ta.ema(ctx.close, 10) : ctx.ta.sma(ctx.close, 5);
return { v };
},
plot: (d, p, o) => ({ V: line(o.v) }),
});
indicator({
name: "ctx.ta ngoài nhánh (đúng)",
onBar: (ctx) => {
const ema = ctx.ta.ema(ctx.close, 10);
const sma = ctx.ta.sma(ctx.close, 5);
return { v: ctx.close > ctx.open ? ema : sma };
},
plot: (d, p, o) => ({ V: line(o.v) }),
});
Hoặc giữ trong nhánh nhưng truyền khoá riêng: ctx.ta.ema(ctx.close, 10, "nhanh-tang").
state.f: trạng thái onBar chỉ chứa dữ liệu … không chứa hàm
Vì sao: state() trả đối tượng có hàm, Date, hay lớp tự viết. Trạng thái phải là dữ liệu thuần (số, chữ, mảng, đối tượng thường) — vì nó được sao chép mỗi lần giá mới. Đặt hàm ở ngoài state.
// ✗ lỗi: state-not-json
indicator({
name: "Hàm trong state (sai)",
state: () => ({ tinh: (x) => x * 2 }),
onBar: (ctx, s) => ({ v: ctx.close }),
plot: (d, p, o) => ({ V: line(o.v) }),
});
d.tf("60") nhỏ hơn khung đang tính 1D — chưa hỗ trợ
Vì sao: d.tf chỉ nhận khung lớn hơn khung của biểu đồ. Hay gặp khi script viết cho biểu đồ phút (dùng d.tf("60")) được mở trên biểu đồ ngày. Sửa: đổi khung biểu đồ, dùng khung lớn hơn, hoặc — nếu thật sự cần nến nhỏ bên trong mỗi nến — dùng d.ltf("60") (Khung nhỏ hơn).
// ✗ lỗi: tf-lower — biểu đồ ngày không có nến 60 phút
indicator({ name: "Khung nhỏ hơn", plot: (d) => ({ A: line(d.tf("60").close) }) });
rank() là phép cắt ngang giữa các mã — chỉ dùng được ở dạng rổ (Studio)
Vì sao: rank, topN, zscore, sumAcross… so giữa nhiều mã; biểu đồ chỉ có một mã. Viết trong Quant AI Studio, hoặc dùng bản theo thời gian trên một mã: x.rolling(n).rank(), x.rolling(n).zscore().
// ✗ lỗi: universe-only
indicator({ name: "Xếp hạng giữa các mã", plot: (d) => ({ A: line(d.close.pct(20).rank()) }) });
indicator({ name: "Hạng theo thời gian (đúng)", plot: (d) => ({ "Hạng 250 phiên": line(d.close.pct(20).rolling(250).rank()) }) });
data: đọc giá trị lúc dựng chỉ có ở dạng rổ (Studio) — ở biểu đồ dùng onBar(ctx, state)
Vì sao: đọc giá trị của chuỗi ngay trong plot (.data, .last(), .get()) — trên biểu đồ giá trị chỉ có khi tính từng nến. Muốn xử lý từng giá trị: onBar (đọc ctx.v.x) hoặc x.map((v) => …).
// ✗ lỗi: eager-in-stream
indicator({
name: "Đọc giá trị lúc dựng (sai)",
plot: (d) => {
const cuoi = d.close.last();
return { A: line(d.close) };
},
});
fillna(value) cần một số — hline(value) cần một số
Vì sao: truyền chữ hoặc chuỗi vào chỗ cần số. hline chỉ nhận số cố định — mức thay đổi theo nến dùng line(…).
// ✗ lỗi: param
indicator({ name: "hline nhận chữ", plot: (d) => ({ A: hline("70") }) });
indicator({ name: "Mức động dùng line", plot: (d) => ({ "MA 20": line(ta.sma(d.close, 20)), "Đỉnh 20": line(d.high.highest(20)) }) });
Các lỗi tham số khác: d.source: nguồn "…" không có (open, high, low, close, volume, hl2, hlc3, ohlc4), param.session("9-11"): khung giờ dạng "0900-1130", d.macro cần mã chuỗi vĩ mô, ví dụ d.macro("cpi_yoy"), ctx.ta.ema: n cần số nguyên ≥ 1.
use("…"): chưa nạp thư viện này — kiểm tra id và phiên bản trong Script của tôi
Vì sao: dòng use("<id>@<phiên bản>") trỏ tới thư viện (hoặc phiên bản) không có trong Script của tôi — gõ tay sai id, thư viện đã xoá, hoặc script được chép từ máy / người khác. Mở Thư viện › Của tôi, chọn thư viện, bấm Chép dòng use (bản …) rồi dán lại. Các lỗi cùng loại (mã import): use(…) cần chuỗi "<id>@<phiên bản>" (khoá phải là chuỗi viết sẵn, không dùng biến), Thư viện dùng vòng tròn: …, Thư viện … gọi indicator(…) — thư viện chỉ khai báo library({ name, exports }) — Thư viện.
Tối đa 4 mã khác mỗi script
Mỗi script đọc tối đa 4 mã khác qua d.symbol (không tính VNINDEX của d.bench). Ngoài ra: plot có … đầu ra — tối đa 64, alerts có … điều kiện — tối đa 32, Trạng thái onBar vượt 256 KB — Giới hạn.
Script chạy quá 2 giây … — đã dừng
Thông báo: Vượt ngân sách: Script chạy quá 2 giây khi tính toàn bộ lịch sử — đã dừng. Có thể do vòng lặp quá nặng hoặc vô hạn. (khi cập nhật giá: … quá 50 ms mỗi lần cập nhật theo tick …).
Vì sao và cách sửa: vòng lặp lồng nhau trong onBar (mỗi nến lặp lại cả trăm nến trước), mảng trong state dài mãi không cắt, while không dừng. Thay vòng lặp bằng phép chuỗi (highest, rolling), cắt mảng (shift() khi quá dài) — xem Hỏi đáp › Hiệu năng.
Kiểm thử dành cho chiến lược
Kiểm thử dành cho chiến lược — tạo bằng Mới ▸ Chiến lược mới hoặc chọn mẫu chiến lược ở Thư viện.: script đang là indicator. Đổi thành strategy({ … }) với signals (hoặc lệnh giấy trong onBar) — Bài 9.
Chiến lược kiểm thử trên biểu đồ cần signals(d, p) hoặc onBar (lệnh giấy): strategy thiếu cả hai. Chiến lược onBar chạy trong bảng kiểm thử khi đã Thêm vào biểu đồ (Lệnh trong onBar).
Kiểm thử chạy xong nhưng không có giao dịch nào
- Điều kiện
entrykhông bao giờ đúng: thêm tạmplotvẽshape(entry…)để thấy, hoặc xem số ▲ trên biểu đồ. - Tối ưu / Walk-forward ghi Bỏ khỏi lưới vì không đủ nến khởi động: … hoặc báo Biểu đồ có … nến, không đủ cho đoạn khởi động của mọi giá trị "…" trong lưới:
maxcủa tham số quá lớn — mọi tổ hợp chấm sau đoạn khởi động của tổ hợp dài nhất (EMA 300 cần khoảng 6.300 nến), giá trị không chừa đủ nến để chấm bị bỏ. Hạmax— Bài 11. - Kiểm thử bắt đầu sau đoạn khởi động: chỉ báo đệ quy cần khoảng 20 × chu kỳ nến (EMA 200 cần ~4.000 nến; MACD của nến tuần cần hơn 500 tuần). Biểu đồ ít lịch sử (mã mới niêm yết, khung phút) có thể hết nến trước khi kiểm thử bắt đầu — dùng chu kỳ ngắn hơn, SMA thay EMA, hoặc xem khung lớn hơn.
- Kỳ quá ngắn (3 năm) cho chiến lược dài hạn: chọn Toàn bộ lịch sử.
Đường dùng d.fin, khối ngoại, d.flow… trống
d.fin,d.macro, khối ngoại, tự doanh, định giá (trường Quant): biểu đồ FireAnt Chart chưa đưa các dữ liệu này vào script — không phải lỗi của bạn. Dùng chúng khi Lọc bằng script trên rổ có tên hoặc trong Quant AI Studio — Bài 12.d.flow,d.footprint(dòng tiền chủ động): cần gói Chuyên nghiệp; khung giây không có.d.symbol,d.bench,d.ltf: biểu đồ tự nạp nến; đường trống một lúc rồi hiện khi nạp xong. Mã gõ sai (không có nến) thì trống mãi; nến cũ hơn dữ liệu 1 phút thì phép gộp củad.ltfra NaN.
Each entry below shows the exact text FireAnt Terminal displays, why it happens and how to fix it. Code marked ✗ is the wrong way (so you can recognize it), followed right after by the correct way.
Where errors appear
| Where | When | Example |
|---|---|---|
| Gạch đỏ trong trình soạn (red underline in the editor) | As you type — syntax and types are checked against the language spec. Hover over the underline to read it | Không có tên 'len' (gõ sai, hay quên khai báo?) (No name 'len' — typo, or forgot to declare it?) |
| Dải đỏ trên trình soạn (red bar above the editor) | When you run (Thêm vào biểu đồ (Add to chart) / Chạy lại (Run again)), with the line number, the error line highlighted and a Sửa giúp tôi (Fix it for me) button (asks Copilot to fix it — Copilot) | Lỗi cú pháp: … (Syntax error), Lỗi: … (Error), Vượt ngân sách: … (Budget exceeded) |
| Thẻ Nhật ký (Log tab) | Runtime errors, with the line number; plus lines you write with ctx.log | |
| Cảnh báo vàng (yellow warning) | The script runs but something is off | Script không vẽ gì: … (Script draws nothing) |
Fix red underlines before you run: some errors do not stop the script but give wrong results (see Using >, && with series).
Using >, <, &&, || with series
The editor reports: Không dùng toán tử '>' cho kiểu 'Series' và 'number' — với chuỗi dùng .add() / .sub() / .gt()… (Operator '>' cannot be used on 'Series' and 'number' — for series use .add() / .sub() / .gt()…)
Why: a series is a whole column of numbers; JavaScript operators compare objects, not each bar. The script still runs but is wrong: r > 70 is always false (the marker never shows), and a && b returns b (dropping condition a).
// ✗ sai — chạy được nhưng dấu không bao giờ hiện
indicator({
name: "RSI trên 70 (sai)",
plot: (d) => {
const r = ta.rsi(d.close, 14);
return { RSI: line(r), "Trên 70": shape(r > 70, { at: "top" }) };
},
});
indicator({
name: "RSI trên 70 (đúng)",
plot: (d) => {
const r = ta.rsi(d.close, 14);
return { RSI: line(r), "Trên 70": shape(r.gt(70), { at: "top" }) };
},
});
Conversion table: > → .gt(), >= → .gte(), < → .lt(), <= → .lte(), == → .eq(), != → .neq(), && → .and(), || → .or(), ! → .not(). Inside onBar it is the opposite: values are numbers, so use the normal operators.
A[0]: needs a series or a number
Why: adding / subtracting series with +, - — JavaScript turns the series into text ("[object Object]1"), so the output receives something that is not a series.
// ✗ lỗi: param — dùng + với chuỗi
indicator({
name: "Giá cộng 1 (sai)",
plot: (d) => ({ A: line(d.close + 1) }),
});
indicator({
name: "Giá cộng 1 (đúng)",
plot: (d) => ({ A: line(d.close.add(1)) }),
});
shift(n) with negative n uses future data — not allowed
Why: shift(-1) reads the next bar — the signal would "know in advance". Similarly: ctx.bar: barsAgo phải ≥ 0 (không đọc được nến sau) (barsAgo must be ≥ 0 — cannot read later bars), ctx.prev: barsAgo phải ≥ 0 …, ctx.track(...).at(k): k phải ≥ 0 ….
// ✗ lỗi: lookahead — so với giá ngày mai
indicator({
name: "Ngày mai tăng (sai)",
pane: "price",
plot: (d) => ({ "Tăng": shape(d.close.shift(-1).gt(d.close), { at: "below" }) }),
});
Fix: compare only with the past — "up today versus yesterday" is d.close.gt(d.close.shift(1)). To see how price moves after a signal, use the Kiểm thử chiến lược (Strategy tester) panel — do not put it into the indicator.
No name 'x' (typo, or forgot to declare it?)
At runtime: Lỗi: len is not defined. Why: you used a name that was never declared — usually you forgot p. before a parameter name, got the upper/lower case wrong, or used a variable declared in another function.
// ✗ lỗi: runtime — quên p. trước tham số
indicator({
name: "Quên p. (sai)",
params: { len: param.int(14) },
plot: (d, p) => ({ RSI: line(ta.rsi(d.close, len)) }),
});
indicator({
name: "Có p. (đúng)",
params: { len: param.int(14) },
plot: (d, p) => ({ RSI: line(ta.rsi(d.close, p.len)) }),
});
ta.rsii is not a function — Type '…' has no property '…'
Why: a misspelled function / property name. The editor suggests: Kiểu 'typeof ta' không có thuộc tính 'rsii' — có phải 'rsi'? (Type 'typeof ta' has no property 'rsii' — did you mean 'rsi'?). Type ta. and wait for the suggestion list to pick the right name; look it up in the Reference.
// ✗ lỗi: runtime — gõ sai tên hàm
indicator({ name: "Sai tên hàm", plot: (d) => ({ RSI: line(ta.rsii(d.close, 14)) }) });
Syntax error: Unexpected token …
Why: a missing / extra bracket, comma or quote. The red bar points to the exact line and column. Common cases:
- a missing comma between two entries of
{ … }; plot: (d) => { A: line(…) }— a function returning an object must wrap it in parentheses:(d) => ({ A: line(…) });- an extra dot (
d.close.), an unclosed bracket.
// ✗ lỗi: syntax — thừa dấu chấm
indicator({ name: "Thừa dấu chấm", plot: (d) => ({ A: line(d.close.) }) });
The script must call one of indicator(...), strategy(...), screen(...), research(...)
Why: the script does not call indicator({ … }) or strategy({ … }) — it may contain only a few calculation lines, or the name is misspelled (Indicator).
// ✗ lỗi: no-declaration
const r = 1;
Script gọi 2 lần indicator / strategy / screen / research — mỗi script chỉ một (Script calls indicator / strategy / screen / research twice — only one per script): each script has exactly one declaration. For two indicators, write two scripts (one chart can hold 5 scripts) or merge the outputs into a single plot.
// ✗ lỗi: multiple-declarations
indicator({ name: "Một", plot: (d) => ({ A: line(d.close) }) });
indicator({ name: "Hai", plot: (d) => ({ B: line(d.open) }) });
plot: "A" must be line(…), hist(…), band(…)…
Why: a value in plot is a series rather than an output. Wrap it with line(…), hist(…)…
// ✗ lỗi: runtime
indicator({ name: "Thiếu line", plot: (d) => ({ A: ta.sma(d.close, 20) }) });
Script draws nothing
Warning: Script không vẽ gì: giá trị onBar trả về chỉ hiện khi plot dùng tham số thứ ba o, vd onBar trả { "Tên": v } thì plot: (d, p, o) => ({ "Tên": line(o["Tên"]) }). (Script draws nothing: values returned by onBar only show when plot uses the third parameter o, e.g. if onBar returns { "Name": v } then plot: (d, p, o) => ({ "Name": line(o["Name"]) }).)
Why: onBar returns values but plot does not use them.
// ✗ sai — onBar tính nhưng không vẽ
indicator({
name: "Quên vẽ (sai)",
state: () => ({ n: 0 }),
onBar: (ctx, s) => {
s.n = ctx.close > ctx.open ? s.n + 1 : 0;
return { n: s.n };
},
plot: () => ({}),
});
indicator({
name: "Có vẽ (đúng)",
state: () => ({ n: 0 }),
onBar: (ctx, s) => {
s.n = ctx.close > ctx.open ? s.n + 1 : 0;
return { n: s.n };
},
plot: (d, p, o) => ({ "Nến tăng liên tiếp": hist(o.n) }),
});
An empty chart can also be because: the condition is never true (check it with the Tại nến (At bar) column), the period is too long for the number of bars available (SMA 500 on a chart with 300 bars), or you used data not yet available on the chart (d.fin, foreign investors… — Lesson 8).
o.x: the script has no onBar
Why: you used the o argument in plot / alerts when the script has no onBar — o only contains the output of onBar.
// ✗ lỗi: runtime
indicator({ name: "o không có onBar", plot: (d, p, o) => ({ A: line(o.x) }) });
ctx.ta: call #… was previously …, on this bar it is …
Full message: ctx.ta: lần gọi thứ 0 trước là sma:5, nến này là ema:10 — gọi ctx.ta ở mọi nến theo cùng thứ tự, hoặc truyền key khi gọi trong nhánh (ctx.ta: call #0 was previously sma:5, on this bar it is ema:10 — call ctx.ta on every bar in the same order, or pass a key when calling inside a branch).
Why: ctx.ta.* is called inside if / else, so the call order differs between bars.
// ✗ lỗi: ta-order
indicator({
name: "ctx.ta trong nhánh (sai)",
onBar: (ctx) => {
const v = ctx.close > ctx.open ? ctx.ta.ema(ctx.close, 10) : ctx.ta.sma(ctx.close, 5);
return { v };
},
plot: (d, p, o) => ({ V: line(o.v) }),
});
indicator({
name: "ctx.ta ngoài nhánh (đúng)",
onBar: (ctx) => {
const ema = ctx.ta.ema(ctx.close, 10);
const sma = ctx.ta.sma(ctx.close, 5);
return { v: ctx.close > ctx.open ? ema : sma };
},
plot: (d, p, o) => ({ V: line(o.v) }),
});
Or keep it inside the branch but pass a unique key: ctx.ta.ema(ctx.close, 10, "nhanh-tang").
state.f: onBar state may only contain data … not functions
Why: state() returns an object containing functions, Date, or a custom class. State must be plain data (numbers, text, arrays, plain objects) — because it is copied on every new price. Put functions outside state.
// ✗ lỗi: state-not-json
indicator({
name: "Hàm trong state (sai)",
state: () => ({ tinh: (x) => x * 2 }),
onBar: (ctx, s) => ({ v: ctx.close }),
plot: (d, p, o) => ({ V: line(o.v) }),
});
d.tf("60") is smaller than the current timeframe 1D — not supported yet
Why: d.tf only accepts timeframes larger than the chart's timeframe. This often happens when a script written for a minute chart (using d.tf("60")) is opened on a daily chart. Fix: change the chart timeframe, use a larger timeframe, or — if you really need the smaller bars inside each bar — use d.ltf("60") (Lower timeframe).
// ✗ lỗi: tf-lower — biểu đồ ngày không có nến 60 phút
indicator({ name: "Khung nhỏ hơn", plot: (d) => ({ A: line(d.tf("60").close) }) });
rank() is a cross-sectional operation across tickers — only available in basket mode (Studio)
Why: rank, topN, zscore, sumAcross… compare across many tickers; a chart has only one ticker. Write it in Quant AI Studio, or use the time-based version on a single ticker: x.rolling(n).rank(), x.rolling(n).zscore().
// ✗ lỗi: universe-only
indicator({ name: "Xếp hạng giữa các mã", plot: (d) => ({ A: line(d.close.pct(20).rank()) }) });
indicator({ name: "Hạng theo thời gian (đúng)", plot: (d) => ({ "Hạng 250 phiên": line(d.close.pct(20).rolling(250).rank()) }) });