/// <summary>
        /// MainWindow constructor, create chart ViewModels setup ObservableCollections for View binding
        /// </summary>
        /// <param name="dbuilder">DataBuilder object, data source for charts</param>
        public MainWindowViewModel(DataBuilder dbuilder)
        {
            DBuilder = dbuilder;

            // candlestick and volume charts
            int nseries = 30; // number of data points in candlestick and volume

            CSChartVM       = new ViewModelCandleStickChart(DBuilder.OHLC[0], nseries);
            CSOpenCloseUp   = CSChartVM.CSOpenCloseUp;
            CSOpenCloseDown = CSChartVM.CSOpenCloseDown;
            CSHighLowUp     = CSChartVM.CSHighLowUp;
            CSHighLowDown   = CSChartVM.CSHighLowDown;

            VolumeChartVM = new ViewModelVolumeChart(DBuilder.Volume[0], nseries);
            BidVolume     = VolumeChartVM.VBid;
            AskVolume     = VolumeChartVM.VAsk;
            TradedVolume  = VolumeChartVM.VTraded;

            CSXAxisMax       = CSChartVM.SDataCount + 1;      // axis
            CSLabelFormatter = CSChartVM.XAxisLabelFormatter; // label formatter

            // bid ask trade value distribution charts
            BidVDChartVM    = new ViewModelValueDistChart <DataTMValueDistribution>(DBuilder.TFAnalytics.BidVolumeDist[0], "vertical", "negative");
            AskVDChartVM    = new ViewModelValueDistChart <DataTMValueDistribution>(DBuilder.TFAnalytics.AskVolumeDist[0], "vertical");
            TradedVDChartVM = new ViewModelValueDistChart <DataTMValueDistribution>(DBuilder.TFAnalytics.TradedVolumeDist[0], "vertical", "negative");
            BidVDist        = BidVDChartVM.ValueDist;
            AskVDist        = AskVDChartVM.ValueDist;
            TradedVDist     = TradedVDChartVM.ValueDist;

            BATVDistYAxisMin    = BATVDistYAxisMax = 0;            // axis
            LabelFormatterEmpty = (d0) => "";                      // label formatter empty
            LabelFormatterAbs   = (d0) => Math.Abs(d0).ToString(); // label formmatter absolute value

            // price range, volume total value distribution charts
            PriceRDChartVM  = new ViewModelValueDistChart <DataTMValueRangeDistribution>(DBuilder.TFAnalytics.PriceRangeDist[0]);
            VolumeTDChartVM = new ViewModelValueDistChart <DataTMValueTotalDistribution>(DBuilder.TFAnalytics.VolumeTotalDist[0]);
            PriceRDist      = PriceRDChartVM.ValueDist;
            VolumeTDist     = VolumeTDChartVM.ValueDist;

            // quotes text display
            QuotesTextVM  = new ViewModelQuotesText(DBuilder.Current);
            QuotesTextStr = QuotesTextVM.TextStr;

            // vwap text display
            VWAPTextVM  = new ViewModelVWAPText(DBuilder.TFAnalytics.VWAP);
            VWAPTextStr = VWAPTextVM.TextStr;

            // add data source selection buttons each set of charts
            ButtonsVM = new ViewModelButtonsSelect();
            ButtonsVM.AddSrcDS("OHLCVolume", DBuilder.OHLC, "View OHLC Volume: ");
            ButtonsVM.AddSrcDS("BATVDist", DBuilder.TFAnalytics.BidVolumeDist);
            ButtonsVM.AddSrcDS("PriceRDist", DBuilder.TFAnalytics.PriceRangeDist);
            ButtonsVM.AddSrcDS("VolumeTDist", DBuilder.TFAnalytics.VolumeTotalDist);

            ButtonsVM.AddSrcString("SimControl",
                                   new Collection <string> {
                "PauseContinueSim", "Forward"
            },
                                   new Collection <string> {
                "Pause / Continue Simulation", "Run simulation forward [input] number of prints in background"
            });

            ButtonsBind = ButtonsVM.BtnBind;
            BtnsSimControlVisibility = System.Windows.Visibility.Hidden;

            // selection button click command handler
            ButtonsClick = new RelayCommand(SelectSourceCommand);
        }
        public App()
        {
            // initialize DataFeedSimulator and DataBuilder objects
            // change simulator to datafeed api to handle real-time data
            DataFeedSim = new DataFeedSimulator();
            DBuilder    = new DataBuilder();

            // build TimeSeries OHLC and Volume with different time settings
            DBuilder.NewTimeSeriesOHLCVolume(1);
            DBuilder.NewTimeSeriesOHLCVolume(5);
            DBuilder.NewTimeSeriesOHLCVolume(10);
            DBuilder.NewTimeSeriesOHLCVolume(17);
            DBuilder.NewTimeSeriesOHLCVolume(30);
            DBuilder.NewTimeSeriesOHLCVolume(60);

            // create TimeFrame with default time and value settings.
            // it would greatly enhance the application to handle dynamic
            // data object construction from user input
            DBuilder.NewTimeFrameAnalytics();

            // create GUI window
            MWindow = new MainWindow(new MainWindowViewModel(DBuilder));

            // add event listeners
            DataFeedSim.PrintUpdate    += DBuilder.HandlePrintUpdate;  // DataBuilder listen to new prints event from DataFeed Simulator
            DBuilder.DataUpdate        += MWindow.VM.HandleDataUpdate; // on data update, update GUI
            MWindow.VM.SimControlEvent += HandleSimControlClick;       // on SimControl button click event, DataFeed responding accordingly
            MWindow.WindowClosed       += HandleWindowClosed;          // add window close, app shutdown to main window closed event
            DataFeedSim.EndofDataFile  += EndofDataFileDialog;         // end of data file dialog on DataFeedSimulator end of data file event

            // file paths
            string data_dir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @"Data\");
            string zip_path = Path.Combine(data_dir, @"trade_data.zip");
            string csv_path = Path.Combine(data_dir, @"trade_data.csv");

            if (!File.Exists(csv_path))
            {
                // if data csv file does not exist, check if data zip file exist and extract
                if (File.Exists(zip_path))
                {
                    ZipFile.ExtractToDirectory(zip_path, data_dir);
                }
                else
                {
                    MessageBox.Show(string.Format("Please ensure the following directory contains either trade_data.csv or trade_data.zip. A sample trade_data.zip is found in `SolutionDir\\Data`\n     {0}\n\nApplication will shutdown after message box close.", data_dir),
                                    "Can Not Find Data File");
                }
            }

            // start simulation if data csv file exist, otherwise shutdown
            if (File.Exists(csv_path))
            {
                MWindow.Show();
                DataFeedSim.SetDataFile(csv_path);
                DataFeedSim.StartSimulation();
            }
            else
            {
                Application.Current.Shutdown();
            }
        }