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