@simahfud/klinecharts-pro
v1.2.1
Published
Professional TradingView-inspired charting component built on KLineChart. Features bar replay, keyboard shortcuts, 25 drawing tools, 6 chart types, and custom tool API.
Maintainers
Readme
KLineChart Pro
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
✨ Features
Core
- 📊 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
Chart Types (6 types)
| 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 |
Drawing Tools (25 built-in)
| 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 |
Technical Indicators (26 built-in)
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
Advanced Features
| 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 |
📦 Installation
Using npm
npm install klinecharts @simahfud/klinecharts-proUsing unpkg or jsDelivr (CDN)
The 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.
Building from source
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.css🚀 Quick Start
import { 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())Constructor Options
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
})🔧 API Reference
Core Methods
// 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()Chart Type
// Switch chart type
chart.setChartType('ohlc') // candle_solid | candle_stroke | candle_up_stroke | candle_down_stroke | ohlc | area
chart.getChartType() // Returns current typeBar Replay
// 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 cleanupObject Tree & Style Editor
// Open Object Tree modal (lists all overlays)
chart.showObjectTree()
// Open Style Editor for a specific overlay
chart.showStyleEditor('overlay_id_123')Keyboard Shortcuts
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()Chart Templates
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')Crosshair Sync
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()Custom Indicators & Overlays
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'
})Price Alerts
chart.setAlert({ id: 'alert1', price: 150.00, condition: 'crosses_above', symbol: 'AAPL' })
chart.getAlerts()
chart.removeAlert('alert1')Multi-Symbol Comparison
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.
Multi-Chart Layout
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()💾 Settings Save & Database Sync
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 })
}
})Applying Data from your Database
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()📊 DefaultDatafeed
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
}🏗 Architecture
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)🧪 Testing
# Run all tests
npx vitest run
# Run with verbose output
npx vitest run --reporter=verbose
# Watch mode
npx vitestCurrent status: 38/38 tests passing ✅
🛠 Development
# Install dependencies
npm install
# Start dev server
npm run dev
# Build for production
npm run build-core
# Run tests
npx vitest run📄 License
Apache License 2.0
