nautilus_system/
trader.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Central orchestrator for managing actors, strategies, and execution algorithms.
17//!
18//! The `Trader` component serves as the primary coordination layer between the kernel
19//! and individual trading components. It manages component lifecycles, provides
20//! unique identification, and coordinates with system engines.
21
22use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
23
24use nautilus_common::{
25    actor::DataActor,
26    cache::Cache,
27    clock::{Clock, TestClock},
28    component::{
29        Component, dispose_component, register_component_actor, reset_component, start_component,
30        stop_component,
31    },
32    enums::{ComponentState, ComponentTrigger, Environment},
33};
34use nautilus_core::{UUID4, UnixNanos};
35use nautilus_model::identifiers::{ActorId, ComponentId, ExecAlgorithmId, StrategyId, TraderId};
36
37/// Central orchestrator for managing trading components.
38///
39/// The `Trader` manages the lifecycle and coordination of actors, strategies,
40/// and execution algorithms within the trading system. It provides component
41/// registration, state management, and integration with system engines.
42pub struct Trader {
43    /// The unique trader identifier.
44    pub trader_id: TraderId,
45    /// The unique instance identifier.
46    pub instance_id: UUID4,
47    /// The trading environment context.
48    pub environment: Environment,
49    /// Component state for lifecycle management.
50    state: ComponentState,
51    /// System clock for timestamping.
52    clock: Rc<RefCell<dyn Clock>>,
53    /// System cache for data storage.
54    cache: Rc<RefCell<Cache>>,
55    /// Registered actor IDs (actors stored in global registry).
56    actor_ids: Vec<ActorId>,
57    /// Registered strategies by strategy ID.
58    strategies: HashMap<StrategyId, Box<dyn Component>>,
59    /// Registered execution algorithms by algorithm ID.
60    exec_algorithms: HashMap<ExecAlgorithmId, Box<dyn Component>>,
61    /// Component clocks for individual components.
62    clocks: HashMap<ComponentId, Rc<RefCell<dyn Clock>>>, // TODO: TBD global clock?
63    /// Timestamp when the trader was created.
64    ts_created: UnixNanos,
65    /// Timestamp when the trader was last started.
66    ts_started: Option<UnixNanos>,
67    /// Timestamp when the trader was last stopped.
68    ts_stopped: Option<UnixNanos>,
69}
70
71impl Debug for Trader {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        write!(f, "{:?}", stringify!(TraderId)) // TODO
74    }
75}
76
77impl Trader {
78    /// Creates a new [`Trader`] instance.
79    #[must_use]
80    pub fn new(
81        trader_id: TraderId,
82        instance_id: UUID4,
83        environment: Environment,
84        clock: Rc<RefCell<dyn Clock>>,
85        cache: Rc<RefCell<Cache>>,
86    ) -> Self {
87        let ts_created = clock.borrow().timestamp_ns();
88
89        Self {
90            trader_id,
91            instance_id,
92            environment,
93            state: ComponentState::PreInitialized,
94            clock,
95            cache,
96            actor_ids: Vec::new(),
97            strategies: HashMap::new(),
98            exec_algorithms: HashMap::new(),
99            clocks: HashMap::new(),
100            ts_created,
101            ts_started: None,
102            ts_stopped: None,
103        }
104    }
105
106    /// Returns the trader ID.
107    #[must_use]
108    pub const fn trader_id(&self) -> TraderId {
109        self.trader_id
110    }
111
112    /// Returns the instance ID.
113    #[must_use]
114    pub const fn instance_id(&self) -> UUID4 {
115        self.instance_id
116    }
117
118    /// Returns the trading environment.
119    #[must_use]
120    pub const fn environment(&self) -> Environment {
121        self.environment
122    }
123
124    /// Returns the current component state.
125    #[must_use]
126    pub const fn state(&self) -> ComponentState {
127        self.state
128    }
129
130    /// Returns the timestamp when the trader was created (UNIX nanoseconds).
131    #[must_use]
132    pub const fn ts_created(&self) -> UnixNanos {
133        self.ts_created
134    }
135
136    /// Returns the timestamp when the trader was last started (UNIX nanoseconds).
137    #[must_use]
138    pub const fn ts_started(&self) -> Option<UnixNanos> {
139        self.ts_started
140    }
141
142    /// Returns the timestamp when the trader was last stopped (UNIX nanoseconds).
143    #[must_use]
144    pub const fn ts_stopped(&self) -> Option<UnixNanos> {
145        self.ts_stopped
146    }
147
148    /// Returns the number of registered actors.
149    #[must_use]
150    pub const fn actor_count(&self) -> usize {
151        self.actor_ids.len()
152    }
153
154    /// Returns the number of registered strategies.
155    #[must_use]
156    pub fn strategy_count(&self) -> usize {
157        self.strategies.len()
158    }
159
160    /// Returns the number of registered execution algorithms.
161    #[must_use]
162    pub fn exec_algorithm_count(&self) -> usize {
163        self.exec_algorithms.len()
164    }
165
166    /// Returns the total number of registered components.
167    #[must_use]
168    pub fn component_count(&self) -> usize {
169        self.actor_ids.len() + self.strategies.len() + self.exec_algorithms.len()
170    }
171
172    /// Returns a list of all registered actor IDs.
173    #[must_use]
174    pub fn actor_ids(&self) -> Vec<ActorId> {
175        self.actor_ids.clone()
176    }
177
178    /// Returns a list of all registered strategy IDs.
179    #[must_use]
180    pub fn strategy_ids(&self) -> Vec<StrategyId> {
181        self.strategies.keys().copied().collect()
182    }
183
184    /// Returns a list of all registered execution algorithm IDs.
185    #[must_use]
186    pub fn exec_algorithm_ids(&self) -> Vec<ExecAlgorithmId> {
187        self.exec_algorithms.keys().copied().collect()
188    }
189
190    /// Creates a clock for a component.
191    ///
192    /// Creates a test clock in backtest environment, otherwise returns a reference
193    /// to the system clock.
194    fn create_component_clock(&self) -> Rc<RefCell<dyn Clock>> {
195        match self.environment {
196            Environment::Backtest => {
197                // Create individual test clock for component in backtest
198                Rc::new(RefCell::new(TestClock::new()))
199            }
200            Environment::Live | Environment::Sandbox => {
201                // Share system clock in live environments
202                self.clock.clone()
203            }
204        }
205    }
206
207    /// Adds an actor to the trader.
208    ///
209    /// # Errors
210    ///
211    /// Returns an error if:
212    /// - The trader is not in a valid state for adding components.
213    /// - An actor with the same ID is already registered.
214    pub fn add_actor<T>(&mut self, actor: T) -> anyhow::Result<()>
215    where
216        T: DataActor + Component + Debug + 'static,
217    {
218        self.validate_component_registration()?;
219
220        let actor_id = actor.actor_id();
221
222        // Check for duplicate registration
223        if self.actor_ids.contains(&actor_id) {
224            anyhow::bail!("Actor '{actor_id}' is already registered");
225        }
226
227        let clock = self.create_component_clock();
228        let component_id = ComponentId::new(actor_id.inner().as_str());
229        self.clocks.insert(component_id, clock.clone());
230
231        let mut actor_mut = actor;
232        actor_mut.register(self.trader_id, clock, self.cache.clone())?;
233
234        self.add_registered_actor(actor_mut)
235    }
236
237    /// Adds an actor to the trader using a factory function.
238    ///
239    /// The factory function is called at registration time to create the actor,
240    /// avoiding cloning issues with non-cloneable actor types.
241    ///
242    /// # Errors
243    ///
244    /// Returns an error if:
245    /// - The factory function fails to create the actor.
246    /// - The trader is not in a valid state for adding components.
247    /// - An actor with the same ID is already registered.
248    pub fn add_actor_from_factory<F, T>(&mut self, factory: F) -> anyhow::Result<()>
249    where
250        F: FnOnce() -> anyhow::Result<T>,
251        T: DataActor + Component + Debug + 'static,
252    {
253        let actor = factory()?;
254
255        self.add_actor(actor)
256    }
257
258    /// Adds an already registered actor to the trader's component registry.
259    ///
260    /// # Errors
261    ///
262    /// Returns an error if the actor cannot be registered in the component registry.
263    pub fn add_registered_actor<T>(&mut self, actor: T) -> anyhow::Result<()>
264    where
265        T: DataActor + Component + Debug + 'static,
266    {
267        let actor_id = actor.actor_id();
268        let mem_addr = actor.mem_address();
269
270        // Register in both component and actor registries (this consumes the actor)
271        register_component_actor(actor);
272
273        // Store actor ID for lifecycle management
274        self.actor_ids.push(actor_id);
275
276        log::info!(
277            "Registered '{actor_id}' at mem_addr {mem_addr} with trader {}",
278            self.trader_id
279        );
280
281        Ok(())
282    }
283
284    /// Adds an actor ID to the trader's lifecycle management without consuming the actor.
285    ///
286    /// This is useful when the actor is already registered in the global component registry
287    /// but the trader needs to track it for lifecycle management. The caller is responsible
288    /// for ensuring the actor is properly registered in the global registries.
289    ///
290    /// # Errors
291    ///
292    /// Returns an error if the actor ID is already tracked by this trader.
293    pub fn add_actor_id_for_lifecycle(&mut self, actor_id: ActorId) -> anyhow::Result<()> {
294        // Check for duplicate registration
295        if self.actor_ids.contains(&actor_id) {
296            anyhow::bail!("Actor '{actor_id}' is already tracked by trader");
297        }
298
299        // Store actor ID for lifecycle management
300        self.actor_ids.push(actor_id);
301
302        log::debug!(
303            "Added actor ID '{actor_id}' to trader {} for lifecycle management",
304            self.trader_id
305        );
306
307        Ok(())
308    }
309
310    /// Adds a strategy to the trader.
311    ///
312    /// # Errors
313    ///
314    /// Returns an error if:
315    /// - The trader is not in a valid state for adding components
316    /// - A strategy with the same ID is already registered
317    pub fn add_strategy(&mut self, mut strategy: Box<dyn Component>) -> anyhow::Result<()> {
318        self.validate_component_registration()?;
319
320        let strategy_id = StrategyId::from(strategy.component_id().inner().as_str());
321
322        // Check for duplicate registration
323        if self.strategies.contains_key(&strategy_id) {
324            anyhow::bail!("Strategy '{strategy_id}' is already registered");
325        }
326
327        let clock = self.create_component_clock();
328        let component_id = strategy.component_id();
329        self.clocks.insert(component_id, clock.clone());
330
331        strategy.register(self.trader_id, clock, self.cache.clone())?;
332
333        self.strategies.insert(strategy_id, strategy);
334        log::info!(
335            "Registered strategy '{strategy_id}' with trader {}",
336            self.trader_id
337        );
338
339        Ok(())
340    }
341
342    /// Adds an execution algorithm to the trader.
343    ///
344    /// # Errors
345    ///
346    /// Returns an error if:
347    /// - The trader is not in a valid state for adding components
348    /// - An execution algorithm with the same ID is already registered
349    pub fn add_exec_algorithm(
350        &mut self,
351        mut exec_algorithm: Box<dyn Component>,
352    ) -> anyhow::Result<()> {
353        self.validate_component_registration()?;
354
355        let exec_algorithm_id =
356            ExecAlgorithmId::from(exec_algorithm.component_id().inner().as_str());
357
358        // Check for duplicate registration
359        if self.exec_algorithms.contains_key(&exec_algorithm_id) {
360            anyhow::bail!("Execution algorithm '{exec_algorithm_id}' is already registered");
361        }
362
363        let clock = self.create_component_clock();
364        let component_id = exec_algorithm.component_id();
365        self.clocks.insert(component_id, clock.clone());
366
367        exec_algorithm.register(self.trader_id, clock, self.cache.clone())?;
368
369        self.exec_algorithms
370            .insert(exec_algorithm_id, exec_algorithm);
371        log::info!(
372            "Registered execution algorithm '{exec_algorithm_id}' with trader {}",
373            self.trader_id
374        );
375
376        Ok(())
377    }
378
379    /// Validates that the trader is in a valid state for component registration.
380    fn validate_component_registration(&self) -> anyhow::Result<()> {
381        match self.state {
382            ComponentState::PreInitialized | ComponentState::Ready | ComponentState::Stopped => {
383                Ok(())
384            }
385            ComponentState::Running => {
386                anyhow::bail!("Cannot add components while trader is running")
387            }
388            ComponentState::Disposed => {
389                anyhow::bail!("Cannot add components to disposed trader")
390            }
391            _ => anyhow::bail!("Cannot add components in current state: {}", self.state),
392        }
393    }
394
395    /// Starts all registered components.
396    ///
397    /// # Errors
398    ///
399    /// Returns an error if any component fails to start.
400    pub fn start_components(&mut self) -> anyhow::Result<()> {
401        // Start actors (retrieved from global registry)
402        for actor_id in &self.actor_ids {
403            log::debug!("Starting actor '{actor_id}'");
404            start_component(&actor_id.inner())?;
405        }
406
407        for strategy_id in &mut self.strategies.keys() {
408            log::debug!("Starting strategy '{strategy_id}'");
409            // strategy.start()?; // TODO: TBD
410        }
411
412        for exec_algorithm_id in &mut self.exec_algorithms.keys() {
413            log::debug!("Starting execution algorithm '{exec_algorithm_id}'");
414            // exec_algorithm.start()?;  // TODO: TBD
415        }
416
417        Ok(())
418    }
419
420    /// Stops all registered components.
421    ///
422    /// # Errors
423    ///
424    /// Returns an error if any component fails to stop.
425    pub fn stop_components(&mut self) -> anyhow::Result<()> {
426        for actor_id in &self.actor_ids {
427            log::debug!("Stopping actor '{actor_id}'");
428            stop_component(&actor_id.inner())?;
429        }
430
431        for exec_algorithm_id in &mut self.exec_algorithms.keys() {
432            log::debug!("Stopping execution algorithm '{exec_algorithm_id}'");
433            // exec_algorithm.stop()?;  // TODO: TBD
434        }
435
436        for strategy_id in &mut self.strategies.keys() {
437            log::debug!("Stopping strategy '{strategy_id}'");
438            // strategy.stop()?;  // TODO: TBD
439        }
440
441        Ok(())
442    }
443
444    /// Resets all registered components.
445    ///
446    /// # Errors
447    ///
448    /// Returns an error if any component fails to reset.
449    pub fn reset_components(&mut self) -> anyhow::Result<()> {
450        // Reset actors (retrieved from global registry)
451        for actor_id in &self.actor_ids {
452            log::debug!("Resetting actor '{actor_id}'");
453            reset_component(&actor_id.inner())?;
454        }
455
456        for strategy_id in &mut self.strategies.keys() {
457            log::debug!("Resetting strategy '{strategy_id}'");
458            // strategy.reset()?;  // TODO: TBD
459        }
460
461        for exec_algorithm_id in &mut self.exec_algorithms.keys() {
462            log::debug!("Resetting execution algorithm '{exec_algorithm_id}'");
463            // exec_algorithm.reset()?;  // TODO: TBD
464        }
465
466        Ok(())
467    }
468
469    /// Disposes of all registered components.
470    ///
471    /// # Errors
472    ///
473    /// Returns an error if any component fails to dispose.
474    pub fn dispose_components(&mut self) -> anyhow::Result<()> {
475        // Dispose actors (retrieved from global registry)
476        for actor_id in &self.actor_ids {
477            log::debug!("Disposing actor '{actor_id}'");
478            dispose_component(&actor_id.inner())?;
479        }
480
481        for strategy_id in &mut self.strategies.keys() {
482            log::debug!("Disposing strategy '{strategy_id}'");
483            // strategy.dispose()?;  // TODO: TBD
484        }
485
486        for exec_algorithm_id in &mut self.exec_algorithms.keys() {
487            log::debug!("Disposing execution algorithm '{exec_algorithm_id}'");
488            // exec_algorithm.dispose()?;  // TODO: TBD
489        }
490
491        self.actor_ids.clear();
492        self.strategies.clear();
493        self.exec_algorithms.clear();
494        self.clocks.clear();
495
496        Ok(())
497    }
498
499    /// Initializes the trader, transitioning from `PreInitialized` to `Ready` state.
500    ///
501    /// This method must be called before starting the trader.
502    ///
503    /// # Errors
504    ///
505    /// Returns an error if the trader cannot be initialized from its current state.
506    pub fn initialize(&mut self) -> anyhow::Result<()> {
507        let new_state = self.state.transition(&ComponentTrigger::Initialize)?;
508        self.state = new_state;
509
510        Ok(())
511    }
512
513    fn on_start(&mut self) -> anyhow::Result<()> {
514        self.start_components()?;
515
516        // Transition to running state
517        self.ts_started = Some(self.clock.borrow().timestamp_ns());
518
519        Ok(())
520    }
521
522    fn on_stop(&mut self) -> anyhow::Result<()> {
523        self.stop_components()?;
524
525        self.ts_stopped = Some(self.clock.borrow().timestamp_ns());
526
527        Ok(())
528    }
529
530    fn on_reset(&mut self) -> anyhow::Result<()> {
531        self.reset_components()?;
532
533        self.ts_started = None;
534        self.ts_stopped = None;
535
536        Ok(())
537    }
538
539    fn on_dispose(&mut self) -> anyhow::Result<()> {
540        if self.is_running() {
541            self.stop()?;
542        }
543
544        self.dispose_components()?;
545
546        Ok(())
547    }
548}
549
550impl Component for Trader {
551    fn component_id(&self) -> ComponentId {
552        ComponentId::new(format!("Trader-{}", self.trader_id))
553    }
554
555    fn state(&self) -> ComponentState {
556        self.state
557    }
558
559    fn transition_state(&mut self, trigger: ComponentTrigger) -> anyhow::Result<()> {
560        self.state = self.state.transition(&trigger)?;
561        log::info!("{}", self.state.variant_name());
562        Ok(())
563    }
564
565    fn register(
566        &mut self,
567        _trader_id: TraderId,
568        _clock: Rc<RefCell<dyn Clock>>,
569        _cache: Rc<RefCell<Cache>>,
570    ) -> anyhow::Result<()> {
571        anyhow::bail!("Trader cannot register with itself")
572    }
573
574    fn on_start(&mut self) -> anyhow::Result<()> {
575        Self::on_start(self)
576    }
577
578    fn on_stop(&mut self) -> anyhow::Result<()> {
579        Self::on_stop(self)
580    }
581
582    fn on_reset(&mut self) -> anyhow::Result<()> {
583        Self::on_reset(self)
584    }
585
586    fn on_dispose(&mut self) -> anyhow::Result<()> {
587        Self::on_dispose(self)
588    }
589}
590
591////////////////////////////////////////////////////////////////////////////////
592// Tests
593////////////////////////////////////////////////////////////////////////////////
594
595#[cfg(test)]
596mod tests {
597    use std::{
598        cell::RefCell,
599        ops::{Deref, DerefMut},
600        rc::Rc,
601    };
602
603    use nautilus_common::{
604        actor::{DataActorCore, data_actor::DataActorConfig},
605        cache::Cache,
606        clock::TestClock,
607        enums::{ComponentState, Environment},
608        msgbus::MessageBus,
609    };
610    use nautilus_core::UUID4;
611    use nautilus_data::engine::{DataEngine, config::DataEngineConfig};
612    use nautilus_execution::engine::{ExecutionEngine, config::ExecutionEngineConfig};
613    use nautilus_model::identifiers::{ActorId, ComponentId, TraderId};
614    use nautilus_portfolio::portfolio::Portfolio;
615    use nautilus_risk::engine::{RiskEngine, config::RiskEngineConfig};
616    use rstest::rstest;
617
618    use super::*;
619
620    // Simple DataActor wrapper for testing
621    #[derive(Debug)]
622    struct TestDataActor {
623        core: DataActorCore,
624    }
625
626    impl TestDataActor {
627        fn new(config: DataActorConfig) -> Self {
628            Self {
629                core: DataActorCore::new(config),
630            }
631        }
632    }
633
634    impl DataActor for TestDataActor {}
635
636    impl Deref for TestDataActor {
637        type Target = DataActorCore;
638        fn deref(&self) -> &Self::Target {
639            &self.core
640        }
641    }
642
643    impl DerefMut for TestDataActor {
644        fn deref_mut(&mut self) -> &mut Self::Target {
645            &mut self.core
646        }
647    }
648
649    // Mock component for testing
650    #[derive(Debug)]
651    struct MockComponent {
652        id: ComponentId,
653        state: ComponentState,
654    }
655
656    impl MockComponent {
657        fn new(id: &str) -> Self {
658            Self {
659                id: ComponentId::from(id),
660                state: ComponentState::PreInitialized,
661            }
662        }
663    }
664
665    impl Component for MockComponent {
666        fn component_id(&self) -> ComponentId {
667            self.id
668        }
669
670        fn state(&self) -> ComponentState {
671            self.state
672        }
673
674        fn transition_state(&mut self, trigger: ComponentTrigger) -> anyhow::Result<()> {
675            self.state = self.state.transition(&trigger)?;
676            log::info!("{}", self.state.variant_name());
677            Ok(())
678        }
679
680        fn register(
681            &mut self,
682            _trader_id: TraderId,
683            _clock: Rc<RefCell<dyn Clock>>,
684            _cache: Rc<RefCell<Cache>>,
685        ) -> anyhow::Result<()> {
686            // Mock implementation
687            Ok(())
688        }
689
690        fn on_start(&mut self) -> anyhow::Result<()> {
691            Ok(())
692        }
693    }
694
695    #[allow(clippy::type_complexity)]
696    fn create_trader_components() -> (
697        Rc<RefCell<MessageBus>>,
698        Rc<RefCell<Cache>>,
699        Rc<RefCell<Portfolio>>,
700        Rc<RefCell<DataEngine>>,
701        Rc<RefCell<RiskEngine>>,
702        Rc<RefCell<ExecutionEngine>>,
703        Rc<RefCell<TestClock>>,
704    ) {
705        let trader_id = TraderId::default();
706        let instance_id = UUID4::new();
707        let clock = Rc::new(RefCell::new(TestClock::new()));
708        // Set the clock to a non-zero time for test purposes
709        clock.borrow_mut().set_time(1_000_000_000u64.into());
710        let msgbus = Rc::new(RefCell::new(MessageBus::new(
711            trader_id,
712            instance_id,
713            Some("test".to_string()),
714            None,
715        )));
716        let cache = Rc::new(RefCell::new(Cache::new(None, None)));
717        let portfolio = Rc::new(RefCell::new(Portfolio::new(
718            cache.clone(),
719            clock.clone() as Rc<RefCell<dyn Clock>>,
720            None,
721        )));
722        let data_engine = Rc::new(RefCell::new(DataEngine::new(
723            clock.clone(),
724            cache.clone(),
725            Some(DataEngineConfig::default()),
726        )));
727
728        // Create separate cache and clock instances for RiskEngine to avoid borrowing conflicts
729        let risk_cache = Rc::new(RefCell::new(Cache::new(None, None)));
730        let risk_clock = Rc::new(RefCell::new(TestClock::new()));
731        let risk_portfolio = Portfolio::new(
732            risk_cache.clone(),
733            risk_clock.clone() as Rc<RefCell<dyn Clock>>,
734            None,
735        );
736        let risk_engine = Rc::new(RefCell::new(RiskEngine::new(
737            RiskEngineConfig::default(),
738            risk_portfolio,
739            risk_clock as Rc<RefCell<dyn Clock>>,
740            risk_cache,
741        )));
742        let exec_engine = Rc::new(RefCell::new(ExecutionEngine::new(
743            clock.clone(),
744            cache.clone(),
745            Some(ExecutionEngineConfig::default()),
746        )));
747
748        (
749            msgbus,
750            cache,
751            portfolio,
752            data_engine,
753            risk_engine,
754            exec_engine,
755            clock,
756        )
757    }
758
759    #[rstest]
760    fn test_trader_creation() {
761        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
762            create_trader_components();
763        let trader_id = TraderId::default();
764        let instance_id = UUID4::new();
765
766        let trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
767
768        assert_eq!(trader.trader_id(), trader_id);
769        assert_eq!(trader.instance_id(), instance_id);
770        assert_eq!(trader.environment(), Environment::Backtest);
771        assert_eq!(trader.state(), ComponentState::PreInitialized);
772        assert_eq!(trader.actor_count(), 0);
773        assert_eq!(trader.strategy_count(), 0);
774        assert_eq!(trader.exec_algorithm_count(), 0);
775        assert_eq!(trader.component_count(), 0);
776        assert!(!trader.is_running());
777        assert!(!trader.is_stopped());
778        assert!(!trader.is_disposed());
779        assert!(trader.ts_created() > 0);
780        assert!(trader.ts_started().is_none());
781        assert!(trader.ts_stopped().is_none());
782    }
783
784    #[rstest]
785    fn test_trader_component_id() {
786        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
787            create_trader_components();
788        let trader_id = TraderId::from("TRADER-001");
789        let instance_id = UUID4::new();
790
791        let trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
792
793        assert_eq!(
794            trader.component_id(),
795            ComponentId::from("Trader-TRADER-001")
796        );
797    }
798
799    #[rstest]
800    fn test_add_actor_success() {
801        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
802            create_trader_components();
803        let trader_id = TraderId::default();
804        let instance_id = UUID4::new();
805
806        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
807
808        let actor = TestDataActor::new(DataActorConfig::default());
809        let actor_id = actor.actor_id();
810
811        let result = trader.add_actor(actor);
812        assert!(result.is_ok());
813        assert_eq!(trader.actor_count(), 1);
814        assert_eq!(trader.component_count(), 1);
815        assert!(trader.actor_ids().contains(&actor_id));
816    }
817
818    #[rstest]
819    fn test_add_duplicate_actor_fails() {
820        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
821            create_trader_components();
822        let trader_id = TraderId::default();
823        let instance_id = UUID4::new();
824
825        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
826
827        let config = DataActorConfig {
828            actor_id: Some(ActorId::from("TestActor")),
829            ..Default::default()
830        };
831        let actor1 = TestDataActor::new(config.clone());
832        let actor2 = TestDataActor::new(config);
833
834        // First addition should succeed
835        assert!(trader.add_actor(actor1).is_ok());
836        assert_eq!(trader.actor_count(), 1);
837
838        // Second addition should fail
839        let result = trader.add_actor(actor2);
840        assert!(result.is_err());
841        assert!(
842            result
843                .unwrap_err()
844                .to_string()
845                .contains("already registered")
846        );
847        assert_eq!(trader.actor_count(), 1);
848    }
849
850    #[rstest]
851    fn test_add_strategy_success() {
852        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
853            create_trader_components();
854        let trader_id = TraderId::default();
855        let instance_id = UUID4::new();
856
857        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
858
859        let strategy = Box::new(MockComponent::new("Test-Strategy"));
860        let strategy_id = StrategyId::from(strategy.component_id().inner().as_str());
861
862        let result = trader.add_strategy(strategy);
863        assert!(result.is_ok());
864        assert_eq!(trader.strategy_count(), 1);
865        assert_eq!(trader.component_count(), 1);
866        assert!(trader.strategy_ids().contains(&strategy_id));
867    }
868
869    #[rstest]
870    fn test_add_exec_algorithm_success() {
871        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
872            create_trader_components();
873        let trader_id = TraderId::default();
874        let instance_id = UUID4::new();
875
876        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
877
878        let exec_algorithm = Box::new(MockComponent::new("TestExecAlgorithm"));
879        let exec_algorithm_id =
880            ExecAlgorithmId::from(exec_algorithm.component_id().inner().as_str());
881
882        let result = trader.add_exec_algorithm(exec_algorithm);
883        assert!(result.is_ok());
884        assert_eq!(trader.exec_algorithm_count(), 1);
885        assert_eq!(trader.component_count(), 1);
886        assert!(trader.exec_algorithm_ids().contains(&exec_algorithm_id));
887    }
888
889    #[rstest]
890    fn test_component_lifecycle() {
891        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
892            create_trader_components();
893        let trader_id = TraderId::default();
894        let instance_id = UUID4::new();
895
896        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
897
898        // Add components
899        let actor = TestDataActor::new(DataActorConfig::default());
900        let strategy = Box::new(MockComponent::new("Test-Strategy"));
901        let exec_algorithm = Box::new(MockComponent::new("TestExecAlgorithm"));
902
903        assert!(trader.add_actor(actor).is_ok());
904        assert!(trader.add_strategy(strategy).is_ok());
905        assert!(trader.add_exec_algorithm(exec_algorithm).is_ok());
906        assert_eq!(trader.component_count(), 3);
907
908        // Test start components
909        assert!(trader.start_components().is_ok());
910
911        // Test stop components
912        assert!(trader.stop_components().is_ok());
913
914        // Test reset components
915        assert!(trader.reset_components().is_ok());
916
917        // Test dispose components
918        assert!(trader.dispose_components().is_ok());
919        assert_eq!(trader.component_count(), 0);
920    }
921
922    #[rstest]
923    fn test_trader_component_lifecycle() {
924        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
925            create_trader_components();
926        let trader_id = TraderId::default();
927        let instance_id = UUID4::new();
928
929        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
930
931        // Initially pre-initialized
932        assert_eq!(trader.state(), ComponentState::PreInitialized);
933        assert!(!trader.is_running());
934        assert!(!trader.is_stopped());
935        assert!(!trader.is_disposed());
936
937        // Cannot start from pre-initialized state
938        assert!(trader.start().is_err());
939
940        // Simulate initialization (normally done by kernel)
941        trader.initialize().unwrap();
942
943        // Test start
944        assert!(trader.start().is_ok());
945        assert_eq!(trader.state(), ComponentState::Running);
946        assert!(trader.is_running());
947        assert!(trader.ts_started().is_some());
948
949        // Test stop
950        assert!(trader.stop().is_ok());
951        assert_eq!(trader.state(), ComponentState::Stopped);
952        assert!(trader.is_stopped());
953        assert!(trader.ts_stopped().is_some());
954
955        // Test reset
956        assert!(trader.reset().is_ok());
957        assert_eq!(trader.state(), ComponentState::Ready);
958        assert!(trader.ts_started().is_none());
959        assert!(trader.ts_stopped().is_none());
960
961        // Test dispose
962        assert!(trader.dispose().is_ok());
963        assert_eq!(trader.state(), ComponentState::Disposed);
964        assert!(trader.is_disposed());
965    }
966
967    #[rstest]
968    fn test_cannot_add_components_while_running() {
969        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
970            create_trader_components();
971        let trader_id = TraderId::default();
972        let instance_id = UUID4::new();
973
974        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
975
976        // Simulate running state
977        trader.state = ComponentState::Running;
978
979        let actor = TestDataActor::new(DataActorConfig::default());
980        let result = trader.add_actor(actor);
981        assert!(result.is_err());
982        assert!(
983            result
984                .unwrap_err()
985                .to_string()
986                .contains("while trader is running")
987        );
988    }
989
990    #[rstest]
991    fn test_create_component_clock_backtest_vs_live() {
992        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
993            create_trader_components();
994        let trader_id = TraderId::default();
995        let instance_id = UUID4::new();
996
997        // Test backtest environment - should create individual test clocks
998        let trader_backtest = Trader::new(
999            trader_id,
1000            instance_id,
1001            Environment::Backtest,
1002            clock.clone(),
1003            cache.clone(),
1004        );
1005
1006        let backtest_clock = trader_backtest.create_component_clock();
1007        // In backtest, component clock should be different from system clock
1008        assert_ne!(
1009            backtest_clock.as_ptr() as *const _,
1010            clock.as_ptr() as *const _
1011        );
1012
1013        // Test live environment - should share system clock
1014        let trader_live = Trader::new(
1015            trader_id,
1016            instance_id,
1017            Environment::Live,
1018            clock.clone(),
1019            cache,
1020        );
1021
1022        let live_clock = trader_live.create_component_clock();
1023        // In live, component clock should be same as system clock
1024        assert_eq!(live_clock.as_ptr() as *const _, clock.as_ptr() as *const _);
1025    }
1026}