/// <summary>
        /// Расчитать объем сделки в валюте депозита
        /// </summary>
        public void CalculateDealVolumeInDepoCurrency(MarketOrder deal, QuoteData dealQuote, string depoCurrency, QuoteArchive arc, DateTime date)
        {
            var volumeInCounter = deal.Volume * dealQuote.GetPrice(QuoteType.NonSpecified);

            // перевести из контрвалюты в валюту депо
            bool inverse, pairsAreEqual;
            var smb = DalSpot.Instance.FindSymbol(deal.Symbol, false, depoCurrency, out inverse, out pairsAreEqual);
            if (pairsAreEqual)
            {
                deal.VolumeInDepoCurrency = volumeInCounter;
                return;
            }

            if (string.IsNullOrEmpty(smb)) return;
            var quoteCtDepo = arc.GetQuoteOnDateSeq(smb, date);
            if (quoteCtDepo == null) return;
            var priceCtDepo = quoteCtDepo.GetPrice(QuoteType.NonSpecified);
            if (priceCtDepo == 0) return;

            deal.VolumeInDepoCurrency = inverse ? volumeInCounter / priceCtDepo : volumeInCounter * priceCtDepo;
        }
        /// <summary>
        /// Рассчитать точку кривой доходности "вручную" и сверить её с рассчитанной в классе "CalculateEquityCurve"
        /// </summary>
        private void CheckEquityCurvePoint(AccountPerformanceRaw res, 
            List<BalanceChange> transfers, List<MarketOrder> dealsOpen, List<MarketOrder> dealsClose,
            QuoteArchive quoteArc, Dictionary<string, List<QuoteData>> dicQuote, int index)
        {
            var calculationTime = res.equity[index].time;

            var depoChangesOnDate = transfers.Where(t => t.ValueDate <= calculationTime).Sum(t => t.SignedAmountDepo);
            var dealsStilOpened = dealsOpen.Where(d => d.State == PositionState.Opened && d.TimeEnter <= calculationTime).ToList();
            var allDeals = dealsClose.Where(d => d.TimeEnter < calculationTime && d.TimeExit > calculationTime).ToList();
            allDeals.AddRange(dealsStilOpened);

            allDeals.ForEach(d =>
                {
                    d.State = PositionState.Opened;
                    d.PriceExit = null;
                });
            // получить котировки на момент
            quoteArc.ToBegin();
            var thatTimeQuotes = dicQuote.Keys.ToDictionary(ticker => ticker,
                                                            ticker => quoteArc.GetQuoteOnDateSeq(ticker, calculationTime));

            var sumDealsProfit = DalSpot.Instance.CalculateOpenedPositionsCurrentResult(allDeals, thatTimeQuotes, DepoCurrency);
            var equity = (float) depoChangesOnDate + sumDealsProfit;
            var delta = Math.Abs(res.equity[index].equity - equity);
            Assert.Less(delta, 0.1f, "CalculateEquityCurve - погрешность расчета средств на момент должна быть в пределах 10C");

            var dealByTicker = allDeals.GroupBy(x => x.Symbol).ToDictionary(d => d.Key, d => d.ToList());
            var exposure = dealByTicker.Sum(d =>
                {
                    var totalVolume = Math.Abs(d.Value.Sum(order => order.Side*order.Volume));

                    if (totalVolume == 0) return 0;
                    string errorStr;
                    var exp = DalSpot.Instance.ConvertToTargetCurrency(d.Key, true, DepoCurrency, totalVolume, thatTimeQuotes, out errorStr);
                    return exp ?? 0;
                });

            var leverage = (float) exposure/equity;
            var resultedLeverage = res.leverage.First(l => l.time == calculationTime).equity;
            delta = Math.Abs(resultedLeverage - leverage);
            Assert.Less(delta, 0.05f, "CalculateEquityCurve - погрешность расчета плеча на момент должна быть в пределах 0.05");
        }
        public AccountPerformanceRaw CalculateEquityCurve(List<MarketOrder> deals, string depoCurrency, QuoteArchive quoteArc,
            List<BalanceChange> transactions, DateTime timeStart, DateTime timeEnd)
        {
            Logger.InfoFormat("CalculateEquityCurve(closedDeals:{0}, quotes:{1}, transactions:{2})",
                              deals.Count, quoteArc.dicQuote.Count, transactions.Count);

            var performance = new AccountPerformanceRaw();
            if (transactions.Count == 0) return performance;
            // скопировать списки сделок и транзакций, т.к. они будут модифицированы
            var dueDeals = deals.ToList();
            var dueTransactions = transactions.ToList();

            // посчитать результат
            decimal balance = 0;

            var lstEq = performance.equity;
            var lstLev = performance.leverage;

            // сформировать список дат - момент входа в рынок, конец дня
            // флаг (второй параметр) - означает конец дня
            var dateList = new List<Cortege2<DateTime, bool>>();
            for (var date = timeStart.Date; date <= timeEnd; date = date.AddDays(1))
            {
                var nextDate = date.AddDays(1);
                // сделки за день
                for (var i = 0; i < dueDeals.Count; i++)
                {
                    if (dueDeals[i].TimeEnter > nextDate) continue;
                    dateList.Add(new Cortege2<DateTime, bool>(dueDeals[i].TimeEnter, false));
                    dueDeals.RemoveAt(i);
                    i--;
                }

                // конец дня
                dateList.Add(new Cortege2<DateTime, bool>(date, true));
            }
            dateList = dateList.OrderBy(d => d.a).ToList();
            dueDeals = deals.ToList();

            foreach (var datePart in dateList)
            {
                var date = datePart.a;
                var isEndOfDay = datePart.b;

                if (DaysOff.Instance.IsDayOff(date)) continue;
                // добавить сумму транзакции
                for (var i = 0; i < dueTransactions.Count; i++)
                {
                    var trans = dueTransactions[i];
                    if (date < trans.ValueDate) continue;
                    balance += trans.SignedAmountDepo;
                    dueTransactions.RemoveAt(i);
                    i--;
                }
                if (balance == 0) continue;

                // посчитать результат по открытым сделкам
                // и суммировать плечи по открытым сделкам
                var openResult = 0f;
                for (var i = 0; i < dueDeals.Count; i++)
                {
                    var deal = dueDeals[i];
                    // удалить закрытую сделку
                    if (deal.TimeExit.HasValue && deal.TimeExit.Value < date)
                    {
                        // учесть объем сделки второй раз - на закрытии
                        performance.totalTradedVolume += (long)Math.Round(deal.VolumeInDepoCurrency);
                        dueDeals.RemoveAt(i);
                        i--;
                        continue;
                    }

                    if (date < deal.TimeEnter || date > deal.TimeExit) continue;
                    // получить текущую котиру
                    if (!quoteArc.dicQuote.ContainsKey(deal.Symbol)) continue;
                    var quote = quoteArc.GetQuoteOnDateSeq(deal.Symbol, date);
                    if (quote == null) continue;

                    if (deal.VolumeInDepoCurrency == 0)
                    {
                        CalculateDealVolumeInDepoCurrency(deal, quote, depoCurrency, quoteArc, date);
                        performance.totalTradedVolume += (long)Math.Round(deal.VolumeInDepoCurrency);
                    }

                    // текущая прибыль по сделке
                    var dealProfit = deal.CalculateProfit(quote);
                    // перевести в валюту депо
                    dealProfit = CalculateProfitInDepoCurx(false, dealProfit, deal.Symbol, depoCurrency, quoteArc, date);
                    openResult += dealProfit;
                }

                // посчитать плечо по сделкам
                var dealByTicker = dueDeals.Where(d => d.TimeEnter < date).GroupBy(x => x.Symbol).ToDictionary(d => d.Key, d => d.ToList());
                var sumVolumeDepo = dealByTicker.Sum(d =>
                    {
                        var totalVolume = Math.Abs(d.Value.Sum(order => order.Side*order.Volume));
                        if (totalVolume == 0) return 0;
                        return CalculateProfitInDepoCurx(true, totalVolume, d.Key, depoCurrency, quoteArc, date);
                    });

                var equity = balance + (decimal)openResult;
                var leverage = equity == 0 ? 0 : (decimal)sumVolumeDepo / equity;

                // добавить точки в кривые
                if (isEndOfDay)
                    lstEq.Add(new EquityOnTime((float)equity, date));
                lstLev.Add(new EquityOnTime((float)leverage, date));
            }

            var dateNow = DateTime.Now;
            // открытым сделкам посчитать текущую прибыль / убыток
            foreach (var deal in dueDeals)
            {
                var quote = quoteArc.GetLastQuote(deal.Symbol, dateNow);
                if (quote == null) continue;

                // условно - цена выхода
                deal.PriceExit = deal.Side > 0 ? quote.bid : quote.ask;
                var dealProfit = deal.CalculateProfit(quote);
                // перевести в валюту депо
                dealProfit = CalculateProfitInDepoCurx(false, dealProfit, deal.Symbol, depoCurrency, quoteArc, dateNow);
                deal.ResultDepo = dealProfit;
            }

            return performance;
        }
        /// <summary>
        /// Пересчитать указанный профит по сделке в профит в валюте депозита
        /// </summary>
        public float CalculateProfitInDepoCurx(bool useBase, float profit, string dealTicker, string depoCurrency, QuoteArchive arc, DateTime date)
        {
            bool inverse, pairsAreEqual;
            var smb = DalSpot.Instance.FindSymbol(dealTicker, useBase, depoCurrency, out inverse, out pairsAreEqual);
            if (pairsAreEqual) return profit;

            if (string.IsNullOrEmpty(smb)) return 0;
            // найти котиру по символу
            var quote = arc.GetQuoteOnDateSeq(smb, date);
            if (quote == null) return 0;
            return inverse
                       ? profit / quote.GetPrice(QuoteType.Middle)
                       : profit * quote.GetPrice(QuoteType.Middle);
        }