private void UpdateTablesAndCharts(AccountStatistics stat) { ShowStatistics(stat); BuildEquityChartSafe(stat); }
private void ShowStatistics(AccountStatistics stat) { var depoCurx = AccountStatus.Instance.AccountData.Currency; var statList = new List<StatItem> { new StatItem(Localizer.GetString("TitleAccountNumber"), AccountStatus.Instance.AccountData.ID.ToString()), new StatItem(Localizer.GetString("TitleDate"), string.Format("{0:dd.MM.yyyy}", DateTime.Now)), new StatItem(Localizer.GetString("TitleInitialDate"), string.Format("{0:dd.MM.yyyy}", stat.listEquity.Count > 0 ? stat.listEquity[0].time : cbStartFrom.Checked ? dpStart.Value : DateTime.Now)), new StatItem(Localizer.GetString("TitleInitialDeposit"), stat.InitialBalance.ToStringUniformMoneyFormat(false) + " " + depoCurx), new StatItem(Localizer.GetString("TitleCurrentDeposit"), stat.Statistics.Equity.ToStringUniformMoneyFormat(false) + " " + depoCurx), new StatItem(Localizer.GetString("TitleDealsTotal"), stat.Statistics.DealsCount.ToString()), new StatItem(Localizer.GetString("TitleOpenedDeals"), stat.DealsStillOpened.ToString()), new StatItem(Localizer.GetString("TitleSummaryResult"), (stat.sumClosedResult + stat.sumOpenResult).ToStringUniformMoneyFormat() + " " + depoCurx), new StatItem(Localizer.GetString("TitleSummaryResultInClosedDeals"), stat.sumClosedResult.ToStringUniformMoneyFormat() + " " + depoCurx), new StatItem(Localizer.GetString("TitleSummaryResultInOpenedDeals"), stat.sumOpenResult.ToStringUniformMoneyFormat() + " " + depoCurx), new StatItem(Localizer.GetString("TitleSummaryDepositsAndWithdraws"), stat.sumDeltaBalance.ToStringUniformMoneyFormat() + " " + depoCurx), new StatItem(Localizer.GetString("TitleMaximumRelativeDrawdown"), stat.Statistics.MaxRelDrawDown.ToString("f1") + "%"), new StatItem(Localizer.GetString("TitleGADailyProfit"), (100 * stat.ProfitGeomDay).ToStringUniformMoneyFormat() + "%"), new StatItem(Localizer.GetString("TitleGAMonthlyProfit"), (100 * stat.ProfitGeomMonth).ToStringUniformMoneyFormat() + "%"), new StatItem(Localizer.GetString("TitleGAAnnualProfit"), (100 * stat.ProfitGeomYear).ToStringUniformMoneyFormat() + "%") }; dgStat.DataBind(statList, typeof(StatItem)); }
private void MakeCalculation(object obj, DoWorkEventArgs args) { if (AccountStatus.Instance.AccountData == null || MainForm.serverProxyTrade.proxy == null) { MessageBox.Show(Localizer.GetString("MessageNoConnectionServer")); return; } var accountId = AccountStatus.Instance.AccountData.ID; // получить сделки за указанный период и эквити на начало периода // и все пополнения - снятия SetProgressValueSafe(10, Localizer.GetString("MessageGettingOrderHistory") + "..."); if (worker.CancellationPending) return; List<MarketOrder> orders; var status = TradeSharpAccount.Instance.GetHistoryOrdersUncompressed(accountId, cbStartFrom.Checked ? dpStart.Value : (DateTime?)null, out orders); if (status != RequestStatus.OK || orders == null) { MessageBox.Show(string.Format( Localizer.GetString("MessageUnableToGetOrderHistory") + ": {0}", EnumFriendlyName<RequestStatus>.GetString(status))); return; } // получить изменения баланса if (worker.CancellationPending) return; SetProgressValueSafe(20, Localizer.GetString("MessageGettingTransfersHistory") + "..."); List<BalanceChange> balanceChanges; status = TradeSharpAccount.Instance.proxy.GetBalanceChanges(accountId, null, out balanceChanges); if (status != RequestStatus.OK || balanceChanges == null) { MessageBox.Show(string.Format(Localizer.GetString("MessageUnableToGetTransfersHistory") + ": {0}", EnumFriendlyName<RequestStatus>.GetString(status))); return; } // получить открытые ордера if (worker.CancellationPending) return; SetProgressValueSafe(30, Localizer.GetString("MessageGettingOpenPosHistory") + "..."); List<MarketOrder> openOrders; status = TradeSharpAccount.Instance.proxy.GetMarketOrders(accountId, out openOrders); if (status != RequestStatus.OK || openOrders == null) { MessageBox.Show(string.Format(Localizer.GetString("MessageUnableToGetOpenPosHistory") + ": {0}", EnumFriendlyName<RequestStatus>.GetString(status))); return; } // построить кривую equity и посчитать характеристики торговли по счету var stat = BuildEquityCurve(orders, openOrders, balanceChanges); statistics = stat; // сохранить доходность в словаре if (statByAccount.ContainsKey(accountId)) statByAccount[accountId] = stat; else statByAccount.Add(accountId, stat); if (worker.CancellationPending) return; // построить график и вывести показатели доходности SetProgressValueSafe(90, Localizer.GetString("MessageMakingReport") + "..."); SetProgressValueSafe(100, Localizer.GetString("MessageCalculationCompleted") + "..."); Invoke(new Action(() => { UpdateTablesAndCharts(stat); MessageBox.Show(this, Localizer.GetString("MessageCalculationCompleted"), Localizer.GetString("TitleNotice"), MessageBoxButtons.OK, MessageBoxIcon.Information); })); SetProgressValueSafe(0, string.Empty); }
private AccountStatistics BuildEquityCurve( List<MarketOrder> orders, List<MarketOrder> openOrders, List<BalanceChange> balanceChanges) { var stat = new AccountStatistics { Statistics = new PerformerStat { DealsCount = orders.Count + openOrders.Count, }, DealsStillOpened = openOrders.Count, listEquity = new List<EquityOnTime>() }; if (worker.CancellationPending) return stat; SetProgressValueSafe(40, Localizer.GetString("MessageActualizingQuoteHistory") + "..."); if (balanceChanges.Count == 0) { MessageBox.Show(Localizer.GetString("MessageNoTransfersData")); return stat; } var curxDepo = AccountStatus.Instance.AccountData.Currency; // запросить котировки, если стоит соотв. флаг var quotesDic = UpdateQuotesForStats(orders, openOrders, curxDepo); // построить кривую эквити // отсчет от первого движения на счете либо от стартовой даты balanceChanges = balanceChanges.OrderBy(bc => bc.ValueDate).ToList(); var startDate = cbStartFrom.Checked ? dpStart.Value : balanceChanges[0].ValueDate; // начальный баланс var initialBalance = !cbStartFrom.Checked ? balanceChanges[0].SignedAmountDepo : balanceChanges.Where(bc => bc.ValueDate <= startDate).Sum(bc => bc.SignedAmountDepo); // движения от начального баланса с заданным интервалом дискретизации var dueBalances = balanceChanges.Where(bc => bc.ValueDate > startDate).ToList(); var dueDeals = orders.Union(openOrders).OrderBy(o => o.TimeEnter).ToList(); var endDate = DateTime.Now; var balance = initialBalance; var cursor = new BacktestTickerCursor(); var path = ExecutablePath.ExecPath + TerminalEnvironment.QuoteCacheFolder; if (!cursor.SetupCursor(path, quotesDic.Keys.ToList(), quotesDic.Min(t => t.Value))) { MessageBox.Show(Localizer.GetString("MessageErrorGettingQuotesFromFiles")); return stat; } var currentQuotes = new Dictionary<string, QuoteData>(); var timeframeMinutes = Timeframe; SetProgressValueSafe(60, Localizer.GetString("MessageCalculationInProcess") + "..."); try { for (var time = startDate; time < endDate; time = time.AddMinutes(timeframeMinutes)) { if (worker.CancellationPending) return stat; // получить баланс на дату for (var i = 0; i < dueBalances.Count; i++) { if (dueBalances[i].ValueDate > time) break; balance += dueBalances[i].SignedAmountDepo; dueBalances.RemoveAt(i); i--; } var equity = balance; // получить текущие котировки var candles = cursor.MoveToTime(time); foreach (var candle in candles) { var quote = new QuoteData(candle.Value.open, DalSpot.Instance.GetAskPriceWithDefaultSpread(candle.Key, candle.Value.open), candle.Value.timeOpen); if (currentQuotes.ContainsKey(candle.Key)) currentQuotes[candle.Key] = quote; else currentQuotes.Add(candle.Key, quote); } // уточнить результат по открытым позициям for (var i = 0; i < dueDeals.Count; i++) { if (worker.CancellationPending) return stat; if (dueDeals[i].TimeExit.HasValue) if (dueDeals[i].TimeExit.Value <= time) { dueDeals.RemoveAt(i); i--; continue; } var deal = dueDeals[i]; if (deal.TimeEnter <= time) { // посчитать текущий результат сделки // в контрвалюте if (!currentQuotes.ContainsKey(deal.Symbol)) continue; var dealTickerQuote = currentQuotes[deal.Symbol]; var dealTickerPrice = deal.Side > 0 ? dealTickerQuote.bid : dealTickerQuote.ask; var dealResult = deal.Volume * deal.Side * (dealTickerPrice - deal.PriceEnter); // перевод прибыли в валюту депо bool inverse, areSame; var dealTransferSymbol = DalSpot.Instance.FindSymbol(deal.Symbol, false, curxDepo, out inverse, out areSame); float? baseDepoRate = null; if (areSame) baseDepoRate = 1f; else { if (!string.IsNullOrEmpty(dealTransferSymbol)) if (currentQuotes.ContainsKey(dealTransferSymbol)) { var quote = currentQuotes[dealTransferSymbol]; if (quote.time >= time) baseDepoRate = !inverse ? (deal.Side > 0 ? quote.bid : quote.ask) : (deal.Side > 0 ? 1 / quote.ask : 1 / quote.bid); } } if (!baseDepoRate.HasValue) continue; dealResult *= baseDepoRate.Value; equity += (decimal)dealResult; } } // for (deal ... // сохранить отметку - время/доходность stat.listEquity.Add(new EquityOnTime((float)equity, time)); } // for (time ... += Timeframe } finally { cursor.Close(); } // если история начинается с пустого депо - изъять эти строки из истории var firstNotEmpty = stat.listEquity.FindIndex(e => e.equity > 0); if (firstNotEmpty == stat.listEquity.Count - 1) stat.listEquity.Clear(); else if (firstNotEmpty > 0) stat.listEquity.RemoveRange(0, firstNotEmpty); SetProgressValueSafe(70, Localizer.GetString("MessageBuildingEquityChart") + "..."); stat.Calculate(balanceChanges, openOrders, startDate); return stat; }
private void BuildEquityChartUnsafe(AccountStatistics stat) { chartProfit.Graphs[0].Series[0].Clear(); //chartProfit.Graphs[0].Series[1].Clear(); chartProfit1000.Graphs[0].Series[0].Clear(); if (stat.listEquity == null || stat.listEquity.Count == 0) return; if (stat.listEquity != null) foreach (var pt in stat.listEquity) { chartProfit.Graphs[0].Series[0].Add(new BalanceByDate(pt.time, pt.equity)); } chartProfit.Initialize(); // доход на 1000 foreach (var pt in stat.listProfit1000) { chartProfit1000.Graphs[0].Series[0].Add(new BalanceByDate(pt.time, pt.equity)); } chartProfit1000.Initialize(); }
private void BuildEquityChartSafe(AccountStatistics stat) { Invoke(new Action<AccountStatistics>(BuildEquityChartUnsafe), stat); }