Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/backtesting/results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ impl SimulationStatsResult {
/// - P&L statistics (total, average, max, min)
/// - Holding period information
/// - Exit reason distribution
#[inline(never)]
pub fn print_summary(&self) {
use prettytable::{Cell, Row, Table, color, format};
use rust_decimal_macros::dec;
Expand Down Expand Up @@ -351,6 +352,7 @@ impl SimulationStatsResult {
/// - Final P&L
/// - Holding period
/// - Exit reason
#[inline(never)]
pub fn print_individual_results(&self) {
use prettytable::{Cell, Row, Table, color, format};
use rust_decimal_macros::dec;
Expand Down
10 changes: 10 additions & 0 deletions src/chains/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ impl OptionChain {
/// - `expiration_date` is missing from price params
/// - Failed to get days from expiration date
/// - Failed to get date string from expiration date
#[inline(never)]
pub fn build_chain(params: &OptionChainBuildParams) -> Result<Self, ChainError> {
let underlying_price = params
.price_params
Expand Down Expand Up @@ -1090,6 +1091,7 @@ impl OptionChain {
/// Returns [`ChainError::FileError`] wrapping a `FileErrorKind::IOError`
/// when the file cannot be created or written, or
/// `FileErrorKind::ParseError` when `csv` serialization fails.
#[inline(never)]
pub fn save_to_csv(&self, file_path: &str) -> Result<(), ChainError> {
let full_path = format!("{}/{}.csv", file_path, self.get_title());
let mut wtr = WriterBuilder::new().from_path(full_path)?;
Expand Down Expand Up @@ -1170,6 +1172,7 @@ impl OptionChain {
/// Returns [`ChainError::FileError`] wrapping a `FileErrorKind::IOError`
/// when the file cannot be created or written, or
/// `FileErrorKind::ParseError` when `serde_json` serialization fails.
#[inline(never)]
pub fn save_to_json(&self, file_path: &str) -> Result<(), ChainError> {
let full_path = format!("{}/{}.json", file_path, self.get_title());
let file = File::create(full_path)?;
Expand Down Expand Up @@ -1223,6 +1226,7 @@ impl OptionChain {
/// `FileErrorKind::ParseError` when the CSV records cannot be parsed.
/// Invalid option data (bad strike, volatility or price) surfaces as
/// [`ChainError::OptionDataError`].
#[inline(never)]
pub fn load_from_csv(file_path: &str) -> Result<Self, ChainError> {
let mut rdr = csv::Reader::from_path(file_path)?;
let mut options = BTreeSet::new();
Expand Down Expand Up @@ -1313,6 +1317,7 @@ impl OptionChain {
/// Returns [`ChainError::FileError`] wrapping `FileErrorKind::IOError`
/// when the file cannot be opened, or `FileErrorKind::ParseError`
/// when `serde_json` deserialization fails.
#[inline(never)]
pub fn load_from_json(file_path: &str) -> Result<Self, ChainError> {
let file = File::open(file_path)?;
let mut option_chain: OptionChain = serde_json::from_reader(file)?;
Expand Down Expand Up @@ -1586,6 +1591,7 @@ impl OptionChain {
/// # Returns
///
/// An iterator where each item is a reference to an `OptionData`.
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &OptionData> {
self.get_single_iter()
}
Expand All @@ -1601,6 +1607,7 @@ impl OptionChain {
/// Since the `options` field is stored as a `BTreeSet`, the elements are ordered
/// in ascending order based on the sorting rules of `BTreeSet` (typically defined by `Ord` implementation).
///
#[inline]
pub fn get_single_iter(&self) -> impl Iterator<Item = &OptionData> {
self.options.iter().filter(|option| option.validate())
}
Expand Down Expand Up @@ -2648,6 +2655,7 @@ impl OptionChain {
/// # Returns
///
/// A `String` representing the expiration date of the option chain.
#[inline]
#[must_use]
pub fn get_expiration_date(&self) -> String {
self.expiration_date.clone()
Expand All @@ -2657,6 +2665,7 @@ impl OptionChain {
///
/// # Returns
/// * `Option<ExpirationDate>` - The expiration date if it can be parsed, or `None` if parsing fails.
#[inline]
#[must_use]
pub fn get_expiration(&self) -> Option<ExpirationDate> {
ExpirationDate::from_string(&self.expiration_date).ok()
Expand Down Expand Up @@ -3181,6 +3190,7 @@ impl OptionChain {
/// This method prints the option chain directly to stdout using prettytable's
/// `printstd()` method, which properly displays colors in the terminal.
/// Use this method instead of `info!("{}", chain)` to see colored headers.
#[inline(never)]
pub fn show(&self) {
// Print header information
let mut header = Table::new();
Expand Down
17 changes: 17 additions & 0 deletions src/chains/optiondata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ impl OptionData {
///
/// # Note
/// The `Positive` type is assumed to enforce non-negative values for correctness.
#[inline]
#[must_use]
pub fn get_call_spread(&self) -> Option<Positive> {
match (self.call_bid, self.call_ask) {
Expand Down Expand Up @@ -281,6 +282,7 @@ impl OptionData {
///
/// # Errors
/// This function does not return an error. It simply returns `None` if the calculation is not feasible.
#[inline]
#[must_use]
pub fn get_call_spread_per(&self) -> Option<Positive> {
match (self.call_bid, self.call_ask) {
Expand Down Expand Up @@ -310,6 +312,7 @@ impl OptionData {
/// to convert to a numeric type for calculation purposes.
/// The spread is always represented as a positive value.
///
#[inline]
#[must_use]
pub fn get_put_spread(&self) -> Option<Positive> {
match (self.put_bid, self.put_ask) {
Expand Down Expand Up @@ -337,6 +340,7 @@ impl OptionData {
///
/// # Note
/// This function returns `None` if there are missing values for either `put_bid` or `put_ask`.
#[inline]
#[must_use]
pub fn get_put_spread_per(&self) -> Option<Positive> {
match (self.put_bid, self.put_ask) {
Expand Down Expand Up @@ -364,6 +368,7 @@ impl OptionData {
///
/// Ensure that the `Positive` type enforces constraints to prevent invalid values
/// such as negative volatility.
#[inline]
#[must_use]
pub fn get_volatility(&self) -> Positive {
self.implied_volatility
Expand All @@ -373,6 +378,7 @@ impl OptionData {
///
/// # Arguments
/// * `volatility` - A positive decimal value representing the implied volatility.
#[inline]
pub fn set_volatility(&mut self, volatility: &Positive) {
self.implied_volatility = *volatility;
}
Expand Down Expand Up @@ -449,6 +455,7 @@ impl OptionData {
/// a valid positive number.
///
/// [`Positive`]: struct.Positive.html
#[inline]
#[must_use]
pub fn strike(&self) -> Positive {
self.strike_price
Expand All @@ -464,6 +471,7 @@ impl OptionData {
/// # Returns
///
/// `true` if all required call option data is present, `false` otherwise.
#[inline]
pub(crate) fn valid_call(&self) -> bool {
self.strike_price > Positive::ZERO && self.call_bid.is_some() && self.call_ask.is_some()
}
Expand All @@ -478,6 +486,7 @@ impl OptionData {
/// # Returns
///
/// `true` if all required put option data is present, `false` otherwise.
#[inline]
pub(crate) fn valid_put(&self) -> bool {
self.strike_price > Positive::ZERO && self.put_bid.is_some() && self.put_ask.is_some()
}
Expand All @@ -490,6 +499,7 @@ impl OptionData {
/// # Returns
///
/// The call option's ask price as a `Positive` value, or `None` if the price is unavailable.
#[inline]
#[must_use]
pub fn get_call_buy_price(&self) -> Option<Positive> {
self.call_ask
Expand All @@ -503,6 +513,7 @@ impl OptionData {
/// # Returns
///
/// The call option's bid price as a `Positive` value, or `None` if the price is unavailable.
#[inline]
#[must_use]
pub fn get_call_sell_price(&self) -> Option<Positive> {
self.call_bid
Expand All @@ -516,6 +527,7 @@ impl OptionData {
/// # Returns
///
/// The put option's ask price as a `Positive` value, or `None` if the price is unavailable.
#[inline]
#[must_use]
pub fn get_put_buy_price(&self) -> Option<Positive> {
self.put_ask
Expand All @@ -529,6 +541,7 @@ impl OptionData {
/// # Returns
///
/// The put option's bid price as a `Positive` value, or `None` if the price is unavailable.
#[inline]
#[must_use]
pub fn get_put_sell_price(&self) -> Option<Positive> {
self.put_bid
Expand All @@ -540,6 +553,7 @@ impl OptionData {
/// fields of the `OptionData` struct are `None`, indicating missing price information.
/// It returns `false` if all four fields have valid price data.
///
#[inline]
#[must_use]
pub fn some_price_is_none(&self) -> bool {
self.call_bid.is_none()
Expand Down Expand Up @@ -1070,6 +1084,7 @@ impl OptionData {
/// A tuple containing:
/// * First element: The call option mid-price (bid+ask)/2, or `None` if not available
/// * Second element: The put option mid-price (bid+ask)/2, or `None` if not available
#[inline]
#[must_use]
pub fn get_mid_prices(&self) -> (Option<Positive>, Option<Positive>) {
(self.call_middle, self.put_middle)
Expand Down Expand Up @@ -1101,6 +1116,7 @@ impl OptionData {
/// * Second element: `Option<Decimal>` - The delta value for the put option. May be `None` if
/// the delta value is not available or could not be calculated.
///
#[inline]
#[must_use]
pub fn current_deltas(&self) -> (Option<Decimal>, Option<Decimal>) {
(self.delta_call, self.delta_put)
Expand All @@ -1117,6 +1133,7 @@ impl OptionData {
/// * `Option<Decimal>` - The current gamma value wrapped in `Some` if it exists,
/// or `None` if the gamma value is not set.
///
#[inline]
#[must_use]
pub fn current_gamma(&self) -> Option<Decimal> {
self.gamma
Expand Down
12 changes: 12 additions & 0 deletions src/error/chains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,8 @@ impl ChainError {
///
/// A `ChainError` containing the strike validation error details
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_strike(strike: f64, reason: &str) -> Self {
ChainError::OptionDataError(OptionDataErrorKind::InvalidStrike {
strike,
Expand All @@ -538,6 +540,8 @@ impl ChainError {
///
/// A `ChainError` containing the volatility validation error details
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_volatility(volatility: Option<f64>, reason: &str) -> Self {
ChainError::OptionDataError(OptionDataErrorKind::InvalidVolatility {
volatility,
Expand All @@ -560,6 +564,8 @@ impl ChainError {
///
/// A `ChainError` containing the price validation error details
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_prices(bid: Option<f64>, ask: Option<f64>, reason: &str) -> Self {
ChainError::OptionDataError(OptionDataErrorKind::InvalidPrices {
bid,
Expand All @@ -583,6 +589,8 @@ impl ChainError {
///
/// A `ChainError` containing the strategy legs validation error details
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_legs(expected: usize, found: usize, reason: &str) -> Self {
ChainError::StrategyError(StrategyErrorKind::InvalidLegs {
expected,
Expand All @@ -605,6 +613,8 @@ impl ChainError {
///
/// A `ChainError` containing the parameter validation error details
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_parameters(parameter: &str, reason: &str) -> Self {
ChainError::ChainBuildError(ChainBuildErrorKind::InvalidParameters {
parameter: parameter.to_string(),
Expand All @@ -625,6 +635,8 @@ impl ChainError {
///
/// A `ChainError` containing the parameter validation error details
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_price_calculation(reason: &str) -> Self {
ChainError::OptionDataError(OptionDataErrorKind::PriceCalculationError(
reason.to_string(),
Expand Down
4 changes: 4 additions & 0 deletions src/error/curves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ impl CurveError {
/// - For example, attempting an unsupported computation method on a specific curve type.
///
#[must_use]
#[cold]
#[inline(never)]
pub fn operation_not_supported(operation: &str, reason: &str) -> Self {
CurveError::OperationError(OperationErrorKind::NotSupported {
operation: operation.to_string(),
Expand All @@ -209,6 +211,8 @@ impl CurveError {
/// - For example, providing malformed or missing parameters for interpolation or curve construction.
///
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_parameters(operation: &str, reason: &str) -> Self {
CurveError::OperationError(OperationErrorKind::InvalidParameters {
operation: operation.to_string(),
Expand Down
10 changes: 10 additions & 0 deletions src/error/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ impl DecimalError {
///
/// A new `DecimalError::InvalidValue` instance
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_value(value: f64, reason: &str) -> Self {
DecimalError::InvalidValue {
value,
Expand All @@ -249,6 +251,8 @@ impl DecimalError {
/// # Returns
///
/// A new `DecimalError::ArithmeticError` instance
#[cold]
#[inline(never)]
#[must_use]
pub fn arithmetic_error(operation: &str, reason: &str) -> Self {
DecimalError::ArithmeticError {
Expand All @@ -271,6 +275,8 @@ impl DecimalError {
/// # Returns
///
/// A new `DecimalError::ConversionError` instance
#[cold]
#[inline(never)]
#[must_use]
pub fn conversion_error(from_type: &str, to_type: &str, reason: &str) -> Self {
DecimalError::ConversionError {
Expand All @@ -293,6 +299,8 @@ impl DecimalError {
/// # Returns
///
/// A new `DecimalError::OutOfBounds` instance
#[cold]
#[inline(never)]
#[must_use]
pub fn out_of_bounds(value: f64, min: f64, max: f64) -> Self {
DecimalError::OutOfBounds { value, min, max }
Expand All @@ -311,6 +319,8 @@ impl DecimalError {
/// # Returns
///
/// A new `DecimalError::InvalidPrecision` instance
#[cold]
#[inline(never)]
#[must_use]
pub fn invalid_precision(precision: i32, reason: &str) -> Self {
DecimalError::InvalidPrecision {
Expand Down
10 changes: 8 additions & 2 deletions src/error/greeks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ impl GreeksError {
/// # Returns
/// A `GreeksError::InputError` with `InvalidVolatility` kind
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_volatility(value: f64, reason: &str) -> Self {
GreeksError::InputError(InputErrorKind::InvalidVolatility {
value,
Expand All @@ -514,6 +516,8 @@ impl GreeksError {
/// # Returns
/// A `GreeksError::InputError` with `InvalidTime` kind
#[must_use]
#[cold]
#[inline(never)]
pub fn invalid_time(value: Positive, reason: &str) -> Self {
GreeksError::InputError(InputErrorKind::InvalidTime {
value,
Expand All @@ -533,6 +537,8 @@ impl GreeksError {
/// # Returns
/// A `GreeksError::CalculationError` with `DeltaError` kind
#[must_use]
#[cold]
#[inline(never)]
pub fn delta_error(reason: &str) -> Self {
GreeksError::CalculationError(CalculationErrorKind::DeltaError {
reason: reason.to_string(),
Expand All @@ -545,9 +551,9 @@ impl GreeksError {
/// Intended to be used at `f64` → `Decimal` boundaries inside
/// Greeks kernels, as a thin constructor paired with an
/// `if !value.is_finite() { .. }` guard.
#[must_use]
#[inline]
#[cold]
#[inline(never)]
#[must_use]
pub fn non_finite(context: &'static str, value: f64) -> Self {
GreeksError::NonFinite { context, value }
}
Expand Down
Loading
Loading