optionstratlib 0.17.0

OptionStratLib is a comprehensive Rust library for options trading and strategy development across multiple asset classes.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
/******************************************************************************
    Author: Joaquín Béjar García
    Email: jb@taunais.com
    Date: 24/12/25
******************************************************************************/

//! # Strategy Error Module
//!
//! This module provides error handling for option trading strategies and their operations.
//! It defines error types for strategy calculations, validations, and operations,
//! with integration with the probability analysis system.
//!
//! ## Error Types
//!
//! ### Strategy Error (`StrategyError`)
//! The main error enum with four categories:
//! * `PriceError` - For price calculation failures
//! * `BreakEvenError` - For break-even point calculation errors
//! * `ProfitLossError` - For profit/loss calculation failures
//! * `OperationError` - For strategy operation errors
//!
//! ### Price Errors (`PriceErrorKind`)
//! Specific errors for price-related operations:
//! * Invalid underlying prices
//! * Invalid price ranges with start and end points
//!
//! ### Break-Even Errors (`BreakEvenErrorKind`)
//! Handles break-even point calculations:
//! * Calculation failures
//! * Missing break-even points
//!
//! ### Profit/Loss Errors (`ProfitLossErrorKind`)
//! Manages profit and loss calculations:
//! * Maximum profit calculation errors
//! * Maximum loss calculation errors
//! * Profit range calculation errors
//!
//! ## Integration with Probability Analysis
//!
//! Implements conversion from `StrategyError` to `ProbabilityError` for seamless
//! error handling between strategy and probability calculations.
//!
//! ## Usage Example
//!
//! ```rust
//!
//! use optionstratlib::error::strategies::{StrategyError, StrategyResult};
//!
//! fn validate_strategy_operation(operation: &str, strategy: &str) -> StrategyResult<()> {
//!     if !is_supported_operation(operation) {
//!         return Err(StrategyError::operation_not_supported(operation, strategy));
//!     }
//!     Ok(())
//! }
//!
//! fn is_supported_operation(p0: &str) -> bool  {
//!     false
//! }
//! ```
//!
//! ## Helper Methods
//!
//! The module provides convenient methods for creating common errors:
//! * `operation_not_supported` - Creates an error for unsupported operations
//! * `invalid_parameters` - Creates an error for invalid operation parameters
//!
//! ## Type Alias
//!
//! Provides `StrategyResult<T>` for convenient error handling in strategy operations.
use crate::error::common::OperationErrorKind;
use crate::error::{GreeksError, OptionsError, PositionError, TradeError};
use thiserror::Error;

/// Represents the different types of errors that can occur in options trading strategies.
///
/// This enum acts as a comprehensive error type for the options strategy module,
/// grouping more specific error categories for better error handling. Each variant
/// corresponds to a different domain of potential failures within options strategy
/// operations.
///
/// # Variants
///
/// * `PriceError` - Errors related to pricing operations such as invalid prices or ranges
/// * `BreakEvenError` - Errors encountered when calculating strategy break-even points
/// * `ProfitLossError` - Errors related to profit/loss calculations including maximum values
/// * `OperationError` - General strategy operation errors including unsupported operations
/// * `NotImplemented` - For features or operations that are not yet implemented
/// * `Simulation` - Simulation-layer errors surfaced while evaluating a strategy
///
/// # Usage
///
/// This error type is designed to be returned from functions that perform operations
/// on options trading strategies, providing structured and detailed error information
/// to facilitate debugging and error handling.
#[derive(Error, Debug)]
pub enum StrategyError {
    /// Errors related to price calculations
    #[error("Price error: {0}")]
    PriceError(PriceErrorKind),

    /// Errors related to break-even points
    #[error("Break-even error: {0}")]
    BreakEvenError(BreakEvenErrorKind),

    /// Errors related to profit/loss calculations
    #[error("Profit/loss error: {0}")]
    ProfitLossError(ProfitLossErrorKind),

    /// Errors related to strategy operations
    #[error("Operation error: {0}")]
    OperationError(OperationErrorKind),

    /// Indicates a feature or operation that has not been implemented yet
    #[error("Not implemented")]
    NotImplemented,

    /// A simulation-layer error surfaced while evaluating a strategy.
    #[error(transparent)]
    Simulation(Box<crate::error::SimulationError>),

    /// Greeks errors
    #[error(transparent)]
    GreeksError(#[from] GreeksError),

    /// Positive value errors
    #[error(transparent)]
    PositiveError(#[from] positive::PositiveError),

    /// Numeric conversion failed at the `f64` ↔ `Decimal` boundary.
    ///
    /// Raised when a non-finite (`NaN` / `±Inf`) `f64` cannot be represented
    /// as a `Decimal`, or when a representation overflow occurs.
    #[error("numeric conversion failed: {value} is not a finite Decimal")]
    NumericConversion {
        /// The offending `f64` value that could not be converted.
        value: f64,
    },

    /// A required greek value was missing from an option.
    ///
    /// Raised when a strategy expects a greek (delta, gamma, vega, theta,
    /// rho) to be present on an option (e.g., for delta-neutral validation)
    /// but the option's greek calculation returned `None`.
    #[error("missing greek `{name}`: option not initialized for greek calculation")]
    MissingGreek {
        /// Name of the greek (`"delta"`, `"gamma"`, `"vega"`, `"theta"`, `"rho"`).
        name: &'static str,
    },

    /// A required collection was empty when at least one element was expected.
    ///
    /// Raised when a strategy operation needs to pick an element out of a
    /// collection (option chain, break-even points, profit ranges) and the
    /// collection turned out to be empty.
    #[error("empty collection in strategy operation: {context}")]
    EmptyCollection {
        /// Description of where the empty collection was encountered.
        context: String,
    },
}

/// Represents different types of errors that can occur during price-related operations.
///
/// This enum provides specific error variants for price calculations, validations,
/// and operations. Each variant contains detailed information about the error context
/// to facilitate debugging and error handling in pricing operations.
///
#[derive(Error, Debug)]
pub enum PriceErrorKind {
    /// Error when underlying price is not available or invalid
    ///
    /// This error occurs when the price of the underlying asset cannot be determined
    /// or is considered invalid for calculations (e.g., negative values, NaN, etc.)
    ///
    /// # Fields
    ///
    #[error("Invalid underlying price: {reason}")]
    InvalidUnderlyingPrice {
        /// * `reason` - A detailed explanation of why the price is considered invalid
        reason: String,
    },

    /// Error in price range calculation
    ///
    /// This error occurs when a price range specification is invalid, such as when
    /// the end price is less than the start price, or when price points are outside
    /// of valid bounds.
    ///
    #[error("Invalid price range ({start} to {end}): {reason}")]
    InvalidPriceRange {
        /// * `start` - The beginning price of the attempted range
        start: f64,
        /// * `end` - The ending price of the attempted range
        end: f64,
        /// * `reason` - A detailed explanation of why the price range is invalid
        reason: String,
    },
}

/// Represents the type of errors that can occur during break-even point calculations.
///
/// Break-even points are critical price levels at which an options strategy neither generates
/// profit nor loss. This enum categorizes the various errors that might occur when attempting
/// to calculate these points, providing clear error handling paths for client code.
///
/// # Variants
///
/// * `CalculationError` - Indicates a failure in the mathematical calculation process
///   for break-even points, including the specific reason for the failure.
///
/// * `NoBreakEvenPoints` - Indicates that no break-even points exist for the given strategy.
///   This may occur with certain strategies that are always profitable or always unprofitable
///   across all possible price ranges.
///
/// # Examples
///
/// ```
/// use optionstratlib::error::strategies::BreakEvenErrorKind;
///
/// // When a calculation fails due to invalid input
/// let error = BreakEvenErrorKind::CalculationError {
///     reason: String::from("Division by zero when calculating ratio")
/// };
///
/// // When a strategy has no break-even points
/// let no_points_error = BreakEvenErrorKind::NoBreakEvenPoints;
/// ```
///
/// # Related Errors
///
/// This error type is typically used within the context of `StrategyError` to provide
/// detailed information about failures in strategy analysis, particularly when evaluating
/// profit/loss scenarios at different price points.
#[derive(Error, Debug)]
pub enum BreakEvenErrorKind {
    /// Error that occurs when the break-even calculation process fails
    ///
    /// The `reason` field provides specific details about why the calculation failed,
    /// which can be useful for debugging or providing user feedback.
    #[error("Break-even calculation error: {reason}")]
    CalculationError {
        /// Detailed explanation of what caused the calculation to fail
        reason: String,
    },

    /// Error indicating that no break-even points exist for the given strategy
    ///
    /// This typically occurs with strategies that maintain a consistent profit or loss
    /// profile regardless of the underlying asset's price.
    #[error("No break-even points exist for this strategy")]
    NoBreakEvenPoints,
}

/// Represents error types that can occur during profit and loss calculations.
///
/// This enum provides structured error information for various failure scenarios
/// that may occur when calculating profit, loss, and related metrics for options
/// strategies or financial instruments.
///
/// # Error Categories
///
/// The enum categorizes errors into three main types:
/// * Maximum profit calculation errors
/// * Maximum loss calculation errors
/// * Profit range and breakeven calculation errors
///
/// Each variant includes a detailed reason string to help diagnose the specific
/// cause of the error and facilitate troubleshooting.
#[derive(Error, Debug)]
pub enum ProfitLossErrorKind {
    /// Error that occurs when calculating maximum profit.
    ///
    /// This might happen due to issues such as invalid input parameters,
    /// computational limitations, or logical inconsistencies in the profit model.
    #[error("Maximum profit calculation error: {reason}")]
    MaxProfitError {
        /// Detailed explanation of why the maximum profit calculation failed
        reason: String,
    },

    /// Error that occurs when calculating maximum loss.
    ///
    /// This might happen due to issues such as invalid input parameters,
    /// computational limitations, or logical inconsistencies in the loss model.
    #[error("Maximum loss calculation error: {reason}")]
    MaxLossError {
        /// Detailed explanation of why the maximum loss calculation failed
        reason: String,
    },

    /// Error that occurs during profit range calculation.
    ///
    /// This might happen when trying to determine profit/loss at different price
    /// points, breakeven points, or when analyzing the profit curve of a strategy.
    #[error("Profit range calculation error: {reason}")]
    ProfitRangeError {
        /// Detailed explanation of why the profit range calculation failed
        reason: String,
    },
}

/// A specialized result type for strategy operations.
///
/// This type alias provides a convenient way to handle results from strategy-related
/// operations that might fail with a `StrategyError`. It follows the standard Rust
/// pattern of using `Result<T, E>` for operations that can fail.
///
/// This makes error handling more readable and concise throughout the strategy-related
/// code, compared to explicitly writing `Result<T, StrategyError>` everywhere.
pub type StrategyResult<T> = Result<T, StrategyError>;

// Implementation helpers
impl StrategyError {
    /// Creates a `StrategyError` for an unsupported operation on a specific strategy type.
    ///
    /// This helper method creates a structured error describing an operation that cannot be performed
    /// on a particular strategy type, encapsulating both the attempted operation name and the strategy
    /// type that doesn't support it.
    ///
    /// # Parameters
    /// * `operation` - The name of the operation that was attempted but is not supported
    /// * `strategy_type` - The type of strategy for which the operation is not supported
    ///
    /// # Returns
    /// A `StrategyError::OperationError` variant with the `NotSupported` kind
    ///
    /// # Example
    /// ```
    /// use optionstratlib::error::strategies::StrategyError;
    ///
    /// // Creating an error when trying to calculate butterfly spread adjustment on an iron condor
    /// let error = StrategyError::operation_not_supported("butterfly_adjustment", "IronCondor");
    /// ```
    #[must_use]
    #[cold]
    #[inline(never)]
    pub fn operation_not_supported(operation: &str, strategy_type: &str) -> Self {
        StrategyError::OperationError(OperationErrorKind::NotSupported {
            operation: operation.to_string(),
            reason: strategy_type.to_string(),
        })
    }

    /// Creates a `StrategyError` for invalid parameters provided to an operation.
    ///
    /// This helper method builds a structured error for cases when an operation fails due to
    /// invalid or insufficient parameters, providing context about both the operation and
    /// the specific validation issue.
    ///
    /// # Parameters
    /// * `operation` - The name of the operation that failed due to invalid parameters
    /// * `reason` - A descriptive explanation of why the parameters are invalid
    ///
    /// # Returns
    /// A `StrategyError::OperationError` variant with the `InvalidParameters` kind
    ///
    /// # Example
    /// ```
    /// use optionstratlib::error::strategies::StrategyError;
    ///
    /// // Creating an error when strike prices are invalid for a strategy
    /// let error = StrategyError::invalid_parameters(
    ///     "create_vertical_spread",
    ///     "Short strike must be higher than long strike for call spreads"
    /// );
    /// ```
    #[must_use]
    #[cold]
    #[inline(never)]
    pub fn invalid_parameters(operation: &str, reason: &str) -> Self {
        StrategyError::OperationError(OperationErrorKind::InvalidParameters {
            operation: operation.to_string(),
            reason: reason.to_string(),
        })
    }

    /// Builds a `NumericConversion` error for an `f64` that could not be
    /// converted to a `Decimal` (typically `NaN` / `Inf` or overflow).
    ///
    /// # Errors
    ///
    /// This is an error constructor — it always returns the variant.
    #[cold]
    #[inline(never)]
    #[must_use]
    pub fn numeric_conversion(value: f64) -> Self {
        StrategyError::NumericConversion { value }
    }

    /// Builds a `MissingGreek` error for an option missing a required greek.
    ///
    /// # Errors
    ///
    /// This is an error constructor — it always returns the variant.
    #[cold]
    #[inline(never)]
    #[must_use]
    pub fn missing_greek(name: &'static str) -> Self {
        StrategyError::MissingGreek { name }
    }

    /// Builds an `EmptyCollection` error for an unexpectedly empty collection.
    ///
    /// # Errors
    ///
    /// This is an error constructor — it always returns the variant.
    #[cold]
    #[inline(never)]
    #[must_use]
    pub fn empty_collection(context: impl Into<String>) -> Self {
        StrategyError::EmptyCollection {
            context: context.into(),
        }
    }
}

impl From<PositionError> for StrategyError {
    fn from(err: PositionError) -> Self {
        StrategyError::OperationError(OperationErrorKind::InvalidParameters {
            operation: "Position".to_string(),
            reason: err.to_string(),
        })
    }
}

impl From<OptionsError> for StrategyError {
    fn from(err: OptionsError) -> Self {
        StrategyError::OperationError(OperationErrorKind::InvalidParameters {
            operation: "Options".to_string(),
            reason: err.to_string(),
        })
    }
}

impl From<crate::error::SimulationError> for StrategyError {
    #[inline]
    fn from(err: crate::error::SimulationError) -> Self {
        StrategyError::Simulation(Box::new(err))
    }
}

impl From<crate::error::PricingError> for StrategyError {
    fn from(err: crate::error::PricingError) -> Self {
        StrategyError::OperationError(OperationErrorKind::InvalidParameters {
            operation: "Pricing".to_string(),
            reason: err.to_string(),
        })
    }
}

impl From<TradeError> for StrategyError {
    fn from(value: TradeError) -> Self {
        StrategyError::OperationError(OperationErrorKind::InvalidParameters {
            operation: "Trade".to_string(),
            reason: value.to_string(),
        })
    }
}

#[cfg(test)]
mod tests_from_str {
    use super::*;
    use crate::error::ProbabilityError;

    #[test]
    fn test_strategy_to_probability_error_conversion() {
        let strategy_error = StrategyError::operation_not_supported("max_profit", "TestStrategy");
        let probability_error = ProbabilityError::from(strategy_error);

        assert!(probability_error.to_string().contains("max_profit"));
        assert!(probability_error.to_string().contains("TestStrategy"));
    }

    #[test]
    fn test_profit_loss_error_conversion() {
        let strategy_error = StrategyError::ProfitLossError(ProfitLossErrorKind::MaxProfitError {
            reason: "Test error".to_string(),
        });
        let probability_error = ProbabilityError::from(strategy_error);

        assert!(probability_error.to_string().contains("Test error"));
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_strategy_error_creation() {
        let error = StrategyError::operation_not_supported("max_profit", "TestStrategy");
        assert!(matches!(
            error,
            StrategyError::OperationError(OperationErrorKind::NotSupported { .. })
        ));
    }

    #[test]
    fn test_error_messages() {
        let error = StrategyError::operation_not_supported("max_profit", "TestStrategy");
        let error_string = error.to_string();
        assert!(error_string.contains("max_profit"));
        assert!(error_string.contains("TestStrategy"));
    }
}

#[cfg(test)]
mod tests_panic_free_variants {
    use super::*;

    #[test]
    fn test_numeric_conversion_constructor_and_display() {
        let err = StrategyError::numeric_conversion(f64::NAN);
        assert!(matches!(err, StrategyError::NumericConversion { .. }));
        assert!(err.to_string().contains("NaN"));
    }

    #[test]
    fn test_numeric_conversion_inf_message_includes_value() {
        let err = StrategyError::numeric_conversion(f64::INFINITY);
        assert!(err.to_string().contains("inf"));
    }

    #[test]
    fn test_missing_greek_constructor() {
        let err = StrategyError::missing_greek("delta");
        match err {
            StrategyError::MissingGreek { name } => assert_eq!(name, "delta"),
            other => panic!("expected MissingGreek, got {other:?}"),
        }
    }

    #[test]
    fn test_missing_greek_display() {
        let err = StrategyError::missing_greek("vega");
        assert!(err.to_string().contains("missing greek `vega`"));
    }

    #[test]
    fn test_empty_collection_constructor_accepts_str_and_string() {
        let from_str = StrategyError::empty_collection("option chain");
        let from_string = StrategyError::empty_collection(String::from("break-even points"));
        assert!(matches!(from_str, StrategyError::EmptyCollection { .. }));
        assert!(matches!(from_string, StrategyError::EmptyCollection { .. }));
        assert!(from_str.to_string().contains("option chain"));
        assert!(from_string.to_string().contains("break-even points"));
    }

    #[test]
    fn test_panic_free_variants_route_through_probability_error() {
        use crate::error::ProbabilityError;
        let nc = ProbabilityError::from(StrategyError::numeric_conversion(f64::NAN));
        let mg = ProbabilityError::from(StrategyError::missing_greek("delta"));
        let ec = ProbabilityError::from(StrategyError::empty_collection("chain"));
        assert!(nc.to_string().contains("numeric conversion"));
        assert!(mg.to_string().contains("missing greek"));
        assert!(ec.to_string().contains("empty collection"));
    }
}

#[cfg(test)]
mod tests_display {
    use super::*;

    #[test]
    fn test_price_error_display() {
        let error = StrategyError::PriceError(PriceErrorKind::InvalidUnderlyingPrice {
            reason: "Price cannot be negative".to_string(),
        });
        assert!(error.to_string().contains("Price cannot be negative"));
    }

    #[test]
    fn test_break_even_error_display() {
        let error = StrategyError::BreakEvenError(BreakEvenErrorKind::CalculationError {
            reason: "Invalid input parameters".to_string(),
        });
        assert!(error.to_string().contains("Invalid input parameters"));
    }

    #[test]
    fn test_profit_loss_error_display() {
        let error = StrategyError::ProfitLossError(ProfitLossErrorKind::MaxProfitError {
            reason: "Cannot calculate maximum profit".to_string(),
        });
        assert!(
            error
                .to_string()
                .contains("Cannot calculate maximum profit")
        );
    }

    #[test]
    fn test_operation_error_display() {
        let error = StrategyError::operation_not_supported("max_profit", "TestStrategy");
        assert!(error.to_string().contains("max_profit"));
        assert!(error.to_string().contains("TestStrategy"));
    }
}

#[cfg(test)]
mod tests_extended {
    use super::*;

    #[test]
    fn test_strategy_error_simulation() {
        let sim_error = crate::error::SimulationError::walk_error("simulation failure");
        let error = StrategyError::from(sim_error);
        assert!(matches!(error, StrategyError::Simulation(_)));
    }

    #[test]
    fn test_price_error_invalid_price_range() {
        let error = PriceErrorKind::InvalidPriceRange {
            start: 10.0,
            end: 50.0,
            reason: "Start price must be less than end price".to_string(),
        };
        assert_eq!(
            format!("{error}"),
            "Invalid price range (10 to 50): Start price must be less than end price"
        );
    }

    #[test]
    fn test_break_even_error_no_points() {
        let error = BreakEvenErrorKind::NoBreakEvenPoints;
        assert_eq!(
            format!("{error}"),
            "No break-even points exist for this strategy"
        );
    }

    #[test]
    fn test_profit_loss_error_max_loss_error() {
        let error = ProfitLossErrorKind::MaxLossError {
            reason: "Loss exceeds margin requirements".to_string(),
        };
        assert_eq!(
            format!("{error}"),
            "Maximum loss calculation error: Loss exceeds margin requirements"
        );
    }

    #[test]
    fn test_profit_loss_error_profit_range_error() {
        let error = ProfitLossErrorKind::ProfitRangeError {
            reason: "Profit calculation failed".to_string(),
        };
        assert_eq!(
            format!("{error}"),
            "Profit range calculation error: Profit calculation failed"
        );
    }

    #[test]
    fn test_strategy_error_invalid_parameters_constructor() {
        let error = StrategyError::invalid_parameters("Open position", "Margin insufficient");
        assert_eq!(
            format!("{error}"),
            "Operation error: Invalid parameters for operation 'Open position': Margin insufficient"
        );
    }

    #[test]
    fn test_strategy_error_from_trade_error_invalid_trade() {
        let trade_error = TradeError::invalid_trade("Trade failure");
        let error: StrategyError = trade_error.into();
        assert_eq!(
            format!("{error}"),
            "Operation error: Invalid parameters for operation 'Trade': Invalid trade: Trade failure"
        );
    }

    #[test]
    fn test_strategy_error_from_trade_error_invalid_trade_status() {
        let trade_error = TradeError::invalid_trade_status("Trade status failure");
        let error: StrategyError = trade_error.into();
        assert_eq!(
            format!("{error}"),
            "Operation error: Invalid parameters for operation 'Trade': Invalid trade status: Trade status failure"
        );
    }
}