websites/eddie.sh/oneshots/bofa-rewards/index.html

404 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bank of America Preferred Rewards Suck</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=M+PLUS+Code+Latin&display=swap" rel="stylesheet">
<!-- If anyone is snooping around and would like to contribute a favicon, let me know :P -->
<link rel="icon" href="data:,">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
<style>
html {
display: flex;
justify-content: center;
align-items: center;
}
body {
font-family: 'M PLUS Code Latin', sans-serif;
background-color: #000;
max-width: 84ch;
width: 84ch;
padding: 0 2ch;
margin-top: 10vh;
color: #ccc;
font-size: 16pt;
}
a:visited {
color: #999;
}
a {
color: #ccc;
}
ul {
list-style-type: "> ";
}
.alignRight {
text-align: right;
}
table {
width: 100%;
}
.fill {
min-width: 6ch;
}
</style>
</head>
<body>
<h1>Bank of America Preferred Rewards Probably Suck</h1>
<h3>2023-04-03</h3>
<p>
Bank of America (BofA) has a rewards program that is tiered based on how
much of money you have with BofA or an associated Merrill account. There are
five tiers that they offer, with (among other things) a credit card percent
bonus:
</p>
<table>
<tr>
<th>Tier Name</th>
<th>Minimum Balance Required (USD)</th>
<th>Credit Card Bonus</th>
</tr>
<tr>
<td>Gold</td>
<td class="alignRight">20,000</td>
<td class="alignRight">25%</td>
</tr>
<tr>
<td>Platinum</td>
<td class="alignRight">50,000</td>
<td class="alignRight">50%</td>
</tr>
<tr>
<td>Platinum Honors</td>
<td class="alignRight">100,000</td>
<td class="alignRight">75%</td>
</tr>
<tr>
<td>Diamond</td>
<td class="alignRight">1,000,000</td>
<td class="alignRight">75%</td>
</tr>
<tr>
<td>Diamond Honors</td>
<td class="alignRight">10,000,000</td>
<td class="alignRight">75%</td>
</tr>
</table>
<p>
The credit card percent bonus is an additional amount of points or cash back
based on how much you original were going to get. For example, a 75% percent
bonus on a 1.5 points per dollar purchase gives you an additional 1.125
points, or an effective rate of 2.625 points back.
</p>
<p>
Preferred Rewards also comes with a interest rate booster on a savings
account, from 5% on the Gold tier to a maximum of 20% at the Platinum Honors
and above. That sounds nice, except that the base Annual Percent Yield (APY)
on these savings account as of writing this, is a whopping 0.01% APY. Not
1%. 0.01%. They're generous enough to round up, though, so Gold gets an rate
of 0.02%, Platinum Honors and higher gets an astounding 0.04%. What a steal!
</p>
<p>
High Yield Savings Accounts (HYSA) are FDIC-insured accounts that offer a
high interest rate for money in your account. Because they are a savings
account, you cannot ever lose numerical value in this account. They have a
variable APY though, so while it may have a high APY now, it might not in
the future.
</p>
<p>As of March 2023, they have an APY from anywhere of 2% to 5%.</p>
<p>
If you look at just APY alone, it's clear that if you're looking to park an
emergency fund or have short-term plans in mind, it makes absolutely no
sense to keep your money at BofA. But if you attempt to consider the credit
card bonus, it might not be as clear. After all, earning an extra percent or
two on your credit card purchasing is incredibly enticing. In reality, it
only makes sense if you have an brokerage account with Merrill and you're
happy with what Merrill offers.
</p>
<p>
Lets consider the total value that a person gets from keeping money in a
BofA savings account and getting the credit card bonus over moving to a
HYSA. We'll say that the person wants to keep it in a savings account as
they want to save this money as an emergency fund. We'll also assume that
additional points have a 100 to 1.00 USD exchange rate.
</p>
<div>
<canvas id="myChart"></canvas>
</div>
<br />
<table>
<tr>
<td><label for="saved">Savings (USD)</label></td>
<td class="alignRight fill" id="savedDisplay">25%</td>
<td><input id="saved" type="range" min="0" max="120000" step="5000" value="50000"></input></td>
</tr>
<tr>
<td><label for="hysaPercent">HYSA APY (%)</label></td>
<td class="alignRight fill" id="hysaPercentDisplay">25%</td>
<td><input id="hysaPercent" type="range" min="0" max="9" step="0.05" value="3.75"></input></td>
</tr>
<tr>
<td><label for="monthlySpending">Monthly Spending (USD)</label></td>
<td class="alignRight fill" id="monthlySpendingDisplay">25%</td>
<td><input id="monthlySpending" type="range" min="0" max="10000" step="200" value="2000"></input></td>
</tr>
<tr>
<td><label for="avgBasePoints">Avg. points gained per USD (Without preferred rewards)</label></td>
<td class="alignRight fill" id="avgBasePointsDisplay">25%</td>
<td><input id="avgBasePoints" type="range" min="0" max="5" step="0.5" value="3"></input></td>
</tr>
</table>
<p>
Playing around with the chart, you'll notice that with the bare minimum to
get the Platinum tier, generous amounts of spending on your cards, and an
HYSA APY of 0.5%, you <i>still</i> get better returns in the HYSA than
keeping money in the savings account. Even if you were to find a scenario
where you are earning more with the credit card bonus, unless you're
spending that much already (and not spending for the sake of bonus points),
it makes absolutely no sense to do so.
</p>
<h2>What about Money Market Mutual Funds?</h2>
<p>
Merrill also offers Money Market Mutual Funds (MMMFs) such as <a
href="https://www.blackrock.com/cash/en-us/products/282697/">TTTXX</a> and
offer competitive rates to a HYSA outside BofA. However, the key difference
here is that they are not FDIC-insured and run the risk of <a
href="https://www.investopedia.com/terms/b/breaking-the-buck.asp">"breaking the
buck"</a>. They are also much more sensitive to fluctuations in the
market. As a result, even the risk is miniscule, they are fundamentally
in a different risk class than a savings account.
</p>
<p>
If you think that the Preferred Rewards are valuable enough to expose your
emergency funds and/or short-term holdings to the market, then sure, it's a
way to have the best of both worlds. However, for me, I've decided that it's
not, especially with the current situation.
</p>
<h2>When does it make sense?</h2>
<p>
I think there are only a few real scenarios where keeping your money in a
BofA savings account is reasonable.
</p>
<ol>
<li>You already have a brokerage account in Merrill that meets the minimum for a tier.</li>
<li>You are willing to invest your savings/emergency fund into a Money Market Mutual Fund AND think the Preferred
Rewards are worthwhile.</li>
<li>You have less than 10k in savings AND HYSA APYs are below 0.7% AND you spent over 5k per month.</li>
</ol>
<p>
I put less than a couple hundreds of dollars on my BofA card, so there's
almost zero incentive for me to keep my money with BofA. In fact, with
inflation as large as it is, I'm losing an incredible amount of purchasing
power by keeping it in a BofA saving account.
</p>
<script>
function monthlyInterest(apy) {
return Math.pow(1 + apy, 1 / 12) - 1;
}
function rawGrowth(base, monthlyFunc) {
let data = Array(13).fill(base);
for (let i = 1; i < data.length; i++) {
data[i] = (data[i - 1] * (1 + monthlyFunc(data[i - 1]))).toFixed(2);
}
return data;
}
const PLAT_HONORS_THRESHOLD = 100_000;
const PLAT_THRESHOLD = 50_000;
const GOLD_THRESHOLD = 20_000;
function bofaInterestRateFromSavings(savings) {
if (savings >= PLAT_HONORS_THRESHOLD) {
return 0.0004;
} else if (savings >= PLAT_THRESHOLD) {
return 0.0003;
} else if (savings >= GOLD_THRESHOLD) {
return 0.0002;
} else {
return 0.0001;
}
}
function bofaAdditionalPoints(basePoints, savings) {
if (savings >= PLAT_HONORS_THRESHOLD) {
return basePoints * 0.75;
} else if (savings >= PLAT_THRESHOLD) {
return basePoints * 0.5;
} else if (savings >= GOLD_THRESHOLD) {
return basePoints * 0.25;
} else {
return basePoints;
}
}
function hysaGrowth(base, monthly) {
return rawGrowth(base, () => monthly).map(v => v - base);
}
function bofaSavingsGrowth(base) {
return rawGrowth(base, savings => bofaInterestRateFromSavings(savings)).map(v => v - base);
}
function creditCardGrowth(baseSaved, basePoints, monthlySpending) {
return bofaSavingsGrowth(baseSaved).map(growth => monthlySpending * bofaAdditionalPoints(basePoints, baseSaved + growth) / 100);
}
function bofaCombined(baseSaved, basePoints, monthlySpending) {
let savings = bofaSavingsGrowth(baseSaved);
return creditCardGrowth(baseSaved, basePoints, monthlySpending).map((v, i) => v + savings[i]);
}
function bofaGrossCombined(baseSaved, basePoints, monthlySpending) {
let savings = bofaSavingsGrowth(baseSaved);
return creditCardGrowth(baseSaved, basePoints, monthlySpending).map((v, i) => v + savings[i] - monthlySpending * i);
}
function getDatasets(saved, hysaPercent, monthlySpending, avgBasePoints) {
return [
{
label: 'HYSA',
data: hysaGrowth(saved, monthlyInterest(hysaPercent / 100)),
borderWidth: 1
},
{
label: 'BofA Savings Interest',
data: bofaSavingsGrowth(saved),
borderWidth: 1
},
{
label: 'Credit Card Bonus',
data: creditCardGrowth(saved, avgBasePoints, monthlySpending),
borderWidth: 1
},
{
label: 'BofA Savings Interest + Credit Card Bonus',
data: bofaCombined(saved, avgBasePoints, monthlySpending),
borderWidth: 1
},
{
label: 'BofA Savings Interest + Credit Card Bonus - Amount spent',
data: bofaGrossCombined(saved, avgBasePoints, monthlySpending),
borderWidth: 1,
hidden: true,
}
];
}
document.addEventListener("DOMContentLoaded", () => {
function getOrInit(id, value) {
const ele = document.getElementById(id);
if (ele.value) {
document.getElementById(id + "Display").textContent = ele.value;
return ele.value;
} else {
document.getElementById(id + "Display").textContent = value;
ele.value = value;
return value;
}
}
let saved = getOrInit("saved", 50_000);
let hysaPercent = getOrInit("hysaPercent", 3.75);
let monthlySpending = getOrInit("monthlySpending", 2_000);
let avgBasePoints = getOrInit("avgBasePoints", 1.5);
Chart.defaults.color = "#ccc";
Chart.defaults.borderColor = "#9993";
Chart.defaults.font.family = "'M PLUS Code Latin', sans-serif";
Chart.defaults.font.size = 16;
let chart = new Chart(document.getElementById('myChart'), {
normalized: true,
type: 'line',
data: {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"],
datasets: getDatasets(saved, hysaPercent, monthlySpending, avgBasePoints),
},
options: {
scales: {
y: {
beginAtZero: true,
}
},
animation: false,
}
});
function updateChart() {
chart.data.datasets = getDatasets(saved, hysaPercent, monthlySpending, avgBasePoints);
chart.update();
}
document.getElementById("saved").addEventListener("input", (ev) => {
if (ev.target.value) {
saved = ev.target.value;
document.getElementById("savedDisplay").textContent = ev.target.value;
updateChart();
}
});
document.getElementById("monthlySpending").addEventListener("input", (ev) => {
if (ev.target.value) {
monthlySpending = ev.target.value;
document.getElementById("monthlySpendingDisplay").textContent = ev.target.value;
updateChart();
}
});
document.getElementById("avgBasePoints").addEventListener("input", (ev) => {
if (ev.target.value) {
avgBasePoints = ev.target.value;
document.getElementById("avgBasePointsDisplay").textContent = ev.target.value;
updateChart();
}
});
document.getElementById("hysaPercent").addEventListener("input", (ev) => {
if (ev.target.value) {
hysaPercent = ev.target.value;
document.getElementById("hysaPercentDisplay").textContent = ev.target.value;
updateChart();
}
});
});
</script>
</body>
</html>