Tham chiếu API
API công khai của các lớp cấp cao nhất: Chart, ChartWidget, ChartWidgetGrid và ChartGrid.
● DemoSo sánh, chênh lệch và tỷ lệDữ liệu thị trường giả lập, sinh ngay trong trình duyệt của bạnChart
Bộ render headless. Bạn tự làm giao diện, đăng ký nghe sự kiện và thay đổi trạng thái bằng các lời gọi trực tiếp.
Khởi tạo
new Chart(host: HTMLElement, options?: ChartOptions) Dữ liệu
| Phương thức | Công dụng |
|---|---|
setData(data) | Thay toàn bộ chuỗi dữ liệu. |
appendBar(bar) | Thêm một nến mới; tự cuộn nếu được bật. |
appendBars(bars) | Thêm hàng loạt; chỉ tính lại chỉ báo một lần. |
updateLastBar(bar) | Sửa nến đang hình thành. |
updateLastBarFromTick(tick) | Gộp một tick vào nến cuối. |
getData() | Đọc chuỗi OHLC gốc. |
Thời gian của nến là epoch mili-giây hoặc giây, một đơn vị cho cả chuỗi. Biểu đồ đọc đơn vị một lần, từ nến mới nhất: barTimeUnit(data) trả về 1 (mili-giây, từ 1e11 trở lên) hoặc 1000 (giây). Vì vậy một chuỗi mili-giây giữ đúng ngày cả trước năm 2001, dù các giá trị đó nhỏ hơn 1e12. Hãy giữ một chuỗi ở một đơn vị; thời gian biểu đồ trả lại (sự kiện, getData()) dùng đơn vị đó.
Loại biểu đồ & giao diện
| Phương thức | Công dụng |
|---|---|
setChartType(type) | Một trong 18 loại — xem Loại biểu đồ. |
setTheme(name | theme) | Chuyển giữa các giao diện có sẵn, hoặc đặt theme của bạn. getTheme() trả lại đúng như đã đặt. |
applyOverrides(patch, { layer }) | Đặt style bất kỳ phần nào của biểu đồ theo khoá ('grid.vertical.visible', 'series.candlestick.upColor'…); null bỏ một khoá. Xem Styling. |
setOverrides(overrides, { layer }) · resetOverrides(keys?, { layer }) | Cả một lớp một lúc; bỏ các khoá (hoặc tất cả). |
getOverrides({ layer }) · getStyle() · getStyleValue(key) | Override của một lớp; giao diện đã phân giải; giá trị một khoá ra. |
setTimeframe(tf) | Đổi khung thời gian đang dùng; nối lại luồng dữ liệu trực tiếp. |
Bộ vẽ
| Phương thức | Công dụng |
|---|---|
Tuỳ chọn renderer | 'canvas' (mặc định), 'webgl', hoặc 'auto' (chỉ dùng WebGL khi có GPU phần cứng). |
setRenderer(mode) | Đổi khi đang chạy. Trả về bộ đang vẽ: 'canvas' nếu không có WebGL 2. |
getRenderer() | 'canvas' hoặc 'webgl'. |
GPU vẽ những gì, và số đo: Hiệu năng → Bộ vẽ WebGL.
Chỉ báo
| Phương thức | Công dụng |
|---|---|
addIndicator(id, params?, position?) | Thêm một chỉ báo phủ lên biểu đồ hoặc ở bảng riêng. Trả về id của instance. |
updateIndicator(instanceId, params) | Sửa một chỉ báo đang chạy. |
removeIndicator(instanceId) | Xoá và dọn dẹp chỉ báo. |
updateIndicatorStyle(instanceId, style) | Màu, độ dày, độ mờ, và kiểu nét, ẩn/hiện của từng đường (plots: { signal: { lineStyle: 'dashed' } }). |
setIndicatorDefaults(id, style | null) | Style mà các chỉ báo cùng loại bắt đầu với, từ nay. |
setPaneStyle(instanceId, { background, separator } | null) · getPaneStyle(instanceId) | Giao diện riêng của một pane, lưu cùng chỉ báo của nó. |
Trục & thang
Trục giá (dải bên phải) và trục thời gian (dải bên dưới) nhận thao tác chuột trực tiếp, với các cử chỉ mà trader đã quen:
| Cử chỉ | Tác dụng |
|---|---|
| Kéo trục giá lên / xuống | Thu hẹp / mở rộng khoảng giá theo chiều dọc (tắt tự co giãn thang). |
| Kéo trục thời gian sang trái / phải | Phóng to / thu nhỏ trục thời gian. |
| Bấm đúp vào trục giá | Bật lại tự co giãn thang. |
| Bấm đúp vào trục thời gian | Vừa khít toàn bộ dữ liệu vào khung nhìn. |
Múi giờ. Nhãn trục thời gian và nhãn giờ của con trỏ chữ thập mặc định theo múi giờ cục bộ của trình duyệt; chuyển sang một độ lệch UTC cố định (hoặc trở về giờ máy) từ bảng cài đặt hoặc gọi trực tiếp:
chart.setTimezoneOffset(-300) // EST (UTC-5), in minutes
chart.setTimezoneOffset(330) // IST (UTC+5:30)
chart.setTimezoneOffset(null) // back to browser-local Các tác dụng trên cũng có thể gọi bằng code:
chart.setAutoScale(false) // freeze the current price range
chart.setLogScale(true) // switch to logarithmic price scale
chart.setInvertScale(true) // upside down (Alt+I in the widget)
chart.fitContent() // zoom out to all data
chart.scrollToEnd()
chart.setVisibleRangePreset('3M') // 1D 5D 1M 3M 6M YTD 1Y 5Y All
chart.setMinBarUnit(0.05) // narrower bars, so 'All' fits a long series (0.25 by default)
chart.goToTime(Date.UTC(2025, 0, 1)) // centre that bar (Alt+G in the widget) Khoảng dài. Mỗi nến không hẹp hơn 0,25 px, nên một khoảng dài hơn mức đó (26 năm nến ngày trên laptop) chỉ hiện phần mới nhất. Trong widget, một khoảng dài trên thanh khoảng thời gian sẽ chuyển sang khung có số nến vừa màn hình, từ 2 px mỗi nến: 5Y nến ngày sang nến tuần, All sang nến tháng; khoảng ngắn thì trở về khung bạn đã chọn, không bao giờ mịn hơn. Nó chỉ chuyển sang khung mà nguồn dữ liệu của bạn có (hoặc nến hiện có gộp lên được). features: { 'statusBar.rangeTimeframe': false } giữ nguyên khung. Với Chart dùng trực tiếp, minBarUnit (tuỳ chọn, hoặc setMinBarUnit) cho nến hẹp đến 0,02 px, vẽ theo từng cột pixel.
Chế độ thang giá. Ngoài thang thường và logarit, trục giá
có thể tính lại nhãn so với nến đầu tiên đang hiện — percentage hiện % thay đổi, indexedTo100 quy mốc về 100. Thang thường,
phần trăm và quy về 100 dùng chung một hình học tuyến tính; chỉ có nhãn
là khác. Đặt từ bảng cài đặt biểu đồ hoặc gọi trực tiếp:
chart.setScaleMode('percentage') // axis labels: +12.34% from first visible bar
chart.setScaleMode('indexedTo100') // first visible bar reads as 100
chart.setScaleMode('logarithmic')
chart.getScaleMode() Định dạng giá và thời gian
Giá có thể hiển thị theo định dạng của riêng bạn, hoặc theo phân số của một điểm như cách yết
giá trái phiếu và hợp đồng tương lai của chúng (101'16 là 101 và 16/32). Định dạng
này áp dụng cho mọi chỗ in giá theo thang giá: trục, con trỏ chữ thập, nhãn giá cuối, chú thích,
tooltip, lệnh, cảnh báo và nhãn hình vẽ; trục đặt vạch đúng vào các phân số chẵn và roundPrice làm tròn theo chúng. Các bảng chỉ báo giữ cách hiển thị số riêng. Thời
gian cũng hiển thị được theo cách của bạn; hàm định dạng được cho biết nhãn đang in là loại nào.
new Chart(host, { priceFormat: { denominator: 32 } }) // 101'16
chart.setPriceFormat({ denominator: 32, subDenominator: 2 }) // 101'165: 16½ 32nds
chart.setPriceFormat((p) => ' Cách các trục chọn nhãn
Trục thời gian gắn nhãn cho những nến mở đầu bước lịch lớn nhất: một năm (chữ đậm), một tháng, một ngày, một giờ tròn, cách nhau ít nhất 80 px. Vì vậy ở 5Y trục đọc 2022 · Jul · 2023, ở một tháng Oct · 8 · 15, trong ngày 12:00 · 18:00. Nhãn thuộc về nến chứ không thuộc về màn hình: kéo biểu đồ thì nhãn đi theo nến, nạp thêm lịch sử phía trước thì nhãn vẫn ở chỗ cũ. Bên trái nến cũ nhất không có nhãn. Tên tháng theo setNumberLocale, và các đường lưới nằm đúng chỗ nhãn.
Thang giá đặt số nhãn vừa với chiều cao của nó, cách nhau ít nhất 30 px, theo bước giá của thị trường. Ở thang log bước tăng theo giá, nên phần dưới cũng có nhãn như phần trên; ở thang phần trăm hay chỉ số, nhãn là phần trăm hoặc điểm chỉ số tròn tính từ mốc. Các pane chỉ báo cũng đếm nhãn theo chiều cao của chúng.
Thị trường: bước giá, biên độ và phiên giao dịch
setMarket cho biểu đồ biết quy định của một sàn: màu nến, số chữ số thập
phân, bước giá (có thể đổi theo vùng giá), giá trần và giá sàn trong ngày, và lô giao
dịch. roundPrice và getPriceStep theo bước giá của vùng giá, nên
giá lấy từ menu của biểu đồ luôn là giá hợp lệ, và phiếu đặt lệnh của widget kiểm tra
lệnh theo bước giá, biên độ trong ngày và lô chẵn. Preset còn mang các phiên và múi giờ
của sàn, dùng cho getCurrentSession và code của bạn; vùng tô phiên và giờ mở
rộng trên biểu đồ lấy từ setSymbolInfo. Có sẵn
preset cho các sàn Việt Nam theo quy định tháng 5/2025: HOSE (bước 10, 50 hoặc 100 đồng
theo vùng giá, ±7%), chứng chỉ quỹ ETF và chứng quyền trên HOSE (10 đồng), HNX (±10%, có
phiên sau giờ), UPCoM (±15%) và hợp đồng tương lai VN30 (bước 0,1 điểm, ±7%, mở cửa
8:45); cổ phiếu giao dịch theo lô 100, xanh tăng, đỏ giảm, tím trần, xanh lam sàn, vàng
tham chiếu. Giá trong các preset tính bằng nghìn đồng như trên bảng giá; scaleMarketPrices(preset, 1000) dùng cho giá tính bằng đồng.
import {
MARKET_HOSE, scaleMarketPrices, priceStepAt, roundToPriceStep, getCurrentSession,
vn30fContracts, vn30fLastTradingDay,
} from '@tradecanvas/chart'
chart.setMarket(MARKET_HOSE) // hoặc scaleMarketPrices(MARKET_HOSE, 1000) cho giá 25350
chart.setPriceLimits(25.35) // giá tham chiếu: giá đóng cửa hôm trước
chart.getPriceLimits() // { ceiling: 27.1, floor: 23.6, reference: 25.35 }
chart.roundPrice(25.37) // 25.35, bước 50 đồng
chart.getPriceStep(9.5) // 0.01 dưới 10.000 đồng
// Dùng riêng các quy tắc
priceStepAt(25.35, MARKET_HOSE) // 0.05
roundToPriceStep(10.486, MARKET_HOSE, 'down') // 10.45
getCurrentSession(MARKET_HOSE.sessions, Date.now(), MARKET_HOSE.timeZone) // { name: 'ATC', type: 'preClose', … }, chỉ theo giờ
// Hợp đồng tương lai VN30: bốn hợp đồng đang niêm yết và ngày giao dịch cuối cùng
vn30fContracts(Date.now()) // [{ symbol: 'VN30F2611', lastTradingDay: '2026-11-19', … }, …]
vn30fLastTradingDay(2026, 12, (day) => holidays.has(day)) // thứ Năm thứ ba, lùi lại nếu là ngày nghỉ
Thị trường của riêng bạn là một MarketConfig: priceStep, hoặc priceSteps: [{ from, step }] cho các vùng giá, priceLimits, volumeUnit (lô), sessions, timeZone và colorScheme. minTick của mã đi trước bước giá của thị trường.
Các sàn thỉnh thoảng đổi quy định; hãy đối chiếu preset với quy định hiện hành của sàn
trước khi dựa vào nó.
So sánh các mã
Phần trăm thay đổi của một mã khác trên thang giá (addCompareSymbol), hoặc giá của
nó trên thang riêng, trong bảng riêng, hoặc chênh lệch hay tỷ lệ giữa giá đóng cửa của biểu đồ
và mã đó — mấy cách sau là chỉ báo (compareSymbol, spread), có chú
thích, nhãn giá trị, cảnh báo và được lưu trong bố cục như mọi chỉ báo khác. Nến của mã kia được
khớp với nến của biểu đồ theo thời gian. Biểu đồ tự yêu cầu những nến nó cần; hãy cung cấp lại
sau khi đổi khung thời gian. correlation (theo giá đóng cửa) và correlationlog (theo lợi suất từng nến) đọc mã kia theo cùng cách, cho giá trị từ −1 đến 1.
chart.addCompareSymbol('eth', 'ETHUSDT', ethBars, '#7c4dff') // percent change, on this scale
chart.addIndicator('compareSymbol', { symbol: 'ETHUSDT' }, 'bottom', { scale: 'left' }) // own scale
chart.addIndicator('spread', { symbol: 'ETHUSDT', mode: 'ratio' }) // own pane
chart.addIndicator('correlation', { symbol: 'ETHUSDT', length: 20 }) // −1 to 1, own pane
chart.on('symbolSeriesRequest', async ({ payload }) =>
chart.setSymbolSeries(payload.symbol, await adapter.fetchHistory(payload.symbol, '1h', 1000)))
chart.getRequiredSymbols() // what to fetch again on a new interval
chart.setPaneScale(spreadId, { percent: true }) // a pane in percent of its first value
Trong ChartWidget, nút so sánh trong cây đối tượng hỏi mã, rồi hỏi cách so sánh: thay đổi
phần trăm, thang riêng, pane riêng, chênh lệch, tỷ lệ, hoặc tương quan theo giá hay theo lợi suất.
Xuất dữ liệu
Xuất các nến dưới dạng CSV hoặc JSON, mỗi đường chỉ báo nằm trong một cột riêng, đặt tên theo
tên trên chú thích. ChartWidget có mục Xuất dữ liệu (CSV) trong menu chuột phải
của biểu đồ.
chart.exportAllData('csv', 'btc-1h.csv') // every bar loaded
chart.exportVisibleData('json', undefined, { indicators: false })
const text = chart.getExportText('csv', { range: 'visible' })
const { bars, columns } = chart.getExportData() // columns: { name, values }[]
Hồ sơ khối lượng (Volume Profile)
Biểu đồ tần suất nằm ngang của khối lượng giao dịch, gom theo mức giá trong khoảng
đang hiện. Mặc định tắt — bật bằng code hoặc qua bảng cài đặt
của widget:
chart.setVolumeProfileVisible(true)
chart.setVolumeProfileConfig({
buckets: 48, // resolution of the histogram
widthRatio: 0.18, // % of chart width
opacity: 0.32,
highlightPoC: true, // mark the highest-volume bucket
})
Điểm đảo chiều (pivot)
Đánh dấu các đỉnh/đáy sóng kiểu fractal bằng tam giác nhỏ (▼ phía trên một đỉnh pivot
đã xác nhận, ▲ phía dưới một đáy pivot). Độ mạnh quy định phải có bao nhiêu nến thấp hơn
ở mỗi bên. Bật từ bảng cài đặt, hoặc:
chart.setPivotMarkersVisible(true)
chart.setPivotMarkersConfig({ left: 5, right: 5, showLabels: true })
// market-structure labels (HH / HL / LH / LL) instead of price
chart.setPivotMarkersConfig({ structureLabels: true })
// pure detection + classification are exported
import { findPivots, classifyPivots } from '@tradecanvas/core'
const pivots = findPivots(bars, 5, 5) // [{ index, price, type }]
const structure = classifyPivots(pivots) // adds label: 'HH'|'LH'|'HL'|'LL'
Tô màu phiên (giờ giao dịch chính thức)
Làm mờ các nến nằm ngoài phiên chính (trước/sau giờ giao dịch hoặc khoảng nghỉ qua đêm)
để phiên chính nổi bật. Mặc định là giờ giao dịch chính thức (RTH) của cổ phiếu Mỹ (09:30–16:00 giờ New
York, đã tính giờ mùa hè); cấu hình khung giờ theo số phút trong ngày
cùng múi giờ của thị trường. Mã nào có nguồn dữ liệu báo kèm phiên giao dịch sẽ
tự đặt phiên.
chart.setSessionShadingVisible(true)
chart.setSessionShadingConfig({
startMinute: 9 * 60 + 30, // 09:30
endMinute: 16 * 60, // 16:00 (end-exclusive; end < start wraps midnight)
timeZone: 'America/New_York', // or tzOffsetMinutes: -300 for a fixed offset
})
Giờ giao dịch mở rộng. Khi tắt, các nến nằm ngoài giờ giao dịch chính của mã
(SymbolInfo.sessions theo timezone của mã) rời khỏi biểu đồ; chúng được
cất riêng, cả nến trực tiếp lẫn các trang lịch sử, và trở lại khi bật lên. Nến từ một ngày trở
lên được giữ nguyên.
chart.setSymbolInfo({ symbol: 'AAPL', timezone: 'America/New_York', sessions: [{ start: '09:30', end: '16:00' }] })
chart.setExtendedHours(false) // or new Chart(host, { extendedHours: false })
chart.isExtendedHoursVisible()
Mức của kỳ trước (PDH / PDL / PDC)
Vẽ giá cao, thấp và đóng cửa của ngày (hoặc tuần) trước, cùng giá mở cửa của kỳ
hiện tại, thành các đường ngang có nhãn — những mức hỗ trợ/kháng cự
mà trader trong ngày theo dõi. Bật từ bảng cài đặt, hoặc gọi trực tiếp:
chart.setPeriodLevelsVisible(true)
chart.setPeriodLevelsPeriod('week') // 'day' (PDH/PDL/PDC) | 'week' (PWH/PWL/PWC)
// pure computation is exported
import { computePeriodLevels } from '@tradecanvas/core'
const levels = computePeriodLevels(bars, 'day') // [{ id, label, price }]
Hồ sơ thị trường (TPO)
Biểu đồ tần suất thời gian tại mỗi mức giá: mỗi nến đóng góp một TPO vào mọi ô giá
mà biên độ của nó chạm tới, làm lộ ra Point of Control (mức giá có nhiều TPO nhất) và
vùng giá trị (≈70% số TPO). Khác với Hồ sơ khối lượng — nó tính theo thời gian,
không theo khối lượng — và được ghim bên trái nên cả hai có thể hiện cùng lúc. Mặc định tắt;
bật từ bảng cài đặt hoặc gọi trực tiếp:
chart.setMarketProfileVisible(true)
chart.setMarketProfileConfig({
buckets: 48,
widthRatio: 0.18,
opacity: 0.32,
valueAreaPct: 0.7, // fraction of TPOs in the value area
highlightPoC: true, // dashed line at the point of control
})
// split into one mini-profile per calendar-day session
chart.setMarketProfileConfig({ splitBySession: true })
// classic TPO letters per session (when zoomed in enough to be legible)
chart.setMarketProfileConfig({ splitBySession: true, letters: true })
// pure computation is exported too
import { computeMarketProfile, computeSessionProfiles } from '@tradecanvas/core'
const profile = computeMarketProfile(bars, priceMin, priceMax, { buckets: 48 })
const sessions = computeSessionProfiles(bars, priceMin, priceMax) // per-day TPO
Cảm ứng & di động
Cử chỉ Thao tác Kéo 1 ngón (vùng biểu đồ) Kéo biểu đồ + di chuyển con trỏ chữ thập Chụm 2 ngón Phóng to quanh điểm giữa Nhấn giữ (~500 ms) Ghim chú thích OHLC tại nến (bản di động của Alt+bấm) Kéo 1 ngón trong dải trục giá / thời gian Co giãn trục tương ứng Chạm Bấm: click, barClick, một signal marker; chạm lên hình vẽ thì chọn nó Chạm đúp Bấm đúp: trên trục thì đặt lại trục; trên hình vẽ thì mở cài đặt của nó Kéo 1 ngón trên hình vẽ Di chuyển nó, hoặc đổi dạng bằng một tay nắm của hình đang chọn Khi đang chọn công cụ vẽ: chạm, chạm — hoặc nhấn, kéo, nhấc Đặt các điểm; nhấn, kéo rồi nhấc ngón vẽ xong đường hai điểm trong một lần Nhấn giữ trên hình vẽ Menu của nó (như bấm chuột phải) Kéo 1 ngón trên đường cảnh báo, lệnh, bracket hoặc đường chia pane Kéo nó
Ngón tay với xa hơn chuột: khoảng 16 px tới một đường và 22 px tới một tay nắm, so với 8. Trên trang có cuộn, pageScroll: true để vuốt dọc và con lăn chuột thường cho trang: biểu đồ kéo theo vuốt ngang và phóng to bằng hai ngón hoặc Ctrl + con lăn (chụm trên touchpad), còn nhấn lên hình vẽ, các đường và trục vẫn thuộc về biểu đồ. chart.setPageScroll(on) bật tắt khi đang chạy.
Các hộp thoại (cài đặt, bảng phím tắt, bảng lệnh, tìm mã) tự động
chuyển sang dạng bảng trượt từ dưới lên, có tay nắm để kéo và phần đệm tính đến vùng an toàn
của màn hình khi khung nhìn hẹp hơn 640 px.
Công cụ đo
Giữ Shift và kéo trên biểu đồ để đo số nến × giá giữa
hai điểm — lớp phủ hiện chênh lệch giá Δ (tuyệt đối + %), số nến và khoảng
thời gian. Lớp phủ biến mất ngay khi nhả chuột; nó không được
lưu vào trạng thái.
Sự kiện
Mọi sự kiện đều có kiểu qua ChartEventMap:
chart.on('orderPlace', e => /* OrderPlacePayload */)
chart.on('orderModify', e => /* OrderModifyPayload */)
chart.on('signalMarkerAdd', e => /* { marker } */)
chart.on('tradeZoneAdd', e => /* { zone } */)
chart.on('dataUpdate', e => /* { length } */)
chart.on('ordersChange', e => /* { orders } */)
chart.on('positionsChange', e => /* { positions } */)
chart.on('executionFill', e => /* { side, price, quantity, reason, pnl } */)
chart.on('chartContextMenu', e => /* { area, x, y, price, time } */)
chart.on('stateChange', () => /* hình vẽ, chỉ báo, cảnh báo, loại biểu đồ hoặc giao diện có thể đã thay đổi */)
chart.on('paneChange', e => /* { instanceId, change: 'collapsed' | 'maximized' | 'order' } */)
chart.on('chartTypeChange', e => /* { type, previous } */)
chart.on('symbolChange', e => /* { symbol, previous } */)
chart.on('timeframeChange', e => /* { timeframe, previous } */)
chart.on('historyChange', e => /* { canUndo, canRedo } */)
chart.on('drawingSelect', e => /* { ids, primary } */)
chart.on('rendererChange', e => /* { renderer: 'canvas' | 'webgl', reason?: 'unsupported' | 'contextLost' | 'contextRestored' | 'limit' } */)
Hoàn tác cho thay đổi của riêng bạn
recordUndo đưa một thay đổi của bạn vào lịch sử hoàn tác của biểu đồ, cùng với hình vẽ và
chỉ báo: Ctrl/Cmd+Z gọi undo, làm lại thì gọi redo. Các thay đổi về cùng một subject diễn ra sát nhau được tính là một bước. ChartWidget ghi lại cài đặt và loại biểu đồ của nó theo cách này.
const before = panel.color
panel.color = 'red'
chart.recordUndo({ subject: 'panel-color', undo: () => (panel.color = before), redo: () => (panel.color = 'red') })
Bàn phím và trình đọc màn hình
Khi đang có focus, biểu đồ cuộn bằng các phím mũi tên (giữ Shift để cuộn mười nến), phóng to thu nhỏ bằng ↑/↓
hoặc +/−, và về đầu, về cuối bằng Home và End. Với trình đọc màn hình, biểu đồ là một ứng dụng có phần tóm tắt
(mã, loại biểu đồ, khung thời gian, giá cuối); sau khi các phím di chuyển khung nhìn, nó đọc những gì đang hiện trên
màn hình, và dấu phẩy, dấu chấm đọc từng nến một. a11y.labels chuyển các câu đó sang ngôn ngữ của bạn; a11y: false bỏ hẳn phần này. scrollBars(n) và selectDrawing(id) làm bằng code những gì các phím và một cú bấm chuột làm.
new Chart(host, { a11y: { labels: { role: 'gráfico', summary: '{what}. Último precio {close}.' } } })
Giá lấy từ con trỏ chuột có thể được làm tròn theo bước giá của thị trường bằng chart.roundPrice(price): về bội số của minTick của mã, nếu không có thì theo
độ chính xác của mã. Các menu, phiếu đặt lệnh và tab Coordinates trong cài đặt hình vẽ của ChartWidget đều làm như vậy. chart.getPriceStep() trả về chính bước giá đó.
ChartWidget
Bọc Chart trong một giao diện hoàn chỉnh. Có thể truy cập chính instance đó qua widget.chart.
import { ChartWidget } from '@tradecanvas/chart/widget'
const widget = new ChartWidget(host, {
symbol: 'BTCUSDT',
timeframe: '5m',
theme: 'dark',
adapter: new BinanceAdapter(),
historyLimit: 500,
trading: true,
features: { 'toolbar.replay': false }, // công tắc của widget: xem Tùy biến
onReady: (chart) => { /* ... */ },
})
widget.chart.setData(...)
widget.destroy()
Phím tắt của widget
Phím tắt Thao tác Ctrl / ⌘ + K Bảng lệnh (chỉ báo, loại biểu đồ, công cụ vẽ…) Ctrl / ⌘ + P Tìm mã — tìm gần đúng trong danh sách mã đã cấu hình Ctrl / ⌘ + S Lưu bố cục (lần đầu sẽ hỏi tên) 0–9 Gõ một khung thời gian (5, 15m, 1h, 1D) rồi nhấn Enter (intervalTyping: false để tắt) Alt + T / H / J / V / C / F Đường xu hướng, đường ngang, tia ngang, đường dọc, đường chữ thập, Fibonacci thoái lui ? Hiện bảng phím tắt Alt + bấm vào biểu đồ Ghim chú thích OHLC tại nến đang rê chuột (kèm chênh lệch so với vị trí con trỏ chữ thập hiện tại) Esc Bỏ ghim chú thích / huỷ hình đang vẽ Bấm vào mã trên thanh công cụ Mở hộp thoại tìm mã Bấm nút phát trên thanh công cụ Mở thanh điều khiển phát lại nến (phát/từng bước/tua/tốc độ)
Cập nhật danh sách mã có thể tìm lúc chạy bằng widget.setSymbols(['BTCUSDT', 'ETHUSDT', …]).
Cửa sổ dữ liệu
Một bảng nổi hiển thị chính xác O/H/L/C/V, mức thay đổi của nến và giá trị của mọi
chỉ báo đang bật tại nến đang rê chuột — cập nhật trực tiếp khi bạn di chuyển
con trỏ chữ thập. Bật/tắt từ bảng lệnh (Ctrl/⌘ K →
"Bật/tắt cửa sổ dữ liệu").
Chia sẻ góc nhìn (deep link)
Mã hoá toàn bộ góc nhìn — mã, khung thời gian, loại biểu đồ, thang giá,
chỉ báo (kèm tham số) và hình vẽ — thành một chuỗi gọn, an toàn cho URL để
tạo liên kết sâu. Với shareUrl: true, widget khôi phục hash #tcw=… khi tải trang, và thao tác "Chia sẻ góc nhìn" trong bảng lệnh
sao chép một liên kết vào clipboard.
const widget = new ChartWidget(host, { shareUrl: true })
const token = widget.exportState() // portable string
await widget.importState(token) // restore a view
await widget.copyShareLink() // copy "<url>#tcw=<token>"
Bố cục có tên
Nút bố cục trên thanh công cụ lưu biểu đồ dưới một cái tên: mã, khung thời gian,
thang giá, loại biểu đồ, chỉ báo, hình vẽ và cảnh báo (không gồm giao diện, vì đó
là lựa chọn của người xem). Mở, đổi tên và xoá bố cục từ menu của nút này; bố cục
đang mở tự lưu mỗi khi thay đổi, và Ctrl/⌘ S sẽ lưu nó. Bố cục được lưu
trong localStorage của trình duyệt này, trừ khi bạn truyền vào một storage: bốn hàm, hàm nào cũng có thể trả về một promise.
import { ChartWidget, type LayoutStorage } from '@tradecanvas/chart/widget'
const server: LayoutStorage = {
list: () => api.get('/layouts'), // [{ id, name, symbol, timeframe, updatedAt }]
load: (id) => api.get(`/layouts/${id}`), // { ...summary, content } hoặc null
save: (layout) => api.put(`/layouts/${layout.id}`, layout),
remove: (id) => api.delete(`/layouts/${id}`),
}
const widget = new ChartWidget(host, {
layouts: { storage: server, autoSave: true, openLast: true }, // hoặc false để tắt
})
const layouts = widget.getLayoutSession()!
await layouts.saveAs('Swing BTC')
await layouts.open(id)
layouts.current() // { id, name, … } hoặc null
layouts.setAutoSave(false)
// Chỉ lấy phần nội dung, để cất ở đâu tuỳ bạn
const json = widget.getLayoutContent()
await widget.applyLayoutContent(json)
localStorageLayouts(prefix) và memoryLayouts() là hai kiểu
lưu trữ có sẵn. Nội dung đã lưu được đọc một cách thận trọng: bố cục nào không phân
tích được sẽ bị từ chối, chứ không bị áp dụng nửa chừng. Mỗi bố cục ghi lại kind của nó ('chart' hoặc 'grid'), nên một widget
và một lưới có thể dùng chung một nơi lưu trữ mà mỗi bên chỉ liệt kê bố cục của riêng
mình. Việc lưu, mở và tự động lưu chạy lần lượt từng việc một, nên một lần lưu không bao
giờ ghi vào bố cục được mở sau nó.
Bố cục theo từng mã
Ngoài ra, có thể tự động lưu bộ chỉ báo, hình vẽ, cảnh báo và loại biểu đồ theo từng mã
vào localStorage:
new ChartWidget(host, {
symbol: 'BTCUSDT',
symbols: ['BTCUSDT', 'ETHUSDT', 'SOLUSDT'],
adapter: new BinanceAdapter(),
persistLayouts: true, // or { keyPrefix: 'myapp:', debounceMs: 2000 }
})
// Reset a single symbol's layout
widget.clearSavedLayout('BTCUSDT')
Bố cục được ghi lại khi đổi mã và khi huỷ widget, nên không mất gì
khi người dùng rời trang.
Kéo thả để nhập dữ liệu
Thả một tệp CSV hoặc JSON lên biểu đồ để nạp ngay. Bật sẵn theo
mặc định — tắt bằng dragDropImport: false. Bộ phân tích
xử lý được các bố cục cột thông dụng (time, open, high, low, close, volume),
dấu thời gian ISO 8601 và thời gian unix tính bằng giây/ms.
// Programmatic use
import { parseOHLCV } from '@tradecanvas/chart'
const { data, rowCount, skipped } = parseOHLCV(csvText)
chart.setData(data)
Lấy mẫu lại khung thời gian
Đưa cho widget chuỗi dữ liệu có độ phân giải mịn nhất qua widget.setData() và các nút khung thời gian trên thanh công cụ sẽ gộp nó ngay trên client — một bộ dữ liệu
cho mọi độ phân giải, không cần tải lại. Hoạt động khi không gắn adapter dữ liệu trực tiếp
nào; tắt bằng resampleTimeframes: false. Nhóm theo tuần
mặc định bắt đầu từ thứ Hai (weekStartsOn: 0 để bắt đầu từ Chủ nhật).
const widget = new ChartWidget(host, {
symbol: 'BTCUSDT',
timeframe: '1h',
timeframes: ['5m', '15m', '1h', '4h', '1d', '1w'],
})
widget.setData(oneMinuteBars) // base series; clicking 4h/1d/1w resamples it
// Or use the pure function directly
import { resampleOHLCV, inferTimeframeMs } from '@tradecanvas/chart'
const hourly = resampleOHLCV(oneMinuteBars, '1h') // OHLC merged, volume summed
const fourHour = resampleOHLCV(oneMinuteBars, '4h', { weekStartsOn: 1 })
Gom nhóm theo lịch: khung trong ngày và khung ngày neo theo các mốc epoch
UTC, tuần theo ngày đầu tuần đã cấu hình, còn tháng / quý / năm
theo ranh giới lịch. Nến đầu vào không bao giờ bị sửa đổi.
Danh mục theo dõi
Bảng bên phải gồm các danh sách mã, mỗi dòng có giá cuối, % thay đổi và một sparkline. Chuyển, tạo,
đổi tên và xoá danh sách từ menu của bảng; thêm mã từ ô tìm mã (+), bỏ mã, và sắp xếp lại bằng cách kéo
hoặc Alt+↑/↓. Các dòng lấy giá từ subscribeQuotes của adapter, từ một nguồn giá của riêng bạn,
hoặc từ những gì bạn đẩy vào.
const widget = new ChartWidget(host, {
symbol: 'BTCUSDT',
adapter: new BinanceAdapter(), // its quotes fill the rows
watchlist: {
lists: [
{ id: 'majors', name: 'Majors', symbols: ['BTCUSDT', 'ETHUSDT'] },
{ id: 'alts', name: 'Alts', symbols: ['SOLUSDT', 'ADAUSDT'] },
],
persist: true, // kept in this browser
onChange: (lists, active) => save(lists), // or keep them yourself
},
})
widget.addToWatchlist('BNBUSDT', 'alts')
widget.setActiveWatchlist('alts')
widget.setQuotes([{ symbol: 'AAPL', last: 190.2, prevClose: 188.1 }]) // from your own feed
widget.getQuote('AAPL')
watchlist: true vẫn hiện một danh sách gồm các symbols như trước; setWatchlistEntry vẫn đẩy giá, mức thay đổi và sparkline của một dòng.
Thông tin mã
Một bảng mở từ nút ⓘ trên thanh công cụ (hoặc từ bảng lệnh): tên của mã, giá cuối và mức thay đổi, trạng thái
thị trường kèm đếm ngược tới lần mở hoặc đóng cửa kế tiếp, giá mở cửa, biên độ và khối lượng trong ngày, giá đóng cửa
phiên trước, giá mua và giá bán, bước giá, tiền tệ, múi giờ và giờ giao dịch, cùng tin tức. Thanh trạng thái báo
khi thị trường đóng cửa.
new ChartWidget(host, {
symbol: 'AAPL',
news: (symbol, limit) => api.headlines(symbol, limit), // or the adapter's fetchNews
})
widget.toggleSymbolInfo(true)
Điều hướng, hoàn tác và hướng chữ
Các nút trên biểu đồ — thu nhỏ và phóng to, cuộn về trước và về sau (giữ nút thì cuộn tiếp), đặt lại —
hiện ra khi chuột nằm trên biểu đồ (navigation: false để bỏ chúng). Thay đổi cài đặt và đổi loại
biểu đồ được hoàn tác bằng Ctrl/Cmd+Z, cùng với hình vẽ và chỉ báo. Với tiếng Ả Rập, tiếng Do Thái, tiếng Ba Tư
hoặc tiếng Urdu, widget đảo chiều (dir: 'auto'; hoặc 'rtl' / 'ltr');
biểu đồ vẫn giữ thời gian chạy từ trái sang phải.
Công cụ vẽ yêu thích
Ghim các công cụ vẽ hay dùng vào một dải ở đầu thanh bên.
Bấm chuột phải vào công cụ bất kỳ (trong menu của nhóm hoặc ngay trên dải) để ghim hoặc bỏ ghim;
danh sách được lưu vào localStorage. Đặt các công cụ ghim ban đầu bằng drawingFavorites:
new ChartWidget(host, {
drawingFavorites: ['trendLine', 'horizontalLine', 'fibRetracement', 'rectangle'],
})
Kiểu vẽ & mẫu
Nút bảng màu trên thanh bên công cụ vẽ mở một popover kiểu vẽ — chọn màu,
độ dày nét và kiểu nét cho hình vẽ tiếp theo (và hình đang chọn), rồi
lưu thành các mẫu có tên trong localStorage để dùng lại chỉ với
một cú bấm. Các lệnh tương đương trong code:
chart.setDrawingStyle({ color: '#e8505b', lineWidth: 2, lineStyle: 'dashed' })
chart.getDrawingStyle()
chart.setSelectedDrawingStyle({ color: '#1fa874' }) // restyle the selected drawing
Cây đối tượng
Nút lớp (layers) trên thanh công cụ mở bảng cây đối tượng liệt kê mọi chỉ báo
và hình vẽ đang có. Chỉ báo có thể xoá; mỗi hình vẽ có nút
hiện / ẩn, khoá / mở khoá, cài đặt và xoá, còn các nhóm được liệt kê cùng
các hình vẽ của chúng bên dưới. Bật sẵn theo mặc định — tắt bằng objectTree: false. Các nút cho hình vẽ tương ứng với:
chart.getDrawings() // DrawingState[] (id, type, visible, locked)
chart.setDrawingVisible(id, false) // hide a single drawing
chart.setDrawingLocked(id, true) // lock it from edits
chart.removeDrawing(id)
chart.groupDrawings(ids, 'Weekly levels') // ẩn, khoá và chọn cùng nhau
chart.renameDrawingGroup(groupId, 'Old highs')
chart.getActiveIndicators() // active indicator instances
chart.updateIndicator(instanceId, { period: 50 }) // re-tune params live
chart.removeIndicator(instanceId)
Cài đặt hình vẽ và menu
Bấm đúp vào một hình vẽ, hoặc dùng nút bánh răng của nó trong cây đối tượng, để mở
cài đặt: kiểu, các cài đặt riêng của công cụ (mức Fibonacci, phần kéo dài,
nhãn…) và các điểm của nó theo múi giờ của biểu đồ. Bấm chuột phải vào một
hình vẽ để mở menu: cài đặt, cảnh báo trên đường của nó, thứ tự, nhóm, khoá,
ẩn, nhân bản và xoá. Thanh bên còn có tẩy, công cụ phóng to và nam châm với
các mức tắt, yếu và mạnh. Xem Công cụ vẽ để biết API bên dưới.
Nút bánh răng trên mỗi dòng chỉ báo mở hộp thoại cài đặt,
tự đọc các tham số của chỉ báo (số, công tắc, màu) và
áp dụng thay đổi ngay qua updateIndicator — không cần xoá rồi
thêm lại để đổi chu kỳ hay màu.
Phần So sánh trong cây đối tượng vẽ chồng các mã khác thành
các đường đã chuẩn hoá. Khi có adapter dữ liệu trực tiếp, nút + mở bộ chọn mã,
tải lịch sử của mã đó qua adapter.fetchHistory và thêm
vào ở chế độ phần trăm (để các mã có mức giá khác nhau dùng chung một trục). Các mã so sánh
tự tải lại khi đổi khung thời gian. Các lệnh tương đương trong code:
widget.addCompareSymbol('ETHUSDT') // fetches + overlays (needs an adapter)
// or drive the chart directly with your own data
chart.addCompareSymbol('ETHUSDT', 'ETH', ethBars, '#627eea')
chart.setCompareMode('percent') // 'percent' | 'absolute'
chart.removeCompareSymbol('ETHUSDT')
Cảnh báo giá
Ngoài một mức giá, cảnh báo còn có thể so một đường với một đường khác (giá cắt một đường
trung bình động, MACD cắt đường tín hiệu của nó), kích hoạt khi giá biến động một số phần trăm
trong một số nến (từ 2 đến 500), chỉ xét các nến đã đóng (không kích hoạt bởi một râu nến đâm
qua rồi quay lại), và hết hạn. Bảng cảnh báo của widget có tất cả những tuỳ chọn này; trong code
chúng là tham số cuối cùng của addAlert. Cảnh báo được kiểm tra ở mỗi lần có giá,
dù từ setCurrentPrice hay từ một nguồn dữ liệu đã kết nối, cùng lúc với các đường
mà chúng theo dõi:
const ema = chart.addIndicator('ema', { period: 50 })
const rsi = chart.addIndicator('rsi')
chart.addAlert(NaN, 'crossingUp', 'above the 50 EMA', 'price', undefined, { target: `${ema}:value` })
chart.addAlert(NaN, 'movesUp', 'pump', 'price', undefined, { percent: 5, bars: 12 })
chart.addAlert(70, 'greaterThan', 'RSI closed above 70', `${rsi}:value`, 'RSI', { onBarClose: true })
chart.addAlert(64_000, 'crossing', 'today only', 'price', undefined, { expiresAt: Date.now() + 86_400_000 })
chart.on('alertExpired', (e) => e.payload) // it reached its time without firing
chart.on('alertTriggered', (e) => e.payload) // { id, condition, channel, target?, percent?, bars?, … }
Biểu tượng chuông trên thanh công cụ mở một bảng nổi để thêm, xem và xoá cảnh báo
giá; một thông báo toast hiện lên khi cảnh báo kích hoạt. Đường cảnh báo cũng kéo được — nắm một đường trên biểu đồ và trượt để đổi giá
(di chuyển cảnh báo sẽ đặt nó về trạng thái chờ kích hoạt lại). Bật sẵn theo mặc định — tắt bằng alerts: false. Điều khiển bằng code qua API của Chart và các sự kiện cảnh báo có kiểu:
// Add from code (condition: 'crossing' | 'crossingUp' | 'crossingDown'
// | 'greaterThan' | 'lessThan')
const id = chart.addAlert(64200, 'crossingUp', 'breakout')
chart.removeAlert(id)
chart.getAlerts() // PriceAlert[]
chart.saveAlerts('tcw:alerts:BTCUSDT') // localStorage persistence
chart.loadAlerts('tcw:alerts:BTCUSDT')
// React to triggers
chart.on('alertTriggered', (e) => {
console.log('hit', e.payload.price, e.payload.message)
})
// also: 'alertAdd' / 'alertRemove' / 'alertUpdate' (fired on drag)
// Indicator alerts: bind to an indicator line via channel '<instanceId>:<key>'.
// In the widget, the alerts panel's source dropdown lists every active line.
const ema = chart.addIndicator('rsi')
chart.addAlert(70, 'crossingUp', 'RSI overbought', `${ema}:rsi`, 'RSI')
Có thể bật âm thanh và/hoặc thông báo trên màn hình máy tính khi cảnh báo kích hoạt (cả hai mặc định
tắt). sound: true phát tiếng bíp có sẵn; truyền một URL để dùng
âm thanh riêng. desktop: true dùng Notification API và hỏi
quyền ở lần dùng đầu tiên.
new ChartWidget(host, {
alertNotifications: { sound: true, desktop: true },
})
Công tắc tính năng
112 công tắc, cái nào cũng bật cho tới khi tắt: tên không có dấu chấm là cả một tính năng, tắt ở mọi nơi nó xuất hiện; tên có dấu chấm là một chỗ. Công tắc đổi được khi widget đang chạy. Danh sách đầy đủ và các option cũ tương ứng nằm ở Tùy biến.
new ChartWidget(host, { features: { 'toolbar.replay': false, 'sidebar.patterns': false, hotkeys: false } }) // một nút, một nhóm công cụ vẽ, mọi phím tắt
widget.setFeatures({ sidebar: false, statusBar: false })
widget.isFeatureOn('sidebar') // false
widget.getFeatures() // mọi công tắc, bật hay tắt
Nút và mục menu của riêng bạn
Thêm nút vào thanh công cụ (một biểu tượng có sẵn hoặc phần tử của bạn, chữ, một công
tắc) và thêm mục vào các menu chuột phải của biểu đồ, xếp sau các mục của widget.
const news = widget.addToolbarButton({
id: 'news',
label: 'News',
icon: 'bell', // hoặc một phần tử <svg>; hoặc text: 'News'
side: 'right', // 'left' nằm cùng các nút điều khiển biểu đồ
toggle: true,
onClick: () => news?.setActive(togglePanel()),
})
news?.setText('3')
news?.remove()
new ChartWidget(host, {
chartMenuItems: ({ area, price, time }) => area === 'plot' && price !== undefined
? [{ label: `Copy ${price.toFixed(2)}`, icon: 'check', onSelect: () => copy(price) }]
: [],
})
Ngoài ra còn nút menu trên toolbar, nút trên thanh công cụ vẽ, mục trên thanh trạng thái, chỗ riêng cho bạn trong từng thanh và trên biểu đồ, và mục ở cuối menu của một hình vẽ hay một chỉ báo (xem Tùy biến):
widget.addToolbarDropdown({ id: 'scans', label: 'Scans', icon: 'layers', items: () => [...] }) // được hỏi lại mỗi lần mở
widget.addSidebarButton({ id: 'ruler', label: 'Ruler', icon: 'ruler', toggle: true, onClick })
widget.addStatusBarItem({ id: 'latency', text: '12 ms', label: 'Latency' })
widget.getSlot('chart') // một lớp trên biểu đồ
widget.addHotkey({ keys: 'Alt+N', label: 'New note', onPress }) // phím tắt của riêng bạn, thay phím tắt của widget nếu trùng phím
new ChartWidget(host, {
drawingMenuItems: ({ id, type }) => [{ label: 'Share', onSelect: () => share(id) }],
indicatorMenuItems: ({ instanceId, indicatorId }) => [{ label: 'Explain', onSelect: () => explain(indicatorId) }],
})
ChartWidgetGrid
Nhiều widget biểu đồ đặt cạnh nhau, mỗi biểu đồ có mã, khung thời gian, chỉ báo và
hình vẽ riêng. Một thanh phía trên chọn cách sắp xếp, liên kết các biểu đồ và lưu cả
lưới thành một bố cục có tên. Biểu đồ được bấm gần nhất là biểu đồ đang chọn (có viền).
import { ChartWidgetGrid } from '@tradecanvas/chart/widget'
const grid = new ChartWidgetGrid(host, {
layout: '2x2', // '1x1' '1x2' '2x1' '2x2' '1x3' '3x1' '2x3' '3x2'
widget: { timeframe: '1h' }, // cho mọi biểu đồ
adapter: () => new BinanceAdapter(), // mỗi biểu đồ một adapter: một adapter giữ một luồng
cells: [{ symbol: 'BTCUSDT' }, { symbol: 'ETHUSDT' }, { symbol: 'SOLUSDT' }, { symbol: 'BNBUSDT' }],
sync: { crosshair: true, time: false, symbol: false, interval: false, drawings: false, replay: false },
})
grid.setLayout('1x2')
grid.setSync({ interval: true }) // đưa các biểu đồ khác về cùng khung với biểu đồ đang chọn
grid.applyOverrides({ 'grid.vertical.visible': false }) // giao diện của mọi biểu đồ, kể cả biểu đồ thêm sau
grid.setFeatures({ 'toolbar.replay': false }) // công tắc của mọi biểu đồ, kể cả biểu đồ thêm sau
grid.getActiveWidget().getChart()
grid.getLayoutSession()?.saveAs('Majors')
// Từng biểu đồ ngay khi được tạo (lúc đầu và khi lưới thêm ô)
new ChartWidgetGrid(host, {
onChartAdd: (widget, index) => widget.getChart().addIndicator('ema', { period: 21 }),
})
Đồng bộ con trỏ chữ thập hiện thời điểm dưới con trỏ chuột trên mọi biểu đồ; đồng bộ
thời gian cuộn và phóng to/thu nhỏ các biểu đồ khác theo biểu đồ đang dùng; hình vẽ được
sao chép sang các biểu đồ đang hiện cùng mã (bật mục này sẽ gộp hình vẽ của các biểu đồ
đó lại, không mất hình nào); đồng bộ phát lại cho các biểu đồ khác phát lại đến cùng thời điểm
với biểu đồ đang phát lại (biểu đồ có nến dài hơn các bước phát lại sẽ hiện trọn nến chứa thời điểm đó).
Các biểu đồ bị bớt đi khi lưới thu nhỏ sẽ được cất đi, vẫn
được giữ trong bố cục đã lưu, và trở lại nguyên như cũ khi lưới mở rộng lại; một biểu đồ
hoàn toàn mới sẽ mở theo mã và khung thời gian của biểu đồ đang chọn nếu hai mục đó đang
được đồng bộ.
ChartGrid
Bố cục nhiều biểu đồ headless (không có thanh công cụ) đồng bộ với nhau; xem ChartWidgetGrid nếu cần giao diện đầy đủ.
import { ChartGrid } from '@tradecanvas/chart'
const grid = new ChartGrid(host, { layout: '2x2', theme: 'dark' })
// Mỗi biểu đồ một adapter: một adapter giữ một luồng
await grid.connectAll(() => new BinanceAdapter(), ['BTCUSDT','ETHUSDT','SOLUSDT','BNBUSDT'], '5m')
grid.setLayout('1x2')
Bố cục: '1x1', '1x2', '2x1', '2x2', '1x3', '3x1', '2x3', '3x2'.
+ p.toFixed(2))
chart.setPriceFormat(null) // decimals again
chart.setTimeFormatter((time, { kind, timeZone }) =>
// kind: 'year', 'month', 'day' (the step a label starts), 'time' (within a day), 'crosshair'
new Intl.DateTimeFormat('en-GB', { timeZone: timeZone ?? undefined, hour: '2-digit', minute: '2-digit' }).format(time)) Cách các trục chọn nhãn
Trục thời gian gắn nhãn cho những nến mở đầu bước lịch lớn nhất: một năm (chữ đậm), một tháng, một ngày, một giờ tròn, cách nhau ít nhất 80 px. Vì vậy ở 5Y trục đọc 2022 · Jul · 2023, ở một tháng Oct · 8 · 15, trong ngày 12:00 · 18:00. Nhãn thuộc về nến chứ không thuộc về màn hình: kéo biểu đồ thì nhãn đi theo nến, nạp thêm lịch sử phía trước thì nhãn vẫn ở chỗ cũ. Bên trái nến cũ nhất không có nhãn. Tên tháng theo setNumberLocale, và các đường lưới nằm đúng chỗ nhãn.
Thang giá đặt số nhãn vừa với chiều cao của nó, cách nhau ít nhất 30 px, theo bước giá của thị trường. Ở thang log bước tăng theo giá, nên phần dưới cũng có nhãn như phần trên; ở thang phần trăm hay chỉ số, nhãn là phần trăm hoặc điểm chỉ số tròn tính từ mốc. Các pane chỉ báo cũng đếm nhãn theo chiều cao của chúng.
Thị trường: bước giá, biên độ và phiên giao dịch
setMarket cho biểu đồ biết quy định của một sàn: màu nến, số chữ số thập
phân, bước giá (có thể đổi theo vùng giá), giá trần và giá sàn trong ngày, và lô giao
dịch. roundPrice và getPriceStep theo bước giá của vùng giá, nên
giá lấy từ menu của biểu đồ luôn là giá hợp lệ, và phiếu đặt lệnh của widget kiểm tra
lệnh theo bước giá, biên độ trong ngày và lô chẵn. Preset còn mang các phiên và múi giờ
của sàn, dùng cho getCurrentSession và code của bạn; vùng tô phiên và giờ mở
rộng trên biểu đồ lấy từ setSymbolInfo. Có sẵn
preset cho các sàn Việt Nam theo quy định tháng 5/2025: HOSE (bước 10, 50 hoặc 100 đồng
theo vùng giá, ±7%), chứng chỉ quỹ ETF và chứng quyền trên HOSE (10 đồng), HNX (±10%, có
phiên sau giờ), UPCoM (±15%) và hợp đồng tương lai VN30 (bước 0,1 điểm, ±7%, mở cửa
8:45); cổ phiếu giao dịch theo lô 100, xanh tăng, đỏ giảm, tím trần, xanh lam sàn, vàng
tham chiếu. Giá trong các preset tính bằng nghìn đồng như trên bảng giá; scaleMarketPrices(preset, 1000) dùng cho giá tính bằng đồng.
import {
MARKET_HOSE, scaleMarketPrices, priceStepAt, roundToPriceStep, getCurrentSession,
vn30fContracts, vn30fLastTradingDay,
} from '@tradecanvas/chart'
chart.setMarket(MARKET_HOSE) // hoặc scaleMarketPrices(MARKET_HOSE, 1000) cho giá 25350
chart.setPriceLimits(25.35) // giá tham chiếu: giá đóng cửa hôm trước
chart.getPriceLimits() // { ceiling: 27.1, floor: 23.6, reference: 25.35 }
chart.roundPrice(25.37) // 25.35, bước 50 đồng
chart.getPriceStep(9.5) // 0.01 dưới 10.000 đồng
// Dùng riêng các quy tắc
priceStepAt(25.35, MARKET_HOSE) // 0.05
roundToPriceStep(10.486, MARKET_HOSE, 'down') // 10.45
getCurrentSession(MARKET_HOSE.sessions, Date.now(), MARKET_HOSE.timeZone) // { name: 'ATC', type: 'preClose', … }, chỉ theo giờ
// Hợp đồng tương lai VN30: bốn hợp đồng đang niêm yết và ngày giao dịch cuối cùng
vn30fContracts(Date.now()) // [{ symbol: 'VN30F2611', lastTradingDay: '2026-11-19', … }, …]
vn30fLastTradingDay(2026, 12, (day) => holidays.has(day)) // thứ Năm thứ ba, lùi lại nếu là ngày nghỉ Thị trường của riêng bạn là một MarketConfig: priceStep, hoặc priceSteps: [{ from, step }] cho các vùng giá, priceLimits, volumeUnit (lô), sessions, timeZone và colorScheme. minTick của mã đi trước bước giá của thị trường.
Các sàn thỉnh thoảng đổi quy định; hãy đối chiếu preset với quy định hiện hành của sàn
trước khi dựa vào nó.
So sánh các mã
Phần trăm thay đổi của một mã khác trên thang giá (addCompareSymbol), hoặc giá của
nó trên thang riêng, trong bảng riêng, hoặc chênh lệch hay tỷ lệ giữa giá đóng cửa của biểu đồ
và mã đó — mấy cách sau là chỉ báo (compareSymbol, spread), có chú
thích, nhãn giá trị, cảnh báo và được lưu trong bố cục như mọi chỉ báo khác. Nến của mã kia được
khớp với nến của biểu đồ theo thời gian. Biểu đồ tự yêu cầu những nến nó cần; hãy cung cấp lại
sau khi đổi khung thời gian. correlation (theo giá đóng cửa) và correlationlog (theo lợi suất từng nến) đọc mã kia theo cùng cách, cho giá trị từ −1 đến 1.
chart.addCompareSymbol('eth', 'ETHUSDT', ethBars, '#7c4dff') // percent change, on this scale
chart.addIndicator('compareSymbol', { symbol: 'ETHUSDT' }, 'bottom', { scale: 'left' }) // own scale
chart.addIndicator('spread', { symbol: 'ETHUSDT', mode: 'ratio' }) // own pane
chart.addIndicator('correlation', { symbol: 'ETHUSDT', length: 20 }) // −1 to 1, own pane
chart.on('symbolSeriesRequest', async ({ payload }) =>
chart.setSymbolSeries(payload.symbol, await adapter.fetchHistory(payload.symbol, '1h', 1000)))
chart.getRequiredSymbols() // what to fetch again on a new interval
chart.setPaneScale(spreadId, { percent: true }) // a pane in percent of its first value Trong ChartWidget, nút so sánh trong cây đối tượng hỏi mã, rồi hỏi cách so sánh: thay đổi phần trăm, thang riêng, pane riêng, chênh lệch, tỷ lệ, hoặc tương quan theo giá hay theo lợi suất.
Xuất dữ liệu
Xuất các nến dưới dạng CSV hoặc JSON, mỗi đường chỉ báo nằm trong một cột riêng, đặt tên theo tên trên chú thích. ChartWidget có mục Xuất dữ liệu (CSV) trong menu chuột phải của biểu đồ.
chart.exportAllData('csv', 'btc-1h.csv') // every bar loaded
chart.exportVisibleData('json', undefined, { indicators: false })
const text = chart.getExportText('csv', { range: 'visible' })
const { bars, columns } = chart.getExportData() // columns: { name, values }[] Hồ sơ khối lượng (Volume Profile)
Biểu đồ tần suất nằm ngang của khối lượng giao dịch, gom theo mức giá trong khoảng đang hiện. Mặc định tắt — bật bằng code hoặc qua bảng cài đặt của widget:
chart.setVolumeProfileVisible(true)
chart.setVolumeProfileConfig({
buckets: 48, // resolution of the histogram
widthRatio: 0.18, // % of chart width
opacity: 0.32,
highlightPoC: true, // mark the highest-volume bucket
}) Điểm đảo chiều (pivot)
Đánh dấu các đỉnh/đáy sóng kiểu fractal bằng tam giác nhỏ (▼ phía trên một đỉnh pivot đã xác nhận, ▲ phía dưới một đáy pivot). Độ mạnh quy định phải có bao nhiêu nến thấp hơn ở mỗi bên. Bật từ bảng cài đặt, hoặc:
chart.setPivotMarkersVisible(true)
chart.setPivotMarkersConfig({ left: 5, right: 5, showLabels: true })
// market-structure labels (HH / HL / LH / LL) instead of price
chart.setPivotMarkersConfig({ structureLabels: true })
// pure detection + classification are exported
import { findPivots, classifyPivots } from '@tradecanvas/core'
const pivots = findPivots(bars, 5, 5) // [{ index, price, type }]
const structure = classifyPivots(pivots) // adds label: 'HH'|'LH'|'HL'|'LL' Tô màu phiên (giờ giao dịch chính thức)
Làm mờ các nến nằm ngoài phiên chính (trước/sau giờ giao dịch hoặc khoảng nghỉ qua đêm) để phiên chính nổi bật. Mặc định là giờ giao dịch chính thức (RTH) của cổ phiếu Mỹ (09:30–16:00 giờ New York, đã tính giờ mùa hè); cấu hình khung giờ theo số phút trong ngày cùng múi giờ của thị trường. Mã nào có nguồn dữ liệu báo kèm phiên giao dịch sẽ tự đặt phiên.
chart.setSessionShadingVisible(true)
chart.setSessionShadingConfig({
startMinute: 9 * 60 + 30, // 09:30
endMinute: 16 * 60, // 16:00 (end-exclusive; end < start wraps midnight)
timeZone: 'America/New_York', // or tzOffsetMinutes: -300 for a fixed offset
}) Giờ giao dịch mở rộng. Khi tắt, các nến nằm ngoài giờ giao dịch chính của mã
(SymbolInfo.sessions theo timezone của mã) rời khỏi biểu đồ; chúng được
cất riêng, cả nến trực tiếp lẫn các trang lịch sử, và trở lại khi bật lên. Nến từ một ngày trở
lên được giữ nguyên.
chart.setSymbolInfo({ symbol: 'AAPL', timezone: 'America/New_York', sessions: [{ start: '09:30', end: '16:00' }] })
chart.setExtendedHours(false) // or new Chart(host, { extendedHours: false })
chart.isExtendedHoursVisible() Mức của kỳ trước (PDH / PDL / PDC)
Vẽ giá cao, thấp và đóng cửa của ngày (hoặc tuần) trước, cùng giá mở cửa của kỳ hiện tại, thành các đường ngang có nhãn — những mức hỗ trợ/kháng cự mà trader trong ngày theo dõi. Bật từ bảng cài đặt, hoặc gọi trực tiếp:
chart.setPeriodLevelsVisible(true)
chart.setPeriodLevelsPeriod('week') // 'day' (PDH/PDL/PDC) | 'week' (PWH/PWL/PWC)
// pure computation is exported
import { computePeriodLevels } from '@tradecanvas/core'
const levels = computePeriodLevels(bars, 'day') // [{ id, label, price }] Hồ sơ thị trường (TPO)
Biểu đồ tần suất thời gian tại mỗi mức giá: mỗi nến đóng góp một TPO vào mọi ô giá mà biên độ của nó chạm tới, làm lộ ra Point of Control (mức giá có nhiều TPO nhất) và vùng giá trị (≈70% số TPO). Khác với Hồ sơ khối lượng — nó tính theo thời gian, không theo khối lượng — và được ghim bên trái nên cả hai có thể hiện cùng lúc. Mặc định tắt; bật từ bảng cài đặt hoặc gọi trực tiếp:
chart.setMarketProfileVisible(true)
chart.setMarketProfileConfig({
buckets: 48,
widthRatio: 0.18,
opacity: 0.32,
valueAreaPct: 0.7, // fraction of TPOs in the value area
highlightPoC: true, // dashed line at the point of control
})
// split into one mini-profile per calendar-day session
chart.setMarketProfileConfig({ splitBySession: true })
// classic TPO letters per session (when zoomed in enough to be legible)
chart.setMarketProfileConfig({ splitBySession: true, letters: true })
// pure computation is exported too
import { computeMarketProfile, computeSessionProfiles } from '@tradecanvas/core'
const profile = computeMarketProfile(bars, priceMin, priceMax, { buckets: 48 })
const sessions = computeSessionProfiles(bars, priceMin, priceMax) // per-day TPO Cảm ứng & di động
| Cử chỉ | Thao tác |
|---|---|
| Kéo 1 ngón (vùng biểu đồ) | Kéo biểu đồ + di chuyển con trỏ chữ thập |
| Chụm 2 ngón | Phóng to quanh điểm giữa |
| Nhấn giữ (~500 ms) | Ghim chú thích OHLC tại nến (bản di động của Alt+bấm) |
| Kéo 1 ngón trong dải trục giá / thời gian | Co giãn trục tương ứng |
| Chạm | Bấm: click, barClick, một signal marker; chạm lên hình vẽ thì chọn nó |
| Chạm đúp | Bấm đúp: trên trục thì đặt lại trục; trên hình vẽ thì mở cài đặt của nó |
| Kéo 1 ngón trên hình vẽ | Di chuyển nó, hoặc đổi dạng bằng một tay nắm của hình đang chọn |
| Khi đang chọn công cụ vẽ: chạm, chạm — hoặc nhấn, kéo, nhấc | Đặt các điểm; nhấn, kéo rồi nhấc ngón vẽ xong đường hai điểm trong một lần |
| Nhấn giữ trên hình vẽ | Menu của nó (như bấm chuột phải) |
| Kéo 1 ngón trên đường cảnh báo, lệnh, bracket hoặc đường chia pane | Kéo nó |
Ngón tay với xa hơn chuột: khoảng 16 px tới một đường và 22 px tới một tay nắm, so với 8. Trên trang có cuộn, pageScroll: true để vuốt dọc và con lăn chuột thường cho trang: biểu đồ kéo theo vuốt ngang và phóng to bằng hai ngón hoặc Ctrl + con lăn (chụm trên touchpad), còn nhấn lên hình vẽ, các đường và trục vẫn thuộc về biểu đồ. chart.setPageScroll(on) bật tắt khi đang chạy.
Các hộp thoại (cài đặt, bảng phím tắt, bảng lệnh, tìm mã) tự động chuyển sang dạng bảng trượt từ dưới lên, có tay nắm để kéo và phần đệm tính đến vùng an toàn của màn hình khi khung nhìn hẹp hơn 640 px.
Công cụ đo
Giữ Shift và kéo trên biểu đồ để đo số nến × giá giữa hai điểm — lớp phủ hiện chênh lệch giá Δ (tuyệt đối + %), số nến và khoảng thời gian. Lớp phủ biến mất ngay khi nhả chuột; nó không được lưu vào trạng thái.
Sự kiện
Mọi sự kiện đều có kiểu qua ChartEventMap:
chart.on('orderPlace', e => /* OrderPlacePayload */)
chart.on('orderModify', e => /* OrderModifyPayload */)
chart.on('signalMarkerAdd', e => /* { marker } */)
chart.on('tradeZoneAdd', e => /* { zone } */)
chart.on('dataUpdate', e => /* { length } */)
chart.on('ordersChange', e => /* { orders } */)
chart.on('positionsChange', e => /* { positions } */)
chart.on('executionFill', e => /* { side, price, quantity, reason, pnl } */)
chart.on('chartContextMenu', e => /* { area, x, y, price, time } */)
chart.on('stateChange', () => /* hình vẽ, chỉ báo, cảnh báo, loại biểu đồ hoặc giao diện có thể đã thay đổi */)
chart.on('paneChange', e => /* { instanceId, change: 'collapsed' | 'maximized' | 'order' } */)
chart.on('chartTypeChange', e => /* { type, previous } */)
chart.on('symbolChange', e => /* { symbol, previous } */)
chart.on('timeframeChange', e => /* { timeframe, previous } */)
chart.on('historyChange', e => /* { canUndo, canRedo } */)
chart.on('drawingSelect', e => /* { ids, primary } */)
chart.on('rendererChange', e => /* { renderer: 'canvas' | 'webgl', reason?: 'unsupported' | 'contextLost' | 'contextRestored' | 'limit' } */) Hoàn tác cho thay đổi của riêng bạn
recordUndo đưa một thay đổi của bạn vào lịch sử hoàn tác của biểu đồ, cùng với hình vẽ và
chỉ báo: Ctrl/Cmd+Z gọi undo, làm lại thì gọi redo. Các thay đổi về cùng một subject diễn ra sát nhau được tính là một bước. ChartWidget ghi lại cài đặt và loại biểu đồ của nó theo cách này.
const before = panel.color
panel.color = 'red'
chart.recordUndo({ subject: 'panel-color', undo: () => (panel.color = before), redo: () => (panel.color = 'red') }) Bàn phím và trình đọc màn hình
Khi đang có focus, biểu đồ cuộn bằng các phím mũi tên (giữ Shift để cuộn mười nến), phóng to thu nhỏ bằng ↑/↓
hoặc +/−, và về đầu, về cuối bằng Home và End. Với trình đọc màn hình, biểu đồ là một ứng dụng có phần tóm tắt
(mã, loại biểu đồ, khung thời gian, giá cuối); sau khi các phím di chuyển khung nhìn, nó đọc những gì đang hiện trên
màn hình, và dấu phẩy, dấu chấm đọc từng nến một. a11y.labels chuyển các câu đó sang ngôn ngữ của bạn; a11y: false bỏ hẳn phần này. scrollBars(n) và selectDrawing(id) làm bằng code những gì các phím và một cú bấm chuột làm.
new Chart(host, { a11y: { labels: { role: 'gráfico', summary: '{what}. Último precio {close}.' } } }) Giá lấy từ con trỏ chuột có thể được làm tròn theo bước giá của thị trường bằng chart.roundPrice(price): về bội số của minTick của mã, nếu không có thì theo
độ chính xác của mã. Các menu, phiếu đặt lệnh và tab Coordinates trong cài đặt hình vẽ của ChartWidget đều làm như vậy. chart.getPriceStep() trả về chính bước giá đó.
ChartWidget
Bọc Chart trong một giao diện hoàn chỉnh. Có thể truy cập chính instance đó qua widget.chart.
import { ChartWidget } from '@tradecanvas/chart/widget'
const widget = new ChartWidget(host, {
symbol: 'BTCUSDT',
timeframe: '5m',
theme: 'dark',
adapter: new BinanceAdapter(),
historyLimit: 500,
trading: true,
features: { 'toolbar.replay': false }, // công tắc của widget: xem Tùy biến
onReady: (chart) => { /* ... */ },
})
widget.chart.setData(...)
widget.destroy() Phím tắt của widget
| Phím tắt | Thao tác |
|---|---|
| Ctrl / ⌘ + K | Bảng lệnh (chỉ báo, loại biểu đồ, công cụ vẽ…) |
| Ctrl / ⌘ + P | Tìm mã — tìm gần đúng trong danh sách mã đã cấu hình |
| Ctrl / ⌘ + S | Lưu bố cục (lần đầu sẽ hỏi tên) |
| 0–9 | Gõ một khung thời gian (5, 15m, 1h, 1D) rồi nhấn Enter (intervalTyping: false để tắt) |
| Alt + T / H / J / V / C / F | Đường xu hướng, đường ngang, tia ngang, đường dọc, đường chữ thập, Fibonacci thoái lui |
| ? | Hiện bảng phím tắt |
| Alt + bấm vào biểu đồ | Ghim chú thích OHLC tại nến đang rê chuột (kèm chênh lệch so với vị trí con trỏ chữ thập hiện tại) |
| Esc | Bỏ ghim chú thích / huỷ hình đang vẽ |
| Bấm vào mã trên thanh công cụ | Mở hộp thoại tìm mã |
| Bấm nút phát trên thanh công cụ | Mở thanh điều khiển phát lại nến (phát/từng bước/tua/tốc độ) |
Cập nhật danh sách mã có thể tìm lúc chạy bằng widget.setSymbols(['BTCUSDT', 'ETHUSDT', …]).
Cửa sổ dữ liệu
Một bảng nổi hiển thị chính xác O/H/L/C/V, mức thay đổi của nến và giá trị của mọi chỉ báo đang bật tại nến đang rê chuột — cập nhật trực tiếp khi bạn di chuyển con trỏ chữ thập. Bật/tắt từ bảng lệnh (Ctrl/⌘ K → "Bật/tắt cửa sổ dữ liệu").
Chia sẻ góc nhìn (deep link)
Mã hoá toàn bộ góc nhìn — mã, khung thời gian, loại biểu đồ, thang giá,
chỉ báo (kèm tham số) và hình vẽ — thành một chuỗi gọn, an toàn cho URL để
tạo liên kết sâu. Với shareUrl: true, widget khôi phục hash #tcw=… khi tải trang, và thao tác "Chia sẻ góc nhìn" trong bảng lệnh
sao chép một liên kết vào clipboard.
const widget = new ChartWidget(host, { shareUrl: true })
const token = widget.exportState() // portable string
await widget.importState(token) // restore a view
await widget.copyShareLink() // copy "<url>#tcw=<token>" Bố cục có tên
Nút bố cục trên thanh công cụ lưu biểu đồ dưới một cái tên: mã, khung thời gian,
thang giá, loại biểu đồ, chỉ báo, hình vẽ và cảnh báo (không gồm giao diện, vì đó
là lựa chọn của người xem). Mở, đổi tên và xoá bố cục từ menu của nút này; bố cục
đang mở tự lưu mỗi khi thay đổi, và Ctrl/⌘ S sẽ lưu nó. Bố cục được lưu
trong localStorage của trình duyệt này, trừ khi bạn truyền vào một storage: bốn hàm, hàm nào cũng có thể trả về một promise.
import { ChartWidget, type LayoutStorage } from '@tradecanvas/chart/widget'
const server: LayoutStorage = {
list: () => api.get('/layouts'), // [{ id, name, symbol, timeframe, updatedAt }]
load: (id) => api.get(`/layouts/${id}`), // { ...summary, content } hoặc null
save: (layout) => api.put(`/layouts/${layout.id}`, layout),
remove: (id) => api.delete(`/layouts/${id}`),
}
const widget = new ChartWidget(host, {
layouts: { storage: server, autoSave: true, openLast: true }, // hoặc false để tắt
})
const layouts = widget.getLayoutSession()!
await layouts.saveAs('Swing BTC')
await layouts.open(id)
layouts.current() // { id, name, … } hoặc null
layouts.setAutoSave(false)
// Chỉ lấy phần nội dung, để cất ở đâu tuỳ bạn
const json = widget.getLayoutContent()
await widget.applyLayoutContent(json) localStorageLayouts(prefix) và memoryLayouts() là hai kiểu
lưu trữ có sẵn. Nội dung đã lưu được đọc một cách thận trọng: bố cục nào không phân
tích được sẽ bị từ chối, chứ không bị áp dụng nửa chừng. Mỗi bố cục ghi lại kind của nó ('chart' hoặc 'grid'), nên một widget
và một lưới có thể dùng chung một nơi lưu trữ mà mỗi bên chỉ liệt kê bố cục của riêng
mình. Việc lưu, mở và tự động lưu chạy lần lượt từng việc một, nên một lần lưu không bao
giờ ghi vào bố cục được mở sau nó.
Bố cục theo từng mã
Ngoài ra, có thể tự động lưu bộ chỉ báo, hình vẽ, cảnh báo và loại biểu đồ theo từng mã
vào localStorage:
new ChartWidget(host, {
symbol: 'BTCUSDT',
symbols: ['BTCUSDT', 'ETHUSDT', 'SOLUSDT'],
adapter: new BinanceAdapter(),
persistLayouts: true, // or { keyPrefix: 'myapp:', debounceMs: 2000 }
})
// Reset a single symbol's layout
widget.clearSavedLayout('BTCUSDT') Bố cục được ghi lại khi đổi mã và khi huỷ widget, nên không mất gì khi người dùng rời trang.
Kéo thả để nhập dữ liệu
Thả một tệp CSV hoặc JSON lên biểu đồ để nạp ngay. Bật sẵn theo
mặc định — tắt bằng dragDropImport: false. Bộ phân tích
xử lý được các bố cục cột thông dụng (time, open, high, low, close, volume),
dấu thời gian ISO 8601 và thời gian unix tính bằng giây/ms.
// Programmatic use
import { parseOHLCV } from '@tradecanvas/chart'
const { data, rowCount, skipped } = parseOHLCV(csvText)
chart.setData(data) Lấy mẫu lại khung thời gian
Đưa cho widget chuỗi dữ liệu có độ phân giải mịn nhất qua widget.setData() và các nút khung thời gian trên thanh công cụ sẽ gộp nó ngay trên client — một bộ dữ liệu
cho mọi độ phân giải, không cần tải lại. Hoạt động khi không gắn adapter dữ liệu trực tiếp
nào; tắt bằng resampleTimeframes: false. Nhóm theo tuần
mặc định bắt đầu từ thứ Hai (weekStartsOn: 0 để bắt đầu từ Chủ nhật).
const widget = new ChartWidget(host, {
symbol: 'BTCUSDT',
timeframe: '1h',
timeframes: ['5m', '15m', '1h', '4h', '1d', '1w'],
})
widget.setData(oneMinuteBars) // base series; clicking 4h/1d/1w resamples it
// Or use the pure function directly
import { resampleOHLCV, inferTimeframeMs } from '@tradecanvas/chart'
const hourly = resampleOHLCV(oneMinuteBars, '1h') // OHLC merged, volume summed
const fourHour = resampleOHLCV(oneMinuteBars, '4h', { weekStartsOn: 1 }) Gom nhóm theo lịch: khung trong ngày và khung ngày neo theo các mốc epoch UTC, tuần theo ngày đầu tuần đã cấu hình, còn tháng / quý / năm theo ranh giới lịch. Nến đầu vào không bao giờ bị sửa đổi.
Danh mục theo dõi
Bảng bên phải gồm các danh sách mã, mỗi dòng có giá cuối, % thay đổi và một sparkline. Chuyển, tạo,
đổi tên và xoá danh sách từ menu của bảng; thêm mã từ ô tìm mã (+), bỏ mã, và sắp xếp lại bằng cách kéo
hoặc Alt+↑/↓. Các dòng lấy giá từ subscribeQuotes của adapter, từ một nguồn giá của riêng bạn,
hoặc từ những gì bạn đẩy vào.
const widget = new ChartWidget(host, {
symbol: 'BTCUSDT',
adapter: new BinanceAdapter(), // its quotes fill the rows
watchlist: {
lists: [
{ id: 'majors', name: 'Majors', symbols: ['BTCUSDT', 'ETHUSDT'] },
{ id: 'alts', name: 'Alts', symbols: ['SOLUSDT', 'ADAUSDT'] },
],
persist: true, // kept in this browser
onChange: (lists, active) => save(lists), // or keep them yourself
},
})
widget.addToWatchlist('BNBUSDT', 'alts')
widget.setActiveWatchlist('alts')
widget.setQuotes([{ symbol: 'AAPL', last: 190.2, prevClose: 188.1 }]) // from your own feed
widget.getQuote('AAPL') watchlist: true vẫn hiện một danh sách gồm các symbols như trước; setWatchlistEntry vẫn đẩy giá, mức thay đổi và sparkline của một dòng.
Thông tin mã
Một bảng mở từ nút ⓘ trên thanh công cụ (hoặc từ bảng lệnh): tên của mã, giá cuối và mức thay đổi, trạng thái thị trường kèm đếm ngược tới lần mở hoặc đóng cửa kế tiếp, giá mở cửa, biên độ và khối lượng trong ngày, giá đóng cửa phiên trước, giá mua và giá bán, bước giá, tiền tệ, múi giờ và giờ giao dịch, cùng tin tức. Thanh trạng thái báo khi thị trường đóng cửa.
new ChartWidget(host, {
symbol: 'AAPL',
news: (symbol, limit) => api.headlines(symbol, limit), // or the adapter's fetchNews
})
widget.toggleSymbolInfo(true) Điều hướng, hoàn tác và hướng chữ
Các nút trên biểu đồ — thu nhỏ và phóng to, cuộn về trước và về sau (giữ nút thì cuộn tiếp), đặt lại —
hiện ra khi chuột nằm trên biểu đồ (navigation: false để bỏ chúng). Thay đổi cài đặt và đổi loại
biểu đồ được hoàn tác bằng Ctrl/Cmd+Z, cùng với hình vẽ và chỉ báo. Với tiếng Ả Rập, tiếng Do Thái, tiếng Ba Tư
hoặc tiếng Urdu, widget đảo chiều (dir: 'auto'; hoặc 'rtl' / 'ltr');
biểu đồ vẫn giữ thời gian chạy từ trái sang phải.
Công cụ vẽ yêu thích
Ghim các công cụ vẽ hay dùng vào một dải ở đầu thanh bên.
Bấm chuột phải vào công cụ bất kỳ (trong menu của nhóm hoặc ngay trên dải) để ghim hoặc bỏ ghim;
danh sách được lưu vào localStorage. Đặt các công cụ ghim ban đầu bằng drawingFavorites:
new ChartWidget(host, {
drawingFavorites: ['trendLine', 'horizontalLine', 'fibRetracement', 'rectangle'],
}) Kiểu vẽ & mẫu
Nút bảng màu trên thanh bên công cụ vẽ mở một popover kiểu vẽ — chọn màu, độ dày nét và kiểu nét cho hình vẽ tiếp theo (và hình đang chọn), rồi lưu thành các mẫu có tên trong localStorage để dùng lại chỉ với một cú bấm. Các lệnh tương đương trong code:
chart.setDrawingStyle({ color: '#e8505b', lineWidth: 2, lineStyle: 'dashed' })
chart.getDrawingStyle()
chart.setSelectedDrawingStyle({ color: '#1fa874' }) // restyle the selected drawing Cây đối tượng
Nút lớp (layers) trên thanh công cụ mở bảng cây đối tượng liệt kê mọi chỉ báo
và hình vẽ đang có. Chỉ báo có thể xoá; mỗi hình vẽ có nút
hiện / ẩn, khoá / mở khoá, cài đặt và xoá, còn các nhóm được liệt kê cùng
các hình vẽ của chúng bên dưới. Bật sẵn theo mặc định — tắt bằng objectTree: false. Các nút cho hình vẽ tương ứng với:
chart.getDrawings() // DrawingState[] (id, type, visible, locked)
chart.setDrawingVisible(id, false) // hide a single drawing
chart.setDrawingLocked(id, true) // lock it from edits
chart.removeDrawing(id)
chart.groupDrawings(ids, 'Weekly levels') // ẩn, khoá và chọn cùng nhau
chart.renameDrawingGroup(groupId, 'Old highs')
chart.getActiveIndicators() // active indicator instances
chart.updateIndicator(instanceId, { period: 50 }) // re-tune params live
chart.removeIndicator(instanceId) Cài đặt hình vẽ và menu
Bấm đúp vào một hình vẽ, hoặc dùng nút bánh răng của nó trong cây đối tượng, để mở cài đặt: kiểu, các cài đặt riêng của công cụ (mức Fibonacci, phần kéo dài, nhãn…) và các điểm của nó theo múi giờ của biểu đồ. Bấm chuột phải vào một hình vẽ để mở menu: cài đặt, cảnh báo trên đường của nó, thứ tự, nhóm, khoá, ẩn, nhân bản và xoá. Thanh bên còn có tẩy, công cụ phóng to và nam châm với các mức tắt, yếu và mạnh. Xem Công cụ vẽ để biết API bên dưới.
Nút bánh răng trên mỗi dòng chỉ báo mở hộp thoại cài đặt,
tự đọc các tham số của chỉ báo (số, công tắc, màu) và
áp dụng thay đổi ngay qua updateIndicator — không cần xoá rồi
thêm lại để đổi chu kỳ hay màu.
Phần So sánh trong cây đối tượng vẽ chồng các mã khác thành
các đường đã chuẩn hoá. Khi có adapter dữ liệu trực tiếp, nút + mở bộ chọn mã,
tải lịch sử của mã đó qua adapter.fetchHistory và thêm
vào ở chế độ phần trăm (để các mã có mức giá khác nhau dùng chung một trục). Các mã so sánh
tự tải lại khi đổi khung thời gian. Các lệnh tương đương trong code:
widget.addCompareSymbol('ETHUSDT') // fetches + overlays (needs an adapter)
// or drive the chart directly with your own data
chart.addCompareSymbol('ETHUSDT', 'ETH', ethBars, '#627eea')
chart.setCompareMode('percent') // 'percent' | 'absolute'
chart.removeCompareSymbol('ETHUSDT') Cảnh báo giá
Ngoài một mức giá, cảnh báo còn có thể so một đường với một đường khác (giá cắt một đường
trung bình động, MACD cắt đường tín hiệu của nó), kích hoạt khi giá biến động một số phần trăm
trong một số nến (từ 2 đến 500), chỉ xét các nến đã đóng (không kích hoạt bởi một râu nến đâm
qua rồi quay lại), và hết hạn. Bảng cảnh báo của widget có tất cả những tuỳ chọn này; trong code
chúng là tham số cuối cùng của addAlert. Cảnh báo được kiểm tra ở mỗi lần có giá,
dù từ setCurrentPrice hay từ một nguồn dữ liệu đã kết nối, cùng lúc với các đường
mà chúng theo dõi:
const ema = chart.addIndicator('ema', { period: 50 })
const rsi = chart.addIndicator('rsi')
chart.addAlert(NaN, 'crossingUp', 'above the 50 EMA', 'price', undefined, { target: `${ema}:value` })
chart.addAlert(NaN, 'movesUp', 'pump', 'price', undefined, { percent: 5, bars: 12 })
chart.addAlert(70, 'greaterThan', 'RSI closed above 70', `${rsi}:value`, 'RSI', { onBarClose: true })
chart.addAlert(64_000, 'crossing', 'today only', 'price', undefined, { expiresAt: Date.now() + 86_400_000 })
chart.on('alertExpired', (e) => e.payload) // it reached its time without firing
chart.on('alertTriggered', (e) => e.payload) // { id, condition, channel, target?, percent?, bars?, … } Biểu tượng chuông trên thanh công cụ mở một bảng nổi để thêm, xem và xoá cảnh báo
giá; một thông báo toast hiện lên khi cảnh báo kích hoạt. Đường cảnh báo cũng kéo được — nắm một đường trên biểu đồ và trượt để đổi giá
(di chuyển cảnh báo sẽ đặt nó về trạng thái chờ kích hoạt lại). Bật sẵn theo mặc định — tắt bằng alerts: false. Điều khiển bằng code qua API của Chart và các sự kiện cảnh báo có kiểu:
// Add from code (condition: 'crossing' | 'crossingUp' | 'crossingDown'
// | 'greaterThan' | 'lessThan')
const id = chart.addAlert(64200, 'crossingUp', 'breakout')
chart.removeAlert(id)
chart.getAlerts() // PriceAlert[]
chart.saveAlerts('tcw:alerts:BTCUSDT') // localStorage persistence
chart.loadAlerts('tcw:alerts:BTCUSDT')
// React to triggers
chart.on('alertTriggered', (e) => {
console.log('hit', e.payload.price, e.payload.message)
})
// also: 'alertAdd' / 'alertRemove' / 'alertUpdate' (fired on drag)
// Indicator alerts: bind to an indicator line via channel '<instanceId>:<key>'.
// In the widget, the alerts panel's source dropdown lists every active line.
const ema = chart.addIndicator('rsi')
chart.addAlert(70, 'crossingUp', 'RSI overbought', `${ema}:rsi`, 'RSI') Có thể bật âm thanh và/hoặc thông báo trên màn hình máy tính khi cảnh báo kích hoạt (cả hai mặc định
tắt). sound: true phát tiếng bíp có sẵn; truyền một URL để dùng
âm thanh riêng. desktop: true dùng Notification API và hỏi
quyền ở lần dùng đầu tiên.
new ChartWidget(host, {
alertNotifications: { sound: true, desktop: true },
}) Công tắc tính năng
112 công tắc, cái nào cũng bật cho tới khi tắt: tên không có dấu chấm là cả một tính năng, tắt ở mọi nơi nó xuất hiện; tên có dấu chấm là một chỗ. Công tắc đổi được khi widget đang chạy. Danh sách đầy đủ và các option cũ tương ứng nằm ở Tùy biến.
new ChartWidget(host, { features: { 'toolbar.replay': false, 'sidebar.patterns': false, hotkeys: false } }) // một nút, một nhóm công cụ vẽ, mọi phím tắt
widget.setFeatures({ sidebar: false, statusBar: false })
widget.isFeatureOn('sidebar') // false
widget.getFeatures() // mọi công tắc, bật hay tắt Nút và mục menu của riêng bạn
Thêm nút vào thanh công cụ (một biểu tượng có sẵn hoặc phần tử của bạn, chữ, một công tắc) và thêm mục vào các menu chuột phải của biểu đồ, xếp sau các mục của widget.
const news = widget.addToolbarButton({
id: 'news',
label: 'News',
icon: 'bell', // hoặc một phần tử <svg>; hoặc text: 'News'
side: 'right', // 'left' nằm cùng các nút điều khiển biểu đồ
toggle: true,
onClick: () => news?.setActive(togglePanel()),
})
news?.setText('3')
news?.remove()
new ChartWidget(host, {
chartMenuItems: ({ area, price, time }) => area === 'plot' && price !== undefined
? [{ label: `Copy ${price.toFixed(2)}`, icon: 'check', onSelect: () => copy(price) }]
: [],
}) Ngoài ra còn nút menu trên toolbar, nút trên thanh công cụ vẽ, mục trên thanh trạng thái, chỗ riêng cho bạn trong từng thanh và trên biểu đồ, và mục ở cuối menu của một hình vẽ hay một chỉ báo (xem Tùy biến):
widget.addToolbarDropdown({ id: 'scans', label: 'Scans', icon: 'layers', items: () => [...] }) // được hỏi lại mỗi lần mở
widget.addSidebarButton({ id: 'ruler', label: 'Ruler', icon: 'ruler', toggle: true, onClick })
widget.addStatusBarItem({ id: 'latency', text: '12 ms', label: 'Latency' })
widget.getSlot('chart') // một lớp trên biểu đồ
widget.addHotkey({ keys: 'Alt+N', label: 'New note', onPress }) // phím tắt của riêng bạn, thay phím tắt của widget nếu trùng phím
new ChartWidget(host, {
drawingMenuItems: ({ id, type }) => [{ label: 'Share', onSelect: () => share(id) }],
indicatorMenuItems: ({ instanceId, indicatorId }) => [{ label: 'Explain', onSelect: () => explain(indicatorId) }],
}) ChartWidgetGrid
Nhiều widget biểu đồ đặt cạnh nhau, mỗi biểu đồ có mã, khung thời gian, chỉ báo và hình vẽ riêng. Một thanh phía trên chọn cách sắp xếp, liên kết các biểu đồ và lưu cả lưới thành một bố cục có tên. Biểu đồ được bấm gần nhất là biểu đồ đang chọn (có viền).
import { ChartWidgetGrid } from '@tradecanvas/chart/widget'
const grid = new ChartWidgetGrid(host, {
layout: '2x2', // '1x1' '1x2' '2x1' '2x2' '1x3' '3x1' '2x3' '3x2'
widget: { timeframe: '1h' }, // cho mọi biểu đồ
adapter: () => new BinanceAdapter(), // mỗi biểu đồ một adapter: một adapter giữ một luồng
cells: [{ symbol: 'BTCUSDT' }, { symbol: 'ETHUSDT' }, { symbol: 'SOLUSDT' }, { symbol: 'BNBUSDT' }],
sync: { crosshair: true, time: false, symbol: false, interval: false, drawings: false, replay: false },
})
grid.setLayout('1x2')
grid.setSync({ interval: true }) // đưa các biểu đồ khác về cùng khung với biểu đồ đang chọn
grid.applyOverrides({ 'grid.vertical.visible': false }) // giao diện của mọi biểu đồ, kể cả biểu đồ thêm sau
grid.setFeatures({ 'toolbar.replay': false }) // công tắc của mọi biểu đồ, kể cả biểu đồ thêm sau
grid.getActiveWidget().getChart()
grid.getLayoutSession()?.saveAs('Majors')
// Từng biểu đồ ngay khi được tạo (lúc đầu và khi lưới thêm ô)
new ChartWidgetGrid(host, {
onChartAdd: (widget, index) => widget.getChart().addIndicator('ema', { period: 21 }),
}) Đồng bộ con trỏ chữ thập hiện thời điểm dưới con trỏ chuột trên mọi biểu đồ; đồng bộ thời gian cuộn và phóng to/thu nhỏ các biểu đồ khác theo biểu đồ đang dùng; hình vẽ được sao chép sang các biểu đồ đang hiện cùng mã (bật mục này sẽ gộp hình vẽ của các biểu đồ đó lại, không mất hình nào); đồng bộ phát lại cho các biểu đồ khác phát lại đến cùng thời điểm với biểu đồ đang phát lại (biểu đồ có nến dài hơn các bước phát lại sẽ hiện trọn nến chứa thời điểm đó). Các biểu đồ bị bớt đi khi lưới thu nhỏ sẽ được cất đi, vẫn được giữ trong bố cục đã lưu, và trở lại nguyên như cũ khi lưới mở rộng lại; một biểu đồ hoàn toàn mới sẽ mở theo mã và khung thời gian của biểu đồ đang chọn nếu hai mục đó đang được đồng bộ.
ChartGrid
Bố cục nhiều biểu đồ headless (không có thanh công cụ) đồng bộ với nhau; xem ChartWidgetGrid nếu cần giao diện đầy đủ.
import { ChartGrid } from '@tradecanvas/chart'
const grid = new ChartGrid(host, { layout: '2x2', theme: 'dark' })
// Mỗi biểu đồ một adapter: một adapter giữ một luồng
await grid.connectAll(() => new BinanceAdapter(), ['BTCUSDT','ETHUSDT','SOLUSDT','BNBUSDT'], '5m')
grid.setLayout('1x2') Bố cục: '1x1', '1x2', '2x1', '2x2', '1x3', '3x1', '2x3', '3x2'.