use std::collections::HashSet;
use std::fmt::{Display, Formatter};
use std::marker::PhantomData;
use super::engine::{Engine, EngineInner};
use super::engine_trait::EngineTraitOf;
use super::sync_mode::{AccountSync, FullSync, LocalSync, SyncMode};
use super::{AccountCurrencies, AccountGroups, BlockedAccounts, ConfigRegistry};
use crate::marketdata::MarketDataSync;
use crate::pretrade::PreTradePolicy;
use crate::storage::{LockingPolicyFactory, StorageBuilder};
mod market_data_sync_of {
use super::{AccountSync, FullSync, LocalSync, MarketDataSync};
pub trait MarketDataSyncOf {
type Sync: MarketDataSync;
fn market_data_sync() -> Self::Sync;
}
impl MarketDataSyncOf for LocalSync {
type Sync = LocalSync;
fn market_data_sync() -> LocalSync {
LocalSync
}
}
impl MarketDataSyncOf for FullSync {
type Sync = FullSync;
fn market_data_sync() -> FullSync {
FullSync
}
}
impl MarketDataSyncOf for AccountSync {
type Sync = FullSync;
fn market_data_sync() -> FullSync {
FullSync
}
}
}
use market_data_sync_of::MarketDataSyncOf;
pub trait IntoPolicyObject<Target: ?Sized>: 'static {
fn into_policy_object(self) -> Box<Target>;
}
impl<
Order: 'static,
ExecutionReport: 'static,
AccountAdjustment: 'static,
Sync: SyncMode,
PreTradePolicy: crate::pretrade::PreTradePolicy<Order, ExecutionReport, AccountAdjustment, Sync> + 'static,
>
IntoPolicyObject<
dyn crate::pretrade::PreTradePolicy<Order, ExecutionReport, AccountAdjustment, Sync>,
> for PreTradePolicy
{
fn into_policy_object(
self,
) -> Box<dyn crate::pretrade::PreTradePolicy<Order, ExecutionReport, AccountAdjustment, Sync>>
{
Box::new(self)
}
}
impl<
Order: 'static,
ExecutionReport: 'static,
AccountAdjustment: 'static,
Sync: SyncMode,
PreTradePolicy: crate::pretrade::PreTradePolicy<Order, ExecutionReport, AccountAdjustment, Sync>
+ Send
+ 'static,
>
IntoPolicyObject<
dyn crate::pretrade::PreTradePolicy<Order, ExecutionReport, AccountAdjustment, Sync> + Send,
> for PreTradePolicy
{
fn into_policy_object(
self,
) -> Box<
dyn crate::pretrade::PreTradePolicy<Order, ExecutionReport, AccountAdjustment, Sync> + Send,
> {
Box::new(self)
}
}
impl<
Order: 'static,
ExecutionReport: 'static,
AccountAdjustment: 'static,
Sync: SyncMode,
PreTradePolicy: crate::pretrade::PreTradePolicy<Order, ExecutionReport, AccountAdjustment, Sync>
+ Send
+ std::marker::Sync
+ 'static,
>
IntoPolicyObject<
dyn crate::pretrade::PreTradePolicy<Order, ExecutionReport, AccountAdjustment, Sync>
+ Send
+ std::marker::Sync,
> for PreTradePolicy
{
fn into_policy_object(
self,
) -> Box<
dyn crate::pretrade::PreTradePolicy<Order, ExecutionReport, AccountAdjustment, Sync>
+ Send
+ std::marker::Sync,
> {
Box::new(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum EngineBuildError {
DuplicatePolicyName { name: String },
DuplicatePolicyGroupId {
policy_group_id: crate::core::PolicyGroupId,
},
}
impl Display for EngineBuildError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::DuplicatePolicyName { name } => {
write!(formatter, "duplicate policy name: {name}")
}
Self::DuplicatePolicyGroupId { policy_group_id } => {
write!(
formatter,
"duplicate non-default policy_group_id: {value}",
value = policy_group_id.value()
)
}
}
}
}
impl std::error::Error for EngineBuildError {}
pub struct EngineBuilder<Order: 'static, ExecutionReport: 'static, AccountAdjustment: 'static> {
_marker: PhantomData<(Order, ExecutionReport, AccountAdjustment)>,
}
impl<Order: 'static, ExecutionReport: 'static, AccountAdjustment: 'static>
EngineBuilder<Order, ExecutionReport, AccountAdjustment>
{
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
pub fn sync<Sync>(
self,
sync: Sync,
) -> SyncedEngineBuilder<Order, ExecutionReport, AccountAdjustment, Sync>
where
Sync: SyncMode,
{
let storage_builder = StorageBuilder::new(sync.storage_locking_policy_factory());
let blocked_accounts =
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::new_shared(
BlockedAccounts::new(&storage_builder),
);
let account_groups =
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::new_shared(
AccountGroups::new(&storage_builder),
);
let account_currencies =
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::new_shared(
AccountCurrencies::new(&storage_builder),
);
SyncedEngineBuilder {
pre_trade_policies: Vec::new(),
storage_builder,
blocked_accounts,
account_groups,
account_currencies,
config_entries: Vec::new(),
_marker: PhantomData,
}
}
pub fn full_sync(
self,
) -> SyncedEngineBuilder<Order, ExecutionReport, AccountAdjustment, FullSync> {
self.sync(FullSync)
}
pub fn no_sync(
self,
) -> SyncedEngineBuilder<Order, ExecutionReport, AccountAdjustment, LocalSync> {
self.sync(LocalSync)
}
pub fn account_sync(
self,
) -> SyncedEngineBuilder<Order, ExecutionReport, AccountAdjustment, AccountSync> {
self.sync(AccountSync)
}
}
pub struct SyncedEngineBuilder<
Order: 'static,
ExecutionReport: 'static,
AccountAdjustment: 'static,
Sync: SyncMode,
> {
pre_trade_policies: Vec<
Box<<Sync as SyncMode>::PreTradePolicyObject<Order, ExecutionReport, AccountAdjustment>>,
>,
storage_builder: StorageBuilder<<Sync as SyncMode>::StorageLockingPolicyFactory>,
blocked_accounts:
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::Shared<
BlockedAccounts<<Sync as SyncMode>::StorageLockingPolicyFactory>,
>,
account_groups:
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::Shared<
AccountGroups<<Sync as SyncMode>::StorageLockingPolicyFactory>,
>,
account_currencies:
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::Shared<
AccountCurrencies<<Sync as SyncMode>::StorageLockingPolicyFactory>,
>,
config_entries: Vec<(
String,
crate::core::ConfigEntry<<Sync as SyncMode>::StorageLockingPolicyFactory>,
)>,
_marker: PhantomData<(Order, ExecutionReport, AccountAdjustment)>,
}
impl<Order: 'static, ExecutionReport: 'static, AccountAdjustment: 'static, Sync>
SyncedEngineBuilder<Order, ExecutionReport, AccountAdjustment, Sync>
where
Sync: SyncMode,
{
pub fn storage_builder(
&self,
) -> &StorageBuilder<<Sync as SyncMode>::StorageLockingPolicyFactory> {
&self.storage_builder
}
}
impl<Order: 'static, ExecutionReport: 'static, AccountAdjustment: 'static, Sync>
SyncedEngineBuilder<Order, ExecutionReport, AccountAdjustment, Sync>
where
Sync: SyncMode + MarketDataSyncOf,
{
pub fn market_data(
&self,
default_ttl: crate::QuoteTtl,
) -> crate::marketdata::MarketDataBuilder<<Sync as MarketDataSyncOf>::Sync> {
crate::marketdata::MarketDataBuilder::with_sync(Sync::market_data_sync(), default_ttl)
}
}
impl<Order: 'static, ExecutionReport: 'static, AccountAdjustment: 'static, Sync>
SyncedEngineBuilder<Order, ExecutionReport, AccountAdjustment, Sync>
where
Sync: SyncMode,
{
pub fn pre_trade<Policy>(
mut self,
policy: Policy,
) -> ReadyEngineBuilder<Order, ExecutionReport, AccountAdjustment, Sync>
where
Policy: IntoPolicyObject<
<Sync as SyncMode>::PreTradePolicyObject<Order, ExecutionReport, AccountAdjustment>,
>,
{
let obj = policy.into_policy_object();
if let Some(entry) = obj.built_in_config_entry() {
self.config_entries.push((obj.name().to_owned(), entry));
}
self.pre_trade_policies.push(obj);
ReadyEngineBuilder {
pre_trade_policies: self.pre_trade_policies,
storage_builder: self.storage_builder,
blocked_accounts: self.blocked_accounts,
account_groups: self.account_groups,
account_currencies: self.account_currencies,
config_entries: self.config_entries,
_marker: PhantomData,
}
}
}
pub struct ReadyEngineBuilder<
Order: 'static,
ExecutionReport: 'static,
AccountAdjustment: 'static,
Sync: SyncMode,
> {
pre_trade_policies: Vec<
Box<<Sync as SyncMode>::PreTradePolicyObject<Order, ExecutionReport, AccountAdjustment>>,
>,
storage_builder: StorageBuilder<<Sync as SyncMode>::StorageLockingPolicyFactory>,
blocked_accounts:
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::Shared<
BlockedAccounts<<Sync as SyncMode>::StorageLockingPolicyFactory>,
>,
account_groups:
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::Shared<
AccountGroups<<Sync as SyncMode>::StorageLockingPolicyFactory>,
>,
account_currencies:
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::Shared<
AccountCurrencies<<Sync as SyncMode>::StorageLockingPolicyFactory>,
>,
config_entries: Vec<(
String,
crate::core::ConfigEntry<<Sync as SyncMode>::StorageLockingPolicyFactory>,
)>,
_marker: PhantomData<(Order, ExecutionReport, AccountAdjustment)>,
}
impl<Order: 'static, ExecutionReport: 'static, AccountAdjustment: 'static, Sync>
ReadyEngineBuilder<Order, ExecutionReport, AccountAdjustment, Sync>
where
Sync: SyncMode,
{
pub fn storage_builder(
&self,
) -> &StorageBuilder<<Sync as SyncMode>::StorageLockingPolicyFactory> {
&self.storage_builder
}
}
impl<Order: 'static, ExecutionReport: 'static, AccountAdjustment: 'static, Sync>
ReadyEngineBuilder<Order, ExecutionReport, AccountAdjustment, Sync>
where
Sync: SyncMode + MarketDataSyncOf,
{
pub fn market_data(
&self,
default_ttl: crate::QuoteTtl,
) -> crate::marketdata::MarketDataBuilder<<Sync as MarketDataSyncOf>::Sync> {
crate::marketdata::MarketDataBuilder::with_sync(Sync::market_data_sync(), default_ttl)
}
}
impl<Order: 'static, ExecutionReport: 'static, AccountAdjustment: 'static, Sync>
ReadyEngineBuilder<Order, ExecutionReport, AccountAdjustment, Sync>
where
Sync: SyncMode,
{
pub fn pre_trade<Policy>(mut self, policy: Policy) -> Self
where
Policy: IntoPolicyObject<
<Sync as SyncMode>::PreTradePolicyObject<Order, ExecutionReport, AccountAdjustment>,
>,
{
let obj = policy.into_policy_object();
if let Some(entry) = obj.built_in_config_entry() {
self.config_entries.push((obj.name().to_owned(), entry));
}
self.pre_trade_policies.push(obj);
self
}
pub fn build(
self,
) -> Result<
Engine<EngineTraitOf<Order, ExecutionReport, AccountAdjustment, Sync>>,
EngineBuildError,
> {
ensure_unique_policy_names(self.pre_trade_policies.iter().map(|p| p.name()))?;
ensure_unique_group_ids(self.pre_trade_policies.iter().map(|p| p.policy_group_id()))?;
let config_registry =
<<Sync as SyncMode>::StorageLockingPolicyFactory as LockingPolicyFactory>::new_shared(
ConfigRegistry::from_entries(self.config_entries.into_iter().collect()),
);
Ok(Engine::from_inner(<Sync as SyncMode>::new_strong(
EngineInner {
pre_trade_policies: self.pre_trade_policies,
blocked_accounts: self.blocked_accounts,
account_groups: self.account_groups,
account_currencies: self.account_currencies,
config_registry,
},
)))
}
}
fn ensure_unique_policy_names<'a>(
names: impl Iterator<Item = &'a str>,
) -> Result<(), EngineBuildError> {
let mut unique = HashSet::new();
for name in names {
if !unique.insert(name.to_owned()) {
return Err(EngineBuildError::DuplicatePolicyName {
name: name.to_owned(),
});
}
}
Ok(())
}
fn ensure_unique_group_ids(
group_ids: impl Iterator<Item = crate::core::PolicyGroupId>,
) -> Result<(), EngineBuildError> {
let mut unique = HashSet::new();
for policy_group_id in group_ids {
if policy_group_id == crate::core::DEFAULT_POLICY_GROUP_ID {
continue;
}
if !unique.insert(policy_group_id) {
return Err(EngineBuildError::DuplicatePolicyGroupId { policy_group_id });
}
}
Ok(())
}