Plotting System
QFChart’s plotting system is designed to render technical indicators with flexible styling options. Each indicator consists of one or more plots, where each plot is a time-series of values with associated visual styling.
Core Concepts
Data Structure
An indicator is composed of:
- Indicator: A collection of plots that represent a single technical indicator (e.g., MACD contains three plots: MACD line, signal line, and histogram)
- Plot: A single visual series with a specific style (line, histogram, shape, etc.)
- Point: An individual data point in a plot with a timestamp and value
TypeScript Interfaces
interface IndicatorPoint {
time: number; // Unix timestamp in milliseconds
value: number | null; // Numeric value or null to skip rendering
options?: {
// Optional per-point styling overrides
color?: string;
offset?: number;
// ... style-specific options
};
}
interface IndicatorPlot {
data: IndicatorPoint[];
options: IndicatorOptions; // Global styling for this plot
}
interface Indicator {
id: string; // Unique identifier
plots: { [name: string]: IndicatorPlot };
paneIndex: number; // 0 = main chart, >0 = separate pane
height?: number; // Pane height in percentage (for separate panes)
collapsed?: boolean;
}
Adding Indicators
const plots = {
SMA: {
data: [
{ time: 1620000000000, value: 50200 },
{ time: 1620086400000, value: 50300 },
// ...
],
options: {
style: 'line',
color: '#ff9900',
linewidth: 2,
},
},
};
// Add as overlay on main chart
chart.addIndicator('SMA_14', plots, { isOverlay: true });
// Or add as separate pane
chart.addIndicator('RSI_14', plots, { isOverlay: false, height: 15 });
Plot Styles
1. Line (style: 'line')
Renders data as a continuous line chart.
Options:
color: Line color (hex, rgb, or named color)linewidth: Line thickness in pixels (default: 1)smooth: Enable smooth curve interpolation (default: false)offset: Horizontal offset in bars (positive = right, negative = left)
Per-Point Options:
color: Override color for a specific point (breaks line if'na'ornull)
Example:
const smaPlot = {
data: [
{ time: 1620000000000, value: 50200 },
{ time: 1620086400000, value: 50300, options: { color: 'red' } }, // Override color
{ time: 1620172800000, value: null }, // Break line
{ time: 1620259200000, value: 50400 },
],
options: {
style: 'line',
color: '#ff9900',
linewidth: 2,
smooth: true,
},
};
Behavior:
- Setting
value: nullcreates a gap in the line - Setting
color: 'na'(ornull) also breaks the line - Smooth curves use spline interpolation
2. Step (style: 'step')
Renders data as a step line (staircase pattern).
Options:
Same as line, but renders with horizontal + vertical segments instead of diagonal lines.
Example:
const stepPlot = {
data: [
{ time: 1620000000000, value: 1 },
{ time: 1620086400000, value: 1 },
{ time: 1620172800000, value: 2 },
{ time: 1620259200000, value: 2 },
],
options: {
style: 'step',
color: '#00bcd4',
linewidth: 1,
},
};
3. Columns / Histogram (style: 'columns' or style: 'histogram')
Renders data as vertical bars.
Options:
color: Default bar coloroffset: Horizontal offset in bars
Per-Point Options:
color: Override color for individual bars
Example:
const volumePlot = {
data: [
{ time: 1620000000000, value: 1000, options: { color: 'green' } },
{ time: 1620086400000, value: 1500, options: { color: 'red' } },
{ time: 1620172800000, value: 1200, options: { color: 'green' } },
],
options: {
style: 'histogram',
color: '#888888',
},
};
Behavior:
- Bars are centered on their timestamp
nullvalues are not rendered (no bar)- Commonly used for volume or histogram-based indicators like MACD histogram
4. Circles (style: 'circles')
Renders data as circular markers at each data point.
Options:
color: Marker coloroffset: Horizontal offset in bars
Per-Point Options:
color: Override color for individual circles
Example:
const pivotPlot = {
data: [
{ time: 1620000000000, value: 50200 },
{ time: 1620259200000, value: 50400, options: { color: 'red' } },
],
options: {
style: 'circles',
color: '#00ff00',
},
};
Behavior:
- Fixed size (6px diameter)
- Only renders at data points (not interpolated)
5. Cross (style: 'cross')
Renders data as cross (‘+’) markers at each data point.
Options:
color: Marker coloroffset: Horizontal offset in bars
Per-Point Options:
color: Override color for individual crosses
Example:
const signalPlot = {
data: [
{ time: 1620000000000, value: 50200 },
{ time: 1620259200000, value: 50400, options: { color: 'red' } },
],
options: {
style: 'cross',
color: '#ffff00',
},
};
Behavior:
- Fixed size (16px)
- Useful for marking specific events or signals
6. Background (style: 'background')
Fills the vertical space with a colored background for specific bars.
Options:
color: Default background coloroffset: Horizontal offset in bars
Per-Point Options:
color: Override color for individual bars
Example:
const trendPlot = {
data: [
{ time: 1620000000000, value: 1, options: { color: 'rgba(0, 255, 0, 0.1)' } },
{ time: 1620086400000, value: 1, options: { color: 'rgba(0, 255, 0, 0.1)' } },
{ time: 1620172800000, value: 1, options: { color: 'rgba(255, 0, 0, 0.1)' } },
],
options: {
style: 'background',
color: 'rgba(128, 128, 128, 0.1)',
},
};
Behavior:
- Fills entire vertical space of the pane/chart
valuemust be truthy (non-zero, non-null) for the background to render- Commonly used for trend zones or market regimes
- Use semi-transparent colors (
rgba) to avoid obscuring data
7. Shape (style: 'shape')
Renders custom shapes at data points with extensive customization options. This is the most flexible plot style, supporting various shapes, sizes, text labels, and positioning modes.
Options:
color: Shape color (default: ‘blue’)shape: Shape type (default: ‘circle’)size: Shape size preset (default: ‘normal’)text: Text label to display near the shapetextcolor: Text color (default: ‘white’)location: Positioning mode (default: ‘absolute’)offset: Horizontal offset in barswidth: Custom width in pixels (overridessize)height: Custom height in pixels (overridessize)
Per-Point Options:
All global options can be overridden per-point:
color: Override shape colorshape: Override shape typesize: Override size presettext: Override label texttextcolor: Override text colorlocation: Override positioning modeoffset: Override horizontal offsetwidth: Override widthheight: Override height
Shape Types
| Shape | Description | Direction |
|---|---|---|
circle | Circular marker | None |
square | Square marker | None |
diamond | Diamond (rotated square) | None |
triangleup | Triangle pointing upward | Up |
triangledown | Triangle pointing downward | Down |
arrowup | Arrow pointing upward | Up |
arrowdown | Arrow pointing downward | Down |
flag | Flag marker | None |
cross | Cross (‘+’) marker | None |
xcross | X-shaped cross | None |
labelup | Rounded rectangle with upward pointer (for text) | Up |
labeldown | Rounded rectangle with downward pointer (for text) | Down |
Size Presets
| Size | Pixels | Use Case |
|---|---|---|
tiny | 8px | Subtle markers |
small | 12px | Compact charts |
normal | 16px | Default (balanced) |
large | 24px | Emphasis |
huge | 32px | Maximum visibility |
auto | 16px | Alias for normal |
Custom Dimensions:
- If both
widthandheightare specified, they are used directly:[width, height] - If only
widthis specified, aspect ratio is preserved:[width, width] - If only
heightis specified, aspect ratio is preserved:[height, height] - If neither is specified, falls back to the
sizepreset
Location Modes
The location parameter determines where shapes are positioned relative to the chart:
| Location | Behavior | Value Condition |
|---|---|---|
absolute | Position at the exact value (Y-coordinate) | Always renders |
abovebar | Position above the candle’s high | Only if value is truthy |
belowbar | Position below the candle’s low | Only if value is truthy |
top | Position at the top of the chart (not implemented for dynamic axis yet - uses value) | Only if value is truthy |
bottom | Position at the bottom of the chart (not implemented for dynamic axis yet - uses value) | Only if value is truthy |
Key Point:
- For
abovebarandbelowbar, the shape only renders whenvalueis truthy (non-zero, not null/false) - For
absolute, the shape renders whenever avalueis provided - This behavior makes shapes ideal for conditional signals and event markers
Text Label Positioning
Text labels are positioned relative to the shape based on the location parameter:
| Location | Text Position | Description |
|---|---|---|
abovebar | top | Text appears above the shape |
belowbar | bottom | Text appears below the shape |
top | bottom | Text appears below the shape (at chart top) |
bottom | top | Text appears above the shape (at chart bottom) |
absolute | top or inside | inside for label shapes, top for others |
Shape Examples
Basic Buy/Sell Signals:
const buySignals = {
data: [
{ time: 1620000000000, value: 1 }, // Show shape
{ time: 1620086400000, value: 0 }, // Hide shape
{ time: 1620172800000, value: 1 }, // Show shape
],
options: {
style: 'shape',
shape: 'triangleup',
color: 'green',
size: 'small',
location: 'belowbar', // Below the candle
},
};
const sellSignals = {
data: [
{ time: 1620086400000, value: 1 },
{ time: 1620259200000, value: 1 },
],
options: {
style: 'shape',
shape: 'triangledown',
color: 'red',
size: 'small',
location: 'abovebar', // Above the candle
},
};
With Text Labels:
const entrySignals = {
data: [
{
time: 1620000000000,
value: 1,
options: {
text: 'BUY',
textcolor: 'white',
},
},
{
time: 1620259200000,
value: 1,
options: {
text: 'SELL',
textcolor: 'yellow',
},
},
],
options: {
style: 'shape',
shape: 'labelup',
color: 'green',
size: 'large',
location: 'belowbar',
},
};
Per-Point Shape Overrides:
const dynamicSignals = {
data: [
{
time: 1620000000000,
value: 1,
options: {
shape: 'arrowup',
color: 'lime',
size: 'huge',
text: 'Strong Buy',
},
},
{
time: 1620086400000,
value: 1,
options: {
shape: 'triangleup',
color: 'green',
size: 'small',
text: 'Buy',
},
},
{
time: 1620172800000,
value: 1,
options: {
shape: 'arrowdown',
color: 'red',
size: 'huge',
text: 'Strong Sell',
},
},
],
options: {
style: 'shape',
shape: 'circle',
color: 'blue',
size: 'normal',
location: 'absolute',
text: 'Signal',
textcolor: 'white',
},
};
Custom Dimensions:
const customShapes = {
data: [
{
time: 1620000000000,
value: 50200,
options: {
width: 40, // 40px wide
height: 20, // 20px tall (non-uniform)
},
},
{
time: 1620086400000,
value: 50300,
options: {
height: 60, // 60px tall (will be 60x60 square)
},
},
],
options: {
style: 'shape',
shape: 'square',
color: 'purple',
size: 'normal', // Fallback if width/height not specified
location: 'absolute',
},
};
Per-Point Overrides
All plot styles support per-point styling through the options field in each IndicatorPoint. This allows dynamic coloring and styling based on conditions (e.g., green for bullish, red for bearish).
Color Overrides
const macdHistogram = {
data: [
{ time: 1620000000000, value: 10, options: { color: 'green' } },
{ time: 1620086400000, value: 15, options: { color: 'green' } },
{ time: 1620172800000, value: -5, options: { color: 'red' } },
{ time: 1620259200000, value: -10, options: { color: 'red' } },
],
options: {
style: 'histogram',
color: '#888888', // Fallback color
},
};
Offset Overrides
const displacedMA = {
data: [
{ time: 1620000000000, value: 50200, options: { offset: 5 } }, // Shift 5 bars right
{ time: 1620086400000, value: 50300, options: { offset: 5 } },
],
options: {
style: 'line',
color: '#ff9900',
offset: 0, // Default offset
},
};
Breaking Lines
To create gaps in line plots (e.g., for missing data or conditional rendering):
const conditionalLine = {
data: [
{ time: 1620000000000, value: 50200 },
{ time: 1620086400000, value: 50300, options: { color: 'na' } }, // Break line
{ time: 1620172800000, value: null }, // Also breaks line
{ time: 1620259200000, value: 50400 },
],
options: {
style: 'line',
color: '#00bcd4',
},
};
Multi-Plot Indicators
Many technical indicators consist of multiple plots. For example, MACD has three components:
const macdPlots = {
macd: {
data: [
{ time: 1620000000000, value: 5.2 },
{ time: 1620086400000, value: 6.1 },
// ...
],
options: {
style: 'line',
color: '#2196F3',
linewidth: 2,
},
},
signal: {
data: [
{ time: 1620000000000, value: 4.8 },
{ time: 1620086400000, value: 5.5 },
// ...
],
options: {
style: 'line',
color: '#FF9800',
linewidth: 2,
},
},
histogram: {
data: [
{ time: 1620000000000, value: 0.4, options: { color: 'green' } },
{ time: 1620086400000, value: 0.6, options: { color: 'green' } },
// ...
],
options: {
style: 'histogram',
color: '#888888',
},
},
};
chart.addIndicator('MACD_12_26_9', macdPlots, { isOverlay: false, height: 20 });
Real-Time Updates
To update plot data incrementally (e.g., for WebSocket feeds), use the updateData() method:
// Initial setup
const indicator = chart.addIndicator('RSI_14', rsiPlots, { isOverlay: false });
// Later: update with new data
function onNewBar(bar, indicators) {
// Update indicator first
indicator.updateData(indicators.rsiPlots);
// Then update chart
chart.updateData([bar]);
}
Key Points:
- Always update indicators before calling
chart.updateData() updateData()merges data by timestamp (updates existing or appends new)- Much more efficient than
setMarketData()for incremental updates
Best Practices
-
Use
nullvalues to skip rendering for specific points instead of removing them from the data array (maintains time alignment) -
Color consistency: Use a consistent color scheme across related plots (e.g., green for bullish, red for bearish)
-
Per-point overrides: Use sparingly for conditional styling; avoid overriding every point (defeats the purpose of global options)
-
Shape locations: For signal-based shapes (
abovebar,belowbar), usevalue: 1to show andvalue: 0to hide -
Text labels: Keep text short (2-5 characters) for clarity; use
labelup/labeldownshapes for better text visibility -
Custom dimensions: Use
widthandheightfor non-uniform shapes (e.g., wide rectangles); omit both to use standard size presets -
Overlay vs. Separate Pane:
- Use
isOverlay: truefor indicators that share the same scale as price (e.g., moving averages, Bollinger Bands) - Use
isOverlay: falsefor oscillators with different ranges (e.g., RSI, MACD, Stochastic)
- Use
-
Performance: For high-frequency updates, minimize the number of separate indicators; combine plots into a single indicator when possible
See Also
- API Reference - Detailed method documentation
- Layout & Customization - Pane sizing and layout options
- Plugins - Interactive tools and extensions