diff --git a/src/marks/bollinger.js b/src/marks/bollinger.js index 03b46189d2..8b0528785d 100644 --- a/src/marks/bollinger.js +++ b/src/marks/bollinger.js @@ -1,10 +1,10 @@ import {deviation, mean} from "d3"; import {marks} from "../mark.js"; +import {identity, isNoneish} from "../options.js"; import {map} from "../transforms/map.js"; import {window} from "../transforms/window.js"; import {areaX, areaY} from "./area.js"; import {lineX, lineY} from "./line.js"; -import {identity} from "../options.js"; const defaults = { n: 20, @@ -32,14 +32,18 @@ export function bollingerX( } = {} ) { return marks( - areaX( - data, - map( - {x1: bollinger({k: -k, ...options}), x2: bollinger({k, ...options})}, - {x1: x, x2: x, y, fill, fillOpacity, ...options} - ) - ), - lineX(data, map({x: bollinger(options)}, {x, y, stroke, strokeOpacity, strokeWidth, ...options})) + isNoneish(fill) + ? null + : areaX( + data, + map( + {x1: bollinger({k: -k, ...options}), x2: bollinger({k, ...options})}, + {x1: x, x2: x, y, fill, fillOpacity, ...options} + ) + ), + isNoneish(stroke) + ? null + : lineX(data, map({x: bollinger(options)}, {x, y, stroke, strokeOpacity, strokeWidth, ...options})) ); } @@ -60,14 +64,18 @@ export function bollingerY( } = {} ) { return marks( - areaY( - data, - map( - {y1: bollinger({k: -k, ...options}), y2: bollinger({k, ...options})}, - {x, y1: y, y2: y, fill, fillOpacity, ...options} - ) - ), - lineY(data, map({y: bollinger(options)}, {x, y, stroke, strokeOpacity, strokeWidth, ...options})) + isNoneish(fill) + ? null + : areaY( + data, + map( + {y1: bollinger({k: -k, ...options}), y2: bollinger({k, ...options})}, + {x, y1: y, y2: y, fill, fillOpacity, ...options} + ) + ), + isNoneish(stroke) + ? null + : lineY(data, map({y: bollinger(options)}, {x, y, stroke, strokeOpacity, strokeWidth, ...options})) ); } diff --git a/test/output/aaplBollingerCandlestick.svg b/test/output/aaplBollingerCandlestick.svg new file mode 100644 index 0000000000..6e8c439860 --- /dev/null +++ b/test/output/aaplBollingerCandlestick.svg @@ -0,0 +1,2619 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 68 + 70 + 72 + 74 + 76 + 78 + 80 + 82 + 84 + 86 + 88 + 90 + 92 + + + + + + + + + + + Jan2014 + Feb + Mar + Apr + May + Jun + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/aapl-bollinger.ts b/test/plots/aapl-bollinger.ts index 7d554d301e..5b90ed3561 100644 --- a/test/plots/aapl-bollinger.ts +++ b/test/plots/aapl-bollinger.ts @@ -2,20 +2,20 @@ import * as Plot from "@observablehq/plot"; import * as d3 from "d3"; export async function aaplBollinger() { - const AAPL = await d3.csv("data/aapl.csv", d3.autoType); + const aapl = await d3.csv("data/aapl.csv", d3.autoType); return Plot.plot({ y: { grid: true }, marks: [ - Plot.bollingerY(AAPL, {x: "Date", y: "Close", stroke: "blue"}), - Plot.line(AAPL, {x: "Date", y: "Close", strokeWidth: 1}) + Plot.bollingerY(aapl, {x: "Date", y: "Close", stroke: "blue"}), + Plot.line(aapl, {x: "Date", y: "Close", strokeWidth: 1}) ] }); } export async function aaplBollingerGridInterval() { - const AAPL = await d3.csv("data/aapl.csv", d3.autoType); + const aapl = await d3.csv("data/aapl.csv", d3.autoType); return Plot.plot({ marks: [ Plot.frame({fill: "#eaeaea"}), @@ -25,14 +25,14 @@ export async function aaplBollingerGridInterval() { Plot.gridX({tickSpacing: 40, stroke: "#fff", strokeOpacity: 1, strokeWidth: 0.5}), Plot.gridX({tickSpacing: 80, stroke: "#fff", strokeOpacity: 1}), Plot.axisX({tickSpacing: 80}), - Plot.bollingerY(AAPL, {x: "Date", y: "Close", stroke: "blue"}), - Plot.line(AAPL, {x: "Date", y: "Close", strokeWidth: 1}) + Plot.bollingerY(aapl, {x: "Date", y: "Close", stroke: "blue"}), + Plot.line(aapl, {x: "Date", y: "Close", strokeWidth: 1}) ] }); } export async function aaplBollingerGridSpacing() { - const AAPL = await d3.csv("data/aapl.csv", d3.autoType); + const aapl = await d3.csv("data/aapl.csv", d3.autoType); return Plot.plot({ marks: [ Plot.frame({fill: "#eaeaea"}), @@ -42,8 +42,29 @@ export async function aaplBollingerGridSpacing() { Plot.gridX({interval: "3 months", stroke: "#fff", strokeOpacity: 1, strokeWidth: 0.5}), Plot.gridX({interval: "1 year", stroke: "#fff", strokeOpacity: 1}), Plot.axisX({interval: "1 year"}), - Plot.bollingerY(AAPL, {x: "Date", y: "Close", stroke: "blue"}), - Plot.line(AAPL, {x: "Date", y: "Close", strokeWidth: 1}) + Plot.bollingerY(aapl, {x: "Date", y: "Close", stroke: "blue"}), + Plot.line(aapl, {x: "Date", y: "Close", strokeWidth: 1}) + ] + }); +} + +export async function aaplBollingerCandlestick() { + const aapl = await d3.csv("data/aapl.csv", d3.autoType); + return Plot.plot({ + x: {domain: [new Date("2014-01-01"), new Date("2014-06-01")]}, + y: {domain: [68, 92], grid: true}, + color: {domain: [-1, 0, 1], range: ["red", "black", "green"]}, + marks: [ + Plot.bollingerY(aapl, {x: "Date", y: "Close", stroke: "none", clip: true}), + Plot.ruleX(aapl, {x: "Date", y1: "Low", y2: "High", strokeWidth: 1, clip: true}), + Plot.ruleX(aapl, { + x: "Date", + y1: "Open", + y2: "Close", + strokeWidth: 3, + stroke: (d) => Math.sign(d.Close - d.Open), + clip: true + }) ] }); }