I have been working on a candlestick chart using Observable Plot, and I have come across a problem with gaps in the chart. In my dataset, there are of course gaps for weekends when no trading occurs, but I did not expect these gaps to appear in the final plot. This is my first time using Observable Plot.
const ticker = parseDates(tickerData);
const radioBtns = document.getElementsByName("chartType");
let chartType;
for(var i = 0; i < radioBtns.length; i++) {
radioBtns[i].onclick = updateChart;
}
updateChart();
function updateChart() {
// reset images
candlestickLabel.childNodes[0].src = "assets/candlestick-unselected.png";
lineLabel.childNodes[0].src = "assets/line-unselected.png";
areaLabel.childNodes[0].src = "assets/area-unselected.png";
for (let i = 0; i < radioBtns.length; i++) {
// loop through radio buttons
if (radioBtns[i].checked) {
// set chartType to the value of the selected button
chartType = radioBtns[i];
// loop through labels and set the image to selected if checked
const labels = document.getElementsByTagName("label");
for (let j = 0; j < labels.length; j++) {
if (labels[j].htmlFor == radioBtns[i].id) {
labels[j].childNodes[0].src = "assets/" + chartType.value + "-selected.png";
}
}
}
}
// set up chart based on selected type
let plot = null;
if (chartType.value == "candlestick") {plot = Plot.plot({
inset: 20,
width: 1000,
height: 400,
grid: true,
x: {
domain: [ticker[0].date, ticker[ticker.length - 1].date],
reverse: true,
tickFormat: d => Plot.formatIsoDate(d), // Format ticks as dates
tickRotate: -15,
tickPadding: 5,
},
y: {
label: "↑ Stock price ($)"
},
color: {
domain: [-1, 0, 1],
range: ["#f23645", "currentColor", "#138808"]
},
marks: [
Plot.ruleX(ticker.filter(d => d.low), {
x: "date",
y1: "low",
y2: "high",
stroke: d => Math.sign(d.close - d.open),
}),
Plot.ruleX(ticker, {
x: "date",
y1: "open",
y2: "close",
stroke: d => Math.sign(d.close - d.open),
strokeWidth: 4,
strokeLinecap: "butt"
})
]
});
} else if (chartType.value == "line") {
plot = Plot.plot({
inset: 6,
// width and height match that of container
width: 1000,
height: 400,
grid: true,
x: {
// set the domain
domain: [new Date(ticker[0].date), new Date(ticker[ticker.length - 1].date)],
reverse: true
},
y: {
label: "↑ Stock Price ($)"
},
marks: [
Plot.lineY(ticker, {
x: "date",
y: "close",
stroke: "#6495ed"
})
]
});
} else {
plot = Plot.plot({
inset: 6,
// width and height match that of container
width: 1000,
height: 400,
grid: true,
x: {
// set the domain
domain: [new Date(ticker[0].date), new Date(ticker[ticker.length - 1].date)],
reverse: true
},
y: {
label: "↑ Stock Price ($)"
},
marks: [
Plot.lineY(ticker, {
x: "date",
y: "close",
stroke: "#6495ed"
}),
Plot.areaY(ticker, {
x: "date",
y1: Math.min(...ticker.map((t) => t.close)) - 10,
y2: "close",
fill: "#6495ed",
fillOpacity: 0.3
})
]
});
}
const div = chartDiv;
div.innerHTML = "";
div.append(plot);
}
// UTILITY FUNCTIONS
const weeks = (start, stop, stride) => d3.utcMonday.every(stride).range(start, +stop + 1);
const weekdays = (start, stop) => d3.utcDays(start, +stop + 1).filter(d => d.getUTCDay() !== 0 && d.getUTCDay() !== 6);
function parseDates(data) {
return data.map(d => ({
...d,
date: new Date(d.date) // Ensure `date` is a Date object
}));
}
Here is my dataset format:
const tickerData = [{
date: "2024-09-10",
open: 204.2000,
high: 205.8300,
low: 202.8700,
close: 205.3200,
volume: 3070644
},{
date: "2024-09-09",
open: 201.9400,
high: 205.0500,
low: 201.4300,
close: 203.5300,
volume: 3705004
},{
date: "2024-09-06",
open: 202.3800,
high: 204.1000,
low: 199.3350,
close: 200.7400,
volume: 3304491
}
}]
I have tried several solutions, but to no avail. First I made sure that the dataset was parsed correctly and contained no weekend data. I have also tried several things using the tick
, tickFormat
, and domain
options for the x-axis.