Search code examples
highchartsindicator

Highcharts.js question: is it possible to not draw line beyond MACD indicator histogram?


I develop a project using Highstock.js library. I have a requirement to not draw lines beyond MACD indicator histogram (https://prnt.sc/lxjoit). In Highstock.js documentation related to MACD indicator there is macdLine API: https://api.highcharts.com/highstock/series.macd.macdLine , signalLine API: https://api.highcharts.com/highstock/series.macd.signalLine.zones. Those API allows only to set colors for MACD indicator parts divided by y axis. So it doesn't fit. From documentation it isn't clear if we can to not draw MACD indicator lines beyond the histogram. Do you know if it is possible to implement that and how? Please advise.

Here is live demo in JSFiddle showing chart with MACD indicator: http://jsfiddle.net/ogorobets/x3tcpq72/14/

var ohlc = JSON.parse(ohlcStringified),
    volume = JSON.parse(volumeStringified);
var wvapSerieData = [];    
var lastDayDate = new Date("December 6, 2018 00:00:00");
var lastDayDateTs = lastDayDate.getTime();

Highcharts.stockChart('container', {
    chart: {
        borderWidth: 1
    },
    title: {
        text: 'Volume Weighted Average Price (VWAP)'
    },
    legend: {
        enabled: true
    },
    yAxis: [{
        height: '30%'
    }, {
        top: '30%',
        height: '35%',
        offset: 0
    }, {
        top: '65%',
        height: '35%',
        offset: 0
    }],
    series: [{
        type: 'candlestick',
        id: 'candlestick',
        name: 'AAPL',
        data: ohlc,
        tooltip: {
            valueDecimals: 2
        }
    }, {
        type: 'column',
        id: 'volume',
        name: 'Volume',
        data: volume,
        yAxis: 1
    }, 
    {
        type: 'macd',
        color: '#f05f5f',
        linkedTo: 'candlestick',
        showInLegend: true,
        enableMouseTracking: true,
        dataGrouping: {
            enabled: false,
        },
        zones:[
            {
                value: 0,
                color: '#f05f5f',
            },
            {
                color: '#31c26d'
            }
        ],
        yAxis: 2
    }]
});

Solution

  • Unfortunately, MACD indicator was not designed to not plot MACD indicator lines beyond the histogram.

    However, you can override the method that calculates MACD values and remove first values that are beyond the histogram. Check the code and demo I posted you below.

    Lines added to H.seriesTypes.macd.prototype.getValues method:

    // params.signalPeriod - 1 - amount of points beyond the histroram
    MACD.splice(0, params.signalPeriod - 1);
    xMACD.splice(0, params.signalPeriod - 1);
    yMACD.splice(0, params.signalPeriod - 1);
    

    Whole wrapper code:

    (function(H) {
      H.seriesTypes.macd.prototype.getValues = function(series, params) {
        var j = 0,
            EMA = H.seriesTypes.ema,
          merge = H.merge,
            defined = H.defined,
          correctFloat = H.correctFloat,
          MACD = [],
          xMACD = [],
          yMACD = [],
          signalLine = [],
          shortEMA,
          longEMA,
          i;
    
        if (series.xData.length < params.longPeriod + params.signalPeriod) {
          return false;
        }
    
        // Calculating the short and long EMA used when calculating the MACD
        shortEMA = EMA.prototype.getValues(series, {
          period: params.shortPeriod
        });
    
        longEMA = EMA.prototype.getValues(series, {
          period: params.longPeriod
        });
    
        shortEMA = shortEMA.values;
        longEMA = longEMA.values;
    
    
        // Subtract each Y value from the EMA's and create the new dataset
        // (MACD)
        for (i = 1; i <= shortEMA.length; i++) {
          if (
            defined(longEMA[i - 1]) &&
            defined(longEMA[i - 1][1]) &&
            defined(shortEMA[i + params.shortPeriod + 1]) &&
            defined(shortEMA[i + params.shortPeriod + 1][0])
          ) {
            MACD.push([
              shortEMA[i + params.shortPeriod + 1][0],
              0,
              null,
              shortEMA[i + params.shortPeriod + 1][1] -
              longEMA[i - 1][1]
            ]);
          }
        }
    
        // Set the Y and X data of the MACD. This is used in calculating the
        // signal line.
        for (i = 0; i < MACD.length; i++) {
          xMACD.push(MACD[i][0]);
          yMACD.push([0, null, MACD[i][3]]);
        }
    
        // Setting the signalline (Signal Line: X-day EMA of MACD line).
        signalLine = EMA.prototype.getValues({
          xData: xMACD,
          yData: yMACD
        }, {
          period: params.signalPeriod,
          index: 2
        });
    
        signalLine = signalLine.values;
    
        // Setting the MACD Histogram. In comparison to the loop with pure
        // MACD this loop uses MACD x value not xData.
        for (i = 0; i < MACD.length; i++) {
          if (MACD[i][0] >= signalLine[0][0]) { // detect the first point
    
            MACD[i][2] = signalLine[j][1];
            yMACD[i] = [0, signalLine[j][1], MACD[i][3]];
    
            if (MACD[i][3] === null) {
              MACD[i][1] = 0;
              yMACD[i][0] = 0;
            } else {
              MACD[i][1] = correctFloat(MACD[i][3] -
                signalLine[j][1]);
              yMACD[i][0] = correctFloat(MACD[i][3] -
                signalLine[j][1]);
            }
    
            j++;
          }
        }
    
        MACD.splice(0, params.signalPeriod - 1);
        xMACD.splice(0, params.signalPeriod - 1);
        yMACD.splice(0, params.signalPeriod - 1);
    
        return {
          values: MACD,
          xData: xMACD,
          yData: yMACD
        };
      }
    })(Highcharts);
    

    Demo:
    http://jsfiddle.net/1f2m0yz4/

    Docs:
    https://www.highcharts.com/docs/extending-highcharts/extending-highcharts