ghostfolio / ghostfolio

Open Source Wealth Management Software. Angular + NestJS + Prisma + Nx + TypeScript 🤍
https://Ghostfol.io
GNU Affero General Public License v3.0
3.85k stars 362 forks source link

[BUG] ("EUR") does not match with "AUD" and no exchange rate is available from "EUR" to "AUD" for BAC #2558

Open JahFyahh opened 8 months ago

JahFyahh commented 8 months ago

The Issue tracker is ONLY used for reporting bugs. New features should be discussed in our Slack community or in Discussions.

Bug Description

When importing activities using json file, I get the error: ("EUR") does not match with "AUD" and no exchange rate is available from "EUR" to "AUD". My base currency is EUR and I am not using AUD nowhere. the trading currency of BAC is USD, so not sure why its trying to import AUD. I also added AUD as a currency but still does not work.

To Reproduce

Import the following activity using json: { "accountId": "90ae4a91-33d2-47c2-9616-57f7c2816b6e", "comment": "ISIN: US0605051046 - Dividend: - dividendTax: -0.07", "fee": 0.07, "quantity": 1, "type": "DIVIDEND", "unitPrice": 0.48, "currency": "USD", "dataSource": "YAHOO", "date": "2023-10-02T06:51:00.000Z", "symbol": "BAC" }

Expected behavior

Succesful import

Screenshots

image

Logs

[Nest] 143 - 10/28/2023, 3:48:38 PM ERROR [class ImportController{constructor(e,t,a){this.configurationService=e,this.importService=t,this.request=a}async import(t,a){if(!er(this.request.user.permissions,Da)||!er(this.request.user.permissions,Na))throw new e.HttpException((0,At.getReasonPhrase)(At.StatusCodes.FORBIDDEN),At.StatusCodes.FORBIDDEN);let r=this.configurationService.get("MAX_ACTIVITIES_TO_IMPORT");this.configurationService.get("ENABLE_FEATURE_SUBSCRIPTION")&&"Premium"===this.request.user.subscription.type&&(r=Number.MAX_SAFE_INTEGER);const o=this.request.user.Settings.settings.baseCurrency;try{return{activities:await this.importService.import({isDryRun:a,maxActivitiesToImport:r,userCurrency:o,accountsDto:t.accounts??[],activitiesDto:t.activities,userId:this.request.user.id})}}catch(n){throw e.Logger.error(n,Sc),new e.HttpException({error:(0,At.getReasonPhrase)(At.StatusCodes.BAD_REQUEST),message:[n.message]},At.StatusCodes.BAD_REQUEST)}}async gatherDividends(e,t){const a=this.request.user.Settings.settings.baseCurrency;return{activities:await this.importService.getDividends({dataSource:e,symbol:t,userCurrency:a})}}}] Error: activities.15.currency ("EUR") does not match with "AUD" and no exchange rate is available from "EUR" to "AUD"

Environment

Additional context

dtslvr commented 8 months ago

HI @JahFyahh

Can you please try to import only this activity (and not all 15+)? It looks like the error message does not match the displayed activity.

JahFyahh commented 8 months ago

Hi @dtslvr

You are correct, when i only put in that activity it goes through. Is 15 a limit? And if so, is there anyway to increase or get around this? I was able to import all 800+ with the datasource on manual, but then the overview calculations are incorrect.

dtslvr commented 8 months ago

No, there is no limit. Can you narrow down the problematic activity? It might be close to the 15th activity (before or after).

JahFyahh commented 8 months ago

I removed half of the activities and did an import, I now get the following error on the screen:

activities.24 historical exchange rate at 2023-08-18 is not available from "EUR" to "USD"
{
  "accountId": "90ae4a91-33d2-47c2-9616-57f7c2816b6e",
  "comment": "ISIN: IE00B3RBWM25 - Koop 2 @ 100,8 EUR",
  "fee": 1,
  "quantity": 2,
  "type": "BUY",
  "unitPrice": 100.8,
  "currency": "EUR",
  "dataSource": "YAHOO",
  "date": "2023-08-18T09:04:00.000Z",
  "symbol": "VWRD.L"
}

And the log gives me:

st] 142 - 10/31/2023, 8:37:03 PM ERROR [class ImportController{constructor(e,t,a){this.configurationService=e,this.importService=t,this.request=a}async import(t,a){if(!er(this.request.user.permissions,Da)||!er(this.request.user.permissions,Na))throw new e.HttpException((0,At.getReasonPhrase)(At.StatusCodes.FORBIDDEN),At.StatusCodes.FORBIDDEN);let r=this.configurationService.get("MAX_ACTIVITIES_TO_IMPORT");this.configurationService.get("ENABLE_FEATURE_SUBSCRIPTION")&&"Premium"===this.request.user.subscription.type&&(r=Number.MAX_SAFE_INTEGER);const o=this.request.user.Settings.settings.baseCurrency;try{return{activities:await this.importService.import({isDryRun:a,maxActivitiesToImport:r,userCurrency:o,accountsDto:t.accounts??[],activitiesDto:t.activities,userId:this.request.user.id})}}catch(n){throw e.Logger.error(n,Cc),new e.HttpException({error:(0,At.getReasonPhrase)(At.StatusCodes.BAD_REQUEST),message:[n.message]},At.StatusCodes.BAD_REQUEST)}}async gatherDividends(e,t){const a=this.request.user.Settings.settings.baseCurrency;return{activities:await this.importService.getDividends({dataSource:e,symbol:t,userCurrency:a})}}}] Error: activities.24 historical exchange rate at 2023-08-18 is not available from "EUR" to "USD"

Apart from the timestamp and the line Error: activities.24 historical exchange rate at 2023-08-18 is not available from "EUR" to "USD" the error is the same, regardless of the error i get in the screen. I also had a look at yahoo finance and there is historical data on the given time.

If the json has about 3000 lines it gives errors about historical data for "VWRD.L". If it has more then 3000 lines it complains about does not match with "AUD" even thou there is no AUD in play here. And in all cases it gives the same error in the log.

dtslvr commented 8 months ago

I removed half of the activities and did an import, I now get the following error on the screen:

activities.24 historical exchange rate at 2023-08-18 is not available from "EUR" to "USD"
{
  "accountId": "90ae4a91-33d2-47c2-9616-57f7c2816b6e",
  "comment": "ISIN: IE00B3RBWM25 - Koop 2 @ 100,8 EUR",
  "fee": 1,
  "quantity": 2,
  "type": "BUY",
  "unitPrice": 100.8,
  "currency": "EUR",
  "dataSource": "YAHOO",
  "date": "2023-08-18T09:04:00.000Z",
  "symbol": "VWRD.L"
}

This makes sense though as VWRD.L is listed in USD and not in EUR on Yahoo Finance. Check your market data of USDEUR for completeness in the Admin Control panel.

JahFyahh commented 8 months ago

I do not see USDEUR in among the market data, I cannot add it without using the manual option. Not sure what to do here. I cannot find any documentation, where can i find a user manual for all the options?

dtslvr commented 8 months ago

Do you see 1 USD = 0.94XX EUR as an exchange rate in the Admin Control panel?

If yes, go to Market Data and select the preset Currencies (see Filter by...). There you should find USDEUR.

JahFyahh commented 8 months ago

I see it now, thank you. Not sure how to check for completeness, I did a gather all data and this is what i see:

image

image

dtslvr commented 8 months ago

That looks fine. Are you able to import VWRD.L now?

JahFyahh commented 8 months ago

Thank you, that works now. But i'm still having the main issue activities.15.currency ("EUR") does not match with "AUD" and no exchange rate is available from "EUR" to "AUD" when i try to import the entire batch. I have added AUD as currency, but still no luck. If i remove the activity it says its failing on, the one before or after and import, it fails on the next one.

PS: I removed all fees and dividends and still getting the error, eventhou AUD is nowhere in the import json

image

image

Error:

[Nest] 143 - 11/01/2023, 2:08:37 PM ERROR [class ImportController{constructor(e,t,a){this.configurationService=e,this.importService=t,this.request=a}async import(t,a){if(!er(this.request.user.permissions,Da)||!er(this.request.user.permissions,Na))throw new e.HttpException((0,At.getReasonPhrase)(At.StatusCodes.FORBIDDEN),At.StatusCodes.FORBIDDEN);let r=this.configurationService.get("MAX_ACTIVITIES_TO_IMPORT");this.configurationService.get("ENABLE_FEATURE_SUBSCRIPTION")&&"Premium"===this.request.user.subscription.type&&(r=Number.MAX_SAFE_INTEGER);const o=this.request.user.Settings.settings.baseCurrency;try{return{activities:await this.importService.import({isDryRun:a,maxActivitiesToImport:r,userCurrency:o,accountsDto:t.accounts??[],activitiesDto:t.activities,userId:this.request.user.id})}}catch(n){throw e.Logger.error(n,Cc),new e.HttpException({error:(0,At.getReasonPhrase)(At.StatusCodes.BAD_REQUEST),message:[n.message]},At.StatusCodes.BAD_REQUEST)}}async gatherDividends(e,t){const a=this.request.user.Settings.settings.baseCurrency;return{activities:await this.importService.getDividends({dataSource:e,symbol:t,userCurrency:a})}}}] Error: activities.15.currency ("EUR") does not match with "AUD" and no exchange rate is available from "EUR" to "AUD"

dtslvr commented 8 months ago

Can you please attach a JSON file with a single activity to reproduce this AUD issue? Thank you.

JahFyahh commented 8 months ago

After some removing and importing i came to the what i believe is the issue, this activity which is traded in AUD on yahoo finance:

                       {
                           "accountId":  "90ae4a91-33d2-47c2-9616-57f7c2816b6e",
                           "comment":  "ISIN: AU000000FBR4 - Koop 500 @ 0,039 EUR",
                           "fee":  15,
                           "quantity":  500,
                           "type":  "BUY",
                           "unitPrice":  0.039,
                           "currency":  "EUR",
                           "dataSource":  "YAHOO",
                           "date":  "2021-02-16T14:42:00.000Z",
                           "symbol":  "FBR.AX"
                       }

I have AUD in my exchange rates and USDAUD in my market data. I've tried adding EURAUD to market data but it cannot find the symbol. My Ghostfolio base currency is EUR.

JahFyahh commented 7 months ago

@dtslvr I believe there is still something going wrong when importing. I manually added 6 entries, I then exported them for safe keeping. Now when trying to import them again I'm getting the error same kind of error:

oeps something went wrong

The logging gives the error:

[Nest] 149  - 11/25/2023, 11:17:34 AM   ERROR [class ImportController{constructor(e,t,a){this.configurationService=e,this.importService=t,this.request=a}async import(t,a){if(!ar(this.request.user.permissions,Ra)||!ar(this.request.user.permissions,Ba))throw new e.HttpException((0,Ot.getReasonPhrase)(Ot.StatusCodes.FORBIDDEN),Ot.StatusCodes.FORBIDDEN);let r=this.configurationService.get("MAX_ACTIVITIES_TO_IMPORT");this.configurationService.get("ENABLE_FEATURE_SUBSCRIPTION")&&"Premium"===this.request.user.subscription.type&&(r=Number.MAX_SAFE_INTEGER);const o=this.request.user.Settings.settings.baseCurrency;try{return{activities:await this.importService.import({isDryRun:a,maxActivitiesToImport:r,userCurrency:o,accountsDto:t.accounts??[],activitiesDto:t.activities,userId:this.request.user.id})}}catch(n){throw e.Logger.error(n,Sc),new e.HttpException({error:(0,Ot.getReasonPhrase)(Ot.StatusCodes.BAD_REQUEST),message:[n.message]},Ot.StatusCodes.BAD_REQUEST)}}async gatherDividends(e,t){const a=this.request.user.Settings.settings.baseCurrency;return{activities:await this.importService.getDividends({dataSource:e,symbol:t,userCurrency:a})}}}] Error: activities.10.symbol ("avalanche-2") is not valid for the specified data source ("COINGECKO")
[Nest] 149  - 11/25/2023, 11:49:06 AM   ERROR [class ImportController{constructor(e,t,a){this.configurationService=e,this.importService=t,this.request=a}async import(t,a){if(!ar(this.request.user.permissions,Ra)||!ar(this.request.user.permissions,Ba))throw new e.HttpException((0,Ot.getReasonPhrase)(Ot.StatusCodes.FORBIDDEN),Ot.StatusCodes.FORBIDDEN);let r=this.configurationService.get("MAX_ACTIVITIES_TO_IMPORT");this.configurationService.get("ENABLE_FEATURE_SUBSCRIPTION")&&"Premium"===this.request.user.subscription.type&&(r=Number.MAX_SAFE_INTEGER);const o=this.request.user.Settings.settings.baseCurrency;try{return{activities:await this.importService.import({isDryRun:a,maxActivitiesToImport:r,userCurrency:o,accountsDto:t.accounts??[],activitiesDto:t.activities,userId:this.request.user.id})}}catch(n){throw e.Logger.error(n,Sc),new e.HttpException({error:(0,Ot.getReasonPhrase)(Ot.StatusCodes.BAD_REQUEST),message:[n.message]},Ot.StatusCodes.BAD_REQUEST)}}async gatherDividends(e,t){const a=this.request.user.Settings.settings.baseCurrency;return{activities:await this.importService.getDividends({dataSource:e,symbol:t,userCurrency:a})}}}] Error: activities.4.symbol ("flare-networks") is not valid for the specified data source ("COINGECKO")
st] 149  - 11/25/2023, 12:03:21 PM   ERROR [class ImportController{constructor(e,t,a){this.configurationService=e,this.importService=t,this.request=a}async import(t,a){if(!ar(this.request.user.permissions,Ra)||!ar(this.request.user.permissions,Ba))throw new e.HttpException((0,Ot.getReasonPhrase)(Ot.StatusCodes.FORBIDDEN),Ot.StatusCodes.FORBIDDEN);let r=this.configurationService.get("MAX_ACTIVITIES_TO_IMPORT");this.configurationService.get("ENABLE_FEATURE_SUBSCRIPTION")&&"Premium"===this.request.user.subscription.type&&(r=Number.MAX_SAFE_INTEGER);const o=this.request.user.Settings.settings.baseCurrency;try{return{activities:await this.importService.import({isDryRun:a,maxActivitiesToImport:r,userCurrency:o,accountsDto:t.accounts??[],activitiesDto:t.activities,userId:this.request.user.id})}}catch(n){throw e.Logger.error(n,Sc),new e.HttpException({error:(0,Ot.getReasonPhrase)(Ot.StatusCodes.BAD_REQUEST),message:[n.message]},Ot.StatusCodes.BAD_REQUEST)}}async gatherDividends(e,t){const a=this.request.user.Settings.settings.baseCurrency;return{activities:await this.importService.getDividends({dataSource:e,symbol:t,userCurrency:a})}}}] Error: activities.4.symbol ("stellar") is not valid for the specified data source ("COINGECKO")

[Nest] 149  - 11/25/2023, 12:03:21 PM   ERROR [CoinGeckoService] RequestError: The operation was aborted

I've checked coingecko and the symbols are correct.

dtslvr commented 7 months ago

Hi @JahFyahh

I have created an activity for Flare, exported it (flare-networks) and reimported it again without any issue.

ERROR [CoinGeckoService] RequestError: The operation was aborted

This error indicates that you may have reached the rate limit of the CoinGecko Api. Can you please verify this?

JahFyahh commented 7 months ago

@dtslvr How can i verify this? I do not see it anywhere in the logging. When i run a api request from my vsCode i get no errors.

The request i ran:

$apiUrl = "https://api.coingecko.com/api/v3/coins/list"
$response = Invoke-RestMethod -Uri $apiUrl -Method Get -ErrorAction Stop

Here is my export if that helps: Coinbase-manualEntries-export-202311251200.json

JahFyahh commented 7 months ago

@dtslvr Is there a way to set an CoinGecko API key somewhere for calls?

dtslvr commented 7 months ago

Here is my export if that helps

I was able to reproduce it. The request to CoinGecko is sometimes taking more than 2 seconds, thus the error The operation was aborted. I have improved the error log in #2688.

Is there a way to set an CoinGecko API key somewhere for calls?

Not yet. I have created a crowdfunding campaign to set the CoinGecko API key: https://www.buymeacoffee.com/ghostfolio/w/34190

christoph93 commented 7 months ago

I also have this issue. I think we just need a way to convert different currencies, not just USD. Here is what I found:

activities.0.currency ("EUR") does not match with "CLP" and no exchange rate is available from "EUR" to "CLP"

{
  "accountId": "32a1c44e-bbe8-49c0-b3ce-90ee7da84e3b",
  "comment": null,
  "fee": 4.9,
  "quantity": 5,
  "type": "BUY",
  "unitPrice": 91.04,
  "currency": "EUR",
  "dataSource": "YAHOO",
  "date": "2023-01-30T00:00:00.000Z",
  "symbol": "US02079K3059"
}

Lookup ISIN on Yahoo Finance: image

It is indeed listed as CLP.

Add CLP and any other currency that is giving an error: image

Issue persists.

dtslvr commented 7 months ago

activities.0.currency ("EUR") does not match with "CLP" and no exchange rate is available from "EUR" to "CLP"

@christoph93 can you verify if you have the exchange rate for that specific date (2023-01-30)?

christoph93 commented 7 months ago

activities.0.currency ("EUR") does not match with "CLP" and no exchange rate is available from "EUR" to "CLP"

@christoph93 can you verify if you have the exchange rate for that specific date (2023-01-30)?

Yes, USDCLP is there.

image

dtslvr commented 7 months ago

Yes, USDCLP is there.

When you inspect the request of this dialog via DevTools, how do the marketData entries look like, can you show the first three entries?

christoph93 commented 7 months ago

First 3 entries for marketData from the response

{
    "marketData": [
        {
            "createdAt": "2023-11-25T15:04:51.903Z",
            "dataSource": "YAHOO",
            "date": "2013-11-25T00:00:00.000Z",
            "id": "ecce5ba5-ad7c-4f5a-8add-7b85cc759114",
            "marketPrice": 518.5,
            "state": "CLOSE",
            "symbol": "USDCLP"
        },
        {
            "createdAt": "2023-11-25T15:04:51.903Z",
            "dataSource": "YAHOO",
            "date": "2013-11-26T00:00:00.000Z",
            "id": "1f8e390d-33e6-4cce-b08b-b545b7467635",
            "marketPrice": 520.099976,
            "state": "CLOSE",
            "symbol": "USDCLP"
        },
        {
            "createdAt": "2023-11-25T15:04:51.903Z",
            "dataSource": "YAHOO",
            "date": "2013-11-27T00:00:00.000Z",
            "id": "c00978df-1d6c-4cd0-b60f-b890d743d3a8",
            "marketPrice": 521.5,
            "state": "CLOSE",
            "symbol": "USDCLP"
        },
        ...
    ]
}

I see that from 2013-11, the first 23 days are red squares.

JahFyahh commented 7 months ago

@dtslvr Thank you for adding the proper log output, that does make troubleshooting much easier.

Not yet. I have created a crowdfunding campaign to set the CoinGecko API key: https://www.buymeacoffee.com/ghostfolio/w/34190

This only works with CC, which I do not have. Any PayPall options, i do wish to support?

I created a workaround using a powershell script that adds activities using the ghostfolio API, and whenever it fails, it retries it a couple seconds later after which it does add the activity. Takes a bit longer but it prevents manual actions ;-D

dtslvr commented 7 months ago

First 3 entries for marketData from the response

That looks fine. I thought there might be an issue with the date. Is it possible for you @christoph93 to run the source code locally and connect to your database (create a backup first)? Debugging seems to be the only way to identify the problem.

christoph93 commented 7 months ago

First 3 entries for marketData from the response

That looks fine. I thought there might be an issue with the date. Is it possible for you @christoph93 to run the source code locally and connect to your database (create a backup first)? Debugging seems to be the only way to identify the problem.

from exchange-rate-data.service.ts : hasCurrencyPair

It's passing the currencies EUR and CLP, but the currency pair EURCLP does not exist, so this fails every time.

image

If I add the activity manually, put the ISIN US02079K3059 and select GOOGLCL.SN from the suggestion, the currency is automatically set to CLP. If I change it back to EUR, the post still sends the currency as CLP and it is added without errors.