market_data_source/
generator.rs

1#![allow(unused)]
2//! Main market data generator
3
4use rand::SeedableRng;
5use rand::rngs::StdRng;
6use rust_decimal::Decimal;
7use rust_decimal::prelude::FromPrimitive;
8use crate::algorithms::RandomWalkGenerator;
9use crate::config::GeneratorConfig;
10use crate::types::{OHLC, Tick};
11use std::time::{SystemTime, UNIX_EPOCH};
12
13#[cfg(feature = "regimes")]
14use crate::regimes::{RegimeDetector, RegimeState, RegimeConfig, RegimeTracker, VolatilityRegimeDetector, RegimeController, RegimeSchedule, ScheduleInfo};
15
16/// Main market data generator
17pub struct MarketDataGenerator {
18    /// Random number generator
19    rng: StdRng,
20    /// Configuration
21    config: GeneratorConfig,
22    /// Price generator algorithm
23    price_generator: RandomWalkGenerator,
24    /// Current timestamp in milliseconds
25    current_timestamp: i64,
26    /// Regime detector for market state analysis
27    #[cfg(feature = "regimes")]
28    regime_detector: Option<Box<dyn RegimeDetector>>,
29    /// Regime tracker for historical analysis
30    #[cfg(feature = "regimes")]
31    regime_tracker: Option<RegimeTracker>,
32    /// Historical data buffer for regime detection
33    #[cfg(feature = "regimes")]
34    data_buffer: Vec<OHLC>,
35    /// Regime controller for deterministic regime management
36    #[cfg(feature = "regimes")]
37    regime_controller: Option<RegimeController>,
38}
39
40impl MarketDataGenerator {
41    /// Creates a new generator with default configuration
42    pub fn new() -> Self {
43        Self::with_config(GeneratorConfig::default()).expect("Default config should be valid")
44    }
45
46    /// Creates a new generator with custom configuration
47    pub fn with_config(config: GeneratorConfig) -> Result<Self, String> {
48        // Validate configuration
49        config.validate()
50            .map_err(|e| format!("Invalid configuration: {e}"))?;
51
52        // Create RNG with seed if provided
53        let rng = match config.seed {
54            Some(seed) => StdRng::seed_from_u64(seed),
55            None => StdRng::from_entropy(),
56        };
57
58        // Create price generator
59        let price_generator = RandomWalkGenerator::new(config.clone())?;
60
61        // Get current timestamp
62        let current_timestamp = SystemTime::now()
63            .duration_since(UNIX_EPOCH)
64            .map_err(|e| format!("Failed to get system time: {e}"))?
65            .as_millis() as i64;
66
67        Ok(Self {
68            rng,
69            config: config.clone(),
70            price_generator,
71            current_timestamp,
72            #[cfg(feature = "regimes")]
73            regime_detector: None,
74            #[cfg(feature = "regimes")]
75            regime_tracker: None,
76            #[cfg(feature = "regimes")]
77            data_buffer: Vec::with_capacity(100),
78            #[cfg(feature = "regimes")]
79            regime_controller: None,
80        })
81    }
82
83    /// Generates a single OHLC
84    pub fn generate_ohlc(&mut self) -> OHLC {
85        // Update regime controller if enabled
86        #[cfg(feature = "regimes")]
87        self.update_regime_controller();
88
89        // Generate OHLC prices (using 10 ticks per candle for realism)
90        let (open, high, low, close) = self.price_generator.generate_ohlc(&mut self.rng, 10);
91        
92        // Generate volume
93        let volume = self.price_generator.generate_volume(&mut self.rng);
94        
95        // Get timestamp
96        let timestamp = self.current_timestamp;
97        
98        // Advance timestamp for next candle
99        self.current_timestamp += self.config.time_interval.millis() as i64;
100        
101        let candle = OHLC::new(open, high, low, close, volume, timestamp);
102        
103        // Update regime detection if enabled
104        #[cfg(feature = "regimes")]
105        self.update_regime_detection(&candle);
106        
107        candle
108    }
109    
110    /// Generates a single OHLC candle
111    #[deprecated(since = "0.2.0", note = "Use generate_ohlc() instead")]
112    pub fn generate_candle(&mut self) -> OHLC {
113        self.generate_ohlc()
114    }
115
116    /// Generates a series of OHLC candles
117    pub fn generate_series(&mut self, count: usize) -> Vec<OHLC> {
118        let mut candles = Vec::with_capacity(count);
119        for _ in 0..count {
120            candles.push(self.generate_ohlc());
121        }
122        candles
123    }
124
125    /// Generates a single tick
126    pub fn generate_tick(&mut self) -> Tick {
127        let price = self.price_generator.next_price(&mut self.rng);
128        let volume = self.price_generator.generate_volume(&mut self.rng);
129        let timestamp = self.current_timestamp;
130        
131        // Advance timestamp by 1 second for ticks
132        self.current_timestamp += 1000;
133        
134        // Optionally generate bid/ask spread
135        let spread = Decimal::from_f64(0.001).unwrap(); // 0.1% spread
136        let half_spread = price * spread / Decimal::from(2);
137        
138        Tick::with_spread(
139            price,
140            volume,
141            timestamp,
142            price - half_spread,
143            price + half_spread,
144        )
145    }
146
147    /// Generates a series of ticks
148    pub fn generate_ticks(&mut self, count: usize) -> Vec<Tick> {
149        let mut ticks = Vec::with_capacity(count);
150        for _ in 0..count {
151            ticks.push(self.generate_tick());
152        }
153        ticks
154    }
155
156    /// Resets the generator to initial state
157    pub fn reset(&mut self) {
158        self.price_generator.reset();
159        self.current_timestamp = SystemTime::now()
160            .duration_since(UNIX_EPOCH)
161            .unwrap_or_default()
162            .as_millis() as i64;
163        
164        // Reset RNG with seed if provided
165        if let Some(seed) = self.config.seed {
166            self.rng = StdRng::seed_from_u64(seed);
167        }
168    }
169
170    /// Sets a specific starting timestamp
171    pub fn set_timestamp(&mut self, timestamp: i64) {
172        self.current_timestamp = timestamp;
173    }
174
175    /// Gets the current configuration
176    pub fn config(&self) -> &GeneratorConfig {
177        &self.config
178    }
179
180    /// Updates the configuration
181    pub fn set_config(&mut self, config: GeneratorConfig) -> Result<(), String> {
182        config.validate()
183            .map_err(|e| format!("Invalid configuration: {e}"))?;
184        
185        // Update config
186        self.config = config.clone();
187        
188        // Recreate price generator with new config
189        self.price_generator = RandomWalkGenerator::new(config)?;
190        
191        // Update RNG if seed changed
192        if let Some(seed) = self.config.seed {
193            self.rng = StdRng::seed_from_u64(seed);
194        }
195        
196        Ok(())
197    }
198
199    /// Generate OHLC data and export to CSV file
200    #[cfg(feature = "csv_export")]
201    pub fn generate_to_csv_ohlc<P: AsRef<std::path::Path>>(
202        &mut self,
203        count: usize,
204        path: P,
205    ) -> Result<(), Box<dyn std::error::Error>> {
206        let data = self.generate_series(count);
207        crate::export::to_csv_ohlc(&data, path)?;
208        Ok(())
209    }
210
211    /// Generate tick data and export to CSV file
212    #[cfg(feature = "csv_export")]
213    pub fn generate_to_csv_ticks<P: AsRef<std::path::Path>>(
214        &mut self,
215        count: usize,
216        path: P,
217    ) -> Result<(), Box<dyn std::error::Error>> {
218        let data = self.generate_ticks(count);
219        crate::export::to_csv_ticks(&data, path)?;
220        Ok(())
221    }
222
223    /// Stream generate OHLC data directly to CSV file (memory efficient for large datasets)
224    #[cfg(feature = "csv_export")]
225    pub fn stream_generate_to_csv_ohlc<P: AsRef<std::path::Path>>(
226        &mut self,
227        count: usize,
228        path: P,
229    ) -> Result<usize, Box<dyn std::error::Error>> {
230        use crate::export::csv::CsvExporter;
231        
232        let exporter = CsvExporter::default();
233        
234        // Create an iterator that generates candles on-the-fly
235        let iter = (0..count).map(|_| self.generate_ohlc());
236        
237        Ok(exporter.stream_ohlc(iter, path)?)
238    }
239
240    /// Stream generate tick data directly to CSV file (memory efficient for large datasets)
241    #[cfg(feature = "csv_export")]
242    pub fn stream_generate_to_csv_ticks<P: AsRef<std::path::Path>>(
243        &mut self,
244        count: usize,
245        path: P,
246    ) -> Result<usize, Box<dyn std::error::Error>> {
247        use crate::export::csv::CsvExporter;
248        
249        let exporter = CsvExporter::default();
250        
251        // Create an iterator that generates ticks on-the-fly
252        let iter = (0..count).map(|_| self.generate_tick());
253        
254        Ok(exporter.stream_ticks(iter, path)?)
255    }
256
257}
258
259// Regime detection methods (enabled with "regimes" feature)
260#[cfg(feature = "regimes")]
261impl MarketDataGenerator {
262    // Rule-based detector removed due to missing implementation
263
264    /// Enables regime detection with volatility-based detector
265    pub fn enable_volatility_regime_detection(&mut self, window_size: usize) {
266        self.regime_detector = Some(Box::new(VolatilityRegimeDetector::new(window_size)));
267        self.regime_tracker = Some(RegimeTracker::new(1000));
268    }
269
270    /// Disables regime detection
271    pub fn disable_regime_detection(&mut self) {
272        self.regime_detector = None;
273        self.regime_tracker = None;
274        self.data_buffer.clear();
275    }
276
277    /// Gets the current regime state
278    pub fn current_regime(&self) -> Option<&RegimeState> {
279        self.regime_tracker.as_ref()?.current()
280    }
281
282    /// Gets regime detection analytics
283    pub fn regime_analytics(&self) -> Option<RegimeAnalytics> {
284        let tracker = self.regime_tracker.as_ref()?;
285        Some(RegimeAnalytics {
286            current_regime: tracker.current().cloned(),
287            transitions: tracker.transitions,
288            average_duration: tracker.average_duration(),
289            regime_distribution: tracker.regime_distribution(),
290        })
291    }
292
293    /// Updates regime detection with a new candle
294    fn update_regime_detection(&mut self, candle: &OHLC) {
295        // Add to buffer
296        self.data_buffer.push(candle.clone());
297        
298        // Maintain buffer size (keep last 200 candles)
299        while self.data_buffer.len() > 200 {
300            self.data_buffer.remove(0);
301        }
302
303        // Update regime detection if detector is enabled
304        if let Some(ref mut detector) = self.regime_detector {
305            let config = RegimeConfig::default();
306            if let Some(state) = detector.update(candle, &config) {
307                if let Some(ref mut tracker) = self.regime_tracker {
308                    tracker.record(state);
309                }
310            }
311        }
312    }
313
314    /// Generates regime-aware OHLC series with market transitions
315    pub fn generate_regime_aware_series(&mut self, count: usize, _regime_config: RegimeConfig) -> Vec<RegimeOHLC> {
316        let mut series = Vec::with_capacity(count);
317        
318        for _ in 0..count {
319            let candle = self.generate_ohlc();
320            let current_regime = self.current_regime().cloned();
321            series.push(RegimeOHLC {
322                ohlc: candle,
323                regime_state: current_regime,
324            });
325        }
326        
327        series
328    }
329
330    /// Generates OHLC series with controlled regime schedule
331    pub fn generate_controlled_regime_series(&mut self, count: usize) -> Vec<ControlledRegimeOHLC> {
332        let mut series = Vec::with_capacity(count);
333        
334        for _ in 0..count {
335            let schedule_info = self.regime_control_info();
336            let candle = self.generate_ohlc();
337            
338            series.push(ControlledRegimeOHLC {
339                ohlc: candle,
340                schedule_info: schedule_info.clone(),
341            });
342        }
343        
344        series
345    }
346
347    /// Gets the data buffer for analysis
348    pub fn data_buffer(&self) -> &[OHLC] {
349        &self.data_buffer
350    }
351
352    /// Detects regime on historical data buffer
353    pub fn detect_regime_on_buffer(&mut self) -> Option<RegimeState> {
354        if let Some(ref mut detector) = self.regime_detector {
355            let config = RegimeConfig::default();
356            detector.detect(&self.data_buffer, &config)
357        } else {
358            None
359        }
360    }
361
362    /// Enables regime control with a given schedule
363    pub fn enable_regime_control(&mut self, schedule: RegimeSchedule) {
364        let base_config = self.config.clone();
365        self.regime_controller = Some(RegimeController::new(schedule, base_config));
366    }
367
368    /// Disables regime control
369    pub fn disable_regime_control(&mut self) {
370        self.regime_controller = None;
371    }
372
373    /// Gets current regime control information
374    pub fn regime_control_info(&self) -> Option<ScheduleInfo> {
375        self.regime_controller.as_ref().map(|c| c.schedule_info())
376    }
377
378    /// Forces an immediate regime change (overriding current schedule)
379    pub fn force_regime(&mut self, regime: crate::regimes::MarketRegime, duration: usize, transition_duration: Option<usize>) {
380        if let Some(ref mut controller) = self.regime_controller {
381            controller.force_regime(regime, duration, transition_duration);
382        }
383    }
384
385    /// Adds a regime segment to the current schedule
386    pub fn add_regime_segment(&mut self, segment: crate::regimes::RegimeSegment) {
387        if let Some(ref mut controller) = self.regime_controller {
388            controller.add_segment(segment);
389        }
390    }
391
392    /// Resets the regime schedule to the beginning
393    pub fn reset_regime_schedule(&mut self) {
394        if let Some(ref mut controller) = self.regime_controller {
395            controller.reset();
396        }
397    }
398
399    /// Updates regime controller and applies configuration changes
400    fn update_regime_controller(&mut self) {
401        if let Some(ref mut controller) = self.regime_controller {
402            controller.advance();
403            
404            // Update the generator configuration with the regime-controlled config
405            let new_config = controller.current_config().clone();
406            if let Ok(new_price_generator) = RandomWalkGenerator::new(new_config.clone()) {
407                self.config = new_config;
408                self.price_generator = new_price_generator;
409            }
410        }
411    }
412}
413
414/// OHLC data with regime information
415#[cfg(feature = "regimes")]
416#[derive(Debug, Clone)]
417pub struct RegimeOHLC {
418    /// OHLC candle data
419    pub ohlc: OHLC,
420    /// Associated regime state (if detected)
421    pub regime_state: Option<RegimeState>,
422}
423
424/// OHLC data with controlled regime schedule information
425#[cfg(feature = "regimes")]
426#[derive(Debug, Clone)]
427pub struct ControlledRegimeOHLC {
428    /// OHLC candle data
429    pub ohlc: OHLC,
430    /// Current schedule information from regime controller
431    pub schedule_info: Option<ScheduleInfo>,
432}
433
434/// Regime detection analytics
435#[cfg(feature = "regimes")]
436#[derive(Debug, Clone)]
437pub struct RegimeAnalytics {
438    /// Current regime state
439    pub current_regime: Option<RegimeState>,
440    /// Total number of regime transitions
441    pub transitions: usize,
442    /// Average regime duration
443    pub average_duration: Decimal,
444    /// Time spent in each regime (bull, bear, sideways)
445    pub regime_distribution: (Decimal, Decimal, Decimal),
446}
447
448impl Default for MarketDataGenerator {
449    fn default() -> Self {
450        Self::new()
451    }
452}
453
454#[cfg(test)]
455mod tests {
456    use super::*;
457    use crate::TrendDirection;
458    use crate::config::ConfigBuilder;
459
460    #[test]
461    fn test_generator_creation() {
462        let generator = MarketDataGenerator::new();
463        assert_eq!(generator.config().starting_price, Decimal::from_f64(100.0).unwrap());
464    }
465
466    #[test]
467    fn test_generator_with_config() {
468        let config = ConfigBuilder::new()
469            .starting_price_f64(50.0)
470            .volatility_f64(0.03)
471            .seed(42)
472            .build()
473            .unwrap();
474        
475        let generator = MarketDataGenerator::with_config(config);
476        assert!(generator.is_ok());
477    }
478
479    #[test]
480    fn test_candle_generation() {
481        let config = ConfigBuilder::new()
482            .seed(42)
483            .build()
484            .unwrap();
485        
486        let mut generator = MarketDataGenerator::with_config(config).unwrap();
487        let candle = generator.generate_ohlc();
488        
489        assert!(candle.is_valid());
490        assert!(candle.volume.value() > 0);
491    }
492
493    #[test]
494    fn test_series_generation() {
495        let config = ConfigBuilder::new()
496            .seed(42)
497            .num_points(10)
498            .build()
499            .unwrap();
500        
501        let mut generator = MarketDataGenerator::with_config(config).unwrap();
502        let series = generator.generate_series(10);
503        
504        assert_eq!(series.len(), 10);
505        
506        // Check timestamps are increasing
507        for i in 1..series.len() {
508            assert!(series[i].timestamp > series[i-1].timestamp);
509        }
510    }
511
512    #[test]
513    fn test_tick_generation() {
514        let mut generator = MarketDataGenerator::new();
515        let tick = generator.generate_tick();
516        
517        assert!(tick.price > Decimal::ZERO);
518        assert!(tick.volume.value() > 0);
519        assert!(tick.bid.is_some());
520        assert!(tick.ask.is_some());
521        
522        if let (Some(bid), Some(ask)) = (tick.bid, tick.ask) {
523            assert!(ask > bid); // Spread should be positive
524        }
525    }
526
527    #[test]
528    fn test_deterministic_generation() {
529        let config = ConfigBuilder::new()
530            .seed(42)
531            .build()
532            .unwrap();
533        
534        let mut gen1 = MarketDataGenerator::with_config(config.clone()).unwrap();
535        let mut gen2 = MarketDataGenerator::with_config(config).unwrap();
536        
537        let candles1 = gen1.generate_series(5);
538        let candles2 = gen2.generate_series(5);
539        
540        // With same seed, should generate same data
541        for (c1, c2) in candles1.iter().zip(candles2.iter()) {
542            assert_eq!(c1.open, c2.open);
543            assert_eq!(c1.close, c2.close);
544        }
545    }
546
547    #[test]
548    fn test_reset() {
549        let config = ConfigBuilder::new()
550            .seed(42)
551            .starting_price_f64(100.0)
552            .build()
553            .unwrap();
554        
555        let mut generator = MarketDataGenerator::with_config(config).unwrap();
556        
557        let candle1 = generator.generate_ohlc();
558        generator.reset();
559        let candle2 = generator.generate_ohlc();
560        
561        // After reset with same seed, should generate same values
562        assert_eq!(candle1.open, candle2.open);
563    }
564
565    #[cfg(feature = "regimes")]
566    #[test]
567    fn test_regime_control_basic() {
568        use crate::regimes::{RegimeSchedule, RegimeSegment, MarketRegime};
569        use crate::config::TrendDirection;
570        
571        let config = ConfigBuilder::new()
572            .seed(42)
573            .build()
574            .unwrap();
575        
576        let mut generator = MarketDataGenerator::with_config(config).unwrap();
577        
578        // Create a simple schedule: 5 points Bull, 5 points Bear
579        let segments = vec![
580            RegimeSegment::new(MarketRegime::Bull, 5),
581            RegimeSegment::new(MarketRegime::Bear, 5),
582        ];
583        let schedule = RegimeSchedule::new(segments);
584        
585        generator.enable_regime_control(schedule);
586        
587        // Generate first batch - should be bull market
588        let bull_candles = generator.generate_series(5);
589        let info = generator.regime_control_info().unwrap();
590        assert_eq!(info.current_regime, Some(MarketRegime::Bear)); // Should have switched after 5
591        
592        // Generate second batch - should be bear market
593        let bear_candles = generator.generate_series(5);
594        let info = generator.regime_control_info().unwrap();
595        assert!(info.is_complete); // Should be complete after 10 total
596        
597        // Verify we have proper OHLC data
598        assert_eq!(bull_candles.len(), 5);
599        assert_eq!(bear_candles.len(), 5);
600        
601        for candle in &bull_candles {
602            assert!(candle.is_valid());
603        }
604        for candle in &bear_candles {
605            assert!(candle.is_valid());
606        }
607    }
608
609    #[cfg(feature = "regimes")]
610    #[test]
611    fn test_regime_control_parameter_changes() {
612        use crate::regimes::{RegimeSchedule, RegimeSegment, MarketRegime};
613        
614        let config = ConfigBuilder::new()
615            .seed(42)
616            .trend_f64(TrendDirection::Sideways, 0.001) // Very small base trend
617            .build()
618            .unwrap();
619        
620        let mut generator = MarketDataGenerator::with_config(config).unwrap();
621        
622        // Create schedule with different regimes
623        let segments = vec![
624            RegimeSegment::new(MarketRegime::Bull, 3),
625            RegimeSegment::new(MarketRegime::Sideways, 3),
626        ];
627        let schedule = RegimeSchedule::new(segments);
628        
629        generator.enable_regime_control(schedule);
630        
631        // Generate during bull period
632        let config1 = generator.config().clone();
633        generator.generate_ohlc();
634        generator.generate_ohlc();
635        
636        // Should still be bull
637        assert_eq!(generator.config().trend_direction, TrendDirection::Bullish);
638        
639        // Generate one more to trigger regime change
640        generator.generate_ohlc();
641        
642        // Should now be sideways
643        let config2 = generator.config().clone();
644        assert_eq!(config2.trend_direction, TrendDirection::Sideways);
645        assert_ne!(config1.trend_strength, config2.trend_strength); // Should be different
646    }
647
648    #[cfg(feature = "regimes")]
649    #[test]
650    fn test_regime_control_force_regime() {
651        use crate::regimes::{RegimeSchedule, RegimeSegment, MarketRegime};
652        
653        let config = ConfigBuilder::new()
654            .seed(42)
655            .build()
656            .unwrap();
657        
658        let mut generator = MarketDataGenerator::with_config(config).unwrap();
659        
660        // Start with a schedule
661        let segments = vec![
662            RegimeSegment::new(MarketRegime::Bull, 10),
663        ];
664        let schedule = RegimeSchedule::new(segments);
665        generator.enable_regime_control(schedule);
666        
667        // Verify initial regime
668        let info = generator.regime_control_info().unwrap();
669        assert_eq!(info.current_regime, Some(MarketRegime::Bull));
670        
671        // Force a different regime
672        generator.force_regime(MarketRegime::Bear, 5, None);
673        
674        // Verify regime changed
675        let info = generator.regime_control_info().unwrap();
676        assert_eq!(info.current_regime, Some(MarketRegime::Bear));
677    }
678
679    #[cfg(feature = "regimes")]
680    #[test]
681    fn test_controlled_regime_series() {
682        use crate::regimes::{RegimeSchedule, RegimeSegment, MarketRegime};
683        
684        let config = ConfigBuilder::new()
685            .seed(42)
686            .build()
687            .unwrap();
688        
689        let mut generator = MarketDataGenerator::with_config(config).unwrap();
690        
691        let segments = vec![
692            RegimeSegment::new(MarketRegime::Bull, 3),
693            RegimeSegment::new(MarketRegime::Bear, 2),
694        ];
695        let schedule = RegimeSchedule::new(segments);
696        generator.enable_regime_control(schedule);
697        
698        // Generate controlled series
699        let series = generator.generate_controlled_regime_series(5);
700        
701        assert_eq!(series.len(), 5);
702        
703        // First 3 should be bull market
704        for i in 0..3 {
705            if let Some(ref info) = series[i].schedule_info {
706                assert_eq!(info.current_regime, Some(MarketRegime::Bull));
707            }
708        }
709        
710        // Last 2 should be bear market
711        for i in 3..5 {
712            if let Some(ref info) = series[i].schedule_info {
713                assert_eq!(info.current_regime, Some(MarketRegime::Bear));
714            }
715        }
716    }
717
718    #[cfg(feature = "regimes")]
719    #[test]
720    fn test_regime_schedule_reset() {
721        use crate::regimes::{RegimeSchedule, RegimeSegment, MarketRegime};
722        
723        let config = ConfigBuilder::new()
724            .seed(42)
725            .build()
726            .unwrap();
727        
728        let mut generator = MarketDataGenerator::with_config(config).unwrap();
729        
730        let segments = vec![
731            RegimeSegment::new(MarketRegime::Bull, 2),
732            RegimeSegment::new(MarketRegime::Bear, 2),
733        ];
734        let schedule = RegimeSchedule::new(segments);
735        generator.enable_regime_control(schedule);
736        
737        // Generate all data
738        generator.generate_series(4);
739        let info = generator.regime_control_info().unwrap();
740        assert!(info.is_complete);
741        
742        // Reset schedule
743        generator.reset_regime_schedule();
744        let info = generator.regime_control_info().unwrap();
745        assert!(!info.is_complete);
746        assert_eq!(info.current_regime, Some(MarketRegime::Bull)); // Should be back to start
747    }
748}