Ejemplo n.º 1
0
        private void Download_OnClick(object sender, RoutedEventArgs e)
        {
            var year     = SelectedYear;
            var from     = From.Value ?? year.Days.First();
            var to       = (To.Value ?? year.Days.Last()).EndOfDay();
            var trader   = SelectedTrader;
            var security = SelectedSecurity;
            var tf       = SelectedTimeFrame;
            var series   = new CandleSeries(typeof(TimeFrameCandle), security, tf);

            BusyIndicator.BusyContent = "Подготовка данных...";
            BusyIndicator.IsBusy      = true;

            Dictionary <DateTimeOffset, Tuple <MyTrade[], MyTrade> > trades = null;

            var worker = new BackgroundWorker {
                WorkerReportsProgress = true
            };

            worker.DoWork += (o, ea) =>
            {
                var candleStorage = _dataRegistry.GetCandleStorage(series, format: StorageFormats.Csv);

                _candles = candleStorage.Load(from, to);

                var candlesDatesCache = _candlesDates.SafeAdd(Tuple.Create(security, tf), k => new DatesCache(Path.Combine(((LocalMarketDataDrive)candleStorage.Drive.Drive).GetSecurityPath(security.ToSecurityId()), "{0}min_date.bin".Put((int)tf.TotalMinutes))));

                var minCandleDate = candlesDatesCache.MinValue;
                var maxCandleDate = candlesDatesCache.MaxValue;

                if (from < minCandleDate || to > maxCandleDate)
                {
                    var finamFrom = from;
                    var finamTo   = to;

                    if (maxCandleDate != default(DateTime) && finamFrom >= minCandleDate && finamFrom <= maxCandleDate)
                    {
                        finamFrom = maxCandleDate + TimeSpan.FromDays(1);
                    }

                    if (minCandleDate != default(DateTime) && finamTo >= minCandleDate && finamTo <= maxCandleDate)
                    {
                        finamTo = minCandleDate - TimeSpan.FromDays(1);
                    }

                    if (finamTo > finamFrom)
                    {
                        worker.ReportProgress(1);

                        var newCandles = (tf.Ticks == 1
                                                        ? finamFrom.Range(finamTo, TimeSpan.FromDays(1)).SelectMany(day => _finamHistorySource.GetTrades(security, day, day)).ToEx().ToCandles <TimeFrameCandle>(tf)
                                                        : _finamHistorySource.GetCandles(security, tf, finamFrom, finamTo)
                                          ).ToArray();

                        candleStorage.Save(newCandles);

                        foreach (var date in newCandles.Select(c => c.OpenTime.Date).Distinct())
                        {
                            candlesDatesCache.Add(date);
                        }

                        candlesDatesCache.Save();

                        _candles = _candles.Concat(newCandles);
                    }
                }

                var traderDrive   = new LocalMarketDataDrive(trader);
                var traderStorage = _traderStorages.SafeAdd(trader, key => new StorageRegistry {
                    DefaultDrive = traderDrive
                });

                var olStorage       = traderStorage.GetOrderLogStorage(security, format: StorageFormats.Csv);
                var tradeDatesCache = _tradesDates.SafeAdd(trader, k => new DatesCache(Path.Combine(traderDrive.Path, "dates.bin")));

                trades = from
                         .Range(to, TimeSpan.FromDays(1))
                         .Intersect(year.Days)
                         .SelectMany(date =>
                {
                    if (olStorage.Dates.Contains(date))
                    {
                        return(olStorage.Load(date));
                    }

                    if (tradeDatesCache.Contains(date))
                    {
                        return(Enumerable.Empty <OrderLogItem>());
                    }

                    worker.ReportProgress(2, date);

                    var loadedTrades = year.GetTrades(_securityStorage, trader, date);

                    var secTrades = Enumerable.Empty <OrderLogItem>();

                    foreach (var group in loadedTrades.GroupBy(t => t.Order.Security))
                    {
                        var sec = group.Key;

                        traderStorage
                        .GetOrderLogStorage(sec, format: StorageFormats.Csv)
                        .Save(group.OrderBy(i => i.Order.Time));

                        if (group.Key == security)
                        {
                            secTrades = group;
                        }
                    }

                    tradeDatesCache.Add(date);
                    tradeDatesCache.Save();

                    return(secTrades);
                })
                         .GroupBy(ol =>
                {
                    var time = ol.Order.Time;

                    var period = security.Board.WorkingTime.GetPeriod(time.DateTime);
                    if (period != null && period.Times.Length > 0)
                    {
                        var last = period.Times.Last().Max;

                        if (time.TimeOfDay >= last)
                        {
                            time = time.AddTicks(-1);
                        }
                    }

                    return(time.Truncate(tf));
                })
                         .ToDictionary(g => g.Key, g =>
                {
                    var candleTrades = g
                                       .Select(order => new MyTrade
                    {
                        Order = order.Order,
                        Trade = order.Trade
                    })
                                       .ToArray();

                    if (candleTrades.Length > 0)
                    {
                        var order  = candleTrades[0].Order;
                        var volume = candleTrades.Sum(t1 => t1.Trade.Volume * (t1.Order.Direction == Sides.Buy ? 1 : -1));

                        if (volume == 0)
                        {
                            return(Tuple.Create(candleTrades, (MyTrade)null));
                        }

                        var side = volume > 0 ? Sides.Buy : Sides.Sell;

                        volume = volume.Abs();

                        var availableVolume = volume;
                        var avgPrice        = 0m;

                        foreach (var trade in candleTrades.Where(t1 => t1.Order.Direction == side))
                        {
                            var tradeVol = trade.Trade.Volume.Min(availableVolume);
                            avgPrice    += trade.Trade.Price * tradeVol;

                            availableVolume -= tradeVol;

                            if (availableVolume <= 0)
                            {
                                break;
                            }
                        }

                        avgPrice = avgPrice / volume;

                        return(Tuple.Create(candleTrades, new MyTrade
                        {
                            Order = new Order
                            {
                                Security = order.Security,
                                Direction = side,
                                Time = g.Key,
                                Portfolio = order.Portfolio,
                                Price = avgPrice,
                                Volume = volume,
                            },
                            Trade = new Trade
                            {
                                Security = order.Security,
                                Time = g.Key,
                                Volume = volume,
                                Price = avgPrice
                            }
                        }));
                    }

                    return(null);
                });
            };

            worker.ProgressChanged += (o, ea) =>
            {
                switch (ea.ProgressPercentage)
                {
                case 1:
                    BusyIndicator.BusyContent = "Скачивание свечей...";
                    break;

                default:
                    BusyIndicator.BusyContent = "Скачивание сделок за {0:yyyy-MM-dd}...".Put(ea.UserState);
                    break;
                }
            };

            worker.RunWorkerCompleted += (o, ea) =>
            {
                BusyIndicator.IsBusy = false;

                if (ea.Error == null)
                {
                    Chart.ClearAreas();
                    _statisticManager.Reset();

                    var area = new ChartArea();
                    area.YAxises.Add(new ChartAxis
                    {
                        Id            = "equity",
                        AutoRange     = true,
                        AxisType      = ChartAxisType.Numeric,
                        AxisAlignment = ChartAxisAlignment.Left,
                    });
                    Chart.AddArea(area);

                    var candlesElem = new ChartCandleElement {
                        ShowAxisMarker = false
                    };
                    Chart.AddElement(area, candlesElem, series);

                    var tradesElem = new ChartTradeElement
                    {
                        BuyStrokeColor  = Colors.Black,
                        SellStrokeColor = Colors.Black,
                        FullTitle       = "trades",
                    };
                    Chart.AddElement(area, tradesElem);

                    var equityElem = new ChartIndicatorElement {
                        YAxisId = "equity", FullTitle = "equity", IndicatorPainter = new PnlPainter()
                    };
                    var equityInd = new SimpleMovingAverage {
                        Length = 1
                    };
                    Chart.AddElement(area, equityElem);

                    var positionArea = new ChartArea {
                        Height = 200
                    };
                    Chart.AddArea(positionArea);

                    var positionElem = new ChartIndicatorElement {
                        FullTitle = "position"
                    };
                    var positionInd = new SimpleMovingAverage {
                        Length = 1
                    };
                    Chart.AddElement(positionArea, positionElem);

                    Chart.IsAutoRange = true;

                    var pnlQueue = new PnLQueue(security.ToSecurityId());
                    //var level1Info = new Level1ChangeMessage
                    //{
                    //	SecurityId = pnlQueue.SecurityId,
                    //}
                    //.TryAdd(Level1Fields.PriceStep, security.PriceStep)
                    //.TryAdd(Level1Fields.StepPrice, security.StepPrice);

                    //pnlQueue.ProcessLevel1(level1Info);

                    var pos = 0m;

                    var chartValues = _candles
                                      .Select(c =>
                    {
                        c.State = CandleStates.Finished;

                        pnlQueue.ProcessLevel1(new Level1ChangeMessage
                        {
                            SecurityId = security.ToSecurityId(),
                        }.TryAdd(Level1Fields.LastTradePrice, c.ClosePrice));

                        var values = new Dictionary <IChartElement, object>
                        {
                            { candlesElem, c },
                        };

                        var candleTrade = trades.TryGetValue(c.OpenTime);

                        if (candleTrade != null)
                        {
                            if (candleTrade.Item2 != null)
                            {
                                values.Add(tradesElem, candleTrade.Item2);
                            }

                            foreach (var myTrade in candleTrade.Item1)
                            {
                                pos    += myTrade.Order.Direction == Sides.Buy ? myTrade.Trade.Volume : -myTrade.Trade.Volume;
                                var pnl = pnlQueue.Process(myTrade.ToMessage());

                                _statisticManager.AddMyTrade(pnl);
                            }

                            _statisticManager.AddPosition(c.OpenTime, pos);
                            _statisticManager.AddPnL(c.OpenTime, pnlQueue.RealizedPnL + pnlQueue.UnrealizedPnL);
                        }

                        values.Add(equityElem, equityInd.Process(pnlQueue.RealizedPnL + pnlQueue.UnrealizedPnL));
                        values.Add(positionElem, positionInd.Process(pos));

                        return(new RefPair <DateTimeOffset, IDictionary <IChartElement, object> >
                        {
                            First = c.OpenTime,
                            Second = values
                        });
                    })
                                      .ToArray();

                    Chart.Draw(chartValues);

                    Chart.IsAutoRange = false;
                }
                else
                {
                    new MessageBoxBuilder()
                    .Error()
                    .Owner(this)
                    .Text(ea.Error.ToString())
                    .Show();
                }
            };

            worker.RunWorkerAsync();
        }
Ejemplo n.º 2
0
        private void Download_OnClick(object sender, RoutedEventArgs e)
        {
            var settings = new Settings
            {
                Year      = SelectedYear.Year,
                Trader    = Trader.Text,
                From      = From.Value,
                To        = To.Value,
                Security1 = Security1.Text,
                Security2 = Security2.Text,
                Security3 = Security3.Text,
                Security4 = Security4.Text,
                TimeFrame = SelectedTimeFrame,
                Apart     = Apart.IsChecked == true,
            };

            CultureInfo.InvariantCulture.DoInCulture(() => new XmlSerializer <SettingsStorage>().Serialize(settings.Save(), _settingsFile));

            var year   = SelectedYear;
            var from   = From.Value ?? year.Days.First();
            var to     = (To.Value ?? year.Days.Last()).EndOfDay();
            var trader = SelectedTrader;
            var tf     = SelectedTimeFrame;
            var apart  = Apart.IsChecked == true;

            var seriesSet = _securityCtrls
                            .Where(pair => pair.Key.SelectedSecurity != null)
                            .Select(pair => Tuple.Create(new CandleSeries(typeof(TimeFrameCandle), pair.Key.SelectedSecurity, tf), pair.Value))
                            .ToArray();

            BusyIndicator.BusyContent = "Подготовка данных...";
            BusyIndicator.IsBusy      = true;

            _candles.Clear();

            var trades = new Dictionary <Security, Dictionary <DateTimeOffset, Tuple <MyTrade[], MyTrade> > >();

            var worker = new BackgroundWorker {
                WorkerReportsProgress = true
            };

            worker.DoWork += (o, ea) =>
            {
                foreach (var series in seriesSet)
                {
                    var security      = series.Item1.Security;
                    var candleStorage = _dataRegistry.GetCandleStorage(series.Item1, format: StorageFormats.Csv);
                    var secCandles    = _candles.SafeAdd(security);

                    secCandles.Clear();
                    secCandles.AddRange(candleStorage.Load(from, to));

                    var candlesDatesCache = _candlesDates.SafeAdd(Tuple.Create(security, tf), k => new DatesCache(Path.Combine(((LocalMarketDataDrive)candleStorage.Drive.Drive).GetSecurityPath(security.ToSecurityId()), "{0}min_date.bin".Put((int)tf.TotalMinutes))));

                    var minCandleDate = candlesDatesCache.MinValue;
                    var maxCandleDate = candlesDatesCache.MaxValue;

                    if (from >= minCandleDate && to <= maxCandleDate)
                    {
                        continue;
                    }

                    var finamFrom = from;
                    var finamTo   = to;

                    if (maxCandleDate != null && finamFrom >= minCandleDate && finamFrom <= maxCandleDate)
                    {
                        finamFrom = maxCandleDate.Value + TimeSpan.FromDays(1);
                    }

                    if (minCandleDate != null && finamTo >= minCandleDate && finamTo <= maxCandleDate)
                    {
                        finamTo = minCandleDate.Value - TimeSpan.FromDays(1);
                    }

                    if (finamTo <= finamFrom)
                    {
                        continue;
                    }

                    TimeFrameCandle[] newCandles;

                    if (tf.Ticks == 1)
                    {
                        newCandles = finamFrom.Range(finamTo, TimeSpan.FromDays(1)).SelectMany(day =>
                        {
                            worker.ReportProgress(1, Tuple.Create(security, day));

                            var candles = _finamHistorySource.GetTrades(security, day, day).ToEx().ToCandles <TimeFrameCandle>(tf).ToArray();
                            candleStorage.Save(candles);
                            candlesDatesCache.Add(day);
                            return(candles);
                        }).ToArray();
                    }
                    else
                    {
                        worker.ReportProgress(1, Tuple.Create(security, finamFrom, finamTo));
                        newCandles = _finamHistorySource.GetCandles(security, tf, finamFrom, finamTo).ToArray();

                        candleStorage.Save(newCandles);
                        candlesDatesCache.Add(newCandles.Select(c => c.OpenTime.Date).Distinct().ToArray());
                    }

                    // TODO
                    secCandles.AddRange(newCandles);
                }

                var traderDrive   = new LocalMarketDataDrive(Path.Combine(_settingsDir, trader));
                var traderStorage = _traderStorages.SafeAdd(trader, key => new StorageRegistry {
                    DefaultDrive = traderDrive
                });

                foreach (var series in seriesSet)
                {
                    var security = series.Item1.Security;

                    var olStorage       = traderStorage.GetOrderLogStorage(security, format: StorageFormats.Csv);
                    var tradeDatesCache = _tradesDates.SafeAdd(trader, k => new DatesCache(Path.Combine(traderDrive.Path, "dates.xml")));

                    var secTrades = from
                                    .Range(to, TimeSpan.FromDays(1))
                                    .Intersect(year.Days)
                                    .SelectMany(date =>
                    {
                        if (olStorage.Dates.Contains(date))
                        {
                            return(olStorage.Load(date));
                        }

                        if (tradeDatesCache.Contains(date))
                        {
                            return(Enumerable.Empty <OrderLogItem>());
                        }

                        worker.ReportProgress(2, date);

                        var loadedTrades = year.GetTrades(_securityStorage, trader, date);

                        var dateTrades = Enumerable.Empty <OrderLogItem>();

                        foreach (var group in loadedTrades.GroupBy(t => t.Order.Security))
                        {
                            var sec = group.Key;

                            traderStorage
                            .GetOrderLogStorage(sec, format: StorageFormats.Csv)
                            .Save(group.OrderBy(i => i.Order.Time));

                            if (group.Key == security)
                            {
                                dateTrades = group;
                            }
                        }

                        tradeDatesCache.Add(date);

                        return(dateTrades);
                    })
                                    .GroupBy(ol =>
                    {
                        var time = ol.Order.Time;

                        var period = security.Board.WorkingTime.GetPeriod(time.ToLocalTime(security.Board.Exchange.TimeZoneInfo));
                        if (period != null && period.Times.Length > 0)
                        {
                            var last = period.Times.Last().Max;

                            if (time.TimeOfDay >= last)
                            {
                                time = time.AddTicks(-1);
                            }
                        }

                        if (tf == TimeSpan.FromDays(1) && period != null && period.Times.Length > 0)
                        {
                            return(new DateTimeOffset(time.Date + period.Times[0].Min, time.Offset));
                        }

                        return(time.Truncate(tf));
                    })
                                    .ToDictionary(g => g.Key, g =>
                    {
                        var candleTrades = g.Select(ol => new MyTrade
                        {
                            Order = ol.Order,
                            Trade = ol.Trade
                        })
                                           .ToArray();

                        if (candleTrades.Length == 0)
                        {
                            return(null);
                        }

                        var order  = candleTrades[0].Order;
                        var volume = candleTrades.Sum(t1 => t1.Trade.Volume * (t1.Order.Direction == Sides.Buy ? 1 : -1));

                        if (volume == 0)
                        {
                            return(Tuple.Create(candleTrades, (MyTrade)null));
                        }

                        var side = volume > 0 ? Sides.Buy : Sides.Sell;

                        volume = volume.Abs();

                        var availableVolume = volume;
                        var avgPrice        = 0m;

                        foreach (var trade in candleTrades.Where(t1 => t1.Order.Direction == side))
                        {
                            var tradeVol = trade.Trade.Volume.Min(availableVolume);
                            avgPrice    += trade.Trade.Price * tradeVol;

                            availableVolume -= tradeVol;

                            if (availableVolume <= 0)
                            {
                                break;
                            }
                        }

                        avgPrice = avgPrice / volume;

                        return(Tuple.Create(candleTrades, new MyTrade
                        {
                            Order = new Order
                            {
                                Security = order.Security,
                                Direction = side,
                                Time = g.Key,
                                Portfolio = order.Portfolio,
                                Price = avgPrice,
                                Volume = volume,
                            },
                            Trade = new Trade
                            {
                                Security = order.Security,
                                Time = g.Key,
                                Volume = volume,
                                Price = avgPrice
                            }
                        }));
                    });

                    trades.Add(security, secTrades);
                }
            };

            worker.ProgressChanged += (o, ea) =>
            {
                switch (ea.ProgressPercentage)
                {
                case 1:
                {
                    if (ea.UserState is Tuple <Security, DateTime> )
                    {
                        BusyIndicator.BusyContent = "Скачивание {Item1.Id} тиков за {Item2:yyyy-MM-dd}...".PutEx(ea.UserState);
                    }
                    else
                    {
                        BusyIndicator.BusyContent = "Скачивание {Item1.Id} свечей с {Item2:yyyy-MM-dd} по {Item3:yyyy-MM-dd}...".PutEx(ea.UserState);
                    }

                    break;
                }

                default:
                    BusyIndicator.BusyContent = "Скачивание сделок за {0:yyyy-MM-dd}...".Put(ea.UserState);
                    break;
                }
            };

            worker.RunWorkerCompleted += (o, ea) =>
            {
                BusyIndicator.IsBusy = false;

                if (ea.Error == null)
                {
                    Chart.ClearAreas();

                    _statisticManager.Reset();

                    var equityInd = new SimpleMovingAverage {
                        Length = 1
                    };
                    ChartIndicatorElement equityElem;
                    var candlesAreas = new Dictionary <CandleSeries, ChartArea>();

                    if (apart)
                    {
                        foreach (var series in seriesSet)
                        {
                            var area = new ChartArea {
                                Title = series.Item1.Security.Id
                            };
                            Chart.AddArea(area);
                            area.YAxises.Clear();
                            candlesAreas.Add(series.Item1, area);
                        }

                        var equityArea = new ChartArea {
                            Title = LocalizedStrings.PnL
                        };
                        Chart.AddArea(equityArea);

                        equityElem = new ChartIndicatorElement
                        {
                            FullTitle        = LocalizedStrings.PnL,
                            IndicatorPainter = new PnlPainter()
                        };
                        Chart.AddElement(equityArea, equityElem);
                    }
                    else
                    {
                        var candlesArea = new ChartArea();
                        Chart.AddArea(candlesArea);

                        foreach (var tuple in seriesSet)
                        {
                            candlesAreas.Add(tuple.Item1, candlesArea);
                        }

                        const string equityYAxis = "Equity";

                        candlesArea.YAxises.Clear();
                        candlesArea.YAxises.Add(new ChartAxis
                        {
                            Id            = equityYAxis,
                            AutoRange     = true,
                            AxisType      = ChartAxisType.Numeric,
                            AxisAlignment = ChartAxisAlignment.Left,
                        });
                        equityElem = new ChartIndicatorElement
                        {
                            YAxisId          = equityYAxis,
                            FullTitle        = LocalizedStrings.PnL,
                            IndicatorPainter = new PnlPainter()
                        };
                        Chart.AddElement(candlesArea, equityElem);
                    }

                    var positionArea = new ChartArea {
                        Height = 100
                    };
                    Chart.AddArea(positionArea);
                    positionArea.YAxises.Clear();

                    var chartValues = new SortedDictionary <DateTimeOffset, IDictionary <IChartElement, object> >();
                    var pnlValues   = new Dictionary <DateTimeOffset, decimal>();

                    foreach (var series in seriesSet)
                    {
                        var security = series.Item1.Security;

                        var candleYAxis = "Candles_Y_" + security.Id;

                        var candlesArea = candlesAreas[series.Item1];

                        candlesArea.YAxises.Add(new ChartAxis
                        {
                            Id            = candleYAxis,
                            AutoRange     = true,
                            AxisType      = ChartAxisType.Numeric,
                            AxisAlignment = ChartAxisAlignment.Right,
                        });

                        var candlesElem = new ChartCandleElement
                        {
                            ShowAxisMarker = false,
                            YAxisId        = candleYAxis,
                        };
                        Chart.AddElement(candlesArea, candlesElem, series.Item1);

                        var tradesElem = new ChartTradeElement
                        {
                            BuyStrokeColor  = Colors.Black,
                            SellStrokeColor = Colors.Black,
                            BuyColor        = series.Item2.Buy,
                            SellColor       = series.Item2.Sell,
                            FullTitle       = LocalizedStrings.Str985 + " " + security.Id,
                            YAxisId         = candleYAxis,
                        };
                        Chart.AddElement(candlesArea, tradesElem);

                        var posYAxis = "Pos_Y_" + security.Id;
                        positionArea.YAxises.Add(new ChartAxis
                        {
                            Id            = posYAxis,
                            AutoRange     = true,
                            AxisType      = ChartAxisType.Numeric,
                            AxisAlignment = ChartAxisAlignment.Right,
                        });
                        var positionElem = new ChartIndicatorElement
                        {
                            FullTitle = LocalizedStrings.Str862 + " " + security.Id,
                            YAxisId   = posYAxis,
                            Color     = series.Item2.Position
                        };
                        var positionInd = new SimpleMovingAverage {
                            Length = 1
                        };
                        Chart.AddElement(positionArea, positionElem);

                        var pnlQueue = new PnLQueue(security.ToSecurityId());
                        //var level1Info = new Level1ChangeMessage
                        //{
                        //	SecurityId = pnlQueue.SecurityId,
                        //}
                        //.TryAdd(Level1Fields.PriceStep, security.PriceStep)
                        //.TryAdd(Level1Fields.StepPrice, security.StepPrice);

                        //pnlQueue.ProcessLevel1(level1Info);

                        var pos = 0m;

                        var secTrades = trades[security];

                        var secValues = _candles[security]
                                        .Select(c =>
                        {
                            if (c.State != CandleStates.Finished)
                            {
                                c.State = CandleStates.Finished;
                            }

                            pnlQueue.ProcessLevel1(new Level1ChangeMessage
                            {
                                SecurityId = security.ToSecurityId(),
                            }.TryAdd(Level1Fields.LastTradePrice, c.ClosePrice));

                            var values = new Dictionary <IChartElement, object>
                            {
                                { candlesElem, c },
                            };

                            var candleTrade = secTrades.TryGetValue(c.OpenTime);

                            if (candleTrade != null)
                            {
                                if (candleTrade.Item2 != null)
                                {
                                    values.Add(tradesElem, candleTrade.Item2);
                                }

                                foreach (var myTrade in candleTrade.Item1)
                                {
                                    pos    += myTrade.Order.Direction == Sides.Buy ? myTrade.Trade.Volume : -myTrade.Trade.Volume;
                                    var pnl = pnlQueue.Process(myTrade.ToMessage());

                                    _statisticManager.AddMyTrade(pnl);
                                }

                                _statisticManager.AddPosition(c.OpenTime, pos);
                                _statisticManager.AddPnL(c.OpenTime, pnlQueue.RealizedPnL + pnlQueue.UnrealizedPnL);
                            }

                            pnlValues[c.OpenTime] = pnlValues.TryGetValue(c.OpenTime) + (pnlQueue.RealizedPnL + pnlQueue.UnrealizedPnL);
                            values.Add(positionElem, positionInd.Process(pos));

                            return(new RefPair <DateTimeOffset, IDictionary <IChartElement, object> >
                            {
                                First = c.OpenTime,
                                Second = values
                            });
                        })
                                        .ToArray();

                        foreach (var pair in secValues)
                        {
                            var dict = chartValues.SafeAdd(pair.First, key => new Dictionary <IChartElement, object>());

                            foreach (var pair2 in pair.Second)
                            {
                                dict[pair2.Key] = pair2.Value;
                            }
                        }
                    }

                    foreach (var pair in pnlValues)
                    {
                        chartValues[pair.Key].Add(equityElem, equityInd.Process(pair.Value));
                    }

                    Chart.IsAutoRange = true;

                    try
                    {
                        Chart.Draw(chartValues.Select(p => RefTuple.Create(p.Key, p.Value)));
                    }
                    finally
                    {
                        Chart.IsAutoRange = false;
                    }
                }
                else
                {
                    new MessageBoxBuilder()
                    .Error()
                    .Owner(this)
                    .Text(ea.Error.ToString())
                    .Show();
                }
            };

            worker.RunWorkerAsync();
        }