Returns Distribution Histogram — MUI X Charts

A histogram showing the distribution of financial returns (daily, weekly, or monthly) with a normal distribution overlay for comparison. This visualization is essential for risk analysis, allowing analysts to assess whether returns follow a normal distribution, identify fat tails indicating higher-than-expected extreme events, and measure asymmetry through skewness. Key statistics are displayed directly on the plot for quick interpretation.

Returns Distribution Histogram rendered with MUI X Charts

Renders

JavaScript source (MUI X Charts)

// anyplot.ai
// histogram-returns-distribution: Returns Distribution Histogram
// Library: muix 7.29.1 | JavaScript 22.23.2
// Quality: 93/100 | Created: 2026-09-02
import { ChartContainer } from "@mui/x-charts/ChartContainer";
import { BarPlot } from "@mui/x-charts/BarChart";
import { LinePlot } from "@mui/x-charts/LineChart";
import { ChartsXAxis } from "@mui/x-charts/ChartsXAxis";
import { ChartsYAxis } from "@mui/x-charts/ChartsYAxis";
import { ChartsGrid } from "@mui/x-charts/ChartsGrid";
import { ChartsLegend } from "@mui/x-charts/ChartsLegend";
import { ChartsTooltip } from "@mui/x-charts/ChartsTooltip";
import { ChartsReferenceLine } from "@mui/x-charts/ChartsReferenceLine";

const t = window.ANYPLOT_TOKENS;

// --- Data: one trading year of daily returns (fixed-seed LCG, deterministic) -
let seed = 42;
function rand() {
  seed = (seed * 1103515245 + 12345) & 0x7fffffff;
  return seed / 0x7fffffff;
}
function randNormal() {
  const u1 = Math.max(rand(), 1e-12);
  const u2 = rand();
  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
}

const N_DAYS = 252;
const DAILY_VOL = 1.15; // percent
const DRIFT = 0.04; // percent, slight positive drift
const dailyReturns = [];
for (let i = 0; i < N_DAYS; i += 1) {
  let r = DRIFT + randNormal() * DAILY_VOL;
  // Occasional volatility shock days give the distribution a fat, negatively
  // skewed left tail — the "crash risk" pattern real equity returns show.
  if (rand() < 0.06) {
    r -= Math.abs(randNormal()) * DAILY_VOL * 2.2;
  }
  dailyReturns.push(r);
}

const n = dailyReturns.length;
const mean = dailyReturns.reduce((a, b) => a + b, 0) / n;
const variance = dailyReturns.reduce((a, b) => a + (b - mean) ** 2, 0) / n;
const std = Math.sqrt(variance);
const skewness = dailyReturns.reduce((a, b) => a + ((b - mean) / std) ** 3, 0) / n;
const excessKurtosis =
  dailyReturns.reduce((a, b) => a + ((b - mean) / std) ** 4, 0) / n - 3;

// --- Histogram bins, density-normalized so the normal curve is comparable ---
const BIN_COUNT = 26;
const minReturn = Math.min(...dailyReturns);
const maxReturn = Math.max(...dailyReturns);
const binWidth = (maxReturn - minReturn) / BIN_COUNT;
const binCounts = new Array(BIN_COUNT).fill(0);
dailyReturns.forEach((r) => {
  const idx = Math.min(BIN_COUNT - 1, Math.max(0, Math.floor((r - minReturn) / binWidth)));
  binCounts[idx] += 1;
});
const binCenters = binCounts.map((_, i) => minReturn + binWidth * (i + 0.5));
const density = binCounts.map((c) => c / (n * binWidth));

function normalPdf(x, mu, sigma) {
  return Math.exp(-0.5 * ((x - mu) / sigma) ** 2) / (sigma * Math.sqrt(2 * Math.PI));
}
const normalCurve = binCenters.map((c) => normalPdf(c, mean, std));

// Tail regions beyond +/-2 standard deviations get a distinct color. Both bar
// series are stacked so, per bin, only the applicable one contributes height —
// this renders as a single two-tone histogram rather than grouped bars.
const lowerTail = mean - 2 * std;
const upperTail = mean + 2 * std;
const coreDensity = density.map((d, i) => (binCenters[i] < lowerTail || binCenters[i] > upperTail ? 0 : d));
const tailDensity = density.map((d, i) => (binCenters[i] < lowerTail || binCenters[i] > upperTail ? d : 0));

// Band scale reference lines need an exact category value, so snap to the bin
// center closest to the mean.
const meanBinCenter = binCenters.reduce((closest, c) =>
  Math.abs(c - mean) < Math.abs(closest - mean) ? c : closest
);

const pct = (v) => `${v.toFixed(1)}%`;

// --- Chart (default-exported component — the harness mounts it) -------------
export default function Chart() {
  const { width, height } = window.ANYPLOT_SIZE;
  const titleHeight = 56;
  const chartHeight = height - titleHeight;

  return (
    <div style={{ width, height, display: "flex", flexDirection: "column" }}>
      <div
        style={{
          height: titleHeight,
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          fontSize: 22,
          fontWeight: 600,
          color: t.ink,
        }}
      >
        histogram-returns-distribution · javascript · muix · anyplot.ai
      </div>

      <div style={{ position: "relative", width, height: chartHeight }}>
        <ChartContainer
          width={width}
          height={chartHeight}
          margin={{ top: 90, right: 40, bottom: 70, left: 90 }}
          series={[
            {
              type: "bar",
              data: coreDensity,
              stack: "bins",
              color: t.palette[0],
              label: "Within ±2σ",
              valueFormatter: (v) => (v ? v.toFixed(3) : null),
            },
            {
              type: "bar",
              data: tailDensity,
              stack: "bins",
              color: t.amber,
              label: "Beyond ±2σ (tail)",
              valueFormatter: (v) => (v ? v.toFixed(3) : null),
            },
            {
              type: "line",
              data: normalCurve,
              color: t.ink,
              label: "Normal fit",
              showMark: false,
              curve: "natural",
              valueFormatter: (v) => v.toFixed(3),
            },
          ]}
          xAxis={[
            {
              scaleType: "band",
              data: binCenters,
              categoryGapRatio: 0.05,
              valueFormatter: pct,
              label: "Daily Return (%)",
              labelStyle: { fill: t.ink, fontSize: 16 },
              tickLabelStyle: { fill: t.inkSoft, fontSize: 14 },
              tickLabelInterval: (_, i) => i % 3 === 0,
            },
          ]}
          yAxis={[
            {
              label: "Density",
              labelStyle: { fill: t.ink, fontSize: 16 },
              tickLabelStyle: { fill: t.inkSoft, fontSize: 14 },
              valueFormatter: (v) => v.toFixed(2),
            },
          ]}
        >
          <ChartsGrid horizontal />
          <BarPlot skipAnimation borderRadius={2} />
          <LinePlot skipAnimation />
          <ChartsReferenceLine
            x={meanBinCenter}
            label="Mean"
            labelStyle={{ fill: t.inkSoft, fontSize: 13 }}
            lineStyle={{ stroke: t.ink, strokeDasharray: "6 4", strokeWidth: 1.5 }}
          />
          <ChartsXAxis />
          <ChartsYAxis />
          <ChartsLegend position={{ vertical: "top", horizontal: "right" }} direction="row" />
          <ChartsTooltip trigger="item" />
        </ChartContainer>

        <div
          style={{
            position: "absolute",
            top: 16,
            left: 100,
            background: t.elevatedBg,
            border: `1px solid ${t.grid}`,
            borderRadius: 8,
            padding: "12px 18px",
            fontSize: 15,
            lineHeight: 1.6,
            color: t.ink,
            minWidth: 190,
          }}
        >
          <div style={{ fontWeight: 600, marginBottom: 4 }}>Return statistics</div>
          <div style={{ color: t.inkSoft }}>Mean: {pct(mean)}</div>
          <div style={{ color: t.inkSoft }}>Std dev: {pct(std)}</div>
          <div style={{ color: t.inkSoft }}>Skewness: {skewness.toFixed(2)}</div>
          <div style={{ color: t.inkSoft }}>Kurtosis: {excessKurtosis.toFixed(2)}</div>
        </div>
      </div>
    </div>
  );
}

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/histogram-returns-distribution/muix/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "histogram-returns-distribution",
  "language": "javascript",
  "library": "muix",
  "page": "https://anyplot.ai/histogram-returns-distribution/javascript/muix",
  "hub": "https://anyplot.ai/histogram-returns-distribution",
  "code_json": "https://api.anyplot.ai/specs/histogram-returns-distribution/muix/code",
  "spec_json": "https://api.anyplot.ai/specs/histogram-returns-distribution",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/histogram-returns-distribution/javascript/muix/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/histogram-returns-distribution/javascript/muix/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/histogram-returns-distribution/javascript/muix/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/histogram-returns-distribution/javascript/muix/plot-dark.html",
  "quality_score": 93.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Returns Distribution Histogram on anyplot.ai.

Other implementations