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    /// This function will return an error if the actor cannot be registered
263    /// in the component registry.
264    pub fn add_registered_actor<T>(&mut self, actor: T) -> anyhow::Result<()>
265    where
266        T: DataActor + Component + Debug + 'static,
267    {
268        let actor_id = actor.actor_id();
269        let mem_addr = actor.mem_address();
270
271        // Register in both component and actor registries (this consumes the actor)
272        register_component_actor(actor);
273
274        // Store actor ID for lifecycle management
275        self.actor_ids.push(actor_id);
276
277        log::info!(
278            "Registered '{actor_id}' at mem_addr {mem_addr} with trader {}",
279            self.trader_id
280        );
281
282        Ok(())
283    }
284
285    /// Adds an actor ID to the trader's lifecycle management without consuming the actor.
286    ///
287    /// This is useful when the actor is already registered in the global component registry
288    /// but the trader needs to track it for lifecycle management. The caller is responsible
289    /// for ensuring the actor is properly registered in the global registries.
290    ///
291    /// # Errors
292    ///
293    /// This function will return an error if the actor ID is already tracked by this trader.
294    pub fn add_actor_id_for_lifecycle(&mut self, actor_id: ActorId) -> anyhow::Result<()> {
295        // Check for duplicate registration
296        if self.actor_ids.contains(&actor_id) {
297            anyhow::bail!("Actor '{actor_id}' is already tracked by trader");
298        }
299
300        // Store actor ID for lifecycle management
301        self.actor_ids.push(actor_id);
302
303        log::debug!(
304            "Added actor ID '{actor_id}' to trader {} for lifecycle management",
305            self.trader_id
306        );
307
308        Ok(())
309    }
310
311    /// Adds a strategy to the trader.
312    ///
313    /// # Errors
314    ///
315    /// Returns an error if:
316    /// - The trader is not in a valid state for adding components
317    /// - A strategy with the same ID is already registered
318    pub fn add_strategy(&mut self, mut strategy: Box<dyn Component>) -> anyhow::Result<()> {
319        self.validate_component_registration()?;
320
321        let strategy_id = StrategyId::from(strategy.component_id().inner().as_str());
322
323        // Check for duplicate registration
324        if self.strategies.contains_key(&strategy_id) {
325            anyhow::bail!("Strategy '{strategy_id}' is already registered");
326        }
327
328        let clock = self.create_component_clock();
329        let component_id = strategy.component_id();
330        self.clocks.insert(component_id, clock.clone());
331
332        strategy.register(self.trader_id, clock, self.cache.clone())?;
333
334        self.strategies.insert(strategy_id, strategy);
335        log::info!(
336            "Registered strategy '{strategy_id}' with trader {}",
337            self.trader_id
338        );
339
340        Ok(())
341    }
342
343    /// Adds an execution algorithm to the trader.
344    ///
345    /// # Errors
346    ///
347    /// Returns an error if:
348    /// - The trader is not in a valid state for adding components
349    /// - An execution algorithm with the same ID is already registered
350    pub fn add_exec_algorithm(
351        &mut self,
352        mut exec_algorithm: Box<dyn Component>,
353    ) -> anyhow::Result<()> {
354        self.validate_component_registration()?;
355
356        let exec_algorithm_id =
357            ExecAlgorithmId::from(exec_algorithm.component_id().inner().as_str());
358
359        // Check for duplicate registration
360        if self.exec_algorithms.contains_key(&exec_algorithm_id) {
361            anyhow::bail!("Execution algorithm '{exec_algorithm_id}' is already registered");
362        }
363
364        let clock = self.create_component_clock();
365        let component_id = exec_algorithm.component_id();
366        self.clocks.insert(component_id, clock.clone());
367
368        exec_algorithm.register(self.trader_id, clock, self.cache.clone())?;
369
370        self.exec_algorithms
371            .insert(exec_algorithm_id, exec_algorithm);
372        log::info!(
373            "Registered execution algorithm '{exec_algorithm_id}' with trader {}",
374            self.trader_id
375        );
376
377        Ok(())
378    }
379
380    /// Validates that the trader is in a valid state for component registration.
381    fn validate_component_registration(&self) -> anyhow::Result<()> {
382        match self.state {
383            ComponentState::PreInitialized | ComponentState::Ready | ComponentState::Stopped => {
384                Ok(())
385            }
386            ComponentState::Running => {
387                anyhow::bail!("Cannot add components while trader is running")
388            }
389            ComponentState::Disposed => {
390                anyhow::bail!("Cannot add components to disposed trader")
391            }
392            _ => anyhow::bail!("Cannot add components in current state: {}", self.state),
393        }
394    }
395
396    /// Starts all registered components.
397    ///
398    /// # Errors
399    ///
400    /// Returns an error if any component fails to start.
401    pub fn start_components(&mut self) -> anyhow::Result<()> {
402        // Start actors (retrieved from global registry)
403        for actor_id in &self.actor_ids {
404            log::debug!("Starting actor '{actor_id}'");
405            start_component(&actor_id.inner())?;
406        }
407
408        for strategy_id in &mut self.strategies.keys() {
409            log::debug!("Starting strategy '{strategy_id}'");
410            // strategy.start()?; // TODO: TBD
411        }
412
413        for exec_algorithm_id in &mut self.exec_algorithms.keys() {
414            log::debug!("Starting execution algorithm '{exec_algorithm_id}'");
415            // exec_algorithm.start()?;  // TODO: TBD
416        }
417
418        Ok(())
419    }
420
421    /// Stops all registered components.
422    ///
423    /// # Errors
424    ///
425    /// Returns an error if any component fails to stop.
426    pub fn stop_components(&mut self) -> anyhow::Result<()> {
427        for actor_id in &self.actor_ids {
428            log::debug!("Stopping actor '{actor_id}'");
429            stop_component(&actor_id.inner())?;
430        }
431
432        for exec_algorithm_id in &mut self.exec_algorithms.keys() {
433            log::debug!("Stopping execution algorithm '{exec_algorithm_id}'");
434            // exec_algorithm.stop()?;  // TODO: TBD
435        }
436
437        for strategy_id in &mut self.strategies.keys() {
438            log::debug!("Stopping strategy '{strategy_id}'");
439            // strategy.stop()?;  // TODO: TBD
440        }
441
442        Ok(())
443    }
444
445    /// Resets all registered components.
446    ///
447    /// # Errors
448    ///
449    /// Returns an error if any component fails to reset.
450    pub fn reset_components(&mut self) -> anyhow::Result<()> {
451        // Reset actors (retrieved from global registry)
452        for actor_id in &self.actor_ids {
453            log::debug!("Resetting actor '{actor_id}'");
454            reset_component(&actor_id.inner())?;
455        }
456
457        for strategy_id in &mut self.strategies.keys() {
458            log::debug!("Resetting strategy '{strategy_id}'");
459            // strategy.reset()?;  // TODO: TBD
460        }
461
462        for exec_algorithm_id in &mut self.exec_algorithms.keys() {
463            log::debug!("Resetting execution algorithm '{exec_algorithm_id}'");
464            // exec_algorithm.reset()?;  // TODO: TBD
465        }
466
467        Ok(())
468    }
469
470    /// Disposes of all registered components.
471    ///
472    /// # Errors
473    ///
474    /// Returns an error if any component fails to dispose.
475    pub fn dispose_components(&mut self) -> anyhow::Result<()> {
476        // Dispose actors (retrieved from global registry)
477        for actor_id in &self.actor_ids {
478            log::debug!("Disposing actor '{actor_id}'");
479            dispose_component(&actor_id.inner())?;
480        }
481
482        for strategy_id in &mut self.strategies.keys() {
483            log::debug!("Disposing strategy '{strategy_id}'");
484            // strategy.dispose()?;  // TODO: TBD
485        }
486
487        for exec_algorithm_id in &mut self.exec_algorithms.keys() {
488            log::debug!("Disposing execution algorithm '{exec_algorithm_id}'");
489            // exec_algorithm.dispose()?;  // TODO: TBD
490        }
491
492        self.actor_ids.clear();
493        self.strategies.clear();
494        self.exec_algorithms.clear();
495        self.clocks.clear();
496
497        Ok(())
498    }
499
500    /// Initializes the trader, transitioning from `PreInitialized` to `Ready` state.
501    ///
502    /// This method must be called before starting the trader.
503    ///
504    /// # Errors
505    ///
506    /// Returns an error if the trader cannot be initialized from its current state.
507    pub fn initialize(&mut self) -> anyhow::Result<()> {
508        let new_state = self.state.transition(&ComponentTrigger::Initialize)?;
509        self.state = new_state;
510
511        Ok(())
512    }
513
514    fn on_start(&mut self) -> anyhow::Result<()> {
515        self.start_components()?;
516
517        // Transition to running state
518        self.ts_started = Some(self.clock.borrow().timestamp_ns());
519
520        Ok(())
521    }
522
523    fn on_stop(&mut self) -> anyhow::Result<()> {
524        self.stop_components()?;
525
526        self.ts_stopped = Some(self.clock.borrow().timestamp_ns());
527
528        Ok(())
529    }
530
531    fn on_reset(&mut self) -> anyhow::Result<()> {
532        self.reset_components()?;
533
534        self.ts_started = None;
535        self.ts_stopped = None;
536
537        Ok(())
538    }
539
540    fn on_dispose(&mut self) -> anyhow::Result<()> {
541        if self.is_running() {
542            self.stop()?;
543        }
544
545        self.dispose_components()?;
546
547        Ok(())
548    }
549}
550
551impl Component for Trader {
552    fn component_id(&self) -> ComponentId {
553        ComponentId::new(format!("Trader-{}", self.trader_id))
554    }
555
556    fn state(&self) -> ComponentState {
557        self.state
558    }
559
560    fn transition_state(&mut self, trigger: ComponentTrigger) -> anyhow::Result<()> {
561        self.state = self.state.transition(&trigger)?;
562        log::info!("{}", self.state.variant_name());
563        Ok(())
564    }
565
566    fn register(
567        &mut self,
568        _trader_id: TraderId,
569        _clock: Rc<RefCell<dyn Clock>>,
570        _cache: Rc<RefCell<Cache>>,
571    ) -> anyhow::Result<()> {
572        anyhow::bail!("Trader cannot register with itself")
573    }
574
575    fn on_start(&mut self) -> anyhow::Result<()> {
576        Self::on_start(self)
577    }
578
579    fn on_stop(&mut self) -> anyhow::Result<()> {
580        Self::on_stop(self)
581    }
582
583    fn on_reset(&mut self) -> anyhow::Result<()> {
584        Self::on_reset(self)
585    }
586
587    fn on_dispose(&mut self) -> anyhow::Result<()> {
588        Self::on_dispose(self)
589    }
590}
591
592////////////////////////////////////////////////////////////////////////////////
593// Tests
594////////////////////////////////////////////////////////////////////////////////
595
596#[cfg(test)]
597mod tests {
598    use std::{
599        cell::RefCell,
600        ops::{Deref, DerefMut},
601        rc::Rc,
602    };
603
604    use nautilus_common::{
605        actor::{DataActorCore, data_actor::DataActorConfig},
606        cache::Cache,
607        clock::TestClock,
608        enums::{ComponentState, Environment},
609        msgbus::MessageBus,
610    };
611    use nautilus_core::UUID4;
612    use nautilus_data::engine::{DataEngine, config::DataEngineConfig};
613    use nautilus_execution::engine::{ExecutionEngine, config::ExecutionEngineConfig};
614    use nautilus_model::identifiers::{ActorId, ComponentId, TraderId};
615    use nautilus_portfolio::portfolio::Portfolio;
616    use nautilus_risk::engine::{RiskEngine, config::RiskEngineConfig};
617    use rstest::rstest;
618
619    use super::*;
620
621    // Simple DataActor wrapper for testing
622    #[derive(Debug)]
623    struct TestDataActor {
624        core: DataActorCore,
625    }
626
627    impl TestDataActor {
628        fn new(config: DataActorConfig) -> Self {
629            Self {
630                core: DataActorCore::new(config),
631            }
632        }
633    }
634
635    impl DataActor for TestDataActor {}
636
637    impl Deref for TestDataActor {
638        type Target = DataActorCore;
639        fn deref(&self) -> &Self::Target {
640            &self.core
641        }
642    }
643
644    impl DerefMut for TestDataActor {
645        fn deref_mut(&mut self) -> &mut Self::Target {
646            &mut self.core
647        }
648    }
649
650    // Mock component for testing
651    #[derive(Debug)]
652    struct MockComponent {
653        id: ComponentId,
654        state: ComponentState,
655    }
656
657    impl MockComponent {
658        fn new(id: &str) -> Self {
659            Self {
660                id: ComponentId::from(id),
661                state: ComponentState::PreInitialized,
662            }
663        }
664    }
665
666    impl Component for MockComponent {
667        fn component_id(&self) -> ComponentId {
668            self.id
669        }
670
671        fn state(&self) -> ComponentState {
672            self.state
673        }
674
675        fn transition_state(&mut self, trigger: ComponentTrigger) -> anyhow::Result<()> {
676            self.state = self.state.transition(&trigger)?;
677            log::info!("{}", self.state.variant_name());
678            Ok(())
679        }
680
681        fn register(
682            &mut self,
683            _trader_id: TraderId,
684            _clock: Rc<RefCell<dyn Clock>>,
685            _cache: Rc<RefCell<Cache>>,
686        ) -> anyhow::Result<()> {
687            // Mock implementation
688            Ok(())
689        }
690
691        fn on_start(&mut self) -> anyhow::Result<()> {
692            Ok(())
693        }
694    }
695
696    #[allow(clippy::type_complexity)]
697    fn create_trader_components() -> (
698        Rc<RefCell<MessageBus>>,
699        Rc<RefCell<Cache>>,
700        Rc<RefCell<Portfolio>>,
701        Rc<RefCell<DataEngine>>,
702        Rc<RefCell<RiskEngine>>,
703        Rc<RefCell<ExecutionEngine>>,
704        Rc<RefCell<TestClock>>,
705    ) {
706        let trader_id = TraderId::default();
707        let instance_id = UUID4::new();
708        let clock = Rc::new(RefCell::new(TestClock::new()));
709        // Set the clock to a non-zero time for test purposes
710        clock.borrow_mut().set_time(1_000_000_000u64.into());
711        let msgbus = Rc::new(RefCell::new(MessageBus::new(
712            trader_id,
713            instance_id,
714            Some("test".to_string()),
715            None,
716        )));
717        let cache = Rc::new(RefCell::new(Cache::new(None, None)));
718        let portfolio = Rc::new(RefCell::new(Portfolio::new(
719            cache.clone(),
720            clock.clone() as Rc<RefCell<dyn Clock>>,
721            None,
722        )));
723        let data_engine = Rc::new(RefCell::new(DataEngine::new(
724            clock.clone(),
725            cache.clone(),
726            Some(DataEngineConfig::default()),
727        )));
728
729        // Create separate cache and clock instances for RiskEngine to avoid borrowing conflicts
730        let risk_cache = Rc::new(RefCell::new(Cache::new(None, None)));
731        let risk_clock = Rc::new(RefCell::new(TestClock::new()));
732        let risk_portfolio = Portfolio::new(
733            risk_cache.clone(),
734            risk_clock.clone() as Rc<RefCell<dyn Clock>>,
735            None,
736        );
737        let risk_engine = Rc::new(RefCell::new(RiskEngine::new(
738            RiskEngineConfig::default(),
739            risk_portfolio,
740            risk_clock as Rc<RefCell<dyn Clock>>,
741            risk_cache,
742        )));
743        let exec_engine = Rc::new(RefCell::new(ExecutionEngine::new(
744            clock.clone(),
745            cache.clone(),
746            Some(ExecutionEngineConfig::default()),
747        )));
748
749        (
750            msgbus,
751            cache,
752            portfolio,
753            data_engine,
754            risk_engine,
755            exec_engine,
756            clock,
757        )
758    }
759
760    #[rstest]
761    fn test_trader_creation() {
762        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
763            create_trader_components();
764        let trader_id = TraderId::default();
765        let instance_id = UUID4::new();
766
767        let trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
768
769        assert_eq!(trader.trader_id(), trader_id);
770        assert_eq!(trader.instance_id(), instance_id);
771        assert_eq!(trader.environment(), Environment::Backtest);
772        assert_eq!(trader.state(), ComponentState::PreInitialized);
773        assert_eq!(trader.actor_count(), 0);
774        assert_eq!(trader.strategy_count(), 0);
775        assert_eq!(trader.exec_algorithm_count(), 0);
776        assert_eq!(trader.component_count(), 0);
777        assert!(!trader.is_running());
778        assert!(!trader.is_stopped());
779        assert!(!trader.is_disposed());
780        assert!(trader.ts_created() > 0);
781        assert!(trader.ts_started().is_none());
782        assert!(trader.ts_stopped().is_none());
783    }
784
785    #[rstest]
786    fn test_trader_component_id() {
787        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
788            create_trader_components();
789        let trader_id = TraderId::from("TRADER-001");
790        let instance_id = UUID4::new();
791
792        let trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
793
794        assert_eq!(
795            trader.component_id(),
796            ComponentId::from("Trader-TRADER-001")
797        );
798    }
799
800    #[rstest]
801    fn test_add_actor_success() {
802        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
803            create_trader_components();
804        let trader_id = TraderId::default();
805        let instance_id = UUID4::new();
806
807        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
808
809        let actor = TestDataActor::new(DataActorConfig::default());
810        let actor_id = actor.actor_id();
811
812        let result = trader.add_actor(actor);
813        assert!(result.is_ok());
814        assert_eq!(trader.actor_count(), 1);
815        assert_eq!(trader.component_count(), 1);
816        assert!(trader.actor_ids().contains(&actor_id));
817    }
818
819    #[rstest]
820    fn test_add_duplicate_actor_fails() {
821        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
822            create_trader_components();
823        let trader_id = TraderId::default();
824        let instance_id = UUID4::new();
825
826        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
827
828        let config = DataActorConfig {
829            actor_id: Some(ActorId::from("TestActor")),
830            ..Default::default()
831        };
832        let actor1 = TestDataActor::new(config.clone());
833        let actor2 = TestDataActor::new(config);
834
835        // First addition should succeed
836        assert!(trader.add_actor(actor1).is_ok());
837        assert_eq!(trader.actor_count(), 1);
838
839        // Second addition should fail
840        let result = trader.add_actor(actor2);
841        assert!(result.is_err());
842        assert!(
843            result
844                .unwrap_err()
845                .to_string()
846                .contains("already registered")
847        );
848        assert_eq!(trader.actor_count(), 1);
849    }
850
851    #[rstest]
852    fn test_add_strategy_success() {
853        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
854            create_trader_components();
855        let trader_id = TraderId::default();
856        let instance_id = UUID4::new();
857
858        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
859
860        let strategy = Box::new(MockComponent::new("Test-Strategy"));
861        let strategy_id = StrategyId::from(strategy.component_id().inner().as_str());
862
863        let result = trader.add_strategy(strategy);
864        assert!(result.is_ok());
865        assert_eq!(trader.strategy_count(), 1);
866        assert_eq!(trader.component_count(), 1);
867        assert!(trader.strategy_ids().contains(&strategy_id));
868    }
869
870    #[rstest]
871    fn test_add_exec_algorithm_success() {
872        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
873            create_trader_components();
874        let trader_id = TraderId::default();
875        let instance_id = UUID4::new();
876
877        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
878
879        let exec_algorithm = Box::new(MockComponent::new("TestExecAlgorithm"));
880        let exec_algorithm_id =
881            ExecAlgorithmId::from(exec_algorithm.component_id().inner().as_str());
882
883        let result = trader.add_exec_algorithm(exec_algorithm);
884        assert!(result.is_ok());
885        assert_eq!(trader.exec_algorithm_count(), 1);
886        assert_eq!(trader.component_count(), 1);
887        assert!(trader.exec_algorithm_ids().contains(&exec_algorithm_id));
888    }
889
890    #[rstest]
891    fn test_component_lifecycle() {
892        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
893            create_trader_components();
894        let trader_id = TraderId::default();
895        let instance_id = UUID4::new();
896
897        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
898
899        // Add components
900        let actor = TestDataActor::new(DataActorConfig::default());
901        let strategy = Box::new(MockComponent::new("Test-Strategy"));
902        let exec_algorithm = Box::new(MockComponent::new("TestExecAlgorithm"));
903
904        assert!(trader.add_actor(actor).is_ok());
905        assert!(trader.add_strategy(strategy).is_ok());
906        assert!(trader.add_exec_algorithm(exec_algorithm).is_ok());
907        assert_eq!(trader.component_count(), 3);
908
909        // Test start components
910        assert!(trader.start_components().is_ok());
911
912        // Test stop components
913        assert!(trader.stop_components().is_ok());
914
915        // Test reset components
916        assert!(trader.reset_components().is_ok());
917
918        // Test dispose components
919        assert!(trader.dispose_components().is_ok());
920        assert_eq!(trader.component_count(), 0);
921    }
922
923    #[rstest]
924    fn test_trader_component_lifecycle() {
925        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
926            create_trader_components();
927        let trader_id = TraderId::default();
928        let instance_id = UUID4::new();
929
930        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
931
932        // Initially pre-initialized
933        assert_eq!(trader.state(), ComponentState::PreInitialized);
934        assert!(!trader.is_running());
935        assert!(!trader.is_stopped());
936        assert!(!trader.is_disposed());
937
938        // Cannot start from pre-initialized state
939        assert!(trader.start().is_err());
940
941        // Simulate initialization (normally done by kernel)
942        trader.initialize().unwrap();
943
944        // Test start
945        assert!(trader.start().is_ok());
946        assert_eq!(trader.state(), ComponentState::Running);
947        assert!(trader.is_running());
948        assert!(trader.ts_started().is_some());
949
950        // Test stop
951        assert!(trader.stop().is_ok());
952        assert_eq!(trader.state(), ComponentState::Stopped);
953        assert!(trader.is_stopped());
954        assert!(trader.ts_stopped().is_some());
955
956        // Test reset
957        assert!(trader.reset().is_ok());
958        assert_eq!(trader.state(), ComponentState::Ready);
959        assert!(trader.ts_started().is_none());
960        assert!(trader.ts_stopped().is_none());
961
962        // Test dispose
963        assert!(trader.dispose().is_ok());
964        assert_eq!(trader.state(), ComponentState::Disposed);
965        assert!(trader.is_disposed());
966    }
967
968    #[rstest]
969    fn test_cannot_add_components_while_running() {
970        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
971            create_trader_components();
972        let trader_id = TraderId::default();
973        let instance_id = UUID4::new();
974
975        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
976
977        // Simulate running state
978        trader.state = ComponentState::Running;
979
980        let actor = TestDataActor::new(DataActorConfig::default());
981        let result = trader.add_actor(actor);
982        assert!(result.is_err());
983        assert!(
984            result
985                .unwrap_err()
986                .to_string()
987                .contains("while trader is running")
988        );
989    }
990
991    #[rstest]
992    fn test_create_component_clock_backtest_vs_live() {
993        let (_msgbus, cache, _portfolio, _data_engine, _risk_engine, _exec_engine, clock) =
994            create_trader_components();
995        let trader_id = TraderId::default();
996        let instance_id = UUID4::new();
997
998        // Test backtest environment - should create individual test clocks
999        let trader_backtest = Trader::new(
1000            trader_id,
1001            instance_id,
1002            Environment::Backtest,
1003            clock.clone(),
1004            cache.clone(),
1005        );
1006
1007        let backtest_clock = trader_backtest.create_component_clock();
1008        // In backtest, component clock should be different from system clock
1009        assert_ne!(
1010            backtest_clock.as_ptr() as *const _,
1011            clock.as_ptr() as *const _
1012        );
1013
1014        // Test live environment - should share system clock
1015        let trader_live = Trader::new(
1016            trader_id,
1017            instance_id,
1018            Environment::Live,
1019            clock.clone(),
1020            cache,
1021        );
1022
1023        let live_clock = trader_live.create_component_clock();
1024        // In live, component clock should be same as system clock
1025        assert_eq!(live_clock.as_ptr() as *const _, clock.as_ptr() as *const _);
1026    }
1027}