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); }