def test_rebalance_reinvest(self): portfolio = Portfolio({CCC: Decimal(2000), DDD: Decimal(8000)}) orders = portfolio.rebalance(self.model_portfolio) self.assertEqual(orders, [(SELL, CCC, Decimal(2000)), (SELL, DDD, Decimal(8000)), (BUY, AAA, Decimal(4000)), (BUY, BBB, Decimal(5000))])
def test_get_returns_cash_only(self): portfolio = Portfolio({CASH: Decimal(1000)}) act_dates, act_returns = portfolio.get_returns() self.assertEqual(len(act_dates), 365) self.assertEqual(len(act_returns), len(act_dates)) self.assertEqual(act_returns[0][0], 1000) self.assertEqual(act_returns[-1][0], 1000)
def test_rebalancing2(self): """ Test rebalancing algorithm. Part 2. Cash is not in the same currency as the assets. """ p = Portfolio() p.add_cash(200., "USD") p.add_cash(250., "GBP") tickers = ["VCN.TO", "XAW.TO", "ZAG.TO"] quantities = [5, 12, 20] p.easy_add_assets(tickers=tickers, quantities=quantities) target_asset_alloc = { "VCN.TO": 40.0, "ZAG.TO": 40.0, "XAW.TO": 20.0, } initial_value = p.value("CAD") p.selling_allowed = False (_, prices, _, _) = p.rebalance(target_asset_alloc, verbose=True) final_value = p.value("CAD") self.assertAlmostEqual(initial_value, final_value, -1) # The prices should be in the tickers' currency for ticker in tickers: self.assertEqual(prices[ticker][1], "CAD") # Since there was no CAD to start off with, # there should be none after rebalacing either # (i.e. amount converted to CAD should be the amount used to purchase CAD assets) self.assertAlmostEqual(p.cash["CAD"].amount, 0., 1)
class PortfolioPlottingTest(unittest.TestCase): def setUp(self): self.portfolio = Portfolio({CASH: Decimal(129), AAA: Decimal(4034)}) def test_plot(self): # mock matplotlib.pyplot pie chart fig, ax, plt = [MagicMock() for _ in range(3)] plt.subplots.return_value = (fig, ax) self.portfolio.plot(plt) # check arguments to ax.pie(...) args = ax.pie.call_args values = args[0][0] labels = args[1]['labels'] pos, allocs = self.portfolio.positions, self.portfolio.allocations self.assertEqual(values, [pos[CASH], pos[AAA]]) self.assertEqual(labels, [CASH, AAA]) # call autopct function for the pie wedge texts autopct = args[1]['autopct'] text1 = autopct(allocs[CASH]) text2 = autopct(allocs[AAA]) self.assertEqual(text1, '$129.00 (3.10%)') self.assertEqual(text2, '$4,034.00 (96.90%)')
def test_asset_allocation(self): """ Test asset allocation method. """ p = Portfolio() tickers = ["VCN.TO", "ZAG.TO", "XAW.TO", "TSLA"] quantities = [2, 20, 10, 4] p.easy_add_assets(tickers=tickers, quantities=quantities) asset_alloc = p.asset_allocation() self.assertAlmostEqual(sum(asset_alloc.values()), 100., 7) rates = CurrencyRates() prices = [ yf.Ticker(ticker).info["ask"] * rates.get_rate(yf.Ticker(ticker).info["currency"], "CAD") for ticker in tickers ] total = np.sum(np.asarray(quantities) * np.asarray(prices)) n = len(tickers) for i in range(n): self.assertAlmostEqual(asset_alloc[tickers[i]], quantities[i] * prices[i] / total * 100., 1)
def test_get_returns(self): portfolio = Portfolio({CASH: Decimal(1000), VFV: Decimal(1000)}) act_dates, act_returns = portfolio.get_returns() self.assertEqual(len(act_dates), 365) self.assertEqual(len(act_returns), len(act_dates)) shares = Decimal(1000) / Decimal('28.89') self.assertEqual(act_returns[0][0], 2000) # starting CASH + VFV self.assertEqual(act_returns[-1][0], shares * Decimal('30.69') + 1000)
def test_rebalance_balanced(self): portfolio = Portfolio({ CASH: Decimal(500), AAA: Decimal(2000), BBB: Decimal(2500) }) orders = portfolio.rebalance(self.model_portfolio) self.assertEqual(orders, [])
def test_get_returns_mismatched_dates(self): aaa_dates = np.array([[date(2016, 1, 1)], [date(2016, 1, 2)], [date(2016, 1, 3)]]) aaa_prices = np.array([[42], [43], [44]]) bbb_dates = np.array([[date(2016, 1, 2)], [date(2016, 1, 3)]]) bbb_prices = np.array([[57], [58]]) AAA = FixedPricesInstrument('AAA', aaa_dates, aaa_prices) BBB = FixedPricesInstrument('BBB', bbb_dates, bbb_prices) portfolio = Portfolio({AAA: Decimal(1000), BBB: Decimal(1000)}) act_dates, act_returns = portfolio.get_returns()
def test_portfolio_value(self): """ Test total market value, total cash value, and total value methods. """ p = Portfolio() tickers = ["VCN.TO", "ZAG.TO", "XAW.TO", "TSLA"] quantities = [2, 20, 10, 4] p.easy_add_assets(tickers=tickers, quantities=quantities) mv = p.market_value("CAD") total_mv = np.sum( [asset.market_value_in("CAD") for asset in p.assets.values()]) self.assertAlmostEqual(mv, total_mv, 1) amounts = [500.15, 200.00] currencies = ["CAD", "USD"] p.easy_add_cash(amounts, currencies) cv = p.cash_value("CAD") usd_to_cad = CurrencyRates().get_rate("USD", "CAD") total_cv = np.sum(amounts[0] + amounts[1] * usd_to_cad) self.assertAlmostEqual(cv, total_cv, 1) self.assertAlmostEqual(p.value("CAD"), total_mv + total_cv, 1)
def test_cash_interface2(self): """ Test portfolio's interface related to Cash class. Collectively adding cash to the portfolio. """ p = Portfolio() amounts = [500.15, 200.00] currencies = ["CAD", "USD"] p.easy_add_cash(amounts, currencies) self.assertEqual(p.cash[currencies[0]].amount, amounts[0]) self.assertEqual(p.cash[currencies[1]].amount, amounts[1])
def test_asset_interface2(self): """ Test portfolio's interface related to Asset class. Collectively adding assets to the portfolio. """ p = Portfolio() tickers = ["VCN.TO", "ZAG.TO"] quantities = [2, 20] p.easy_add_assets(tickers=tickers, quantities=quantities) n = len(tickers) for i in range(n): self.assertEqual(tickers[i], p.assets[tickers[i]].ticker) self.assertEqual(quantities[i], p.assets[tickers[i]].quantity) self.assertEqual( yf.Ticker(tickers[i]).info["ask"], p.assets[tickers[i]].price)
def test_asset_interface(self): """ Test portfolio's interface related to Asset class. Adding assets individually to the portfolio. """ p = Portfolio() ticker = "VCN.TO" quantity = 2 ticker_info = yf.Ticker(ticker).info price = ticker_info["ask"] asset = Asset(ticker=ticker, quantity=quantity) p.add_asset(asset) self.assertEqual(asset.ticker, p.assets[ticker].ticker) self.assertEqual(asset.quantity, p.assets[ticker].quantity) self.assertEqual(asset.price, p.assets[ticker].price) ticker = "ZAG.TO" quantity = 20 asset2 = Asset(ticker=ticker, quantity=quantity) p.add_asset(asset2) self.assertEqual(asset2.ticker, p.assets[ticker].ticker) self.assertEqual(asset2.quantity, p.assets[ticker].quantity) self.assertEqual(asset2.price, p.assets[ticker].price)
def test_cash_interface(self): """ Test portfolio's interface related to Cash class. Adding cash of different currency individually to the portfolio. """ p = Portfolio() amount1 = 500.15 p.add_cash(amount1, "cad") amount2 = 200.00 p.add_cash(amount2, "usd") self.assertEqual(p.cash["CAD"].amount, amount1) self.assertEqual(p.cash["USD"].amount, amount2)
def test_rebalance_unbalanced(self): portfolio = Portfolio({AAA: Decimal(2000), BBB: Decimal(8000)}) orders = portfolio.rebalance(self.model_portfolio) self.assertEqual(orders, [(SELL, BBB, Decimal(3000)), (BUY, AAA, Decimal(2000))])
def test_rebalance_cash(self): portfolio = Portfolio({CASH: Decimal(5000)}) orders = portfolio.rebalance(self.model_portfolio) self.assertEqual(orders, [(BUY, AAA, Decimal(2000)), (BUY, BBB, Decimal(2500))])
def setUp(self): self.model_portfolio = Portfolio({ CASH: Decimal(10), AAA: Decimal(40), BBB: Decimal(50) })
def setUp(self): self.portfolio = Portfolio({CASH: Decimal(1200), AAA: Decimal(300)})
def setUp(self): self.portfolio = Portfolio({CASH: Decimal(129), AAA: Decimal(4034)})
def test_rebalancing(self): """ Test rebalancing algorithm. This might break over time as prices increase. If we have enough cash though, the optimizer should ideally be able to match the target asset allocation pretty closely """ p = Portfolio() tickers = ["XBB.TO", "XIC.TO", "ITOT", "IEFA", "IEMG"] quantities = [36, 64, 32, 8, 7] p.easy_add_assets(tickers=tickers, quantities=quantities) p.add_cash(3000, "USD") p.add_cash(515.21, "CAD") p.add_cash(5.00, "GBP") p.selling_allowed = True self.assertTrue(p.selling_allowed) # different order than tickers. # rebalance method should be able to handle such a case target_asset_alloc = { "XBB.TO": 20, "XIC.TO": 20, "IEFA": 20, "ITOT": 36, "IEMG": 4 } initial_value = p.value("CAD") (_, _, _, max_diff) = p.rebalance(target_asset_alloc, verbose=True) final_value = p.value("CAD") self.assertAlmostEqual(initial_value, final_value, 1) self.assertLessEqual(max_diff, 2.) # Error handling with self.assertRaises(Exception): target_asset_alloc = { "XBB.TO": 20, "XIC.TO": 20, "IEFA": 20, } p.rebalance(target_asset_alloc)
def test_exchange(self): """ Test currency exchange in Portfolio. """ p = Portfolio() amounts = [500.15, 200.00] currencies = ["CAD", "USD"] p.easy_add_cash(amounts, currencies) cad_to_usd = CurrencyRates().get_rate("CAD", "USD") p.exchange_currency(to_currency="CAD", from_currency="USD", to_amount=100) self.assertAlmostEqual(p.cash["CAD"].amount, 500.15 + 100., 1) self.assertAlmostEqual(p.cash["USD"].amount, 200. - 100. * cad_to_usd, 1) p.exchange_currency(from_currency="USD", to_currency="CAD", from_amount=50) self.assertAlmostEqual(p.cash["CAD"].amount, 500.15 + 100 + 50 / cad_to_usd, 1) self.assertAlmostEqual(p.cash["USD"].amount, 200. - 100. * cad_to_usd - 50, 1) # error handling: with self.assertRaises(Exception): p.exchange_currency(to_currency="CAD", from_currency="USD", to_amount=100, from_amount=20) # error handling with self.assertRaises(Exception): p.exchange_currency(to_currency="CAD", from_currency="USD")
def setUp(self): self.model_portfolio = Portfolio({AAA: Decimal(10), BBB: Decimal(90)})
from rebalance import Portfolio # My portfolio p = Portfolio() # Cash in portfolio cash_amounts = [5000] cash_currency = ["CAD"] p.easy_add_cash(amounts=cash_amounts, currencies=cash_currency) # Assets in portfolio # The price will be retrieved automatically tickers = ["XIC.TO", "VCN.TO", "TSLA"] quantities = [20, 20, 10] p.easy_add_assets(tickers=tickers, quantities=quantities) # Target asset allocation (in %) target_asset_alloc = {"XIC.TO": 20, "VCN.TO": 30, "TSLA": 50} # rebalance p.selling_allowed = True # Don't allow selling while rebalancing p.rebalance(target_asset_alloc, verbose=True)
def make_portfolio(self, aaa, bbb): return Portfolio({AAA: Decimal(aaa), BBB: Decimal(bbb)})
from rebalance import Portfolio p = Portfolio() tickers = ["XBB.TO", "XIC.TO", "ITOT", "IEFA", "IEMG"] quantities = [36, 64, 32, 8, 7] p.easy_add_assets(tickers=tickers, quantities = quantities) p.add_cash(5000.00, "GBP") p.add_cash(1000.00, "EUR") target_asset_alloc = { "XBB.TO": 20, "XIC.TO": 20, "IEFA": 20, "ITOT": 36, "IEMG": 4 } initial_value = p.value("CAD") p.selling_allowed = False (_, _, exchange_rates, _) = p.rebalance(target_asset_alloc, verbose=True) final_value = p.value("CAD")