private static void HandleSellSignals(SimulationDto simulationDto, IList <CompanyPricesDto> allPrices, SignalEvent signalEvent, SimulationResultDto simulationResult) { var prices = ConvertPrices(allPrices, signalEvent.CompaniesToSell, signalEvent.Date); foreach (var price in prices) { if (!simulationResult.CurrentCompanyQuantity.ContainsKey(price.Key)) { continue; } var transaction = new SimulationTransactionDto { Date = signalEvent.Date, CompanyId = price.Key, Price = price.Value, Action = SignalAction.Sell, Quantity = simulationResult.CurrentCompanyQuantity[price.Key], BudgetAfter = simulationDto.Budget + simulationResult.CurrentCompanyQuantity[price.Key] * price.Value }; simulationResult.TransactionsLog.Add(transaction); simulationDto.Budget = transaction.BudgetAfter; simulationResult.CurrentCompanyQuantity.Remove(price.Key); } }
private static decimal BuyAndKeepStrategyProfit(SimulationDto simulationDto, SimulationResultDto simulationResult, IList <CompanyPricesDto> allPrices) { var budget = simulationResult.StartBudget; var budgetPerCompany = budget / allPrices.Count; foreach (var companyPricesDto in allPrices) { var startDayPrice = companyPricesDto.Prices.Where(item => item.Date >= simulationDto.StartDate) .OrderBy(item => item.Date) .FirstOrDefault(); var endDatePrice = companyPricesDto.Prices.Where(item => item.Date <= simulationDto.EndDate) .OrderByDescending(item => item.Date) .FirstOrDefault(); if (startDayPrice == null || endDatePrice == null) { continue; } var quantity = (int)Math.Floor(budgetPerCompany / startDayPrice.ClosePrice); budget += quantity * (endDatePrice.ClosePrice - startDayPrice.ClosePrice); } var keepStrategyProfit = budget; return(keepStrategyProfit); }
private static void CalculateProfit(SimulationResultDto result) { foreach (var pagedTransaction in result.TransactionsLog.OrderBy(item => item.Date)) { if (pagedTransaction.Action == SignalAction.Buy) { pagedTransaction.Profit = 0m; continue; } var pastTransactions = result.TransactionsLog .Where(item => item.CompanyId == pagedTransaction.CompanyId && item.Date < pagedTransaction.Date) .OrderBy(item => item.Date) .ToList(); var quantity = 0m; var price = 0m; foreach (var pastTransaction in pastTransactions) { if (pastTransaction.Action == SignalAction.Buy) { price = (price * quantity + pastTransaction.Price * pastTransaction.Quantity) / (quantity + pastTransaction.Quantity); quantity += pastTransaction.Quantity; } else { quantity -= pastTransaction.Quantity; } } pagedTransaction.Profit = pagedTransaction.Quantity * (pagedTransaction.Price - price); } }
private static void HandleBuySignals(SimulationDto simulationDto, IList <CompanyPricesDto> allPrices, SignalEvent signalEvent, SimulationResultDto simulationResult) { var prices = ConvertPrices(allPrices, signalEvent.CompaniesToBuy, signalEvent.Date); //.OrderByDescending(item => item.Value); foreach (var price in prices) { var value = simulationDto.Budget; if (simulationDto.HasMaximalTransactionLimit) { value = Math.Min(value, simulationDto.MaximalBudgetPerTransaction); } if (simulationDto.HasMinimalTransactionLimit) { if (value < simulationDto.MinimalBudgetPerTransaction) { continue; // not enough money } value = Math.Max(value, simulationDto.MinimalBudgetPerTransaction); } if (value < price.Value) { continue; } int quantity = (int)Math.Floor(value / price.Value); var transaction = new SimulationTransactionDto { Date = signalEvent.Date, CompanyId = price.Key, Price = price.Value, Action = SignalAction.Buy, Quantity = quantity, BudgetAfter = simulationDto.Budget - quantity * price.Value }; simulationResult.TransactionsLog.Add(transaction); if (simulationResult.CurrentCompanyQuantity.ContainsKey(price.Key)) { simulationResult.CurrentCompanyQuantity[price.Key] += quantity; } else { simulationResult.CurrentCompanyQuantity.Add(price.Key, quantity); } simulationDto.Budget = transaction.BudgetAfter; } }
private static void CalculateAverageGainAndLossOnTransaction(SimulationResultDto resultDto, IList <int> companyIds) { var transactionDiffs = companyIds.ToDictionary(companyId => companyId, companyId => 0m); var gain = 0m; var loss = 0m; int successes = 0; int losses = 0; TransactionStatistics stats = new TransactionStatistics(); foreach (var trans in resultDto.TransactionsLog) { if (trans.Action == SignalAction.Buy) { transactionDiffs[trans.CompanyId] += trans.Quantity * trans.Price; } else if (trans.Action == SignalAction.Sell) { decimal buyValue = transactionDiffs[trans.CompanyId]; decimal sellValue = trans.Quantity * trans.Price; transactionDiffs[trans.CompanyId] = 0; var diff = sellValue - buyValue; if (diff > 0m) { gain += diff; successes++; } else { loss += diff; losses++; } } } if (successes + losses != 0) { stats.SuccessTransactionPercentage = Math.Round(100 * ((double)successes) / (successes + losses), 2); stats.FailedTransactionPercentage = Math.Round(100 * ((double)losses) / (successes + losses), 2); } else { stats.SuccessTransactionPercentage = 0; stats.FailedTransactionPercentage = 0; } stats.AverageGainOnTransaction = new AverageTransactionResult(gain, successes); stats.AverageLossOnTransaction = new AverageTransactionResult(loss, losses); resultDto.TransactionStatistics = stats; }
/// <inheritdoc /> public async Task <SimulationResultDto> RunSimulation(SimulationDto simulationDto) { var simulationResult = new SimulationResultDto { TransactionsLog = new List <SimulationTransactionDto>(), CurrentCompanyQuantity = new Dictionary <int, int>(), StartBudget = simulationDto.Budget, }; var strategy = await _strategyService.GetStrategy(simulationDto.UserId, simulationDto.SelectedStrategyId); if (simulationDto.SelectedCompanyIds == null || !simulationDto.SelectedCompanyIds.Any()) { throw new BusinessException("No companies were specified"); } var signalEvents = await _indicatorsService.GetSignals(simulationDto.StartDate, simulationDto.EndDate, simulationDto.SelectedCompanyIds, strategy.Indicators, strategy.IsConjunctiveStrategy, strategy.SignalDaysPeriod ?? 1); var allPrices = await _priceService.GetPrices(simulationDto.SelectedCompanyIds); foreach (var signalEvent in signalEvents.OrderBy(item => item.Date)) { // Shall we buy and sell companies thesame day? if (signalEvent.CompaniesToSell.Count > 0) { HandleSellSignals(simulationDto, allPrices, signalEvent, simulationResult); } if (signalEvent.CompaniesToBuy.Count > 0) { HandleBuySignals(simulationDto, allPrices, signalEvent, simulationResult); } } var keepStrategyProfit = BuyAndKeepStrategyProfit(simulationDto, simulationResult, allPrices); simulationResult.SimulationTotalValue = simulationResult.CurrentCompanyQuantity.Sum(x => { var companyPricesDto = allPrices.FirstOrDefault(item => item.Company.Id == x.Key); var lastDayPrice = companyPricesDto?.Prices.Where(item => item.Date <= simulationDto.EndDate) .OrderByDescending(item => item.Date) .FirstOrDefault(); if (lastDayPrice != null) { return(x.Value * lastDayPrice.ClosePrice); } return(0); }) + simulationDto.Budget; simulationResult.PercentageProfit = Math.Round((double)((simulationResult.SimulationTotalValue - simulationResult.StartBudget) / simulationResult.StartBudget) * 100, 2); CalculateMinimalAndMaximalSimulationValue(simulationDto.StartDate, simulationDto.EndDate, allPrices, simulationDto.SelectedCompanyIds, simulationResult); CalculateAverageGainAndLossOnTransaction(simulationResult, simulationDto.SelectedCompanyIds); simulationResult.KeepStrategyProfit = keepStrategyProfit; simulationResult.CurrentCompanies = new List <OwnedCompanyStocksDto>(); var companies = await _companyService.GetCompanies(simulationDto.SelectedCompanyIds); foreach (var i in simulationResult.CurrentCompanyQuantity) { simulationResult.CurrentCompanies.Add(new OwnedCompanyStocksDto { CompanyId = i.Key, CompanyName = companies.FirstOrDefault(x => x.Id == i.Key)?.Code, OwnedStocksCount = i.Value, CurrentPrice = allPrices.First(item => item.Company.Id == i.Key).Prices.Where(item => item.Date <= simulationDto.EndDate).OrderByDescending(item => item.Date).First().ClosePrice, CurrentValue = allPrices.First(item => item.Company.Id == i.Key).Prices.Where(item => item.Date <= simulationDto.EndDate).OrderByDescending(item => item.Date).First().ClosePrice *i.Value }); } simulationResult.CurrentCompanies.Add(new OwnedCompanyStocksDto { CompanyId = 0, CompanyName = "Free Budget", OwnedStocksCount = 0, CurrentPrice = 0, CurrentValue = simulationDto.Budget }); CalculateProfit(simulationResult); return(simulationResult); }
private static void CalculateMinimalAndMaximalSimulationValue(DateTime startDate, DateTime endDate, IList <CompanyPricesDto> prices, IList <int> companyIds, SimulationResultDto resultDto) { var dateBudgetDictionary = new Dictionary <DateTime, decimal>(); Dictionary <int, int> quantities = companyIds.ToDictionary(companyId => companyId, companyId => 0); decimal budget = resultDto.StartBudget; decimal minVal = resultDto.StartBudget; decimal maxVal = resultDto.StartBudget; resultDto.MaximalSimulationValue = new ExtremeSimulationValue(startDate, budget, budget); resultDto.MinimalSimulationValue = new ExtremeSimulationValue(startDate, budget, budget); for (DateTime d = startDate; d <= endDate; d = d.AddDays(1)) { foreach (var trans in resultDto.TransactionsLog.Where(trans => trans.Date == d)) { UpdateQuantities(quantities, trans); budget = trans.BudgetAfter; } var dailyPrices = ConvertPrices(prices, companyIds, d); if (dailyPrices.Count == 0) { continue; } decimal value = budget + dailyPrices.Sum(dailyPrice => dailyPrice.Value * quantities[dailyPrice.Key]); if (value > maxVal) { resultDto.MaximalSimulationValue = new ExtremeSimulationValue(d, value, resultDto.StartBudget); maxVal = value; } if (value < minVal) { resultDto.MinimalSimulationValue = new ExtremeSimulationValue(d, value, resultDto.StartBudget); minVal = value; } dateBudgetDictionary.Add(d, value); } resultDto.BudgetHistory = dateBudgetDictionary; }
private async Task <SimulationResultViewModel> BuildSimulationResultViewModel(SimulationViewModel model, SimulationResultDto ret) { var ids = ret.CurrentCompanyQuantity.Keys.ToList(); ids.AddRange(ret.TransactionsLog.Select(item => item.CompanyId)); var companies = await _companyService.GetCompanies(ids); return(new SimulationResultViewModel { CurrentCompanyQuantity = ret.CurrentCompanies, Chart = new LineChartModel { Name = "Budget", CompanyId = 0, Data = ret.BudgetHistory.Select(item => new[] { item.Key.ToJavaScriptTimeStamp(), item.Value }).ToList() }, TransactionsLog = ret.TransactionsLog.Select(item => new SimulationTransaction { Date = item.Date, Price = item.Price, Action = item.Action, BudgetAfter = item.BudgetAfter, Quantity = item.Quantity, Company = companies.FirstOrDefault(x => x.Id == item.CompanyId), Profit = item.Profit }).ToList(), StartBudget = model.Budget, TotalSimulationValue = ret.SimulationTotalValue, PercentageProfit = ret.PercentageProfit, AverageLossOnTransaction = ret.TransactionStatistics.AverageLossOnTransaction, AverageGainOnTransaction = ret.TransactionStatistics.AverageGainOnTransaction, MaximalSimulationValue = ret.MaximalSimulationValue, MinimalSimulationValue = ret.MinimalSimulationValue, SuccessTransactionPercentage = ret.TransactionStatistics.SuccessTransactionPercentage, FailedTransactionPercentage = ret.TransactionStatistics.FailedTransactionPercentage, KeepStrategyProfit = (double)ret.KeepStrategyProfit }); }