nautilus_system/
trader.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.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 posei_trader::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    fn create_trader_components() -> (
661        Rc<RefCell<MessageBus>>,
662        Rc<RefCell<Cache>>,
663        Rc<RefCell<Portfolio>>,
664        Rc<RefCell<DataEngine>>,
665        Rc<RefCell<RiskEngine>>,
666        Rc<RefCell<ExecutionEngine>>,
667        Rc<RefCell<TestClock>>,
668    ) {
669        let trader_id = TraderId::default();
670        let instance_id = UUID4::new();
671        let clock = Rc::new(RefCell::new(TestClock::new()));
672        // Set the clock to a non-zero time for test purposes
673        clock.borrow_mut().set_time(1_000_000_000u64.into());
674        let msgbus = Rc::new(RefCell::new(MessageBus::new(
675            trader_id,
676            instance_id,
677            Some("test".to_string()),
678            None,
679        )));
680        let cache = Rc::new(RefCell::new(Cache::new(None, None)));
681        let portfolio = Rc::new(RefCell::new(Portfolio::new(
682            cache.clone(),
683            clock.clone() as Rc<RefCell<dyn Clock>>,
684            None,
685        )));
686        let data_engine = Rc::new(RefCell::new(DataEngine::new(
687            clock.clone(),
688            cache.clone(),
689            Some(DataEngineConfig::default()),
690        )));
691
692        // Create separate cache and clock instances for RiskEngine to avoid borrowing conflicts
693        let risk_cache = Rc::new(RefCell::new(Cache::new(None, None)));
694        let risk_clock = Rc::new(RefCell::new(TestClock::new()));
695        let risk_portfolio = Portfolio::new(
696            risk_cache.clone(),
697            risk_clock.clone() as Rc<RefCell<dyn Clock>>,
698            None,
699        );
700        let risk_engine = Rc::new(RefCell::new(RiskEngine::new(
701            RiskEngineConfig::default(),
702            risk_portfolio,
703            risk_clock as Rc<RefCell<dyn Clock>>,
704            risk_cache,
705        )));
706        let exec_engine = Rc::new(RefCell::new(ExecutionEngine::new(
707            clock.clone(),
708            cache.clone(),
709            Some(ExecutionEngineConfig::default()),
710        )));
711
712        (
713            msgbus,
714            cache,
715            portfolio,
716            data_engine,
717            risk_engine,
718            exec_engine,
719            clock,
720        )
721    }
722
723    #[rstest]
724    fn test_trader_creation() {
725        let (msgbus, cache, portfolio, data_engine, risk_engine, exec_engine, clock) =
726            create_trader_components();
727        let trader_id = TraderId::default();
728        let instance_id = UUID4::new();
729
730        let trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
731
732        assert_eq!(trader.trader_id(), trader_id);
733        assert_eq!(trader.instance_id(), instance_id);
734        assert_eq!(trader.environment(), Environment::Backtest);
735        assert_eq!(trader.state(), ComponentState::PreInitialized);
736        assert_eq!(trader.actor_count(), 0);
737        assert_eq!(trader.strategy_count(), 0);
738        assert_eq!(trader.exec_algorithm_count(), 0);
739        assert_eq!(trader.component_count(), 0);
740        assert!(!trader.is_running());
741        assert!(!trader.is_stopped());
742        assert!(!trader.is_disposed());
743        assert!(trader.ts_created() > 0);
744        assert!(trader.ts_started().is_none());
745        assert!(trader.ts_stopped().is_none());
746    }
747
748    #[rstest]
749    fn test_trader_component_id() {
750        let (msgbus, cache, portfolio, data_engine, risk_engine, exec_engine, clock) =
751            create_trader_components();
752        let trader_id = TraderId::from("TRADER-001");
753        let instance_id = UUID4::new();
754
755        let trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
756
757        assert_eq!(
758            trader.component_id(),
759            ComponentId::from("Trader-TRADER-001")
760        );
761    }
762
763    #[rstest]
764    fn test_add_actor_success() {
765        let (msgbus, cache, portfolio, data_engine, risk_engine, exec_engine, clock) =
766            create_trader_components();
767        let trader_id = TraderId::default();
768        let instance_id = UUID4::new();
769
770        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
771
772        let actor = TestDataActor::new(DataActorConfig::default());
773        let actor_id = actor.actor_id();
774
775        let result = trader.add_actor(actor);
776        assert!(result.is_ok());
777        assert_eq!(trader.actor_count(), 1);
778        assert_eq!(trader.component_count(), 1);
779        assert!(trader.actor_ids().contains(&actor_id));
780    }
781
782    #[rstest]
783    fn test_add_duplicate_actor_fails() {
784        let (msgbus, cache, portfolio, data_engine, risk_engine, exec_engine, clock) =
785            create_trader_components();
786        let trader_id = TraderId::default();
787        let instance_id = UUID4::new();
788
789        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
790
791        let mut config = DataActorConfig::default();
792        config.actor_id = Some(ActorId::from("TestActor"));
793        let actor1 = TestDataActor::new(config.clone());
794        let actor2 = TestDataActor::new(config);
795
796        // First addition should succeed
797        assert!(trader.add_actor(actor1).is_ok());
798        assert_eq!(trader.actor_count(), 1);
799
800        // Second addition should fail
801        let result = trader.add_actor(actor2);
802        assert!(result.is_err());
803        assert!(
804            result
805                .unwrap_err()
806                .to_string()
807                .contains("already registered")
808        );
809        assert_eq!(trader.actor_count(), 1);
810    }
811
812    #[rstest]
813    fn test_add_strategy_success() {
814        let (msgbus, cache, portfolio, data_engine, risk_engine, exec_engine, clock) =
815            create_trader_components();
816        let trader_id = TraderId::default();
817        let instance_id = UUID4::new();
818
819        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
820
821        let strategy = Box::new(MockComponent::new("Test-Strategy"));
822        let strategy_id = StrategyId::from(strategy.component_id().inner().as_str());
823
824        let result = trader.add_strategy(strategy);
825        assert!(result.is_ok());
826        assert_eq!(trader.strategy_count(), 1);
827        assert_eq!(trader.component_count(), 1);
828        assert!(trader.strategy_ids().contains(&strategy_id));
829    }
830
831    #[rstest]
832    fn test_add_exec_algorithm_success() {
833        let (msgbus, cache, portfolio, data_engine, risk_engine, exec_engine, clock) =
834            create_trader_components();
835        let trader_id = TraderId::default();
836        let instance_id = UUID4::new();
837
838        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
839
840        let exec_algorithm = Box::new(MockComponent::new("TestExecAlgorithm"));
841        let exec_algorithm_id =
842            ExecAlgorithmId::from(exec_algorithm.component_id().inner().as_str());
843
844        let result = trader.add_exec_algorithm(exec_algorithm);
845        assert!(result.is_ok());
846        assert_eq!(trader.exec_algorithm_count(), 1);
847        assert_eq!(trader.component_count(), 1);
848        assert!(trader.exec_algorithm_ids().contains(&exec_algorithm_id));
849    }
850
851    #[rstest]
852    fn test_component_lifecycle() {
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        // Add components
861        let actor = TestDataActor::new(DataActorConfig::default());
862        let strategy = Box::new(MockComponent::new("Test-Strategy"));
863        let exec_algorithm = Box::new(MockComponent::new("TestExecAlgorithm"));
864
865        assert!(trader.add_actor(actor).is_ok());
866        assert!(trader.add_strategy(strategy).is_ok());
867        assert!(trader.add_exec_algorithm(exec_algorithm).is_ok());
868        assert_eq!(trader.component_count(), 3);
869
870        // Test start components
871        assert!(trader.start_components().is_ok());
872
873        // Test stop components
874        assert!(trader.stop_components().is_ok());
875
876        // Test reset components
877        assert!(trader.reset_components().is_ok());
878
879        // Test dispose components
880        assert!(trader.dispose_components().is_ok());
881        assert_eq!(trader.component_count(), 0);
882    }
883
884    #[rstest]
885    fn test_trader_component_lifecycle() {
886        let (msgbus, cache, portfolio, data_engine, risk_engine, exec_engine, clock) =
887            create_trader_components();
888        let trader_id = TraderId::default();
889        let instance_id = UUID4::new();
890
891        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
892
893        // Initially pre-initialized
894        assert_eq!(trader.state(), ComponentState::PreInitialized);
895        assert!(!trader.is_running());
896        assert!(!trader.is_stopped());
897        assert!(!trader.is_disposed());
898
899        // Cannot start from pre-initialized state
900        assert!(trader.start().is_err());
901
902        // Simulate initialization (normally done by kernel)
903        trader.initialize().unwrap();
904
905        // Test start
906        assert!(trader.start().is_ok());
907        assert_eq!(trader.state(), ComponentState::Running);
908        assert!(trader.is_running());
909        assert!(trader.ts_started().is_some());
910
911        // Test stop
912        assert!(trader.stop().is_ok());
913        assert_eq!(trader.state(), ComponentState::Stopped);
914        assert!(trader.is_stopped());
915        assert!(trader.ts_stopped().is_some());
916
917        // Test reset
918        assert!(trader.reset().is_ok());
919        assert_eq!(trader.state(), ComponentState::Ready);
920        assert!(trader.ts_started().is_none());
921        assert!(trader.ts_stopped().is_none());
922
923        // Test dispose
924        assert!(trader.dispose().is_ok());
925        assert_eq!(trader.state(), ComponentState::Disposed);
926        assert!(trader.is_disposed());
927    }
928
929    #[rstest]
930    fn test_cannot_add_components_while_running() {
931        let (msgbus, cache, portfolio, data_engine, risk_engine, exec_engine, clock) =
932            create_trader_components();
933        let trader_id = TraderId::default();
934        let instance_id = UUID4::new();
935
936        let mut trader = Trader::new(trader_id, instance_id, Environment::Backtest, clock, cache);
937
938        // Simulate running state
939        trader.state = ComponentState::Running;
940
941        let actor = TestDataActor::new(DataActorConfig::default());
942        let result = trader.add_actor(actor);
943        assert!(result.is_err());
944        assert!(
945            result
946                .unwrap_err()
947                .to_string()
948                .contains("while trader is running")
949        );
950    }
951
952    #[rstest]
953    fn test_create_component_clock_backtest_vs_live() {
954        let (msgbus, cache, portfolio, data_engine, risk_engine, exec_engine, clock) =
955            create_trader_components();
956        let trader_id = TraderId::default();
957        let instance_id = UUID4::new();
958
959        // Test backtest environment - should create individual test clocks
960        let trader_backtest = Trader::new(
961            trader_id,
962            instance_id,
963            Environment::Backtest,
964            clock.clone(),
965            cache.clone(),
966        );
967
968        let backtest_clock = trader_backtest.create_component_clock();
969        // In backtest, component clock should be different from system clock
970        assert_ne!(
971            backtest_clock.as_ptr() as *const _,
972            clock.as_ptr() as *const _
973        );
974
975        // Test live environment - should share system clock
976        let trader_live = Trader::new(
977            trader_id,
978            instance_id,
979            Environment::Live,
980            clock.clone(),
981            cache,
982        );
983
984        let live_clock = trader_live.create_component_clock();
985        // In live, component clock should be same as system clock
986        assert_eq!(live_clock.as_ptr() as *const _, clock.as_ptr() as *const _);
987    }
988}