private void Init(RebalanceInput input)
        {
            m_input = input ?? throw new ArgumentNullException(nameof(input));
            if (m_input.MaxOrderCount < 1 || m_input.MaxOrderCount > input.Portfolio.Count)
            {
                throw new ArgumentOutOfRangeException(nameof(input.MaxOrderCount));
            }

            m_maxDepth = m_input.MaxOrderCount - 1;
            m_result   = new RebalanceResult();
        }
        public RebalanceResult Rebalance(RebalanceInput input)
        {
            var            bestPercentage = decimal.MaxValue;
            List <ETFItem> bestPortfolio  = null;

            // When the max order amount is minimized, we get all possible item
            // combinations to try out
            var combinations = input.Portfolio.GetKCombs(input.MaxOrderCount);

            var mutex = new object();

            // for each possible combination...
            combinations.AsParallel().ForAll(c =>
            {
                // Simulating investment with this combination
                var newPortfolio = SimulateInvestment(input, c);

                // Calculating final percentage for this combination
                var newPercentage = CalculateCurrentAllocation(newPortfolio);
                lock (mutex)
                {
                    // if combination ends up better than the previous combinations, remember it
                    if (newPercentage < bestPercentage)
                    {
                        bestPercentage = newPercentage;
                        bestPortfolio  = newPortfolio;
                    }
                }
            });

            // Calculation is done, creating the orders and returning the final result
            List <Order> order = new List <Order>();

            foreach (var item in input.Portfolio)
            {
                var itemAfter = bestPortfolio.First(p => p.ISIN == item.ISIN);
                if (itemAfter.CurrentlyOwned > item.CurrentlyOwned)
                {
                    order.Add(new Order {
                        ISIN = itemAfter.ISIN, CurrentPrice = itemAfter.CurrentPrice, OrderAmount = (short)(itemAfter.CurrentlyOwned - item.CurrentlyOwned)
                    });
                }
            }

            var result = new RebalanceResult();

            result.Percentage = bestPercentage;
            result.Orders     = order;

            return(result);
        }