Skip to content

Commit

Permalink
improve IBOND ux
Browse files Browse the repository at this point in the history
  • Loading branch information
rrelyea committed Jan 9, 2024
1 parent fa67c22 commit e510167
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 109 deletions.
10 changes: 8 additions & 2 deletions Pages/AccountView.razor
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@
var advice = Advisor.Advise(investment, account, appData);
int invBuffer = investmentIndex;
investmentIndex++;
if (investment.Selected && investment.SelectedTransaction == null) {
if (investment.Selected && (investment.SelectedTransaction == null || investment.SelectedTransaction.Type == "Edit")) {
bgColor = "yellow";
} else {
bgColor = alternateRow2 ? "white" : "lightgray";
Expand Down Expand Up @@ -550,9 +550,13 @@
selectedInvestment.SelectedTransaction = null;
}

void commit(MouseEventArgs e, Account account, Investment selectedInvestment)
async void commit(MouseEventArgs e, Account account, Investment selectedInvestment)
{
plan(e, account, selectedInvestment);
if (selectedInvestment.CostBasis != null && selectedInvestment.PurchaseDate.HasValue && selectedInvestment.AssetType == global::AssetType.IBond)
{
await selectedInvestment.CalculateIBondValue();
}
}

void cancel(MouseEventArgs e, Investment selectedInvestment)
Expand Down Expand Up @@ -983,6 +987,8 @@

account.SelectedInvestment = new Investment(appData.FamilyData.PIN) { funds = Funds, Selected = true };
account.Investments.Add(account.SelectedInvestment);

account.SelectedInvestment.SelectedTransaction = new Transaction() { Type = "Edit" };
}
}

Expand Down
107 changes: 1 addition & 106 deletions Pages/Portfolio.razor
Original file line number Diff line number Diff line change
Expand Up @@ -1281,7 +1281,7 @@

if (investment.AssetType == AssetType.IBond)
{
await CalculateIBondValue(investment);
await investment.CalculateIBondValue();
}

if (fetchQuote && investment.Ticker != null) {
Expand Down Expand Up @@ -1327,111 +1327,6 @@
refreshButtonText = "🔃 Quotes";
}

public static Dictionary<string,List<double>>? IBondRates { get; set; }

async Task LoadIBondRates() {
IBondRates = new();
var ibondsUri = new Uri("https://raw.githubusercontent.com/bogle-tools/financial-variables/main/data/usa/treasury-direct/i-bond-rate-chart.csv");
var httpClient = new HttpClient();
var ibondsCsv = await httpClient.GetAsync(ibondsUri.AbsoluteUri);
var stream = await ibondsCsv.Content.ReadAsStreamAsync();
using var reader = new CsvReader(stream);
var RowEnumerator = reader.GetRowEnumerator().GetAsyncEnumerator();
await RowEnumerator.MoveNextAsync();
await RowEnumerator.MoveNextAsync();
while (await RowEnumerator.MoveNextAsync())
{
string[] chunks = RowEnumerator.Current;
int chunkNum = 0;
string? date = null;
List<double> rates = new();
foreach (var chunk in chunks)
{
switch (chunkNum)
{
case 0:
date = chunk[..5];
if (!char.IsDigit(date[0]))
{
// lines at bottom of the csv file that don't start with a dates should be skipped.
return;
}
break;
case 1:
break;
default:
if (string.IsNullOrEmpty(chunk))
{
continue;
}
else
{
var rate = DoubleFromPercentageString(chunk);
rates.Add(rate);
}
break;
}

chunkNum++;
}

IBondRates[date!] = rates;
}
}

string GetRateDate(int month, int year)
{
if (month < 5) {
return "11/" + (year-1).ToString().Substring(2);
}
else if (month < 11) {
return "05/" + (year).ToString().Substring(2);
} else {
return "11/" + (year).ToString().Substring(2);
}
}

async Task CalculateIBondValue(Investment investment)
{
if (IBondRates == null) {
await LoadIBondRates();
}

if (IBondRates != null)
{
if (investment.PurchaseDate.HasValue)
{
var month = investment.PurchaseDate.Value.Month;
var year = investment.PurchaseDate.Value.Year;
var date = GetRateDate(month, year);
var rates = Portfolio.IBondRates[date];

var nowMonth = DateTime.Now.Month;
var nowYear = DateTime.Now.Year;
double value = investment.CostBasis ?? 0.0;
double bondQuantity = (value / 25.0);
for (int i = rates.Count - 1; i >= 0; i--)
{
var monthCount = i > 0 ? 6 : GetMonthsLeft(investment.PurchaseDate.Value, DateTime.Now);
value = (bondQuantity*Math.Round(value/bondQuantity*Math.Pow((1.0+rates[i]/2.0),((double)monthCount/6.0)),2));
}

investment.ValuePIN = (int)value;
}
}
}

private int GetMonthsLeft(DateOnly purchaseDate, DateTime now)
{
var months = ((now.Year - purchaseDate.Year) * 12 + now.Month - purchaseDate.Month) % 6;
return months;
}

private static double DoubleFromPercentageString(string value)
{
return double.Parse(value.Replace("%","")) / 100;
}

enum MarketDay {
MarketDay,
Holiday,
Expand Down
105 changes: 105 additions & 0 deletions Shared/Models/FamilyData/Investment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,4 +337,109 @@ public void UpdateValue() {

[JsonIgnore]
public double Percentage { get; set; }

public static Dictionary<string,List<double>>? IBondRates { get; set; }

static async Task LoadIBondRates() {
IBondRates = new();
var ibondsUri = new Uri("https://raw.githubusercontent.com/bogle-tools/financial-variables/main/data/usa/treasury-direct/i-bond-rate-chart.csv");
var httpClient = new HttpClient();
var ibondsCsv = await httpClient.GetAsync(ibondsUri.AbsoluteUri);
var stream = await ibondsCsv.Content.ReadAsStreamAsync();
using var reader = new CsvReader(stream);
var RowEnumerator = reader.GetRowEnumerator().GetAsyncEnumerator();
await RowEnumerator.MoveNextAsync();
await RowEnumerator.MoveNextAsync();
while (await RowEnumerator.MoveNextAsync())
{
string[] chunks = RowEnumerator.Current;
int chunkNum = 0;
string? date = null;
List<double> rates = new();
foreach (var chunk in chunks)
{
switch (chunkNum)
{
case 0:
date = chunk[..5];
if (!char.IsDigit(date[0]))
{
// lines at bottom of the csv file that don't start with a dates should be skipped.
return;
}
break;
case 1:
break;
default:
if (string.IsNullOrEmpty(chunk))
{
continue;
}
else
{
var rate = DoubleFromPercentageString(chunk);
rates.Add(rate);
}
break;
}

chunkNum++;
}

IBondRates[date!] = rates;
}
}

static string GetRateDate(int month, int year)
{
if (month < 5) {
return "11/" + (year-1).ToString().Substring(2);
}
else if (month < 11) {
return "05/" + (year).ToString().Substring(2);
} else {
return "11/" + (year).ToString().Substring(2);
}
}

public async Task CalculateIBondValue()
{
if (IBondRates == null) {
await LoadIBondRates();
}

if (IBondRates != null)
{
if (PurchaseDate.HasValue)
{
var month = PurchaseDate.Value.Month;
var year = PurchaseDate.Value.Year;
var date = GetRateDate(month, year);
var rates = IBondRates[date];

var nowMonth = DateTime.Now.Month;
var nowYear = DateTime.Now.Year;
double value = CostBasis ?? 0.0;
double bondQuantity = (value / 25.0);
for (int i = rates.Count - 1; i >= 0; i--)
{
var monthCount = i > 0 ? 6 : GetMonthsLeft(PurchaseDate.Value, DateTime.Now);
value = (bondQuantity*Math.Round(value/bondQuantity*Math.Pow((1.0+rates[i]/2.0),((double)monthCount/6.0)),2));
}

ValuePIN = (int)value;
}
}
}

private static int GetMonthsLeft(DateOnly purchaseDate, DateTime now)
{
var months = ((now.Year - purchaseDate.Year) * 12 + now.Month - purchaseDate.Month) % 6;
return months;
}

private static double DoubleFromPercentageString(string value)
{
return double.Parse(value.Replace("%","")) / 100;
}
}
2 changes: 1 addition & 1 deletion wwwroot/cache.manifest
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CACHE MANIFEST

# Version 1.0108
# Version 1.0109

NETWORK:
*

0 comments on commit e510167

Please sign in to comment.