Exciting Update: Version 1.0.0 is now available, introducing high-performance technical indicators and custom drawing tools. Read more
Version: 1.0.0

Series Primitives

Series Primitives are drawing overlays bound directly to a specific series. Unlike basic indicators, they have deep access to the coordinate system and can render custom graphics on the main pane, the Y-axis price scale, and the X-axis time scale.

Rendering on Axis Scales

Series primitives can extend their canvas painting beyond the clipped main viewport to render text tags and markers on the Y-axis (price scale) and X-axis (time scale) by implementing:

  • drawPriceScale(ctx, priceScaleW, chartH): Called inside the price scale render cycle. Exposes the axis canvas context, scale width, and pane height.
  • drawTimeScale(ctx, chartW, timeScaleH): Called inside the time scale render cycle. Exposes the axis canvas context, pane width, and scale height.

Example: Custom Price Line Primitive

Below is the full implementation of CustomPriceLinePrimitive, which draws a dashed line at a specific target price and places a price marker on the Y-axis scale:

class CustomPriceLinePrimitive {
  constructor(price, color) {
    this.price = price;
    this.color = color || '#2962ff';
  }

  attached({ chart }) {
    this.chart = chart;
  }

  // Draw the line on the main pane
  draw(ctx, chartW, chartH) {
    const minMax = this.chart.getVisibleMinMax();
    const y = this.chart.priceToY(this.price, minMax.minPrice, minMax.maxPrice);
    ctx.save();
    ctx.strokeStyle = this.color;
    ctx.lineWidth = 1.5;
    ctx.setLineDash([6, 4]);
    ctx.beginPath();
    ctx.moveTo(0, y); ctx.lineTo(chartW, y);
    ctx.stroke();
    ctx.restore();
  }

  // Draw the marker on the Y-axis scale
  drawPriceScale(ctx, priceScaleW, chartH) {
    const minMax = this.chart.getVisibleMinMax();
    const y = this.chart.priceToY(this.price, minMax.minPrice, minMax.maxPrice);
    const chartW = this.chart.logicalWidth - this.chart.paddingRight;
    ctx.save();
    ctx.fillStyle = this.color;
    ctx.fillRect(chartW + 2, y - 9, priceScaleW - 10, 18);
    ctx.fillStyle = '#ffffff';
    ctx.font = 'bold 10px Inter, Arial, sans-serif';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(this.price.toFixed(2), chartW + 2 + (priceScaleW - 10)/2, y);
    ctx.restore();
  }
}