public void Calculate()
        {
            // ReSharper disable SuspiciousTypeConversion.Global
            ((IMockableProxy)fakeTradeAccount).IncludeMockMethod(getHistoryOrdersName, getHistoryOrdersFake);
            ((IMockableProxy)fakeTradeAccount).IncludeMockMethod(getMarketOrdersName, getMarketOrdersFake);
            ((IMockableProxy)fakeTradeAccount).IncludeMockMethod(getBalanceChangesName, getBalanceChangesFake);
            // ReSharper restore SuspiciousTypeConversion.Global

            var accEff = new AccountEfficiency(new PerformerStat
                {
                    Account = AccountId,
                    DepoCurrency = "USD"
                });

            //accEff.Statistics.Account = accountId;

            efficiencyCalculator = new EfficiencyCalculator(dailyQuoteStorage, new EquityCurveCalculator());
            efficiencyCalculator.Calculate(accEff);
        }
        private void CalcProfit1000(AccountEfficiency ef, List<EquityOnTime> listDatedTWR)
        {
            const float startBalance = 1000;
            if (listDatedTWR.Count == 0) return;
            ef.listProfit1000 = new List<EquityOnTime> { new EquityOnTime(startBalance, ef.StartDate) };
            var balance = startBalance;
            for (var i = 0; i < listDatedTWR.Count; i++)
            {
                var twr = listDatedTWR[i];
                balance = balance * twr.equity;

                // Хардкод на последнее число - дублировать запись
                if (i == listDatedTWR.Count - 1 && i > 0 &&
                    listDatedTWR[i].time.Month != listDatedTWR[i - 1].time.Month) // !!
                {
                    var doubleDate = twr.time;
                    doubleDate = doubleDate.Hour < 12 ? doubleDate.AddHours(1) : doubleDate.AddHours(-1);
                    ef.listProfit1000.Add(new EquityOnTime(balance, doubleDate));
                }
                ef.listProfit1000.Add(new EquityOnTime(balance, twr.time));
            }
        }
        private void CalcDrawDown(AccountEfficiency ef)
        {
            float drawDown = 0;
            float drawStart = 0;
            for (var i = 0; i < ef.listEquity.Count; i++)
            {
                var level = ef.listEquity[i].equity;
                var curDD = 0f;
                var j = i + 1;
                var probablyDrawStart = level;
                for (; j < ef.listEquity.Count; j++)
                {
                    var cl = ef.listEquity[j].equity;
                    if (cl >= level) break;
                    var delta = cl - level;
                    if (delta < curDD) curDD = delta;
                }
                i = j;
                if (curDD < drawDown)
                {
                    drawDown = curDD;
                    drawStart = level;
                }
            }

            if (drawStart > 0 && drawDown < 0)
            {
                ef.Statistics.MaxRelDrawDown = 100 * (-drawDown) / drawStart;
            }
        }
        public void CalculateProfitCoeffs(AccountEfficiency ef)
        {
            if (ef.listEquity == null) return;
            if (ef.listEquity.Count == 0) return;

            // посчитать TWR
            var lastEq = ef.listEquity[ef.listEquity.Count - 1].equity;
            ef.Statistics.Equity = lastEq;

            var dueTransactions = ef.listTransaction.Where(t => t.ChangeType == BalanceChangeType.Deposit ||
                                                         t.ChangeType == BalanceChangeType.Withdrawal).ToList();
            // вычесть стартовый баланс - первую транзакцию
            if (dueTransactions.Count > 0)
                dueTransactions.RemoveAt(0);
            var sumTrans = (float)dueTransactions.Sum(t => t.SignedAmountDepo);

            var absTWR = ef.InitialBalance == 0 ? 0 : (lastEq - sumTrans) / ef.InitialBalance;
            ef.Statistics.Profit = (absTWR - 1f) * 100f;

            // Посчитать относительные доходности на дату
            // Pi = 1 + (Di - Di-1 - SUM(T)) / Di
            // P - доходность, D - депозит, SUM(T) - сумма транзакций с момента i-1 по i
            var listTWR = new List<float>();
            var listDatedTWR = new List<EquityOnTime>();

            for (var i = 1; i < ef.listEquity.Count; i++)
            {
                var time = ef.listEquity[i].time;
                var sumT = 0f;
                for (var j = 0; j < dueTransactions.Count(); j++)
                {
                    var trans = dueTransactions[j];
                    if (trans.ValueDate > time) continue;
                    sumT += (float)trans.SignedAmountDepo;
                    dueTransactions.RemoveAt(j);
                    j--;
                }
                var depo = ef.listEquity[i].equity;
                var depoPrev = ef.listEquity[i - 1].equity;
                if (depoPrev == 0) continue;
                var twr = 1 + (depo - depoPrev - sumT) / depoPrev;
                listTWR.Add(twr);
                listDatedTWR.Add(new EquityOnTime(twr, time));
            }
            if (listTWR.Count < 2) return;

            // Шарп
            var sko = 0f;
            var avgTWR = listTWR.Average();
            for (var i = 0; i < listTWR.Count; i++)
            {
                var deltaTWR = listTWR[i] - avgTWR;
                sko += (deltaTWR * deltaTWR);
            }
            ef.Statistics.Sharp = sko == 0 ? 0 : (absTWR - 1) / (float)Math.Sqrt(sko);

            // макс. проседание
            CalcDrawDown(ef);

            // доходность на 1000
            if (ef.listProfit1000 == null)
                CalcProfit1000(ef, listDatedTWR);

            // среднегеометрическая
            var prodProfit = listTWR.Product(t => t);
            if (prodProfit < 0) prodProfit = 0;

            ef.ProfitGeomMonth = prodProfit == 0 ? 0 : 100 * ((float)Math.Pow(prodProfit, 20.5f / listDatedTWR.Count) - 1);
            ef.ProfitGeomYear = prodProfit == 0 ? 0 : 100 * ((float)Math.Pow(prodProfit, 250f / listDatedTWR.Count) - 1);
            ef.Statistics.AvgYearProfit = ef.ProfitGeomYear;

            // доходность за последние N месяцев
            // по кривой доходности на 1 000 долларов
            if (ef.listProfit1000.Count > 1)
            {
                const int months = -3;
                var dateOld = DateTime.Now.Date.AddMonths(months);
                var lastEquityDate = ef.listProfit1000[0];
                var curEquity = ef.listProfit1000[ef.listProfit1000.Count - 1].equity;
                if (lastEquityDate.time.Date < dateOld)
                {
                    lastEquityDate = ef.listProfit1000.FirstOrDefault(p => p.time >= dateOld);
                    if (lastEquityDate.Equals(default(EquityOnTime)))
                        lastEquityDate = ef.listProfit1000[0];
                }
                var delta = curEquity - lastEquityDate.equity;
                ef.Statistics.ProfitLastMonths = lastEquityDate.equity == 0
                                        ? (curEquity == 0 ? 0 : 100)
                                        : 100 * delta / lastEquityDate.equity;

                // доходность за N месяцев, по абс. величине
                ef.Statistics.ProfitLastMonthsAbs = (ef.closedDeals == null || ef.closedDeals.Count == 0) ? 0 :
                    ef.closedDeals.Where(d => d.TimeExit >= dateOld).Sum(d => d.ResultDepo);
            }
        }
        public void CalculateProfitCoeffs()
        {
            var accEff = new AccountEfficiency(new PerformerStat
                {
                    Account = AccountId,
                    DepoCurrency = "USD"
                })
                {
                    listEquity = TestDataGenerator.GetEquityOnTime(),
                    listTransaction = TestDataGenerator.GetBalanceChange().Select(LinqToEntity.DecorateBalanceChange).ToList()
                };

            efficiencyCalculator = new EfficiencyCalculator(dailyQuoteStorage, new EquityCurveCalculator());
            efficiencyCalculator.CalculateProfitCoeffs(accEff);
        }
        // отобразить данные "Совокупные позиции"
        public static void BindSummaryPositions(AccountEfficiency efficiency, TickersAndVolumesBarControl tickersAndVolumesBarControl)
        {
            if (efficiency.openedDeals == null || efficiency.openedDeals.Count == 0)
                return;
            // исходные объемы валютных инструментов
            // ticker, <buy_volume, sell_volume>
            var summaryPositions = new Dictionary<string, TradeSharp.Util.Cortege2<int, int>>();
            var symbols = efficiency.openedDeals.Select(d => d.Symbol).Distinct().ToList();
            foreach (var symbol in symbols)
            {
                var ticker = symbol;
                var deals = efficiency.openedDeals.Where(d => d.Symbol == ticker).ToList();
                var buyVolume = deals.Sum(d => d.Side > 0 ? d.Volume : 0);
                var sellVolume = deals.Sum(d => d.Side < 0 ? d.Volume : 0);
                summaryPositions.Add(ticker, new TradeSharp.Util.Cortege2<int, int>(buyVolume, sellVolume));
            }

            // оценка исходных объемов в валюте депозита
            // ticker, <buy_volume, sell_volume>
            var summaryPositionsDepo = new Dictionary<string, TradeSharp.Util.Cortege2<decimal?, decimal?>>();
            decimal maxDepoValue = 0; // максимальное значение; исп. для масштабирования
            var quotes = QuoteStorage.Instance.ReceiveAllData();
            foreach (var sumPos in summaryPositions)
            {
                string errorString;
                var buyDepoVolume = DalSpot.Instance.ConvertToTargetCurrency(sumPos.Key, true,
                                                                             efficiency.Statistics.DepoCurrency,
                                                                             sumPos.Value.a, quotes, out errorString);
                if (buyDepoVolume.HasValue && buyDepoVolume.Value > maxDepoValue)
                    maxDepoValue = buyDepoVolume.Value;
                var sellDepoVolume = DalSpot.Instance.ConvertToTargetCurrency(sumPos.Key, true,
                                                                             efficiency.Statistics.DepoCurrency,
                                                                             sumPos.Value.b, quotes, out errorString);
                if (sellDepoVolume.HasValue && sellDepoVolume.Value > maxDepoValue)
                    maxDepoValue = sellDepoVolume.Value;
                summaryPositionsDepo.Add(sumPos.Key, new TradeSharp.Util.Cortege2<decimal?, decimal?>(buyDepoVolume, sellDepoVolume));
            }

            // визуализация
            try
            {

                var graphics = tickersAndVolumesBarControl.CreateGraphics();
                tickersAndVolumesBarControl.Height = summaryPositions.Count * tickersAndVolumesBarControl.RowHeight;
                // под центральную надпись выделяем чуть больше расчетного места
                tickersAndVolumesBarControl.CenterLabelWidth =
                    (int) symbols.Max(s => graphics.MeasureString(s, tickersAndVolumesBarControl.Font).Width) + 10;
                for (var i = 0; i < summaryPositions.Count; i++)
                {
                    var sumPos = summaryPositions.ElementAt(i);
                    var sumPosDepo = summaryPositionsDepo[sumPos.Key];
                    var buyVolumeString = sumPos.Value.a > 1000
                                              ? (sumPos.Value.a / 1000).ToStringUniformMoneyFormat() + " K"
                                              : sumPos.Value.a.ToStringUniformMoneyFormat();
                    var sellVolumeString = sumPos.Value.b > 1000
                                               ? (sumPos.Value.b / 1000).ToStringUniformMoneyFormat() + " K"
                                               : sumPos.Value.b.ToStringUniformMoneyFormat();
                    tickersAndVolumesBarControl.Lines.Add(new TickerVolumes
                        {
                            CenterLabel = sumPos.Key,
                            LeftValue = sumPosDepo.b.HasValue ? (double) (sumPosDepo.b.Value / maxDepoValue) : 0,
                            RightValue = sumPosDepo.a.HasValue ? (double) (sumPosDepo.a.Value / maxDepoValue) : 0,
                            LeftLabel = sellVolumeString,
                            RightLabel = buyVolumeString
                        });
                }
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("PerformerStatistic.BindSummaryPositions", ex);
            }
        }
 public void CalculateProfitCoeffs(AccountEfficiency ef)
 {
 }
        private static PerformerStat GeneratePortfolioAccountEfficiency(AccountEfficiency efficiency)
        {
            if (!AccountModel.Instance.AccountId.HasValue || efficiency.listProfit1000.Count == 0)
                return null;

            // pofit calc
            efficiency.listEquity = new List<EquityOnTime>();
            float equity = 10000;
            if (AccountModel.Instance.Account != null && AccountModel.Instance.Account != null)
                equity = (float) AccountModel.Instance.Account.Equity;
            // пусть performerStat.Equity соответствует последней точке на кривой доходности на 1000
            var profitFactor = equity / efficiency.listProfit1000.Last().equity;
            foreach (var equityOnTime in efficiency.listProfit1000)
            {
                efficiency.listEquity.Add(new EquityOnTime(equityOnTime.equity * profitFactor, equityOnTime.time));
            }

            // stats calc
            efficiency.listTransaction = new List<BalanceChange>();
            efficiency.InitialBalance = efficiency.listEquity[0].equity;
            efficiency.StartDate = efficiency.listEquity[0].time;
            new EfficiencyCalculator().CalculateProfitCoeffs(efficiency);

            var stat = efficiency.Statistics;
                //new PerformerStat
                //{
                //    Profit = efficiency.TWR,
                //    GreedyRatio = efficiency.GreedyRatio,
                //    MaxRelDrawDown = efficiency.MaxDrawdown,
                //    Sharp = efficiency.Sharp
                //};
            //stat.MaxLeverage = efficiency.MaxLeverage;
            return stat;
        }
        private static AccountEfficiency GeneratePortfolioAccountEfficiency(TopPortfolio portfolio, IAccountStatistics proxy)
        {
            var portfolioEfficiency = new AccountEfficiency();
            var efficiencies = new List<AccountEfficiency>();

            // detect beginning
            DateTime? beginDate = null;
            foreach (var performerStat in portfolio.Managers)
            {
                var efficiency = proxy.GetAccountEfficiencyShort(performerStat.Account, false, false);
                efficiencies.Add(efficiency);
                var firstDate = efficiency.listProfit1000.Min(e => e.time);
                if (!beginDate.HasValue)
                    beginDate = firstDate;
                else
                    if (firstDate < beginDate.Value)
                        beginDate = firstDate;
            }
            if (!beginDate.HasValue)
                return null;

            // pofit1000 calc
            portfolioEfficiency.listProfit1000 = new List<EquityOnTime>();
            portfolioEfficiency.listEquity = new List<EquityOnTime>();
            for (var date = beginDate.Value; date < DateTime.Now; date = date.AddDays(1))
            {
                float equity = 0;
                foreach (var efficiency in efficiencies)
                {
                    var equityOnTime = efficiency.listProfit1000.Find(e => e.time == date);
                    if (equityOnTime.time == default(DateTime))
                        continue;
                    equity += equityOnTime.equity;
                }
                equity /= efficiencies.Count;
                portfolioEfficiency.listProfit1000.Add(new EquityOnTime(equity, date));
            }
            if (!AccountModel.Instance.AccountId.HasValue || portfolioEfficiency.listProfit1000.Count == 0)
                return null;

            // pofit calc
            var stat = proxy.GetPerformerByAccountId(AccountModel.Instance.AccountId.Value);
            // пусть performerStat.Equity соответствует последней точке на кривой доходности на 1000
            var profitFactor = stat.Equity / portfolioEfficiency.listProfit1000.Last().equity;
            foreach (var equityOnTime in portfolioEfficiency.listProfit1000)
            {
                portfolioEfficiency.listEquity.Add(new EquityOnTime(equityOnTime.equity * profitFactor, equityOnTime.time));
            }

            // stats calc
            portfolioEfficiency.listTransaction = new List<BalanceChange>();
            portfolioEfficiency.InitialBalance = portfolioEfficiency.listEquity[0].equity;
            portfolioEfficiency.StartDate = portfolioEfficiency.listEquity[0].time;
            new EfficiencyCalculator().CalculateProfitCoeffs(portfolioEfficiency);

            return portfolioEfficiency;
        }
        /// <summary>
        /// кэш обновляется периодически для заданных акаунтов (accountIDs)
        /// </summary>
        public void UpdateCache()
        {
            var performers = TradeSharpServer.Instance.proxy.GetAllManagers(null);
            // добавить эталонные счета
            var topManagedAccounts = TradeSharpServer.Instance.proxy.GetCompanyTopPortfolioManagedAccounts() ?? new List<PerformerStat>();
            var missedAccounts = topManagedAccounts.Where(a => performers.All(p => p.Account != a.Account));
            performers.AddRange(missedAccounts);

            Logger.InfoFormat("UpdateCache({0})", performers.Count);

            // обновить кэш котировок
            using (new TimeLogger("Обновление котировок заняло "))
                dailyQuoteStorage.UpdateStorageSync();

            using (new TimeLogger("Расчет статистики занял "))
            foreach (var performer in performers)
            {
                if (isStopping)
                    return;
                AccountEfficiency efficiency;
                try
                {
                    efficiency = new AccountEfficiency(performer);
                    efficiencyCalculator.Calculate(efficiency);
                }
                catch (Exception ex)
                {
                    Logger.Error("UpdateCache - ошибка в AccountEfficiency ctor: ", ex);
                    continue;
                }
                try
                {
                    dicPerformers.UpdateValues(performer.Account, efficiency);
                }
                catch (Exception ex)
                {
                    Logger.Error("UpdateCache - ошибка в расчетах PerformerStat: ", ex);
                }
            }
            cacheUpdated = true;
            Logger.InfoFormat("UpdateCache() - {0} records OK", performers.Count);
        }
        public TopPortfolio GetTopPortfolio(int id, out AccountEfficiency userAccountEfficiency)
        {
            userAccountEfficiency = null;
            TopPortfolio result;

            try
            {
                // reading db
                using (var context = DatabaseContext.Instance.Make())
                {
                    try
                    {
                        var portfolio = context.TOP_PORTFOLIO.FirstOrDefault(p => p.Id == id);
                        if (portfolio == null)
                            return null;

                        result = new TopPortfolio
                            {
                                Id = portfolio.Id,
                                Name = portfolio.Name,
                                Criteria = portfolio.Criteria,
                                ParticipantCount = portfolio.ParticipantCount,
                                DescendingOrder = portfolio.DescendingOrder,
                                MarginValue = (float?) portfolio.MarginValue,
                                ManagedAccount = portfolio.ManagedAccount,
                                OwnerUser = portfolio.OwnerUser
                            };
                    }
                    catch (Exception ex)
                    {
                        Logger.Error("AccountEfficiencyCache.GetTopPortfolio.db - read error", ex);
                        return null;
                    }
                }

                // Statistics
                if (result.ManagedAccount.HasValue)
                    result.Statistics = GetPerformerByAccountId(result.ManagedAccount.Value);

                // идентификаторы сигнальщиков
                // тут будут корректировки, потому что вхождение сигнальщика в топ не всегда на данный момент времени соответствует критерию
                // потому что обновление топов и обновлние статистики сигнальщиков выполняются в разное время, а результаты выборки хранятся только run-time
                var performers = GetAllPerformersWithCriteria(true, result.Criteria, result.ParticipantCount,
                                                              !result.DescendingOrder, result.MarginValue, 0);

                if (result.Statistics == null || performers == null)
                {
                    Logger.ErrorFormat("GetTopPortfolio() - статистика / перформеры не получены, кеш обновлен: {0}", cacheUpdated);
                    return result;
                }

                // ManagerIds
                result.ManagerIds = performers.Select(p => p.Account).ToList();

                // Manages
                result.Managers = performers;

                // Orders
                if (result.ManagedAccount.HasValue)
                    result.Orders = GetAccountDeals(result.ManagedAccount.Value, true);

                if (result.IsCompanyPortfolio)
                    return result;

                // ----------------
                // 4 user portfolio
                // ----------------
                if (!cacheUpdated)
                    return null;
                var managersFullStat = new List<AccountEfficiency>();

                // beginning of profit1000 chart
                if (result.ManagerIds == null)
                    throw new Exception("GetTopPortfolio(польз. портфель) - список Id менеджеров = null");

                DateTime? beginDate = null;
                try
                {
                    foreach (var managerId in result.ManagerIds)
                    {
                        var stat = dicPerformers.ReceiveValue(managerId);
                        if (stat == null)
                        {
                            Logger.Error("AccountEfficiencyCache.GetTopPortfolio.dicPerformers.TryGetValue returned null");
                            return null; // ManagerIds.Count != managersFullStat.Count
                        }
                        if (stat.listProfit1000 == null || stat.listProfit1000.Count == 0)
                            continue;
                        managersFullStat.Add(stat);
                        // detect beginning
                        var firstDate = stat.listProfit1000.Min(e => e.time);
                        if (!beginDate.HasValue)
                            beginDate = firstDate;
                        else
                            if (firstDate < beginDate.Value)
                                beginDate = firstDate;
                    }
                }
                catch (Exception ex)
                {
                    Logger.Error("AccountEfficiencyCache.GetTopPortfolio - stat gen error", ex);
                    return null;
                }

                // userAccountEfficiency
                userAccountEfficiency = new AccountEfficiency
                    {
                        listProfit1000 = new List<EquityOnTime>(),
                    };

                // pofit1000 calc
                if (beginDate.HasValue)
                    for (var date = beginDate.Value.Date; date < DateTime.Now; date = date.AddDays(1))
                    {
                        float equity = 0;
                        var equityCount = 0;
                        foreach (var fullStat in managersFullStat)
                        {
                            var equityOnTime = fullStat.listProfit1000.Find(e => e.time == date);
                            if (equityOnTime.time == default(DateTime))
                                continue;
                            equity += equityOnTime.equity;
                            equityCount++;
                        }
                        if (equityCount == 0) // данных на этот день недостаточно - пропускаем
                            continue;
                        equity /= equityCount;
                        userAccountEfficiency.listProfit1000.Add(new EquityOnTime(equity, date));
                    }

                // stats calc
                userAccountEfficiency.Statistics.DealsCount = managersFullStat.Sum(s => s.Statistics.DealsCount);
                Logger.InfoFormat("запрошен портфель - GetTopPortfolio({0}, {1} управляющих)", result.Name, result.Managers);

                return result;
            }
            catch (Exception ex)
            {
                Logger.Error("Ошибка в GetTopPortfolio", ex);
                throw;
            }
        }
        public static void RebindStatisticsFastGrid(FastGrid.FastGrid grid, PerformerStat performer, AccountEfficiency efficiency)
        {
            if (performer == null)
                return;
            var singleValueData = new List<TradeSharp.Util.Cortege2<string, string>>();
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleProfitInPercents"),
                    b = performer.Profit.ToString("f2")
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleDealsTotal"),
                    b = performer.DealsCount.ToStringUniformMoneyFormat()
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleMaximumRelativeDrawdownInPercents"),
                    b = performer.MaxRelDrawDown.ToString("N2")
                });

            if (efficiency != null) // при получении всех сделок
            {
                singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleProfitableDealCount"),
                    b = (efficiency.closedDeals.Count(d => d.ResultDepo > 0) +
                         efficiency.openedDeals.Count(d => d.ResultDepo > 0)).ToStringUniformMoneyFormat()
                });
                singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleLosingDealCount"),
                    b = (efficiency.closedDeals.Count(d => d.ResultDepo < 0) +
                         efficiency.openedDeals.Count(d => d.ResultDepo < 0)).ToStringUniformMoneyFormat()
                });
                singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                    {
                        a = Localizer.GetString("TitleDealsOpened"),
                        b = efficiency.DealsStillOpened.ToStringUniformMoneyFormat()
                    });
            }

            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleRatioOfAverageProfitToAverageLossShort"),
                    b = performer.GreedyRatio.ToString("N2")
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleMaximumLeverage"),
                    b = performer.MaxLeverage.ToString("N2")
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleProfitForNMonthsInPercents"),
                    b = performer.ProfitLastMonths.ToString("f2")
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleGAAnnualProfitInPercentsShort"),
                    b = performer.AvgYearProfit.ToString("N3")
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleSharpeRatioShort"),
                    b = performer.Sharp.ToString("N2")
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleSubscriberCount"),
                    b = performer.SubscriberCount.ToString("d")
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleFundsUnderManagementShort") + ", " + performer.DepoCurrency,
                    b = performer.Equity.ToStringUniformMoneyFormat()
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleRatingFS"),
                    b = performer.Score.ToString("f2")
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleTradeTimeInDays"),
                    b = performer.DaysTraded.ToString("d")
                });
            singleValueData.Add(new TradeSharp.Util.Cortege2<string, string>
                {
                    a = Localizer.GetString("TitleProfitInPoints"),
                    b = performer.SumProfitPoints.ToStringUniformMoneyFormat(false)
                });
            grid.DataBind(singleValueData);

            // для удобочитаемости первая колонка сохраняет первоначальную минимальную ширину
            var skippedColumns = grid.Columns.Where(c => c.PropertyName == "a").ToList();
            var minWidths = skippedColumns.ToDictionary(c => c, c => c.ColumnMinWidth);
            grid.CheckSize(true);
            skippedColumns.ForEach(c => c.ColumnMinWidth = minWidths[c]);
            grid.Invalidate();
        }
        /// <summary>
        /// Получить статистику распределения сделок по инструментам
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<DealCountBySymbol> GetDealCountBySymbol(AccountEfficiency efficiency)
        {
            if (efficiency == null) return new List<DealCountBySymbol>();
            if (efficiency.closedDeals == null) return new List<DealCountBySymbol>();

            var dic = new Dictionary<string, DealCountBySymbol>();

            var dealLists = new[] { efficiency.closedDeals, efficiency.openedDeals };
            foreach (var dealList in dealLists)
                foreach (var deal in dealList)
            {
                if (dic.ContainsKey(deal.Symbol))
                    dic[deal.Symbol].DealCount++;
                else
                    dic.Add(deal.Symbol, new DealCountBySymbol { DealCount = 1, Title = deal.Symbol });
            }

            var result = new List<DealCountBySymbol>();

            // calc percentage
            var dealsTotal = efficiency.closedDeals.Count + efficiency.openedDeals.Count;
            var zeroPercentageSymbols = new List<string>();
            var zeroPercentageDealCount = 0;
            foreach (var symbol in dic)
            {
                var percent = symbol.Value.DealCount * 100 / dealsTotal;
                if (percent == 0)
                {
                    zeroPercentageSymbols.Add(symbol.Key);
                    zeroPercentageDealCount += symbol.Value.DealCount;
                    continue;
                }
                result.Add(new DealCountBySymbol
                    {
                        DealCount = symbol.Value.DealCount,
                        Title = string.Format("{0}\t({1}%)", symbol.Value.Title, percent)
                    });
            }

            if (zeroPercentageSymbols.Count != 0)
                result.Add(new DealCountBySymbol
                    {
                        DealCount = zeroPercentageDealCount,
                        Title = string.Format("{0}\t({1}%)", Localizer.GetString("TitleOthers"), zeroPercentageDealCount * 100 / dealsTotal)
                    });

            return result;
        }
        // отобразить данные суммарной статистики
        public static void BindSummaryStatistics(FastGrid.FastGrid gridSummaryStat, PerformerStat performerStat, AccountEfficiency efficiency, bool showFullInfo)
        {
            if (performerStat == null)
                return;

            // суммарная статистика
            var rowColors = new[] { Color.Red, Color.Blue, Color.Black };
            var color = performerStat.Profit >= 0 ? rowColors[1] : rowColors[0];
            var statItems = new List<SummaryStatItem>();
            if (showFullInfo)
            {
                statItems.Add(
                    new SummaryStatItem(
                        string.Format("{0}\t{1}", Localizer.GetString("TitleSubscriberCount"),
                                      performerStat.SubscriberCount),
                        rowColors[2]));
                statItems.Add(
                    new SummaryStatItem(string.Format("{0}\t{1}", Localizer.GetString("TitleInvestorCount"), 0),
                                        rowColors[2]));
                statItems.Add(new SummaryStatItem(
                                  string.Format("{0}, {1}\t{2}", Localizer.GetString("TitleFundsUnderManagementShort"),
                                                performerStat.DepoCurrency,
                                                performerStat.Equity.ToStringUniformMoneyFormat()), rowColors[2]));
            }
            statItems.Add(new SummaryStatItem(Localizer.GetString("TitleTotalProfit"), rowColors[2]));
            statItems.Add(new SummaryStatItem(string.Format("{0} {1} / {2:f2}% / {3} {4}",
                                                            performerStat.ProfitLastMonthsAbs.ToStringUniformMoneyFormat(false),
                                                            performerStat.DepoCurrency,
                                                            performerStat.Profit,
                                                            performerStat.SumProfitPoints.ToStringUniformMoneyFormat(false),
                                                            Localizer.GetString("TitlePointsUnits")), color));

            if (efficiency != null && efficiency.openedDeals != null && efficiency.openedDeals.Count > 0)
            {
                var openResultDepo = efficiency.openedDeals.Sum(o => o.ResultDepo);
                var openResultPoints = efficiency.openedDeals.Sum(o => o.ResultPoints);
                var openResultPercent = performerStat.Equity == 0 ? 0
                                            : 100 * openResultDepo / performerStat.Equity;
                var colorOpen = openResultDepo >= 0 ? rowColors[1] : rowColors[0];
                statItems.Add(new SummaryStatItem(Localizer.GetString("TitleCurrentProfit"), rowColors[2]));
                statItems.Add(new SummaryStatItem(string.Format("{0} {1} / {2:f2}% / {3} {4}",
                                                      openResultDepo.ToStringUniformMoneyFormat(),
                                                      performerStat.DepoCurrency,
                                                      openResultPercent,
                                                      openResultPoints.ToStringUniformMoneyFormat(),
                                                      Localizer.GetString("TitlePointsUnits")),
                                        colorOpen));
            }

            gridSummaryStat.DataBind(statItems);
        }
        /// <summary>
        /// считаются по кривой плеча
        /// </summary>
        private void CalculateRiskCoeffs(AccountEfficiency ef)
        {
            if (ef.closedDeals.Count == 0 && ef.openedDeals.Count == 0) return;
            if (ef.listLeverage == null || ef.listLeverage.Count == 0) return;

            // наибольшее плечо в момент
            ef.Statistics.MaxLeverage = ef.listLeverage.Max(l => l.equity);

            // среднее плечо (без 0-х значений)
            var countLev = ef.Statistics.MaxLeverage == 0 ? 0 : ef.listLeverage.Count(l => l.equity > 0);
            ef.Statistics.AvgLeverage = countLev == 0 ? 0 : ef.listLeverage.Sum(l => l.equity) / countLev;

            // коэффициент жадности - средний профит по сделке, деленный на средний убыток
            var countProfit = ef.closedDeals.Count(d => d.ResultDepo > 0);
            var countLoss = ef.closedDeals.Count(d => d.ResultDepo > 0);
            if (countProfit > 0 || countLoss > 0)
            {
                var avgProfit = ef.closedDeals.Sum(d => d.ResultDepo > 0 ? d.ResultDepo : 0) / countProfit;
                var avgLoss = ef.closedDeals.Sum(d => d.ResultDepo < 0 ? -d.ResultDepo : 0) / countLoss;
                ef.Statistics.GreedyRatio = avgLoss == 0 ? 0 : avgProfit / avgLoss;
            }
        }
 public bool Calculate(AccountEfficiency ef)
 {
     return true;
 }
        public bool Calculate(AccountEfficiency ef)
        {
            if (ef == null)
                throw new ArgumentException("EfficiencyCalculator.Calculate - input ptr is NULL");
            if (ef.Statistics.Account == 0)
                throw new ArgumentException("EfficiencyCalculator.Calculate - input_ptr.AccountId is 0");

            // получить сделки
            var deals = DealStorage.Instance.GetDeals(ef.Statistics.Account);
            ef.openedDeals = new List<MarketOrder>();
            ef.closedDeals = new List<MarketOrder>();

            foreach (var deal in deals)
                if (deal.IsOpened)
                    ef.openedDeals.Add(deal);
                else
                    ef.closedDeals.Add(deal);

            if (deals.Count == 0)
            {
                Logger.Info("AccountEfficiency.Calculate - нет сделок");
                return false;
            }

            ef.Statistics.DealsCount = deals.Count;
            ef.DealsStillOpened = ef.openedDeals.Count;

            // транзакции
            ef.listTransaction = BalanceStorage.Instance.GetBalanceChanges(ef.Statistics.Account);
            if (ef.listTransaction == null || ef.listTransaction.Count == 0)
            {
                Logger.Info("AccountEfficiency.Calculate - нет транзакций");
                return false;
            }

            Logger.Info("AccountEfficiency.Calculate(" + ef.Statistics.Account + ")");

            // время отсчета - время первого заведения средств
            var startDate = ef.listTransaction.Min(t => t.ValueDate);

            // получить список используемых котировок
            var symbolsUsed = ef.closedDeals.Select(d => d.Symbol).Union(ef.openedDeals.Select(o => o.Symbol)).Distinct().ToList();
            // ... в т.ч., котировок для перевода базовой валюты в валюту депо (плечо)
            // и перевода контрвалюты в валюту депо (профит)
            var symbolsMore = new List<string>();
            foreach (var smb in symbolsUsed)
            {
                bool inverse, eq;
                var smbBase = DalSpot.Instance.FindSymbol(smb, true, ef.Statistics.DepoCurrency, out inverse, out eq);
                if (!string.IsNullOrEmpty(smbBase)) symbolsMore.Add(smbBase);
                var smbCounter = DalSpot.Instance.FindSymbol(smb, false, ef.Statistics.DepoCurrency, out inverse, out eq);
                if (!string.IsNullOrEmpty(smbCounter)) symbolsMore.Add(smbCounter);
            }
            symbolsUsed.AddRange(symbolsMore);
            symbolsUsed = symbolsUsed.Distinct().ToList();

            // котировки
            var dicQuote = new Dictionary<string, List<QuoteData>>();
            foreach (var smb in symbolsUsed)
            {
                dicQuote.Add(smb,
                    dailyQuoteStorage.GetQuotes(smb).Select(q => new QuoteData(q.b, q.b, q.a)).ToList());
            }
            //TickerStorage.Instance.GetQuotes(symbolsUsed.ToDictionary(s => s, s => (DateTime?)null));
            if (dicQuote == null || dicQuote.Count == 0)
            {
                Logger.Info("AccountEfficiency.Calculate - нет котировок");
                return false;
            }

            if (ef.openedDeals.Count > 0)
            {
                foreach (var t in ef.openedDeals)
                {
                    List<QuoteData> dicQuoteValue;
                    if (!dicQuote.TryGetValue(t.Symbol, out dicQuoteValue))
                        Logger.Error(String.Format("Symbol {0} was not found in dicQuote", t.Symbol));
                    else
                        if (dicQuoteValue.Count == 0)
                            Logger.Error(String.Format("No quote data for symbol {0}", t.Symbol));
                        else
                            t.PriceExit = dicQuoteValue.Last().GetPrice(t.Side == 1
                                ? QuoteType.Bid : QuoteType.Ask);
                }
            }

            var quoteArc = new QuoteArchive(dicQuote);
            AccountPerformanceRaw performance;
            try
            {
                performance = equityCurveCalculator.CalculateEquityCurve(deals, ef.Statistics.DepoCurrency, quoteArc, ef.listTransaction);
            }
            catch (Exception ex)
            {
                Logger.Error("Ошибка в EfficiencyCalculator.CalculateEquityCurve()", ex);
                return false;
            }

            ef.Statistics.TotalTradedInDepoCurrency = performance.totalTradedVolume;
            var lstEquity = performance.equity;
            if (lstEquity == null) return false;
            ef.listLeverage = performance.leverage;

            // исключить пустые значения с начала отсчета
            ef.listEquity = new List<EquityOnTime>();
            var startCopy = false;
            foreach (var eq in lstEquity)
            {
                if (eq.equity > 0) startCopy = true;
                if (startCopy) ef.listEquity.Add(eq);
            }

            if (ef.listEquity.Count == 0) return false;
            ef.StartDate = startDate;
            ef.InitialBalance = ef.listEquity[0].equity;

            // рассчитать коэффициенты доходности
            CalculateProfitCoeffs(ef);
            CalculateRiskCoeffs(ef);

            // актуальные котировки
            ef.currentQuotes = quoteArc.GetCurrentQuotes();

            ef.Statistics.Chart = ef.listProfit1000 == null ||
                                         ef.listProfit1000.Count == 0
                                             ? new byte[MiniChartPacker.profitChartPointCount]
                                             : MiniChartPacker.PackChartInArray(ef.listProfit1000);

            // дней торгует
            var startDayOpen = DateTime.Now;
            if (ef.openedDeals.Count > 0)
                startDayOpen = ef.openedDeals.Max(d => d.TimeEnter);
            if (ef.closedDeals.Count > 0)
            {
                var dateClosed = ef.closedDeals.Min(d => d.TimeEnter);
                if (dateClosed < startDayOpen)
                    startDayOpen = dateClosed;
            }
            ef.Statistics.DaysTraded = (int)Math.Round((DateTime.Now - startDayOpen).TotalDays);

            // сумма профитных сделок (результат) к сумме убыточных сделок
            var sumProf = ef.closedDeals.Sum(d => d.ResultDepo > 0 ? d.ResultDepo : 0) +
                ef.openedDeals.Sum(d => d.ResultDepo > 0 ? d.ResultDepo : 0);
            var sumLoss = ef.closedDeals.Sum(d => d.ResultDepo > 0 ? d.ResultDepo : 0) +
                ef.openedDeals.Sum(d => d.ResultDepo > 0 ? d.ResultDepo : 0);
            ef.Statistics.AvgWeightedDealProfitToLoss = sumLoss == 0 && sumProf == 0
                                                           ? 0
                                                           : 100 * sumProf / (sumProf + sumLoss);
            var dateFirst = DateTime.Now.Date.AddMonths(-3);
            ef.Statistics.WithdrawalLastMonths = (float)ef.listTransaction.Sum(t =>
                                                                               (t.ChangeType ==
                                                                                BalanceChangeType.Withdrawal &&
                                                                                t.ValueDate >= dateFirst)
                                                                                   ? t.AmountDepo : 0);
            // профит в ПП
            ef.Statistics.SumProfitPoints = ef.closedDeals.Sum(d => d.ResultPoints);

            return true;
        }
        private void PerformerEfficiencyWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            efficiency = (AccountEfficiency) e.Result;
            if (efficiency == null)
                return;

            // строим детализированные графики доходности
            CreateDetailedCharts();

            if (PortfolioChanged != null)
                PortfolioChanged(this, new EventArgs());

            if (LoadAllData && !openedOrdersWorker.IsBusy)
                openedOrdersWorker.RunWorkerAsync(portfolio.ManagedAccount);
        }