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;
        }
예제 #8
0
        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
            });
        }