Skip to content

Commit

Permalink
Invest in VSMAX for exposure to small cap
Browse files Browse the repository at this point in the history
The funds that I've chosen to capture the whole of the US stock markets
are heavily weighted towards large cap. My portfolio is meant to be
long-term retirement investments. Accordingly, I can increase the amount
of holdings in small cap. I'll be doing this through VSMAX.
  • Loading branch information
DavidCain committed Dec 27, 2021
1 parent 630170c commit abec12a
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 23 deletions.
9 changes: 5 additions & 4 deletions data/classified.csv
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
ticker_name,asset_class,name
VTSAX,USStocks,"Vanguard Total Stock Market Index Fund Admiral Shares"
VFIAX,USStocks,"Vanguard 500 Index Fund Admiral Shares"
FSKAX,USStocks,"Fidelity Total Market Index Fund"
FZROX,USStocks,"Fidelity ZERO Total Market Index Fund"
VTSAX,USTotal,"Vanguard Total Stock Market Index Fund Admiral Shares"
VFIAX,USTotal,"Vanguard 500 Index Fund Admiral Shares"
FSKAX,USTotal,"Fidelity Total Market Index Fund"
FZROX,USTotal,"Fidelity ZERO Total Market Index Fund"
VSMAX,USSmall,"Vanguard Small-Cap Index Fund Admiral Shares"
FZILX,IntlStocks,"Fidelity ZERO International Index Fund"
VTIAX,IntlStocks,"Vanguard Total International Stock Index Fund Admiral Shares"
VBTLX,USBonds,"Vanguard Total Bond Market Index Fund Admiral Shares"
Expand Down
40 changes: 34 additions & 6 deletions src/allocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,27 @@ pub fn bond_allocation(birthday: NaiveDate, from_years: u8) -> Decimal {
/// - 10% to REIT
///
/// [core-four]: https://www.bogleheads.org/wiki/Lazy_portfolios#Core_four_portfolios
///
/// I add a simple modification, which is to not exceed 50% of my US stocks in large cap.
/// Some of my largest funds are heavily weighted towards large cap:
/// - VTSAX (~72% large cap):
/// - 41% Giant Cap
/// - 31% Large Cap
/// - 19% Mid Cap
/// - 6% Small Cap
/// - 2% Micro Cap
/// - VFIAX (~84% large cap)
/// - 50% Giant Cap
/// - 34% Largo Cap
/// - 16% Mid Cap
/// - FZILX (~84% large cap)
/// - 49% Giant Cap
/// - 36% Largo Cap
/// - 15% Mid Cap
/// For simplicity, just say that any US Total Market fund is 75% large cap.
/// Accordingly, for a 50/50 split of small+mid vs. large+giant,
/// I want $1 in VSMAX for every $2 in VTSAX.
///
pub fn core_four(ratio_bonds: Decimal) -> Vec<AssetAllocation> {
let one: Decimal = 1.into();

Expand All @@ -63,8 +84,9 @@ pub fn core_four(ratio_bonds: Decimal) -> Vec<AssetAllocation> {
vec![
// Allocate ratio of bonds
AssetAllocation::new(AssetClass::USBonds, ratio_bonds),
// Split remaining funds between US, International, and REIT (50%, 40%, and 10%)
AssetAllocation::new(AssetClass::USStocks, Decimal::new(50, 2) * ratio_stocks),
// Split remaining funds between US Total, US Small/Mid, International, and REIT
AssetAllocation::new(AssetClass::USTotal, Decimal::new(33, 2) * ratio_stocks),
AssetAllocation::new(AssetClass::USSmall, Decimal::new(17, 2) * ratio_stocks),
AssetAllocation::new(AssetClass::IntlStocks, Decimal::new(40, 2) * ratio_stocks),
AssetAllocation::new(AssetClass::REIT, Decimal::new(10, 2) * ratio_stocks),
]
Expand Down Expand Up @@ -112,30 +134,36 @@ mod tests {
core_four(0.into()),
vec![
AssetAllocation::new(AssetClass::USBonds, 0.into()),
AssetAllocation::new(AssetClass::USStocks, Decimal::new(50, 2)),
AssetAllocation::new(AssetClass::USTotal, Decimal::new(33, 2)),
AssetAllocation::new(AssetClass::USSmall, Decimal::new(17, 2)),
AssetAllocation::new(AssetClass::IntlStocks, Decimal::new(40, 2)),
AssetAllocation::new(AssetClass::REIT, Decimal::new(10, 2)),
]
);
}

#[test]
fn test_core_four() {
fn test_core_four_young() {
assert_eq!(
core_four(Decimal::new(20, 2)),
vec![
AssetAllocation::new(AssetClass::USBonds, Decimal::new(20, 2)),
AssetAllocation::new(AssetClass::USStocks, Decimal::new(40, 2)),
AssetAllocation::new(AssetClass::USTotal, Decimal::new(264, 3)),
AssetAllocation::new(AssetClass::USSmall, Decimal::new(136, 3)),
AssetAllocation::new(AssetClass::IntlStocks, Decimal::new(32, 2)),
AssetAllocation::new(AssetClass::REIT, Decimal::new(8, 2)),
]
);
}

#[test]
fn test_core_four_middle_aged() {
assert_eq!(
core_four(Decimal::new(60, 2)),
vec![
AssetAllocation::new(AssetClass::USBonds, Decimal::new(60, 2)),
AssetAllocation::new(AssetClass::USStocks, Decimal::new(20, 2)),
AssetAllocation::new(AssetClass::USTotal, Decimal::new(132, 3)),
AssetAllocation::new(AssetClass::USSmall, Decimal::new(68, 3)),
AssetAllocation::new(AssetClass::IntlStocks, Decimal::new(16, 2)),
AssetAllocation::new(AssetClass::REIT, Decimal::new(4, 2)),
]
Expand Down
16 changes: 9 additions & 7 deletions src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ impl fmt::Display for Asset {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AssetClass {
USBonds,
USStocks,
USTotal,
USSmall,
IntlBonds,
IntlStocks,
REIT,
Expand All @@ -124,7 +125,8 @@ impl fmt::Display for AssetClass {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match self {
AssetClass::USBonds => "US bonds",
AssetClass::USStocks => "US stocks",
AssetClass::USTotal => "US total market",
AssetClass::USSmall => "US small + mid cap",
AssetClass::IntlBonds => "International bonds",
AssetClass::IntlStocks => "International stocks",
AssetClass::REIT => "REIT",
Expand Down Expand Up @@ -199,16 +201,16 @@ mod tests {

#[test]
fn test_serializing_from_csv() {
let data = "ticker_name,asset_class\nVTSAX,USStocks\nVFIAX,USStocks";
let data = "ticker_name,asset_class\nVTSAX,USTotal\nVFIAX,USTotal";
let rdr = csv::Reader::from_reader(data.as_bytes());
let ac = AssetClassifications::from_reader(rdr).unwrap();
assert_eq!(
ac.classify("VTSAX").unwrap().to_owned(),
AssetClass::USStocks
AssetClass::USTotal
);
assert_eq!(
ac.classify("VFIAX").unwrap().to_owned(),
AssetClass::USStocks
AssetClass::USTotal
);
assert_eq!(
ac.classify("ABCDE"),
Expand All @@ -224,7 +226,7 @@ mod tests {
let data = "\
ticker_name,asset_class
AAAAA,USBonds
BBBBB,USStocks
BBBBB,USTotal
CCCCC,IntlBonds
DDDDD,IntlStocks
EEEEE,REIT
Expand All @@ -245,7 +247,7 @@ GGGGG,Cash";
String::from("Private Company"),
None,
5196.into(),
AssetClass::USStocks,
AssetClass::USTotal,
Some(Decimal::from(400)),
Some(Decimal::new(1299, 2)),
None,
Expand Down
12 changes: 6 additions & 6 deletions src/rebalance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ mod tests {
#[test]
#[should_panic(expected = "Asset types must match")]
fn test_asset_types_must_match() {
let mut stocks = AssetAllocation::new(AssetClass::USStocks, 1.into());
let mut stocks = AssetAllocation::new(AssetClass::USTotal, 1.into());

stocks.add_asset(Asset::new(
String::from("Vanguard Total Intl Bd Idx Admiral"),
Expand All @@ -346,14 +346,14 @@ mod tests {

#[test]
fn test_current_value_is_summed_assets() {
let mut stocks = AssetAllocation::new(AssetClass::USStocks, 1.into());
let mut stocks = AssetAllocation::new(AssetClass::USTotal, 1.into());
assert_eq!(stocks.current_value(), 0.into());

stocks.add_asset(Asset::new(
String::from("Vanguard Total Stock Market Index Fund Admiral Shares"),
Some(String::from("VTSAX")),
8675.into(),
AssetClass::USStocks,
AssetClass::USTotal,
None,
None,
None,
Expand All @@ -365,7 +365,7 @@ mod tests {
String::from("Fidelity ZERO Total Market Index Fund"),
Some(String::from("FZROX")),
10000.into(),
AssetClass::USStocks,
AssetClass::USTotal,
None,
None,
None,
Expand Down Expand Up @@ -403,7 +403,7 @@ mod tests {
#[should_panic(expected = "Cannot rebalance unless total is 100%")]
fn test_allocations_do_not_sum() {
let does_not_sum = vec![
AssetAllocation::new(AssetClass::USStocks, Decimal::new(3, 1)),
AssetAllocation::new(AssetClass::USTotal, Decimal::new(3, 1)),
AssetAllocation::new(AssetClass::USBonds, Decimal::new(3, 1)),
];
let portfolio = Portfolio::new(does_not_sum);
Expand All @@ -413,7 +413,7 @@ mod tests {

#[test]
fn test_should_sort_by_current_allocation_value() {
let mut stocks = AssetAllocation::new(AssetClass::USStocks, Decimal::new(50, 2));
let mut stocks = AssetAllocation::new(AssetClass::USTotal, Decimal::new(50, 2));
let mut bonds = AssetAllocation::new(AssetClass::USBonds, Decimal::new(50, 2));

// We keep $10 in bonds, but plan to contribute nearly $1 million in stocks
Expand Down

0 comments on commit abec12a

Please sign in to comment.