private void ChartUpdateTimerOnTick(object sender, EventArgs eventArgs) { if (IsRealtime.IsChecked == true && _lastPrice != 0m) { var step = _priceStep; var price = Round(_lastPrice + (decimal)((RandomGen.GetDouble() - 0.5) * 5 * (double)step), step); AppendTick(_security, new ExecutionMessage { ServerTime = _lastTime, TradePrice = price, TradeVolume = RandomGen.GetInt(50) + 1, OriginSide = Sides.Buy, }); _lastTime += TimeSpan.FromSeconds(10); } TimeFrameCandle[] candlesToUpdate; lock (_updatedCandles.SyncRoot) { candlesToUpdate = _updatedCandles.OrderBy(p => p.Key).Select(p => p.Value).ToArray(); _updatedCandles.Clear(); } var lastCandle = _allCandles.LastOrDefault(); _allCandles.AddRange(candlesToUpdate.Where(c => lastCandle == null || c.OpenTime != lastCandle.OpenTime)); var hasValue = false; var chartData = new ChartDrawData(); foreach (var candle in candlesToUpdate) { chartData.Group(candle.OpenTime).Add(_candleElement1, candle); hasValue = true; } if (hasValue) { Chart.Draw(chartData); } }
private void CustomColors2_Changed(object sender, RoutedEventArgs e) { var colored = CustomColors2.IsChecked == true; _drawWithColor = colored; _dataThreadActions.Add(() => { if (_allCandles.IsEmpty()) { return; } var dd = new ChartDrawData(); foreach (var c in _allCandles) { dd.Group(c.Value.OpenTime).Add(_candleElement, colored ? GetRandomColor() : (Color?)null); } Chart.Draw(dd); }); }
private void ChartUpdateTimerOnTick(object sender, EventArgs eventArgs) { TimeFrameCandle[] candlesToUpdate; lock (_updatedCandles.SyncRoot) { candlesToUpdate = _updatedCandles.OrderBy(p => p.Key).Select(p => p.Value).ToArray(); _updatedCandles.Clear(); } var lastCandle = _allCandles.LastOrDefault(); _allCandles.AddRange(candlesToUpdate.Where(c => lastCandle == null || c.OpenTime != lastCandle.OpenTime)); var data = new ChartDrawData(); foreach (var candle in candlesToUpdate) { data.Group(candle.OpenTime).Add(_candleElement, candle); } Chart.Draw(data); }
private void StartBtnClick(object sender, RoutedEventArgs e) { if (_connectors.Count > 0) { foreach (var connector in _connectors) { connector.Start(); } return; } if (HistoryPath.Folder.IsEmpty() || !Directory.Exists(HistoryPath.Folder)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (_connectors.Any(t => t.State != EmulationStates.Stopped)) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var id = SecId.Text.ToSecurityId(); //if (secIdParts.Length != 2) //{ // MessageBox.Show(this, LocalizedStrings.Str3016); // return; //} var timeFrame = TimeSpan.FromMinutes(TimeFrame.SelectedIndex == 0 ? 1 : 5); var secCode = id.SecurityCode; var board = _exchangeInfoProvider.GetOrCreateBoard(id.BoardCode); // create test security var security = new Security { Id = SecId.Text, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // create backtesting modes var settings = new[] { Tuple.Create( TicksCheckBox, TicksProgress, TicksParameterGrid, // ticks new EmulationInfo { UseTicks = true, CurveColor = Colors.DarkGreen, StrategyName = LocalizedStrings.Ticks }, TicksChart, TicksEquity, TicksPosition), Tuple.Create( TicksAndDepthsCheckBox, TicksAndDepthsProgress, TicksAndDepthsParameterGrid, // ticks + order book new EmulationInfo { UseTicks = true, UseMarketDepth = true, CurveColor = Colors.Red, StrategyName = LocalizedStrings.XamlStr757 }, TicksAndDepthsChart, TicksAndDepthsEquity, TicksAndDepthsPosition), Tuple.Create( DepthsCheckBox, DepthsProgress, DepthsParameterGrid, // order book new EmulationInfo { UseMarketDepth = true, CurveColor = Colors.OrangeRed, StrategyName = LocalizedStrings.MarketDepths }, DepthsChart, DepthsEquity, DepthsPosition), Tuple.Create( CandlesCheckBox, CandlesProgress, CandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Candles }, CandlesChart, CandlesEquity, CandlesPosition), Tuple.Create( CandlesAndDepthsCheckBox, CandlesAndDepthsProgress, CandlesAndDepthsParameterGrid, // candles + orderbook new EmulationInfo { UseMarketDepth = true, UseCandleTimeFrame = timeFrame, CurveColor = Colors.Cyan, StrategyName = LocalizedStrings.XamlStr635 }, CandlesAndDepthsChart, CandlesAndDepthsEquity, CandlesAndDepthsPosition), Tuple.Create( OrderLogCheckBox, OrderLogProgress, OrderLogParameterGrid, // order log new EmulationInfo { UseOrderLog = true, CurveColor = Colors.CornflowerBlue, StrategyName = LocalizedStrings.OrderLog }, OrderLogChart, OrderLogEquity, OrderLogPosition), Tuple.Create( Level1CheckBox, Level1Progress, Level1ParameterGrid, // order log new EmulationInfo { UseLevel1 = true, CurveColor = Colors.Aquamarine, StrategyName = LocalizedStrings.Level1 }, Level1Chart, Level1Equity, Level1Position), Tuple.Create( FinamCandlesCheckBox, FinamCandlesProgress, FinamCandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CustomHistoryAdapter = g => new FinamMessageAdapter(g), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.FinamCandles }, FinamCandlesChart, FinamCandlesEquity, FinamCandlesPosition), Tuple.Create( YahooCandlesCheckBox, YahooCandlesProgress, YahooCandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CustomHistoryAdapter = g => new YahooMessageAdapter(g), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.YahooCandles }, YahooCandlesChart, YahooCandlesEquity, YahooCandlesPosition), Tuple.Create( RandomCheckBox, RandomProgress, RandomParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CustomHistoryAdapter = g => new OwnMessageAdapter(g), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Custom }, RandomChart, RandomEquity, RandomPosition), }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Folder) }; var startTime = ((DateTime)From.EditValue).UtcKind(); var stopTime = ((DateTime)To.EditValue).UtcKind(); // (ru only) ОЛ необходимо загружать с 18.45 пред дня, чтобы стаканы строились правильно if (OrderLogCheckBox.IsChecked == true) { startTime = startTime.Subtract(TimeSpan.FromDays(1)).AddHours(18).AddMinutes(45).AddTicks(1).ApplyTimeZone(TimeHelper.Moscow).UtcDateTime; } // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // set ProgressBar bounds _progressBars.ForEach(p => { p.Value = 0; p.Maximum = 100; }); var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); //logManager.Listeners.Add(new DebugLogListener()); // for track logs in output window in Vusial Studio (poor performance). var generateDepths = GenDepthsCheckBox.IsChecked == true; var maxDepth = MaxDepth.Text.To <int>(); var maxVolume = MaxVolume.Text.To <int>(); var secId = security.ToSecurityId(); SetIsEnabled(false, false, false); foreach (var set in settings) { if (set.Item1.IsChecked == false) { continue; } var title = (string)set.Item1.Content; InitChart(set.Item5, set.Item6, set.Item7); var progressBar = set.Item2; var statistic = set.Item3; var emulationInfo = set.Item4; var level1Info = new Level1ChangeMessage { SecurityId = secId, ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = Portfolio.CreateSimulator(); var secProvider = (ISecurityProvider) new CollectionSecurityProvider(new[] { security }); // create backtesting connector var connector = new HistoryEmulationConnector( secProvider, new[] { portfolio }) { 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 = false, } } }, //UseExternalCandleSource = emulationInfo.UseCandleTimeFrame != null, //CreateDepthFromOrdersLog = emulationInfo.UseOrderLog, //CreateTradesFromOrdersLog = emulationInfo.UseOrderLog, HistoryMessageAdapter = { StorageRegistry = storageRegistry, OrderLogMarketDepthBuilders = { { secId, new OrderLogMarketDepthBuilder(secId) } } }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; logManager.Sources.Add(connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame) { BuildCandlesMode = emulationInfo.UseCandleTimeFrame == null ? MarketDataBuildModes.Build : MarketDataBuildModes.Load, BuildCandlesFrom = emulationInfo.UseOrderLog ? (MarketDataTypes?)MarketDataTypes.OrderLog : null, }; _shortMa = new SimpleMovingAverage { Length = 10 }; _shortElem = new ChartIndicatorElement { Color = Colors.Coral, ShowAxisMarker = false, FullTitle = _shortMa.ToString() }; var chart = set.Item5; chart.AddElement(_area, _shortElem); _longMa = new SimpleMovingAverage { Length = 80 }; _longElem = new ChartIndicatorElement { ShowAxisMarker = false, FullTitle = _longMa.ToString() }; chart.AddElement(_area, _longElem); // create strategy based on 80 5-min и 10 5-min var strategy = new SmaStrategy(chart, _candlesElem, _tradesElem, _shortMa, _shortElem, _longMa, _longElem, series) { Volume = 1, Portfolio = portfolio, Security = security, Connector = connector, LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; logManager.Sources.Add(strategy); if (emulationInfo.CustomHistoryAdapter != null) { connector.Adapter.InnerAdapters.Remove(connector.MarketDataAdapter); connector.Adapter.InnerAdapters.Add(new CustomHistoryMessageAdapter(emulationInfo.CustomHistoryAdapter(connector.TransactionIdGenerator), secProvider)); } // set history range connector.HistoryMessageAdapterEx.StartDate = startTime; connector.HistoryMessageAdapterEx.StopDate = stopTime; connector.NewSecurity += s => { if (s != security) { return; } // fill level1 values connector.HistoryMessageAdapterEx.SendOutMessage(level1Info); if (emulationInfo.UseMarketDepth) { connector.SubscribeMarketDepth(security); if ( // if order book will be generated generateDepths || // or backtesting will be on candles emulationInfo.UseCandleTimeFrame != TimeSpan.Zero ) { // if no have order book historical data, but strategy is required, // use generator based on last prices connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = maxDepth, MaxBidsDepth = maxDepth, UseTradeVolume = true, MaxVolume = maxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } if (emulationInfo.UseOrderLog) { connector.SubscribeOrderLog(security); } if (emulationInfo.UseTicks) { connector.SubscribeTrades(security); } if (emulationInfo.UseLevel1) { connector.SubscribeLevel1(security); } // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed connector.Start(); }; // fill parameters panel statistic.Parameters.Clear(); statistic.Parameters.AddRange(strategy.StatisticManager.Parameters); var equity = set.Item6; var pnlCurve = equity.CreateCurve(LocalizedStrings.PnL + " " + emulationInfo.StrategyName, Colors.Green, Colors.Red, ChartIndicatorDrawStyles.Area); var unrealizedPnLCurve = equity.CreateCurve(LocalizedStrings.PnLUnreal + " " + emulationInfo.StrategyName, Colors.Black, ChartIndicatorDrawStyles.Line); var commissionCurve = equity.CreateCurve(LocalizedStrings.Str159 + " " + emulationInfo.StrategyName, Colors.Red, ChartIndicatorDrawStyles.DashedLine); strategy.PnLChanged += () => { var data = new ChartDrawData(); data .Group(strategy.CurrentTime) .Add(pnlCurve, strategy.PnL - strategy.Commission ?? 0) .Add(unrealizedPnLCurve, strategy.PnLManager.UnrealizedPnL ?? 0) .Add(commissionCurve, strategy.Commission ?? 0); equity.Draw(data); }; var posItems = set.Item7.CreateCurve(emulationInfo.StrategyName, emulationInfo.CurveColor, ChartIndicatorDrawStyles.Line); strategy.PositionChanged += () => { var data = new ChartDrawData(); data .Group(strategy.CurrentTime) .Add(posItems, strategy.Position); set.Item7.Draw(data); }; 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(() => progressBar.Value = steps); }; connector.StateChanged += () => { if (connector.State == EmulationStates.Stopped) { strategy.Stop(); SetIsChartEnabled(chart, false); if (_connectors.All(c => c.State == EmulationStates.Stopped)) { logManager.Dispose(); _connectors.Clear(); SetIsEnabled(true, false, false); } this.GuiAsync(() => { if (connector.IsFinished) { progressBar.Value = progressBar.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime), title); } else { MessageBox.Show(this, LocalizedStrings.cancelled, title); } }); } else if (connector.State == EmulationStates.Started) { if (_connectors.All(c => c.State == EmulationStates.Started)) { SetIsEnabled(false, true, true); } SetIsChartEnabled(chart, true); } else if (connector.State == EmulationStates.Suspended) { if (_connectors.All(c => c.State == EmulationStates.Suspended)) { SetIsEnabled(true, false, true); } } }; if (ShowDepth.IsChecked == true) { MarketDepth.UpdateFormat(security); connector.NewMessage += message => { if (message is QuoteChangeMessage quoteMsg) { MarketDepth.UpdateDepth(quoteMsg); } }; } _connectors.Add(connector); progressBar.Value = 0; } _startEmulationTime = DateTime.Now; // start emulation foreach (var connector in _connectors) { // 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 } }); } TabControl.Items.Cast <TabItem>().First(i => i.Visibility == Visibility.Visible).IsSelected = true; }
private void StartBtnClick(object sender, RoutedEventArgs e) { if (_batchEmulation != null) { _batchEmulation.Resume(); return; } if (HistoryPath.Folder.IsEmpty() || !Directory.Exists(HistoryPath.Folder)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } TestingProcess.Value = 0; Curve.Clear(); Stat.Clear(); var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); // SMA periods var periods = new List <(int longMa, int shortMa, Color color)>(); for (var l = 100; l >= 50; l -= 10) { for (var s = 10; s >= 5; s -= 1) { periods.Add((l, s, Color.FromRgb((byte)RandomGen.GetInt(255), (byte)RandomGen.GetInt(255), (byte)RandomGen.GetInt(255)))); } } // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Folder) }; var timeFrame = TimeSpan.FromMinutes(1); // create test security var security = new Security { Id = "SBER@TQBR", // sec id has the same name as folder with historical data Code = "SBER", Name = "SBER", Board = ExchangeBoard.Micex, }; var startTime = new DateTime(2020, 4, 1); var stopTime = new DateTime(2020, 4, 20); var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 0.01m) .TryAdd(Level1Fields.StepPrice, 0.01m) .TryAdd(Level1Fields.MinPrice, 0.01m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = Portfolio.CreateSimulator(); // create backtesting connector _batchEmulation = new BatchEmulation(new[] { security }, new[] { portfolio }, storageRegistry) { EmulationSettings = { MarketTimeChangedInterval = timeFrame, StartTime = startTime, StopTime = stopTime, // count of parallel testing strategies // if not set, then CPU count * 2 //BatchSize = 3, } }; // handle historical time for update ProgressBar _batchEmulation.TotalProgressChanged += (currBatch, total) => this.GuiAsync(() => TestingProcess.Value = total); _batchEmulation.StateChanged += (oldState, newState) => { var isFinished = _batchEmulation.IsFinished; if (_batchEmulation.State == ChannelStates.Stopped) { _batchEmulation = null; } this.GuiAsync(() => { switch (newState) { case ChannelStates.Stopping: case ChannelStates.Starting: case ChannelStates.Suspending: SetIsEnabled(false, false, false); break; case ChannelStates.Stopped: SetIsEnabled(true, false, false); if (isFinished) { TestingProcess.Value = TestingProcess.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else { MessageBox.Show(this, LocalizedStrings.cancelled); } break; case ChannelStates.Started: SetIsEnabled(false, true, true); break; case ChannelStates.Suspended: SetIsEnabled(true, false, true); break; default: throw new ArgumentOutOfRangeException(newState.ToString()); } }); }; _startEmulationTime = DateTime.Now; var strategies = periods .Select(period => { var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); // create strategy based SMA var strategy = new SampleHistoryTesting.SmaStrategy(series) { ShortSma = { Length = period.shortMa }, LongSma = { Length = period.longMa }, Volume = 1, Security = security, Portfolio = portfolio, //Connector = connector, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; this.GuiSync(() => { var curveElem = Curve.CreateCurve(LocalizedStrings.Str3026Params.Put(period.Item1, period.Item2), period.Item3, ChartIndicatorDrawStyles.Line); strategy.PnLChanged += () => { var data = new ChartDrawData(); data .Group(strategy.CurrentTime) .Add(curveElem, strategy.PnL); Curve.Draw(data); }; Stat.AddStrategies(new[] { strategy }); }); return(strategy); }); // start emulation _batchEmulation.Start(strategies, periods.Count); }
private void LoadData() { _lastPrice = 0m; _candles.Clear(); var id = _securityId.ToSecurityId(); _security = new Security { Id = _securityId, PriceStep = _priceStep, Board = _exchangeInfoProvider.GetExchangeBoard(id.BoardCode) ?? ExchangeBoard.Associated }; Chart.Reset(new IChartElement[] { _candleElement }); var storage = new StorageRegistry(); var maxDays = 50; //BusyIndicator.IsBusy = true; var path = _historyPath; _curCandleNum = 0; Task.Factory.StartNew(() => { var date = DateTime.MinValue; foreach (var tick in storage.GetTickMessageStorage(_security, new LocalMarketDataDrive(path)).Load()) { if (date != tick.ServerTime.Date) { date = tick.ServerTime.Date; //var str = $"Loading ticks for {date:dd MMM yyyy}..."; //this.GuiAsync(() => BusyIndicator.BusyContent = str); if (--maxDays == 0) { break; } } AppendTick(tick); } }) .ContinueWith(t => { //this.GuiAsync(() => BusyIndicator.IsBusy = false); for (var i = 0; i < _candles.Count; i += _candlesPacketSize) { var data = new ChartDrawData(); var candles = _candles.GetRange(i, Math.Min(_candlesPacketSize, _candles.Count - i)).Select(c => c.ToCandle(_tfSpan, _security)); foreach (var candle in candles) { candle.State = CandleStates.Finished; var group = data.Group(candle.OpenTime); group.Add(_candleElement, candle); if (_indicatorElement != null) { group.Add(_indicatorElement, _indicator.Process((double)candle.ClosePrice)); } } Chart.Draw(data); } }) .ContinueWith(t => { if (t.Exception != null) { Error(t.Exception.Message); } _dataIsLoaded = true; //this.GuiAsync(() => BusyIndicator.IsBusy = false); Chart.IsAutoRange = false; //_area.YAxises.FirstOrDefault().Do(a => a.AutoRange = false); }, TaskScheduler.FromCurrentSynchronizationContext()); }
private void StartBtnClick(object sender, RoutedEventArgs e) { // if process was already started, will stop it now if (_connector != null && _connector.State != EmulationStates.Stopped) { _strategy.Stop(); _connector.Disconnect(); _logManager.Sources.Clear(); _connector = null; return; } // create test security var security = new Security { Id = "AAPL@NASDAQ", Code = "AAPL", Name = "AAPL Inc", Board = ExchangeBoard.Nasdaq, }; var startTime = new DateTime(2009, 6, 1); var stopTime = new DateTime(2009, 9, 1); var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; var timeFrame = TimeSpan.FromMinutes(5); // create backtesting connector _connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { HistoryMessageAdapter = { // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; _logManager.Sources.Add(_connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); // create strategy based on 80 5-min и 10 5-min _strategy = new SmaStrategy(_connector, series, new SimpleMovingAverage { Length = 80 }, new SimpleMovingAverage { Length = 10 }) { Volume = 1, Security = security, Portfolio = portfolio, Connector = _connector, }; _connector.NewSecurity += s => { if (s != security) { return; } // fill level1 values _connector.SendInMessage(level1Info); _connector.RegisterTrades(new RandomWalkTradeGenerator(_connector.GetSecurityId(security))); _connector.RegisterMarketDepth(new TrendMarketDepthGenerator(_connector.GetSecurityId(security)) { GenerateDepthOnEachTrade = false }); // start strategy before emulation started _strategy.Start(); _connector.SubscribeCandles(series); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; // fill parameters panel ParameterGrid.Parameters.Clear(); ParameterGrid.Parameters.AddRange(_strategy.StatisticManager.Parameters); _strategy.PnLChanged += () => { var data = new ChartDrawData(); data .Group(_strategy.CurrentTime) .Add(_curveElem, _strategy.PnL); Curve.Draw(data); }; _logManager.Sources.Add(_strategy); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); var nextTime = startTime + progressStep; TestingProcess.Maximum = 100; TestingProcess.Value = 0; // handle historical time for update ProgressBar _connector.MarketTimeChanged += diff => { 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(() => TestingProcess.Value = steps); }; _connector.StateChanged += () => { if (_connector.State == EmulationStates.Stopped) { this.GuiAsync(() => { Report.IsEnabled = true; if (_connector.IsFinished) { TestingProcess.Value = TestingProcess.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else { MessageBox.Show(this, LocalizedStrings.cancelled); } }); } }; Curve.Reset(new[] { _curveElem }); Report.IsEnabled = false; _startEmulationTime = DateTime.Now; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); }
private void ProcessCandle(Candle candle) { // strategy are stopping if (ProcessState == ProcessStates.Stopping) { CancelActiveOrders(); return; } this.AddInfoLog(LocalizedStrings.Str3634Params.Put(candle.OpenTime, candle.OpenPrice, candle.HighPrice, candle.LowPrice, candle.ClosePrice, candle.TotalVolume, candle.Security)); // process new candle var longValue = LongSma.Process(candle); var shortValue = ShortSma.Process(candle); if (LongSma.IsFormed && ShortSma.IsFormed) { // calc new values for short and long var isShortLessThenLong = ShortSma.GetCurrentValue() < LongSma.GetCurrentValue(); if (_isShortLessThenLong == null) { _isShortLessThenLong = isShortLessThenLong; } else if (_isShortLessThenLong != isShortLessThenLong) // crossing happened { // if short less than long, the sale, otherwise buy var direction = isShortLessThenLong ? Sides.Sell : Sides.Buy; // calc size for open position or revert var volume = Position == 0 ? Volume : Position.Abs().Min(Volume) * 2; // calc order price as a close price + offset var price = candle.ClosePrice + ((direction == Sides.Buy ? Security.PriceStep : -Security.PriceStep) ?? 1); RegisterOrder(this.CreateOrder(direction, price, volume)); // or revert position via market quoting //var strategy = new MarketQuotingStrategy(direction, volume); //ChildStrategies.Add(strategy); // store current values for short and long _isShortLessThenLong = isShortLessThenLong; } } if (_chart == null) { return; } var trade = _myTrades.FirstOrDefault(); _myTrades.Clear(); var data = new ChartDrawData(); data .Group(candle.OpenTime) .Add(_candlesElem, candle) .Add(_shortElem, shortValue) .Add(_longElem, longValue) .Add(_tradesElem, trade); _chart.Draw(data); }
private void StartBtnClick(object sender, RoutedEventArgs e) { if (HistoryPath.Folder.IsEmpty() || !Directory.Exists(HistoryPath.Folder)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (Math.Abs(TestingProcess.Value - 0) > double.Epsilon) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); // SMA periods var periods = new[] { new Tuple <int, int, Color>(80, 10, Colors.DarkGreen), new Tuple <int, int, Color>(70, 8, Colors.Red), new Tuple <int, int, Color>(60, 6, Colors.DarkBlue) }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Folder) }; var timeFrame = TimeSpan.FromMinutes(5); // create test security var security = new Security { Id = "RIZ2@FORTS", // sec id has the same name as folder with historical data Code = "RIZ2", Name = "RTS-12.12", Board = ExchangeBoard.Forts, }; var startTime = new DateTime(2012, 10, 1); var stopTime = new DateTime(2012, 10, 31); var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector var batchEmulation = new BatchEmulation(new[] { security }, new[] { portfolio }, storageRegistry) { EmulationSettings = { MarketTimeChangedInterval = timeFrame, StartTime = startTime, StopTime = stopTime, // count of parallel testing strategies BatchSize = periods.Length, } }; // handle historical time for update ProgressBar batchEmulation.ProgressChanged += (curr, total) => this.GuiAsync(() => TestingProcess.Value = total); batchEmulation.StateChanged += (oldState, newState) => { if (batchEmulation.State != EmulationStates.Stopped) { return; } this.GuiAsync(() => { if (batchEmulation.IsFinished) { TestingProcess.Value = TestingProcess.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else { MessageBox.Show(this, LocalizedStrings.cancelled); } }); }; // get emulation connector var connector = batchEmulation.EmulationConnector; logManager.Sources.Add(connector); connector.NewSecurity += s => { if (s != security) { return; } // fill level1 values connector.SendInMessage(level1Info); }; TestingProcess.Maximum = 100; TestingProcess.Value = 0; _startEmulationTime = DateTime.Now; var strategies = periods .Select(period => { var candleManager = new CandleManager(connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); // create strategy based SMA var strategy = new SmaStrategy(candleManager, series, new SimpleMovingAverage { Length = period.Item1 }, new SimpleMovingAverage { Length = period.Item2 }) { Volume = 1, Security = security, Portfolio = portfolio, Connector = connector, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; strategy.SetCandleManager(candleManager); var curveElem = Curve.CreateCurve(LocalizedStrings.Str3026Params.Put(period.Item1, period.Item2), period.Item3, ChartIndicatorDrawStyles.Line); strategy.PnLChanged += () => { var data = new ChartDrawData(); data .Group(strategy.CurrentTime) .Add(curveElem, strategy.PnL); Curve.Draw(data); }; Stat.AddStrategies(new[] { strategy }); return(strategy); }); // start emulation batchEmulation.Start(strategies, periods.Length); }
private void ProcessCandle(Candle candle) { // strategy are stopping if (ProcessState == ProcessStates.Stopping) { CancelActiveOrders(); return; } this.AddInfoLog(LocalizedStrings.Str3634Params.Put(candle.OpenTime, candle.OpenPrice, candle.HighPrice, candle.LowPrice, candle.ClosePrice, candle.TotalVolume, candle.Security)); // process new candle var longValue = LongSma.Process(candle); var shortValue = ShortSma.Process(candle); // calc new values for short and long var isShortLessThenLong = ShortSma.GetCurrentValue() < LongSma.GetCurrentValue(); // crossing happened if (_isShortLessThenLong != isShortLessThenLong) { // if short less than long, the sale, otherwise buy var direction = isShortLessThenLong ? Sides.Sell : Sides.Buy; // calc size for open position or revert var volume = Position == 0 ? Volume : Position.Abs().Min(Volume) * 2; if (!SafeGetConnector().RegisteredMarketDepths.Contains(Security)) { var price = Security.GetMarketPrice(Connector, direction); // register "market" order (limit order with guaranteed execution price) if (price != null) { RegisterOrder(this.CreateOrder(direction, price.Value, volume)); } } else { // register order (limit order) RegisterOrder(this.CreateOrder(direction, (decimal)(Security.GetCurrentPrice(this, direction) ?? 0), volume)); // or revert position via market quoting //var strategy = new MarketQuotingStrategy(direction, volume) //{ // WaitAllTrades = true, //}; //ChildStrategies.Add(strategy); } // store current values for short and long _isShortLessThenLong = isShortLessThenLong; } var trade = _myTrades.FirstOrDefault(); _myTrades.Clear(); var data = new ChartDrawData(); data .Group(candle.OpenTime) .Add(_candlesElem, candle) .Add(_shortElem, shortValue) .Add(_longElem, longValue) .Add(_tradesElem, trade); _chart.Draw(data); }