/// <summary>
        /// Calculates the savings plan for the given <paramref name="tag"/>.
        /// </summary>
        /// <param name="tag">The tag.</param>
        public ISavingsPlan CalculateSavingsPlan(string tag)
        {
            var savingsPlan = new SavingsPlan {
                Tag = tag, Date = DateTime.Now
            };

            var yearQuery = new TransactionYearsQuery()
                            .Register(new TransactionStartDateFilter(DateTime.MinValue))
                            .Register(new TransactionEndDateFilter(_dateCalculationService.GetEndOfToDay()))
                            .Register(new TransactionTagFilter(tag))
                            .Register(new TransactionDividendFilter(true));

            var years = _queryDispatcher.Execute(yearQuery).ToList();

            foreach (var year in years)
            {
                var period = new SavingsPlanPeriod()
                {
                    Year = year.ToString()
                };

                //Time period
                var start       = _dateCalculationService.GetStartAndEndDateOfYear(new DateTime(year, 1, 1), out DateTime end);
                var lastYearEnd = _dateCalculationService.GetEndDateOfYear(new DateTime(years[0] - 1, 1, 1));

                //Only this year
                var actualPeriodResult = CalculateCurrentPeriod(start, end, tag);

                period.PerformanceActualPeriodPercentage = actualPeriodResult.PerformanceActualPeriodPercentage;
                period.SumDividends = actualPeriodResult.SumDividends;

                //Accumulated till this end of year
                var periodResult = CalculateOverallPeriod(lastYearEnd, end, tag);

                period.SumOrderCostsPercentage = periodResult.SumOrderCostsPercentage;
                period.SumOrderCosts           = periodResult.SumOrderCosts;
                period.SumInpayment            = periodResult.SumInpayment;
                period.SumCapital = periodResult.SumCapital;
                period.PerformanceOverallPeriodPercentage = periodResult.PerformanceOverallPeriodPercentage;
                period.SumOverallDividends           = periodResult.SumOverallDividends;
                period.SumOverallDividendsPercentage = decimal.Round((periodResult.SumOverallDividends / periodResult.SumInpayment) * 100, 2);
                period.IsCurrentYear = DateTime.Now.Year == year;
                period.IsForecast    = false;

                savingsPlan.Periods.Add(period);
            }

            //Forecast
            if (savingsPlan.Periods.Any())
            {
                var startYear             = years.Max() + 1;
                var amountOfForecastYears = years.Min() + 35 - startYear;
                foreach (ISavingsPlanPeriod forecast in GetTagPeriodForecast(amountOfForecastYears, startYear, savingsPlan.Periods))
                {
                    savingsPlan.Periods.Add(forecast);
                }
            }

            return(savingsPlan);
        }
        /// <summary>
        /// Calculate current saving for the given period
        /// </summary>
        /// <param name="start">The start.</param>
        /// <param name="end">The end.</param>
        /// <param name="tag">The tag.</param>
        /// <returns></returns>
        internal ISavingsPlanPeriodCurrent CalculateCurrentPeriod(DateTime start, DateTime end, string tag)
        {
            var query = new TransactionAllQuery()
                        .Register(new TransactionStartDateFilter(start))
                        .Register(new TransactionEndDateFilter(end))
                        .Register(new TransactionTagFilter(tag));

            var period = new SavingsPlanPeriod
            {
                SumDividends = _transactionCalculationService.CalculateSumDividends(query),
                PerformanceActualPeriodPercentage =
                    _transactionCalculationService.CalculatePerformancePercentageForPeriod(
                        new TransactionAllQuery().Register(new TransactionTagFilter(tag)), start, end)
            };

            return(period);
        }
        /// <summary>
        /// Caculate overall savings for the given period
        /// </summary>
        /// <param name="start">The start.</param>
        /// <param name="end">The end.</param>
        /// <param name="tag">The tag.</param>
        /// <returns></returns>
        internal ISavingsPlanPeriodOverall CalculateOverallPeriod(DateTime start, DateTime end, string tag)
        {
            var query = new TransactionAllQuery()
                        .Register(new TransactionStartDateFilter(start))
                        .Register(new TransactionEndDateFilter(end))
                        .Register(new TransactionTagFilter(tag));

            var transactions = _queryDispatcher.Execute(query).ToList();

            var period = new SavingsPlanPeriod {
                IsForecast = false
            };

            if (!transactions.Any())
            {
                return(period);
            }

            //Inpayments
            period.SumInpayment = _transactionCalculationService.CalculateSumInpayments(query);

            //Order costs
            period.SumOrderCosts          -= transactions.Sum(t => t.OrderCosts);
            period.SumOrderCostsPercentage = decimal.Round((period.SumOrderCosts / (period.SumOrderCosts - period.SumInpayment) * 100), 2);

            //Sum capital
            period.SumCapital = _transactionCalculationService.CalculateSumCapital(query, end);

            //Dividends
            period.SumOverallDividends = _transactionCalculationService.CalculateSumDividends(query);

            //PerformancePercentage
            var cashFlowEnd = new CashFlow(period.SumCapital, end);

            period.PerformanceOverallPeriodPercentage = _transactionCalculationService.CalculatePerformancePercentageIir(query, new CashFlow(0, start), cashFlowEnd);

            return(period);
        }
        /// <summary>
        /// Calculates a forecast for the given years
        /// </summary>
        /// <param name="startYear">Year to start with</param>
        /// <param name="years">Amount of years</param>
        /// <param name="historical">Historical data</param>
        /// <returns>Forecast</returns>
        private IEnumerable <ISavingsPlanPeriod> GetTagPeriodForecast(int years, int startYear, IEnumerable <ISavingsPlanPeriod> historical)
        {
            if (historical == null)
            {
                return(Enumerable.Empty <ISavingsPlanPeriod>());
            }

            var periods        = new List <ISavingsPlanPeriod>();
            var historicalData = historical.OrderByDescending(t => t.Year).ToList();

            var tempForecast = new List <ISavingsPlanPeriod>();

            tempForecast.InsertRange(0, historicalData);

            var actualYear = startYear;

            while (actualYear <= startYear + years)
            {
                var lastYear = (actualYear - 1).ToString();
                var period   = new SavingsPlanPeriod()
                {
                    Year = actualYear.ToString()
                };

                decimal lastYearCapital;
                decimal lastYearInPayment;
                decimal lastYearOrderCosts;
                decimal lastYearSumOverallDividends;

                var lastYearCalculation = tempForecast.FirstOrDefault(t => t.Year == lastYear);

                if (lastYearCalculation.Year == DateTime.Now.Year.ToString())
                {
                    lastYearCapital             = lastYearCalculation.SumCapital + ((lastYearCalculation.SumCapital / (((tempForecast.Count - 1) * 12) + DateTime.Now.Month)) * (12 - DateTime.Now.Month));
                    lastYearInPayment           = lastYearCalculation.SumInpayment + ((lastYearCalculation.SumInpayment / (((tempForecast.Count - 1) * 12) + DateTime.Now.Month)) * (12 - DateTime.Now.Month));
                    lastYearOrderCosts          = lastYearCalculation.SumOrderCosts + ((lastYearCalculation.SumOrderCosts / (((tempForecast.Count - 1) * 12) + DateTime.Now.Month)) * (12 - DateTime.Now.Month));
                    lastYearSumOverallDividends = lastYearCalculation.SumOverallDividends + ((lastYearCalculation.SumOverallDividends / (((tempForecast.Count - 1) * 12) + DateTime.Now.Month)) * (12 - DateTime.Now.Month));
                }
                else
                {
                    lastYearCapital             = lastYearCalculation.SumCapital;
                    lastYearInPayment           = lastYearCalculation.SumInpayment;
                    lastYearOrderCosts          = lastYearCalculation.SumOrderCosts;
                    lastYearSumOverallDividends = lastYearCalculation.SumOverallDividends;
                }

                period.PerformanceOverallPeriodPercentage = historicalData.FirstOrDefault().PerformanceOverallPeriodPercentage;
                period.PerformanceActualPeriodPercentage  = period.PerformanceOverallPeriodPercentage;

                var converted            = period.PerformanceOverallPeriodPercentage / 100;
                var percentagePerfActual = converted < 0 ? 1 - (converted * -1) : 1 + converted;

                period.SumInpayment            = (lastYearInPayment / tempForecast.Count) + lastYearInPayment;
                period.SumCapital              = (lastYearCapital * percentagePerfActual) + ((lastYearInPayment / tempForecast.Count) * percentagePerfActual);
                period.SumOrderCosts           = (lastYearOrderCosts / tempForecast.Count) + lastYearOrderCosts;
                period.SumOrderCostsPercentage = decimal.Round((period.SumOrderCosts / (period.SumOrderCosts - period.SumInpayment) * 100), 2);

                period.SumOverallDividendsPercentage = historicalData.FirstOrDefault().SumOverallDividendsPercentage;
                period.SumOverallDividends           = decimal.Round((period.SumOverallDividendsPercentage / 100) * period.SumInpayment, 2) + lastYearSumOverallDividends;
                period.SumDividends = 0;

                period.IsForecast = true;

                periods.Add(period);
                tempForecast.Add(period);
                actualYear++;
            }

            return(periods);
        }