private List <ETFItem> SimulateInvestment(RebalanceInput input, IEnumerable <ETFItem> itemsToBuy)
        {
            var clonedItemsToBuy = itemsToBuy.OrderBy(p => p.Variance).ToList().Clone();
            var clonedPortfolio  = input.Portfolio.Clone();
            var moneyLeft        = input.MoneyToSpent;

            // Investing money until nothing else can be bought anymore
            while (clonedItemsToBuy.Any(p => p.CurrentPrice <= moneyLeft))
            {
                ETFItem bestETF        = null;
                decimal bestPercentage = decimal.MaxValue;
                // foreach item in the combination...
                foreach (var itemToBuy in clonedItemsToBuy)
                {
                    if (itemToBuy.CurrentPrice > moneyLeft)
                    {
                        continue;
                    }

                    // calculate "before" percentage
                    var beforePercentage = CalculateCurrentAllocation(clonedPortfolio);

                    var etf = clonedPortfolio.First(p => p.ISIN == itemToBuy.ISIN);
                    etf.CurrentlyOwned++;

                    // calculate "after" percentage
                    decimal percentage = CalculateCurrentAllocation(clonedPortfolio);

                    // here comes the "magic"
                    if (percentage < beforePercentage && etf.Variance < 0)
                    {
                        // this is the best investment for now, lets get out of here right now
                        bestETF        = etf;
                        bestPercentage = percentage;
                        etf.CurrentlyOwned--;
                        break;
                    }
                    else if (bestETF == null || percentage < bestPercentage)
                    {
                        // this could be the best investment, but let's see if other investments are even better
                        bestETF        = etf;
                        bestPercentage = percentage;
                        etf.CurrentlyOwned--;
                    }
                    else
                    {
                        // this is a bad investment, do a rollback and forget about it
                        etf.CurrentlyOwned--;
                    }
                }

                bestETF.CurrentlyOwned++;
                moneyLeft -= bestETF.CurrentPrice;
            }

            // the optimal future portfolio
            return(clonedPortfolio.ToList());
        }
        public RebalanceResult Rebalance(RebalanceInput input)
        {
            Init(input);

            var itemsToBuy = CalculateItemsToBuy();
            var dict       = m_input.Portfolio.ToDictionary(p => p.ISIN);

            CalculateRecursive(dict, itemsToBuy, m_input.MoneyToSpent, m_input.MoneyToSpent, 0);
            return(m_result);
        }
        private List <ETFItem> CalculateItemsToBuy(RebalanceInput input)
        {
            // Calculate the current percentages for each item in our portfolio
            CalculateCurrentAllocation(input.Portfolio);

            // we would like to invest in the worst maxOrderCount of the items
            return(input.Portfolio
                   .OrderBy(p => p.Variance)
                   .Take(input.MaxOrderCount)
                   .ToList());
        }
        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);
        }
Exemplo n.º 6
0
        public void GivesBackACorrectResult()
        {
            var portfolio = new List <ETFItem>
            {
                new ETFItem("ISIN_1", 0, 10.0m, 0.2m),
                new ETFItem("ISIN_2", 0, 20.0m, 0.8m),
            };

            var input = new RebalanceInput(1000, 2, portfolio);

            var algo   = new FastButIntelligentAlgo();
            var result = algo.Rebalance(input);

            Assert.AreEqual(0.0m, result.Percentage);
            Assert.AreEqual(2, result.Orders.Count);
            Assert.AreEqual(20, result.Orders[0].OrderAmount);
            Assert.AreEqual(40, result.Orders[1].OrderAmount);
        }