Exemple #1
0
        public void Redistribute_TotalWeightLessThanOne_RedistributedWeightIsAprroxOne()
        {
            var weights = new StockWeights
            {
                { "TLV", 0.1m }, { "FP", 0.2m }, { "EL", 0.3m }
            };

            weights = weights.Redistribute();

            Assert.IsTrue(weights.Sum(w => w.Value).IsApproxOne());
        }
Exemple #2
0
        public void Redistribute_TotalWeightAlreadyOne_WeightsRemainUnchanged()
        {
            var weights = new StockWeights
            {
                { "TLV", 0.3m }, { "FP", 0.6m }, { "EL", 0.1m }
            };

            weights = weights.Redistribute();

            Assert.AreEqual(weights["TLV"], 0.3);
            Assert.AreEqual(weights["FP"], 0.6);
            Assert.AreEqual(weights["EL"], 0.1);
        }
        public void AdjustWeights_EmptyPortfolio_ToBuyWeightsCorrectlyCalculated()
        {
            var currentWeights = new StockWeights();

            var targetWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.3m }, { "EL", 0.5m }
            };

            var strategy     = new FollowTargetAdjustmentStrategy();
            var toBuyWeights = strategy.AdjustWeights(currentWeights, targetWeights, toBuyInverseRatio: 0);

            Assert.AreEqual(0.2m, toBuyWeights["TLV"]);
            Assert.AreEqual(0.3m, toBuyWeights["FP"]);
            Assert.AreEqual(0.5m, toBuyWeights["EL"]);
        }
        public void AdjustWeights_CurrentWeightHigherThanTargetWeight_WouldNormallyResultInNegativeWeight_ToBuyWeightIsRemoved()
        {
            var currentWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.2m }, { "EL", 0.6m }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.1m }, { "FP", 0.3m }, { "EL", 0.5m }, { "SNG", 0.1m }
            };

            var strategy     = new FollowTargetAdjustmentStrategy();
            var toBuyWeights = strategy.AdjustWeights(currentWeights, targetWeights, toBuyInverseRatio: 2);

            Assert.IsFalse(toBuyWeights.ContainsKey("TLV"));
        }
        public void AdjustWeights_InverseToBuyRatioVeryHigh_ToBuyWeightsSumEqualsOneIsCorrecltyEnforced()
        {
            var currentWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.2m }, { "EL", 0.6m }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.3m }, { "EL", 0.5m }
            };

            var strategy     = new FollowTargetAdjustmentStrategy();
            var toBuyWeights = strategy.AdjustWeights(currentWeights, targetWeights, toBuyInverseRatio: 10);

            Assert.IsTrue(toBuyWeights.Sum(w => w.Value).IsApproxOne());
        }
Exemple #6
0
        public void AdjustWeights_WeightsBellowMinimalWeightAreCutOff_CutOffWeightIsRedistributed()
        {
            var currentWeights = new StockWeights
            {
                { "TLV", 0.08m }, { "SNG", 0.09m }, { "FP", 0.22m }, { "EL", 0.61m }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.08m }, { "SNG", 0.09m }, { "FP", 0.22m }, { "EL", 0.61m }
            };

            var strategy     = new MinOrderValueCutOffStrategy(new FollowTargetAdjustmentStrategy(), 0.1m);
            var toBuyWeights = strategy.AdjustWeights(currentWeights, targetWeights, toBuyInverseRatio: 2);

            Assert.IsFalse(toBuyWeights.ContainsKey("TLV"));
            Assert.IsTrue(toBuyWeights.Sum(w => w.Value).IsApproxOne());
        }
Exemple #7
0
        public void BuildPortfolio_AtLeastOneTargetWeightAboveAboveOneHundredPercent_ThrowsArgumentException()
        {
            var prices = new StockPrices
            {
                { "TLV", 10 }, { "FP", 20 }, { "EL", 30 }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 1.2m }, { "FP", 0.3m }, { "EL", 0.5m }
            };

            var builder = new PortfolioBuilder()
                          .UsePrices(prices)
                          .UseTargetWeights(targetWeights)
                          .UseToBuyAmount(100);

            Assert.Throws <ArgumentException>(() => builder.Build());
        }
        public void AdjustWeights_InverseToBuyRatioIsOne_ToBuyWeightsCorrectlyCalculated()         // Simulates second buying sessions => inverseToBuyRatio = 1
        {
            var currentWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.2m }, { "EL", 0.6m }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.3m }, { "EL", 0.5m }
            };

            var strategy     = new FollowTargetAdjustmentStrategy();
            var toBuyWeights = strategy.AdjustWeights(currentWeights, targetWeights, toBuyInverseRatio: 1);

            Assert.AreEqual(0.2m, toBuyWeights["TLV"]);
            Assert.AreEqual(0.4m, toBuyWeights["FP"]);
            Assert.AreEqual(0.4m, toBuyWeights["EL"]);
        }
Exemple #9
0
        public void BuildPortfolio_SumOfTargetWeightApproxEqualWithOneHundredPercent_DoesNotThrow()
        {
            var prices = new StockPrices
            {
                { "TLV", 10 }, { "FP", 20 }, { "EL", 30 }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.21m }, { "FP", 0.3m }, { "EL", 0.5m }
            };

            var builder = new PortfolioBuilder()
                          .UsePrices(prices)
                          .UseTargetWeights(targetWeights)
                          .UseToBuyAmount(100);

            Assert.DoesNotThrow(() => builder.Build());
        }
Exemple #10
0
        public void BuildPortfolio_MissingPriceForTargetSymbol_ThrowsArgumentException()
        {
            var prices = new StockPrices
            {
                { "TLV", 10 }, { "FP", 20 }, { "EL", 30 }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.21m }, { "FP", 0.3m }
            };

            var builder = new PortfolioBuilder()
                          .UsePrices(prices)
                          .UseTargetWeights(targetWeights)
                          .UseToBuyAmount(100);

            Assert.Throws <ArgumentException>(() => builder.Build());
        }
Exemple #11
0
        public void BuildPortfolio_InitialStocks_EnoughAvailableAmount_NewStocksAreAdded()
        {
            var prices = new StockPrices
            {
                { "TLV", 10 }, { "FP", 20 }, { "EL", 30 }
            };

            var stocks = new[]
            {
                new Stock("TLV")
                {
                    Count = 2, Price = 10, Weight = 0.2m
                },                                                                         // should add 2
                new Stock("FP")
                {
                    Count = 1, Price = 20, Weight = 0.2m
                },                                                                        // should add 3
                new Stock("EL")
                {
                    Count = 2, Price = 30, Weight = 0.6m
                }                                                                        // should add 2
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.3m }, { "EL", 0.5m }
            };                                                               // target stock 4 * TLV + 4 * FP + 4 * EL = 240

            var portfolio = new PortfolioBuilder()
                            .UsePrices(prices)
                            .UseStocks(stocks)
                            .UseWeightAdjustmentStrategy(new FollowTargetAdjustmentStrategy())
                            .UseTargetWeights(targetWeights)
                            .UseToBuyAmount(140)
                            .Build();

            Assert.AreEqual(4, portfolio["EL"].Count);
            Assert.AreEqual(4, portfolio["FP"].Count);
            Assert.AreEqual(4, portfolio["TLV"].Count);

            Assert.AreEqual(240, portfolio.TotalValue);
        }
        public void AdjustWeights_TargetWeightsHasMoreSymbols_ToBuyWeightsCorrectlyCalculated()
        {
            var currentWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.2m }, { "EL", 0.6m }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.2m }, { "EL", 0.5m }, { "SNG", 0.1m }
            };

            var strategy     = new FollowTargetAdjustmentStrategy();
            var toBuyWeights = strategy.AdjustWeights(currentWeights, targetWeights, toBuyInverseRatio: 2);

            Assert.AreEqual(0.2m, toBuyWeights["TLV"]);
            Assert.AreEqual(0.2m, toBuyWeights["FP"]);
            Assert.AreEqual(0.3m, toBuyWeights["EL"]);
            Assert.AreEqual(0.3m, toBuyWeights["SNG"]);
        }
Exemple #13
0
        public void BuildPortfolio_NotEnoughForAny_PortfolioValueIsZero()
        {
            var prices = new StockPrices
            {
                { "TLV", 10 }, { "FP", 20 }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.5m }, { "FP", 0.5m }
            };

            var portfolio = new PortfolioBuilder()
                            .UsePrices(prices)
                            .UseTargetWeights(targetWeights)
                            .UseToBuyAmount(9)
                            .Build();

            Assert.Zero(portfolio.TotalValue);
        }
Exemple #14
0
        public void BuildPortfolio_NotEnoughForAll_PortfolioValueIsCorrect()
        {
            var prices = new StockPrices
            {
                { "TLV", 10 }, { "FP", 20 }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.5m }, { "FP", 0.5m }
            };

            var portfolio = new PortfolioBuilder()
                            .UsePrices(prices)
                            .UseTargetWeights(targetWeights)
                            .UseToBuyAmount(20)
                            .Build();

            Assert.AreEqual(10, portfolio.TotalValue);
        }
Exemple #15
0
        public StockWeights AdjustWeights(StockWeights currentWeights, StockWeights targetWeights, decimal toBuyInverseRatio)
        {
            // Adjust weights based on initial strategy
            var adjustedWeights = _innerStrategy.AdjustWeights(currentWeights, targetWeights, toBuyInverseRatio);

            // Cut off insufficient weights trying to maximize number of resulting weigthts
            targetWeights = CutOffAndRedistribute(adjustedWeights);

            return(targetWeights);

            StockWeights CutOffAndRedistribute(StockWeights weights)
            {
                weights = weights.Redistribute();
                if (weights.Any(w => w.Value <= _minWeight))
                {
                    weights = CutOffAndRedistribute(weights.Take(weights.Count - 1).AsStockWeights());
                }

                return(weights);
            }
        }
Exemple #16
0
        public void BuildPortfolio_MinOrderValueIsSet_StockTotalValueLessThanMinimal_StockIsNotAddedToPortfolio()
        {
            var prices = new StockPrices
            {
                { "TLV", 10 }, { "FP", 20 }, { "EL", 30 }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.3m }, { "EL", 0.5m }
            };

            var portfolio = new PortfolioBuilder()
                            .UsePrices(prices)
                            .UseTargetWeights(targetWeights)
                            .UseToBuyAmount(140)
                            .UseMinOrderValue(31)
                            .Build();

            Assert.IsFalse(portfolio.Any(s => s.Symbol == "TLV"));
        }
Exemple #17
0
        public StockWeights AdjustWeights(StockWeights currentWeights, StockWeights targetWeights, decimal toBuyInverseRatio)
        {
            // Starting from: cn * C + bn * B = tn * T
            // , where cn - weight for stock 'n', C - total current stock value
            // , bn - to buy weight for stock 'n', B - to buy available amount
            // , tn - target weight for stock 'n', T - total final stock value
            // We get: bn = tn * (C / B + 1) - cn * (C / B), where C / B is the to buy inverse ratio
            // With first constraint that bn >= 0 which results in: cn <= tn * (1 + B / C)
            // With second constraint that b1 + b2 + ... + bn = 1

            // Apply the first constraint and remove the target weights that don't fulfill it
            var allowedWeights = targetWeights
                                 .Where(w => toBuyInverseRatio == 0 || // 0 only on the first buy
                                        SafeGetCurrentWeight(w.Key) <= w.Value * (1 + 1 / toBuyInverseRatio))
                                 .OrderByDescending(w => w.Value)
                                 .AsStockWeights();

            // Redistribute the removed weights to the other weights
            allowedWeights = allowedWeights.Redistribute();

            var toBuyWeights = allowedWeights
                               .Select(w => (w.Key, w.Value * (toBuyInverseRatio + 1) - SafeGetCurrentWeight(w.Key) * toBuyInverseRatio))
                               .AsStockWeights();

            // Enforce the second constraint
            return(toBuyWeights.Redistribute());

            decimal SafeGetCurrentWeight(string symbol)
            {
                if (currentWeights.ContainsKey(symbol))
                {
                    return(currentWeights[symbol]);
                }

                return(0);
            }
        }
Exemple #18
0
        public void BuildPortfolio_AlmostEnoughForAll_SmallestEligibleStockIsDecreased()
        {
            var prices = new StockPrices
            {
                { "TLV", 10 }, { "FP", 20 }, { "EL", 30 }
            };

            var targetWeights = new StockWeights
            {
                { "TLV", 0.2m }, { "FP", 0.3m }, { "EL", 0.5m }
            };

            var portfolio = new PortfolioBuilder()
                            .UsePrices(prices)
                            .UseTargetWeights(targetWeights)
                            .UseToBuyAmount(119)
                            .Build();

            Assert.AreEqual(2, portfolio["EL"].Count);
            Assert.AreEqual(2, portfolio["FP"].Count);
            Assert.AreEqual(1, portfolio["TLV"].Count);

            Assert.AreEqual(110, portfolio.TotalValue);
        }
Exemple #19
0
 public StockWeights AdjustWeights(StockWeights currentWeights, StockWeights targetWeights, decimal toBuyInverseRatio)
 {
     return(targetWeights);
 }
Exemple #20
0
        /// <summary>
        /// Equaly redistributes weights such that their sum is 1.
        /// </summary>
        /// <remarks>
        /// This applies for both cases, weights over 1 or less than 1.
        /// </remarks>
        public static StockWeights Redistribute(this StockWeights weights)
        {
            var totalWeight = weights.Sum(w => w.Value);             // this needs to become 1 after redistribution

            return(weights.Select(w => (w.Key, w.Value / totalWeight)).AsStockWeights());
        }