use crate::ToField;
use std::fmt;
use time::{Date, Duration, Month, OffsetDateTime, Weekday};
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Symbol(pub String);
impl Symbol {
pub fn new(s: impl Into<String>) -> Self {
Symbol(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for Symbol {
fn from(s: &str) -> Self {
Symbol(s.to_string())
}
}
impl From<String> for Symbol {
fn from(s: String) -> Self {
Symbol(s)
}
}
impl From<&String> for Symbol {
fn from(s: &String) -> Self {
Symbol(s.clone())
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl ToField for Symbol {
fn to_field(&self) -> String {
self.0.clone()
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Exchange(pub String);
impl Exchange {
pub fn new(s: impl Into<String>) -> Self {
Exchange(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl Default for Exchange {
fn default() -> Self {
Exchange("SMART".to_string())
}
}
impl From<&str> for Exchange {
fn from(s: &str) -> Self {
Exchange(s.to_string())
}
}
impl From<String> for Exchange {
fn from(s: String) -> Self {
Exchange(s)
}
}
impl From<&String> for Exchange {
fn from(s: &String) -> Self {
Exchange(s.clone())
}
}
impl fmt::Display for Exchange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl ToField for Exchange {
fn to_field(&self) -> String {
self.0.clone()
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Currency(pub String);
impl Currency {
pub fn new(s: impl Into<String>) -> Self {
Currency(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Default for Currency {
fn default() -> Self {
Currency("USD".to_string())
}
}
impl From<&str> for Currency {
fn from(s: &str) -> Self {
Currency(s.to_string())
}
}
impl From<String> for Currency {
fn from(s: String) -> Self {
Currency(s)
}
}
impl From<&String> for Currency {
fn from(s: &String) -> Self {
Currency(s.clone())
}
}
impl fmt::Display for Currency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl ToField for Currency {
fn to_field(&self) -> String {
self.0.clone()
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OptionRight {
Call,
Put,
}
impl OptionRight {
pub fn as_str(&self) -> &str {
match self {
OptionRight::Call => "C",
OptionRight::Put => "P",
}
}
}
impl fmt::Display for OptionRight {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Strike(f64);
impl Strike {
pub fn new(price: f64) -> Result<Self, String> {
if price <= 0.0 {
Err("Strike price must be positive".to_string())
} else {
Ok(Strike(price))
}
}
pub(crate) fn new_unchecked(price: f64) -> Self {
Strike::new(price).expect("Strike price must be positive")
}
pub fn value(&self) -> f64 {
self.0
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExpirationDate {
year: u16,
month: u8,
day: u8,
}
impl ExpirationDate {
pub fn new(year: u16, month: u8, day: u8) -> Self {
ExpirationDate { year, month, day }
}
fn days_until_friday(from_weekday: Weekday) -> i64 {
match from_weekday {
Weekday::Saturday => 6,
Weekday::Sunday => 5,
Weekday::Monday => 4,
Weekday::Tuesday => 3,
Weekday::Wednesday => 2,
Weekday::Thursday => 1,
Weekday::Friday => 0,
}
}
pub fn next_friday() -> Self {
let today = OffsetDateTime::now_utc().date();
let days_to_add = match today.weekday() {
Weekday::Friday => 7, other => Self::days_until_friday(other),
};
let next_friday = today + Duration::days(days_to_add);
ExpirationDate {
year: next_friday.year() as u16,
month: next_friday.month() as u8,
day: next_friday.day(),
}
}
pub fn third_friday_of_month() -> Self {
let now = OffsetDateTime::now_utc();
let year = now.year();
let month = now.month();
let first_of_month = Date::from_calendar_date(year, month, 1).expect("Valid date");
let days_to_first_friday = Self::days_until_friday(first_of_month.weekday());
let third_friday = first_of_month + Duration::days(days_to_first_friday + 14);
if now.date() > third_friday {
let next_month = if month == Month::December {
Date::from_calendar_date(year + 1, Month::January, 1)
} else {
Date::from_calendar_date(year, month.next(), 1)
}
.expect("Valid date");
let days_to_first_friday_next = Self::days_until_friday(next_month.weekday());
let third_friday_next = next_month + Duration::days(days_to_first_friday_next + 14);
ExpirationDate {
year: third_friday_next.year() as u16,
month: third_friday_next.month() as u8,
day: third_friday_next.day(),
}
} else {
ExpirationDate {
year: third_friday.year() as u16,
month: third_friday.month() as u8,
day: third_friday.day(),
}
}
}
}
impl fmt::Display for ExpirationDate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04}{:02}{:02}", self.year, self.month, self.day)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContractMonth {
year: u16,
month: u8,
}
impl ContractMonth {
pub fn new(year: u16, month: u8) -> Self {
ContractMonth { year, month }
}
pub fn front() -> Self {
let now = OffsetDateTime::now_utc();
let current_year = now.year() as u16;
let current_month = now.month() as u8;
let current_day = now.day();
if current_day > 15 {
if current_month == 12 {
ContractMonth::new(current_year + 1, 1)
} else {
ContractMonth::new(current_year, current_month + 1)
}
} else {
ContractMonth::new(current_year, current_month)
}
}
pub fn next_quarter() -> Self {
let now = OffsetDateTime::now_utc();
let current_year = now.year() as u16;
let current_month = now.month() as u8;
let current_day = now.day();
let next_quarter_month = match current_month {
1 | 2 => 3,
3 => {
if current_day > 15 {
6
} else {
3
}
}
4 | 5 => 6,
6 => {
if current_day > 15 {
9
} else {
6
}
}
7 | 8 => 9,
9 => {
if current_day > 15 {
12
} else {
9
}
}
10 | 11 => 12,
12 => {
if current_day > 15 {
3
} else {
12
}
}
_ => 3,
};
let year = if current_month == 12 && next_quarter_month == 3 {
current_year + 1
} else {
current_year
};
ContractMonth::new(year, next_quarter_month)
}
}
impl fmt::Display for ContractMonth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04}{:02}", self.year, self.month)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Cusip(pub String);
impl Cusip {
pub fn new(s: impl Into<String>) -> Self {
Cusip(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for Cusip {
fn from(s: &str) -> Self {
Cusip(s.to_string())
}
}
impl From<String> for Cusip {
fn from(s: String) -> Self {
Cusip(s)
}
}
impl From<&String> for Cusip {
fn from(s: &String) -> Self {
Cusip(s.clone())
}
}
impl fmt::Display for Cusip {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Isin(pub String);
impl Isin {
pub fn new(s: impl Into<String>) -> Self {
Isin(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for Isin {
fn from(s: &str) -> Self {
Isin(s.to_string())
}
}
impl From<String> for Isin {
fn from(s: String) -> Self {
Isin(s)
}
}
impl From<&String> for Isin {
fn from(s: &String) -> Self {
Isin(s.clone())
}
}
impl fmt::Display for Isin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BondIdentifier {
Cusip(Cusip),
Isin(Isin),
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LegAction {
Buy,
Sell,
}
impl LegAction {
pub fn as_str(&self) -> &str {
match self {
LegAction::Buy => "BUY",
LegAction::Sell => "SELL",
}
}
}
impl fmt::Display for LegAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy)]
pub struct Missing;