Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Quantrs supports options pricing with various models for both vanilla and exotic
| --------------------------- | --------------- | -------- | ------------ | ------------ | ------------- | ------ |
| European | ✅ | ✅ | ✅ | ✅ | ⏳ | ⏳ |
| American | ❌ | ❌ | ✅ | ❌ (L. Sq.) | ⏳ | ❌ |
| Bermudan | ❌ | ❌ | | ❌ (L. Sq.) | ❌ (complex) | ❌ |
| Bermudan | ❌ | ❌ | | ❌ (L. Sq.) | ❌ (complex) | ❌ |
| ¹Basket | ⏳ (∀component) | ❌ | ⏳ (approx.) | ⏳ | ❌ | ❌ |
| ¹Rainbow | ✅ (∀component) | ❌ | ✅ | ✅ | ❌ | ❌ |
| ²Barrier | ❌ (mod. BSM) | ❌ | ⏳ | ⏳ | ⏳ | ⏳ |
Expand Down
13 changes: 11 additions & 2 deletions src/options/models/binomial_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,13 @@ impl OptionPricing for BinomialTreeModel {
let expected_value =
discount_factor * (p * option_values[i + 1] + (1.0 - p) * option_values[i]);

if matches!(option.style(), OptionStyle::American) {
if matches!(option.style(), OptionStyle::American)
|| matches!(option.style(), OptionStyle::Bermudan)
&& option
.expiration_dates()
.unwrap()
.contains(&(step as f64 * dt))
{
let early_exercise = option.payoff(Some(
option.instrument().spot() * u.powi(i as i32) * d.powi((step - i) as i32),
));
Expand All @@ -149,7 +155,10 @@ impl OptionPricing for BinomialTreeModel {
}
}

if matches!(option.style(), OptionStyle::American) {
if matches!(option.style(), OptionStyle::American)
|| matches!(option.style(), OptionStyle::Bermudan)
&& option.expiration_dates().unwrap().contains(&0.0)
{
option_values[0].max(option.strike() - option.instrument().spot()) // TODO: Change to max(0.0, self.payoff(Some(self.spot)))
} else {
option_values[0] // Return the root node value
Expand Down
11 changes: 10 additions & 1 deletion src/options/traits/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ pub trait Option: Clone + Send + Sync {
/// The time horizon (in years).
fn time_to_maturity(&self) -> f64;

/// Get the expiration dates of the option.
///
/// # Returns
///
/// The expiration dates of the option. (Only for Bermudan options)
fn expiration_dates(&self) -> std::option::Option<&Vec<f64>> {
None
}

/// Set the time horizon (in years).
///
/// # Arguments
Expand All @@ -60,7 +69,7 @@ pub trait Option: Clone + Send + Sync {
/// # Returns
///
/// The style of the option.
fn style(&self) -> &OptionStyle;
fn style(&self) -> OptionStyle;

/// Flip the option type (Call to Put or Put to Call).
///
Expand Down
4 changes: 4 additions & 0 deletions src/options/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@

pub use american_option::AmericanOption;
pub use asian_option::AsianOption;
pub use barrier_option::BarrierOption;
pub use bermudan_option::BermudanOption;
pub use binary_option::BinaryOption;
pub use european_option::EuropeanOption;
pub use lookback_option::LookbackOption;
pub use rainbow_option::RainbowOption;

mod american_option;
mod asian_option;
mod barrier_option;
mod bermudan_option;
mod binary_option;
mod european_option;
mod lookback_option;
Expand Down
6 changes: 3 additions & 3 deletions src/options/types/american_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ pub struct AmericanOption {
pub instrument: Instrument,
/// Strike price of the option (aka exercise price).
pub strike: f64,
/// Type of the option (Call or Put).
/// The time horizon (in years).
pub time_to_maturity: f64,
/// Type of the option (Call or Put).
pub option_type: OptionType,
}

Expand Down Expand Up @@ -86,8 +86,8 @@ impl Option for AmericanOption {
self.option_type
}

fn style(&self) -> &OptionStyle {
&OptionStyle::American
fn style(&self) -> OptionStyle {
OptionStyle::American
}

fn flip(&self) -> Self {
Expand Down
19 changes: 8 additions & 11 deletions src/options/types/asian_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,8 @@ pub struct AsianOption {
pub time_to_maturity: f64,
/// Type of the option (Call or Put).
pub option_type: OptionType,
/// The style of the option (Asian).
pub option_style: OptionStyle,
/// The type of the Asian option (Fixed or Floating).
pub asian_type: Permutation,
/// Style of the option (Asian with specific type).
pub permutation: Permutation,
}

impl AsianOption {
Expand All @@ -48,15 +46,14 @@ impl AsianOption {
strike: f64,
time_to_maturity: f64,
option_type: OptionType,
asian_type: Permutation,
permutation: Permutation,
) -> Self {
Self {
instrument,
strike,
time_to_maturity,
option_type,
option_style: OptionStyle::Asian(asian_type),
asian_type,
permutation,
}
}

Expand Down Expand Up @@ -121,13 +118,13 @@ impl Option for AsianOption {
self.option_type
}

fn style(&self) -> &OptionStyle {
&self.option_style
fn style(&self) -> OptionStyle {
OptionStyle::Asian(self.permutation)
}

fn payoff(&self, avg_price: std::option::Option<f64>) -> f64 {
let avg_price = avg_price.unwrap_or(self.instrument.spot());
match self.asian_type {
match self.permutation {
Permutation::Fixed => match self.option_type {
OptionType::Call => (avg_price - self.strike).max(0.0),
OptionType::Put => (self.strike - avg_price).max(0.0),
Expand All @@ -149,7 +146,7 @@ impl Option for AsianOption {
self.strike,
self.time_to_maturity,
flipped_option_type,
self.asian_type,
self.permutation,
)
}

Expand Down
101 changes: 101 additions & 0 deletions src/options/types/barrier_option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! Module for Barrier option type.

use std::any::Any;

use super::{BarrierType, OptionStyle, OptionType};
use crate::options::{Instrument, Option};

/// A struct representing an Bermudan option.
#[derive(Clone, Debug)]
pub struct BarrierOption {
/// The underlying instrument.
pub instrument: Instrument,
/// Strike price of the option (aka exercise price).
pub strike: f64,
/// The barrier price
pub barrier: f64,
/// The time horizon (in years).
pub time_to_maturity: f64,
/// Type of the option (Call or Put).
pub option_type: OptionType,
/// Style of the option (Barrier with specific type).
pub barrier_type: BarrierType,
}

impl BarrierOption {
/// Create a new `BarrierOption`.
pub fn new(
instrument: Instrument,
strike: f64,
barrier: f64,
time_to_maturity: f64,
option_type: OptionType,
barrier_type: BarrierType,
) -> Self {
Self {
instrument,
strike,
barrier,
time_to_maturity,
option_type,
barrier_type,
}
}
}

impl Option for BarrierOption {
fn instrument(&self) -> &Instrument {
&self.instrument
}

fn instrument_mut(&mut self) -> &mut Instrument {
&mut self.instrument
}

fn set_instrument(&mut self, instrument: Instrument) {
self.instrument = instrument;
}

fn strike(&self) -> f64 {
self.strike
}

fn time_to_maturity(&self) -> f64 {
self.time_to_maturity
}

fn set_time_to_maturity(&mut self, time_to_maturity: f64) {
self.time_to_maturity = time_to_maturity;
}

fn option_type(&self) -> OptionType {
self.option_type
}

fn style(&self) -> OptionStyle {
OptionStyle::Barrier(self.barrier_type)
}

fn payoff(&self, avg_price: std::option::Option<f64>) -> f64 {
todo!("Implement payoff calculation for BarrierOption");
}

fn flip(&self) -> Self {
let flipped_option_type = match self.option_type {
OptionType::Call => OptionType::Put,
OptionType::Put => OptionType::Call,
};
BarrierOption::new(
self.instrument.clone(),
self.strike,
self.barrier,
self.time_to_maturity,
flipped_option_type,
self.barrier_type,
)
}

fn as_any(&self) -> &dyn Any {
self
}
}
106 changes: 106 additions & 0 deletions src/options/types/bermudan_option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! Module for Bermudan option type.
//!
//! A Bermuda option can be exercised early, but only on a set of specific dates before its expiration.
//! These exercise dates are often set in one-month increments.
//! Premiums for Bermuda options are typically lower than those of American options, which can be exercised any time before expiry.

use std::any::Any;

use super::{OptionStyle, OptionType};
use crate::{
log_warn,
options::{Instrument, Option},
};

/// A struct representing an Bermudan option.
#[derive(Clone, Debug)]
pub struct BermudanOption {
/// The underlying instrument.
pub instrument: Instrument,
/// Strike price of the option (aka exercise price).
pub strike: f64,
/// The time horizon (in years).
pub time_to_maturity: f64,
/// The expiration dates of the option (in years).
pub expiration_dates: Vec<f64>,
/// Type of the option (Call or Put).
pub option_type: OptionType,
}

impl BermudanOption {
/// Create a new `BermudanOption`.
pub fn new(
instrument: Instrument,
strike: f64,
expiration_dates: Vec<f64>,
option_type: OptionType,
) -> Self {
Self {
instrument,
strike,
time_to_maturity: if let Some(&last_date) = expiration_dates.last() {
last_date
} else {
log_warn!("Expiration dates are empty, setting time to maturity to 0.0");
0.0
},
expiration_dates,
option_type,
}
}
}

impl Option for BermudanOption {
fn instrument(&self) -> &Instrument {
&self.instrument
}

fn instrument_mut(&mut self) -> &mut Instrument {
&mut self.instrument
}

fn set_instrument(&mut self, instrument: Instrument) {
self.instrument = instrument;
}

fn strike(&self) -> f64 {
self.strike
}

fn time_to_maturity(&self) -> f64 {
self.time_to_maturity
}

fn expiration_dates(&self) -> std::option::Option<&Vec<f64>> {
Some(&self.expiration_dates)
}

fn set_time_to_maturity(&mut self, time_to_maturity: f64) {
self.time_to_maturity = time_to_maturity;
}

fn option_type(&self) -> OptionType {
self.option_type
}

fn style(&self) -> OptionStyle {
OptionStyle::Bermudan
}

fn flip(&self) -> Self {
let flipped_option_type = match self.option_type {
OptionType::Call => OptionType::Put,
OptionType::Put => OptionType::Call,
};
BermudanOption::new(
self.instrument.clone(),
self.strike,
self.expiration_dates.clone(),
flipped_option_type,
)
}

fn as_any(&self) -> &dyn Any {
self
}
}
8 changes: 4 additions & 4 deletions src/options/types/binary_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ impl BinaryOption {
strike: f64,
time_to_maturity: f64,
option_type: OptionType,
binary_option_type: BinaryType,
binary_type: BinaryType,
) -> Self {
Self {
instrument,
strike,
time_to_maturity,
option_type,
option_style: OptionStyle::Binary(binary_option_type),
option_style: OptionStyle::Binary(binary_type),
}
}

Expand Down Expand Up @@ -133,8 +133,8 @@ impl Option for BinaryOption {
self.option_type
}

fn style(&self) -> &OptionStyle {
&self.option_style
fn style(&self) -> OptionStyle {
self.option_style
}

fn flip(&self) -> Self {
Expand Down
Loading