private void StartEmulation() { if (_connector != null && _connector.State != EmulationStates.Stopped) { throw new InvalidOperationException(LocalizedStrings.Str3015); } if (Strategy == null) { throw new InvalidOperationException("Strategy not selected."); } var strategy = (EmulationDiagramStrategy)Strategy; var settings = strategy.EmulationSettings; if (settings.MarketDataSettings == null) { throw new InvalidOperationException(LocalizedStrings.Str3014); } new SetDefaultEmulationSettingsCommand(settings).Process(this); strategy .Composition .Parameters .ForEach(p => { if (p.Type == typeof(Security) && p.Value == null) { throw new InvalidOperationException(LocalizedStrings.Str1380); } }); strategy.Reset(); Reset(); var securityId = "empty@empty"; var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(securityId); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = settings.CandlesTimeFrame; var useCandles = settings.MarketDataSource == MarketDataSource.Candles; // create test security var security = new Security { Id = securityId, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StudioStorageRegistry { MarketDataSettings = settings.MarketDataSettings }; var startTime = settings.StartDate.ChangeKind(DateTimeKind.Utc); var stopTime = settings.StopDate.ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; var securityProvider = ConfigManager.GetService <ISecurityProvider>(); // create backtesting connector _connector = new HistoryEmulationConnector(securityProvider, new[] { portfolio }, new StorageRegistry()) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = settings.MatchOnTouch, IsSupportAtomicReRegister = settings.IsSupportAtomicReRegister, Latency = settings.EmulatoinLatency, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, StorageFormat = settings.StorageFormat, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)_connector).LogLevel = settings.DebugLog ? LogLevels.Debug : LogLevels.Info; ConfigManager.GetService <LogManager>().Sources.Add(_connector); strategy.Volume = 1; strategy.Portfolio = portfolio; strategy.Security = security; strategy.Connector = _connector; strategy.LogLevel = settings.DebugLog ? LogLevels.Debug : LogLevels.Info; // by default interval is 1 min, // it is excessively for time range with several months strategy.UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>(); strategy.SetCandleManager(new CandleManager(_connector)); _connector.NewSecurity += s => { //TODO send real level1 message var level1Info = new Level1ChangeMessage { SecurityId = s.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // fill level1 values _connector.SendInMessage(level1Info); if (settings.UseMarketDepths) { _connector.RegisterMarketDepth(security); if ( // if order book will be generated settings.GenerateDepths || // of backtesting will be on candles useCandles ) { // if no have order book historical data, but strategy is required, // use generator based on last prices _connector.RegisterMarketDepth(new TrendMarketDepthGenerator(_connector.GetSecurityId(s)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = settings.MaxDepths, MaxBidsDepth = settings.MaxDepths, UseTradeVolume = true, MaxVolume = settings.MaxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) { return; } var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To <TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.LookupSecuritiesResult += (ss) => { if (strategy.ProcessState != ProcessStates.Stopped) { return; } // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); this.GuiAsync(() => { if (_connector.IsFinished) { TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; } }); break; case EmulationStates.Started: break; } }; _connector.Disconnected += () => { this.GuiAsync(() => _connector.Dispose()); }; TicksAndDepthsProgress.Value = 0; DiagramDebuggerControl.Debugger.IsEnabled = true; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }