{"id":21149,"date":"2021-10-22T07:18:13","date_gmt":"2021-10-22T07:18:13","guid":{"rendered":"https:\/\/www.highcharts.com\/blog\/?p=21149"},"modified":"2026-01-13T11:21:33","modified_gmt":"2026-01-13T11:21:33","slug":"automated-avg-plotline-min-and-max-labels-using-annotations-part-2","status":"publish","type":"post","link":"https:\/\/www.highcharts.com\/blog\/tutorials\/automated-avg-plotline-min-and-max-labels-using-annotations-part-2\/","title":{"rendered":"Automated AVG plotLine, min and max labels using annotations (Part 2)"},"content":{"rendered":"<p>Here is another exciting topic of the series of articles about how to use the <a href=\"https:\/\/www.highcharts.com\/docs\/advanced-chart-features\/annotations-module\" target=\"_blank\" rel=\"noopener\">Highcharts annotation module<\/a> to display and highlight important information on the chart. This article will focus on dynamically positioning an annotation to calculate its position based on the chart\u2019s and series\u2019 properties.<\/p>\n<p>The goal is to create an annotation, which consists of one shape and 3 labels. As we pointed out in <a href=\"https:\/\/www.highcharts.com\/blog\/tutorials\/drawing-plotbands-with-limits-using-annotations-part-1\/\" target=\"_blank\" rel=\"noopener\">the previous article<\/a>, the annotations\u2019 configuration is an array of objects, whereas, for each object, we can specify an unlimited number of labels and shapes. In our case, the shape will be a line, which will indicate the average value of the visible series with one label describing its value, and two additional labels to specify the current min and max values.<\/p>\n<p>Our initial chart configuration looks like this:<\/p>\n<pre>Highcharts.getJSON('https:\/\/demo-live-data.highcharts.com\/aapl-c.json', function(data) {\r\n  Highcharts.stockChart('container', {\r\n    chart: {\r\n      zoomType: 'x'\r\n    },\r\n    xAxis: {\r\n      type: 'datetime'\r\n    },\r\n    series: [{\r\n      data: data\r\n    }]\r\n  });\r\n});<\/pre>\n<p>The first step of creating our annotation is to define its initial configuration:   <\/p>\n<pre>annotations: [{\r\n  draggable: '',\r\n  shapes: [],\r\n  labels: []\r\n}],<\/pre>\n<p>We define the <code>draggable: \u2018\u2019<\/code> property so that the dragging feature is disabled. We are calculating the labels\u2019 and shape\u2019s positions for correct positioning.<br \/>\nThe next step is defining a shape configuration. The shape will be <code>type: \u2018path\u2019<\/code> which is the most effective annotation for drawing straight lines. We need to add the points array with at least two points\u2019 positions to draw this annotation. And here is the fun part: The points\u2019 positions don\u2019t have to be added as objects; they can be callback functions, which will return the desired position of the points! Using this piece of information, we can create the shapes config in the following way: <\/p>\n<pre>shapes: [{\r\n  type: 'path',\r\n  points: [\r\n    annotation => {\r\n      \/\/ Calculate once\r\n      getMinMax(annotation.chart);\r\n\r\n      return {\r\n        x: annotation.chart.xAxis[0].min,\r\n        xAxis: 0,\r\n        y: (minMax.yMin + minMax.yMax) \/ 2,\r\n        yAxis: 0\r\n      };\r\n    },\r\n    annotation => ({\r\n      x: annotation.chart.xAxis[0].max,\r\n      xAxis: 0,\r\n      y: (minMax.yMin + minMax.yMax) \/ 2,\r\n      yAxis: 0\r\n    })\r\n  ]\r\n}],<\/pre>\n<p>As you notice, we also call the <code>getMinMax(annotation.chart);<\/code> function, which calculates the current min and max for each axis. Then it assigns calculated values to a global variable (<code>minMax<\/code>), which is accessible from each callback function so that it needs to be called only once.<br \/>\nThis function looks like this: <\/p>\n<pre>function getMinMax(chart) {\r\n  const yMin = Math.min.apply(null, chart.series[0].processedYData.slice(1, -1)),\r\n    yMax = Math.max.apply(null, chart.series[0].processedYData.slice(1, -1)),\r\n    maxIndex = chart.series[0].processedYData.indexOf(yMax),\r\n    minIndex = chart.series[0].processedYData.indexOf(yMin);\r\n  minMax = {\r\n    xMin: chart.series[0].processedXData[minIndex],\r\n    xMax: chart.series[0].processedXData[maxIndex],\r\n    yMin,\r\n    yMax\r\n  }\r\n}<\/pre>\n<p>As of now, we have the line rendered. Let\u2019s move our focus to labels; their config will be basic as well. Since we have the values calculated, we just need to form the correct value for each label. Each label\u2019s config consists of the point callback function that will return its position. Some other fields are used to describe the label\u2019s format, position relative to the calculated point, and some clipping options.<br \/>\nHere is a configuration of a first label: <\/p>\n<pre>{\r\n  point: () => ({\r\n    x: minMax.xMax,\r\n    xAxis: 0,\r\n    y: minMax.yMax,\r\n    yAxis: 0\r\n  }),\r\n  format: 'max: {y:.2f}'\r\n},<\/pre>\n<p>As you can see, the point function is really simple, yet thanks to the fact that it is fired in each render, the label and shape adjust to the current chart and update after some events, like zooming in or panning the current plot area.<br \/>\nFull chart configuration looks like this:<\/p>\n<pre>Highcharts.getJSON(\r\n  'https:\/\/demo-live-data.highcharts.com\/aapl-c.json',\r\n  function(data) {\r\n    let minMax = {};\r\n\r\n    function getMinMax(chart) {\r\n      const yMin = Math.min.apply(\r\n          null,\r\n          chart.series[0].processedYData.slice(1, -1)\r\n        ),\r\n        yMax = Math.max.apply(\r\n          null,\r\n          chart.series[0].processedYData.slice(1, -1)\r\n        ),\r\n        maxIndex = chart.series[0].processedYData.indexOf(yMax),\r\n        minIndex = chart.series[0].processedYData.indexOf(yMin);\r\n\r\n      minMax = {\r\n        xMin: chart.series[0].processedXData[minIndex],\r\n        xMax: chart.series[0].processedXData[maxIndex],\r\n        yMin,\r\n        yMax\r\n      };\r\n    }\r\n\r\n    Highcharts.stockChart('container', {\r\n      chart: {\r\n        zoomType: 'x'\r\n      },\r\n\r\n      xAxis: {\r\n        type: 'datetime'\r\n      },\r\n\r\n      annotations: [{\r\n        draggable: '',\r\n        shapes: [{\r\n          type: 'path',\r\n          points: [\r\n            annotation => {\r\n              \/\/ Calculate once\r\n              getMinMax(annotation.chart);\r\n\r\n              return {\r\n                x: annotation.chart.xAxis[0].min,\r\n                xAxis: 0,\r\n                y: (minMax.yMin + minMax.yMax) \/ 2,\r\n                yAxis: 0\r\n              };\r\n            },\r\n            annotation => ({\r\n              x: annotation.chart.xAxis[0].max,\r\n              xAxis: 0,\r\n              y: (minMax.yMin + minMax.yMax) \/ 2,\r\n              yAxis: 0\r\n            })\r\n          ]\r\n        }],\r\n        labels: [{\r\n          point: () => ({\r\n            x: minMax.xMax,\r\n            xAxis: 0,\r\n            y: minMax.yMax,\r\n            yAxis: 0\r\n          }),\r\n          format: 'max: {y:.2f}'\r\n        }, {\r\n          point: () => ({\r\n            x: minMax.xMin,\r\n            xAxis: 0,\r\n            y: minMax.yMin,\r\n            yAxis: 0\r\n          }),\r\n          format: 'min: {y:.2f}'\r\n        }, {\r\n          point: annotation => ({\r\n            x: annotation.chart.xAxis[0].min + 10e7,\r\n            xAxis: 0,\r\n            y: (minMax.yMin + minMax.yMax) \/ 2,\r\n            yAxis: 0\r\n          }),\r\n          y: 11,\r\n          x: 30,\r\n          clip: false,\r\n          overflow: 'none',\r\n          format: 'avg: {y:.2f}'\r\n        }]\r\n      }],\r\n      series: [{\r\n        data: data\r\n      }]\r\n    });\r\n  }\r\n);<\/pre>\n<p>It looks pretty basic, right? Here is a live example:<\/p>\n<p class=\"demo-container\"><iframe   style=\"width: 100%; height: 480px; border: none;\" src=\"https:\/\/www.highcharts.com\/samples\/embed\/highcharts\/blog\/annotations-avg-plotline\" title=\"An automated AVG plotLine with min and max labels using annotations\"><\/iframe><\/p>\n<p>Notice that the labels&#8217; and shape&#8217;s position update when you zoom in or change the extremes using the navigator. As you can see, the annotation module is really powerful and allows you to configure the chart in really interesting ways. <\/p>\n<p>Let us know, if you find this article useful, and don\u2019t hesitate to reach out to us in the comments or one of our support channels. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>A step-by-step tutorial to learn how to display and highlight important information on a chart using the Highcharts annotation module.<\/p>\n","protected":false},"author":264,"featured_media":21156,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"meta_title":"","meta_description":"","hc_selected_options":[],"footnotes":""},"categories":[210],"tags":[788,883],"coauthors":[890],"class_list":["post-21149","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-finance","tag-highcharts-stock"],"_links":{"self":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts\/21149","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/users\/264"}],"replies":[{"embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/comments?post=21149"}],"version-history":[{"count":1,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts\/21149\/revisions"}],"predecessor-version":[{"id":29355,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts\/21149\/revisions\/29355"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/media\/21156"}],"wp:attachment":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/media?parent=21149"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/categories?post=21149"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/tags?post=21149"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/coauthors?post=21149"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}