diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e02e7a9..5a5a9917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed + +- Improved the symbol lookup in the _Trackinsight_ data enhancer for asset profile data + +### Fixed + +- Handled an exception in the benchmark service related to unnamed asset profiles + +## 2.142.0 - 2025-02-28 + ### Added - Extended the export functionality by the platforms +- Extended the portfolio snapshot in the portfolio calculator by the `createdAt` timestamp - Extended the _Trackinsight_ data enhancer for asset profile data by `cusip` - Added _Storybook_ to the build process +### Changed + +- Upgraded `eslint` dependencies + ## 2.141.0 - 2025-02-25 ### Added diff --git a/apps/api/src/app/endpoints/public/public.controller.ts b/apps/api/src/app/endpoints/public/public.controller.ts index 7488e420..bdcd86af 100644 --- a/apps/api/src/app/endpoints/public/public.controller.ts +++ b/apps/api/src/app/endpoints/public/public.controller.ts @@ -57,7 +57,7 @@ export class PublicController { } const [ - { holdings, markets }, + { createdAt, holdings, markets }, { performance: performance1d }, { performance: performanceMax }, { performance: performanceYtd } @@ -81,6 +81,7 @@ export class PublicController { }); const publicPortfolioResponse: PublicPortfolioResponse = { + createdAt, hasDetails, markets, alias: access.alias, diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index e9c4fdcc..54d80a95 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -176,6 +176,7 @@ export abstract class PortfolioCalculator { if (!transactionPoints.length) { return { activitiesCount: 0, + createdAt: new Date(), currentValueInBaseCurrency: new Big(0), errors: [], hasErrors: false, diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts index 209a0efd..059b8544 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts @@ -104,6 +104,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { activitiesCount: this.activities.filter(({ type }) => { return ['BUY', 'SELL'].includes(type); }).length, + createdAt: new Date(), errors: [], historicalData: [], totalLiabilitiesWithCurrencyEffect: new Big(0), diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 797d0449..c3e46d50 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -105,6 +105,7 @@ export class PortfolioController { const { accounts, + createdAt, hasErrors, holdings, markets, @@ -233,6 +234,7 @@ export class PortfolioController { return { accounts, + createdAt, hasError, holdings, platforms, diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 811c59f9..d4dc42d9 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -376,7 +376,7 @@ export class PortfolioService { currency: userCurrency }); - const { currentValueInBaseCurrency, hasErrors, positions } = + const { createdAt, currentValueInBaseCurrency, hasErrors, positions } = await portfolioCalculator.getSnapshot(); const cashDetails = await this.accountService.getCashDetails({ @@ -617,6 +617,7 @@ export class PortfolioService { return { accounts, + createdAt, hasErrors, holdings, markets, diff --git a/apps/api/src/services/benchmark/benchmark.service.ts b/apps/api/src/services/benchmark/benchmark.service.ts index 57105da7..95cb9e5d 100644 --- a/apps/api/src/services/benchmark/benchmark.service.ts +++ b/apps/api/src/services/benchmark/benchmark.service.ts @@ -133,7 +133,9 @@ export class BenchmarkService { symbol }; }) - .sort((a, b) => a.name.localeCompare(b.name)); + .sort((a, b) => { + return a.name?.localeCompare(b?.name) ?? 0; + }); } public async addBenchmark({ diff --git a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts index 32b49408..8b885c01 100644 --- a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts @@ -192,7 +192,10 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { .then((jsonRes) => { if ( jsonRes['results']?.['count'] === 1 || - jsonRes['results']?.['docs']?.[0]?.['ticker'] === symbol + // Allow exact match + jsonRes['results']?.['docs']?.[0]?.['ticker'] === symbol || + // Allow EXCHANGE:SYMBOL + jsonRes['results']?.['docs']?.[0]?.['ticker']?.endsWith(`:${symbol}`) ) { return jsonRes['results']['docs'][0]['ticker']; } diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index 41961edd..54ea8854 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -260,6 +260,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { this.platforms = {}; this.portfolioDetails = { accounts: {}, + createdAt: undefined, holdings: {}, platforms: {}, summary: undefined diff --git a/libs/common/src/lib/interfaces/portfolio-details.interface.ts b/libs/common/src/lib/interfaces/portfolio-details.interface.ts index e455f73c..746736f6 100644 --- a/libs/common/src/lib/interfaces/portfolio-details.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-details.interface.ts @@ -14,6 +14,7 @@ export interface PortfolioDetails { valueInPercentage?: number; }; }; + createdAt: Date; holdings: { [symbol: string]: PortfolioPosition }; markets?: { [key in Market]: { diff --git a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts index dc6e5758..7a98e3c8 100644 --- a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts @@ -32,6 +32,7 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { } interface PublicPortfolioResponseV1 { + createdAt: Date; performance: { '1d': { relativeChange: number; diff --git a/libs/common/src/lib/models/portfolio-snapshot.ts b/libs/common/src/lib/models/portfolio-snapshot.ts index d0c4c66e..e30585b8 100644 --- a/libs/common/src/lib/models/portfolio-snapshot.ts +++ b/libs/common/src/lib/models/portfolio-snapshot.ts @@ -11,6 +11,8 @@ import { Transform, Type } from 'class-transformer'; export class PortfolioSnapshot { activitiesCount: number; + createdAt: Date; + @Transform(transformToBig, { toClassOnly: true }) @Type(() => Big) currentValueInBaseCurrency: Big; diff --git a/package-lock.json b/package-lock.json index 47bd15e2..601c8f42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.141.0", + "version": "2.142.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.141.0", + "version": "2.142.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { @@ -141,9 +141,9 @@ "cypress": "6.2.1", "eslint": "9.18.0", "eslint-config-prettier": "9.1.0", - "eslint-plugin-cypress": "3.2.0", + "eslint-plugin-cypress": "4.1.0", "eslint-plugin-import": "2.31.0", - "eslint-plugin-storybook": "0.10.2", + "eslint-plugin-storybook": "0.11.2", "husky": "9.1.7", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0", @@ -17087,42 +17087,26 @@ "license": "MIT" }, "node_modules/eslint-plugin-cypress": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-3.2.0.tgz", - "integrity": "sha512-HaxMz6BoU4ay+K4WrG9ZJC1NdX06FqSlAwtRDStjM0ORFT7zCNPNuRJ+kUPc17Rt2AMUBSqeD9L0zTR3uZhPpw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-4.1.0.tgz", + "integrity": "sha512-JhqkMY02mw74USwK9OFhectx3YSj6Co1NgWBxlGdKvlqiAp9vdEuQqt33DKGQFvvGS/NWtduuhWXWNnU29xDSg==", "dev": true, "license": "MIT", "dependencies": { - "globals": "^13.20.0" + "globals": "^15.11.0" }, "peerDependencies": { - "eslint": ">=7" + "eslint": ">=9" } }, "node_modules/eslint-plugin-cypress/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", "dev": true, "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-cypress/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -17216,9 +17200,9 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.10.2.tgz", - "integrity": "sha512-iOrFJfyLgRLIbWDLbbs3J4yrknvIB+uiZ8KGqGOEXTL7/BGuBMZLiIU9Q4Pm/VYJrHN4JqmMtwCSrAemHL2nFg==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.11.2.tgz", + "integrity": "sha512-0Z4DUklJrC+GHjCRXa7PYfPzWC15DaVnwaOYenpgXiCEijXPZkLKCms+rHhtoRcWccP7Z8DpOOaP1gc3P9oOwg==", "dev": true, "license": "MIT", "dependencies": { @@ -17230,7 +17214,7 @@ "node": ">= 18" }, "peerDependencies": { - "eslint": ">=6" + "eslint": ">=8" } }, "node_modules/eslint-scope": { diff --git a/package.json b/package.json index 213de684..d49ce503 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.141.0", + "version": "2.142.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", @@ -187,9 +187,9 @@ "cypress": "6.2.1", "eslint": "9.18.0", "eslint-config-prettier": "9.1.0", - "eslint-plugin-cypress": "3.2.0", + "eslint-plugin-cypress": "4.1.0", "eslint-plugin-import": "2.31.0", - "eslint-plugin-storybook": "0.10.2", + "eslint-plugin-storybook": "0.11.2", "husky": "9.1.7", "jest": "29.7.0", "jest-environment-jsdom": "29.7.0",