Powered by AppSignal & Oban Pro

HowMuch demo

demo.livemd

HowMuch demo

Application.put_all_env(
  handsontable: [
    license_key: "non-commercial-and-evaluation",
    working_dir: __DIR__
  ],
  ex_money: [
    open_exchange_rates_app_id: System.fetch_env!("LB_OPEN_EXCHANGE_RATES_APP_ID"),
    exchange_rates_cache_module: HowMuch.MoneyExchangeRatesDets,
    default_cldr_backend: HowMuch.Cldr
  ],
  how_much: [
    money_exchange_rate_dets: Path.join(__DIR__, "priv/money_exchange_rate.dets"),
    pricing_dets: Path.join(__DIR__, "priv/pricing.dets")
  ]
)

IO.inspect(__DIR__)
|> Path.join("priv")
|> File.mkdir_p!()

Mix.install([
  {:handsontable_kino_smartcell,
   git: "https://github.com/pastleo/handsontable_kino_smartcell.git", tag: "0.1.9"},
  {:how_much, git: "https://github.com/pastleo/how_much.git", tag: "0.1.9"},
  {:kino_explorer, "~> 0.1.11"},
  {:vega_lite, "~> 0.1.6"},
  {:kino_vega_lite, "~> 0.1.7"}
])
[project]
name = "project"
version = "0.0.0"
requires-python = "==3.13.*"
dependencies = [
  "yfinance"
]

Readme

add OPEN_EXCHANGE_RATES_APP_ID to secret, visit https://openexchangerates.org/ to get one

See:

1. Define asset records

twse_stocks_data = [
  ["", "TWSE.0050", "TWSE.2330", "", "", "", "", "", "", ""],
  ["2023-09-18", "1000", "2000", "", "", "", "", "", "", ""],
  ["2023-10-01", "1000", "3000", "", "", "", "", "", "", ""],
  ["2023-10-05", "1500", "3000", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""]
]
twse_assets_records = HowMuch.Record.from_table_data(twse_stocks_data)
IO.puts("twse_assets_records: #{length(twse_assets_records)}, latest: #{Enum.at(twse_assets_records, -1).date}")
fiat_assets_data = [
  [
    "",
    "bank1:TWD",
    "bank2:USD #fixed-deposit",
    "bank3:JPY #fixed-deposit",
    "",
    "",
    "",
    "",
    "",
    ""
  ],
  ["2023-09-18", "10,000", "1,000.5", "25,000", "", "", "", "", "", ""],
  ["2023-10-01", "12,000", "2,000.0", "20,000", "", "", "", "", "", ""],
  ["2023-10-05", "10000", "2,000.0", "20,000", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""]
]
fiat_assets_records = HowMuch.Record.from_table_data(fiat_assets_data)
IO.puts("fiat_assets_records: #{length(fiat_assets_records)}, latest: #{Enum.at(fiat_assets_records, -1).date}")
firstrade_data = [
  ["", "YH.VOO", "", "", "", "", "", "", "", ""],
  ["2023-09-18", "1", "", "", "", "", "", "", "", ""],
  ["2023-10-01", "2", "", "", "", "", "", "", "", ""],
  ["2023-10-05", "2", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""],
  ["", "", "", "", "", "", "", "", "", ""]
]
firstrade_records = HowMuch.Record.from_table_data(firstrade_data, tags: ["#firstrade"])
IO.puts("firstrade_records: #{length(firstrade_records)}, latest: #{Enum.at(firstrade_records, -1).date}")

2. Calculate & Serialize

require Explorer.DataFrame
alias Explorer.DataFrame, as: DF
alias Explorer.Series, as: DS
alias VegaLite, as: Vl
target_currency = :TWD
# today = Date.utc_today()
today = ~D[2023-10-10]
calculate_until = HowMuch.Utils.unix_timestamp(today)

all_assets_values_serialized =
  (twse_assets_records ++ fiat_assets_records ++ firstrade_records)
  |> HowMuch.Value.calculate(target_currency, calculate_until)
  |> HowMuch.Value.serialize(target_currency)

all_assets_values_data_frame = DF.new(all_assets_values_serialized)
IO.puts("length of all_assets_values_serialized: #{length(all_assets_values_serialized)}")
two_weeks_ago = today |> Date.add(-2 * 7)
start_time_input = Kino.Input.date("Select chart start time:", default: two_weeks_ago)
start_time = Kino.Input.read(start_time_input)

3. Summarizing & Charts

all_recorded_data_frame = 
  HowMuch.Explorer.DataFrame.summarize(all_assets_values_data_frame, "name")
  |> HowMuch.Explorer.DataFrame.filter_summarized_has_record()
HowMuch.Explorer.DataFrame.current(all_assets_values_data_frame, "name", Date.add(today, -1))
|> HowMuch.VegaLite.current_pie_chart(
  "name",
  "Assets Value Distribution"
)
HowMuch.VegaLite.summarized_recorded_bar_line_chart(
  DF.filter(all_assets_values_data_frame, date > ^start_time),
  DF.filter(all_recorded_data_frame, date > ^start_time),
  "name",
  "Assets Value Summary since #{start_time}"
)
HowMuch.VegaLite.summarized_recorded_bar_line_chart(
  all_assets_values_data_frame,
  all_recorded_data_frame,
  "name",
  "Assets Value Summary (all time)"
)