montzar-chart
montzar-chart
Vue 3 candlestick chart with TradingView-style drawing tools on lightweight-charts.
The chart does not fetch your API. You load OHLC in the host app and pass candles.
Install
npm install montzar-chart vue lightweight-charts
vue (^3.5.0) and lightweight-charts (^5.2.1) are peer dependencies. ESM-only.
Usage
<script setup lang="ts">
import { ref, watch } from 'vue'
import { Chart, type ChartCandle, type ChartInterval, type ChartRange, type ChartSymbol } from 'montzar-chart'
import 'montzar-chart/styles.css'
const symbol: ChartSymbol = { id: 'XAUUSD', title: 'XAU / USD' }
const interval = ref<ChartInterval>('15m')
const range = ref<ChartRange>('24h')
const candles = ref<ChartCandle[]>([])
const lastPrice = ref<number | null>(null)
const loading = ref(false)
async function load() {
loading.value = true
try {
const rows = await fetchYourOhlc({ interval: interval.value, range: range.value })
candles.value = rows.map((row) => ({
time: Math.floor(new Date(row.date).getTime() / 1000),
open: row.open,
high: row.high,
low: row.low,
close: row.close,
}))
} finally {
loading.value = false
}
}
watch([interval, range], load, { immediate: true })
</script>
<template>
<Chart
:symbol="symbol"
:candles="candles"
:last-price="lastPrice"
:loading="loading"
:empty="!candles.length"
storage-key="demo"
v-model:interval="interval"
v-model:range="range"
/>
</template>
time is UNIX seconds, not milliseconds. Import montzar-chart/styles.css once.
Host time / date labels
Candle time stays UNIX seconds (UTC). The chart does not hardcode a timezone or calendar. Pass formatters from the host app:
<Chart
:time-formatter="formatChartTime"
:tick-mark-formatter="formatChartTick"
...
/>
// Example: Iran / Jalali in the host — other apps pass their own helpers
function formatChartTime(time: number) {
return formatUnixInHostTz(time, 'full') // host-owned
}
function formatChartTick(time: number, tickMarkType: number) {
// tickMarkType: 0=Year 1=Month 2=Day 3=Time 4=TimeWithSeconds
return formatUnixTickInHostTz(time, tickMarkType)
}
Omit either prop to keep the lightweight-charts default (browser local).
When the user changes interval or range, update your fetch and replace candles. Pass live ticks as lastPrice. For bid/ask last-price lines (MetaTrader-style), also pass askPrice and bidPrice; dual lines appear only when both are finite numbers.
To limit UI choices (for example when your API only supports a few timeframes), pass intervals and/or ranges:
<Chart
:intervals="['1m', '5m', '15m', '1h']"
:ranges="['1h', '24h', '7d', '30d']"
...
/>
Omit either prop (or pass an empty array) to keep the full built-in list. If the current v-model:interval / v-model:range is not in the allowed list, the chart switches to the first allowed value.
Price pick (interactionMode)
Default is analysis: left-click pans / draws as before. Pass interaction-mode="trading" to add a desktop context-menu item «ثبت سفارش در این قیمت». Choosing it emits @price-pick and does not place an order — order UI stays in the host app.
For tap-to-pick (mobile-first, also works on desktop), the host arms the chart:
<Chart
interaction-mode="trading"
:price-pick-armed="armed"
@price-pick="onPricePick"
...
/>
function onPricePick(payload: { price: number; time: number | null; source: 'context-menu' | 'tap' }) {
// `price` is in the same units as `candles` (e.g. toman). Convert in the host if the API uses rial.
}
While pricePickArmed is true, a tap/click on the canvas emits source: 'tap' and pan/draw are paused. Right-click place-order stays available. Bid/ask lines are unrelated and stay as they are.
Trading lines (tradingLines)
The chart only draws host-owned price lines. The host maps backend rows and passes visual props. The package does not know Limit, Stop, TP, Position, or admin.
<Chart
:trading-lines="tradingLines"
@trading-line-action="onTradingLineAction"
@trading-line-move="onTradingLineMove"
...
/>
const tradingLines: ChartTradingLine[] = [
{ id: 'ordri:12', kind: 'ordri', side: 'buy', price: 4_200_000, label: 'اردری خرید', lineStyle: 'solid' },
{ id: 'trigger:7', kind: 'trigger', side: 'sell', price: 4_250_000, condition: 'gte', label: 'حد فروش', cancelable: true, lineStyle: 'dashed' },
{ id: 'tp:3', kind: 'trigger', side: 'sell', price: 4_300_000, label: 'حد سود', draggable: true, cancelable: true, color: '#0d9488' },
]
color/lineStyleare host-owned. If omitted: buy green / sell red;ordrisolid, otherwise dashedpriceis in the same units ascandles- a short click near a line emits
@trading-line-action{ id, kind, action: 'cancel' | 'click' }(cancelwhencancelable) - set
draggable: trueto allow vertical drag. The chart emits@trading-line-move{ id, kind, price, phase: 'start' | 'move' | 'end' | 'cancel' }. Persist onend; the package does not call your API - pass
armed-trading-line-idwith adraggableline id to let the next pointerdown anywhere on the canvas drag/place that line (no 8px hit). Clicking the line itself still uses the normal grab/click path. Pan is paused for that gesture; cursor isns-resize
Props
| Prop | Type | Role |
|---|---|---|
symbol |
{ id, title, logo? } |
Header title / logo |
candles |
{ time, open, high, low, close }[] |
OHLC |
lastPrice |
number | null |
Live last price (updates the forming candle) |
askPrice |
number | null |
Live buy/ask; with bidPrice shows two last-price lines |
bidPrice |
number | null |
Live sell/bid; with askPrice shows two last-price lines |
loading |
boolean |
Loading message |
empty |
boolean |
Empty message |
symbols |
ChartSymbol[] |
Header symbol search |
storageKey |
string |
localStorage key for drawings |
intervals |
ChartInterval[] |
Timeframes shown in the picker (default: all) |
ranges |
ChartRange[] |
Range chips shown at the bottom (default: all) |
v-model:interval |
1m … 1M |
Timeframe |
v-model:range |
1h | 24h | 7d | 30d |
Range chips |
interactionMode |
'analysis' | 'trading' |
Default analysis. trading shows the place-order context item |
pricePickArmed |
boolean |
With trading, tap/click emits @price-pick (source: 'tap') |
tradingLines |
ChartTradingLine[] |
Host-owned price lines (color/label from host) |
armedTradingLineId |
string | null |
Pointerdown anywhere drags that draggable line |
timeFormatter |
(time: number) => string |
Host-owned crosshair time label (UNIX seconds UTC). Omit → library default |
tickMarkFormatter |
(time, tickMarkType, locale) => string | null |
Host-owned axis ticks. Omit → library default |
@select-symbol |
id |
Symbol picked in the header |
@price-pick |
{ price, time, source } |
Place-order from context menu or armed tap |
@trading-line-action |
{ id, kind, action } |
Click near a host trading line |
@trading-line-move |
{ id, kind, price, phase } |
Vertical drag of a draggable line |
Versions
After you publish a new version (0.1.1, 0.2.0, …):
npm install montzar-chart@latest
Use "montzar-chart": "^0.7.1" so npm update receives compatible updates.
Maintainers: from the repo root, npm version patch -w montzar-chart then npm publish -w montzar-chart --access public.
License
MIT
Related Packages
Financial (Candlestick/Ohlc) charts with chartjs.org and npm package vue-chartjs
Vue 3 component for @tradecanvas/chart — reactive props, template ref, zero boil...
Vue 3 wrapper for @rekurt/openkline-core — component and useOHLCVChart composabl...
This is the first part of the admetricks challenge, that consist on creating two...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.7.2 | ^3.5.0 | MIT | |
| 0.7.1 | ^3.5.0 | MIT | |
| 0.7.0 | ^3.5.0 | MIT | |
| 0.6.0 | ^3.5.0 | MIT | |
| 0.5.0 | ^3.5.0 | MIT | |
| 0.4.0 | ^3.5.0 | MIT | |
| 0.3.0 | ^3.5.0 | MIT | |
| 0.2.4 | ^3.5.0 | MIT | |
| 0.2.3 | ^3.5.0 | MIT | |
| 0.2.2 | ^3.5.0 | MIT | |
| 0.2.1 | ^3.5.0 | MIT |