montzar-chart

Vue 3 candlestick chart shell with TradingView-style drawing tools on lightweight-charts
1,880
Install
npm install montzar-chart
Latest Version:0.7.2
Vue:3
License:MIT
Last Updated:Sep 8, 2026
Links: npm
Maintainer: montzar

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 / lineStyle are host-owned. If omitted: buy green / sell red; ordri solid, otherwise dashed
  • price is in the same units as candles
  • a short click near a line emits @trading-line-action { id, kind, action: 'cancel' | 'click' } (cancel when cancelable)
  • set draggable: true to allow vertical drag. The chart emits @trading-line-move { id, kind, price, phase: 'start' | 'move' | 'end' | 'cancel' }. Persist on end; the package does not call your API
  • pass armed-trading-line-id with a draggable line 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 is ns-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

vue-chartjs-financial

Financial (Candlestick/Ohlc) charts with chartjs.org and npm package vue-chartjs

3,033 7
@tradecanvas/vue

Vue 3 component for @tradecanvas/chart — reactive props, template ref, zero boil...

2,250 27
@rekurt/openkline-vue

Vue 3 wrapper for @rekurt/openkline-core — component and useOHLCVChart composabl...

205 1
@efont/vue-baselib

vue-baselib

2,721 2
@phronesyss/bar-chart

This is the first part of the admetricks challenge, that consist on creating two...

407 0

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