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

Bug: Transaction in BRL but Market Price is in USD #1564

Open
vinhadelli opened this issue Dec 20, 2024 · 11 comments
Open

Bug: Transaction in BRL but Market Price is in USD #1564

vinhadelli opened this issue Dec 20, 2024 · 11 comments
Labels
🐛 Bug Something isn't working Community OSS contributions accepted

Comments

@vinhadelli
Copy link

vinhadelli commented Dec 20, 2024

Describe the bug
The purchase is in BRL, but the market price is in USD. In the screenshot bellow, I have 4 shares of HTMX11, with a total value of 631.16 BRL, but since the market price is in USD, it is converting the value to 3258.24 BRL.

To Reproduce
Steps to reproduce the behavior:

  1. Setup Maybe with BRL as the default currency
  2. Import a transaction with the values in BRL
  3. Check the data of the transaction, see that the market price is in USD

Expected behavior
I expected the market price to conform with the currency used to make the transaction

What version of Maybe are you using?
Self-hosted v0.2.0

What operating system and browser are you using?
Opera GX

Screenshots / Recordings
image

image

@vinhadelli vinhadelli added the 🐛 Bug Something isn't working label Dec 20, 2024
@zachgoll
Copy link
Collaborator

@vinhadelli can you share more details about how you imported this trade? Was it through a CSV import? Or was it through the add trade modal?

The ticker HTMX11 does not seem to be available in our securities provider (Synth), so I'm wondering how you were able to import this and get a price populated.

@vinhadelli
Copy link
Author

vinhadelli commented Dec 30, 2024

Hello @zachgoll. I imported using a CSV that I generated from my usual investment website and customized to fit the format that Maybe expected.
The only provider that I configured was the Free Plan from Synth, when Maybe asked for an API Key.

HTMX11 is listed under the Bovespa Stocks, from Brazil.
image

Here is a example from the CSV (From left to right: Date, Ticker, Quantity, Value, Currency and Account)
image

@cybervirtu
Copy link

Hello @zachgoll, facing same problem when added a share from NSE india, Instead of INR of the share price it is taking USD and calculations are getting wrong based on it.
Screenshot 2025-01-01 at 1 24 09 PM
Screenshot 2025-01-01 at 1 24 33 PM

The current value need to be ₹ 1357.05 instead it is taking as $ and all the calculations are getting deviated because of it

@cybervirtu
Copy link

E418EC01-8C0F-412F-B6A2-352101B793C2_1_201_a

@cybervirtu
Copy link

@nikhilbadyal
Copy link
Contributor

nikhilbadyal commented Jan 2, 2025

This happened because you imported data by CSV and the currency by default goes USD in the Database. @cybervirtu . I did this, this this , and this as a hack in my fork.

@zachgoll
Copy link
Collaborator

zachgoll commented Jan 2, 2025

@nikhilbadyal is correct here. If you do not explicitly set the currency for your imported CSV, it will try to apply several defaults:

  1. First looks at the CSV row for a currency column
  2. Falls back to your family's currency preference (set in settings)

Currently, the currency dropdown is disabled for trades, but now that we support multi-currency investments, I think we could probably enable this:

CleanShot 2025-01-02 at 11 09 30

@vinhadelli
Copy link
Author

@zachgoll, to provide further data. In my install I did have the "currency" column in my CSV.
image

And my default currency in Maybe was already set as Brazilian Real (BRL)
image

@cybervirtu
Copy link

cybervirtu commented Jan 3, 2025

This happened because you imported data by CSV and the currency by default goes USD in the Database. @cybervirtu . I did this, this this , and this as a hack in my fork.

Hi @nikhilbadyal @zachgoll i hadn't imported any CSV i updated this manually and this happened for a manual entry

@nikhilbadyal
Copy link
Contributor

nikhilbadyal commented Jan 3, 2025

@zachgoll to clarify more. The currency from CSV is not used at all, and this happens irrespective of the how data was added.(In fact with CSV import there is much more mess than manual entry). Because the stock data is populated(in security_prices and securities table) when it's fetched from synth and the final currency is decided by synth response only.

currency: price.dig("currency") || "USD"

And since synth doesn't provides. It fall back to USD and default currency.

I had this hack to extract correct currency.

def fetch_security_prices(ticker:, start_date:, end_date:, mic_code: nil)
    params = {
      start_date: start_date,
      end_date: end_date
    }

    params[:mic_code] = mic_code if mic_code.present?

    stock_exchange = StockExchange.find_by(mic: mic_code)

    # If no stock exchange is found for the given mic_code, we fall back to USD as default
    currency = stock_exchange ? stock_exchange.currency_code : "USD"

    prices = paginate(
      "#{base_url}/tickers/#{ticker}/open-close",
      params
    ) do |body|
      body.dig("prices").map do |price|
        {
          date: price.dig("date"),
          price: price.dig("close")&.to_f || price.dig("open")&.to_f,
          currency: currency
        }
      end
    end

    SecurityPriceResponse.new \
      prices: prices,
      success?: true,
      raw_response: prices.to_json
  rescue StandardError => error
    SecurityPriceResponse.new \
      success?: false,
      error: error,
      raw_response: error
  end

For CSV import more code is need to take exchange code as input to properly calculate the currency.

@zachgoll
Copy link
Collaborator

zachgoll commented Jan 7, 2025

@nikhilbadyal @vinhadelli @cybervirtu thanks for the details on this, very helpful.

This is a tricky one—the code that @nikhilbadyal provided is not going to work in all cases, because Synth should be returning the currency that the price is quoted in. We need to treat that as our source of truth and then use ExchangeRate within the Maybe system to convert any prices that are not in the Account/Family currency. While the Synth provider does not respect the currency of the CSV import, the CSV import will post the entries to the account in the currency specified.

If you create the following trade:

  • Ticker: ABC
  • Currency: BRL
  • Price: 100
  • Qty: 1

And Synth returns:

  • Ticker: ABC
  • Currency: USD
  • Price: 80

The trade should still be stated in BRL at a price of 100, but the holdings (and the trade drawer details view) should be converting the Synth USD value -> BRL using exchange rates.

In other words, we shouldn't be altering the Synth code, but rather, I think the solution needs to be implemented where we're syncing the holdings for the account, and in the drawer:

price = security.dig(:prices)&.find { |p| p.date == date }
next if price.blank?
converted_price = Money.new(price.price, price.currency).exchange_to(account.currency, fallback_rate: 1).amount

@zachgoll zachgoll added the Community OSS contributions accepted label Jan 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 Bug Something isn't working Community OSS contributions accepted
Projects
None yet
Development

No branches or pull requests

4 participants