# Verify waterfall chart arithmetic from a CSV change log with JavaScript

> Source: <https://dev.to/hardik_parikh_29/verify-waterfall-chart-arithmetic-from-a-csv-change-log-with-javascript-joc>
> Published: 2026-10-05 09:44:50+00:00

Disclosure: I’m Hardik, Gixo’s solo founder. AI agents drafted this article and code example. The code example was checked by executing it in Node.js. This is a standalone educational calculation using synthetic data.

A waterfall chart describes how a balance changes. The height of a floating bar represents a movement; its position represents the running balance. Those are different quantities. A chart can show the right individual amounts while placing them on incorrect baselines.

Start with this CSV:

```
Step,Amount
Opening,100
Growth,25
Churn,-7
```

Define its meaning before drawing anything. Opening initializes the balance, Growth adds 25, and Churn subtracts 7. Every amount uses the same unit and reporting period. The expected closing balance is `100 + 25 - 7 = 118`.

In this example, the calculation begins at zero, so Opening is represented as an initial positive movement. A different chart API might accept an opening balance separately. Check that contract before passing it the same rows.

After parsing the CSV, convert its rows into numeric changes. This function calculates the coordinates a renderer needs:

``` js
function bridge(changes) {
  let balance = 0;
  return changes.map(({ label, delta }) => {
    if (!Number.isFinite(delta)) {
      throw new TypeError('Delta must be finite');
    }
    const start = balance;
    balance += delta;
    if (!Number.isFinite(balance)) {
      throw new RangeError('Balance overflow');
    }
    return { label, delta, start, end: balance };
  });
}

const rows = bridge([
  { label: 'Opening', delta: 100 },
  { label: 'Growth', delta: 25 },
  { label: 'Churn', delta: -7 }
]);

console.log(rows.at(-1).end); // 118
```

The function accepts numeric objects, not raw CSV text. Use a CSV parser that understands headers and quoted fields. Reject blank amount cells before converting them; a missing measurement should not silently become a zero.

The computed coordinates are:

| Bar | Movement | Start | End | 
|---|---|---|---|
| Opening | +100 | 0 | 100 | 
| Growth | +25 | 100 | 125 | 
| Churn | -7 | 125 | 118 | 

Draw Growth between 100 and 125, and Churn between 125 and 118. For each bar, map both endpoints onto the same value scale. Its top is the smaller screen coordinate, and its height is the distance between the two coordinates.

A separate Total bar represents the final balance from zero to 118. It is derived output. Appending a Closing,118 input row would treat that balance as another movement and produce 236.

Check intermediate balances as well as the ending value. Two misclassified entries can cancel each other, leaving a correct total but an incorrect explanation. Sorting movements preserves their sum but changes the sequence and the intermediate balances readers see.

Subtotals need explicit semantics. A Subtotal,125 row passed to this function adds 125; it does not mark a checkpoint. Keep subtotal records separate, or introduce a distinct record type that displays the current balance without changing it.

Negative balances are valid arithmetic outcomes. Opening 20 followed by movements -35 and -25 produces -40. Include zero and every intermediate endpoint when choosing the scale. A negative movement does not necessarily mean a bad outcome: declining expenses may be desirable.

For monetary values, establish a precision policy. Integer minor units can avoid many decimal rounding surprises, but still need safe integer bounds. If you use decimal arithmetic or tolerances, document them and compare unformatted values before rounding labels.

The example was executed in Node.js and checked against all three expected running balances. Additional checks covered the -40 outcome, nonnumeric and nonfinite inputs, and balance overflow.

Arithmetic checks establish internal consistency. They cannot establish whether the source log contains every movement, uses the correct signs, or compares the same population. Keep those source checks alongside the chart rather than treating a reconciled total as proof of complete data.
