High-performance financial chart library with a single-frame generation time of just 2ms, stable scrolling at 190–200fps in a 200Hz environment, native support for AI Agent control, full-link ResizeObserver-driven crisp rendering, and a pluggable architecture.
English | 简体中文
📈 KLineChartQuant
Crisp Rendering · High Performance · Optimized Interaction · Mobile-Friendly
A lightweight financial K-line charting library focused on quantitative trading scenarios. Agent is a first-class citizen — supports AI Agent direct control of chart operations, providing TradingView-level interaction experience.
✨ Core Features
- Agent First / MCP Native - Supports AI Agent direct control of charts via the Model Context Protocol. Built-in WebSocket-bridged MCP server enables any MCP client (Inspector, Claude Desktop, Cursor, etc.) to zoom, pan, add/remove indicators, and change theme in real time
- Crisp Rendering - Full-chain ResizeObserver driven, physical pixel alignment, K-lines, wicks, and lines are sharp and clear on all DPR screens
- Plugin Architecture - Renderer plugin-based design, supporting dynamic registration, configuration, and lifecycle management
- Custom Markers - Supports semantic configuration of custom markers and custom information
- High Performance - Smoothly handles tens of thousands of data points, no lag during zoom or pan; supports 190-200fps on 200Hz displays with single-frame generation time as low as 2ms
- Multi-Backend Rendering - Submit drawing primitives once, render via WebGPU, WebGL, or Canvas2D. WebGPU provides hybrid DOM canvas (no
compositeTocopy), single-command-buffer-per-frame submission with 4x MSAA, and per-instance geometry caching via ResourceTable. Automatic fallback chain: WebGPU → WebGL → Canvas2D. Reaching 190fps on 200Hz displays with per-frame GPU time under 1ms - Optimized Interaction - Stable zoom anchor, precise crosshair cursor, smooth drag
- Mobile-Optimized Interaction - Long-press crosshair for data exploration, tap to dismiss, slide to browse data without triggering chart scroll, gesture-based scroll mode
- Multi-Symbol Comparison - Supports unlimited number of instruments for trend comparison
- Multi-Source Aggregation - Supports aggregation and unification of multiple data sources
- Batch Data Export - Select a date range and export multiple stocks' K-line data into a single CSV file, with progress indication
- Custom Tooltip - Fully customizable tooltip via named slots (
#kline-tooltip,#marker-tooltip), with engine-provided hover data, position, and styling
🚀 Quick Start
Prerequisites
KLineChart requires a stock data backend. Please ensure kmap and stockbao are in the same directory:
workspace/
├── KLineChartQuant/ # This repository
└── stockbao/ # Data backend repository
1. Clone Repositories
git clone https://github.com/363045841/KLineChartQuant.git
git clone https://github.com/363045841/stockbao.git
2. Start Data Backend
cd KLineChartQuant
npm run stockbao
After startup, the API is available at http://localhost:8000
3. Install and Use
npm install @363045841yyt/klinechart @363045841yyt/klinechart-core
Use the component:
<template>
<div class="app-container" :data-theme="currentTheme">
<KlineChart v-model:theme="currentTheme" :custom-data="customData" :settings="chartSettings" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ChartSettings } from '@363045841yyt/klinechart-core'
import { type CustomDataSource, KlineChart } from '@363045841yyt/klinechart'
import demoData from './demo-data.json'
const currentTheme = ref<'light' | 'dark'>('dark')
const customData = ref<CustomDataSource>(demoData as CustomDataSource)
const chartSettings: ChartSettings = {
showGridLines: true,
isAsiaMarket: true,
showVolumePriceMarkers: false,
leftAxisType: 'none',
theme: 'dark',
colorPresetSettings: {
dark: {
candleUpBody: '#e85d04',
candleDownBody: '#1b4332',
crosshairLine: '#faa307',
gridMajor: '#3e2723',
},
},
}
</script>
<style>
.app-container {
display: flex;
flex-direction: column;
height: 80vh;
}
.app-container[data-theme='dark'] {
background: #000;
color: #e5e7eb;
}
</style>
Import CSS in main.ts:
import '@363045841yyt/klinechart/style.css'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
Slot Usage — Custom Tooltip:
<KlineChart>
<template #kline-tooltip="{ hoverData, upColor, downColor }">
<div class="custom-tooltip">
<div class="custom-tooltip__title">
<span>{{ hoverData.stockCode }}</span>
<span>{{ formatTimestamp(hoverData.timestamp, { timeZone: 'Asia/Shanghai' }) }}</span>
</div>
<div
class="custom-tooltip__price"
:style="{ color: hoverData.close >= hoverData.open ? upColor : downColor }"
>
{{ hoverData.close.toFixed(2) }}
</div>
<div class="custom-tooltip__detail">
O: {{ hoverData.open.toFixed(2) }}<br />
H: {{ hoverData.high.toFixed(2) }}<br />
L: {{ hoverData.low.toFixed(2) }}<br />
C: {{ hoverData.close.toFixed(2) }}
</div>
</div>
</template>
</KlineChart>
Slot Usage — Custom Main-Pane Legend:
Providing #legend fully replaces the default Canvas legend. The slot scope is the full LegendTemplateContext (OHLC, timeshare, main indicators, comparisons, layout, colors).
<template #legend="{ index, currentBar, timeshare, indicators, comparisons, colors }">
<div class="my-legend">
<!-- Custom fields added to KLineData[] for PR #98 are exposed through currentBar -->
<div v-if="currentBar" class="my-legend__row">
<span :style="{ color: currentBar.color }">
开盘 {{ currentBar.open.toFixed(2) }} 最高 {{ currentBar.high.toFixed(2) }} 最低
{{ currentBar.low.toFixed(2) }} 收盘 {{ currentBar.close.toFixed(2) }}
</span>
<span v-if="currentBar.volumeText"> Vol {{ currentBar.volumeText }}</span>
</div>
<div v-if="timeshare" class="my-legend__row">
<span :style="{ color: timeshare.changeColor }">
现价 {{ timeshare.price.toFixed(2) }} 涨幅 {{ timeshare.changePercent.toFixed(2) }}%
</span>
</div>
<!-- Using main chart indicator legend data -->
<div v-for="indicator in indicators" :key="indicator.name" class="my-legend__row">
<span>{{ indicator.name }}:</span>
<template v-for="value in indicator.values" :key="value.label">
<span :style="{ color: value.color }">
{{ value.label }} {{ value.value.toFixed(3) }}
</span>
</template>
</div>
<!-- Using comparison commodity data -->
<div
v-for="comparison in comparisons"
:key="comparison.symbol"
class="my-legend__row"
:style="{ color: comparison.percentColor }"
>
{{ comparison.symbol }}
{{ comparison.percent > 0 ? '+' : '' }}{{ comparison.percent.toFixed(2) }}%
</div>
</div>
</template>
4. (Optional) Enable MCP / AI Agent Control
npm install @363045841yyt/klinechart-ai-runtime
<template>
<div class="app-container">
<KlineChart ref="chartRef" :mcp="mcpConfig" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { KlineChart } from '@363045841yyt/klinechart'
import { executeTool } from '@363045841yyt/klinechart-ai-runtime'
const chartRef = ref<InstanceType<typeof KlineChart> | null>(null)
const mcpConfig = {
wsUrl: 'ws://localhost:8080',
autoReconnect: true,
onToolCall: (call) => {
const ctrl = chartRef.value?.getController?.()
if (!ctrl) return { success: false, error: 'Controller not ready' }
return executeTool(ctrl, call)
},
}
</script>
<style>
.app-container {
height: 80vh;
}
</style>
Then start the MCP server:
cd packages/ai-runtime
pnpm inspect
Connect via MCP Inspector and call chart.zoomToLevel, indicators.add, etc.
📖 More Documentation
- Rendering Pipeline - Current paint path: FrameTransaction, Scene/Layer, Renderer backends
📋 Component Props
| Prop | Type | Default | Description |
|---|---|---|---|
| semanticConfig | SemanticChartConfig |
— | Semantic configuration (optional). When provided, drives chart data, indicators, markers and chart options |
| dataFetcher | DataFetcher |
built-in | Data fetching function. Defaults to an internal fetcher that proxies /api/stock |
| theme | 'light' | 'dark' |
— | Chart theme. Use v-model:theme for two-way binding |
| isFullscreen | boolean |
— | Controlled fullscreen state. Leave unbound for internal (non-controlled) mode |
| timezone | string |
'Asia/Shanghai' |
Time zone for date/time display |
| yPaddingPx | number |
20 | Y-axis padding in pixels |
| minKWidth | number |
1 | Minimum K-line width (logical pixels) |
| maxKWidth | number |
50 | Maximum K-line width (logical pixels) |
| rightAxisWidth | number |
0 | Right price axis width |
| leftAxisWidth | number |
0 | Left price axis width (0 = hidden) |
| bottomAxisHeight | number |
24 | Bottom time axis height |
| priceLabelWidth | number |
60 | Price label extra width for showing change percentage |
| zoomLevels | number |
20 | Total number of zoom levels |
| initialZoomLevel | number |
3 | Initial zoom level (1 ~ zoomLevels) |
| customData | CustomDataSource |
— | Inline data bundle: { symbol?, period?, data, comparisons? }. Bypasses the fetcher pipeline entirely. See example above |
| teleportContainer | string | HTMLElement |
— | Teleport target for dropdowns/modals (CSS selector or element). Defaults to internal .chart-wrapper |
| mcp | McpConfig |
— | MCP/AI runtime bridge config: { wsUrl?, autoReconnect?, onToolCall? }. See @363045841yyt/klinechart-ai-runtime |
🗺️ Roadmap
- K-line zoom anchor stability, improved zoom feel
- Right axis detached from scroll container, completely solving clipping issues
- Blank area drawing support
- Limit vertical pan range to prevent viewport from leaving data
- Drawing system
- Right axis zoom
- Latest price line and right axis label style optimization
- Area primitive tools and rendering
- More advanced drawing tools
- Support for minute, multi-day, monthly, and yearly K-line display
- Support convert the drawing to quant code
📦 Packages
| Package | Description | npm |
|---|---|---|
@363045841yyt/klinechart-core |
Headless chart engine + controllers | npm |
@363045841yyt/klinechart |
Vue 3 bindings | npm |
@363045841yyt/klinechart-react |
React bindings | npm |
@363045841yyt/klinechart-angular |
Angular bindings | npm |
@363045841yyt/klinechart-ai-runtime |
MCP server + AI tool schemas (optional) | npm |
🚀 What's New
- v0.9.0 Self-developed Core-layer reactive state model migration, timing issues eliminated
- v0.9.0 Single-path Scene renderer + WebGPU backend (hybrid DOM canvas, no compositeTo), FrameTransaction reactivity, device-lost recovery, auto-fallback WebGPU → WebGL → Canvas2D
- v0.8 Symbol comparison, multi-source data aggregation
- v0.7 Renderer registration chain AOP refactoring with decorator syntax, monorepo split, Vue/React bindings (experimental), standalone core package, tokenized color system
- v0.6.10 Unified WebGL rendering context sharing for all panes, plus sub-pane lifecycle refactoring — centralized pane instance management via SubPaneManager with first-class paneId identity
- v0.6.6 Comprehensive rendering optimizations: batched price-to-Y calculations, cached tick positions and geometry, optimized month-key operations; achieves stable 190-200fps on 200Hz displays with frame generation time down to 2ms
- v0.6.3 WebGL rendering for K-lines, volume bars, and MACD bars; significant performance boost across the board
- v0.6.1 Dual-layer canvas architecture: Main + Overlay separation with UpdateLevel filtering, achieves stable 180fps with low jitter on 200Hz displays
- v0.6.0 Stateless indicator pipeline: MA/BOLL/EXPMA/ENE/RSI/CCI/STOCH/MOM/WMSR/KST/FASTK now use unified Calculator → Scheduler → StateStore → Renderer architecture for better performance and maintainability
- v0.5.6 Logarithmic price axis with evenly distributed grid lines at pixel level
- v0.5.2 Advanced drawing tools: parallel channel, regression channel, smooth top/bottom, and non-intersecting channel
- v0.5.0 Complete drawing tool system, supporting line, rectangle, text drawing and style editing
- v0.4 Modern UI, left toolbar, right axis optimization, TradingView-style zoom feel
No comments yet
Be the first to share your take.