{
const fy = null;
const xyt = {
x: (d) => d3.timeFormat("%Y-%W")(d.date),
y: "price_in_usd",
text: "price_in_usd",
fy
};
const colors = {
pos: d3.schemeCategory10[2],
neg: d3.schemeCategory10[3]
};
const yformat = d3.format("$,.0f");
const pformat = d3.format("+.1%");
const percchange = (v, d) => d / d3.mean(v) - 1;
const group_xyt = Plot.groupX({ y: "sum", text: "sum" }, xyt);
return Plot.plot({
marginLeft: 100,
marginRight: 150,
insetBottom: 20,
y: { grid: true, tickFormat: yformat },
width,
marks: [
Plot.barY(gdata, {
...Plot.map(
{
y1: (v) => v.map((d) => (d > d3.mean(v) ? d : null)),
y2: (v) => v.map((d) => d3.mean(v))
},
group_xyt
),
fill: colors.pos,
opacity: 0.8
}),
Plot.text(gdata, {
...Plot.map(
{
text: (v) =>
v.map((d) =>
percchange(v, d) >= 0 ? pformat(percchange(v, d)) : null
)
},
group_xyt
),
lineAnchor: "bottom",
fill: colors.pos,
dy: -3,
opacity: 0.8
}),
Plot.text(gdata, {
...Plot.map(
{
text: (v) =>
v.map((d) =>
percchange(v, d) < 0 ? pformat(percchange(v, d)) : null
)
},
group_xyt
),
lineAnchor: "top",
fill: colors.neg,
dy: 3,
opacity: 0.8
}),
Plot.barY(gdata, {
...Plot.map(
{
y1: (v) => v.map((d) => (d < d3.mean(v) ? d : d3.mean(v))),
y2: (v) => v.map((d) => d3.mean(v))
},
group_xyt
),
fill: colors.neg,
opacity: 0.8
}),
Plot.ruleY(gdata, {
...Plot.groupZ(
{
y: "mean"
},
group_xyt
),
x: null
}),
Plot.text(gdata, {
...Plot.groupZ(
{
y: "mean"
},
group_xyt
),
x: null,
frameAnchor: "right",
textAnchor: "start",
dx: 5,
text: (d) =>
`Mean: ${yformat(d3.mean(d, (v) => d3.sum(v, (s) => s[xyt.y])))}`
}),
Plot.crosshairY(gdata, {
...group_xyt,
x: null
})
]
});
}