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