Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-currency budgeting #26

Open
GuillaumedeVolpiano opened this issue Apr 16, 2021 · 8 comments
Open

Multi-currency budgeting #26

GuillaumedeVolpiano opened this issue Apr 16, 2021 · 8 comments
Labels
enhancement New feature or request

Comments

@GuillaumedeVolpiano
Copy link
Contributor

To sum things up, I live and work in a country and my immediate family lives in another country, with a different currency, so I need to be able to budget in multiple currencies, so I've tweaked the code for my personal use, creating options allowing to have multiple budgets over multiple currencies.
This is probably not on the project roadmap for now, but just in case, would you be interested in me sharing my code?

@polarmutex
Copy link
Owner

I would love to bring that feature in, it is on my list just not a top priority

@polarmutex polarmutex added the enhancement New feature or request label Apr 16, 2021
@GuillaumedeVolpiano
Copy link
Contributor Author

I've posted the code as a pull request, if you feel like giving it a look.

@polarmutex
Copy link
Owner

polarmutex commented Apr 17, 2021

just have a question. for your way of budgeting would you consider converting to one currency? I only budget in one, I am thinking in the future to support having a budget per currency or converting all other currencies to one.

Thoughts?

@GuillaumedeVolpiano
Copy link
Contributor Author

That's a very good point, and something that shouldn't be to hard to implement in the current code.

As is, some bits are still very much in a "works for me" state, where I have current accounts in the major currencies I operate in, and just transfer from one account to the other (hence the creation of the "income account" option).

But a very real real life scenario, at least in the country I'm currently in, is to have a credit card in USD tied to your account in SFC (some foreign currency), for which you might want to budget. Or you might want to have one budget over multiple currencies.

As the code currently stands, it does "flatten" out spending in other currencies when they have a price, which, I think, covers all spending to an account (lines 223-229 of the current commit):

            if posting.units.currency != self.currency:
                orig=posting.units.number
                if posting.price is not None:
                    converted=posting.price.number*orig
                    posting=data.Posting(posting.account,amount.Amount(converted,self.currency), posting.cost, None, posting.flag,posting.meta)
                else:
                    continue

So it's just a matter of implementing an else clause that fetches the price. There are four options here:

1 - use the price at the time of the transaction (or the closest time to that transaction)
2 - use the price at the time of the generation (or the closest to it)
3 - use an automatically generated average price for the month of the transaction
4 - use an average price, set for that month, as an "envelope" option.

Option 1 and 2 both, in my opinion, have strong issues:

  • option 1 as the price at the time of the transaction will not necessarily be the price you actually pay for the currency (back to my credit` card example, you usually pay those at 45 days). It depends on real-life usage, but this could be offset by having an Expenses:ForeignExchangeRisk category to absorb the offset, although this is going to be hard to calculate if one doesn't pay their credit card in full or a new expense cycle has started by the time you pay the previous month.
  • option 2 is the worst, as it will create fluctuations based on foreign exchange variations long after the transaction has taken place.
    Option 3 and 4 are fairly equivalent, and could, actually, be implemented concurrently, with 3 as the default behaviour and 4 as an override. 3 would give you an idea of the costs whilst the bill is running, and you could set 4 once you know your actual price.

Thoughts?

Before I look into it, though, as you have accepted my code, I'll first document it (once again, up to know, it was mostly "works for me" tweaks, so it's pretty rough).

@polarmutex
Copy link
Owner

thanks for your input, just want to understand how people use multiple currencies so when I make future changes I do not make it harder for them.

going to keep this open for future discussions

@egetzner
Copy link
Contributor

Great work so far with this feature. I created a PR documenting the usage in a separate example file. Would probably be good to have it in the README as well.

Some notes regarding multi-currency:

  • I personally would prefer to have everything on one page, similar to what fava itself does - listing the different currencies below each other but in each category.
  • As mentioned above, it would also make sense to me to convert all foreign currency entries to one operating currency -> perhaps via drop-down, similar to other fava controls.
  • When assigning money to the envelopes, I would prefer to use the same beancount language, i.e. have the currency follow the amount. instead of "envelopeUSD" "allocate" "account" 10, "envelope" "allocate" "account" 10 USD.

@valpackett
Copy link

Thanks for the pointer @GuillaumedeVolpiano!

To just get these transactions to not be ignored, I quickly implemented option 1:

--- /home/val/.local/pipx/venvs/beancount/lib/python3.12/site-packages/fava_envelope/modules/beancount_envelope.py.orig 2024-01-10 19:06:04.783010743 -0300
+++ /home/val/.local/pipx/venvs/beancount/lib/python3.12/site-packages/fava_envelope/modules/beancount_envelope.py      2024-01-10 19:18:38.528336092 -0300
@@ -277,18 +277,17 @@
                 account_type = account_types.get_account_type(account)
                 if posting.units.currency != self.currency:
                     orig = posting.units.number
-                    if posting.price is not None:
-                        converted = posting.price.number * orig
-                        posting = data.Posting(
-                            posting.account,
-                            amount.Amount(converted, self.currency),
-                            posting.cost,
-                            None,
-                            posting.flag,
-                            posting.meta,
-                        )
-                    else:
+                    amt = convert.convert_position(posting, self.currency, self.price_map, entry.date)
+                    if amt.currency != self.currency:
                         continue
+                    posting = data.Posting(
+                        posting.account,
+                        amt,
+                        posting.cost,
+                        None,
+                        posting.flag,
+                        posting.meta,
+                    )

                 if account_type == self.acctypes.income or (
                     any(

In my situation (want to budget in USD, most daily transactions are in a local currency with high inflation) price at the time of the transaction (option 1) is the most appropriate. The extra cool dream bonus option for this situation would be to actually track the purchase cost of the local currency, as if the {whatev USD} syntax were used (which is normally not used for regular cash)..

@gabrielkoerich
Copy link

any plans on adding this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants