Skip to content

Commit

Permalink
calculate IBOND prices
Browse files Browse the repository at this point in the history
  • Loading branch information
rrelyea committed Jan 9, 2024
1 parent 3a8d761 commit fa67c22
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 4 deletions.
112 changes: 111 additions & 1 deletion Pages/Portfolio.razor
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,7 @@
}
private async Task UpdateInvestmentsPrice(string ticker, List<Investment> investments)
{
if (!string.IsNullOrEmpty(appData.EODHistoricalDataApiKey)) {
if (!string.IsNullOrEmpty(appData.EODHistoricalDataApiKey) && !string.IsNullOrEmpty(ticker)) {
var quoteDataJson = await Http.GetStreamAsync($"https://api.bogle.tools/api/getquotes?ticker={ticker}&apikey={appData.EODHistoricalDataApiKey}");
var quoteData = await JsonSerializer.DeserializeAsync<QuoteData>(quoteDataJson);
if (quoteData?.Close != null) {
Expand Down Expand Up @@ -1279,6 +1279,11 @@
fetchQuote = investment.LastUpdated == null || investment.LastUpdated?.Date != previousMarketClose.Date;
}

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

if (fetchQuote && investment.Ticker != null) {
if (!quotes.ContainsKey(investment.Ticker)) {
quotes.Add(investment.Ticker, new List<Investment> () { investment });
Expand Down Expand Up @@ -1322,6 +1327,111 @@
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
1 change: 1 addition & 0 deletions Shared/Models/FamilyData/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ public void UpdateInvestmentCategoryTotals(Investment investment, FamilyData fam
case AssetType.Bond:
case AssetType.Bond_ETF:
case AssetType.Bond_Fund:
case AssetType.IBond:
case AssetType.InternationalBond:
case AssetType.InternationalBond_ETF:
case AssetType.InternationalBond_Fund:
Expand Down
1 change: 1 addition & 0 deletions Shared/Models/FamilyData/Advisor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static List<string> Advise(Investment investment, Account account, IAppDa
adviceItems.Add("2nd: international->taxable");
}
break;
case AssetType.IBond:
case AssetType.Bond:
case AssetType.Bond_ETF:
case AssetType.Bond_Fund:
Expand Down
1 change: 1 addition & 0 deletions Shared/Models/FamilyData/AssetType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public enum AssetType
Cash,
Cash_BankAccount,
Cash_MoneyMarket,
IBond,
InternationalStock,
InternationalStock_ETF,
InternationalStock_Fund,
Expand Down
3 changes: 2 additions & 1 deletion Shared/Models/FamilyData/Investment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public int InvestmentOrder {
{
global::AssetType.USStock or global::AssetType.USStock_ETF or global::AssetType.USStock_Fund or global::AssetType.Stock=> 1,
global::AssetType.InternationalStock or global::AssetType.InternationalStock_ETF or global::AssetType.InternationalStock_Fund => 2,
global::AssetType.Bond or global::AssetType.Bond_ETF or global::AssetType.Bond_Fund or global::AssetType.InternationalBond or global::AssetType.InternationalBond_ETF or global::AssetType.InternationalBond_Fund => 3,
global::AssetType.Bond or global::AssetType.IBond or global::AssetType.Bond_ETF or global::AssetType.Bond_Fund or global::AssetType.InternationalBond or global::AssetType.InternationalBond_ETF or global::AssetType.InternationalBond_Fund => 3,
global::AssetType.StocksAndBonds_ETF or global::AssetType.StocksAndBonds_Fund => 4,
global::AssetType.Cash or global::AssetType.Cash_BankAccount or global::AssetType.Cash_MoneyMarket => 5,
_ => 6,
Expand Down Expand Up @@ -334,6 +334,7 @@ public void UpdateValue() {
public double? PreviousClose { get; set; }
public double? PercentChange { get; set; }
public DateTime? LastUpdated { get; set; }

[JsonIgnore]
public double Percentage { get; set; }
}
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.0107
# Version 1.0108

NETWORK:
*
2 changes: 1 addition & 1 deletion wwwroot/data/funds.json

Large diffs are not rendered by default.

0 comments on commit fa67c22

Please sign in to comment.