diff --git a/CHANGELOG.md b/CHANGELOG.md index 297677b966..6453580dcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added a periodic refresh of the numbers on the home, analysis and accounts pages while the page is visible, briefly coloring changed values green or red, moving the chart of the home page without redrawing it, with a live indicator in the header - Added a link to the holdings tab in the holdings table on the overview tab of the home page (experimental) ### Changed +- Colored the changes of the performance in green or red only while the market of a holding is open (in gray while it is closed) and highlighted a change in percent together with its absolute change - Improved the market price in the benchmark detail dialog by adding the currency and reducing the precision on mobile - Improved the style of the close button in the dialog footer component - Improved the style of the close button in the dialog header component diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index a8a3fc6740..d963570b4e 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -679,9 +679,24 @@ export class PortfolioService { }; }); - const symbolProfiles = await this.symbolProfileService.getSymbolProfiles( - assetProfileIdentifiers - ); + const [symbolProfiles, quotes] = await Promise.all([ + this.symbolProfileService.getSymbolProfiles(assetProfileIdentifiers), + // The quotes are cached, they only provide the state of the markets + this.dataProviderService + .getQuotes({ + items: positions + .filter(({ quantity }) => { + return !quantity.eq(0); + }) + .map(({ dataSource, symbol }) => { + return { dataSource, symbol }; + }), + subscriptionType: user.subscription?.type + }) + .catch(() => { + return {} as Awaited>; + }) + ]); const cashSymbolProfiles = this.getCashSymbolProfiles(cashDetails); symbolProfiles.push(...cashSymbolProfiles); @@ -764,6 +779,9 @@ export class PortfolioService { markets, marketsAdvanced, marketPrice, + marketState: + quotes[getAssetProfileIdentifier({ dataSource, symbol })] + ?.marketState, tags, allocationInPercentage: filteredValueInBaseCurrency.eq(0) ? 0 @@ -1223,11 +1241,32 @@ export class PortfolioService { subscriptionType: user.subscription?.type }); - const { errors, hasErrors, historicalData } = + const { errors, hasErrors, historicalData, positions } = await portfolioCalculator.getSnapshot(); const { endDate, startDate } = getIntervalFromDateRange({ dateRange }); + // The quotes are cached, they only provide the state of the markets + const isMarketOpen = await this.dataProviderService + .getQuotes({ + items: positions + .filter(({ quantity }) => { + return !quantity.eq(0); + }) + .map(({ dataSource, symbol }) => { + return { dataSource, symbol }; + }), + subscriptionType: user.subscription?.type + }) + .then((quotes) => { + return Object.values(quotes).some(({ marketState }) => { + return marketState === 'open'; + }); + }) + .catch(() => { + return undefined; + }); + const { chart: chartOfDateRange } = await portfolioCalculator.getPerformance({ end: endDate, @@ -1271,6 +1310,7 @@ export class PortfolioService { chart, errors, hasErrors, + isMarketOpen, dateOfFirstActivity: parseDate(historicalData[0]?.date), performance: { dividendInBaseCurrency, diff --git a/apps/client/src/app/components/header/header.component.html b/apps/client/src/app/components/header/header.component.html index 88ab9f09fc..eb7a578f4a 100644 --- a/apps/client/src/app/components/header/header.component.html +++ b/apps/client/src/app/components/header/header.component.html @@ -12,6 +12,7 @@ +