Published
Edited
Apr 15, 2021
Importers
Insert cell
md`# Wall Street Bets`
Insert cell
d3 = require("d3@^6.2.0")
Insert cell
stock_data = FileAttachment("stock_data.csv").csv()
Insert cell
function getPercentChange(stock_data){
let B = Object.keys(stock_data[0]).filter((colName) => colName.toUpperCase() === colName && colName.length > 0);
let stock_percent_change = stock_data.slice(0,-1).map(obj => ({...obj}));
for (let i = 0; i < B.length; i++){
let current_stock_percent_change = arrayPercentChange(stock_data, B[i]);
for (let j = 0; j < stock_percent_change.length; j++){
stock_percent_change[j][B[i]] = current_stock_percent_change[j]
}
}
return stock_percent_change
}
Insert cell
function arrayPercentChange(stock_data, stock) {
let percentsArray = new Array(stock_data.length-1);
for(let i = 0; i < stock_data.length-1; i++){
percentsArray[i] = (stock_data[i+1][stock] - stock_data[i][stock])/stock_data[i][stock];
}
return percentsArray
}
Insert cell
function max_multiplier(stock_data) {
let percents = [];
let percentsArray = getPercentChange(stock_data);
let B = Object.keys(stock_data[0]).filter((colName) => colName.toUpperCase() === colName && colName.length > 0);
for (let i = 0; i < percentsArray.length; i++) {
let stock_values = B.map(name => percentsArray[i][name])
let maximum = Math.max(...stock_values, 0);
percents.push(maximum);
}
var max_multiplier = {"name": "Max Percent Increase", "values": percents};
return max_multiplier;
}
Insert cell
function min_multiplier(stock_data) {
let percents = [];
let percentsArray = getPercentChange(stock_data);
let B = Object.keys(stock_data[0]).filter((colName) => colName.toUpperCase() === colName && colName.length > 0);
for (let i = 0; i < percentsArray.length; i++) {
let stock_values = B.map(name => percentsArray[i][name])
let minimum = Math.min(...stock_values, 0);;
percents.push(minimum);
}
var min_multiplier = {"name": "Max Percent Decrease", "values": percents};
return min_multiplier;
}
Insert cell
function ave_multiplier(stock_data) {
let percents = [];
let percentsArray = getPercentChange(stock_data);
let B = Object.keys(stock_data[0]).filter((colName) => colName.toUpperCase() === colName && colName.length > 0);
for (let i = 0; i < percentsArray.length; i++) {
let stock_values = B.map(name => percentsArray[i][name])
let something = stock_values.reduce((a,b) => (a+b),0)/stock_values.length;
percents.push(something);
}
var ave_multiplier = {"name": "Average Percent Change", "values": percents};
return ave_multiplier;
}
Insert cell
function wrap_multipliers(stock_data) {
let maximum = max_multiplier(stock_data);
let minimum = min_multiplier(stock_data);
let average = ave_multiplier(stock_data);
let zero = (min_multiplier(stock_data));
zero.values = zero.values.map(d => 0);
zero.name = "y = 0";
let allMultipliers = [maximum, minimum, average, zero];
let datesArray = [];
for (let i = 1; i<stock_data.length; i++){
datesArray.push(new Date(stock_data[i]["Date"].slice(0,-6)));
}
let multipliers = {"y": "100s of Percent Change From Pervious Day", "series": allMultipliers, "dates": datesArray}
return multipliers
}
Insert cell
function rand_multiplier(stock_data) {
let multiplier = 1;
let percentsArray = getPercentChange(stock_data);
let B = Object.keys(stock_data[0]).filter((colName) => colName.toUpperCase() === colName && colName.length > 0);
for (let i = 0; i < percentsArray.length; i++) {
let stock_values = B.map(name => percentsArray[i][name])
let weights = B.map(() => Math.random());
let weights_sum = weights.reduce((a,b) => (a+b),0);
weights = weights.map((a) => a/weights_sum);
stock_values = stock_values.map((a,index) => a*weights[index]);
let something = stock_values.reduce((a,b) => (a+b),0);
multiplier *= (something+1);
}
return multiplier;
}
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
svg.append("g")
.attr("fill", color)
.selectAll("rect")
.data(bins)
.join("rect")
.attr("x", d => x(d.x0) + 1)
.attr("width", d => Math.max(0, x(d.x1) - x(d.x0) - 1))
.attr("y", d => y(d.length))
.attr("height", d => y(0) - y(d.length));

svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
return svg.node();
}
//ave = data.reduce((a,b) => (a+b),0)/data.length;
//max = Math.max(...data);
//min = Math.min(...data);
Insert cell
ave = data.reduce((a,b) => (a+b),0)/data.length;
Insert cell
data = Object.assign(getRandPercentChange(stock_data), {x: "Percent Multiplier", y: "Frequency"})
Insert cell
bins = d3.bin().thresholds(40)(data)
Insert cell
x = d3.scaleLinear()
.domain([bins[0].x0, bins[bins.length - 1].x1])
.range([margin.left, width - margin.right])
Insert cell
y = d3.scaleLinear()
.domain([0, d3.max(bins, d => d.length)]).nice()
.range([height - margin.bottom, margin.top])
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80 ).tickSizeOuter(0))
.call(g => g.append("text")
.attr("x", width - margin.right)
.attr("y", -4)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text(data.x))
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(height / 40))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 4)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data.y))
Insert cell
color = "steelblue"
Insert cell
height = 500
Insert cell
margin = ({top: 20, right: 20, bottom: 30, left: 40})
Insert cell
function getRandPercentChange(stock_data) {
let data = [];
for(let i = 0; i < 100000; i++){
data.push(rand_multiplier(stock_data));
}
return data;
}
Insert cell
chart3 = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height3])
.style("overflow", "visible");

svg.append("g")
.call(xAxis_3);

svg.append("g")
.call(yAxis_3);

const path = svg.append("g")
.attr("fill", "none")
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.selectAll("path")
.data(data_min.series)
.join("path")
.style("mix-blend-mode", "multiply")
.attr("d", d => line(d.values))
.attr("stroke", d => d.name === "Max Percent Increase" ? "green" : d.name === "y = 0" ? "black" : d.name === "Average Percent Change" ? "steelblue": "red")
//.attr("fill", "grey")
svg.append("text")
.attr("x", 50)
.attr("y", 50)
.text("Percent change over each day of January - February 2021, given different investments over the chosen stocks");

//svg.call(hover, path);
return svg.node();
}
Insert cell
function hover(svg, path) {
if ("ontouchstart" in document) svg
.style("-webkit-tap-highlight-color", "transparent")
.on("touchmove", moved)
.on("touchstart", entered)
.on("touchend", left)
else svg
.on("mousemove", moved)
.on("mouseenter", entered)
.on("mouseleave", left);

const dot = svg.append("g")
.attr("display", "none");

dot.append("circle")
.attr("r", 2.5);

dot.append("text")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.attr("y", -8);

function moved(event) {
event.preventDefault();
const pointer = d3.pointer(event, this);
const xm = x.invert(pointer[0]);
const ym = y.invert(pointer[1]);
const i = d3.bisectCenter(data.dates, xm);
const s = d3.least(data.series, d => Math.abs(d.values[i] - ym));
path.attr("stroke", d => d === s ? null : "#ddd").filter(d => d === s).raise();
dot.attr("transform", `translate(${x(data.dates[i])},${y(s.values[i])})`);
dot.select("text").text(s.name);
}

function entered() {
path.style("mix-blend-mode", null).attr("stroke", "#ddd");
dot.attr("display", null);
}

function left() {
path.style("mix-blend-mode", "multiply").attr("stroke", null);
dot.attr("display", "none");
}
}
Insert cell
data_min = wrap_multipliers(stock_data)
Insert cell
quotient_max = {let quotient = 1
for(let i = 0; i < data_min["series"][0]["values"].length; i++){
quotient = (1+data_min["series"][0]["values"][i]) * quotient;
}
return quotient}
Insert cell
quotient_min = {let quotient = 1
for(let i = 0; i < data_min["series"][0]["values"].length; i++){
quotient = (1+data_min["series"][1]["values"][i]) * quotient;
}
return quotient}
Insert cell
quotient_ave = {let quotient = 1
for(let i = 0; i < data_min["series"][0]["values"].length; i++){
quotient = (1+data_min["series"][2]["values"][i]) * quotient;
}
return quotient}
Insert cell
data_min["series"][0]["values"][1]
Insert cell
margin3 = ({top: 20, right: 20, bottom: 30, left: 30})
Insert cell
height3 = 600
Insert cell
x_3 = d3.scaleUtc()
.domain(d3.extent(data_min.dates))
.range([margin3.left, width - margin3.right])
Insert cell
y_3 = d3.scaleLinear()
.domain([d3.min(data_min.series, d => d3.min(d.values)), d3.max(data_min.series, d => d3.max(d.values))]).nice()
.range([height3 - margin3.bottom, margin3.top])
Insert cell
xAxis_3 = g => g
.attr("transform", `translate(0,${height3 - margin3.bottom})`)
.call(d3.axisBottom(x_3).ticks(width / 80).tickSizeOuter(0))
Insert cell
yAxis_3 = g => g
.attr("transform", `translate(${margin3.left},0)`)
.call(d3.axisLeft(y_3))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data_min.y))
Insert cell
line = d3.line()
.defined(d => !isNaN(d))
.x((d, i) => x_3(data_min.dates[i]))
.y(d => y_3(d))
Insert cell
chart4 = chart3
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more