optionstratlib_simulation/simulation/steps/
y.rs1use crate::error::SimulationError;
8use optionstratlib_core::model::Positive;
9use serde::{Serialize, Serializer};
10use std::convert::TryInto;
11use std::fmt::{Display, Formatter};
12
13#[derive(Debug, Clone)]
35pub struct Ystep<T>
36where
37 T: TryInto<Positive> + Display + Clone,
38{
39 index: i32,
41
42 value: T,
44}
45
46impl<T> Ystep<T>
71where
72 T: TryInto<Positive> + Display + Clone,
73{
74 pub fn new(index: i32, value: T) -> Self {
86 Self { index, value }
87 }
88
89 pub fn next(&self, value: T) -> Result<Self, SimulationError> {
108 let index = self.index.checked_add(1).ok_or_else(|| {
109 SimulationError::step_error("step index overflowed advancing the walk")
110 })?;
111 Ok(Self { index, value })
112 }
113
114 pub fn value(&self) -> &T {
120 &self.value
121 }
122
123 pub fn positive(&self) -> Result<Positive, SimulationError> {
135 self.value
136 .clone()
137 .try_into()
138 .map_err(|_| SimulationError::step_error("Failed to convert value to Positive"))
139 }
140
141 pub fn index(&self) -> &i32 {
150 &self.index
151 }
152
153 pub fn value_mut(&mut self) -> &mut T {
161 &mut self.value
162 }
163}
164
165impl<T> Display for Ystep<T>
166where
167 T: TryInto<Positive> + Display + Clone,
168{
169 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
170 let positive_value: Positive = self.positive().unwrap_or(Positive::ZERO);
171 write!(
172 f,
173 "Ystep {{ index: {}, value: {} }}",
174 self.index,
175 positive_value.to_dec().round_dp(3).normalize()
178 )
179 }
180}
181
182impl<T> Serialize for Ystep<T>
183where
184 T: TryInto<Positive> + Display + Serialize + Clone,
185{
186 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
187 where
188 S: Serializer,
189 {
190 let value: Positive = self.positive().unwrap_or(Positive::ZERO);
192
193 use serde::ser::SerializeStruct;
195 let mut state = serializer.serialize_struct("Ystep", 2)?;
196 state.serialize_field("index", &self.index)?;
197 state.serialize_field("value", &value)?;
198 state.end()
199 }
200}
201
202impl<T> TryFrom<Ystep<T>> for Positive
203where
204 T: TryInto<Positive> + Display + Clone,
205{
206 type Error = SimulationError;
207
208 fn try_from(step: Ystep<T>) -> Result<Self, Self::Error> {
209 step.positive()
210 }
211}
212
213#[cfg(test)]
214mod tests_ystep {
215 use super::*;
216
217 #[test]
218 fn test_ystep_new() {
219 let value = 42.5;
221 let step = Ystep::new(0, value);
222
223 assert_eq!(step.index, 0);
224 assert_eq!(step.value, 42.5);
225 }
226}
227
228#[cfg(test)]
229mod tests_serialize {
230 use super::*;
231 use optionstratlib_core::pos_or_panic;
232
233 use rust_decimal_macros::dec;
234 use serde_json::{Value, json};
235
236 #[test]
237 fn test_basic_serialization() {
238 let step = Ystep::new(5, 10.5f64);
239 let serialized = serde_json::to_string(&step).unwrap();
240 let parsed: Value = serde_json::from_str(&serialized).unwrap();
241
242 assert_eq!(parsed["index"], 5);
243 assert_eq!(parsed["value"], "10.5");
244 }
245
246 #[test]
247 fn test_serialized_structure() {
248 let step = Ystep::new(42, 15.75f64);
249 let serialized = serde_json::to_string(&step).unwrap();
250 let parsed: Value = serde_json::from_str(&serialized).unwrap();
251
252 assert!(parsed.is_object());
254 assert_eq!(parsed.as_object().unwrap().len(), 2);
255 assert!(parsed.get("index").unwrap().is_i64());
256 assert!(parsed.get("value").unwrap().is_string());
257
258 assert_eq!(parsed["index"], json!(42));
260 assert_eq!(parsed["value"], json!("15.75"));
261 }
262
263 #[test]
264 fn test_type_conversion() {
265 let step_f64 = Ystep::new(1, 2.5f64);
267 let step_decimal = Ystep::new(1, dec!(2.5));
268 let step_positive = Ystep::new(1, pos_or_panic!(2.5));
269
270 let json_f64 = serde_json::to_string(&step_f64).unwrap();
272 let json_decimal = serde_json::to_string(&step_decimal).unwrap();
273 let json_positive = serde_json::to_string(&step_positive).unwrap();
274
275 let parsed_f64: Value = serde_json::from_str(&json_f64).unwrap();
277 let parsed_decimal: Value = serde_json::from_str(&json_decimal).unwrap();
278 let parsed_positive: Value = serde_json::from_str(&json_positive).unwrap();
279
280 assert_eq!(parsed_f64["value"], json!("2.5"));
282 assert_eq!(parsed_decimal["value"], json!("2.5"));
283 assert_eq!(parsed_positive["value"], json!("2.5"));
284 }
285
286 #[test]
287 fn test_json_format_identity() {
288 let step_f64 = Ystep::new(3, 4.01f64);
290 let step_decimal = Ystep::new(3, dec!(4.01));
291 let step_positive = Ystep::new(3, pos_or_panic!(4.01));
292
293 let json_f64 = serde_json::to_string(&step_f64).unwrap();
295 let json_decimal = serde_json::to_string(&step_decimal).unwrap();
296 let json_positive = serde_json::to_string(&step_positive).unwrap();
297
298 assert_eq!(json_f64, json_decimal);
300 assert_eq!(json_decimal, json_positive);
301 }
302
303 #[test]
304 fn test_edge_cases() {
305 let step_zero = Ystep::new(0, 0.1f64);
307 let json_zero = serde_json::to_string(&step_zero).unwrap();
308 let parsed_zero: Value = serde_json::from_str(&json_zero).unwrap();
309 assert_eq!(parsed_zero["value"], json!("0.1"));
310
311 let step_small = Ystep::new(1, 0.000001f64);
313 let json_small = serde_json::to_string(&step_small).unwrap();
314 let parsed_small: Value = serde_json::from_str(&json_small).unwrap();
315 assert!(
316 parsed_small["value"]
317 .as_str()
318 .unwrap()
319 .parse::<f64>()
320 .unwrap()
321 > 0.0
322 );
323 assert!(
324 parsed_small["value"]
325 .as_str()
326 .unwrap()
327 .parse::<f64>()
328 .unwrap()
329 < 0.0001
330 );
331
332 let step_large = Ystep::new(2, 1_000_000.01f64);
334 let json_large = serde_json::to_string(&step_large).unwrap();
335 let parsed_large: Value = serde_json::from_str(&json_large).unwrap();
336 assert_eq!(parsed_large["value"], json!("1000000.01"));
337 }
338
339 #[test]
340 fn test_decimal_precision() {
341 let step = Ystep::new(1, 1.23456789f64);
342 let serialized = serde_json::to_string(&step).unwrap();
343 let parsed: Value = serde_json::from_str(&serialized).unwrap();
344
345 let value = parsed["value"].as_str().unwrap().parse::<f64>().unwrap();
347 assert!((value - 1.23456789).abs() < 0.0000001);
348 }
349
350 #[test]
351 fn test_next_serialization() {
352 let step = Ystep::new(1, 5.0f64);
353 let next_step = step.next(10.0f64).unwrap();
354
355 let serialized = serde_json::to_string(&next_step).unwrap();
356 let parsed: Value = serde_json::from_str(&serialized).unwrap();
357
358 assert_eq!(parsed["index"], 2);
360 assert_eq!(parsed["value"], "10");
361 }
362
363 #[test]
364 fn test_pretty_serialization() {
365 let step = Ystep::new(7, 15.25f64);
366 let serialized = serde_json::to_string_pretty(&step).unwrap();
367
368 assert!(serialized.contains("\n"));
370 assert!(serialized.contains(" "));
371
372 let parsed: Value = serde_json::from_str(&serialized).unwrap();
374 assert_eq!(parsed["index"], 7);
375 assert_eq!(parsed["value"], "15.25");
376 }
377}
378
379#[cfg(test)]
382mod tests_next_panic_freedom {
383 use super::*;
384
385 #[test]
386 fn test_next_at_max_index_returns_error() {
387 let step = Ystep::new(i32::MAX, 1.0f64);
388 assert!(matches!(
389 step.next(2.0f64),
390 Err(SimulationError::StepError { .. })
391 ));
392 }
393
394 #[test]
395 fn test_next_increments_index() -> Result<(), SimulationError> {
396 let step = Ystep::new(i32::MAX - 1, 1.0f64).next(2.0f64)?;
397 assert_eq!(*step.index(), i32::MAX);
398 Ok(())
399 }
400}