optionstratlib_pricing/pricing/payoff.rs
1//! Profit contracts: [`Profit`], the profit of a contract or position at a
2//! given underlying price. The payoff contracts at expiry (`Payoff`,
3//! `PayoffInfo`) are core's: `optionstratlib_core::model::payoff`.
4
5use crate::error::PricingError;
6use optionstratlib_core::model::Positive;
7use optionstratlib_core::model::{Options, Position};
8use rust_decimal::Decimal;
9use tracing::trace;
10
11/// Defines the profit calculation behavior for financial instruments.
12///
13/// This trait is used to calculate and visualize profit values at different price points
14/// for various financial instruments and strategies. It provides:
15/// 1. A required method to calculate the actual profit value at a given price
16/// 2. A default implementation to convert the profit calculation into a visualization point
17///
18/// # Usage
19///
20/// Implement this trait for any type that can calculate profit at a specific price point,
21/// such as options contracts, spreads, or complex trading strategies.
22///
23pub trait Profit {
24 /// Calculates the profit at a specified price.
25 ///
26 /// # Parameters
27 ///
28 /// * `price` - A positive value representing the price at which to calculate profit
29 ///
30 /// # Returns
31 ///
32 /// * `Result<Decimal, PricingError>` - The calculated profit as a Decimal value,
33 /// or an error if the calculation fails
34 ///
35 /// # Errors
36 ///
37 /// Returns `PricingError::MethodError` when the strategy cannot
38 /// evaluate its payoff at `price` (typically propagated from the
39 /// underlying `Position::pnl_at_expiration` or Black–Scholes
40 /// evaluation).
41 fn calculate_profit_at(&self, price: &Positive) -> Result<Decimal, PricingError>;
42
43 /// Creates a chart point representation of the profit at the given price.
44 ///
45 /// This method automatically determines the appropriate visualization properties based
46 /// on the profit value, such as color (green for positive profit, red for negative).
47 ///
48 /// # Parameters
49 ///
50 /// * `price` - A positive value representing the price for which to create a chart point
51 ///
52 /// # Returns
53 ///
54 /// * `ChartPoint<(f64, f64)>` - A formatted chart point with coordinates (price, profit),
55 /// styling, and a formatted profit label
56 ///
57 /// # Errors
58 ///
59 /// Propagates any `PricingError` returned by
60 /// [`Profit::calculate_profit_at`].
61 fn get_point_at_price(&self, _price: &Positive) -> Result<(Decimal, Decimal), PricingError> {
62 let profit = self.calculate_profit_at(_price)?;
63 let price: Decimal = _price.into();
64 let point = (price, profit);
65 trace!("get_point_at_price - point: {:?}", point);
66 Ok(point)
67 }
68}
69
70impl Profit for Options {
71 fn calculate_profit_at(&self, price: &Positive) -> Result<Decimal, PricingError> {
72 Ok(self.payoff_at_price(price)?)
73 }
74}
75
76/// Implementation of the Profit trait for the Position struct.
77///
78/// This allows calculating the profit of a position at a given price by using the
79/// position's profit and loss (PnL) calculation at expiration.
80///
81impl Profit for Position {
82 /// Calculates the profit of the position at a specific price.
83 ///
84 /// This method computes the profit or loss that would be realized if the position
85 /// were to expire with the underlying asset at the specified price.
86 ///
87 /// # Parameters
88 ///
89 /// * `price` - The price at which to calculate the profit, represented as a Positive value.
90 ///
91 /// # Returns
92 ///
93 /// * `Result<Decimal, PricingError>` - The calculated profit as a Decimal if successful,
94 /// or an error if the calculation fails.
95 fn calculate_profit_at(&self, price: &Positive) -> Result<Decimal, PricingError> {
96 Ok(self.pnl_at_expiration(&Some(price))?)
97 }
98}