308 lines
10 KiB
Rust
308 lines
10 KiB
Rust
//! Sample asset data generation
|
|
|
|
use crate::{Asset, PortfolioPreset, Result};
|
|
|
|
/// Available sample presets
|
|
pub const SAMPLE_PRESETS: &[PortfolioPreset] = &[
|
|
PortfolioPreset::Conservative,
|
|
PortfolioPreset::Balanced,
|
|
PortfolioPreset::Aggressive,
|
|
PortfolioPreset::GlobalDiversified,
|
|
PortfolioPreset::TechFocus,
|
|
];
|
|
|
|
/// Generate sample assets for a given preset
|
|
pub fn generate_sample_assets(preset: PortfolioPreset) -> Result<Vec<Asset>> {
|
|
let assets = match preset {
|
|
PortfolioPreset::Conservative => conservative_portfolio(),
|
|
PortfolioPreset::Balanced => balanced_portfolio(),
|
|
PortfolioPreset::Aggressive => aggressive_portfolio(),
|
|
PortfolioPreset::GlobalDiversified => global_diversified_portfolio(),
|
|
PortfolioPreset::TechFocus => tech_focus_portfolio(),
|
|
};
|
|
Ok(assets)
|
|
}
|
|
|
|
fn conservative_portfolio() -> Vec<Asset> {
|
|
vec![
|
|
Asset {
|
|
ticker: "BND".to_string(),
|
|
name: "Vanguard Total Bond Market ETF".to_string(),
|
|
asset_class: "bond".to_string(),
|
|
sector: Some("Fixed Income".to_string()),
|
|
expected_return: 0.03,
|
|
volatility: 0.04,
|
|
},
|
|
Asset {
|
|
ticker: "TLT".to_string(),
|
|
name: "iShares 20+ Year Treasury Bond ETF".to_string(),
|
|
asset_class: "bond".to_string(),
|
|
sector: Some("Fixed Income".to_string()),
|
|
expected_return: 0.04,
|
|
volatility: 0.12,
|
|
},
|
|
Asset {
|
|
ticker: "VNQ".to_string(),
|
|
name: "Vanguard Real Estate ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Real Estate".to_string()),
|
|
expected_return: 0.06,
|
|
volatility: 0.18,
|
|
},
|
|
Asset {
|
|
ticker: "GLD".to_string(),
|
|
name: "SPDR Gold Trust".to_string(),
|
|
asset_class: "commodity".to_string(),
|
|
sector: Some("Precious Metals".to_string()),
|
|
expected_return: 0.04,
|
|
volatility: 0.15,
|
|
},
|
|
]
|
|
}
|
|
|
|
fn balanced_portfolio() -> Vec<Asset> {
|
|
vec![
|
|
Asset {
|
|
ticker: "SPY".to_string(),
|
|
name: "SPDR S&P 500 ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Broad Market".to_string()),
|
|
expected_return: 0.10,
|
|
volatility: 0.16,
|
|
},
|
|
Asset {
|
|
ticker: "BND".to_string(),
|
|
name: "Vanguard Total Bond Market ETF".to_string(),
|
|
asset_class: "bond".to_string(),
|
|
sector: Some("Fixed Income".to_string()),
|
|
expected_return: 0.03,
|
|
volatility: 0.04,
|
|
},
|
|
Asset {
|
|
ticker: "VNQ".to_string(),
|
|
name: "Vanguard Real Estate ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Real Estate".to_string()),
|
|
expected_return: 0.06,
|
|
volatility: 0.18,
|
|
},
|
|
Asset {
|
|
ticker: "GLD".to_string(),
|
|
name: "SPDR Gold Trust".to_string(),
|
|
asset_class: "commodity".to_string(),
|
|
sector: Some("Precious Metals".to_string()),
|
|
expected_return: 0.04,
|
|
volatility: 0.15,
|
|
},
|
|
Asset {
|
|
ticker: "VWO".to_string(),
|
|
name: "Vanguard FTSE Emerging Markets ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Emerging Markets".to_string()),
|
|
expected_return: 0.08,
|
|
volatility: 0.22,
|
|
},
|
|
]
|
|
}
|
|
|
|
fn aggressive_portfolio() -> Vec<Asset> {
|
|
vec![
|
|
Asset {
|
|
ticker: "SPY".to_string(),
|
|
name: "SPDR S&P 500 ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Broad Market".to_string()),
|
|
expected_return: 0.10,
|
|
volatility: 0.16,
|
|
},
|
|
Asset {
|
|
ticker: "QQQ".to_string(),
|
|
name: "Invesco QQQ Trust".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Technology".to_string()),
|
|
expected_return: 0.13,
|
|
volatility: 0.20,
|
|
},
|
|
Asset {
|
|
ticker: "VWO".to_string(),
|
|
name: "Vanguard FTSE Emerging Markets ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Emerging Markets".to_string()),
|
|
expected_return: 0.12,
|
|
volatility: 0.25,
|
|
},
|
|
Asset {
|
|
ticker: "IWM".to_string(),
|
|
name: "iShares Russell 2000 ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Small Cap".to_string()),
|
|
expected_return: 0.11,
|
|
volatility: 0.22,
|
|
},
|
|
]
|
|
}
|
|
|
|
fn global_diversified_portfolio() -> Vec<Asset> {
|
|
vec![
|
|
Asset {
|
|
ticker: "SPY".to_string(),
|
|
name: "SPDR S&P 500 ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("US Equity".to_string()),
|
|
expected_return: 0.10,
|
|
volatility: 0.16,
|
|
},
|
|
Asset {
|
|
ticker: "EFA".to_string(),
|
|
name: "iShares MSCI EAFE ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("International Equity".to_string()),
|
|
expected_return: 0.08,
|
|
volatility: 0.18,
|
|
},
|
|
Asset {
|
|
ticker: "VWO".to_string(),
|
|
name: "Vanguard FTSE Emerging Markets ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Emerging Markets".to_string()),
|
|
expected_return: 0.09,
|
|
volatility: 0.24,
|
|
},
|
|
Asset {
|
|
ticker: "BND".to_string(),
|
|
name: "Vanguard Total Bond Market ETF".to_string(),
|
|
asset_class: "bond".to_string(),
|
|
sector: Some("US Bonds".to_string()),
|
|
expected_return: 0.03,
|
|
volatility: 0.04,
|
|
},
|
|
Asset {
|
|
ticker: "BNDX".to_string(),
|
|
name: "Vanguard Total International Bond ETF".to_string(),
|
|
asset_class: "bond".to_string(),
|
|
sector: Some("International Bonds".to_string()),
|
|
expected_return: 0.02,
|
|
volatility: 0.06,
|
|
},
|
|
Asset {
|
|
ticker: "VNQ".to_string(),
|
|
name: "Vanguard Real Estate ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Real Estate".to_string()),
|
|
expected_return: 0.06,
|
|
volatility: 0.18,
|
|
},
|
|
Asset {
|
|
ticker: "GLD".to_string(),
|
|
name: "SPDR Gold Trust".to_string(),
|
|
asset_class: "commodity".to_string(),
|
|
sector: Some("Precious Metals".to_string()),
|
|
expected_return: 0.04,
|
|
volatility: 0.15,
|
|
},
|
|
]
|
|
}
|
|
|
|
fn tech_focus_portfolio() -> Vec<Asset> {
|
|
vec![
|
|
Asset {
|
|
ticker: "QQQ".to_string(),
|
|
name: "Invesco QQQ Trust".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Technology".to_string()),
|
|
expected_return: 0.14,
|
|
volatility: 0.22,
|
|
},
|
|
Asset {
|
|
ticker: "ARKK".to_string(),
|
|
name: "ARK Innovation ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Technology".to_string()),
|
|
expected_return: 0.15,
|
|
volatility: 0.35,
|
|
},
|
|
Asset {
|
|
ticker: "SOXX".to_string(),
|
|
name: "iShares Semiconductor ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Technology".to_string()),
|
|
expected_return: 0.16,
|
|
volatility: 0.28,
|
|
},
|
|
Asset {
|
|
ticker: "CLOU".to_string(),
|
|
name: "Global X Cloud Computing ETF".to_string(),
|
|
asset_class: "equity".to_string(),
|
|
sector: Some("Technology".to_string()),
|
|
expected_return: 0.13,
|
|
volatility: 0.26,
|
|
},
|
|
]
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_sample_presets_available() {
|
|
assert_eq!(SAMPLE_PRESETS.len(), 5);
|
|
assert!(SAMPLE_PRESETS.contains(&PortfolioPreset::Conservative));
|
|
assert!(SAMPLE_PRESETS.contains(&PortfolioPreset::TechFocus));
|
|
}
|
|
|
|
#[test]
|
|
fn test_generate_conservative_assets() {
|
|
let assets = generate_sample_assets(PortfolioPreset::Conservative).unwrap();
|
|
assert_eq!(assets.len(), 4);
|
|
assert!(assets.iter().any(|a| a.ticker == "BND"));
|
|
assert!(assets.iter().any(|a| a.asset_class == "bond"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_generate_balanced_assets() {
|
|
let assets = generate_sample_assets(PortfolioPreset::Balanced).unwrap();
|
|
assert_eq!(assets.len(), 5);
|
|
assert!(assets.iter().any(|a| a.ticker == "SPY"));
|
|
assert!(assets.iter().any(|a| a.ticker == "BND"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_generate_aggressive_assets() {
|
|
let assets = generate_sample_assets(PortfolioPreset::Aggressive).unwrap();
|
|
assert_eq!(assets.len(), 4);
|
|
assert!(assets.iter().all(|a| a.asset_class == "equity"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_generate_global_diversified_assets() {
|
|
let assets = generate_sample_assets(PortfolioPreset::GlobalDiversified).unwrap();
|
|
assert_eq!(assets.len(), 7);
|
|
assert!(assets.iter().any(|a| a.ticker == "EFA"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_generate_tech_focus_assets() {
|
|
let assets = generate_sample_assets(PortfolioPreset::TechFocus).unwrap();
|
|
assert_eq!(assets.len(), 4);
|
|
assert!(assets.iter().all(|a| {
|
|
a.sector
|
|
.as_ref()
|
|
.map(|s| s.contains("Technology"))
|
|
.unwrap_or(false)
|
|
}));
|
|
}
|
|
|
|
#[test]
|
|
fn test_asset_properties() {
|
|
let assets = generate_sample_assets(PortfolioPreset::Balanced).unwrap();
|
|
for asset in assets {
|
|
assert!(!asset.ticker.is_empty());
|
|
assert!(!asset.name.is_empty());
|
|
assert!(!asset.asset_class.is_empty());
|
|
assert!(asset.expected_return >= 0.0);
|
|
assert!(asset.volatility > 0.0);
|
|
}
|
|
}
|
|
}
|