Financial Charts
Mix.install([
{:plotly_ex, "~> 0.1"},
{:kino, "~> 0.18"},
{:req, "~> 0.5"}
])
Financial Charts > Basic Waterfall Chart
Waterfall.new(x:, y:, measure:) — measure is a list of strings:
-
"relative"— bar height = the y value (positive or negative increment) -
"total"— bar shows the running total (y value ignored; set to 0) -
"absolute"— bar starts from zero (used for the initial bar)
connector: %{line:} styles the horizontal lines connecting bar tops.
textposition: "outside" and text: add value labels above/below each bar.
alias Plotly.{Figure, Waterfall}
Figure.new()
|> Figure.add_trace(
Waterfall.new(
name: "2018",
orientation: "v",
measure: ["relative", "relative", "total", "relative", "relative", "total"],
x: ["Sales", "Consulting", "Net revenue", "Purchases", "Other expenses", "Profit before tax"],
textposition: "outside",
text: ["+60", "+80", "", "-40", "-20", "Total"],
y: [60, 80, 0, -40, -20, 0],
connector: %{line: %{color: "rgb(63, 63, 63)"}}
)
)
|> Figure.update_layout(
title: "Profit and Loss Statement 2018",
xaxis: %{type: "category"},
yaxis: %{type: "linear"},
showlegend: true
)
|> Plotly.show()
Financial Charts > Multi Category Waterfall Chart
Hierarchical x: uses a list of two lists — outer and inner category labels — creating
a multi-level x-axis. base: 1000 shifts the starting value of all bars. nil (nil in
Elixir) as a y value for "total" bars lets Plotly compute the running total automatically.
layout.waterfallgroupgap: controls spacing between groups.
alias Plotly.{Figure, Waterfall}
outer = ["2016", "2017", "2017", "2017", "2017", "2018", "2018", "2018", "2018"]
inner = ["initial", "q1", "q2", "q3", "total", "q1", "q2", "q3", "total"]
measure = [
"absolute",
"relative",
"relative",
"relative",
"total",
"relative",
"relative",
"relative",
"total"
]
Figure.new()
|> Figure.add_trace(
Waterfall.new(
x: [outer, inner],
measure: measure,
y: [1, 2, 3, -1, nil, 1, 2, -4, nil],
base: 1000
)
)
|> Figure.add_trace(
Waterfall.new(
x: [outer, inner],
measure: measure,
y: [1.1, 2.2, 3.3, -1.1, nil, 1.1, 2.2, -4.4, nil],
base: 1000
)
)
|> Figure.update_layout(
waterfallgroupgap: 0.5,
xaxis: %{title: %{text: "MULTI-CATEGORY"}, tickfont: %{size: 16}, ticks: "outside"}
)
|> Plotly.show()
Financial Charts > Horizontal Waterfall Chart
orientation: "h" flips the waterfall chart horizontally. Roles are swapped:
y: holds the category labels, x: holds the numeric values. nil y values for "total"
bars still work the same way. connector: %{mode: "between", line:} draws connector lines
between bar ends. yaxis.autorange: "reversed" lists categories top-to-bottom.
alias Plotly.{Figure, Waterfall}
Figure.new()
|> Figure.add_trace(
Waterfall.new(
name: "2018",
orientation: "h",
measure: [
"relative",
"relative",
"relative",
"relative",
"total",
"relative",
"relative",
"relative",
"relative",
"total",
"relative",
"relative",
"total",
"relative",
"total"
],
y: [
"Sales",
"Consulting",
"Maintenance",
"Other revenue",
"Net revenue",
"Purchases",
"Material expenses",
"Personnel expenses",
"Other expenses",
"Operating profit",
"Investment income",
"Financial income",
"Profit before tax",
"Income tax (15%)",
"Profit after tax"
],
x: [375, 128, 78, 27, nil, -327, -12, -78, -12, nil, 32, 89, nil, -45, nil],
connector: %{
mode: "between",
line: %{width: 4, color: "rgb(0, 0, 0)", dash: 0}
}
)
)
|> Figure.update_layout(
title: "Profit and Loss Statement 2018",
yaxis: %{type: "category", autorange: "reversed"},
xaxis: %{type: "linear"},
margin: %{l: 150},
showlegend: true
)
|> Plotly.show()
Financial Charts > Style Waterfall Chart
decreasing:, increasing:, and totals: maps each accept a marker: field with
color: and line: for per-type bar styling. layout.waterfallgap: sets the gap fraction
between bars (0–1). This allows full visual differentiation between positive, negative, and
summary bars without needing to style each bar individually.
alias Plotly.{Figure, Waterfall}
Figure.new()
|> Figure.add_trace(
Waterfall.new(
x: [
["2016", "2017", "2017", "2017", "2017", "2018", "2018", "2018", "2018"],
["initial", "q1", "q2", "q3", "total", "q1", "q2", "q3", "total"]
],
measure: [
"absolute",
"relative",
"relative",
"relative",
"total",
"relative",
"relative",
"relative",
"total"
],
y: [10, 20, 30, -10, nil, 10, 20, -40, nil],
base: 300,
decreasing: %{marker: %{color: "Maroon", line: %{color: "red", width: 2}}},
increasing: %{marker: %{color: "Teal"}},
totals: %{marker: %{color: "deep sky blue", line: %{color: "blue", width: 3}}}
)
)
|> Figure.update_layout(
title: "Profit and Loss Statement",
waterfallgap: 0.3,
xaxis: %{tickfont: %{size: 15}, ticks: "outside"}
)
|> Plotly.show()
Financial Charts > Indicators Overview
Indicator traces display key metrics as numbers, deltas, or gauges. mode: is a "+" joined
string combining "number", "delta", and/or "gauge". Multiple indicators share a grid via
layout.grid with domain: %{row:, column:} per trace.
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "number+delta+gauge",
value: 200,
delta: %{reference: 160},
gauge: %{axis: %{visible: false, range: [0, 250]}},
domain: %{row: 0, column: 0}
)
)
|> Figure.add_trace(
Indicator.new(
mode: "number+gauge+delta",
value: 120,
gauge: %{shape: "bullet", axis: %{visible: false, range: [-200, 200]}},
domain: %{x: [0.1, 0.5], y: [0.15, 0.35]}
)
)
|> Figure.add_trace(
Indicator.new(
mode: "number+delta",
value: 300,
domain: %{row: 0, column: 1}
)
)
|> Figure.add_trace(
Indicator.new(
mode: "delta",
value: 40,
domain: %{row: 1, column: 1}
)
)
|> Figure.update_layout(
width: 600,
height: 400,
margin: %{t: 25, b: 25, l: 25, r: 25},
grid: %{rows: 2, columns: 2, pattern: "independent"},
template: %{
data: %{
indicator: [%{
title: %{text: "Speed"},
mode: "number+delta+gauge",
delta: %{reference: 90}
}]
}
}
)
|> Plotly.show()
Financial Charts > A Single Angular Gauge Chart
An angular gauge uses mode: "gauge+number". gauge.axis.range: [min, max] sets the scale.
delta.reference: shows the change from a baseline value. nil as the lower bound lets Plotly
auto-determine it (equivalent to JS null).
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "gauge+number",
value: 450,
title: %{text: "Speed"},
delta: %{reference: 400},
gauge: %{axis: %{range: [nil, 500]}},
domain: %{x: [0, 1], y: [0, 1]}
)
)
|> Figure.update_layout(width: 600, height: 400)
|> Plotly.show()
Financial Charts > Bullet Gauge
A bullet gauge uses gauge: %{shape: "bullet"} — it renders as a horizontal bar rather than
an angular arc. mode: "number+gauge+delta" shows the numeric value, bullet bar, and delta
from the reference all in one compact widget.
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "number+gauge+delta",
value: 220,
title: %{text: "Profit"},
delta: %{reference: 300},
gauge: %{shape: "bullet"},
domain: %{x: [0, 1], y: [0, 1]}
)
)
|> Figure.update_layout(width: 600, height: 250)
|> Plotly.show()
Financial Charts > Showing Information above Your Chart
An Indicator trace can be positioned over other chart types by giving it a domain that
occupies the upper portion of the plot area. A regular Scatter trace fills the lower portion.
Both share the same figure; the indicator overlays as a floating label.
alias Plotly.{Figure, Indicator, Scatter}
y_data = [325, 324, 405, 400, 424, 404, 417, 432, 419, 394, 410, 426, 413, 419, 404,
408, 401, 377, 368, 361, 356, 359, 375, 397, 394, 418, 437, 450, 430, 442,
424, 443, 420, 418, 423, 423, 426, 440, 437, 436, 447, 460, 478, 472, 450,
456, 436, 418, 429, 412, 429, 442, 464, 447, 434, 457, 474, 480, 499, 497,
480, 502, 512, 492]
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "number+delta",
value: 492,
title: %{text: "Users online"},
delta: %{reference: 512, valueformat: ".0f"},
domain: %{y: [0, 1], x: [0.25, 0.75]}
)
)
|> Figure.add_trace(
Scatter.new(y: y_data)
)
|> Figure.update_layout(
width: 600,
height: 450,
xaxis: %{range: [0, 62]}
)
|> Plotly.show()
Financial Charts > Data Cards / Big Numbers
number: %{prefix: "$"} adds a currency prefix. delta: %{position: "top"} places the change
indicator above the main value. delta: %{relative: true} shows percentage change. Multiple
indicators tiled via domain: %{x:, y:} create a KPI dashboard layout. HTML in title.text
allows multi-line subtitles with inline styles.
alias Plotly.{Figure, Indicator}
# Single big number card
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "number+delta",
value: 400,
number: %{prefix: "$"},
delta: %{position: "top", reference: 320},
domain: %{x: [0, 1], y: [0, 1]}
)
)
|> Figure.update_layout(
paper_bgcolor: "lightgray",
width: 600,
height: 200,
margin: %{t: 0, b: 0, l: 0, r: 0}
)
|> Plotly.show()
# Multiple KPI cards in a grid
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "number+delta",
value: 200,
delta: %{reference: 400, relative: true, position: "top"},
domain: %{x: [0, 0.5], y: [0, 0.5]}
)
)
|> Figure.add_trace(
Indicator.new(
mode: "number+delta",
value: 350,
delta: %{reference: 400, relative: true},
domain: %{x: [0, 0.5], y: [0.5, 1]}
)
)
|> Figure.add_trace(
Indicator.new(
mode: "number+delta",
value: 450,
title: %{text: "Accounts
Subtitle"},
delta: %{reference: 400, relative: true},
domain: %{x: [0.6, 1], y: [0, 1]}
)
)
|> Figure.update_layout(
width: 600,
height: 400,
margin: %{t: 25, r: 25, l: 25, b: 25}
)
|> Plotly.show()
Financial Charts > Simple Candlestick Chart
Candlestick.new(x:, open:, high:, low:, close:) — x is a list of date strings (ISO 8601).
By default a rangeslider appears below the x-axis. increasing: and decreasing: style the
up and down bars respectively.
alias Plotly.{Figure, Candlestick}
Figure.new()
|> Figure.add_trace(
Candlestick.new(
x: ["2017-01-04","2017-01-05","2017-01-06","2017-01-09","2017-01-10",
"2017-01-11","2017-01-12","2017-01-13","2017-01-17","2017-01-18",
"2017-01-19","2017-01-20","2017-01-23","2017-01-24","2017-01-25",
"2017-01-26","2017-01-27","2017-01-30","2017-01-31","2017-02-01",
"2017-02-02","2017-02-03","2017-02-06","2017-02-07","2017-02-08",
"2017-02-09","2017-02-10","2017-02-13","2017-02-14","2017-02-15"],
open: [115.849998,115.919998,116.779999,117.949997,118.769997,
118.739998,118.900002,119.110001,118.339996,120.0,
119.400002,120.449997,120.0,119.550003,120.419998,
121.669998,122.139999,120.93,121.150002,127.029999,
127.980003,128.309998,129.130005,130.539993,131.350006,
131.649994,132.460007,133.080002,133.470001,135.520004],
high: [116.510002,116.860001,118.160004,119.43,119.379997,
119.93,119.300003,119.620003,120.239998,120.5,
120.089996,120.449997,120.809998,120.099998,122.099998,
122.440002,122.349998,121.629997,121.389999,130.490005,
129.389999,129.190002,130.5,132.089996,132.220001,
132.449997,132.940002,133.820007,135.089996,136.270004],
low: [115.75,115.809998,116.470001,117.940002,118.300003,
118.599998,118.209999,118.809998,118.220001,119.709999,
119.370003,119.730003,119.769997,119.5,120.279999,
121.599998,121.599998,120.660004,120.620003,127.010002,
127.779999,128.160004,128.899994,130.449997,131.220001,
131.119995,132.050003,132.75,133.25,134.619995],
close: [116.019997,116.610001,117.910004,118.989998,119.110001,
119.75,119.25,119.040001,120.0,119.989998,
119.779999,120.0,120.080002,119.970001,121.879997,
121.940002,121.949997,121.629997,121.349998,128.75,
128.529999,129.080002,130.289993,131.529999,132.039993,
132.419998,132.119995,133.289993,135.020004,135.509995],
increasing: %{line: %{color: "#17BECF"}},
decreasing: %{line: %{color: "#7F7F7F"}}
)
)
|> Figure.update_layout(
title: "Apple Stock (Jan–Feb 2017)",
showlegend: false,
xaxis: %{
type: "date",
title: %{text: "Date"},
rangeslider: %{range: ["2017-01-03 12:00", "2017-02-15 12:00"]}
}
)
|> Plotly.show()
Financial Charts > Candlestick Chart without Rangeslider
layout.xaxis.rangeslider: %{visible: false} removes the default rangeslider. This example
uses a larger AAPL dataset fetched from GitHub. CSV parsing uses String.split — no extra
dependencies needed.
alias Plotly.{Figure, Candlestick}
csv = Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed = Enum.map(rows, fn row ->
Enum.zip(headers, String.split(row, ",")) |> Map.new()
end)
dates = Enum.map(parsed, & &1["Date"])
open = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Open"]), 0)))
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
close = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Close"]), 0)))
Figure.new()
|> Figure.add_trace(
Candlestick.new(
x: dates, open: open, high: high, low: low, close: close,
increasing: %{line: %{color: "black"}},
decreasing: %{line: %{color: "red"}}
)
)
|> Figure.update_layout(
title: "AAPL — No Rangeslider",
showlegend: false,
xaxis: %{rangeslider: %{visible: false}}
)
|> Plotly.show()
Financial Charts > Customise Candlestick Chart with Shapes and Annotations
layout.shapes and layout.annotations work on candlestick charts exactly as on scatter
plots. A rect shape with xref: "x" and yref: "paper" highlights a date range across the
full height of the chart. An annotation with xanchor: "right" labels the event.
alias Plotly.{Figure, Candlestick}
Figure.new()
|> Figure.add_trace(
Candlestick.new(
x: ["2017-01-17","2017-01-18","2017-01-19","2017-01-20","2017-01-23",
"2017-01-24","2017-01-25","2017-01-26","2017-01-27","2017-01-30",
"2017-01-31","2017-02-01","2017-02-02","2017-02-03","2017-02-06",
"2017-02-07","2017-02-08","2017-02-09","2017-02-10"],
open: [118.339996,120.0,119.400002,120.449997,120.0,
119.550003,120.419998,121.669998,122.139999,120.93,
121.150002,127.029999,127.980003,128.309998,129.130005,
130.539993,131.350006,131.649994,132.460007],
high: [120.239998,120.5,120.089996,120.449997,120.809998,
120.099998,122.099998,122.440002,122.349998,121.629997,
121.389999,130.490005,129.389999,129.190002,130.5,
132.089996,132.220001,132.449997,132.940002],
low: [118.220001,119.709999,119.370003,119.730003,119.769997,
119.5,120.279999,121.599998,121.599998,120.660004,
120.620003,127.010002,127.779999,128.160004,128.899994,
130.449997,131.220001,131.119995,132.050003],
close: [120.0,119.989998,119.779999,120.0,120.080002,
119.970001,121.879997,121.940002,121.949997,121.629997,
121.349998,128.75,128.529999,129.080002,130.289993,
131.529999,132.039993,132.419998,132.119995],
increasing: %{line: %{color: "#17BECF"}},
decreasing: %{line: %{color: "#7F7F7F"}}
)
)
|> Figure.update_layout(
showlegend: false,
xaxis: %{
type: "date",
title: %{text: "Date"},
rangeslider: %{range: ["2017-01-17 12:00", "2017-02-10 12:00"]}
},
shapes: [%{
type: "rect",
xref: "x", yref: "paper",
x0: "2017-01-31", y0: 0,
x1: "2017-02-01", y1: 1,
fillcolor: "#d3d3d3",
opacity: 0.2,
line: %{width: 0}
}],
annotations: [%{
x: "2017-01-31",
y: 0.9,
xref: "x", yref: "paper",
text: "largest movement",
font: %{color: "magenta"},
showarrow: true,
xanchor: "right",
ax: -20, ay: 0
}]
)
|> Plotly.show()
Financial Charts > Customizing Candlestick Chart Colors
increasing: %{line: %{color:}} and decreasing: %{line: %{color:}} set the bar outline
colours for up and down movements. xaxis.range: restricts the visible date window to a
6-month period while the full dataset is loaded.
alias Plotly.{Figure, Candlestick}
csv = Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed = Enum.map(rows, fn row ->
Enum.zip(headers, String.split(row, ",")) |> Map.new()
end)
dates = Enum.map(parsed, & &1["Date"])
open = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Open"]), 0)))
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
close = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Close"]), 0)))
Figure.new()
|> Figure.add_trace(
Candlestick.new(
x: dates, open: open, high: high, low: low, close: close,
increasing: %{line: %{color: "black"}},
decreasing: %{line: %{color: "red"}}
)
)
|> Figure.update_layout(
title: "AAPL — Custom Colors",
showlegend: false,
xaxis: %{
title: %{text: "Date"},
range: ["2016-06-01 12:00", "2017-01-01 12:00"]
}
)
|> Plotly.show()
Financial Charts > Add Rangeselector
xaxis.rangeselector: %{buttons: [...]} adds preset time-period buttons above the chart.
Each button needs step: ("month", "year", "all"), stepmode: ("backward" counts
back from today; "todate" goes to start of current period), count:, and label:.
step: "all" shows the full dataset.
alias Plotly.{Figure, Candlestick}
csv = Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed = Enum.map(rows, fn row ->
Enum.zip(headers, String.split(row, ",")) |> Map.new()
end)
dates = Enum.map(parsed, & &1["Date"])
open = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Open"]), 0)))
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
close = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Close"]), 0)))
Figure.new()
|> Figure.add_trace(
Candlestick.new(
x: dates, open: open, high: high, low: low, close: close,
increasing: %{line: %{color: "black"}},
decreasing: %{line: %{color: "red"}}
)
)
|> Figure.update_layout(
title: "AAPL — With Rangeselector",
showlegend: false,
xaxis: %{
title: %{text: "Date"},
rangeselector: %{
x: 0, y: 1.2,
xanchor: "left",
font: %{size: 8},
buttons: [
%{step: "month", stepmode: "backward", count: 1, label: "1 month"},
%{step: "month", stepmode: "backward", count: 6, label: "6 months"},
%{step: "all", label: "All dates"}
]
}
}
)
|> Plotly.show()
Financial Charts > Basic Funnel Plot
Funnel.new(x:, y:) — x holds the numeric values per stage, y holds the stage labels.
Plotly automatically calculates percent-initial and percent-previous values for hover and
display. Stages shrink left-to-right following the value order.
alias Plotly.{Figure, Funnel}
Figure.new()
|> Figure.add_trace(
Funnel.new(
y: ["Website visit", "Downloads", "Potential customers", "Invoice sent", "Closed deals"],
x: [13873, 10533, 5443, 2703, 908],
hoverinfo: "x+percent previous+percent initial"
)
)
|> Figure.update_layout(
title: "Sales Funnel",
margin: %{l: 150},
width: 600,
height: 500
)
|> Plotly.show()
Financial Charts > Setting Marker Size and Color
marker.color as a list assigns per-stage fill colours. marker.line.color and
marker.line.width as lists style the border of each stage bar. connector.line styles the
trapezoid connectors between stages. textinfo: "value+percent initial" shows both the
raw value and percentage in each bar.
alias Plotly.{Figure, Funnel}
Figure.new()
|> Figure.add_trace(
Funnel.new(
y: ["Sales person A", "Sales person B", "Sales person C", "Sales person D", "Sales person E"],
x: [1200, 909.4, 600.6, 300, 80],
textposition: "inside",
textinfo: "value+percent initial",
hoverinfo: "percent total+x",
opacity: 0.65,
marker: %{
color: ["#59D4E8", "#DDB6C6", "#A696C8", "#67EACA", "#94D2E6"],
line: %{
width: [4, 2, 2, 3, 1],
color: ["#3E4E88", "#606470", "#3E4E88", "#606470", "#3E4E88"]
}
},
connector: %{
line: %{color: "royalblue", dash: "dot", width: 3}
}
)
)
|> Figure.update_layout(
title: "Sales by Person",
margin: %{l: 100},
width: 600,
height: 500
)
|> Plotly.show()
Financial Charts > Stacked Funnel
layout.funnelmode: "stack" stacks multiple Funnel traces side-by-side at each stage.
Each trace can use a different textinfo and textposition. Stages with more traces than
others simply have fewer bars — missing stages are skipped gracefully.
alias Plotly.{Figure, Funnel}
Figure.new()
|> Figure.add_trace(
Funnel.new(
name: "Montreal",
y: ["Website visit", "Downloads", "Potential customers", "Requested price"],
x: [120, 60, 30, 20],
textinfo: "value+percent initial"
)
)
|> Figure.add_trace(
Funnel.new(
name: "Toronto",
y: ["Website visit", "Downloads", "Potential customers", "Requested price", "Invoice sent"],
x: [100, 60, 40, 30, 20],
textposition: "inside",
textinfo: "value+percent previous"
)
)
|> Figure.add_trace(
Funnel.new(
name: "Vancouver",
y: ["Website visit", "Downloads", "Potential customers", "Requested price", "Invoice sent", "Closed deals"],
x: [90, 70, 50, 30, 10, 5],
textposition: "outside",
textinfo: "value+percent total"
)
)
|> Figure.update_layout(
title: "Multi-City Sales Funnel",
margin: %{l: 130, r: 0},
width: 600,
funnelmode: "stack",
showlegend: true
)
|> Plotly.show()
Financial Charts > Funnelarea Plot
Funnelarea renders a triangular funnel where each section’s area is proportional to its
values: entry. text: labels each section. Unlike Funnel, it does not show absolute widths
but relative areas. textfont: styles all section labels at once.
alias Plotly.{Figure, Funnelarea}
Figure.new()
|> Figure.add_trace(
Funnelarea.new(
values: [5, 4, 3, 2, 1],
text: ["The 1st", "The 2nd", "The 3rd", "The 4th", "The 5th"],
marker: %{
colors: ["#59D4E8", "#DDB6C6", "#A696C8", "#67EACA", "#94D2E6"],
line: %{
color: ["#3E4E88", "#606470", "#3E4E88", "#606470", "#3E4E88"],
width: [2, 1, 5, 0, 3]
}
},
textfont: %{family: "Old Standard TT", size: 13, color: "black"},
opacity: 0.65
)
)
|> Figure.update_layout(
title: "Funnelarea Chart",
margin: %{l: 200, r: 200},
showlegend: true
)
|> Plotly.show()
Financial Charts > Multi Funnelarea
Multiple Funnelarea traces are positioned via domain: %{x: [...], y: [...]}. scalegroup:
links traces so their funnel widths scale relative to each other — traces in "first" share
one scale, "second" another. title: %{position:, text:} labels each panel.
alias Plotly.{Figure, Funnelarea}
Figure.new()
|> Figure.add_trace(
Funnelarea.new(
scalegroup: "first",
values: [500, 450, 340, 230, 220, 110],
textinfo: "value",
title: %{position: "top center", text: "Sales for Sale Person A in U.S."},
domain: %{x: [0, 0.5], y: [0, 0.5]}
)
)
|> Figure.add_trace(
Funnelarea.new(
scalegroup: "first",
values: [600, 500, 400, 300, 200, 100],
textinfo: "value",
title: %{position: "top center", text: "Sales of Sale Person B in Canada"},
domain: %{x: [0, 0.5], y: [0.55, 1]}
)
)
|> Figure.add_trace(
Funnelarea.new(
scalegroup: "second",
values: [510, 480, 440, 330, 220, 100],
textinfo: "value",
title: %{position: "top left", text: "Sales of Sale Person A in Canada"},
domain: %{x: [0.55, 1], y: [0, 0.5]}
)
)
|> Figure.add_trace(
Funnelarea.new(
scalegroup: "second",
values: [360, 250, 240, 130, 120, 60],
textinfo: "value",
title: %{position: "top left", text: "Sales of Sale Person B in U.S."},
domain: %{x: [0.55, 1], y: [0.55, 1]}
)
)
|> Figure.update_layout(width: 600)
|> Plotly.show()
Financial Charts > Date Strings
Plotly accepts ISO 8601 date strings directly in x:. No special parsing needed — pass them
as plain Elixir strings. xaxis.type: "date" is usually auto-detected but can be set
explicitly. Date format: "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS".
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: ["2013-10-04 22:23:00", "2013-11-04 22:23:00", "2013-12-04 22:23:00"],
y: [1, 3, 6]
)
)
|> Figure.update_layout(title: "Date Strings in Plotly")
|> Plotly.show()
Financial Charts > Basic Time Series
This example plots AAPL High and Low prices over ~2 years. Data is fetched from a GitHub CSV.
mode: "lines" with named traces gives a clean time series. Plotly auto-detects the date
axis from the ISO string format.
alias Plotly.{Figure, Scatter}
csv = Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed = Enum.map(rows, fn row ->
Enum.zip(headers, String.split(row, ",")) |> Map.new()
end)
dates = Enum.map(parsed, & &1["Date"])
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
Figure.new()
|> Figure.add_trace(
Scatter.new(
mode: "lines",
name: "AAPL High",
x: dates, y: high,
line: %{color: "#17BECF"}
)
)
|> Figure.add_trace(
Scatter.new(
mode: "lines",
name: "AAPL Low",
x: dates, y: low,
line: %{color: "#7F7F7F"}
)
)
|> Figure.update_layout(title: "Apple Stock High & Low")
|> Plotly.show()
Financial Charts > Manually Set Range
xaxis.range: [date_string, date_string] restricts the visible time window even when the
full dataset is loaded. xaxis.type: "date" must be set explicitly when providing a range —
Plotly needs to know the axis type before the data is inspected.
alias Plotly.{Figure, Scatter}
csv = Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed = Enum.map(rows, fn row ->
Enum.zip(headers, String.split(row, ",")) |> Map.new()
end)
dates = Enum.map(parsed, & &1["Date"])
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
Figure.new()
|> Figure.add_trace(Scatter.new(mode: "lines", x: dates, y: high, line: %{color: "#17BECF"}))
|> Figure.add_trace(Scatter.new(mode: "lines", x: dates, y: low, line: %{color: "#7F7F7F"}))
|> Figure.update_layout(
title: "AAPL — Custom Range (H2 2016)",
xaxis: %{
type: "date",
range: ["2016-07-01", "2016-12-31"]
},
yaxis: %{autorange: true}
)
|> Plotly.show()
Financial Charts > Time Series with Rangeslider
xaxis.rangeslider: %{range: [start, end]} adds an interactive minimap slider below the
chart. xaxis.rangeselector: %{buttons: [...]} adds preset period buttons above the chart.
Both can be combined. step: "all" shows the complete dataset.
alias Plotly.{Figure, Scatter}
csv = Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed = Enum.map(rows, fn row ->
Enum.zip(headers, String.split(row, ",")) |> Map.new()
end)
dates = Enum.map(parsed, & &1["Date"])
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
Figure.new()
|> Figure.add_trace(
Scatter.new(mode: "lines", name: "AAPL High", x: dates, y: high, line: %{color: "#17BECF"})
)
|> Figure.add_trace(
Scatter.new(mode: "lines", name: "AAPL Low", x: dates, y: low, line: %{color: "#7F7F7F"})
)
|> Figure.update_layout(
title: "Time Series with Rangeslider",
xaxis: %{
type: "date",
autorange: true,
rangeselector: %{
buttons: [
%{count: 1, label: "1m", step: "month", stepmode: "backward"},
%{count: 6, label: "6m", step: "month", stepmode: "backward"},
%{step: "all"}
]
},
rangeslider: %{range: ["2015-02-17", "2017-02-16"]}
},
yaxis: %{autorange: true}
)
|> Plotly.show()
Financial Charts > Simple OHLC Chart
Ohlc.new(x:, open:, high:, low:, close:) works identically to Candlestick but renders
as open/close tick stubs on a vertical high–low line rather than filled bodies. x contains
ISO 8601 date strings. A rangeslider appears below the chart by default.
alias Plotly.{Figure, Ohlc}
csv =
Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed =
Enum.map(rows, fn row -> Enum.zip(headers, String.split(row, ",")) |> Map.new() end)
dates = Enum.map(parsed, & &1["Date"])
open = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Open"]), 0)))
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
close = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Close"]), 0)))
Figure.new()
|> Figure.add_trace(Ohlc.new(x: dates, open: open, high: high, low: low, close: close))
|> Figure.update_layout(title: "Apple OHLC Chart")
|> Plotly.show()
Financial Charts > OHLC Chart without Rangeslider
Set layout.xaxis.rangeslider: %{visible: false} to remove the default rangeslider that
appears below OHLC (and Candlestick) charts.
alias Plotly.{Figure, Ohlc}
csv =
Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed =
Enum.map(rows, fn row -> Enum.zip(headers, String.split(row, ",")) |> Map.new() end)
dates = Enum.map(parsed, & &1["Date"])
open = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Open"]), 0)))
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
close = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Close"]), 0)))
Figure.new()
|> Figure.add_trace(Ohlc.new(x: dates, open: open, high: high, low: low, close: close))
|> Figure.update_layout(
title: "Apple OHLC — No Rangeslider",
xaxis: %{rangeslider: %{visible: false}}
)
|> Plotly.show()
Financial Charts > Customizing the Figure with Shapes and Annotations
layout.shapes and layout.annotations work on OHLC charts exactly as on scatter plots.
A rect shape with xref: "x" and yref: "paper" highlights a date range across the full
chart height. Pair with an annotation to label the event.
alias Plotly.{Figure, Ohlc}
csv =
Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed =
Enum.map(rows, fn row -> Enum.zip(headers, String.split(row, ",")) |> Map.new() end)
dates = Enum.map(parsed, & &1["Date"])
open = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Open"]), 0)))
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
close = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Close"]), 0)))
Figure.new()
|> Figure.add_trace(Ohlc.new(x: dates, open: open, high: high, low: low, close: close))
|> Figure.update_layout(
title: "Apple OHLC — Shapes & Annotations",
showlegend: false,
xaxis: %{type: "date", rangeslider: %{visible: false}},
shapes: [
%{
type: "rect",
xref: "x", yref: "paper",
x0: "2016-01-01", y0: 0,
x1: "2016-02-01", y1: 1,
fillcolor: "#d3d3d3",
opacity: 0.3,
line: %{width: 0}
}
],
annotations: [
%{
x: "2016-01-15",
y: 0.95,
xref: "x", yref: "paper",
text: "Jan 2016 drop",
font: %{color: "darkblue"},
showarrow: true,
ax: 0, ay: -30
}
]
)
|> Plotly.show()
Financial Charts > Customise OHLC Chart Colors
increasing: %{line: %{color:}} and decreasing: %{line: %{color:}} set the colors for
up and down bars. OHLC uses lines only (no filled bodies), so only line.color applies —
unlike Candlestick which also supports fillcolor.
alias Plotly.{Figure, Ohlc}
csv =
Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed =
Enum.map(rows, fn row -> Enum.zip(headers, String.split(row, ",")) |> Map.new() end)
dates = Enum.map(parsed, & &1["Date"])
open = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Open"]), 0)))
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
close = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Close"]), 0)))
Figure.new()
|> Figure.add_trace(
Ohlc.new(
x: dates,
open: open, high: high, low: low, close: close,
increasing: %{line: %{color: "#17BECF"}},
decreasing: %{line: %{color: "#7F7F7F"}}
)
)
|> Figure.update_layout(
title: "Apple OHLC — Custom Colors",
xaxis: %{rangeslider: %{visible: false}}
)
|> Plotly.show()
Financial Charts > Add Rangeselector
layout.xaxis.rangeselector: %{buttons: [...]} adds preset-period buttons above the chart.
Each button is %{count:, label:, step:, stepmode:}. step: "all" shows the full dataset.
stepmode: "backward" counts back from the last date; "todate" counts back from now.
alias Plotly.{Figure, Ohlc}
csv =
Req.get!("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv").body
[header | rows] = String.split(csv, "\n", trim: true)
headers = String.split(header, ",")
parsed =
Enum.map(rows, fn row -> Enum.zip(headers, String.split(row, ",")) |> Map.new() end)
dates = Enum.map(parsed, & &1["Date"])
open = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Open"]), 0)))
high = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.High"]), 0)))
low = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Low"]), 0)))
close = Enum.map(parsed, &(elem(Float.parse(&1["AAPL.Close"]), 0)))
Figure.new()
|> Figure.add_trace(Ohlc.new(x: dates, open: open, high: high, low: low, close: close))
|> Figure.update_layout(
title: "Apple OHLC — Rangeselector",
xaxis: %{
rangeselector: %{
buttons: [
%{count: 1, label: "1m", step: "month", stepmode: "backward"},
%{count: 6, label: "6m", step: "month", stepmode: "backward"},
%{count: 1, label: "YTD", step: "year", stepmode: "todate"},
%{count: 1, label: "1y", step: "year", stepmode: "backward"},
%{step: "all"}
]
},
rangeslider: %{visible: true},
type: "date"
}
)
|> Plotly.show()
Financial Charts > Basic Gauge
A basic gauge uses Indicator.new(mode: "gauge+number", ...). The gauge.axis.range sets
the scale — [nil, max] lets Plotly auto-determine the lower bound (equivalent to JS null).
This is the same as notebook 06 (single angular gauge) but without a delta.
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "gauge+number",
value: 270,
title: %{text: "Speed"},
gauge: %{axis: %{range: [nil, 500]}},
domain: %{x: [0, 1], y: [0, 1]}
)
)
|> Figure.update_layout(width: 600, height: 400)
|> Plotly.show()
Financial Charts > Add Steps, Threshold, and Delta
gauge.steps: adds colored bands to the gauge arc — each step is %{range: [lo, hi], color:}.
gauge.threshold: draws a marker line at a specific value: %{line: %{color:, width:}, thickness:, value:}.
delta.reference: shows the change from a baseline value.
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "gauge+number+delta",
value: 450,
title: %{text: "Speed"},
delta: %{reference: 380},
gauge: %{
axis: %{range: [nil, 500]},
steps: [
%{range: [0, 250], color: "lightgray"},
%{range: [250, 400], color: "gray"}
],
threshold: %{
line: %{color: "red", width: 4},
thickness: 0.75,
value: 490
}
},
domain: %{x: [0, 1], y: [0, 1]}
)
)
|> Figure.update_layout(width: 600, height: 400)
|> Plotly.show()
Financial Charts > Custom Gauge Chart
Full gauge styling: gauge.bar: %{color:} sets the value bar color. gauge.bgcolor: sets the
arc background. gauge.borderwidth: and gauge.bordercolor: style the border. Custom steps
and threshold can use any CSS color. number.prefix: and delta.relative: true format the display.
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "gauge+number+delta",
value: 420,
title: %{text: "Speed", font: %{size: 24}},
delta: %{reference: 400, increasing: %{color: "RebeccaPurple"}},
number: %{suffix: " km/h", font: %{size: 20}},
gauge: %{
axis: %{range: [nil, 500], tickwidth: 1, tickcolor: "darkblue"},
bar: %{color: "darkblue"},
bgcolor: "white",
borderwidth: 2,
bordercolor: "gray",
steps: [
%{range: [0, 250], color: "cyan"},
%{range: [250, 400], color: "royalblue"}
],
threshold: %{
line: %{color: "red", width: 4},
thickness: 0.75,
value: 490
}
},
domain: %{x: [0, 1], y: [0, 1]}
)
)
|> Figure.update_layout(
width: 600,
height: 400,
paper_bgcolor: "lavender",
font: %{color: "darkblue", family: "Arial"}
)
|> Plotly.show()
Financial Charts > Basic Bullet Charts
A bullet chart uses gauge: %{shape: "bullet"} on an Indicator trace. It renders as a
compact horizontal bar, useful for dashboards. mode: "number+gauge+delta" displays the
numeric value, bullet bar, and delta from the reference.
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "number+gauge+delta",
value: 220,
title: %{text: "Profit"},
delta: %{reference: 200},
gauge: %{shape: "bullet", axis: %{range: [nil, 300]}},
domain: %{x: [0, 1], y: [0, 1]}
)
)
|> Figure.update_layout(width: 600, height: 250)
|> Plotly.show()
Financial Charts > Add Steps and Threshold
gauge.steps: and gauge.threshold: work on bullet charts exactly as on angular gauges.
Steps add colored bands along the bullet bar; the threshold marker shows a target value.
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "number+gauge+delta",
value: 220,
title: %{text: "Profit"},
delta: %{reference: 200},
gauge: %{
shape: "bullet",
axis: %{range: [nil, 300]},
steps: [
%{range: [0, 150], color: "lightgray"},
%{range: [150, 250], color: "gray"}
],
threshold: %{
line: %{color: "red", width: 2},
thickness: 0.75,
value: 280
}
},
domain: %{x: [0, 1], y: [0, 1]}
)
)
|> Figure.update_layout(width: 600, height: 250)
|> Plotly.show()
Financial Charts > Custom Bullet Chart
Full bullet styling: gauge.bar: %{color:, thickness:} styles the value bar. gauge.bgcolor:
sets the bullet background. delta.relative: true shows percentage change from reference.
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "number+gauge+delta",
value: 220,
title: %{text: "Revenue"},
delta: %{reference: 200, relative: true, position: "top"},
number: %{suffix: "$", font: %{size: 20}},
gauge: %{
shape: "bullet",
axis: %{range: [nil, 300]},
bar: %{color: "darkblue", thickness: 0.2},
bgcolor: "white",
borderwidth: 2,
bordercolor: "gray",
steps: [
%{range: [0, 150], color: "cyan"},
%{range: [150, 250], color: "royalblue"}
],
threshold: %{
line: %{color: "red", width: 2},
thickness: 0.75,
value: 280
}
},
domain: %{x: [0, 1], y: [0, 1]}
)
)
|> Figure.update_layout(
width: 600,
height: 250,
paper_bgcolor: "lavender"
)
|> Plotly.show()
Financial Charts > Multi Bullet
Multiple bullet indicators are placed by assigning each trace a domain: %{x: [0,1], y: [lo, hi]}
to split the vertical space. Alternatively use domain: %{row:, column:} with layout.grid.
Here we use explicit y domains to stack three bullets vertically.
alias Plotly.{Figure, Indicator}
Figure.new()
|> Figure.add_trace(
Indicator.new(
mode: "number+gauge+delta",
value: 180,
title: %{text: "Revenue"},
delta: %{reference: 200},
gauge: %{
shape: "bullet",
axis: %{range: [nil, 300]},
steps: [%{range: [0, 150], color: "lightgray"}, %{range: [150, 250], color: "gray"}],
threshold: %{line: %{color: "red", width: 2}, thickness: 0.75, value: 280}
},
domain: %{x: [0.25, 1], y: [0.67, 1]}
)
)
|> Figure.add_trace(
Indicator.new(
mode: "number+gauge+delta",
value: 35,
title: %{text: "Profit"},
delta: %{reference: 30},
gauge: %{
shape: "bullet",
axis: %{range: [nil, 50]},
steps: [%{range: [0, 25], color: "lightgray"}, %{range: [25, 40], color: "gray"}],
threshold: %{line: %{color: "red", width: 2}, thickness: 0.75, value: 45}
},
domain: %{x: [0.25, 1], y: [0.33, 0.66]}
)
)
|> Figure.add_trace(
Indicator.new(
mode: "number+gauge+delta",
value: 78,
title: %{text: "Satisfaction"},
delta: %{reference: 80},
gauge: %{
shape: "bullet",
axis: %{range: [nil, 100]},
steps: [%{range: [0, 50], color: "lightgray"}, %{range: [50, 75], color: "gray"}],
threshold: %{line: %{color: "red", width: 2}, thickness: 0.75, value: 90}
},
domain: %{x: [0.25, 1], y: [0, 0.32]}
)
)
|> Figure.update_layout(
title: "KPI Dashboard",
width: 700,
height: 500,
margin: %{l: 120}
)
|> Plotly.show()