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)); } }
/// <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 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); }
/// <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 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 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 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); } }
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) { }
public bool Calculate(AccountEfficiency ef) { return(true); }
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; } }
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); }
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); }
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); } }