diff --git a/CHANGELOG.md b/CHANGELOG.md index 81d4bd0ce..6b5849d46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 2.208.0 - 2025-10-11 ### Added @@ -14,11 +14,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Changed the _As seen in_ section on the landing page to an animated carousel +- Refactored `transactionCount` to `activitiesCount` in the endpoint `GET api/v1/portfolio/holding/:dataSource/:symbol` - Refactored various components to use self-closing tags +- Removed the deprecated endpoint `GET api/v1/portfolio/position/:dataSource/:symbol` +- Removed the deprecated endpoint `PUT api/v1/portfolio/position/:dataSource/:symbol/tags` +- Improved the language localization for German (`de`) +- Upgraded `prisma` from version `6.16.1` to `6.16.3` ### Fixed - Fixed the server startup message to properly display IPv6 addresses +- Enabled IPv6 connectivity for _Redis_ in the job queue module by setting the address family +- Fixed an issue where importing custom asset profiles failed due to validation errors ## 2.207.0 - 2025-10-08 diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 8596aa0eb..86ceede28 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -71,9 +71,10 @@ import { UserModule } from './user/user.module'; BullModule.forRoot({ redis: { db: parseInt(process.env.REDIS_DB ?? '0', 10), + family: 0, host: process.env.REDIS_HOST, - port: parseInt(process.env.REDIS_PORT ?? '6379', 10), - password: process.env.REDIS_PASSWORD + password: process.env.REDIS_PASSWORD, + port: parseInt(process.env.REDIS_PORT ?? '6379', 10) } }), CacheModule, diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 82231d237..69ec781c3 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -373,6 +373,7 @@ export class ImportService { const assetProfiles = await this.validateActivities({ activitiesDto, + assetProfilesWithMarketDataDto, maxActivitiesToImport, user }); @@ -698,10 +699,12 @@ export class ImportService { private async validateActivities({ activitiesDto, + assetProfilesWithMarketDataDto, maxActivitiesToImport, user }: { activitiesDto: Partial[]; + assetProfilesWithMarketDataDto: ImportDataDto['assetProfiles']; maxActivitiesToImport: number; user: UserWithSettings; }) { @@ -749,6 +752,41 @@ export class ImportService { )?.[symbol] }; + if (!assetProfile?.name) { + const assetProfileInImport = assetProfilesWithMarketDataDto?.find( + (profile) => { + return ( + profile.dataSource === dataSource && profile.symbol === symbol + ); + } + ); + + if (assetProfileInImport) { + // Merge all fields of custom asset profiles into the validation object + Object.assign(assetProfile, { + assetClass: assetProfileInImport.assetClass, + assetSubClass: assetProfileInImport.assetSubClass, + comment: assetProfileInImport.comment, + countries: assetProfileInImport.countries, + currency: assetProfileInImport.currency, + cusip: assetProfileInImport.cusip, + dataSource: assetProfileInImport.dataSource, + figi: assetProfileInImport.figi, + figiComposite: assetProfileInImport.figiComposite, + figiShareClass: assetProfileInImport.figiShareClass, + holdings: assetProfileInImport.holdings, + isActive: assetProfileInImport.isActive, + isin: assetProfileInImport.isin, + name: assetProfileInImport.name, + scraperConfiguration: assetProfileInImport.scraperConfiguration, + sectors: assetProfileInImport.sectors, + symbol: assetProfileInImport.symbol, + symbolMapping: assetProfileInImport.symbolMapping, + url: assetProfileInImport.url + }); + } + } + if ( (dataSource !== 'MANUAL' && type === 'BUY') || type === 'DIVIDEND' || diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 5659818a8..19b0636c7 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -610,36 +610,6 @@ export class PortfolioController { return performanceInformation; } - /** - * @deprecated - */ - @Get('position/:dataSource/:symbol') - @UseInterceptors(RedactValuesInResponseInterceptor) - @UseInterceptors(TransformDataSourceInRequestInterceptor) - @UseInterceptors(TransformDataSourceInResponseInterceptor) - @UseGuards(AuthGuard('jwt'), HasPermissionGuard) - public async getPosition( - @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, - @Param('dataSource') dataSource: DataSource, - @Param('symbol') symbol: string - ): Promise { - const holding = await this.portfolioService.getHolding({ - dataSource, - impersonationId, - symbol, - userId: this.request.user.id - }); - - if (!holding) { - throw new HttpException( - getReasonPhrase(StatusCodes.NOT_FOUND), - StatusCodes.NOT_FOUND - ); - } - - return holding; - } - @Get('report') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async getReport( @@ -699,40 +669,4 @@ export class PortfolioController { userId: this.request.user.id }); } - - /** - * @deprecated - */ - @HasPermission(permissions.updateOrder) - @Put('position/:dataSource/:symbol/tags') - @UseInterceptors(TransformDataSourceInRequestInterceptor) - @UseGuards(AuthGuard('jwt'), HasPermissionGuard) - public async updatePositionTags( - @Body() data: UpdateHoldingTagsDto, - @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, - @Param('dataSource') dataSource: DataSource, - @Param('symbol') symbol: string - ): Promise { - const holding = await this.portfolioService.getHolding({ - dataSource, - impersonationId, - symbol, - userId: this.request.user.id - }); - - if (!holding) { - throw new HttpException( - getReasonPhrase(StatusCodes.NOT_FOUND), - StatusCodes.NOT_FOUND - ); - } - - await this.portfolioService.updateTags({ - dataSource, - impersonationId, - symbol, - tags: data.tags, - userId: this.request.user.id - }); - } } diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index f1f53d435..bb8ad2130 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -796,6 +796,7 @@ export class PortfolioService { if (activities.length === 0) { return { activities: [], + activitiesCount: 0, averagePrice: undefined, dataProviderInfo: undefined, dividendInBaseCurrency: undefined, @@ -820,7 +821,6 @@ export class PortfolioService { quantity: undefined, SymbolProfile: undefined, tags: [], - transactionCount: undefined, value: undefined }; } @@ -984,8 +984,8 @@ export class PortfolioService { marketPriceMin, SymbolProfile, tags, - transactionCount, activities: activitiesOfHolding, + activitiesCount: transactionCount, averagePrice: averagePrice.toNumber(), dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0], dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), @@ -1088,6 +1088,7 @@ export class PortfolioService { marketPriceMin, SymbolProfile, activities: [], + activitiesCount: 0, averagePrice: 0, dataProviderInfo: undefined, dividendInBaseCurrency: 0, @@ -1113,7 +1114,6 @@ export class PortfolioService { }, quantity: 0, tags: [], - transactionCount: undefined, value: 0 }; } @@ -2152,7 +2152,7 @@ export class PortfolioService { .plus(fees) .toNumber(), interest: interest.toNumber(), - liabilities: liabilities.toNumber(), + liabilitiesInBaseCurrency: liabilities.toNumber(), totalInvestment: totalInvestment.toNumber(), totalValueInBaseCurrency: netWorth }; 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 8f6616174..d4c1c59c1 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 @@ -100,6 +100,7 @@ import { HoldingDetailDialogParams } from './interfaces/interfaces'; templateUrl: 'holding-detail-dialog.html' }) export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { + public activitiesCount: number; public activityForm: FormGroup; public accounts: Account[]; public assetClass: string; @@ -151,8 +152,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { public SymbolProfile: EnhancedSymbolProfile; public tags: Tag[]; public tagsAvailable: Tag[]; - public totalItems: number; - public transactionCount: number; public user: User; public value: number; @@ -261,6 +260,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { .pipe(takeUntil(this.unsubscribeSubject)) .subscribe( ({ + activitiesCount, averagePrice, dataProviderInfo, dividendInBaseCurrency, @@ -279,9 +279,9 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { quantity, SymbolProfile, tags, - transactionCount, value }) => { + this.activitiesCount = activitiesCount; this.averagePrice = averagePrice; if ( @@ -429,8 +429,6 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { this.activityForm.setValue({ tags: this.tags }, { emitEvent: false }); - this.transactionCount = transactionCount; - this.totalItems = transactionCount; this.value = value; if (SymbolProfile?.assetClass) { diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html index 651f01a65..298692303 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -223,9 +223,9 @@ - @if (transactionCount === 1) { + @if (activitiesCount === 1) { Activity } @else { Activities @@ -363,7 +363,7 @@ [sortColumn]="sortColumn" [sortDirection]="sortDirection" [sortDisabled]="true" - [totalItems]="totalItems" + [totalItems]="activitiesCount" (activityToClone)="onCloneActivity($event)" (activityToUpdate)="onUpdateActivity($event)" (export)="onExport()" diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html index 19f125523..c8d710019 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html @@ -242,7 +242,10 @@
Liabilities
- @if (summary?.liabilities || summary?.liabilities === 0) { + @if ( + summary?.liabilitiesInBaseCurrency || + summary?.liabilitiesInBaseCurrency === 0 + ) { - }
diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index 3e12ef852..9211617f0 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -5409,7 +5409,7 @@ , - entnehmen, +  entnehmen, apps/client/src/app/pages/portfolio/fire/fire-page.html 93 diff --git a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts index 05fac0ba0..092a4bb97 100644 --- a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts @@ -21,7 +21,7 @@ export interface PortfolioSummary extends PortfolioPerformance { grossPerformance: number; grossPerformanceWithCurrencyEffect: number; interest: number; - liabilities: number; + liabilitiesInBaseCurrency: number; totalBuy: number; totalSell: number; totalValueInBaseCurrency?: number; diff --git a/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts b/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts index 000460228..b82a8f85d 100644 --- a/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts @@ -10,6 +10,7 @@ import { Tag } from '@prisma/client'; export interface PortfolioHoldingResponse { activities: Activity[]; + activitiesCount: number; averagePrice: number; dataProviderInfo: DataProviderInfo; dividendInBaseCurrency: number; @@ -34,6 +35,5 @@ export interface PortfolioHoldingResponse { quantity: number; SymbolProfile: EnhancedSymbolProfile; tags: Tag[]; - transactionCount: number; value: number; } diff --git a/package-lock.json b/package-lock.json index 55e453cce..75e1f50fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.207.0", + "version": "2.208.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.207.0", + "version": "2.208.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { @@ -44,7 +44,7 @@ "@nestjs/schedule": "6.0.0", "@nestjs/serve-static": "5.0.3", "@openrouter/ai-sdk-provider": "0.7.2", - "@prisma/client": "6.16.3", + "@prisma/client": "6.17.1", "@simplewebauthn/browser": "13.1.0", "@simplewebauthn/server": "13.1.1", "@stripe/stripe-js": "7.9.0", @@ -149,7 +149,7 @@ "nx": "21.5.1", "prettier": "3.6.2", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.16.3", + "prisma": "6.17.1", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", @@ -11960,9 +11960,9 @@ "license": "MIT" }, "node_modules/@prisma/client": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.16.3.tgz", - "integrity": "sha512-JfNfAtXG+/lIopsvoZlZiH2k5yNx87mcTS4t9/S5oufM1nKdXYxOvpDC1XoTCFBa5cQh7uXnbMPsmZrwZY80xw==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.17.1.tgz", + "integrity": "sha512-zL58jbLzYamjnNnmNA51IOZdbk5ci03KviXCuB0Tydc9btH2kDWsi1pQm2VecviRTM7jGia0OPPkgpGnT3nKvw==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { @@ -11982,9 +11982,9 @@ } }, "node_modules/@prisma/config": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.3.tgz", - "integrity": "sha512-VlsLnG4oOuKGGMToEeVaRhoTBZu5H3q51jTQXb/diRags3WV0+BQK5MolJTtP6G7COlzoXmWeS11rNBtvg+qFQ==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.17.1.tgz", + "integrity": "sha512-fs8wY6DsvOCzuiyWVckrVs1LOcbY4LZNz8ki4uUIQ28jCCzojTGqdLhN2Jl5lDnC1yI8/gNIKpsWDM8pLhOdwA==", "devOptional": true, "license": "Apache-2.0", "dependencies": { @@ -11995,53 +11995,53 @@ } }, "node_modules/@prisma/debug": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.3.tgz", - "integrity": "sha512-89DdqWtdKd7qoc9/qJCKLTazj3W3zPEiz0hc7HfZdpjzm21c7orOUB5oHWJsG+4KbV4cWU5pefq3CuDVYF9vgA==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.17.1.tgz", + "integrity": "sha512-Vf7Tt5Wh9XcndpbmeotuqOMLWPTjEKCsgojxXP2oxE1/xYe7PtnP76hsouG9vis6fctX+TxgmwxTuYi/+xc7dQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.3.tgz", - "integrity": "sha512-b+Rl4nzQDcoqe6RIpSHv8f5lLnwdDGvXhHjGDiokObguAAv/O1KaX1Oc69mBW/GFWKQpCkOraobLjU6s1h8HGg==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.17.1.tgz", + "integrity": "sha512-D95Ik3GYZkqZ8lSR4EyFOJ/tR33FcYRP8kK61o+WMsyD10UfJwd7+YielflHfKwiGodcqKqoraWw8ElAgMDbPw==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.16.3", - "@prisma/engines-version": "6.16.1-1.bb420e667c1820a8c05a38023385f6cc7ef8e83a", - "@prisma/fetch-engine": "6.16.3", - "@prisma/get-platform": "6.16.3" + "@prisma/debug": "6.17.1", + "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", + "@prisma/fetch-engine": "6.17.1", + "@prisma/get-platform": "6.17.1" } }, "node_modules/@prisma/engines-version": { - "version": "6.16.1-1.bb420e667c1820a8c05a38023385f6cc7ef8e83a", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.1-1.bb420e667c1820a8c05a38023385f6cc7ef8e83a.tgz", - "integrity": "sha512-fftRmosBex48Ph1v2ll1FrPpirwtPZpNkE5CDCY1Lw2SD2ctyrLlVlHiuxDAAlALwWBOkPbAll4+EaqdGuMhJw==", + "version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac.tgz", + "integrity": "sha512-17140E3huOuD9lMdJ9+SF/juOf3WR3sTJMVyyenzqUPbuH+89nPhSWcrY+Mf7tmSs6HvaO+7S+HkELinn6bhdg==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.3.tgz", - "integrity": "sha512-bUoRIkVaI+CCaVGrSfcKev0/Mk4ateubqWqGZvQ9uCqFv2ENwWIR3OeNuGin96nZn5+SkebcD7RGgKr/+mJelw==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.17.1.tgz", + "integrity": "sha512-AYZiHOs184qkDMiTeshyJCtyL4yERkjfTkJiSJdYuSfc24m94lTNL5+GFinZ6vVz+ktX4NJzHKn1zIFzGTWrWg==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.16.3", - "@prisma/engines-version": "6.16.1-1.bb420e667c1820a8c05a38023385f6cc7ef8e83a", - "@prisma/get-platform": "6.16.3" + "@prisma/debug": "6.17.1", + "@prisma/engines-version": "6.17.1-1.272a37d34178c2894197e17273bf937f25acdeac", + "@prisma/get-platform": "6.17.1" } }, "node_modules/@prisma/get-platform": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.3.tgz", - "integrity": "sha512-X1LxiFXinJ4iQehrodGp0f66Dv6cDL0GbRlcCoLtSu6f4Wi+hgo7eND/afIs5029GQLgNWKZ46vn8hjyXTsHLA==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.17.1.tgz", + "integrity": "sha512-AKEn6fsfz0r482S5KRDFlIGEaq9wLNcgalD1adL+fPcFFblIKs1sD81kY/utrHdqKuVC6E1XSRpegDK3ZLL4Qg==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.16.3" + "@prisma/debug": "6.17.1" } }, "node_modules/@redis/client": { @@ -35747,15 +35747,15 @@ } }, "node_modules/prisma": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.3.tgz", - "integrity": "sha512-4tJq3KB9WRshH5+QmzOLV54YMkNlKOtLKaSdvraI5kC/axF47HuOw6zDM8xrxJ6s9o2WodY654On4XKkrobQdQ==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.17.1.tgz", + "integrity": "sha512-ac6h0sM1Tg3zu8NInY+qhP/S9KhENVaw9n1BrGKQVFu05JT5yT5Qqqmb8tMRIE3ZXvVj4xcRA5yfrsy4X7Yy5g==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/config": "6.16.3", - "@prisma/engines": "6.16.3" + "@prisma/config": "6.17.1", + "@prisma/engines": "6.17.1" }, "bin": { "prisma": "build/index.js" diff --git a/package.json b/package.json index 5f99a4d11..28968c2c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.207.0", + "version": "2.208.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", @@ -90,7 +90,7 @@ "@nestjs/schedule": "6.0.0", "@nestjs/serve-static": "5.0.3", "@openrouter/ai-sdk-provider": "0.7.2", - "@prisma/client": "6.16.3", + "@prisma/client": "6.17.1", "@simplewebauthn/browser": "13.1.0", "@simplewebauthn/server": "13.1.1", "@stripe/stripe-js": "7.9.0", @@ -195,7 +195,7 @@ "nx": "21.5.1", "prettier": "3.6.2", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.16.3", + "prisma": "6.17.1", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", diff --git a/test/import/ok/penthouse-apartment.json b/test/import/ok/penthouse-apartment.json new file mode 100644 index 000000000..3b230cf76 --- /dev/null +++ b/test/import/ok/penthouse-apartment.json @@ -0,0 +1,53 @@ +{ + "meta": { + "date": "2023-02-05T00:00:00.000Z", + "version": "dev" + }, + "accounts": [], + "assetProfiles": [ + { + "assetClass": null, + "assetSubClass": null, + "comment": null, + "countries": [], + "currency": "USD", + "cusip": null, + "dataSource": "MANUAL", + "figi": null, + "figiComposite": null, + "figiShareClass": null, + "holdings": [], + "isActive": true, + "isin": null, + "marketData": [], + "name": "Penthouse Apartment", + "scraperConfiguration": null, + "sectors": [], + "symbol": "7e91b7d4-1430-4212-8380-289a06c9bbc1", + "symbolMapping": {}, + "url": null + } + ], + "platforms": [], + "tags": [], + "activities": [ + { + "accountId": null, + "comment": null, + "currency": "USD", + "dataSource": "MANUAL", + "date": "2022-01-01T00:00:00.000Z", + "fee": 0, + "quantity": 1, + "symbol": "7e91b7d4-1430-4212-8380-289a06c9bbc1", + "tags": [], + "type": "BUY", + "unitPrice": 500000, + } + ], + "user": { + "settings": { + "currency": "USD" + } + } +}