javascripthighcharts

Highcharts stacked vertical columns getting squished to the middle


I have built a stacked vertical bar chart using Highcharts for some odd reason all the data is squished in the middle instead of spreading to the full width of the chart

let depthChart;

    const buildDepthChart = (bidPrices, askPrices, bidsData, asksData) => {
        const maxAbsValue = Math.max(
            ...bidsData.map(item => Math.abs(item.y)),
            ...asksData.map(item => Math.abs(item.y))
        );

        // Combine and sort prices
        const allPrices = [...bidPrices, ...askPrices].sort((a, b) => a - b);
        const uniquePrices = [...new Set(allPrices)];

        depthChart = Highcharts.chart('marketDepthContainer', {
            chart: {
                type: 'column'
            },
            title: {
                text: 'עומק שוק'
            },
            xAxis: {
                categories: uniquePrices, // Sorted unique prices
                title: {
                    text: 'Price'
                },
                labels: {
                    step: 1
                },
                // tickInterval: 0.001, // Try tickInterval
                // min: 1.18, // Explicit min
                // max: 1.4, // Explicit max
            },
            yAxis: {
                title: {
                    text: 'Volume'
                },
                min: -maxAbsValue,
                max: maxAbsValue
            },
            plotOptions: {
                column: {
                    stacking: 'normal',
                    // pointWidth: 2,
                    borderRadius: '5%'
                }
            },
            tooltip: {
                useHTML: true,
                formatter: function () {
                    const price = this.x;
                    const volume = this.y;
                    return `<span><b>Price:</b> ${price}</span><br><span><b>${this.series.name}:</b> ${volume}</span>`;
                }
            },
            series: [{
                name: 'bids',
                data: bidsData,
                color: 'rgba(47, 171, 126, 0.2)'
            }, {
                name: 'asks',
                data: asksData,
                color: 'rgba(255, 88, 67, 0.2)'
            }]
        });
    };

here is a codepen snippet of my code with some stuff removed

image of said chart

and as can be seen in the image, and in the codepen they are all overlapping eachother in the middle, instead of spreading around the x-axis i'm fairly new to highcharts but i did create a similar horizontal chart that works fine so i have no idea if it's specific to column charts or i'm missing something

edit: the intended display should look something like this

<blockquote class="imgur-embed-pub" lang="en" data-id="a/EC6rNRd" data-context="false" ><a href="//imgur.com/a/EC6rNRd"></a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>


Solution

  • If the graphic itself is OK, and you are sure you want to put all data into one category, you can fill the horizontal space: by setting

    plotOptions: {
       column: {
          pointPadding: 0,
          groupPadding: 0,
          // ..... other options
       }
    }
    

    or use small values, like 0.04 and 0.02.

    The default value of groupPadding is 0.2 and of pointPadding is 0.1, which are absolute x values (i.e, they are not fractions, 20% and 10% of the whole interval). Since your x values are on a small interval (approx. 0.32) these are very large values, which explains the large spaces.

    Those default values were thought for the more usual form of series data that doesn't contain x values, in which case pointInterval is used, which defaults to 1, so in that case, they would happen to actually be. 20% and 10%.

    As for the x axis, if the intention is to have each bar centred on the x value, so each bar is slightly moved to the left of right depending on its x, then your categories do produce that result, but you have to also set tickInterval to a value smaller than 1 - to correspond with the data order of magnitude, you may set tickInterval: 0.1.

    xAxis: {
       categories: uniquePrices, // Sorted unique prices
       // ..... other xAxis options
       tickInterval: 0.1
    }
    

    Here's a fork of the codepen with those modifications, or as stack snippet:

    let depthChart;
    
    const buildDepthChart = (bidPrices, askPrices, bidsData, asksData) => {
       const allPrices = [...bidPrices, ...askPrices].sort((a, b) => a - b);
       const uniquePrices = [...new Set(allPrices)];
    
       depthChart = Highcharts.chart("marketDepthContainer", {
          chart: {
             type: "column"
          },
          title: {
             text: "עומק שוק"
          },
          xAxis: {
             categories: uniquePrices, // Sorted unique prices
             type: 'category',
             title: {
                text: "Price"
             },
             tickInterval: 0.1,
          },
          yAxis: {
             title: {
                text: "Volume"
             },
          },
          plotOptions: {
             column: {
                stacking: "normal",
                // pointWidth: 2,
                borderRadius: "5%",
                pointPadding: 0,
                groupPadding: 0,
             }
          },
          tooltip: {
             useHTML: true,
             formatter: function () {
                const price = this.x;
                const volume = this.y;
                return `<span><b>Price:</b> ${price}</span><br><span><b>${this.series.name}:</b> ${volume}</span>`;
             }
          },
          series: [
             {
                name: "bids",
                data: bidsData,
                color: "rgba(47, 171, 126, 0.52)"
             },
             {
                name: "asks",
                data: asksData,
                color: "rgba(255, 88, 67, 0.52)"
             }
          ]
       });
    };
    
    const getFXData = async (url, symbol) => {
       const bidsData = [];
       const asksData = [];
       const bidLabels = [];
       const askLabels = [];
       const bidPrices = [];
       const askPrices = [];
       const minPrice = 1.18;
       const maxPrice = 1.4;
    
       try {
          const data = JSON.parse('{"error":false,"content":{"actual":1.28769,"asks":[[1.034,0.01],[1.035,0.09],[1.036,0.05],[1.037,0.23],[1.038,0],[1.039,0.34],[1.040,0.24],[1.041,0],[1.042,0],[1.043,0],[1.044,0.56],[1.045,0],[1.046,0],[1.047,0],[1.048,0],[1.049,0.01],[1.050,0],[1.051,0.05],[1.052,0.01],[1.053,0.08],[1.054,0],[1.055,0],[1.056,0.02],[1.057,0],[1.058,0.14],[1.059,0],[1.060,0],[1.061,0],[1.062,0.2],[1.063,0],[1.064,1],[1.065,0],[1.066,0.21],[1.067,0.01],[1.068,0],[1.069,1.02],[1.070,0],[1.071,0.13],[1.072,0.06],[1.073,0],[1.074,0],[1.075,1.94],[1.076,0],[1.077,0],[1.078,0],[1.079,0],[1.080,0],[1.081,0],[1.082,0],[1.083,0],[1.084,0],[1.085,0],[1.086,0],[1.087,0],[1.088,0],[1.089,0],[1.090,0.01],[1.091,0],[1.092,0],[1.093,0],[1.094,0.02],[1.095,0.14],[1.096,0],[1.097,0.01],[1.098,0],[1.099,0],[1.100,0],[1.101,0],[1.102,0],[1.103,0],[1.104,0],[1.105,0],[1.106,0.03],[1.107,0],[1.108,0],[1.109,0],[1.110,0],[1.111,0],[1.112,0],[1.113,0],[1.114,0],[1.115,0],[1.116,0.03],[1.117,0],[1.118,1.01],[1.119,0.04],[1.120,0],[1.121,0],[1.122,0],[1.123,0.35],[1.124,0],[1.125,0],[1.126,0],[1.127,0.05],[1.128,0.05],[1.129,0],[1.130,0],[1.131,1],[1.132,0.01],[1.133,0],[1.134,2.38],[1.135,0.05],[1.136,0],[1.137,0.11],[1.138,4.21],[1.139,0],[1.140,0],[1.141,0],[1.142,0.02],[1.143,0.02],[1.144,0],[1.145,0],[1.146,0],[1.147,0],[1.148,0.15],[1.149,0.39],[1.150,0.46],[1.151,0.39],[1.152,0.06],[1.153,0],[1.154,0.3],[1.155,0.01],[1.156,0],[1.157,0],[1.158,0.03],[1.159,0.32],[1.160,0],[1.161,0],[1.162,0],[1.163,0.02],[1.164,0.20],[1.165,0.10],[1.166,0.7],[1.167,0.08],[1.168,0.32],[1.169,0.25],[1.170,0.5],[1.171,0],[1.172,0],[1.173,0],[1.174,0.12],[1.175,0],[1.176,0.05],[1.177,0.03],[1.178,0.37],[1.179,0.01],[1.180,0.01],[1.181,0.21],[1.182,0.29],[1.183,0.01],[1.184,0.12],[1.185,0.3],[1.186,0],[1.187,0.32],[1.188,0],[1.189,0.25],[1.190,6.51],[1.191,0.13],[1.192,1.31],[1.193,0.10],[1.194,0.10],[1.195,0.5],[1.196,0.74],[1.197,0.71],[1.198,0.33],[1.199,0.72],[1.200,0.81],[1.201,0.03],[1.202,0.42],[1.203,0.02],[1.204,0.08],[1.205,0.25],[1.206,1.54],[1.207,0.58],[1.208,0.72],[1.209,0.01],[1.210,6.04],[1.211,20.66],[1.212,43.20],[1.213,9.53],[1.214,14.06],[1.215,21.52],[1.216,19.71],[1.217,27.66],[1.218,26.06],[1.219,11.70],[1.220,30.98],[1.221,388.94],[1.222,44.23],[1.223,16.05],[1.224,18.03],[1.225,24.24],[1.226,77.87],[1.227,102.26],[1.228,132.42],[1.229,194.20],[1.230,238.94],[1.231,26.04],[1.232,17.61],[1.233,23.68],[1.234,28.36],[1.235,17.67],[1.236,43.98],[1.237,20.31],[1.238,107.59],[1.239,203.01],[1.240,86.53],[1.241,50.48],[1.242,73.78],[1.243,66.54],[1.244,54.76],[1.245,41.17],[1.246,14.66],[1.247,39.16],[1.248,31.48],[1.249,41.56],[1.250,30.39],[1.251,15.90],[1.252,28.96],[1.253,31.27],[1.254,29.40],[1.255,29.67],[1.256,316.14],[1.257,31.59],[1.258,130.73],[1.259,53.54],[1.260,101.01],[1.261,34.26],[1.262,45.49],[1.263,48.47],[1.264,19.47],[1.265,60.27],[1.266,44.69],[1.267,37.91],[1.268,50.55],[1.269,63.16],[1.270,36.22],[1.271,73.34],[1.272,49.08],[1.273,53.37],[1.274,67.10],[1.275,45.37],[1.276,29.70],[1.277,59.38],[1.278,47.49],[1.279,120.36],[1.280,36.88],[1.281,43.35],[1.282,107.90],[1.283,45.15],[1.284,50.20],[1.285,36.33],[1.286,215.68],[1.287,517.34],[1.288,128.53],[1.289,79.28],[1.290,79.89],[1.291,73.23],[1.292,30.02],[1.293,72.96],[1.294,82.28],[1.295,138.75],[1.296,29.22],[1.297,178.71],[1.298,34.13],[1.299,36.08],[1.300,102.97],[1.301,277.80],[1.302,30.17],[1.303,39.59],[1.304,49.49],[1.305,56.84],[1.306,47.18],[1.307,22.99],[1.308,19.78],[1.309,39.13],[1.310,44.00],[1.311,30.88],[1.312,33.34],[1.313,19.12],[1.314,23.08],[1.315,35.72],[1.316,165.08],[1.317,67.76],[1.318,22.73],[1.319,9.95],[1.320,47.53],[1.321,1.07],[1.322,4.34],[1.323,4.48],[1.324,7.02],[1.325,2.42],[1.326,0],[1.327,0],[1.328,0],[1.329,0],[1.330,0],[1.331,0],[1.332,0],[1.333,0],[1.334,0],[1.335,0],[1.336,0],[1.337,0],[1.338,0],[1.339,0],[1.340,0],[1.341,0],[1.342,0],[1.343,0],[1.344,0],[1.345,0],[1.346,0],[1.347,0],[1.348,0],[1.349,0],[1.350,0],[1.351,0],[1.352,0],[1.353,0],[1.354,0],[1.355,0],[1.356,0],[1.357,0],[1.358,0],[1.359,0],[1.360,0],[1.361,0],[1.362,0],[1.363,0],[1.364,0],[1.365,0],[1.366,0],[1.367,0],[1.368,0],[1.369,0],[1.370,0],[1.371,0],[1.372,0],[1.373,0],[1.374,0],[1.375,0],[1.376,0],[1.377,0],[1.378,0],[1.379,0],[1.380,0]],"bids":[[1.195,0.37],[1.207,0.01],[1.208,0.02],[1.209,0.01],[1.210,0.08],[1.212,0.02],[1.213,0.02],[1.214,0.10],[1.215,0.03],[1.217,0.03],[1.219,1.49],[1.221,1.78],[1.222,0.39],[1.223,0.58],[1.227,0.02],[1.228,0.15],[1.229,0.03],[1.230,0.50],[1.231,1.99],[1.232,0.6],[1.233,0.03],[1.235,0.52],[1.236,0.02],[1.237,1.22],[1.240,4.61],[1.241,0.51],[1.242,0.08],[1.243,0.12],[1.244,0.17],[1.245,2.28],[1.246,0.16],[1.247,0.04],[1.248,0.38],[1.249,0.60],[1.250,0.06],[1.251,2.76],[1.252,0.38],[1.253,9.88],[1.254,1.38],[1.255,16.44],[1.256,6.72],[1.257,0.08],[1.258,21.35],[1.259,6.31],[1.260,2.55],[1.261,9.14],[1.262,1.53],[1.263,2.71],[1.264,11.14],[1.265,3.03],[1.266,0.22],[1.267,16.38],[1.268,3.24],[1.269,1.87],[1.270,6.80],[1.271,5.04],[1.272,2.88],[1.273,2.16],[1.274,0.26],[1.275,2.14],[1.276,7.54],[1.277,1.31],[1.278,5.37],[1.279,0.04],[1.280,0.39],[1.281,1.80],[1.282,0.51],[1.283,3.37],[1.284,2.97],[1.285,2.31],[1.286,27.81],[1.287,63.86],[1.288,68.04],[1.289,72.88],[1.290,102.92],[1.291,45.07],[1.292,43.25],[1.293,122.83],[1.294,77.80],[1.295,31.92],[1.296,70.15],[1.297,472.74],[1.298,129.17],[1.299,373.12],[1.300,240.30],[1.301,143.67],[1.302,517.80],[1.303,71.88],[1.304,67.23],[1.305,80.85],[1.306,86.64],[1.307,98.35],[1.308,69.50],[1.309,78.55],[1.310,125.98],[1.311,86.66],[1.312,28.75],[1.313,27.71],[1.314,45.17],[1.315,47.09],[1.316,147.30],[1.317,161.69],[1.318,41.41],[1.319,50.25],[1.320,68.19],[1.321,18.39],[1.322,3.33],[1.323,8.33],[1.324,2.69],[1.325,12.90],[1.326,56.89],[1.327,21.89],[1.328,17.02],[1.329,6.43],[1.330,15.75],[1.331,4.21],[1.332,3.87],[1.333,8.56],[1.334,4.63],[1.335,13.80],[1.336,10.84],[1.337,33.32],[1.338,4.47],[1.339,3.27],[1.340,8.43],[1.341,17.64],[1.342,65.79],[1.343,5.20],[1.344,0.32],[1.345,0.01],[1.346,0.15],[1.347,0.24],[1.348,0.89],[1.349,0.02],[1.350,0.02],[1.351,0.78],[1.352,0.41],[1.353,20.72],[1.354,0.57],[1.355,0.03],[1.356,0.36],[1.357,0.14],[1.358,0.08],[1.359,16.53],[1.360,0.17],[1.361,8.40],[1.362,5.13],[1.363,10.27],[1.365,0.12],[1.367,0.37],[1.370,0.13],[1.371,0.16],[1.372,0.10],[1.374,0.23],[1.376,6],[1.377,1.26],[1.380,0.11],[1.382,0.01],[1.383,5.19],[1.385,0.02],[1.386,0.04],[1.387,0.04],[1.388,0.06],[1.389,0.06],[1.390,0.03],[1.391,0.06],[1.392,0.03],[1.393,5],[1.394,0.12],[1.395,0.08],[1.396,0.12],[1.397,0.15],[1.398,0.12],[1.400,0.1],[1.401,0.04],[1.402,0.03],[1.407,0.03],[1.410,0.02],[1.412,0.05],[1.416,5],[1.417,5.07],[1.419,0.01],[1.420,5.02],[1.421,5.09],[1.422,0.08],[1.423,0.32],[1.424,0.02],[1.425,0.15],[1.426,0.05],[1.427,0.04],[1.430,0.04],[1.431,0.03],[1.432,0.18],[1.438,0.01],[1.450,0.02],[1.453,0.02],[1.456,0.02],[1.457,0.06],[1.458,0.02],[1.459,0.3],[1.460,0.02],[1.461,0.02],[1.470,0.02],[1.474,0.02],[1.477,0.02],[1.480,0.02],[1.483,0.02],[1.486,0.02],[1.488,0.03],[1.489,0.02],[1.492,0.02],[1.495,0.02],[1.498,0.03],[1.499,0.03],[1.501,0.02],[1.661,0.01]]}}');
    
          if (data && data.content && data.content.bids && data.content.asks) {
             data.content.bids.forEach((bid) => {
                if (bid[0] >= minPrice && bid[0] <= maxPrice) {
                   // Filter bids
                   bidsData.push({ y: -bid[1], x: bid[0] });
                   bidPrices.push(bid[0]);
                }
             });
    
             data.content.asks.forEach((ask) => {
                if (ask[0] >= minPrice && ask[0] <= maxPrice) {
                   // Filter asks
                   asksData.push({ y: ask[1], x: ask[0] });
                   askPrices.push(ask[0]);
                }
             });
    
             if (url.includes("order-book-data")) {
                buildPositionChart(
                   bidLabels,
                   askLabels,
                   bidsData.map((item) => item.y),
                   asksData.map((item) => item.y),
                   bidPrices,
                   askPrices
                ); //adjusting data to be just y
             } else if (url.includes("market-depth-data")) {
                buildDepthChart(bidPrices, askPrices, bidsData, asksData);
             }
          } else {
             console.error("Invalid data structure.");
          }
       } catch (error) {
          console.error("Error fetching data:", error);
       }
    };
    getFXData("market-depth-data.json","GBPUSD");
    <div id="marketDepthContainer"></div>
    <div> hello </div>
    <script src="https://code.highcharts.com/highcharts.js"></script>