1use plotly_derive::FieldSetter;
4use serde::Serialize;
5
6use crate::{
7 color::NamedColor,
8 common::{
9 Calendar, Dim, Direction, HoverInfo, Label, LegendGroupTitle, Line, PlotType, Visible,
10 },
11 Trace,
12};
13
14#[serde_with::skip_serializing_none]
47#[derive(Serialize, Debug, Clone, FieldSetter)]
48pub struct Candlestick<T, O>
49where
50 T: Serialize + Clone,
51 O: Serialize + Clone,
52{
53 #[field_setter(default = "PlotType::Candlestick")]
54 r#type: PlotType,
55 x: Option<Vec<T>>,
56 open: Option<Vec<O>>,
57 high: Option<Vec<O>>,
58 low: Option<Vec<O>>,
59 close: Option<Vec<O>>,
60 name: Option<String>,
61 visible: Option<Visible>,
62 #[serde(rename = "showlegend")]
63 show_legend: Option<bool>,
64 #[serde(rename = "legendgroup")]
65 legend_group: Option<String>,
66 #[serde(rename = "legendgrouptitle")]
67 legend_group_title: Option<LegendGroupTitle>,
68 opacity: Option<f64>,
69 text: Option<Dim<String>>,
70 #[serde(rename = "hovertext")]
71 hover_text: Option<Dim<String>>,
72 #[serde(rename = "hoverinfo")]
73 hover_info: Option<HoverInfo>,
74 #[serde(rename = "xaxis")]
75 x_axis: Option<String>,
76 #[serde(rename = "yaxis")]
77 y_axis: Option<String>,
78 line: Option<Line>,
79 #[serde(rename = "whiskerwidth")]
80 whisker_width: Option<f64>,
81 increasing: Option<Direction>,
82 decreasing: Option<Direction>,
83 #[serde(rename = "hoverlabel")]
84 hover_label: Option<Label>,
85 #[serde(rename = "xcalendar")]
86 x_calendar: Option<Calendar>,
87}
88
89impl<T, O> Candlestick<T, O>
90where
91 T: Serialize + Clone,
92 O: Serialize + Clone,
93{
94 pub fn new(x: Vec<T>, open: Vec<O>, high: Vec<O>, low: Vec<O>, close: Vec<O>) -> Box<Self> {
95 let iline = Line::new().width(1.0).color(NamedColor::Green);
96 let dline = Line::new().width(1.0).color(NamedColor::Red);
97 Box::new(Candlestick {
98 x: Some(x),
99 open: Some(open),
100 high: Some(high),
101 low: Some(low),
102 close: Some(close),
103 increasing: Some(Direction::Increasing { line: iline }),
104 decreasing: Some(Direction::Decreasing { line: dline }),
105 ..Default::default()
106 })
107 }
108}
109
110impl<X, Y> Trace for Candlestick<X, Y>
111where
112 X: Serialize + Clone,
113 Y: Serialize + Clone,
114{
115 fn to_json(&self) -> String {
116 serde_json::to_string(self).unwrap()
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use serde_json::{json, to_value};
123
124 use super::*;
125
126 #[test]
127 fn test_default_candlestick() {
128 let trace: Candlestick<i32, i32> = Candlestick::default();
129 let expected = json!({"type": "candlestick"}).to_string();
130
131 assert_eq!(trace.to_json(), expected);
132 }
133
134 #[test]
135 fn test_serialize_candlestick() {
136 let trace = Candlestick::new(
137 vec!["2020-05-20", "2020-05-21"],
138 vec![5, 6],
139 vec![9, 11],
140 vec![2, 3],
141 vec![6, 5],
142 )
143 .name("candlestick_trace")
144 .visible(Visible::True)
145 .show_legend(false)
146 .legend_group("group_1")
147 .legend_group_title(LegendGroupTitle::new("Legend Group Title"))
148 .opacity(0.3)
149 .text_array(vec!["text", "here"])
150 .text("text here")
151 .hover_text_array(vec!["hover", "text"])
152 .hover_text("hover text")
153 .hover_info(HoverInfo::Skip)
154 .x_axis("x1")
155 .y_axis("y1")
156 .line(Line::new())
157 .whisker_width(0.4)
158 .increasing(Direction::Increasing { line: Line::new() })
159 .decreasing(Direction::Decreasing { line: Line::new() })
160 .hover_label(Label::new())
161 .x_calendar(Calendar::DiscWorld);
162
163 let expected = json!({
164 "type": "candlestick",
165 "x": ["2020-05-20", "2020-05-21"],
166 "open": [5, 6],
167 "high": [9, 11],
168 "low": [2, 3],
169 "close": [6, 5],
170 "name": "candlestick_trace",
171 "visible": true,
172 "showlegend": false,
173 "legendgroup": "group_1",
174 "legendgrouptitle": {"text": "Legend Group Title"},
175 "opacity": 0.3,
176 "text": "text here",
177 "hovertext": "hover text",
178 "hoverinfo": "skip",
179 "xaxis": "x1",
180 "yaxis": "y1",
181 "line": {},
182 "whiskerwidth": 0.4,
183 "increasing": {"line": {}},
184 "decreasing": {"line": {}},
185 "hoverlabel": {},
186 "xcalendar": "discworld"
187 });
188
189 assert_eq!(to_value(trace).unwrap(), expected);
190 }
191}