Đi tới nội dung chính
Chuyển tới nội dung chính

Chiến lược

Các mục của strategy({...}) quyết định khi nào mua / bán và kiểm thử khớp lệnh thế nào. Hướng dẫn từng bước: Bài 9, Bài 10, Bài 11.

Mọi cấu hình (risk, portfolio, execution, costs, rules) viết được dạng đối tượng hoặc hàm của tham số: risk: (p) => ({ stopLoss: p.sl }).

signals​

signals: (d, p) => dieuKien | { entry, exit?, score?, limit?, stop?, short? }

KhoáKiểuNghĩa
entryđiều kiện, bắt buộcMua khi đang không giữ
exitđiều kiệnBán (đóng vị thế) khi đang giữ. Cùng nến có entry và exit thì không mua
scorechuỗi | sốƯu tiên khi nhiều tín hiệu hơn chỗ trống (rổ nhiều mã trong Studio); một mã trên biểu đồ không dùng
limitchuỗi | số (giá)Lệnh vào thành lệnh giới hạn ở mức này cho nến kế tiếp (chỉ khớp nếu giá xuống tới mức)
stopchuỗi | số (giá)Lệnh vào thành lệnh dừng ở mức này (chỉ khớp nếu giá vượt lên mức). Có cả limit và stop: lệnh dừng – giới hạn
short{ entry, exit?, limit?, stop? }Vị thế bán khống — chỉ mã cho phép (hợp đồng tương lai); đang mua mà có tín hiệu bán khống thì đảo chiều

Trả thẳng một điều kiện thì đó là entry. Trên biểu đồ, tín hiệu hiện ▲ (vào) / ▼ (ra) theo cách: vào khi entry đúng lúc chưa giữ, ra khi exit đúng lúc đang giữ (thiếu exit: ra khi entry hết đúng — riêng bảng kiểm thử thì giữ tới khi risk đóng vị thế).

filter​

filter: (d, p) => dieuKien — chỉ cho vào lệnh ở nến điều kiện đúng (như thêm .and(filter) vào entry). Trong Studio kết hợp với lọc thanh khoản của rổ.

risk​

KhoáNghĩa
stopLossDừng lỗ: bán khi giá giảm stopLoss (tỷ lệ) so với giá vốn
takeProfitChốt lời: bán khi lãi takeProfit
trailingStopDừng lỗ kéo theo: bán khi giá giảm trailingStop so với mức cao nhất từ lúc mua
maxHoldBarsGiữ tối đa số nến (của khung đang tính)
maxHoldDaysTên cũ của Quant: số phiên
stopMode"close" (mặc định): xét ngưỡng theo giá đóng cửa, bán ở nến sau. "intraday": khớp tại ngưỡng trong nến (giá mở cửa đã vượt ngưỡng → khớp giá mở cửa)

Lý do ra lệnh hiện ở thẻ Giao dịch: Tín hiệu bán, Dừng lỗ, Chốt lời, Dừng lỗ kéo theo, Hết thời gian…

portfolio​

KhoáMặc địnhNghĩa
capital1.000.000.000Vốn ban đầu (đồng)
sizing"equal"Mỗi lần mua: "equal" = toàn bộ vốn cho mã đang xem (rổ trong Studio: vốn ÷ số vị thế tối đa); số 0–1 = tỷ trọng vốn
pyramiding1Số lần được vào cùng chiều (1 = không mua thêm khi đang giữ)
maxPositions—Số mã giữ cùng lúc (rổ trong Studio)
cashRate0Lãi suất năm cho tiền nhàn rỗi

execution​

KhoáMặc địnhNghĩa
at"open"Lệnh thị trường từ tín hiệu khớp giá mở cửa ("open") hoặc đóng cửa ("close")
delay1Khớp ở nến thứ delay sau nến tín hiệu. at: "close", delay: 0 = khớp ngay giá đóng cửa nến tín hiệu (lạc quan)
intrabar"ohlc"Đường đi giá trong nến khi xét lệnh giới hạn / dừng: O→H→L→C nếu cao gần mở hơn, ngược lại O→L→H→C. "1m": dùng nến 1 phút khi có
validBars1Lệnh giới hạn / dừng từ signals.limit / stop còn hiệu lực bao nhiêu nến

costs​

KhoáMặc định cổ phiếuMặc định phái sinhNghĩa
fees0,00150Phí mỗi chiều (tỷ lệ giá trị)
sellTax0,0010Thuế bán
slippage0,0011 bước giáTrượt giá mỗi lệnh
impact0,010Trượt thêm = impact × √(KL lệnh ÷ KL TB 20) — lệnh càng lớn so với thanh khoản càng trượt
feePerContract—5.000Phái sinh: phí mỗi hợp đồng mỗi chiều (đồng)

rules​

Luật thị trường áp theo loại mã (market: "auto"): cổ phiếu Việt Nam ("vn-equity"), phái sinh ("vn-derivative"), hoặc "none" (không luật).

KhoáMặc định cổ phiếuNghĩa
market"auto"Bộ luật
settlement2Số phiên thanh toán (T+2)
sellFrom"afternoon"Hàng về bán được từ chiều T+2 (nến ngày: ATC T+2); "next-open": mở cửa T+3
lotSize100Lô giao dịch
priceLimit"exchange"Biên độ theo sàn (HOSE 7 %, HNX 10 %, UPCoM 15 %); số tự đặt; null bỏ biên độ
tickSize"exchange"Bước giá theo sàn; số tự đặt; null không làm tròn
limitLocktrueKhông mua được khi trắng bên bán ở giá trần, không bán được khi trắng bên mua ở giá sàn
retryBlockedBuysfalseLệnh mua bị chặn (trần, thiếu tiền…) có thử lại ở nến sau không
maxAdvPct0,1Mỗi lệnh tối đa tỷ lệ này của KL trung bình 20 phiên trước (0 = không giới hạn)
allowShortfalse (cổ phiếu)Cho bán khống
flatAt—Phái sinh: đóng hết vị thế từ giờ này ("14:25")
multiplier, marginRate—Phái sinh: đồng mỗi điểm, tỷ lệ ký quỹ
strategy({
name: "Chiến lược khai báo đủ cấu hình",
params: {
n: param.int(20, { min: 10, max: 60, label: "Đỉnh n nến" }),
sl: param.pct(0.06, { min: 0.02, max: 0.15, label: "Dừng lỗ" }),
},
filter: (d) => d.close.gt(ta.sma(d.close, 100)),
signals: (d, p) => ({
entry: d.close.gt(d.high.highest(p.n).shift(1)),
exit: d.close.lt(ta.ema(d.close, 20)),
limit: d.close.mul(1.01),
}),
risk: (p) => ({ stopLoss: p.sl, takeProfit: 0.3, trailingStop: 0.12, maxHoldBars: 60, stopMode: "close" }),
portfolio: { capital: 500000000, sizing: 0.8, pyramiding: 1, cashRate: 0.02 },
execution: { at: "open", delay: 1, intrabar: "ohlc", validBars: 2 },
costs: { fees: 0.0015, sellTax: 0.001, slippage: 0.0015, impact: 0.01 },
rules: { market: "auto", settlement: 2, sellFrom: "afternoon", lotSize: 100, priceLimit: "exchange", tickSize: "exchange", limitLock: true, retryBlockedBuys: false, maxAdvPct: 0.1 },
plot: (d, p) => ({ [`Đỉnh ${p.n} nến trước`]: line(d.high.highest(p.n).shift(1), { color: "up", step: true, dash: "dash" }) }),
});

Cách khớp lệnh khi kiểm thử​

  1. Tín hiệu tính ở nến đã đóng t (không nhìn trước).
  2. Lệnh thị trường khớp theo execution (mặc định giá mở cửa nến t + 1), cộng trượt giá; lệnh giới hạn / dừng xét ở nến t + 1 theo đường đi giá trong nến.
  3. Khối lượng: theo sizing, làm tròn xuống lô, không quá maxAdvPct khối lượng trung bình; thiếu tiền → mua ít hơn.
  4. Luật: không mua ở trần khi trắng bên bán, không bán ở sàn khi trắng bên mua; hàng chưa về (T+2) chưa bán được — lệnh bán chờ.
  5. risk xét mỗi nến sau khi mua.
  6. Trong một nến, thứ tự chạm giá mặc định theo đường đi O–H–L–C ước lượng (lệnh khớp kiểu này được đánh dấu "ước lượng"). Ô Khớp trong nến › Kính lúp nến 1 phút của bảng kiểm thử (hoặc execution.intrabar: "1m") khớp theo nến 1 phút thật bên trong; nến cũ hơn dữ liệu 1 phút lùi về đường đi ước lượng. Ô Trễ lệnh (1–3 nến) đổi nến khớp của lệnh thị trường cho lần chạy đó, không sửa mã.

Kết quả kiểm thử​

Chỉ sốNghĩa
Lợi nhuậnLãi / lỗ cả kỳ ÷ vốn ban đầu, sau phí, thuế, trượt giá
CAGRLợi nhuận kép năm
SharpeLợi nhuận vượt lãi suất phi rủi ro (3 %/năm) ÷ độ biến động, quy năm
Sụt giảm tối đaMức giảm sâu nhất của vốn từ đỉnh
Số giao dịch, Tỷ lệ thắngSố lần mua – bán đã đóng; phần trăm có lãi
Profit factorTổng lãi ÷ tổng lỗ
Thời gian có vị thếPhần trăm thời gian đang giữ
MAE, MFE (mỗi giao dịch)Lỗ / lãi tạm lớn nhất trong lúc giữ
Mua và giữĐường vốn nếu mua ở đầu kỳ và giữ tới cuối — để so

Mục dành cho Quant AI Studio​

Trên biểu đồ chiến lược luôn chạy trên mã đang xem; các mục dưới được bỏ qua và dùng khi Mở trong Quant AI Studio.

MụcNghĩa
universeRổ mã: universe("VN30"), universe("HOSE").liquid(5e9), universe(["FPT", "HPG"]), universe({ industry: "Ngân hàng" }), universe({ watchlist: "Tên" }). Nối thêm .exchange("HOSE"), .industry(…), .exclude([...]), .include([...]), .minPrice(10), .top(50), .where((m) => …). Chi tiết: Quant AI Studio › Rổ mã
period{ from: "2018-01-01", to? } — kỳ kiểm thử
benchmarkChỉ số so sánh (mặc định VNINDEX; null = không)
warmupSố phiên khởi động (mặc định 250)
rank + select, weights, rebalanceChiến lược xoay vòng / tỷ trọng theo rổ Studio — Quant AI Studio › Chiến lược
optimize{ objective?: "sharpe" | "cagr" | "calmar" | "sortino", grid?: { tham_số: [giá trị…] }, maxCombos?, tests?: ["oos", "walkforward", "montecarlo", "costs", "delay", "regimes"], walkForward?: { folds, anchored, trainPct }, oos?: { splitPct } } — tối ưu và kiểm định độ vững trong Studio. Thẻ Tối ưu trên biểu đồ dùng min / max của tham số

Lệnh trong onBar (lệnh giấy)​

Thay cho signals, chiến lược viết được bằng lệnh giấy trong onBar (như strategy.entry / strategy.exit của Pine) — khi logic vào / ra cần trạng thái theo từng nến:

LệnhNghĩa
ctx.buy(qty?, opts?), ctx.sell(qty?, opts?)Lệnh mua / bán; opts như ctx.order (type, price, stopPrice, tif…). Thiếu qty: theo portfolio.sizing
ctx.order({ side, type, qty, price, stopPrice, tif, oco, reduceOnly, comment })Đặt / sửa lệnh; trả id. type: thị trường, giới hạn, dừng, dừng – giới hạn; tif: "bar" (một nến), "day" (hết phiên), "gtc" (tới khi huỷ); oco: nhóm huỷ lẫn nhau
ctx.exit({ id?, from?, qty?, limit?, stop?, trailPct?, trailPoints? })Lệnh thoát gắn với vị thế: chốt lời limit, cắt lỗ stop, dừng kéo theo — cùng một nhóm OCO, chỉ giảm vị thế. Gọi lại cùng id = sửa
ctx.closePosition({ qty? })Đóng vị thế (hoặc một phần) bằng lệnh thị trường
ctx.cancel(id), ctx.cancelAll()Huỷ lệnh chờ
ctx.positionside ("flat", "long", "short"), qty, sellable (bán được ngay — cổ phiếu đã qua T+2), avgPrice, openPnl, openPnlPct, bars, entryTime
ctx.orders, ctx.equity, ctx.cashLệnh đang có (status, lý do bị từ chối: lot, price-limit, t2, short, cash…), vốn, tiền mặt

Lệnh đặt ở nến t xét khớp từ nến t + 1 theo cùng luật thị trường và execution như signals; nến khởi động (ctx.warmup) không đặt lệnh. Bảng Kiểm thử chiến lược chạy kiểu này khi chiến lược đã Thêm vào biểu đồ (cả Tối ưu, Walk-forward). Chiến lược onBar chưa dùng được cho Lọc bằng script, và không có dấu ▲ / ▼ tín hiệu như signals — lệnh khớp hiện trên biểu đồ sau khi chạy kiểm thử.

// Chiến lược onBar: mua khi vượt đỉnh 20 phiên, thoát bằng cắt lỗ 2 × ATR dưới giá vốn hoặc dừng kéo theo 10 %
strategy({
name: "Bứt phá đỉnh 20 phiên, cắt lỗ theo ATR",
series: (d) => ({ dinh: d.high.highest(20).shift(1), atr: ta.atr(d, 14) }),
onBar: (ctx) => {
if (ctx.position.side === "flat" && ctx.close > ctx.v.dinh) ctx.buy();
if (ctx.position.side === "long") ctx.exit({ id: "thoat", stop: ctx.position.avgPrice - 2 * ctx.v.atr, trailPct: 0.1 });
},
});