Skip to content

Commit dfa4b53

Browse files
committed
add account cost-basis command
1 parent 44fc813 commit dfa4b53

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

src/main.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,6 +1959,66 @@ impl AnnualRealizedGain {
19591959
}
19601960
}
19611961

1962+
async fn process_account_cost_basis(
1963+
db: &Db,
1964+
when: NaiveDate,
1965+
) -> Result<(), Box<dyn std::error::Error>> {
1966+
let mut held_tokens =
1967+
BTreeMap::<MaybeToken, Vec<(/*amount: */ u64, /*price: */ Decimal)>>::default();
1968+
1969+
println!("Average Cost Basis on {when}");
1970+
for disposed_lot in db.disposed_lots() {
1971+
if disposed_lot.lot.acquisition.when > when || disposed_lot.when < when {
1972+
continue;
1973+
}
1974+
held_tokens
1975+
.entry(disposed_lot.token)
1976+
.or_insert_with(Vec::new)
1977+
.push((
1978+
disposed_lot.lot.amount,
1979+
disposed_lot.lot.acquisition.price(),
1980+
));
1981+
}
1982+
1983+
for account in db.get_accounts() {
1984+
let held_token = held_tokens.entry(account.token).or_insert_with(Vec::new);
1985+
for lot in account.lots {
1986+
if lot.acquisition.when <= when {
1987+
held_token.push((lot.amount, lot.acquisition.price()));
1988+
}
1989+
}
1990+
}
1991+
1992+
// Merge wSOL lots into SOL
1993+
if let Some(mut lots) = held_tokens.remove(&Token::wSOL.into()) {
1994+
held_tokens
1995+
.entry(MaybeToken::SOL())
1996+
.or_insert_with(Vec::new)
1997+
.append(&mut lots);
1998+
}
1999+
2000+
for (token, lots) in held_tokens {
2001+
if lots.is_empty() || token.fiat_fungible() {
2002+
continue;
2003+
}
2004+
2005+
let mut total_amount = 0;
2006+
let mut total_price = Decimal::default();
2007+
2008+
for (amount, price) in lots {
2009+
total_amount += amount;
2010+
total_price += Decimal::from_u64(amount).unwrap() * price;
2011+
}
2012+
println!(
2013+
" {:>7}: {:<20} at ${:.2}",
2014+
token.to_string(),
2015+
token.format_amount(total_amount),
2016+
total_price / Decimal::from_u64(total_amount).unwrap()
2017+
);
2018+
}
2019+
Ok(())
2020+
}
2021+
19622022
async fn process_account_list(
19632023
db: &Db,
19642024
rpc_client: &RpcClient,
@@ -4308,6 +4368,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
43084368
.help("Limit output to summary line"),
43094369
),
43104370
)
4371+
.subcommand(
4372+
SubCommand::with_name("cost-basis")
4373+
.about("Display average cost basis of holdings")
4374+
.arg(
4375+
Arg::with_name("when")
4376+
.value_name("YY/MM/DD")
4377+
.takes_value(true)
4378+
.required(false)
4379+
.validator(|value| naivedate_of(&value).map(|_| ()))
4380+
.default_value(&default_when)
4381+
.help("Date to calculate cost basis for")
4382+
)
4383+
)
43114384
.subcommand(
43124385
SubCommand::with_name("xls")
43134386
.about("Export an Excel spreadsheet file")
@@ -5796,6 +5869,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
57965869
)
57975870
.await?;
57985871
}
5872+
("cost-basis", Some(arg_matches)) => {
5873+
let when = value_t!(arg_matches, "when", String)
5874+
.map(|s| naivedate_of(&s).unwrap())
5875+
.unwrap();
5876+
5877+
process_account_cost_basis(&db, when).await?;
5878+
}
57995879
("xls", Some(arg_matches)) => {
58005880
let outfile = value_t_or_exit!(arg_matches, "outfile", String);
58015881
let filter_by_year = value_t!(arg_matches, "year", i32).ok();

0 commit comments

Comments
 (0)