diff --git a/CHANGELOG.md b/CHANGELOG.md index c021b89b44..c432086ba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,14 +15,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Extended the emergency fund dialog in the portfolio summary to include the currency suffix - Migrated the client to the code generated by the new `prisma-client` generator of _Prisma_ +- Migrated from `lodash` to `lodash-es` ### Fixed - Fixed the colors of the investment chart component (dark mode) - Fixed the colors of the charts for the light color scheme (dark mode) -- Fixed the value column of the top holdings on the allocations page in the _Presenter View_ +- Fixed the allocation column of the top holdings of ETFs on the allocations page after a change of the user settings +- Fixed the value column of the top holdings of ETFs on the allocations page in the _Presenter View_ - Fixed the projected sustainable retirement income on the _FIRE_ page for a projected total amount of zero (experimental) - Fixed the emergency fund in the portfolio summary for redacted values +- Fixed the redaction of `valueInBaseCurrency` in the holdings of ETFs in a restricted view ## 3.75.0 - 2026-09-28 diff --git a/apps/api/src/app/account-balance/account-balance.service.ts b/apps/api/src/app/account-balance/account-balance.service.ts index 3823498df3..ad990c66bd 100644 --- a/apps/api/src/app/account-balance/account-balance.service.ts +++ b/apps/api/src/app/account-balance/account-balance.service.ts @@ -24,7 +24,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter'; import { AccountBalance, Prisma } from '@prisma/client'; import { Big } from 'big.js'; import { endOfToday, format, min, parseISO } from 'date-fns'; -import { groupBy } from 'lodash'; +import { groupBy } from 'lodash-es'; @Injectable() export class AccountBalanceService { diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts index 59315602a8..d1a895a5a3 100644 --- a/apps/api/src/app/account/account.service.ts +++ b/apps/api/src/app/account/account.service.ts @@ -28,7 +28,7 @@ import { } from '@prisma/client'; import { Big } from 'big.js'; import { format } from 'date-fns'; -import { groupBy, isNil } from 'lodash'; +import { groupBy, isNil } from 'lodash-es'; import { CashDetails } from './interfaces/cash-details.interface'; diff --git a/apps/api/src/app/activities/activities-filter.dto.ts b/apps/api/src/app/activities/activities-filter.dto.ts index 0c56aacc85..17385148cc 100644 --- a/apps/api/src/app/activities/activities-filter.dto.ts +++ b/apps/api/src/app/activities/activities-filter.dto.ts @@ -5,7 +5,7 @@ import { DateRange } from '@ghostfolio/common/types'; import { Type as ActivityType } from '@prisma/client'; import { Transform, TransformFnParams } from 'class-transformer'; import { IsEnum, IsOptional, Matches } from 'class-validator'; -import { isString } from 'lodash'; +import { isString } from 'lodash-es'; export class ActivitiesFilterDto extends FilterDto { @IsEnum(ActivityType, { each: true }) diff --git a/apps/api/src/app/activities/activities.service.ts b/apps/api/src/app/activities/activities.service.ts index ecc2296a90..59d1ba9c33 100644 --- a/apps/api/src/app/activities/activities.service.ts +++ b/apps/api/src/app/activities/activities.service.ts @@ -60,7 +60,7 @@ import { Type as ActivityType } from '@prisma/client'; import { Big } from 'big.js'; -import { groupBy, uniqBy } from 'lodash'; +import { groupBy, uniqBy } from 'lodash-es'; import { randomUUID } from 'node:crypto'; @Injectable() diff --git a/apps/api/src/app/asset/asset.controller.ts b/apps/api/src/app/asset/asset.controller.ts index 49afb43d71..352d3e18b1 100644 --- a/apps/api/src/app/asset/asset.controller.ts +++ b/apps/api/src/app/asset/asset.controller.ts @@ -5,7 +5,7 @@ import type { AssetResponse } from '@ghostfolio/common/interfaces'; import { Controller, Get, Param, UseInterceptors } from '@nestjs/common'; import { DataSource } from '@prisma/client'; -import { pick } from 'lodash'; +import { pick } from 'lodash-es'; @Controller('asset') export class AssetController { diff --git a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts index e9aad6911f..4a2507e697 100644 --- a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts +++ b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts @@ -29,7 +29,7 @@ import { MarketDataPreset } from '@ghostfolio/common/types'; import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client'; -import { groupBy } from 'lodash'; +import { groupBy } from 'lodash-es'; @Injectable() export class AssetProfilesService { diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts index beff7ffa60..908db59b7f 100644 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts @@ -14,7 +14,7 @@ import { DateRange } from '@ghostfolio/common/types'; import { Injectable, Logger } from '@nestjs/common'; import { format, isSameDay } from 'date-fns'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; @Injectable() export class BenchmarksService { diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts index ec0eeeb868..4042241449 100644 --- a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts @@ -40,7 +40,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { Big } from 'big.js'; import { addMilliseconds, isBefore } from 'date-fns'; -import { isEmpty } from 'lodash'; +import { isEmpty } from 'lodash-es'; @Injectable() export class GhostfolioService { diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index 35db209935..db08f62052 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -11,7 +11,7 @@ import { import { Injectable } from '@nestjs/common'; import { Platform, Prisma, Type as ActivityType } from '@prisma/client'; -import { groupBy, uniqBy } from 'lodash'; +import { groupBy, uniqBy } from 'lodash-es'; @Injectable() export class ExportService { diff --git a/apps/api/src/app/export/get-export.dto.ts b/apps/api/src/app/export/get-export.dto.ts index 5fc3c81baa..524c357112 100644 --- a/apps/api/src/app/export/get-export.dto.ts +++ b/apps/api/src/app/export/get-export.dto.ts @@ -2,7 +2,7 @@ import { ActivitiesFilterDto } from '@ghostfolio/api/app/activities/activities-f import { Transform, TransformFnParams } from 'class-transformer'; import { IsOptional, IsUUID } from 'class-validator'; -import { isString } from 'lodash'; +import { isString } from 'lodash-es'; export class GetExportDto extends ActivitiesFilterDto { @IsOptional() diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 5f202f4ac4..5afa4c33dd 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -47,7 +47,7 @@ import { Account, DataSource, Prisma } from '@prisma/client'; import { Big } from 'big.js'; import { isISIN } from 'class-validator'; import { isSameSecond, parseISO } from 'date-fns'; -import { omit, uniqBy } from 'lodash'; +import { omit, uniqBy } from 'lodash-es'; import { randomUUID } from 'node:crypto'; import { ImportValidationError } from './errors/import-validation.error'; diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index a0759b1698..2a7382446c 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -26,7 +26,7 @@ import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { MarketData } from '@prisma/client'; import { subDays } from 'date-fns'; -import { isNil } from 'lodash'; +import { isNil } from 'lodash-es'; @Injectable() export class InfoService { diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index d868b65b3f..e03b530258 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -72,7 +72,7 @@ import { startOfYear, subDays } from 'date-fns'; -import { groupBy, sortBy, uniqBy } from 'lodash'; +import { groupBy, sortBy, uniqBy } from 'lodash-es'; export abstract class PortfolioCalculator { protected static readonly ENABLE_LOGGING = false; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts index 2b7b4e6dce..c0b7337350 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts @@ -32,7 +32,7 @@ import { isBefore, isThisYear } from 'date-fns'; -import { sum } from 'lodash'; +import { sum } from 'lodash-es'; export class RoaiPortfolioCalculator extends PortfolioCalculator { protected calculateOverallPerformance( diff --git a/apps/api/src/app/portfolio/current-rate.service.ts b/apps/api/src/app/portfolio/current-rate.service.ts index 79363c6344..80d37259a4 100644 --- a/apps/api/src/app/portfolio/current-rate.service.ts +++ b/apps/api/src/app/portfolio/current-rate.service.ts @@ -14,7 +14,7 @@ import { import { Injectable } from '@nestjs/common'; import { isBefore, isToday } from 'date-fns'; -import { isEmpty, uniqBy } from 'lodash'; +import { isEmpty, uniqBy } from 'lodash-es'; import { GetValueObject } from './interfaces/get-value-object.interface'; import { GetValuesObject } from './interfaces/get-values-object.interface'; diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 7350f5fc01..72c5b5946a 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -104,7 +104,7 @@ import { set, startOfDay } from 'date-fns'; -import { groupBy } from 'lodash'; +import { groupBy } from 'lodash-es'; import { PortfolioCalculator } from './calculator/portfolio-calculator'; import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory'; diff --git a/apps/api/src/app/symbol/symbol.controller.ts b/apps/api/src/app/symbol/symbol.controller.ts index 9d8abb47ba..cf26d39d54 100644 --- a/apps/api/src/app/symbol/symbol.controller.ts +++ b/apps/api/src/app/symbol/symbol.controller.ts @@ -25,7 +25,7 @@ import { AuthGuard } from '@nestjs/passport'; import { DataSource } from '@prisma/client'; import { isValid, parseISO } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; -import { isEmpty } from 'lodash'; +import { isEmpty } from 'lodash-es'; import { GetLookupDto } from './get-lookup.dto'; import { SymbolService } from './symbol.service'; diff --git a/apps/api/src/app/user/user.controller.ts b/apps/api/src/app/user/user.controller.ts index ae9e7d680b..32ac353351 100644 --- a/apps/api/src/app/user/user.controller.ts +++ b/apps/api/src/app/user/user.controller.ts @@ -53,7 +53,7 @@ import { AuthGuard } from '@nestjs/passport'; import { Throttle } from '@nestjs/throttler'; import { User as UserModel } from '@prisma/client'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; -import { merge, size } from 'lodash'; +import { merge, size } from 'lodash-es'; import { UserService } from './user.service'; diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index a74e32ca93..2e54a7ddc3 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -46,7 +46,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter'; import { InjectThrottlerStorage, ThrottlerStorage } from '@nestjs/throttler'; import { Prisma, Role, User } from '@prisma/client'; import { differenceInDays, subDays } from 'date-fns'; -import { isNil, without } from 'lodash'; +import { isNil, without } from 'lodash-es'; import { createHmac } from 'node:crypto'; @Injectable() diff --git a/apps/api/src/dtos/filter.dto.ts b/apps/api/src/dtos/filter.dto.ts index 09206984ab..84d9ffc0d0 100644 --- a/apps/api/src/dtos/filter.dto.ts +++ b/apps/api/src/dtos/filter.dto.ts @@ -9,7 +9,7 @@ import { IsUUID, MaxLength } from 'class-validator'; -import { isString } from 'lodash'; +import { isString } from 'lodash-es'; export class FilterDto { @IsOptional() diff --git a/apps/api/src/helper/activity.helper.ts b/apps/api/src/helper/activity.helper.ts index b281106c77..f7cb20505e 100644 --- a/apps/api/src/helper/activity.helper.ts +++ b/apps/api/src/helper/activity.helper.ts @@ -5,7 +5,7 @@ import { import { Prisma, Type as ActivityType } from '@prisma/client'; import { endOfToday, isAfter } from 'date-fns'; -import { uniqBy } from 'lodash'; +import { uniqBy } from 'lodash-es'; export const WHERE_ACTIVITY_NOT_DRAFT: Prisma.OrderWhereInput = { tags: { diff --git a/apps/api/src/helper/object.helper.spec.ts b/apps/api/src/helper/object.helper.spec.ts index 415851a6c6..ca487ed8f7 100644 --- a/apps/api/src/helper/object.helper.spec.ts +++ b/apps/api/src/helper/object.helper.spec.ts @@ -1,4 +1,5 @@ import { DEFAULT_REDACTED_PATHS } from '@ghostfolio/common/config'; +import { PortfolioDetails } from '@ghostfolio/common/interfaces'; import { query, redactPaths } from './object.helper'; @@ -133,6 +134,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.044900865255793135, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { @@ -149,7 +151,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.3183066634822068, grossPerformancePercentWithCurrencyEffect: 0.3183066634822068, grossPerformanceWithCurrencyEffect: 2665.5, - holdings: [], investment: 0.060265768702233234, name: 'Apple Inc', netPerformance: 2664.5, @@ -185,6 +186,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.026912563036519527, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { code: 'DE', weight: 1, continent: 'Europe', name: 'Germany' } @@ -196,7 +198,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.3719230057375532, grossPerformancePercentWithCurrencyEffect: 0.2650716044872953, grossPerformanceWithCurrencyEffect: 1386.429698978564, - holdings: [], investment: 0.03471025137190358, name: 'Allianz SE', netPerformance: 1789.1095737558583, @@ -232,6 +233,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.07646101417126275, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { @@ -248,7 +250,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.8594552890963852, grossPerformancePercentWithCurrencyEffect: 0.8594552890963852, grossPerformanceWithCurrencyEffect: 8689.05, - holdings: [], investment: 0.07275900505029173, name: 'Amazon.com, Inc.', netPerformance: 8608.26, @@ -290,6 +291,7 @@ describe('redactAttributes', () => { ], allocationInPercentage: 0.15042891393226654, assetClass: 'LIQUIDITY', + assetProfile: { holdings: [] }, assetSubClass: 'CRYPTOCURRENCY', countries: [], dataSource: 'COINGECKO', @@ -299,7 +301,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 17.4925166352, grossPerformancePercentWithCurrencyEffect: 17.4925166352, grossPerformanceWithCurrencyEffect: 34985.0332704, - holdings: [], investment: 0.014393543993846005, name: 'Bitcoin', netPerformance: 34955.1332704, @@ -341,6 +342,7 @@ describe('redactAttributes', () => { ], allocationInPercentage: 0.009076749759365777, assetClass: 'FIXED_INCOME', + assetProfile: { holdings: [] }, assetSubClass: 'BOND', countries: [], dataSource: 'MANUAL', @@ -350,7 +352,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0, grossPerformancePercentWithCurrencyEffect: -0.06153834320225245, grossPerformanceWithCurrencyEffect: -125.68932723700505, - holdings: [], investment: 0.016060638243523776, name: 'Bondora Go & Grow', netPerformance: 0, @@ -392,6 +393,7 @@ describe('redactAttributes', () => { ], allocationInPercentage: 0.09095764645669335, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'ETF', countries: [ { @@ -474,7 +476,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.27579517683678895, grossPerformancePercentWithCurrencyEffect: 0.458553421589667, grossPerformanceWithCurrencyEffect: 5322.44900391902, - holdings: [], investment: 0.13551383737034509, name: 'frankly Extreme 95 Index', netPerformance: 3533.389614611676, @@ -510,6 +511,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.05222646409742627, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { @@ -526,7 +528,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.7865431171216295, grossPerformancePercentWithCurrencyEffect: 0.7865431171216295, grossPerformanceWithCurrencyEffect: 5653.2, - holdings: [], investment: 0.051726079050684395, name: 'Microsoft Corporation', netPerformance: 5653.2, @@ -562,6 +563,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.1589050142378352, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { @@ -578,7 +580,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 17.184314638161936, grossPerformancePercentWithCurrencyEffect: 17.184314638161936, grossPerformanceWithCurrencyEffect: 36920.500000005, - holdings: [], investment: 0.01546226463535309, name: 'Tesla, Inc.', netPerformance: 36890.500000005, @@ -614,6 +615,85 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.057358979326040366, assetClass: 'EQUITY', + assetProfile: { + holdings: [ + { + allocationInPercentage: 0.06099941636982121, + name: 'APPLE INC', + valueInBaseCurrency: 860.2442693554036 + }, + { + allocationInPercentage: 0.05862464529372787, + name: 'MICROSOFT CORP', + valueInBaseCurrency: 826.7540602547973 + }, + { + allocationInPercentage: 0.05156070760128074, + name: 'NVIDIA CORP', + valueInBaseCurrency: 727.1348789470617 + }, + { + allocationInPercentage: 0.03301535551128066, + name: 'AMAZON COM INC', + valueInBaseCurrency: 465.5990510978355 + }, + { + allocationInPercentage: 0.01962204914568647, + name: 'FACEBOOK CLASS A INC', + valueInBaseCurrency: 276.71994807704345 + }, + { + allocationInPercentage: 0.01902835637666313, + name: 'ALPHABET INC CLASS A', + valueInBaseCurrency: 268.34739580189176 + }, + { + allocationInPercentage: 0.01555676306627245, + name: 'ALPHABET INC CLASS C', + valueInBaseCurrency: 219.3892511421072 + }, + { + allocationInPercentage: 0.01463100485016827, + name: 'BERKSHIRE HATHAWAY INC CLASS B', + valueInBaseCurrency: 206.33374589949804 + }, + { + allocationInPercentage: 0.01403731208114493, + name: 'BROADCOM INC', + valueInBaseCurrency: 197.96119362434638 + }, + { + allocationInPercentage: 0.01297067761476403, + name: 'ELI LILLY', + valueInBaseCurrency: 182.91898106220972 + }, + { + allocationInPercentage: 0.0118637927911612, + name: 'TESLA INC', + valueInBaseCurrency: 167.30913783735082 + }, + { + allocationInPercentage: 0.01152166475477487, + name: 'JPMORGAN CHASE & CO', + valueInBaseCurrency: 162.48427720421262 + }, + { + allocationInPercentage: 0.0100324015375638, + name: 'EXXON MOBIL CORP', + valueInBaseCurrency: 141.4819426834935 + }, + { + allocationInPercentage: 0.01000221376964736, + name: 'UNITEDHEALTH GROUP INC', + valueInBaseCurrency: 141.0562196864519 + }, + { + allocationInPercentage: 0.007818631890358146, + name: 'VISA INC CLASS A', + valueInBaseCurrency: 110.26225623377576 + } + ] + }, assetSubClass: 'ETF', countries: [ { @@ -660,83 +740,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.8832083851170418, grossPerformancePercentWithCurrencyEffect: 0.8832083851170418, grossPerformanceWithCurrencyEffect: 5856.3, - holdings: [ - { - allocationInPercentage: 0.06099941636982121, - name: 'APPLE INC', - valueInBaseCurrency: 860.2442693554036 - }, - { - allocationInPercentage: 0.05862464529372787, - name: 'MICROSOFT CORP', - valueInBaseCurrency: 826.7540602547973 - }, - { - allocationInPercentage: 0.05156070760128074, - name: 'NVIDIA CORP', - valueInBaseCurrency: 727.1348789470617 - }, - { - allocationInPercentage: 0.03301535551128066, - name: 'AMAZON COM INC', - valueInBaseCurrency: 465.5990510978355 - }, - { - allocationInPercentage: 0.01962204914568647, - name: 'FACEBOOK CLASS A INC', - valueInBaseCurrency: 276.71994807704345 - }, - { - allocationInPercentage: 0.01902835637666313, - name: 'ALPHABET INC CLASS A', - valueInBaseCurrency: 268.34739580189176 - }, - { - allocationInPercentage: 0.01555676306627245, - name: 'ALPHABET INC CLASS C', - valueInBaseCurrency: 219.3892511421072 - }, - { - allocationInPercentage: 0.01463100485016827, - name: 'BERKSHIRE HATHAWAY INC CLASS B', - valueInBaseCurrency: 206.33374589949804 - }, - { - allocationInPercentage: 0.01403731208114493, - name: 'BROADCOM INC', - valueInBaseCurrency: 197.96119362434638 - }, - { - allocationInPercentage: 0.01297067761476403, - name: 'ELI LILLY', - valueInBaseCurrency: 182.91898106220972 - }, - { - allocationInPercentage: 0.0118637927911612, - name: 'TESLA INC', - valueInBaseCurrency: 167.30913783735082 - }, - { - allocationInPercentage: 0.01152166475477487, - name: 'JPMORGAN CHASE & CO', - valueInBaseCurrency: 162.48427720421262 - }, - { - allocationInPercentage: 0.0100324015375638, - name: 'EXXON MOBIL CORP', - valueInBaseCurrency: 141.4819426834935 - }, - { - allocationInPercentage: 0.01000221376964736, - name: 'UNITEDHEALTH GROUP INC', - valueInBaseCurrency: 141.0562196864519 - }, - { - allocationInPercentage: 0.007818631890358146, - name: 'VISA INC CLASS A', - valueInBaseCurrency: 110.26225623377576 - } - ], investment: 0.05934602124102648, name: 'Vanguard Total Stock Market Index Fund ETF Shares', netPerformance: 5756.8, @@ -786,6 +789,85 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.09386983901959013, assetClass: 'EQUITY', + assetProfile: { + holdings: [ + { + allocationInPercentage: 0.042520261085, + name: 'APPLE INC', + valueInBaseCurrency: 981.3336460398625 + }, + { + allocationInPercentage: 0.037017038038, + name: 'MICROSOFT CORP', + valueInBaseCurrency: 854.3236559815404 + }, + { + allocationInPercentage: 0.018861883836, + name: 'AMAZON COM INC', + valueInBaseCurrency: 435.31720557783655 + }, + { + allocationInPercentage: 0.017806548325, + name: 'NVIDIA CORP', + valueInBaseCurrency: 410.9609053487602 + }, + { + allocationInPercentage: 0.012188534864, + name: 'ALPHABET INC CLASS A', + valueInBaseCurrency: 281.3016442693628 + }, + { + allocationInPercentage: 0.010831709166, + name: 'ALPHABET INC CLASS C', + valueInBaseCurrency: 249.98719145833246 + }, + { + allocationInPercentage: 0.010813551981, + name: 'TESLA INC', + valueInBaseCurrency: 249.56813813873381 + }, + { + allocationInPercentage: 0.009934819182, + name: 'FACEBOOK CLASS A INC', + valueInBaseCurrency: 229.28768737165962 + }, + { + allocationInPercentage: 0.007403227621000001, + name: 'BERKSHIRE HATHAWAY INC CLASS B', + valueInBaseCurrency: 170.8605772494153 + }, + { + allocationInPercentage: 0.007076883908, + name: 'ELI LILLY', + valueInBaseCurrency: 163.32882514892185 + }, + { + allocationInPercentage: 0.006844271583, + name: 'EXXON MOBIL CORP', + valueInBaseCurrency: 157.96031857861325 + }, + { + allocationInPercentage: 0.006718061670999999, + name: 'UNITEDHEALTH GROUP INC', + valueInBaseCurrency: 155.0474946695187 + }, + { + allocationInPercentage: 0.006456949621, + name: 'JPMORGAN CHASE & CO', + valueInBaseCurrency: 149.02123722158794 + }, + { + allocationInPercentage: 0.006293890054, + name: 'TAIWAN SEMICONDUCTOR MANUFACTURING', + valueInBaseCurrency: 145.25795272326576 + }, + { + allocationInPercentage: 0.00600392555, + name: 'VISA INC CLASS A', + valueInBaseCurrency: 138.56580369427397 + } + ] + }, assetSubClass: 'ETF', countries: [ { @@ -1060,83 +1142,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.3683200415015591, grossPerformancePercentWithCurrencyEffect: 0.5806366182968891, grossPerformanceWithCurrencyEffect: 6402.248165662604, - holdings: [ - { - allocationInPercentage: 0.042520261085, - name: 'APPLE INC', - valueInBaseCurrency: 981.3336460398625 - }, - { - allocationInPercentage: 0.037017038038, - name: 'MICROSOFT CORP', - valueInBaseCurrency: 854.3236559815404 - }, - { - allocationInPercentage: 0.018861883836, - name: 'AMAZON COM INC', - valueInBaseCurrency: 435.31720557783655 - }, - { - allocationInPercentage: 0.017806548325, - name: 'NVIDIA CORP', - valueInBaseCurrency: 410.9609053487602 - }, - { - allocationInPercentage: 0.012188534864, - name: 'ALPHABET INC CLASS A', - valueInBaseCurrency: 281.3016442693628 - }, - { - allocationInPercentage: 0.010831709166, - name: 'ALPHABET INC CLASS C', - valueInBaseCurrency: 249.98719145833246 - }, - { - allocationInPercentage: 0.010813551981, - name: 'TESLA INC', - valueInBaseCurrency: 249.56813813873381 - }, - { - allocationInPercentage: 0.009934819182, - name: 'FACEBOOK CLASS A INC', - valueInBaseCurrency: 229.28768737165962 - }, - { - allocationInPercentage: 0.007403227621000001, - name: 'BERKSHIRE HATHAWAY INC CLASS B', - valueInBaseCurrency: 170.8605772494153 - }, - { - allocationInPercentage: 0.007076883908, - name: 'ELI LILLY', - valueInBaseCurrency: 163.32882514892185 - }, - { - allocationInPercentage: 0.006844271583, - name: 'EXXON MOBIL CORP', - valueInBaseCurrency: 157.96031857861325 - }, - { - allocationInPercentage: 0.006718061670999999, - name: 'UNITEDHEALTH GROUP INC', - valueInBaseCurrency: 155.0474946695187 - }, - { - allocationInPercentage: 0.006456949621, - name: 'JPMORGAN CHASE & CO', - valueInBaseCurrency: 149.02123722158794 - }, - { - allocationInPercentage: 0.006293890054, - name: 'TAIWAN SEMICONDUCTOR MANUFACTURING', - valueInBaseCurrency: 145.25795272326576 - }, - { - allocationInPercentage: 0.00600392555, - name: 'VISA INC CLASS A', - valueInBaseCurrency: 138.56580369427397 - } - ], investment: 0.13346122254229614, name: 'Vanguard FTSE All-World UCITS ETF', netPerformance: 4438.993935069568, @@ -1194,6 +1199,80 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.03598477442100562, assetClass: 'EQUITY', + assetProfile: { + holdings: [ + { + allocationInPercentage: 0.051778373, + name: 'APPLE INC', + valueInBaseCurrency: 458.1016731945994 + }, + { + allocationInPercentage: 0.0403267055, + name: 'MICROSOFT CORP', + valueInBaseCurrency: 356.78469974280296 + }, + { + allocationInPercentage: 0.0221895862, + name: 'AMAZON COM INC', + valueInBaseCurrency: 196.3191575315778 + }, + { + allocationInPercentage: 0.0208100035, + name: 'NVIDIA CORP', + valueInBaseCurrency: 184.1134989416425 + }, + { + allocationInPercentage: 0.0139820061, + name: 'ALPHABET INC CLASS A', + valueInBaseCurrency: 123.70377858390985 + }, + { + allocationInPercentage: 0.0126263246, + name: 'ALPHABET INC CLASS C', + valueInBaseCurrency: 111.70958240727516 + }, + { + allocationInPercentage: 0.0121596126, + name: 'TESLA INC', + valueInBaseCurrency: 107.58041542669048 + }, + { + allocationInPercentage: 0.0114079282, + name: 'FACEBOOK CLASS A INC', + valueInBaseCurrency: 100.92999631533141 + }, + { + allocationInPercentage: 0.0081570352, + name: 'BERKSHIRE HATHAWAY INC CLASS B', + valueInBaseCurrency: 72.16819024860523 + }, + { + allocationInPercentage: 0.0079471416, + name: 'EXXON MOBIL CORP', + valueInBaseCurrency: 70.31118695201964 + }, + { + allocationInPercentage: 0.0078190388, + name: 'ELI LILLY', + valueInBaseCurrency: 69.1778159397456 + }, + { + allocationInPercentage: 0.0077121293, + name: 'UNITEDHEALTH GROUP INC', + valueInBaseCurrency: 68.23194958681098 + }, + { + allocationInPercentage: 0.0074484861, + name: 'JPMORGAN CHASE & CO', + valueInBaseCurrency: 65.89940447098863 + }, + { + allocationInPercentage: 0.006978079, + name: 'VISA INC CLASS A', + valueInBaseCurrency: 61.73754562709217 + } + ] + }, assetSubClass: 'ETF', countries: [ { @@ -1342,78 +1421,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.3474381850624522, grossPerformancePercentWithCurrencyEffect: 0.28744846894552306, grossPerformanceWithCurrencyEffect: 1975.348026988124, - holdings: [ - { - allocationInPercentage: 0.051778373, - name: 'APPLE INC', - valueInBaseCurrency: 458.1016731945994 - }, - { - allocationInPercentage: 0.0403267055, - name: 'MICROSOFT CORP', - valueInBaseCurrency: 356.78469974280296 - }, - { - allocationInPercentage: 0.0221895862, - name: 'AMAZON COM INC', - valueInBaseCurrency: 196.3191575315778 - }, - { - allocationInPercentage: 0.0208100035, - name: 'NVIDIA CORP', - valueInBaseCurrency: 184.1134989416425 - }, - { - allocationInPercentage: 0.0139820061, - name: 'ALPHABET INC CLASS A', - valueInBaseCurrency: 123.70377858390985 - }, - { - allocationInPercentage: 0.0126263246, - name: 'ALPHABET INC CLASS C', - valueInBaseCurrency: 111.70958240727516 - }, - { - allocationInPercentage: 0.0121596126, - name: 'TESLA INC', - valueInBaseCurrency: 107.58041542669048 - }, - { - allocationInPercentage: 0.0114079282, - name: 'FACEBOOK CLASS A INC', - valueInBaseCurrency: 100.92999631533141 - }, - { - allocationInPercentage: 0.0081570352, - name: 'BERKSHIRE HATHAWAY INC CLASS B', - valueInBaseCurrency: 72.16819024860523 - }, - { - allocationInPercentage: 0.0079471416, - name: 'EXXON MOBIL CORP', - valueInBaseCurrency: 70.31118695201964 - }, - { - allocationInPercentage: 0.0078190388, - name: 'ELI LILLY', - valueInBaseCurrency: 69.1778159397456 - }, - { - allocationInPercentage: 0.0077121293, - name: 'UNITEDHEALTH GROUP INC', - valueInBaseCurrency: 68.23194958681098 - }, - { - allocationInPercentage: 0.0074484861, - name: 'JPMORGAN CHASE & CO', - valueInBaseCurrency: 65.89940447098863 - }, - { - allocationInPercentage: 0.006978079, - name: 'VISA INC CLASS A', - valueInBaseCurrency: 61.73754562709217 - } - ], investment: 0.04725441287200783, name: 'Xtrackers MSCI World UCITS ETF 1C', netPerformance: 2247.935728632002, @@ -1454,6 +1461,7 @@ describe('redactAttributes', () => { currency: 'USD', allocationInPercentage: 0.20291717628620132, assetClass: 'LIQUIDITY', + assetProfile: { holdings: [] }, assetSubClass: 'CASH', countries: [], dividend: 0, @@ -1461,7 +1469,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0, grossPerformancePercentWithCurrencyEffect: 0, grossPerformanceWithCurrencyEffect: 0, - holdings: [], investment: 0.35904695492648864, marketPrice: 0, name: 'USD', @@ -1637,6 +1644,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.044900865255793135, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { @@ -1653,7 +1661,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.3183066634822068, grossPerformancePercentWithCurrencyEffect: 0.3183066634822068, grossPerformanceWithCurrencyEffect: null, - holdings: [], investment: null, name: 'Apple Inc', netPerformance: null, @@ -1689,6 +1696,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.026912563036519527, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { code: 'DE', weight: 1, continent: 'Europe', name: 'Germany' } @@ -1700,7 +1708,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.3719230057375532, grossPerformancePercentWithCurrencyEffect: 0.2650716044872953, grossPerformanceWithCurrencyEffect: null, - holdings: [], investment: null, name: 'Allianz SE', netPerformance: null, @@ -1736,6 +1743,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.07646101417126275, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { @@ -1752,7 +1760,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.8594552890963852, grossPerformancePercentWithCurrencyEffect: 0.8594552890963852, grossPerformanceWithCurrencyEffect: null, - holdings: [], investment: null, name: 'Amazon.com, Inc.', netPerformance: null, @@ -1794,6 +1801,7 @@ describe('redactAttributes', () => { ], allocationInPercentage: 0.15042891393226654, assetClass: 'LIQUIDITY', + assetProfile: { holdings: [] }, assetSubClass: 'CRYPTOCURRENCY', countries: [], dataSource: 'COINGECKO', @@ -1803,7 +1811,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 17.4925166352, grossPerformancePercentWithCurrencyEffect: 17.4925166352, grossPerformanceWithCurrencyEffect: null, - holdings: [], investment: null, name: 'Bitcoin', netPerformance: null, @@ -1845,6 +1852,7 @@ describe('redactAttributes', () => { ], allocationInPercentage: 0.009076749759365777, assetClass: 'FIXED_INCOME', + assetProfile: { holdings: [] }, assetSubClass: 'BOND', countries: [], dataSource: 'MANUAL', @@ -1854,7 +1862,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0, grossPerformancePercentWithCurrencyEffect: -0.06153834320225245, grossPerformanceWithCurrencyEffect: null, - holdings: [], investment: null, name: 'Bondora Go & Grow', netPerformance: null, @@ -1896,6 +1903,7 @@ describe('redactAttributes', () => { ], allocationInPercentage: 0.09095764645669335, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'ETF', countries: [ { @@ -1958,7 +1966,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.27579517683678895, grossPerformancePercentWithCurrencyEffect: 0.458553421589667, grossPerformanceWithCurrencyEffect: null, - holdings: [], investment: null, name: 'frankly Extreme 95 Index', netPerformance: null, @@ -1994,6 +2001,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.05222646409742627, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { @@ -2010,7 +2018,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.7865431171216295, grossPerformancePercentWithCurrencyEffect: 0.7865431171216295, grossPerformanceWithCurrencyEffect: null, - holdings: [], investment: null, name: 'Microsoft Corporation', netPerformance: null, @@ -2046,6 +2053,7 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.1589050142378352, assetClass: 'EQUITY', + assetProfile: { holdings: [] }, assetSubClass: 'STOCK', countries: [ { @@ -2062,7 +2070,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 17.184314638161936, grossPerformancePercentWithCurrencyEffect: 17.184314638161936, grossPerformanceWithCurrencyEffect: null, - holdings: [], investment: null, name: 'Tesla, Inc.', netPerformance: null, @@ -2098,6 +2105,85 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.057358979326040366, assetClass: 'EQUITY', + assetProfile: { + holdings: [ + { + allocationInPercentage: 0.06099941636982121, + name: 'APPLE INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.05862464529372787, + name: 'MICROSOFT CORP', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.05156070760128074, + name: 'NVIDIA CORP', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.03301535551128066, + name: 'AMAZON COM INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.01962204914568647, + name: 'FACEBOOK CLASS A INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.01902835637666313, + name: 'ALPHABET INC CLASS A', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.01555676306627245, + name: 'ALPHABET INC CLASS C', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.01463100485016827, + name: 'BERKSHIRE HATHAWAY INC CLASS B', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.01403731208114493, + name: 'BROADCOM INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.01297067761476403, + name: 'ELI LILLY', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0118637927911612, + name: 'TESLA INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.01152166475477487, + name: 'JPMORGAN CHASE & CO', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0100324015375638, + name: 'EXXON MOBIL CORP', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.01000221376964736, + name: 'UNITEDHEALTH GROUP INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.007818631890358146, + name: 'VISA INC CLASS A', + valueInBaseCurrency: null + } + ] + }, assetSubClass: 'ETF', countries: [ { @@ -2144,83 +2230,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.8832083851170418, grossPerformancePercentWithCurrencyEffect: 0.8832083851170418, grossPerformanceWithCurrencyEffect: null, - holdings: [ - { - allocationInPercentage: 0.06099941636982121, - name: 'APPLE INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.05862464529372787, - name: 'MICROSOFT CORP', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.05156070760128074, - name: 'NVIDIA CORP', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.03301535551128066, - name: 'AMAZON COM INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.01962204914568647, - name: 'FACEBOOK CLASS A INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.01902835637666313, - name: 'ALPHABET INC CLASS A', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.01555676306627245, - name: 'ALPHABET INC CLASS C', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.01463100485016827, - name: 'BERKSHIRE HATHAWAY INC CLASS B', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.01403731208114493, - name: 'BROADCOM INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.01297067761476403, - name: 'ELI LILLY', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0118637927911612, - name: 'TESLA INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.01152166475477487, - name: 'JPMORGAN CHASE & CO', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0100324015375638, - name: 'EXXON MOBIL CORP', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.01000221376964736, - name: 'UNITEDHEALTH GROUP INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.007818631890358146, - name: 'VISA INC CLASS A', - valueInBaseCurrency: null - } - ], investment: null, name: 'Vanguard Total Stock Market Index Fund ETF Shares', netPerformance: null, @@ -2270,6 +2279,85 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.09386983901959013, assetClass: 'EQUITY', + assetProfile: { + holdings: [ + { + allocationInPercentage: 0.042520261085, + name: 'APPLE INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.037017038038, + name: 'MICROSOFT CORP', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.018861883836, + name: 'AMAZON COM INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.017806548325, + name: 'NVIDIA CORP', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.012188534864, + name: 'ALPHABET INC CLASS A', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.010831709166, + name: 'ALPHABET INC CLASS C', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.010813551981, + name: 'TESLA INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.009934819182, + name: 'FACEBOOK CLASS A INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.007403227621000001, + name: 'BERKSHIRE HATHAWAY INC CLASS B', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.007076883908, + name: 'ELI LILLY', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.006844271583, + name: 'EXXON MOBIL CORP', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.006718061670999999, + name: 'UNITEDHEALTH GROUP INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.006456949621, + name: 'JPMORGAN CHASE & CO', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.006293890054, + name: 'TAIWAN SEMICONDUCTOR MANUFACTURING', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.00600392555, + name: 'VISA INC CLASS A', + valueInBaseCurrency: null + } + ] + }, assetSubClass: 'ETF', countries: [ { @@ -2539,83 +2627,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.3683200415015591, grossPerformancePercentWithCurrencyEffect: 0.5806366182968891, grossPerformanceWithCurrencyEffect: null, - holdings: [ - { - allocationInPercentage: 0.042520261085, - name: 'APPLE INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.037017038038, - name: 'MICROSOFT CORP', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.018861883836, - name: 'AMAZON COM INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.017806548325, - name: 'NVIDIA CORP', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.012188534864, - name: 'ALPHABET INC CLASS A', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.010831709166, - name: 'ALPHABET INC CLASS C', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.010813551981, - name: 'TESLA INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.009934819182, - name: 'FACEBOOK CLASS A INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.007403227621000001, - name: 'BERKSHIRE HATHAWAY INC CLASS B', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.007076883908, - name: 'ELI LILLY', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.006844271583, - name: 'EXXON MOBIL CORP', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.006718061670999999, - name: 'UNITEDHEALTH GROUP INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.006456949621, - name: 'JPMORGAN CHASE & CO', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.006293890054, - name: 'TAIWAN SEMICONDUCTOR MANUFACTURING', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.00600392555, - name: 'VISA INC CLASS A', - valueInBaseCurrency: null - } - ], investment: null, name: 'Vanguard FTSE All-World UCITS ETF', netPerformance: null, @@ -2670,6 +2681,80 @@ describe('redactAttributes', () => { tags: [], allocationInPercentage: 0.03598477442100562, assetClass: 'EQUITY', + assetProfile: { + holdings: [ + { + allocationInPercentage: 0.051778373, + name: 'APPLE INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0403267055, + name: 'MICROSOFT CORP', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0221895862, + name: 'AMAZON COM INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0208100035, + name: 'NVIDIA CORP', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0139820061, + name: 'ALPHABET INC CLASS A', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0126263246, + name: 'ALPHABET INC CLASS C', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0121596126, + name: 'TESLA INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0114079282, + name: 'FACEBOOK CLASS A INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0081570352, + name: 'BERKSHIRE HATHAWAY INC CLASS B', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0079471416, + name: 'EXXON MOBIL CORP', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0078190388, + name: 'ELI LILLY', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0077121293, + name: 'UNITEDHEALTH GROUP INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.0074484861, + name: 'JPMORGAN CHASE & CO', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.006978079, + name: 'VISA INC CLASS A', + valueInBaseCurrency: null + } + ] + }, assetSubClass: 'ETF', countries: [ { @@ -2818,78 +2903,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0.3474381850624522, grossPerformancePercentWithCurrencyEffect: 0.28744846894552306, grossPerformanceWithCurrencyEffect: null, - holdings: [ - { - allocationInPercentage: 0.051778373, - name: 'APPLE INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0403267055, - name: 'MICROSOFT CORP', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0221895862, - name: 'AMAZON COM INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0208100035, - name: 'NVIDIA CORP', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0139820061, - name: 'ALPHABET INC CLASS A', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0126263246, - name: 'ALPHABET INC CLASS C', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0121596126, - name: 'TESLA INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0114079282, - name: 'FACEBOOK CLASS A INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0081570352, - name: 'BERKSHIRE HATHAWAY INC CLASS B', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0079471416, - name: 'EXXON MOBIL CORP', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0078190388, - name: 'ELI LILLY', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0077121293, - name: 'UNITEDHEALTH GROUP INC', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.0074484861, - name: 'JPMORGAN CHASE & CO', - valueInBaseCurrency: null - }, - { - allocationInPercentage: 0.006978079, - name: 'VISA INC CLASS A', - valueInBaseCurrency: null - } - ], investment: null, name: 'Xtrackers MSCI World UCITS ETF 1C', netPerformance: null, @@ -2930,6 +2943,7 @@ describe('redactAttributes', () => { currency: 'USD', allocationInPercentage: 0.20291717628620132, assetClass: 'LIQUIDITY', + assetProfile: { holdings: [] }, assetSubClass: 'CASH', countries: [], dividend: null, @@ -2937,7 +2951,6 @@ describe('redactAttributes', () => { grossPerformancePercent: 0, grossPerformancePercentWithCurrencyEffect: 0, grossPerformanceWithCurrencyEffect: null, - holdings: [], investment: null, marketPrice: 0, name: 'USD', @@ -3237,4 +3250,101 @@ describe('redactAttributes', () => { ] }); }); + + // The type makes sure that the object keeps the shape of the response, in + // which the holdings of an ETF are part of its asset profile + it('should redact the values of the holdings of an ETF', () => { + expect( + redactPaths({ + object: { + holdings: [ + { + activitiesCount: 5, + allocationInPercentage: 0.057358979326040366, + assetProfile: { + assetClass: 'EQUITY', + assetSubClass: 'ETF', + countries: [], + currency: 'USD', + dataSource: 'YAHOO', + holdings: [ + { + allocationInPercentage: 0.06099941636982121, + name: 'APPLE INC', + valueInBaseCurrency: 860.2442693554036 + }, + { + allocationInPercentage: 0.05862464529372787, + name: 'MICROSOFT CORP', + valueInBaseCurrency: 826.7540602547973 + } + ], + name: 'Vanguard Total Stock Market Index Fund ETF Shares', + sectors: [], + symbol: 'VTI' + }, + dateOfFirstActivity: new Date('2019-02-28T23:00:00.000Z'), + dividend: 0, + grossPerformance: 5856.3, + grossPerformancePercent: 0.8832083851170418, + grossPerformancePercentWithCurrencyEffect: 0.8832083851170418, + grossPerformanceWithCurrencyEffect: 5856.3, + investment: 8246.2, + marketPrice: 282.05, + netPerformance: 5756.8, + netPerformancePercent: 0.8682024540139314, + netPerformancePercentWithCurrencyEffect: 0.8328704068843998, + netPerformanceWithCurrencyEffect: 5756.8, + quantity: 50, + valueInBaseCurrency: 14102.5 + } + ] + } satisfies Pick, + paths: DEFAULT_REDACTED_PATHS + }) + ).toStrictEqual({ + holdings: [ + { + activitiesCount: 5, + allocationInPercentage: 0.057358979326040366, + assetProfile: { + assetClass: 'EQUITY', + assetSubClass: 'ETF', + countries: [], + currency: 'USD', + dataSource: 'YAHOO', + holdings: [ + { + allocationInPercentage: 0.06099941636982121, + name: 'APPLE INC', + valueInBaseCurrency: null + }, + { + allocationInPercentage: 0.05862464529372787, + name: 'MICROSOFT CORP', + valueInBaseCurrency: null + } + ], + name: 'Vanguard Total Stock Market Index Fund ETF Shares', + sectors: [], + symbol: 'VTI' + }, + dateOfFirstActivity: '2019-02-28T23:00:00.000Z', + dividend: null, + grossPerformance: null, + grossPerformancePercent: 0.8832083851170418, + grossPerformancePercentWithCurrencyEffect: 0.8832083851170418, + grossPerformanceWithCurrencyEffect: null, + investment: null, + marketPrice: 282.05, + netPerformance: null, + netPerformancePercent: 0.8682024540139314, + netPerformancePercentWithCurrencyEffect: 0.8328704068843998, + netPerformanceWithCurrencyEffect: null, + quantity: null, + valueInBaseCurrency: null + } + ] + }); + }); }); diff --git a/apps/api/src/helper/object.helper.ts b/apps/api/src/helper/object.helper.ts index 350d5fe04a..c040c5e0fc 100644 --- a/apps/api/src/helper/object.helper.ts +++ b/apps/api/src/helper/object.helper.ts @@ -1,6 +1,6 @@ import fastRedact from 'fast-redact'; import jsonpath from 'jsonpath'; -import { cloneDeep, isObject } from 'lodash'; +import { cloneDeep, isObject } from 'lodash-es'; export function hasNotDefinedValuesInObject(aObject: Object): boolean { for (const key in aObject) { diff --git a/apps/api/src/models/rule.ts b/apps/api/src/models/rule.ts index 4985b26477..f7f6c53637 100644 --- a/apps/api/src/models/rule.ts +++ b/apps/api/src/models/rule.ts @@ -7,7 +7,7 @@ import { } from '@ghostfolio/common/interfaces'; import { Big } from 'big.js'; -import { groupBy } from 'lodash'; +import { groupBy } from 'lodash-es'; import { EvaluationResult } from './interfaces/evaluation-result.interface'; import { RuleInterface } from './interfaces/rule.interface'; diff --git a/apps/api/src/services/benchmark/benchmark.service.ts b/apps/api/src/services/benchmark/benchmark.service.ts index 953939caac..4ea27b2f2e 100644 --- a/apps/api/src/services/benchmark/benchmark.service.ts +++ b/apps/api/src/services/benchmark/benchmark.service.ts @@ -23,7 +23,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { SymbolProfile } from '@prisma/client'; import { Big } from 'big.js'; import { addHours, isPast, subDays } from 'date-fns'; -import { round, uniqBy } from 'lodash'; +import { round, uniqBy } from 'lodash-es'; import ms from 'ms'; import { BenchmarkValue } from './interfaces/benchmark-value.interface'; diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 87c1e85c61..cfacab3231 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -42,7 +42,7 @@ import { Inject, Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { DataSource, MarketData, Prisma, SymbolProfile } from '@prisma/client'; import { Big } from 'big.js'; import { eachDayOfInterval, format, isValid } from 'date-fns'; -import { groupBy, isEmpty, isNumber, omit, uniqWith } from 'lodash'; +import { groupBy, isEmpty, isNumber, omit, uniqWith } from 'lodash-es'; import ms from 'ms'; import { AssetProfileInvalidError } from './errors/asset-profile-invalid.error'; diff --git a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts index 6bc003fc23..7dbb3b859c 100644 --- a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts +++ b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts @@ -31,7 +31,7 @@ import { SymbolProfile } from '@prisma/client'; import { addDays, format, isSameDay, isToday } from 'date-fns'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; @Injectable() export class EodHistoricalDataService diff --git a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts index 7c840b5fc6..a8f1c6287c 100644 --- a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts +++ b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts @@ -47,7 +47,7 @@ import { isSameDay, parseISO } from 'date-fns'; -import { isArray, uniqBy } from 'lodash'; +import { isArray, uniqBy } from 'lodash-es'; @Injectable() export class FinancialModelingPrepService diff --git a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts index ca6357e257..d2b741d372 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts @@ -24,7 +24,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; import { addDays, format, isSameDay } from 'date-fns'; import { ReasonPhrases, StatusCodes } from 'http-status-codes'; -import { uniqBy } from 'lodash'; +import { uniqBy } from 'lodash-es'; import YahooFinance from 'yahoo-finance2'; import type { ChartResultArray } from 'yahoo-finance2/modules/chart'; import type { diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts index 0d7e98e2e0..824149e74f 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts @@ -26,7 +26,7 @@ import { isToday, subDays } from 'date-fns'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; import ms from 'ms'; import { ExchangeRatesByCurrency } from './interfaces/exchange-rate-data.interface'; diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts index 4cbe6969ae..3a98757591 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts @@ -33,7 +33,7 @@ import { Inject, Injectable, Logger } from '@nestjs/common'; import { Prisma } from '@prisma/client'; import { Job, JobOptions, Queue } from 'bull'; import { format, min, subDays, subMilliseconds, subYears } from 'date-fns'; -import { isEmpty } from 'lodash'; +import { isEmpty } from 'lodash-es'; import ms, { StringValue } from 'ms'; @Injectable() diff --git a/apps/api/src/services/twitter-bot/twitter-bot.service.ts b/apps/api/src/services/twitter-bot/twitter-bot.service.ts index 2dbed82d21..6282b4363b 100644 --- a/apps/api/src/services/twitter-bot/twitter-bot.service.ts +++ b/apps/api/src/services/twitter-bot/twitter-bot.service.ts @@ -10,7 +10,7 @@ import { import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { isWeekend } from 'date-fns'; -import { round } from 'lodash'; +import { round } from 'lodash-es'; import { TwitterApi, TwitterApiReadWrite } from 'twitter-api-v2'; @Injectable() diff --git a/apps/client/jest.config.ts b/apps/client/jest.config.ts index 26d772e11f..d60a24ae4f 100644 --- a/apps/client/jest.config.ts +++ b/apps/client/jest.config.ts @@ -18,6 +18,6 @@ export default { } ] }, - transformIgnorePatterns: ['node_modules/(?!.*.mjs$)'], + transformIgnorePatterns: ['node_modules/(?!.*.mjs$|lodash-es/)'], preset: '../../jest.preset.js' }; diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts index b8c7f16dbc..44f7280473 100644 --- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts +++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts @@ -55,7 +55,7 @@ import { readerOutline, swapVerticalOutline } from 'ionicons/icons'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { filter, forkJoin } from 'rxjs'; diff --git a/apps/client/src/app/components/admin-jobs/admin-jobs.component.ts b/apps/client/src/app/components/admin-jobs/admin-jobs.component.ts index fd90bff2c1..e8e3a21255 100644 --- a/apps/client/src/app/components/admin-jobs/admin-jobs.component.ts +++ b/apps/client/src/app/components/admin-jobs/admin-jobs.component.ts @@ -49,7 +49,7 @@ import { removeCircleOutline, timeOutline } from 'ionicons/icons'; -import { get } from 'lodash'; +import { get } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @Component({ diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 154211b918..ba119b565f 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -106,7 +106,7 @@ import { serverOutline, trashOutline } from 'ionicons/icons'; -import { isBoolean } from 'lodash'; +import { isBoolean } from 'lodash-es'; import ms from 'ms'; import { EMPTY } from 'rxjs'; import { catchError } from 'rxjs/operators'; diff --git a/apps/client/src/app/components/admin-settings/admin-settings.component.ts b/apps/client/src/app/components/admin-settings/admin-settings.component.ts index b0c3dc2d22..d49f3fb0aa 100644 --- a/apps/client/src/app/components/admin-settings/admin-settings.component.ts +++ b/apps/client/src/app/components/admin-settings/admin-settings.component.ts @@ -42,7 +42,7 @@ import { RouterModule } from '@angular/router'; import { IonIcon } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; import { ellipsisHorizontal, trashOutline } from 'ionicons/icons'; -import { get } from 'lodash'; +import { get } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { catchError, filter, of } from 'rxjs'; diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index 4a16c1a4e2..8c3ec2206e 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -82,7 +82,7 @@ import { swapVerticalOutline, walletOutline } from 'ionicons/icons'; -import { isNumber, round, uniqBy } from 'lodash'; +import { isNumber, round, uniqBy } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { filter, switchMap } from 'rxjs/operators'; diff --git a/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts b/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts index a48d77a2dd..4f7f35dd1e 100644 --- a/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts +++ b/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts @@ -23,7 +23,7 @@ import { IonIcon } from '@ionic/angular/standalone'; import { CountUp } from 'countup.js'; import { addIcons } from 'ionicons'; import { timeOutline } from 'ionicons/icons'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @Component({ diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts index feedddacae..13686624f7 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts @@ -23,7 +23,7 @@ import { ellipsisHorizontalCircleOutline, informationCircleOutline } from 'ionicons/icons'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @Component({ diff --git a/apps/client/src/app/pages/api/api-page.component.ts b/apps/client/src/app/pages/api/api-page.component.ts index 8b80370e8d..36c780a04e 100644 --- a/apps/client/src/app/pages/api/api-page.component.ts +++ b/apps/client/src/app/pages/api/api-page.component.ts @@ -32,7 +32,7 @@ import { import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatCardModule } from '@angular/material/card'; import { format, startOfYear } from 'date-fns'; -import { isObject } from 'lodash'; +import { isObject } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { catchError, map, Observable, of, OperatorFunction } from 'rxjs'; diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts index 26255f2061..a1a9bddd6b 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts @@ -51,7 +51,7 @@ import { MatTableDataSource } from '@angular/material/table'; import { IonIcon } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; import { cloudUploadOutline, warningOutline } from 'ionicons/icons'; -import { isArray, sortBy } from 'lodash'; +import { isArray, sortBy } from 'lodash-es'; import ms from 'ms'; import { ImportStep } from './enums/import-step'; 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 6d67b0253c..647bc761e3 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 @@ -53,7 +53,7 @@ import { MatCardModule } from '@angular/material/card'; import { MatDialog } from '@angular/material/dialog'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { ActivatedRoute, Router } from '@angular/router'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; import { DeviceDetectorService } from 'ngx-device-detector'; import { filter, switchMap, tap } from 'rxjs'; @@ -134,11 +134,6 @@ export class GfAllocationsPageComponent implements OnInit { protected readonly UNKNOWN_KEY = UNKNOWN_KEY; protected user: User; - private topHoldingsMap: { - [name: string]: { name: string; value: number }; - }; - private totalValueInEtf = 0; - private readonly changeDetectorRef = inject(ChangeDetectorRef); private readonly dataService = inject(DataService); private readonly destroyRef = inject(DestroyRef); @@ -365,7 +360,6 @@ export class GfAllocationsPageComponent implements OnInit { value: 0 } }; - this.topHoldingsMap = {}; } private initializeAllocationsData() { @@ -388,7 +382,12 @@ export class GfAllocationsPageComponent implements OnInit { }; } + const topHoldingsMap: { + [name: string]: { name: string; value: number }; + } = {}; + let totalValueExcludingCashPositions = 0; + let totalValueInEtf = 0; for (const position of this.portfolioDetails.holdings) { const assetProfileIdentifier = getAssetProfileIdentifier( @@ -474,12 +473,12 @@ export class GfAllocationsPageComponent implements OnInit { ? valueInBaseCurrency : allocationInPercentage * (position.valueInPercentage ?? 0); - const holdingData = this.topHoldingsMap[normalizedAssetName]; + const holdingData = topHoldingsMap[normalizedAssetName]; if (holdingData) { holdingData.value += value; } else { - this.topHoldingsMap[normalizedAssetName] = { + topHoldingsMap[normalizedAssetName] = { name, value }; @@ -515,7 +514,7 @@ export class GfAllocationsPageComponent implements OnInit { } if (this.holdings[assetProfileIdentifier].assetSubClass === 'ETF') { - this.totalValueInEtf += this.holdings[assetProfileIdentifier].value; + totalValueInEtf += this.holdings[assetProfileIdentifier].value; } const symbol = position.assetProfile.symbol; @@ -582,7 +581,7 @@ export class GfAllocationsPageComponent implements OnInit { }; } - this.topHoldings = Object.values(this.topHoldingsMap) + this.topHoldings = Object.values(topHoldingsMap) .map(({ name, value }): HoldingWithParents => { if (this.showValuesInPercentage()) { return { @@ -594,7 +593,7 @@ export class GfAllocationsPageComponent implements OnInit { return { name, allocationInPercentage: - this.totalValueInEtf > 0 ? value / this.totalValueInEtf : 0, + totalValueInEtf > 0 ? value / totalValueInEtf : 0, parents: this.portfolioDetails.holdings .map((holding) => { if (holding.assetProfile.holdings.length > 0) { diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts index 898c7f51a8..880753cce5 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts @@ -51,7 +51,7 @@ import { RouterModule } from '@angular/router'; import { IonIcon } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; import { copyOutline, ellipsisVertical } from 'ionicons/icons'; -import { isNumber, keyBy, sortBy, union } from 'lodash'; +import { isNumber, keyBy, sortBy, union } from 'lodash-es'; import ms from 'ms'; import { DeviceDetectorService } from 'ngx-device-detector'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts index dc787ae26a..33b877d42f 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts @@ -27,7 +27,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormControl } from '@angular/forms'; import { Big } from 'big.js'; -import { isFinite, isNumber } from 'lodash'; +import { isFinite, isNumber } from 'lodash-es'; import { DeviceDetectorService } from 'ngx-device-detector'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; diff --git a/apps/client/src/app/services/import-activities.service.ts b/apps/client/src/app/services/import-activities.service.ts index 599c66551a..e302f00f9b 100644 --- a/apps/client/src/app/services/import-activities.service.ts +++ b/apps/client/src/app/services/import-activities.service.ts @@ -16,7 +16,7 @@ import { DataSource, Type as ActivityType } from '@ghostfolio/prisma/enums'; import { HttpClient } from '@angular/common/http'; import { inject, Service } from '@angular/core'; -import { isFinite, isNumber, isString } from 'lodash'; +import { isFinite, isNumber, isString } from 'lodash-es'; import { parse as csvToJson } from 'papaparse'; import { firstValueFrom } from 'rxjs'; import { v4 as uuidv4 } from 'uuid'; diff --git a/eslint.config.cjs b/eslint.config.cjs index ee5e0a557c..282e1b548c 100644 --- a/eslint.config.cjs +++ b/eslint.config.cjs @@ -116,7 +116,24 @@ module.exports = [ 'no-eval': 'error', 'no-fallthrough': 'error', 'no-new-wrappers': 'error', - 'no-restricted-imports': ['error', 'rxjs/Rx'], + 'no-restricted-imports': [ + 'error', + { + paths: [ + 'rxjs/Rx', + { + message: 'Import from lodash-es instead.', + name: 'lodash' + } + ], + patterns: [ + { + group: ['lodash/*'], + message: 'Import from lodash-es instead.' + } + ] + } + ], 'no-undef-init': 'error', 'no-underscore-dangle': 'off', 'no-var': 'error', diff --git a/jest.preset.js b/jest.preset.js index f078ddcec1..3cdca44c04 100644 --- a/jest.preset.js +++ b/jest.preset.js @@ -1,3 +1,6 @@ const nxPreset = require('@nx/jest/preset').default; -module.exports = { ...nxPreset }; +module.exports = { + ...nxPreset, + transformIgnorePatterns: ['node_modules/(?!lodash-es/)'] +}; diff --git a/libs/common/src/lib/calculation-helper.ts b/libs/common/src/lib/calculation-helper.ts index d49663d6cf..2cc1fa2d1c 100644 --- a/libs/common/src/lib/calculation-helper.ts +++ b/libs/common/src/lib/calculation-helper.ts @@ -10,7 +10,7 @@ import { subMilliseconds, subYears } from 'date-fns'; -import { isFinite, isNumber } from 'lodash'; +import { isFinite, isNumber } from 'lodash-es'; import { resetHours } from './helper'; import { DateRange } from './types'; diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index beedb741cb..03d36f577b 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -158,10 +158,10 @@ export const DEFAULT_REDACTED_PATHS = [ 'grossPerformance', 'grossPerformanceWithCurrencyEffect', 'historicalData[*].quantity', + 'holdings[*].assetProfile.holdings[*].valueInBaseCurrency', 'holdings[*].dividend', 'holdings[*].grossPerformance', 'holdings[*].grossPerformanceWithCurrencyEffect', - 'holdings[*].holdings[*].valueInBaseCurrency', 'holdings[*].investment', 'holdings[*].netPerformance', 'holdings[*].netPerformanceWithCurrencyEffect', diff --git a/libs/common/src/lib/dtos/create-account.dto.ts b/libs/common/src/lib/dtos/create-account.dto.ts index 92879dbb2c..5c1275db76 100644 --- a/libs/common/src/lib/dtos/create-account.dto.ts +++ b/libs/common/src/lib/dtos/create-account.dto.ts @@ -11,7 +11,7 @@ import { MaxLength, ValidateIf } from 'class-validator'; -import { isString } from 'lodash'; +import { isString } from 'lodash-es'; export class CreateAccountDto { /** diff --git a/libs/common/src/lib/dtos/create-order.dto.ts b/libs/common/src/lib/dtos/create-order.dto.ts index fd062b610b..d473e8e7b6 100644 --- a/libs/common/src/lib/dtos/create-order.dto.ts +++ b/libs/common/src/lib/dtos/create-order.dto.ts @@ -25,7 +25,7 @@ import { Min, Validate } from 'class-validator'; -import { isString } from 'lodash'; +import { isString } from 'lodash-es'; export class CreateOrderDto { @IsOptional() diff --git a/libs/common/src/lib/dtos/update-account.dto.ts b/libs/common/src/lib/dtos/update-account.dto.ts index deec684aae..c5f9f34679 100644 --- a/libs/common/src/lib/dtos/update-account.dto.ts +++ b/libs/common/src/lib/dtos/update-account.dto.ts @@ -11,7 +11,7 @@ import { MaxLength, ValidateIf } from 'class-validator'; -import { isString } from 'lodash'; +import { isString } from 'lodash-es'; export class UpdateAccountDto { /** diff --git a/libs/common/src/lib/dtos/update-order.dto.ts b/libs/common/src/lib/dtos/update-order.dto.ts index 0e381f2069..f7dfb0df53 100644 --- a/libs/common/src/lib/dtos/update-order.dto.ts +++ b/libs/common/src/lib/dtos/update-order.dto.ts @@ -24,7 +24,7 @@ import { Min, Validate } from 'class-validator'; -import { isString } from 'lodash'; +import { isString } from 'lodash-es'; export class UpdateOrderDto { @IsOptional() diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 10fea42f1a..fcf330172d 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -42,7 +42,7 @@ import { uk, zhCN } from 'date-fns/locale'; -import { get, isNil, isString } from 'lodash'; +import { get, isNil, isString } from 'lodash-es'; import { DEFAULT_CURRENCY, diff --git a/libs/ui/jest.config.ts b/libs/ui/jest.config.ts index 116fe6d54f..f9b97fe1a4 100644 --- a/libs/ui/jest.config.ts +++ b/libs/ui/jest.config.ts @@ -14,7 +14,7 @@ export default { } ] }, - transformIgnorePatterns: ['node_modules/(?!.*.mjs$)'], + transformIgnorePatterns: ['node_modules/(?!.*.mjs$|lodash-es/)'], snapshotSerializers: [ 'jest-preset-angular/build/serializers/no-ng-attributes', 'jest-preset-angular/build/serializers/ng-snapshot', diff --git a/libs/ui/src/lib/account-balances/account-balances.component.ts b/libs/ui/src/lib/account-balances/account-balances.component.ts index e27d29516c..04f13ccf10 100644 --- a/libs/ui/src/lib/account-balances/account-balances.component.ts +++ b/libs/ui/src/lib/account-balances/account-balances.component.ts @@ -40,7 +40,7 @@ import { ellipsisHorizontal, trashOutline } from 'ionicons/icons'; -import { get, isNil } from 'lodash'; +import { get, isNil } from 'lodash-es'; import { GfValueComponent } from '../value'; diff --git a/libs/ui/src/lib/activities-filter/activities-filter.component.ts b/libs/ui/src/lib/activities-filter/activities-filter.component.ts index 7a434d94c8..b6af11b0dc 100644 --- a/libs/ui/src/lib/activities-filter/activities-filter.component.ts +++ b/libs/ui/src/lib/activities-filter/activities-filter.component.ts @@ -29,7 +29,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { IonIcon } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; import { closeOutline, searchOutline } from 'ionicons/icons'; -import { groupBy } from 'lodash'; +import { groupBy } from 'lodash-es'; import { BehaviorSubject } from 'rxjs'; import { translate } from '../i18n'; diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index 6babc10398..ef0fb554ce 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -39,7 +39,7 @@ import { closeOutline, searchOutline } from 'ionicons/icons'; -import { isFunction, sample } from 'lodash'; +import { isFunction, sample } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { EMPTY, Observable, merge, of } from 'rxjs'; import { diff --git a/libs/ui/src/lib/benchmark/benchmark.component.ts b/libs/ui/src/lib/benchmark/benchmark.component.ts index 4f76978d45..bee06e3265 100644 --- a/libs/ui/src/lib/benchmark/benchmark.component.ts +++ b/libs/ui/src/lib/benchmark/benchmark.component.ts @@ -33,7 +33,7 @@ import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { IonIcon } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; import { ellipsisHorizontal, trashOutline } from 'ionicons/icons'; -import { isNumber, round } from 'lodash'; +import { isNumber, round } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { GfEntityLogoComponent } from '../entity-logo/entity-logo.component'; diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts index cfa2ee5cd2..959e934553 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts @@ -59,7 +59,7 @@ import { } from 'date-fns'; import { addIcons } from 'ionicons'; import { calendarClearOutline } from 'ionicons/icons'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { debounceTime } from 'rxjs'; diff --git a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts index 17a5a5e612..4f8540974f 100644 --- a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts +++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts @@ -29,7 +29,7 @@ import { MatInputModule } from '@angular/material/input'; import { IonIcon } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; import { calendarClearOutline, refreshOutline } from 'ionicons/icons'; -import { isNil } from 'lodash'; +import { isNil } from 'lodash-es'; import { HistoricalMarketDataEditorDialogParams } from './interfaces/interfaces'; diff --git a/libs/ui/src/lib/notifications/notification.service.ts b/libs/ui/src/lib/notifications/notification.service.ts index 4eb1cbd54d..d1c8459a6c 100644 --- a/libs/ui/src/lib/notifications/notification.service.ts +++ b/libs/ui/src/lib/notifications/notification.service.ts @@ -3,7 +3,7 @@ import { translate } from '@ghostfolio/ui/i18n'; import { inject, Service } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { isFunction } from 'lodash'; +import { isFunction } from 'lodash-es'; import { GfAlertDialogComponent } from './alert-dialog/alert-dialog.component'; import { GfConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component'; diff --git a/libs/ui/src/lib/services/admin.service.ts b/libs/ui/src/lib/services/admin.service.ts index aba02c9b3f..0307909dc1 100644 --- a/libs/ui/src/lib/services/admin.service.ts +++ b/libs/ui/src/lib/services/admin.service.ts @@ -31,7 +31,7 @@ import { GF_ENVIRONMENT } from '@ghostfolio/ui/environment'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { inject, Service } from '@angular/core'; import { JobStatus } from 'bull'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; @Service() export class AdminService { diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index 9bbb0bf5fa..8545c36ba4 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -82,7 +82,7 @@ import { inject, Service } from '@angular/core'; import { SortDirection } from '@angular/material/sort'; import { utc } from '@date-fns/utc'; import { format, parseISO } from 'date-fns'; -import { cloneDeep, groupBy, isNumber } from 'lodash'; +import { cloneDeep, groupBy, isNumber } from 'lodash-es'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts index 683f7c9252..d56aa177bf 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts @@ -36,7 +36,7 @@ import { } from '@angular/material/form-field'; import { MatInput, MatInputModule } from '@angular/material/input'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { isString } from 'lodash'; +import { isString } from 'lodash-es'; import { tap } from 'rxjs'; import { debounceTime, diff --git a/libs/ui/src/lib/top-holdings/top-holdings.component.ts b/libs/ui/src/lib/top-holdings/top-holdings.component.ts index 03e7f6cc46..1a675a5531 100644 --- a/libs/ui/src/lib/top-holdings/top-holdings.component.ts +++ b/libs/ui/src/lib/top-holdings/top-holdings.component.ts @@ -18,7 +18,7 @@ import { import { MatButtonModule } from '@angular/material/button'; import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; -import { capitalize } from 'lodash'; +import { capitalize } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { GfValueComponent } from '../value/value.component'; diff --git a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts index 54c8d7f907..a35d3deef5 100644 --- a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts +++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts @@ -32,7 +32,7 @@ import { Chart, LinearScale, Tooltip } from 'chart.js'; import { TreemapController, TreemapElement } from 'chartjs-chart-treemap'; import { isUUID } from 'class-validator'; import { differenceInDays, max } from 'date-fns'; -import { orderBy, round } from 'lodash'; +import { orderBy, round } from 'lodash-es'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import OpenColor from 'open-color'; diff --git a/libs/ui/src/lib/value/value.component.ts b/libs/ui/src/lib/value/value.component.ts index c2c7d4621d..4101b4066f 100644 --- a/libs/ui/src/lib/value/value.component.ts +++ b/libs/ui/src/lib/value/value.component.ts @@ -21,7 +21,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { IonIcon } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; import { checkmarkOutline, copyOutline } from 'ionicons/icons'; -import { isNumber } from 'lodash'; +import { isNumber } from 'lodash-es'; import ms from 'ms'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; diff --git a/package-lock.json b/package-lock.json index 3f8ee0f880..c3ee51dc53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,7 +83,7 @@ "ionicons": "8.0.13", "jsonpath": "1.3.0", "katex": "0.16.47", - "lodash": "4.18.1", + "lodash-es": "4.18.1", "marked": "18.0.12", "marked-katex-extension": "5.1.12", "ms": "3.0.0-canary.1", @@ -147,7 +147,7 @@ "@types/google-spreadsheet": "3.1.5", "@types/jest": "30.0.0", "@types/jsonpath": "0.2.4", - "@types/lodash": "4.17.25", + "@types/lodash-es": "4.17.12", "@types/node": "22.20.1", "@types/papaparse": "5.5.2", "@types/passport-google-oauth20": "2.0.17", @@ -15221,6 +15221,16 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/luxon": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", @@ -27042,7 +27052,6 @@ "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", - "dev": true, "license": "MIT" }, "node_modules/lodash.debounce": { diff --git a/package.json b/package.json index a3f94b74b6..ca4f3ebcff 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "ionicons": "8.0.13", "jsonpath": "1.3.0", "katex": "0.16.47", - "lodash": "4.18.1", + "lodash-es": "4.18.1", "marked": "18.0.12", "marked-katex-extension": "5.1.12", "ms": "3.0.0-canary.1", @@ -191,7 +191,7 @@ "@types/google-spreadsheet": "3.1.5", "@types/jest": "30.0.0", "@types/jsonpath": "0.2.4", - "@types/lodash": "4.17.25", + "@types/lodash-es": "4.17.12", "@types/node": "22.20.1", "@types/papaparse": "5.5.2", "@types/passport-google-oauth20": "2.0.17",