use crate::core::account_outcome::OutcomeAmount;
use crate::core::sync_mode::SyncMode;
use crate::core::{
AccountControl, AccountOutcomeEntry, HasAccountAdjustmentBalance,
HasAccountAdjustmentBalanceLowerBound, HasAccountAdjustmentBalanceUpperBound,
HasAccountAdjustmentHeld, HasAccountAdjustmentHeldLowerBound,
HasAccountAdjustmentHeldUpperBound, HasAccountAdjustmentIncoming,
HasAccountAdjustmentIncomingLowerBound, HasAccountAdjustmentIncomingUpperBound,
HasBalanceAsset,
};
use crate::marketdata::MarketDataSync;
use crate::param::{AccountId, PositionSize};
use crate::pretrade::holdings::{AdjustmentTarget, Holdings};
use crate::pretrade::policy::missing_required_field_account_adjustment_reject;
use crate::pretrade::{RejectScope, Rejects};
use crate::Mutations;
use super::rejects::{
account_adjustment_bounds_exceeded_reject, adj_field, arithmetic_overflow_reject,
};
use super::views::AdjustmentRequestView;
use super::SpotFundsPolicy;
impl<Sync, MarketDataSyncMode> SpotFundsPolicy<Sync, MarketDataSyncMode>
where
Sync: SyncMode,
MarketDataSyncMode: MarketDataSync,
{
pub(super) fn read_adjustment_request<AccountAdjustment>(
&self,
adjustment: &AccountAdjustment,
) -> Result<AdjustmentRequestView, Rejects>
where
AccountAdjustment: HasBalanceAsset
+ HasAccountAdjustmentBalance
+ HasAccountAdjustmentBalanceLowerBound
+ HasAccountAdjustmentBalanceUpperBound
+ HasAccountAdjustmentHeld
+ HasAccountAdjustmentHeldLowerBound
+ HasAccountAdjustmentHeldUpperBound
+ HasAccountAdjustmentIncoming
+ HasAccountAdjustmentIncomingLowerBound
+ HasAccountAdjustmentIncomingUpperBound,
{
let asset = adjustment
.balance_asset()
.map_err(|e| {
Rejects::from(missing_required_field_account_adjustment_reject(
self,
"balance asset",
&e,
))
})?
.clone();
let balance = adj_field(self, "balance", adjustment.balance())?;
let balance_lower = adj_field(self, "balance lower bound", adjustment.balance_lower())?;
let balance_upper = adj_field(self, "balance upper bound", adjustment.balance_upper())?;
let held = adj_field(self, "held", adjustment.held())?;
let held_lower = adj_field(self, "held lower bound", adjustment.held_lower())?;
let held_upper = adj_field(self, "held upper bound", adjustment.held_upper())?;
let incoming = adj_field(self, "incoming", adjustment.incoming())?;
let incoming_lower = adj_field(self, "incoming lower bound", adjustment.incoming_lower())?;
let incoming_upper = adj_field(self, "incoming upper bound", adjustment.incoming_upper())?;
Ok(AdjustmentRequestView {
asset,
balance,
balance_lower,
balance_upper,
held,
held_lower,
held_upper,
incoming,
incoming_lower,
incoming_upper,
})
}
pub(super) fn apply_account_adjustment_impl<AccountAdjustment>(
&self,
account_control: Option<AccountControl<<Sync as SyncMode>::StorageLockingPolicyFactory>>,
account_id: AccountId,
adjustment: &AccountAdjustment,
mutations: &mut Mutations,
) -> Result<Vec<AccountOutcomeEntry>, Rejects>
where
AccountAdjustment: HasBalanceAsset
+ HasAccountAdjustmentBalance
+ HasAccountAdjustmentBalanceLowerBound
+ HasAccountAdjustmentBalanceUpperBound
+ HasAccountAdjustmentHeld
+ HasAccountAdjustmentHeldLowerBound
+ HasAccountAdjustmentHeldUpperBound
+ HasAccountAdjustmentIncoming
+ HasAccountAdjustmentIncomingLowerBound
+ HasAccountAdjustmentIncomingUpperBound,
<<Sync as SyncMode>::StorageLockingPolicyFactory as crate::storage::LockingPolicyFactory>::Policy: 'static,
{
let request = self.read_adjustment_request(adjustment)?;
if request.balance.is_none() && request.held.is_none() && request.incoming.is_none() {
return Ok(Vec::new());
}
let key = (account_id, request.asset.clone());
let (new, available_delta, held_delta, incoming_delta) = self.holdings.with_mut_or_insert(
key.clone(),
Holdings::zero,
|slot, _is_new| -> Result<(Holdings, PositionSize, PositionSize, PositionSize), Rejects> {
let current = *slot;
let mut new = current;
if let Some(amount) = request.balance {
new = new
.apply_adjustment(AdjustmentTarget::Available, amount)
.map_err(|_| {
Rejects::from(arithmetic_overflow_reject(
Self::NAME,
RejectScope::Account,
format!(
"account adjustment overflow: account {account_id}, \
asset {asset}, field balance, current {val}, applied {amount}",
asset = request.asset,
val = new.available(),
),
))
})?;
if !new.available_within_bounds(request.balance_lower, request.balance_upper) {
return Err(Rejects::from(account_adjustment_bounds_exceeded_reject(
Self::NAME,
account_id,
&request.asset,
"balance",
new.available(),
request.balance_lower,
request.balance_upper,
)));
}
}
if let Some(amount) = request.held {
new = new
.apply_adjustment(AdjustmentTarget::Held, amount)
.map_err(|_| {
Rejects::from(arithmetic_overflow_reject(
Self::NAME,
RejectScope::Account,
format!(
"account adjustment overflow: account {account_id}, \
asset {asset}, field held, current {val}, applied {amount}",
asset = request.asset,
val = new.held(),
),
))
})?;
if !new.held_within_bounds(request.held_lower, request.held_upper) {
return Err(Rejects::from(account_adjustment_bounds_exceeded_reject(
Self::NAME,
account_id,
&request.asset,
"held",
new.held(),
request.held_lower,
request.held_upper,
)));
}
}
if let Some(amount) = request.incoming {
new = new
.apply_adjustment(AdjustmentTarget::Incoming, amount)
.map_err(|_| {
Rejects::from(arithmetic_overflow_reject(
Self::NAME,
RejectScope::Account,
format!(
"account adjustment overflow: account {account_id}, \
asset {asset}, field incoming, current {val}, applied {amount}",
asset = request.asset,
val = new.incoming(),
),
))
})?;
if !new.incoming_within_bounds(request.incoming_lower, request.incoming_upper) {
return Err(Rejects::from(account_adjustment_bounds_exceeded_reject(
Self::NAME,
account_id,
&request.asset,
"incoming",
new.incoming(),
request.incoming_lower,
request.incoming_upper,
)));
}
}
let available_delta = new
.available()
.checked_sub(current.available())
.map_err(|_| {
Rejects::from(arithmetic_overflow_reject(
Self::NAME,
RejectScope::Account,
format!(
"account adjustment delta overflow: account {account_id}, \
asset {asset}, field balance",
asset = request.asset,
),
))
})?;
let held_delta = new
.held()
.checked_sub(current.held())
.map_err(|_| {
Rejects::from(arithmetic_overflow_reject(
Self::NAME,
RejectScope::Account,
format!(
"account adjustment delta overflow: account {account_id}, \
asset {asset}, field held",
asset = request.asset,
),
))
})?;
let incoming_delta = new
.incoming()
.checked_sub(current.incoming())
.map_err(|_| {
Rejects::from(arithmetic_overflow_reject(
Self::NAME,
RejectScope::Account,
format!(
"account adjustment delta overflow: account {account_id}, \
asset {asset}, field incoming",
asset = request.asset,
),
))
})?;
*slot = new; Ok((new, available_delta, held_delta, incoming_delta))
},
)?;
if new.is_zero() {
self.holdings.remove_if_zero(&key);
}
self.register_adjustment_rollback(
mutations,
account_control,
key,
available_delta,
held_delta,
incoming_delta,
);
let balance_outcome = request.balance.map(|_| OutcomeAmount {
delta: available_delta,
absolute: new.available(),
});
let held_outcome = request.held.map(|_| OutcomeAmount {
delta: held_delta,
absolute: new.held(),
});
let incoming_outcome = request.incoming.map(|_| OutcomeAmount {
delta: incoming_delta,
absolute: new.incoming(),
});
Ok(vec![AccountOutcomeEntry {
asset: request.asset,
balance: balance_outcome,
held: held_outcome,
incoming: incoming_outcome,
}])
}
}