Bài 9 — Chiến lược đầu tiên
- 🇻🇳 Tiếng Việt
- 🇬🇧 English
- 🇰🇷 한국어
- 🇨🇳 中文
Bạn sẽ học: chuyển từ chỉ báo sang chiến lược — khai báo khi nào mua, khi nào bán, thêm dừng lỗ, chốt lời, dừng lỗ kéo theo, giữ tối đa bao lâu; vào lệnh bằng lệnh giới hạn / lệnh dừng; hiểu lệnh được khớp thế nào.
Các chiến lược trong chương là ví dụ để học cách viết, không phải khuyến nghị đầu tư. Chiến lược là lệnh giấy: script không có đường nào tới tài khoản giao dịch thật.
9.1. Khung của một chiến lược
strategy({
name: "Giá cắt MA 50",
params: { len: param.int(50, { min: 10, max: 200, label: "Chu kỳ MA" }) },
signals: (d, p) => {
const ma = ta.sma(d.close, p.len);
return {
entry: d.close.crossAbove(ma),
exit: d.close.crossBelow(ma),
};
},
plot: (d, p) => ({ [`MA ${p.len}`]: line(ta.sma(d.close, p.len), { color: "ref" }) }),
});
| Phần | Vai trò |
|---|---|
strategy({ … }) | Khai báo chiến lược (thay cho indicator) |
signals: (d, p) => ({ entry, exit }) | entry — điều kiện mua; exit — điều kiện bán (đóng vị thế). Cả hai là chuỗi điều kiện như ở Bài 3 |
plot | Tuỳ chọn: vẽ thêm đường để thấy vì sao có tín hiệu. Chiến lược mặc định vẽ chồng lên giá |
Thêm vào biểu đồ: tín hiệu mua hiện ▲, tín hiệu bán ▼. Thẻ Kiểm thử chiến lược chạy chiến lược trên nến của biểu đồ và hiện lợi nhuận, các giao dịch (Bài 10).

Ảnh trên là chiến lược ở 9.1 chạy trên FPT toàn bộ lịch sử: 140 giao dịch, tỷ lệ thắng 23 %, lỗ so với vốn và thua xa Mua và giữ — cắt MA 50 đơn thuần cho rất nhiều tín hiệu giả khi giá đi ngang. Đó là lý do các bài sau thêm bộ lọc, quản lý rủi ro và kiểm thử cẩn thận.
9.2. Tín hiệu được thực hiện thế nào
- Tín hiệu xét ở nến đã đóng; lệnh khớp ở giá mở cửa của nến kế tiếp (đổi bằng
execution, Tham chiếu). - Đang không có vị thế mà
entryđúng → mua. Đang có vị thế màexitđúng → bán. Cùng một nến có cảentryvàexitthì không mua. - Mặc định mỗi lần mua dùng toàn bộ vốn (1 tỷ đồng), làm tròn lô 100 cổ phiếu, không mua thêm khi đang giữ.
- Luật thị trường Việt Nam áp tự động theo loại mã: cổ phiếu T+2 (hàng mua về chiều ngày T+2 mới bán được; tín hiệu bán sớm hơn thì chờ), biên độ giá theo sàn, không khớp khi trắng bên ở giá trần / sàn, phí 0,15 % mỗi chiều, thuế bán 0,1 %, trượt giá 0,1 %. Hợp đồng tương lai (VN30F…) được bán khống, tính phí theo hợp đồng.
Thiếu exit, dấu ▼ trên biểu đồ hiện khi tín hiệu mua hết đúng, nhưng bảng kiểm thử giữ vị thế cho tới khi chạm dừng lỗ / chốt lời / hết thời gian giữ trong risk. Viết rõ exit để hai nơi khớp nhau.
9.3. Dừng lỗ, chốt lời, dừng lỗ kéo theo
risk quản lý vị thế sau khi mua, độc lập với exit:
strategy({
name: "EMA 20/50 có quản lý rủi ro",
params: {
fast: param.int(20, { min: 5, max: 60, label: "EMA nhanh" }),
slow: param.int(50, { min: 20, max: 200, label: "EMA chậm" }),
},
signals: (d, p) => {
const f = ta.ema(d.close, p.fast);
const s = ta.ema(d.close, p.slow);
return { entry: f.crossAbove(s), exit: f.crossBelow(s) };
},
risk: {
stopLoss: 0.07,
takeProfit: 0.25,
trailingStop: 0.1,
maxHoldBars: 60,
},
});
risk | Nghĩa |
|---|---|
stopLoss: 0.07 | Bán khi giá giảm 7 % so với giá mua |
takeProfit: 0.25 | Bán khi lãi 25 % |
trailingStop: 0.1 | Bán khi giá giảm 10 % so với mức cao nhất từ lúc mua |
maxHoldBars: 60 | Bán sau 60 nến giữ (nến của khung đang tính) |
stopMode: "close" / "intraday" | "close" (mặc định): xét ngưỡng theo giá đóng cửa, bán ở nến sau. "intraday": khớp ngay tại ngưỡng trong nến (mở cửa đã vượt ngưỡng thì khớp giá mở cửa) |
Số dạng tỷ lệ: 0,07 = 7 %. Muốn người dùng chỉnh được, viết risk thành hàm của tham số:
strategy({
name: "Bứt phá có dừng lỗ chỉnh được",
params: {
n: param.int(55, { min: 10, max: 120, label: "Đỉnh n phiên" }),
sl: param.pct(0.08, { min: 0.02, max: 0.2, label: "Dừng lỗ" }),
trail: param.pct(0.15, { min: 0.05, max: 0.3, label: "Dừng lỗ kéo theo" }),
},
signals: (d, p) => ({
entry: d.close.gt(d.high.highest(p.n).shift(1)),
exit: d.close.lt(d.low.lowest(20).shift(1)),
}),
risk: (p) => ({ stopLoss: p.sl, trailingStop: p.trail }),
});