A professional-grade, TradingView-inspired charting component built on KLineChart, powered by SolidJS. Features 25 built-in drawing tools, bar replay, keyboard shortcuts, multi-chart type support, multi-symbol comparison overlays, price alerts, chart templates, and a complete custom tool API.
π Repository: github.com/SiMahfud/klinechartpro
π¦ Forked from klinecharts/pro
- π Built on KLineChart with SolidJS rendering
- π¨ Light & Dark themes with CSS custom properties
- π 5 locales: Chinese, English, Indonesian, Japanese, Korean
- πΎ Data persistence via localStorage (symbols, periods, indicators, drawings)
- π Price alert system with browser notifications
- π Multi-symbol comparison mode
- β‘ Performance monitoring panel (FPS, memory, WS latency)
- βΏ Full modal accessibility (focus trap, keyboard nav, ARIA)
- π± Responsive layout with mobile support
| Type | Key | Description |
|---|---|---|
| Candles | candle_solid |
Standard filled candlestick (default) |
| Hollow Candles | candle_stroke |
All candles outlined |
| Up Hollow | candle_up_stroke |
Up candles hollow, down filled |
| Down Hollow | candle_down_stroke |
Down candles hollow, up filled |
| OHLC Bars | ohlc |
Traditional open-high-low-close bars |
| Area | area |
Filled area chart |
| Category | Tools |
|---|---|
| Lines | Horizontal/Vertical (straight, ray, segment), Trend Line, Ray, Segment, Arrow, Price Line |
| Channels | Price Channel, Parallel Line |
| Shapes | Circle, Rectangle, Parallelogram, Triangle |
| Fibonacci | Line, Segment, Circle, Spiral, Fan, Extension |
| Gann | Gann Box |
| Waves | XABCD, ABCD, 3-Wave, 5-Wave, 8-Wave, Any Wave |
| Trading | Long Position, Short Position, Measure Tool |
| Annotation | Volume Profile (FRVP), Price Range, Text Note, Anchored VWAP, Price Label |
Main chart: MA, EMA, SMA, BOLL, SAR, BBI
Sub chart: VOL, MACD, KDJ, RSI, BIAS, BRAR, CCI, DMI, CR, PSY, DMA, TRIX, OBV, VR, WR, MTM, EMV, ROC, PVT, AO
| Feature | Description |
|---|---|
| β― Bar Replay | Step through historical data bar by bar with play/pause/speed controls |
| β¨οΈ Keyboard Shortcuts | Configurable hotkeys for all chart operations |
| π³ Object Tree | Manage all drawings β toggle visibility, select, delete |
| πΎ Chart Templates | Save/load indicator + style presets via localStorage |
| π·οΈ Price Labels | Custom price markers on Y-axis |
| π Crosshair Sync | Synchronize crosshair across multiple chart instances |
| π¨ Style Editor | Edit drawing properties (color, width, style) via right-click |
| π Context Menu | Right-click overlay menu with Edit/Hide/Delete/Alert/Performance actions |
| π Chart Type Selector | Switch between 6 chart types from the toolbar |
| π₯οΈ Multi-Chart Layout | onLayoutClick callback for parent-managed split views |
npm install klinecharts @simahfud/klinecharts-proThe library is published as a UMD bundle, so you can load it directly via <script> tags:
<!-- KLineChart (dependency, must be loaded first) -->
<script src="https://unpkg.com/klinecharts/dist/klinecharts.min.js"></script>
<!-- KLineChart Pro -->
<!-- using unpkg -->
<script src="https://unpkg.com/@simahfud/klinecharts-pro/dist/klinecharts-pro.umd.js"></script>
<!-- OR using jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@simahfud/klinecharts-pro/dist/klinecharts-pro.umd.js"></script>
<!-- CSS styles (required) -->
<link rel="stylesheet" href="https://unpkg.com/@simahfud/klinecharts-pro/dist/klinecharts-pro.css" />After loading, the global variable klinechartspro is available:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/@simahfud/klinecharts-pro/dist/klinecharts-pro.css" />
</head>
<body>
<div id="chart-container" style="width:100%;height:600px;"></div>
<script src="https://unpkg.com/klinecharts/dist/klinecharts.min.js"></script>
<script src="https://unpkg.com/@simahfud/klinecharts-pro/dist/klinecharts-pro.umd.js"></script>
<script>
// Access via global: klinechartspro
const { KLineChartPro, DefaultDatafeed } = klinechartspro
const chart = new KLineChartPro({
container: document.getElementById('chart-container'),
symbol: { ticker: 'AAPL', name: 'Apple Inc.', market: 'stocks' },
period: { multiplier: 1, timespan: 'day', text: 'D' },
datafeed: new DefaultDatafeed('YOUR_POLYGON_API_KEY'),
theme: 'dark',
locale: 'en-US'
})
</script>
</body>
</html>π‘ Note: For your fork, after running
npm run build-core, thedist/folder will contain your customized UMD/ES bundles. You can self-host these files or publish to npm to use via CDN.
git clone https://github.com/SiMahfud/klinechartpro.git
cd klinechartpro
npm install
npm run build-core # β dist/klinecharts-pro.umd.js + klinecharts-pro.js + klinecharts-pro.cssimport { KLineChartPro, DefaultDatafeed } from '@simahfud/klinecharts-pro'
const chart = new KLineChartPro({
container: 'chart-container',
symbol: { ticker: 'AAPL', name: 'Apple Inc.', market: 'stocks' },
period: { multiplier: 1, timespan: 'day', text: 'D' },
datafeed: new DefaultDatafeed('YOUR_POLYGON_API_KEY'),
theme: 'dark',
locale: 'en-US'
})
// Wait for chart to be ready before calling methods
await chart.ready()
console.log('Chart initialized:', chart.getSymbol())const chart = new KLineChartPro({
container: 'chart-container', // Required: DOM element or selector
symbol: { ticker: 'AAPL' }, // Required: Initial symbol
period: { multiplier: 1, timespan: 'day', text: 'D' }, // Required
datafeed: myDatafeed, // Required: Datafeed implementation
theme: 'dark', // 'dark' | 'light'
locale: 'en-US', // 'en-US' | 'zh-CN' | 'id-ID' | 'ja-JP' | 'ko-KR'
drawingBarVisible: true, // Show drawing toolbar
mainIndicators: ['MA', 'EMA'], // Initial main indicators
subIndicators: ['VOL', 'MACD'], // Initial sub indicators
chartType: 'candle_solid', // Initial chart type
renkoBrickSize: 10, // Renko brick size
rangeBarSize: 10, // Range bar size
onSettingsChange: (settings) => {}, // Called on settings change
onDrawingsChange: (ticker, d) => {},// Called on drawings change
onLayoutClick: () => {}, // Called when Layout button is clicked
})// Theme
chart.setTheme('dark') // 'dark' | 'light'
chart.getTheme()
// Locale
chart.setLocale('en-US') // 'en-US' | 'zh-CN' | 'id-ID' | 'ja-JP' | 'ko-KR'
chart.getLocale()
// Symbol & Period
chart.setSymbol({ ticker: 'MSFT', name: 'Microsoft' })
chart.getSymbol()
chart.setPeriod({ multiplier: 5, timespan: 'minute', text: '5m' })
chart.getPeriod()
// Timezone
chart.setTimezone('Asia/Jakarta')
chart.getTimezone()
// Styles (deep partial merge)
chart.setStyles({ candle: { bar: { upColor: '#26A69A' } } })
chart.getStyles()// Switch chart type
chart.setChartType('ohlc') // candle_solid | candle_stroke | candle_up_stroke | candle_down_stroke | ohlc | area
chart.getChartType() // Returns current type// Start replay using current chart data
chart.startReplay('current')
// Or start replay loading custom historical data
chart.startReplay('custom')
// Stop and restore original data
chart.stopReplay()Programmatic control via BarReplayManager:
import { BarReplayManager } from '@simahfud/klinecharts-pro'
const replay = new BarReplayManager(chartWidget, datafeed)
replay.setHandlers({
onStep: (index, total, bar) => console.log(`${index}/${total}`),
onPlay: () => console.log('Playing'),
onPause: () => console.log('Paused'),
onEnd: () => console.log('Replay ended'),
onStatusChange: (status) => console.log('Status:', status)
})
await replay.start({ dataSource: 'current', speed: 500, startFrom: 0 })
// Controls
replay.play() // Auto-advance
replay.pause() // Pause
replay.stepForward() // Next bar
replay.stepBackward() // Previous bar
replay.seekTo(50) // Jump to index
replay.setSpeed(100) // 100ms per bar
// State
replay.getStatus() // 'idle' | 'playing' | 'paused' | 'ended'
replay.getIndex() // Current position
replay.getTotal() // Total bars
// Cleanup
replay.stop() // Restore original data
replay.destroy() // Full cleanup// Open Object Tree modal (lists all overlays)
chart.showObjectTree()
// Open Style Editor for a specific overlay
chart.showStyleEditor('overlay_id_123')import { KeyboardShortcutManager } from '@simahfud/klinecharts-pro'
const shortcuts = new KeyboardShortcutManager(containerElement)
shortcuts.register({ key: 'ctrl+z', description: 'Undo', callback: () => drawings.undo() })
shortcuts.register({ key: 'ctrl+y', description: 'Redo', callback: () => drawings.redo() })
shortcuts.register({ key: 'delete', callback: () => chart.removeOverlay() })
shortcuts.register({ key: 'escape', callback: () => chart.removeOverlay() })
// Disable during modals
shortcuts.setEnabled(false)
shortcuts.setEnabled(true)
shortcuts.destroy()import { ChartTemplateManager } from '@simahfud/klinecharts-pro'
const templates = new ChartTemplateManager(chartStore)
// Save current configuration
templates.saveTemplate({
name: 'My Scalping Setup',
mainIndicators: ['EMA', 'BOLL'],
subIndicators: ['RSI', 'MACD'],
theme: 'dark',
period: { multiplier: 5, timespan: 'minute', text: '5m' },
createdAt: Date.now()
})
// List and load
templates.getTemplateNames() // ['My Scalping Setup']
const tmpl = templates.loadTemplate('My Scalping Setup')
// Delete
templates.deleteTemplate('My Scalping Setup')import { CrosshairSyncManager } from '@simahfud/klinecharts-pro'
const sync = new CrosshairSyncManager()
sync.addChart(chart1)
sync.addChart(chart2)
// Moving crosshair on chart1 now moves it on chart2 and vice versa
sync.removeChart(chart1)
sync.destroy()import { registerCustomIndicator, registerCustomOverlay } from '@simahfud/klinecharts-pro'
// Register a custom indicator (auto-appears in Indicator menu)
registerCustomIndicator({
template: { name: 'MyRSI', calc: (dataList) => { /* ... */ }, figures: [{ key: 'value', type: 'line' }] },
label: 'My Custom RSI',
paneType: 'sub'
})
// Register a custom overlay (auto-appears in Drawing menu)
registerCustomOverlay({
template: { name: 'myTool', totalStep: 2, createPointFigures: (params) => { /* ... */ } },
label: 'My Tool'
})chart.setAlert({ id: 'alert1', price: 150.00, condition: 'crosses_above', symbol: 'AAPL' })
chart.getAlerts()
chart.removeAlert('alert1')import { ComparisonManager } from '@simahfud/klinecharts-pro'
const comparison = new ComparisonManager(chartWidget, datafeed)
await comparison.addSymbol({ ticker: 'MSFT' }, period, from, to)
comparison.getSymbols()
comparison.toggleVisibility('MSFT')
comparison.removeSymbol('MSFT')
comparison.destroy()UI Integration: Click the + Compare button in the toolbar to add a comparison symbol via the search modal. Each comparison symbol is drawn as a colored line chart on the main candle pane, normalized to percentage change from the first visible bar. A floating legend in the top-left corner shows active comparisons with a remove button.
const chart = new KLineChartPro({
// ... other options
onLayoutClick: () => {
// Handle layout button click β create your own split view
// For example, create a second chart instance side by side
}
})
// Access the underlying klinecharts widget for advanced integrations
const widget = chart.getWidget()Example: Dual Chart with Crosshair Sync
import { KLineChartPro, CrosshairSyncManager } from '@simahfud/klinecharts-pro'
// Create two chart instances
const chart1 = new KLineChartPro({ container: 'chart-left', ... })
const chart2 = new KLineChartPro({ container: 'chart-right', ... })
// Sync crosshairs
const sync = new CrosshairSyncManager()
sync.addChart(chart1.getWidget())
sync.addChart(chart2.getWidget())
// Cleanup
sync.destroy()The library provides robust API callbacks to help you synchronize the user's chart configuration and drawings directly to your backend database (e.g., MySQL, MongoDB) in real-time.
const chart = new KLineChartPro({
// ...other options
onSettingsChange: (settings) => {
// Fired when theme, timezone, period, or indicators change
console.log('Settings changed:', settings)
// Example: api.post('/save-settings', { settings })
},
onDrawingsChange: (ticker, drawings) => {
// Fired when a user adds, modifies, or deletes a drawing/overlay
console.log(`Drawings updated for ${ticker}:`, drawings)
// Example: api.post(`/save-drawings/${ticker}`, { drawings })
}
})When the user reloads the page or logs in from another device, you can fetch their saved data from your database and inject it into the chart:
// 1. Fetch from your DB
const savedSettings = await api.get('/user/chart-settings')
const savedDrawings = await api.get(`/user/drawings/${ticker}`)
// 2. Wait for chart to be ready
await chart.ready()
// 3. Inject into the chart
if (savedSettings) {
chart.setSettings(savedSettings)
}
if (savedDrawings) {
chart.setDrawings(ticker, savedDrawings)
}
// You can also retrieve current state imperatively:
const currentDrawings = chart.getDrawings(ticker)
const currentSettings = chart.getSettings()import { DefaultDatafeed } from '@simahfud/klinecharts-pro'
// Uses Polygon.io API
const datafeed = new DefaultDatafeed('YOUR_API_KEY')Implements the Datafeed interface:
interface Datafeed {
searchSymbols(search?: string): Promise<SymbolInfo[]>
getHistoryKLineData(symbol: SymbolInfo, period: Period, from: number, to: number): Promise<KLineData[]>
subscribe(symbol: SymbolInfo, period: Period, callback: DatafeedSubscribeCallback): void
unsubscribe(symbol: SymbolInfo, period: Period): void
}src/
βββ index.ts # Entry point + exports
βββ KLineChartPro.tsx # Main class (lifecycle, API)
βββ ChartProComponent.tsx # SolidJS component (all UI wiring)
βββ types.ts # TypeScript interfaces
βββ config.ts # Shared constants
βββ registry.ts # Custom tool registry (auto-menu)
βββ error.ts # Error handling system
βββ store.ts # localStorage persistence
βββ drawing-store.ts # Undo/redo + drawing persistence
βββ comparison.ts # Multi-symbol comparison
βββ bar-replay.ts # Bar replay engine
βββ keyboard-shortcuts.ts # Keyboard shortcut manager
βββ chart-template.ts # Template save/load
βββ crosshair-sync.ts # Multi-chart crosshair sync
βββ DefaultDatafeed.ts # Polygon.io data feed
βββ i18n/ # 5 locale files (168+ keys each)
βββ extension/ # 25 overlay templates
β βββ longPosition.ts # Long Position tool
β βββ shortPosition.ts # Short Position tool
β βββ frvp.ts # Volume Profile
β βββ measure.ts # Measure tool
β βββ priceRange.ts # Price Range zone
β βββ textNote.ts # Text annotation
β βββ anchoredVwap.ts # Anchored VWAP
β βββ priceLabel.ts # Price Label
β βββ ... (17 more)
βββ widget/
β βββ period-bar/ # Toolbar (periods, chart type, settings)
β βββ drawing-bar/ # Drawing toolbar (8 groups)
β βββ replay-bar/ # Replay controls (play/pause/step/speed)
β βββ object-tree/ # Object manager modal
β βββ drawing-style-editor/ # Style editor modal
β βββ context-menu/ # Right-click menu
β βββ error-banner/ # Error notification
β βββ performance-panel/ # Debug panel
β βββ alert-modal/ # Price alerts UI
β βββ ...
βββ component/ # Reusable UI (Modal, Select, List, etc.)
βββ __tests__/ # 38 unit tests (100% pass)
# Run all tests
npx vitest run
# Run with verbose output
npx vitest run --reporter=verbose
# Watch mode
npx vitestCurrent status: 38/38 tests passing β
# Install dependencies
npm install
# Start dev server
npm run dev
# Build for production
npm run build-core
# Run tests
npx vitest runApache License 2.0