use crate::param::{
AccountId, AdjustmentAmount, Asset, Fee, Leverage, Pnl, PositionEffect, PositionMode,
PositionSide, PositionSize, Price, Quantity, Side, Trade, TradeAmount,
};
use crate::pretrade::PreTradeLock;
use super::Instrument;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RequestFieldAccessError {
pub field: &'static str,
}
impl RequestFieldAccessError {
pub fn new(field: &'static str) -> Self {
Self { field }
}
}
impl std::fmt::Display for RequestFieldAccessError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "failed to access field '{}'", self.field)
}
}
impl std::error::Error for RequestFieldAccessError {}
#[macro_export]
macro_rules! has_request_field_trait {
(
$(#[$meta:meta])*
$trait:ident,
$method:ident -> $ret:ty
) => {
$(#[$meta])*
pub trait $trait {
fn $method(
&self,
) -> ::std::result::Result<$ret, $crate::RequestFieldAccessError>;
}
impl<T> $trait for T
where
T: std::ops::Deref,
T::Target: $trait,
{
fn $method(
&self,
) -> ::std::result::Result<$ret, $crate::RequestFieldAccessError> {
self.deref().$method()
}
}
};
}
has_request_field_trait!(HasAccountId, account_id -> AccountId);
has_request_field_trait!(HasBalanceAsset, balance_asset -> &Asset);
has_request_field_trait!(HasCollateralAsset, collateral_asset -> &Asset);
has_request_field_trait!(HasInstrument, instrument -> &Instrument);
has_request_field_trait!(HasPositionInstrument, position_instrument -> &Instrument);
has_request_field_trait!(HasSide, side -> Side);
has_request_field_trait!(HasTradeAmount, trade_amount -> TradeAmount);
has_request_field_trait!(HasReduceOnly, reduce_only -> bool);
has_request_field_trait!(HasClosePosition, close_position -> bool);
has_request_field_trait!(HasAutoBorrow, auto_borrow -> bool);
has_request_field_trait!(HasPnl, pnl -> Pnl);
has_request_field_trait!(HasFee, fee -> Fee);
has_request_field_trait!(
HasLeavesQuantity,
leaves_quantity -> Quantity
);
has_request_field_trait!(
HasLock,
lock -> PreTradeLock
);
has_request_field_trait!(
HasPositionMode,
position_mode -> PositionMode
);
has_request_field_trait!(
HasOrderPrice,
price -> Option<Price>
);
has_request_field_trait!(HasOrderPositionSide, position_side -> Option<PositionSide>);
has_request_field_trait!(HasOrderLeverage, leverage -> Option<Leverage>);
has_request_field_trait!(HasOrderCollateralAsset, collateral_asset -> Option<&Asset>);
has_request_field_trait!(HasExecutionReportLastTrade, last_trade -> Option<Trade>);
has_request_field_trait!(HasExecutionReportIsFinal, is_final -> bool);
has_request_field_trait!(
HasExecutionReportPositionEffect,
position_effect -> Option<PositionEffect>
);
has_request_field_trait!(HasExecutionReportPositionSide, position_side -> Option<PositionSide>);
has_request_field_trait!( HasAverageEntryPrice, average_entry_price -> Price);
has_request_field_trait!(
HasAccountAdjustmentBalanceAverageEntryPrice,
balance_average_entry_price -> Option<Price>
);
has_request_field_trait!(
HasAccountAdjustmentTotal,
total -> Option<AdjustmentAmount>
);
has_request_field_trait!(
HasAccountAdjustmentReserved,
reserved -> Option<AdjustmentAmount>
);
has_request_field_trait!(
HasAccountAdjustmentPending,
pending -> Option<AdjustmentAmount>
);
has_request_field_trait!(
HasAccountAdjustmentTotalUpperBound,
total_upper -> Option<PositionSize>
);
has_request_field_trait!(
HasAccountAdjustmentTotalLowerBound,
total_lower -> Option<PositionSize>
);
has_request_field_trait!(
HasAccountAdjustmentReservedUpperBound,
reserved_upper -> Option<PositionSize>
);
has_request_field_trait!(
HasAccountAdjustmentReservedLowerBound,
reserved_lower -> Option<PositionSize>
);
has_request_field_trait!(
HasAccountAdjustmentPendingUpperBound,
pending_upper -> Option<PositionSize>
);
has_request_field_trait!(
HasAccountAdjustmentPendingLowerBound,
pending_lower -> Option<PositionSize>
);
has_request_field_trait!(HasAccountAdjustmentPositionLeverage, position_leverage -> Option<Leverage>);
#[cfg(test)]
mod tests {
use super::{HasSide, RequestFieldAccessError};
use crate::core::order::OrderOperation;
use crate::param::{Asset, Quantity, Side, TradeAmount};
use crate::Instrument;
fn operation() -> OrderOperation {
use crate::param::AccountId;
OrderOperation {
instrument: Instrument::new(
Asset::new("SPX").expect("must be valid"),
Asset::new("USD").expect("must be valid"),
),
account_id: AccountId::from_u64(99224416),
side: Side::Buy,
trade_amount: TradeAmount::Quantity(Quantity::from_str("1").expect("must be valid")),
price: None,
}
}
#[test]
fn deref_dispatch_calls_method_on_target() {
let boxed: Box<OrderOperation> = Box::new(operation());
assert_eq!(boxed.side(), Ok(Side::Buy));
}
#[test]
fn display_is_stable() {
let err = RequestFieldAccessError::new("instrument");
assert_eq!(err.to_string(), "failed to access field 'instrument'");
}
#[test]
fn equality() {
assert_eq!(
RequestFieldAccessError::new("side"),
RequestFieldAccessError::new("side")
);
assert_ne!(
RequestFieldAccessError::new("side"),
RequestFieldAccessError::new("instrument")
);
}
}