From a4c78739bbe0e4584402e541947b6fc36af90591 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 14 Mar 2025 08:00:07 +0100 Subject: [PATCH 01/56] Feature/improve width of user account registration dialog on mobile (#4434) * Improve width on mobile --- .../src/app/pages/register/register-page.component.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/client/src/app/pages/register/register-page.component.ts b/apps/client/src/app/pages/register/register-page.component.ts index 45fd53fcc..66a5f4beb 100644 --- a/apps/client/src/app/pages/register/register-page.component.ts +++ b/apps/client/src/app/pages/register/register-page.component.ts @@ -61,15 +61,18 @@ export class RegisterPageComponent implements OnDestroy, OnInit { public async onLoginWithInternetIdentity() { try { const { authToken } = await this.internetIdentityService.login(); + this.tokenStorageService.saveToken(authToken); - this.router.navigate(['/']); + + await this.router.navigate(['/']); } catch {} } public openShowAccessTokenDialog() { const dialogRef = this.dialog.open(ShowAccessTokenDialog, { disableClose: true, - width: '30rem' + height: this.deviceType === 'mobile' ? '98vh' : undefined, + width: this.deviceType === 'mobile' ? '100vw' : '30rem' }); dialogRef From 9e44023f86e49e80b7f95489d98e2f097726cf6c Mon Sep 17 00:00:00 2001 From: Tobias Kugel <78074722+tobikugel@users.noreply.github.com> Date: Sat, 15 Mar 2025 07:55:48 -0300 Subject: [PATCH 02/56] Feature/improve usability of AI prompt actions (#4426) * Improve usability of AI prompt actions * Update changelog --- CHANGELOG.md | 6 +- .../components/header/header.component.html | 2 +- .../analysis/analysis-page.component.ts | 27 +++++- .../portfolio/analysis/analysis-page.html | 90 ++++++++++++------- .../analysis/analysis-page.module.ts | 2 + apps/client/src/styles.scss | 2 +- 6 files changed, 89 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bfcf526a..a71397b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -### Added - -- Improved the usability of the user account registration - ### Changed +- Improved the usability of the user account registration +- Improved the usability of the _Copy AI prompt to clipboard_ actions on the analysis page (experimental) - Improved the language localization for German (`de`) - Upgraded `angular` from version `19.0.5` to `19.2.1` - Upgraded `Nx` from version `20.3.2` to `20.5.0` diff --git a/apps/client/src/app/components/header/header.component.html b/apps/client/src/app/components/header/header.component.html index 8a611d935..680cc98f0 100644 --- a/apps/client/src/app/components/header/header.component.html +++ b/apps/client/src/app/components/header/header.component.html @@ -134,7 +134,7 @@ ; public benchmarkDataItems: HistoricalDataItem[] = []; public benchmarks: Partial[]; @@ -46,10 +55,12 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { public investments: InvestmentItem[]; public investmentTimelineDataLabel = $localize`Investment`; public investmentsByGroup: InvestmentItem[]; + public isLoadingAnalysisPrompt: boolean; public isLoadingBenchmarkComparator: boolean; public isLoadingDividendTimelineChart: boolean; public isLoadingInvestmentChart: boolean; public isLoadingInvestmentTimelineChart: boolean; + public isLoadingPortfolioPrompt: boolean; public mode: GroupBy = 'month'; public modeOptions: ToggleOption[] = [ { label: $localize`Monthly`, value: 'month' }, @@ -143,6 +154,12 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { } public onCopyPromptToClipboard(mode: AiPromptMode) { + if (mode === 'analysis') { + this.isLoadingAnalysisPrompt = true; + } else if (mode === 'portfolio') { + this.isLoadingPortfolioPrompt = true; + } + this.dataService .fetchPrompt(mode) .pipe(takeUntil(this.unsubscribeSubject)) @@ -163,6 +180,14 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { .subscribe(() => { window.open('https://duck.ai', '_blank'); }); + + this.actionsMenuButton.closeMenu(); + + if (mode === 'analysis') { + this.isLoadingAnalysisPrompt = false; + } else if (mode === 'portfolio') { + this.isLoadingPortfolioPrompt = false; + } }); } diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html index 84ca54e06..56c95e40f 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -5,6 +5,7 @@
- - - + +
+ + +
diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.module.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.module.ts index fb39b2ab9..e02e15ec8 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.module.ts +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.module.ts @@ -10,6 +10,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatMenuModule } from '@angular/material/menu'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { AnalysisPageRoutingModule } from './analysis-page-routing.module'; @@ -29,6 +30,7 @@ import { AnalysisPageComponent } from './analysis-page.component'; MatButtonModule, MatCardModule, MatMenuModule, + MatProgressSpinnerModule, NgxSkeletonLoaderModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/apps/client/src/styles.scss b/apps/client/src/styles.scss index b13b088b7..b4723d377 100644 --- a/apps/client/src/styles.scss +++ b/apps/client/src/styles.scss @@ -451,7 +451,7 @@ ngx-skeleton-loader { } .mat-mdc-menu-panel { - &.assistant { + &.no-max-width { max-width: unset !important; .mat-mdc-menu-content { From 1917c17cf9efcf17c806a28986bc66346c9471c8 Mon Sep 17 00:00:00 2001 From: Ken Tandrian <60643640+KenTandrian@users.noreply.github.com> Date: Sat, 15 Mar 2025 17:57:01 +0700 Subject: [PATCH 03/56] Bugfix/fix issue with serving Storybook related to contentSecurityPolicy (#4437) * Fix issue with serving Storybook related to contentSecurityPolicy * Update changelog --- CHANGELOG.md | 4 +++ apps/api/src/main.ts | 35 +++++++++++-------- .../middlewares/html-template.middleware.ts | 3 +- libs/common/src/lib/config.ts | 2 ++ 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a71397b2c..70fb91984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `angular` from version `19.0.5` to `19.2.1` - Upgraded `Nx` from version `20.3.2` to `20.5.0` +### Fixed + +- Fixed an issue with serving _Storybook_ related to the `contentSecurityPolicy` + ## 2.145.1 - 2025-03-10 ### Added diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index fdd6c1d99..7cd5953b0 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -1,3 +1,5 @@ +import { STORYBOOK_PATH } from '@ghostfolio/common/config'; + import { Logger, LogLevel, @@ -7,6 +9,7 @@ import { import { ConfigService } from '@nestjs/config'; import { NestFactory } from '@nestjs/core'; import type { NestExpressApplication } from '@nestjs/platform-express'; +import { NextFunction, Request, Response } from 'express'; import helmet from 'helmet'; import { AppModule } from './app/app.module'; @@ -50,20 +53,24 @@ async function bootstrap() { app.useBodyParser('json', { limit: '10mb' }); if (configService.get('ENABLE_FEATURE_SUBSCRIPTION') === 'true') { - app.use( - helmet({ - contentSecurityPolicy: { - directives: { - connectSrc: ["'self'", 'https://js.stripe.com'], // Allow connections to Stripe - frameSrc: ["'self'", 'https://js.stripe.com'], // Allow loading frames from Stripe - scriptSrc: ["'self'", "'unsafe-inline'", 'https://js.stripe.com'], // Allow inline scripts and scripts from Stripe - scriptSrcAttr: ["'self'", "'unsafe-inline'"], // Allow inline event handlers - styleSrc: ["'self'", "'unsafe-inline'"] // Allow inline styles - } - }, - crossOriginOpenerPolicy: false // Disable Cross-Origin-Opener-Policy header (for Internet Identity) - }) - ); + app.use((req: Request, res: Response, next: NextFunction) => { + if (req.path.startsWith(STORYBOOK_PATH)) { + next(); + } else { + helmet({ + contentSecurityPolicy: { + directives: { + connectSrc: ["'self'", 'https://js.stripe.com'], // Allow connections to Stripe + frameSrc: ["'self'", 'https://js.stripe.com'], // Allow loading frames from Stripe + scriptSrc: ["'self'", "'unsafe-inline'", 'https://js.stripe.com'], // Allow inline scripts and scripts from Stripe + scriptSrcAttr: ["'self'", "'unsafe-inline'"], // Allow inline event handlers + styleSrc: ["'self'", "'unsafe-inline'"] // Allow inline styles + } + }, + crossOriginOpenerPolicy: false // Disable Cross-Origin-Opener-Policy header (for Internet Identity) + })(req, res, next); + } + }); } app.use(HtmlTemplateMiddleware); diff --git a/apps/api/src/middlewares/html-template.middleware.ts b/apps/api/src/middlewares/html-template.middleware.ts index b6c8f2e54..256876952 100644 --- a/apps/api/src/middlewares/html-template.middleware.ts +++ b/apps/api/src/middlewares/html-template.middleware.ts @@ -3,6 +3,7 @@ import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { DEFAULT_LANGUAGE_CODE, DEFAULT_ROOT_URL, + STORYBOOK_PATH, SUPPORTED_LANGUAGE_CODES } from '@ghostfolio/common/config'; import { DATE_FORMAT, interpolate } from '@ghostfolio/common/helper'; @@ -129,7 +130,7 @@ export const HtmlTemplateMiddleware = async ( if ( path.startsWith('/api/') || - path.startsWith('/development/storybook') || + path.startsWith(STORYBOOK_PATH) || isFileRequest(path) || !environment.production ) { diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index edfe7fa69..696ca86d2 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -153,6 +153,8 @@ export const REPLACE_NAME_PARTS = [ 'Xtrackers (IE) Plc -' ]; +export const STORYBOOK_PATH = '/development/storybook'; + export const SUPPORTED_LANGUAGE_CODES = [ 'ca', 'de', From 2f868b8902e1243913053eea2c73c13b95c7df5d Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 15 Mar 2025 12:22:24 +0100 Subject: [PATCH 04/56] Feature/add guard to TransformDataSourceInRequestInterceptor (#4438) * Add guard --- .../transform-data-source-in-request.interceptor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts index 227d06d7b..b24fb8404 100644 --- a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts @@ -26,7 +26,7 @@ export class TransformDataSourceInRequestInterceptor const request = http.getRequest(); if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { - if (request.body.activities) { + if (request.body?.activities) { request.body.activities = request.body.activities.map((activity) => { if (DataSource[activity.dataSource]) { return activity; From 6319661b0c3aed5127b25d4419e199e70f48e366 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 15 Mar 2025 18:24:05 +0100 Subject: [PATCH 05/56] Feature/upgrade prettier to version 3.5.3 (#4435) * Upgrade prettier to version 3.5.3 * Update changelog --- CHANGELOG.md | 1 + package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70fb91984..cb597c155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the language localization for German (`de`) - Upgraded `angular` from version `19.0.5` to `19.2.1` - Upgraded `Nx` from version `20.3.2` to `20.5.0` +- Upgraded `prettier` from version `3.5.1` to `3.5.3` ### Fixed diff --git a/package-lock.json b/package-lock.json index a0447ccba..59f7d2dde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -147,7 +147,7 @@ "jest-environment-jsdom": "29.7.0", "jest-preset-angular": "14.4.2", "nx": "20.5.0", - "prettier": "3.5.1", + "prettier": "3.5.3", "prettier-plugin-organize-attributes": "1.0.0", "prisma": "6.4.1", "react": "18.2.0", @@ -28052,9 +28052,9 @@ } }, "node_modules/prettier": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", - "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", "bin": { diff --git a/package.json b/package.json index 77a336be8..6a96bed12 100644 --- a/package.json +++ b/package.json @@ -193,7 +193,7 @@ "jest-environment-jsdom": "29.7.0", "jest-preset-angular": "14.4.2", "nx": "20.5.0", - "prettier": "3.5.1", + "prettier": "3.5.3", "prettier-plugin-organize-attributes": "1.0.0", "prisma": "6.4.1", "react": "18.2.0", From b90e3076d033731d439b2280d623e1230655e30b Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 15 Mar 2025 18:25:15 +0100 Subject: [PATCH 06/56] Feature/remove exchange rates from admin control overview (#4439) * Remove exchange rates * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/admin/admin.service.ts | 28 -------- .../admin-overview.component.ts | 44 +++--------- .../admin-overview/admin-overview.html | 67 ------------------- .../lib/interfaces/admin-data.interface.ts | 7 -- 5 files changed, 11 insertions(+), 136 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb597c155..1ac273b7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the usability of the user account registration - Improved the usability of the _Copy AI prompt to clipboard_ actions on the analysis page (experimental) +- Removed the exchange rates from the overview of the admin control panel - Improved the language localization for German (`de`) - Upgraded `angular` from version `19.0.5` to `19.2.1` - Upgraded `Nx` from version `20.3.2` to `20.5.0` diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 7f2b0da5a..edb96a28e 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -10,7 +10,6 @@ import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { - DEFAULT_CURRENCY, PROPERTY_CURRENCIES, PROPERTY_IS_READ_ONLY_MODE, PROPERTY_IS_USER_SIGNUP_ENABLED @@ -37,7 +36,6 @@ import { BadRequestException, Injectable, Logger } from '@nestjs/common'; import { AssetClass, AssetSubClass, - DataSource, Prisma, PrismaClient, Property, @@ -132,31 +130,6 @@ export class AdminService { } public async get(): Promise { - const exchangeRates = this.exchangeRateDataService - .getCurrencies() - .filter((currency) => { - return currency !== DEFAULT_CURRENCY; - }) - .map((currency) => { - const label1 = DEFAULT_CURRENCY; - const label2 = currency; - - return { - label1, - label2, - dataSource: - DataSource[ - this.configurationService.get('DATA_SOURCE_EXCHANGE_RATES') - ], - symbol: `${label1}${label2}`, - value: this.exchangeRateDataService.toCurrency( - 1, - DEFAULT_CURRENCY, - currency - ) - }; - }); - const [settings, transactionCount, userCount] = await Promise.all([ this.propertyService.get(), this.prismaService.order.count(), @@ -164,7 +137,6 @@ export class AdminService { ]); return { - exchangeRates, settings, transactionCount, userCount, diff --git a/apps/client/src/app/components/admin-overview/admin-overview.component.ts b/apps/client/src/app/components/admin-overview/admin-overview.component.ts index f54af4174..2f814668a 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.component.ts +++ b/apps/client/src/app/components/admin-overview/admin-overview.component.ts @@ -6,7 +6,6 @@ import { DataService } from '@ghostfolio/client/services/data.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { PROPERTY_COUPONS, - PROPERTY_CURRENCIES, PROPERTY_IS_DATA_GATHERING_ENABLED, PROPERTY_IS_READ_ONLY_MODE, PROPERTY_IS_USER_SIGNUP_ENABLED, @@ -41,8 +40,6 @@ import { takeUntil } from 'rxjs/operators'; export class AdminOverviewComponent implements OnDestroy, OnInit { public couponDuration: StringValue = '14 days'; public coupons: Coupon[]; - public customCurrencies: string[]; - public exchangeRates: { label1: string; label2: string; value: number }[]; public hasPermissionForSubscription: boolean; public hasPermissionForSystemMessage: boolean; public hasPermissionToToggleReadOnlyMode: boolean; @@ -138,19 +135,6 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { }); } - public onDeleteCurrency(aCurrency: string) { - this.notificationService.confirm({ - confirmFn: () => { - const currencies = this.customCurrencies.filter((currency) => { - return currency !== aCurrency; - }); - this.putAdminSetting({ key: PROPERTY_CURRENCIES, value: currencies }); - }, - confirmType: ConfirmationDialogType.Warn, - title: $localize`Do you really want to delete this currency?` - }); - } - public onDeleteSystemMessage() { this.notificationService.confirm({ confirmFn: () => { @@ -231,25 +215,17 @@ export class AdminOverviewComponent implements OnDestroy, OnInit { this.adminService .fetchAdminData() .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe( - ({ exchangeRates, settings, transactionCount, userCount, version }) => { - this.coupons = (settings[PROPERTY_COUPONS] as Coupon[]) ?? []; - this.customCurrencies = settings[PROPERTY_CURRENCIES] as string[]; - this.exchangeRates = exchangeRates; - this.isDataGatheringEnabled = - settings[PROPERTY_IS_DATA_GATHERING_ENABLED] === false - ? false - : true; - this.systemMessage = settings[ - PROPERTY_SYSTEM_MESSAGE - ] as SystemMessage; - this.transactionCount = transactionCount; - this.userCount = userCount; - this.version = version; + .subscribe(({ settings, transactionCount, userCount, version }) => { + this.coupons = (settings[PROPERTY_COUPONS] as Coupon[]) ?? []; + this.isDataGatheringEnabled = + settings[PROPERTY_IS_DATA_GATHERING_ENABLED] === false ? false : true; + this.systemMessage = settings[PROPERTY_SYSTEM_MESSAGE] as SystemMessage; + this.transactionCount = transactionCount; + this.userCount = userCount; + this.version = version; - this.changeDetectorRef.markForCheck(); - } - ); + this.changeDetectorRef.markForCheck(); + }); } private generateCouponCode(aLength: number) { diff --git a/apps/client/src/app/components/admin-overview/admin-overview.html b/apps/client/src/app/components/admin-overview/admin-overview.html index a85c32d43..bcbf666ef 100644 --- a/apps/client/src/app/components/admin-overview/admin-overview.html +++ b/apps/client/src/app/components/admin-overview/admin-overview.html @@ -30,73 +30,6 @@ } -
-
Exchange Rates
-
- - @for (exchangeRate of exchangeRates; track exchangeRate) { - - - - - - - - - } -
- - {{ exchangeRate.label1 }}= - - {{ exchangeRate.label2 }} - - - - - - Edit - - - @if (customCurrencies.includes(exchangeRate.label2)) { -
- - } -
-
-
-
User Signup
diff --git a/libs/common/src/lib/interfaces/admin-data.interface.ts b/libs/common/src/lib/interfaces/admin-data.interface.ts index 3dc476df8..e14429493 100644 --- a/libs/common/src/lib/interfaces/admin-data.interface.ts +++ b/libs/common/src/lib/interfaces/admin-data.interface.ts @@ -1,11 +1,4 @@ -import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; - export interface AdminData { - exchangeRates: ({ - label1: string; - label2: string; - value: number; - } & AssetProfileIdentifier)[]; settings: { [key: string]: boolean | object | string | string[] }; transactionCount: number; userCount: number; From 755ab15755e8c0d4363d3f170080fef6d82e2798 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 15 Mar 2025 18:47:07 +0100 Subject: [PATCH 07/56] Feature/format name in financial modeling prep service (#4441) * Format name * Update changelog --- CHANGELOG.md | 1 + .../financial-modeling-prep.service.ts | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ac273b7a..9def6ab50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the usability of the user account registration - Improved the usability of the _Copy AI prompt to clipboard_ actions on the analysis page (experimental) +- Formatted the name in the _Financial Modeling Prep_ service - Removed the exchange rates from the overview of the admin control panel - Improved the language localization for German (`de`) - Upgraded `angular` from version `19.0.5` to `19.2.1` 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 5216ed214..119e8ea67 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 @@ -12,6 +12,7 @@ import { IDataProviderHistoricalResponse, IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; +import { REPLACE_NAME_PARTS } from '@ghostfolio/common/config'; import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; import { DataProviderInfo, @@ -186,7 +187,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { response.isin = assetProfile.isin; } - response.name = assetProfile.companyName; + response.name = this.formatName({ name: assetProfile.companyName }); if (assetProfile.website) { response.url = assetProfile.website; @@ -398,7 +399,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { assetSubClass: undefined, // TODO dataProviderInfo: this.getDataProviderInfo(), dataSource: this.getName(), - name: companyName + name: this.formatName({ name: companyName }) }; }); } else { @@ -414,12 +415,12 @@ export class FinancialModelingPrepService implements DataProviderInterface { items = result.map(({ currency, name, symbol }) => { return { currency, - name, symbol, assetClass: undefined, // TODO assetSubClass: undefined, // TODO dataProviderInfo: this.getDataProviderInfo(), - dataSource: this.getName() + dataSource: this.getName(), + name: this.formatName({ name }) }; }); } @@ -438,6 +439,18 @@ export class FinancialModelingPrepService implements DataProviderInterface { return { items }; } + private formatName({ name }: { name: string }) { + if (name) { + for (const part of REPLACE_NAME_PARTS) { + name = name.replace(part, ''); + } + + name = name.trim(); + } + + return name; + } + private getUrl({ version }: { version: number }) { return `https://financialmodelingprep.com/api/v${version}`; } From 0cc367478051f909dbe0365b36da3d7e252dc329 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 15 Mar 2025 18:47:33 +0100 Subject: [PATCH 08/56] Feature/upgrade prisma to version 6.5.0 (#4440) * Upgrade prisma to version 6.5.0 * Update changelog --- CHANGELOG.md | 1 + package-lock.json | 78 ++++++++++++++++++++++++++--------------------- package.json | 4 +-- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9def6ab50..d08e8b7e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `angular` from version `19.0.5` to `19.2.1` - Upgraded `Nx` from version `20.3.2` to `20.5.0` - Upgraded `prettier` from version `3.5.1` to `3.5.3` +- Upgraded `prisma` from version `6.4.1` to `6.5.0` ### Fixed diff --git a/package-lock.json b/package-lock.json index 59f7d2dde..305eb185d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "@nestjs/platform-express": "10.4.15", "@nestjs/schedule": "4.1.2", "@nestjs/serve-static": "4.0.2", - "@prisma/client": "6.4.1", + "@prisma/client": "6.5.0", "@simplewebauthn/browser": "13.1.0", "@simplewebauthn/server": "13.1.1", "@stripe/stripe-js": "5.4.0", @@ -149,7 +149,7 @@ "nx": "20.5.0", "prettier": "3.5.3", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.4.1", + "prisma": "6.5.0", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", @@ -8641,9 +8641,9 @@ "license": "MIT" }, "node_modules/@prisma/client": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.4.1.tgz", - "integrity": "sha512-A7Mwx44+GVZVexT5e2GF/WcKkEkNNKbgr059xpr5mn+oUm2ZW1svhe+0TRNBwCdzhfIZ+q23jEgsNPvKD9u+6g==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.5.0.tgz", + "integrity": "sha512-M6w1Ql/BeiGoZmhMdAZUXHu5sz5HubyVcKukbLs3l0ELcQb8hTUJxtGEChhv4SVJ0QJlwtLnwOLgIRQhpsm9dw==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { @@ -8662,54 +8662,65 @@ } } }, + "node_modules/@prisma/config": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.5.0.tgz", + "integrity": "sha512-sOH/2Go9Zer67DNFLZk6pYOHj+rumSb0VILgltkoxOjYnlLqUpHPAN826vnx8HigqnOCxj9LRhT6U7uLiIIWgw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "esbuild": ">=0.12 <1", + "esbuild-register": "3.6.0" + } + }, "node_modules/@prisma/debug": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.4.1.tgz", - "integrity": "sha512-Q9xk6yjEGIThjSD8zZegxd5tBRNHYd13GOIG0nLsanbTXATiPXCLyvlYEfvbR2ft6dlRsziQXfQGxAgv7zcMUA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.5.0.tgz", + "integrity": "sha512-fc/nusYBlJMzDmDepdUtH9aBsJrda2JNErP9AzuHbgUEQY0/9zQYZdNlXmKoIWENtio+qarPNe/+DQtrX5kMcQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.4.1.tgz", - "integrity": "sha512-KldENzMHtKYwsOSLThghOIdXOBEsfDuGSrxAZjMnimBiDKd3AE4JQ+Kv+gBD/x77WoV9xIPf25GXMWffXZ17BA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.5.0.tgz", + "integrity": "sha512-FVPQYHgOllJklN9DUyujXvh3hFJCY0NX86sDmBErLvoZjy2OXGiZ5FNf3J/C4/RZZmCypZBYpBKEhx7b7rEsdw==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.4.1", - "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", - "@prisma/fetch-engine": "6.4.1", - "@prisma/get-platform": "6.4.1" + "@prisma/debug": "6.5.0", + "@prisma/engines-version": "6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60", + "@prisma/fetch-engine": "6.5.0", + "@prisma/get-platform": "6.5.0" } }, "node_modules/@prisma/engines-version": { - "version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d.tgz", - "integrity": "sha512-Xq54qw55vaCGrGgIJqyDwOq0TtjZPJEWsbQAHugk99hpDf2jcEeQhUcF+yzEsSqegBaDNLA4IC8Nn34sXmkiTQ==", + "version": "6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60.tgz", + "integrity": "sha512-iK3EmiVGFDCmXjSpdsKGNqy9hOdLnvYBrJB61far/oP03hlIxrb04OWmDjNTwtmZ3UZdA5MCvI+f+3k2jPTflQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.4.1.tgz", - "integrity": "sha512-uZ5hVeTmDspx7KcaRCNoXmcReOD+84nwlO2oFvQPRQh9xiFYnnUKDz7l9bLxp8t4+25CsaNlgrgilXKSQwrIGQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.5.0.tgz", + "integrity": "sha512-3LhYA+FXP6pqY8FLHCjewyE8pGXXJ7BxZw2rhPq+CZAhvflVzq4K8Qly3OrmOkn6wGlz79nyLQdknyCG2HBTuA==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.4.1", - "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", - "@prisma/get-platform": "6.4.1" + "@prisma/debug": "6.5.0", + "@prisma/engines-version": "6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60", + "@prisma/get-platform": "6.5.0" } }, "node_modules/@prisma/get-platform": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.4.1.tgz", - "integrity": "sha512-gXqZaDI5scDkBF8oza7fOD3Q3QMD0e0rBynlzDDZdTWbWmzjuW58PRZtj+jkvKje2+ZigCWkH8SsWZAsH6q1Yw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.5.0.tgz", + "integrity": "sha512-xYcvyJwNMg2eDptBYFqFLUCfgi+wZLcj6HDMsj0Qw0irvauG4IKmkbywnqwok0B+k+W+p+jThM2DKTSmoPCkzw==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.4.1" + "@prisma/debug": "6.5.0" } }, "node_modules/@redis/bloom": { @@ -28133,16 +28144,15 @@ } }, "node_modules/prisma": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.4.1.tgz", - "integrity": "sha512-q2uJkgXnua/jj66mk6P9bX/zgYJFI/jn4Yp0aS6SPRrjH/n6VyOV7RDe1vHD0DX8Aanx4MvgmUPPoYnR6MJnPg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.5.0.tgz", + "integrity": "sha512-yUGXmWqv5F4PByMSNbYFxke/WbnyTLjnJ5bKr8fLkcnY7U5rU9rUTh/+Fja+gOrRxEgtCbCtca94IeITj4j/pg==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/engines": "6.4.1", - "esbuild": ">=0.12 <1", - "esbuild-register": "3.6.0" + "@prisma/config": "6.5.0", + "@prisma/engines": "6.5.0" }, "bin": { "prisma": "build/index.js" diff --git a/package.json b/package.json index 6a96bed12..d0b3e1644 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "@nestjs/platform-express": "10.4.15", "@nestjs/schedule": "4.1.2", "@nestjs/serve-static": "4.0.2", - "@prisma/client": "6.4.1", + "@prisma/client": "6.5.0", "@simplewebauthn/browser": "13.1.0", "@simplewebauthn/server": "13.1.1", "@stripe/stripe-js": "5.4.0", @@ -195,7 +195,7 @@ "nx": "20.5.0", "prettier": "3.5.3", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.4.1", + "prisma": "6.5.0", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.3.0", From 3efe06409e1e859d71d83606e33872d73bdda7ab Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 15 Mar 2025 19:36:10 +0100 Subject: [PATCH 09/56] Feature/improve language localization for de 20250315 (#4442) * Update translations --- apps/client/src/locales/messages.ca.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.de.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.es.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.fr.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.it.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.nl.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.pl.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.pt.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.tr.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.uk.xlf | 98 ++++++++++--------------- apps/client/src/locales/messages.xlf | 96 ++++++++++-------------- apps/client/src/locales/messages.zh.xlf | 98 ++++++++++--------------- 12 files changed, 444 insertions(+), 730 deletions(-) diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index 8be91baf3..0f9de4906 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -1185,10 +1185,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -1219,11 +1215,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1967,15 +1959,7 @@ Està segur qeu vol eliminar aquest cupó? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - Està segur que vol eliminar aquesta divisa? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -1983,7 +1967,7 @@ Està segur que vol eliminar aquest missatge del sistema? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -1991,7 +1975,7 @@ Està segur que vol depurar el cache? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -1999,7 +1983,7 @@ Si us plau, afegeixi el seu missatge del sistema: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -2034,14 +2018,6 @@ 28 - - Exchange Rates - Tipus de Canvi - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency Afegir Divisa @@ -2055,7 +2031,7 @@ Registrar Usuari apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -2063,7 +2039,7 @@ Mode Només Lecutra apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -2071,7 +2047,7 @@ Recollida de Dades apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -2079,7 +2055,7 @@ Missatge del Sistema apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -2087,7 +2063,7 @@ Estableix el Missatge apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -2095,7 +2071,7 @@ Coupons apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -2103,7 +2079,7 @@ Afegir apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -2115,7 +2091,7 @@ Ordre apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -2123,7 +2099,7 @@ Depurar el Cache apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -5063,7 +5039,7 @@ Dividend apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -5075,11 +5051,11 @@ Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -5091,7 +5067,7 @@ Monthly apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -5099,7 +5075,7 @@ Yearly apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -5115,7 +5091,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -5123,7 +5099,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -5131,7 +5107,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -5139,7 +5115,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -5147,7 +5123,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -5155,7 +5131,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -5163,7 +5139,7 @@ Top apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -5171,7 +5147,7 @@ Bottom apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -5179,7 +5155,7 @@ Portfolio Evolution apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -5187,7 +5163,7 @@ Investment Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -5195,7 +5171,7 @@ Current Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -5203,7 +5179,7 @@ Longest Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -5211,7 +5187,7 @@ Dividend Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -7613,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7709,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7748,12 +7724,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7761,7 +7737,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index 882a7d251..223253bae 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -264,10 +264,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -298,11 +294,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -626,15 +618,7 @@ Möchtest du diesen Gutscheincode wirklich löschen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - Möchtest du diese Währung wirklich löschen? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -642,7 +626,7 @@ Möchtest du den Cache wirklich leeren? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -650,7 +634,7 @@ Bitte gebe deine Systemmeldung ein: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -697,14 +681,6 @@ 45 - - Exchange Rates - Wechselkurse - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency Währung hinzufügen @@ -718,7 +694,7 @@ Systemmeldung apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -726,7 +702,7 @@ Systemmeldung setzen apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -734,7 +710,7 @@ Lese-Modus apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -742,7 +718,7 @@ Gutscheincodes apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -750,7 +726,7 @@ Hinzufügen apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -762,7 +738,7 @@ Verwaltung apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -770,7 +746,7 @@ Cache leeren apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -2142,7 +2118,7 @@ Zeitstrahl der Investitionen apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -2150,7 +2126,7 @@ Gewinner apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -2158,7 +2134,7 @@ Verlierer apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -2786,7 +2762,7 @@ Monatlich apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -2978,7 +2954,7 @@ Portfolio Wertentwicklung apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -3298,7 +3274,7 @@ Zeitstrahl der Dividenden apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -3306,7 +3282,7 @@ Dividenden apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -3326,7 +3302,7 @@ Benutzer Registrierung apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -3414,7 +3390,7 @@ Jährlich apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4162,7 +4138,7 @@ Aktueller Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -4170,7 +4146,7 @@ Längster Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -6135,7 +6111,7 @@ Möchtest du diese Systemmeldung wirklich löschen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -6291,11 +6267,11 @@ Einlage apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6307,7 +6283,7 @@ Absolute Anlage Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -6315,7 +6291,7 @@ Anlage Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -6323,7 +6299,7 @@ Absolute Währungsperformance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -6331,7 +6307,7 @@ Währungsperformance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -6339,7 +6315,7 @@ Absolute Netto Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -6347,7 +6323,7 @@ Netto Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -6455,7 +6431,7 @@ Daten einholen apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -7613,7 +7589,7 @@ KI-Anweisung wurde in die Zwischenablage kopiert apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7709,7 +7685,7 @@ Öffne Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7748,12 +7724,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Kopiere Portfolio-Daten in die Zwischenablage für KI-Anweisung apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7761,7 +7737,7 @@ Kopiere KI-Anweisung in die Zwischenablage für Analyse apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index 60ba7f15b..a763a3902 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -265,10 +265,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -299,11 +295,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -627,15 +619,7 @@ ¿Estás seguro de eliminar este cupón? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - ¿Estás seguro de eliminar esta divisa? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -643,7 +627,7 @@ ¿Estás seguro de limpiar la caché? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -651,7 +635,7 @@ Por favor, establece tu mensaje del sistema: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -698,14 +682,6 @@ 45 - - Exchange Rates - Tipos de cambio - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency Añadir divisa @@ -719,7 +695,7 @@ Mensaje del sistema apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -727,7 +703,7 @@ Establecer mensaje apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -735,7 +711,7 @@ Modo de solo lectura apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -743,7 +719,7 @@ Cupones apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -751,7 +727,7 @@ Añadir apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -763,7 +739,7 @@ Tareas domésticas apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -771,7 +747,7 @@ Limpiar caché apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -2143,7 +2119,7 @@ Cronología de la inversión apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -2151,7 +2127,7 @@ Lo mejor apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -2159,7 +2135,7 @@ Lo peor apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -2815,7 +2791,7 @@ Mensual apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -2979,7 +2955,7 @@ Evolución cartera apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -3299,7 +3275,7 @@ Dividendo apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -3311,7 +3287,7 @@ Calendario de dividendos apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -3327,7 +3303,7 @@ Registro de usuario apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -3415,7 +3391,7 @@ Anual apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4163,7 +4139,7 @@ Current Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -4171,7 +4147,7 @@ Longest Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -6136,7 +6112,7 @@ Do you really want to delete this system message? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -6292,11 +6268,11 @@ Inversión apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6308,7 +6284,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -6316,7 +6292,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -6324,7 +6300,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -6332,7 +6308,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -6340,7 +6316,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -6348,7 +6324,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -6456,7 +6432,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -7614,7 +7590,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7710,7 +7686,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7749,12 +7725,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7762,7 +7738,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index da1592a72..0e7b6b9aa 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -320,10 +320,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -354,11 +350,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -882,15 +874,7 @@ Voulez-vous vraiment supprimer ce code promotionnel ? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - Voulez-vous vraiment supprimer cette devise ? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -898,7 +882,7 @@ Voulez-vous vraiment vider le cache ? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -906,7 +890,7 @@ Veuillez définir votre message système : apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -933,14 +917,6 @@ 28 - - Exchange Rates - Taux de Conversion - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency Ajouter Devise @@ -970,7 +946,7 @@ Inscription de Nouveaux Utilisateurs apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -978,7 +954,7 @@ Mode Lecture Seule apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -986,7 +962,7 @@ Message Système apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -994,7 +970,7 @@ Définir Message apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -1002,7 +978,7 @@ Codes promotionnels apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -1010,7 +986,7 @@ Ajouter apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1022,7 +998,7 @@ Maintenance apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -1030,7 +1006,7 @@ Vider le Cache apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -2742,7 +2718,7 @@ Dividende apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -2762,7 +2738,7 @@ Mensuel apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -2778,7 +2754,7 @@ Haut apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -2786,7 +2762,7 @@ Bas apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -2794,7 +2770,7 @@ Évolution du Portefeuille apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -2802,7 +2778,7 @@ Historique des Investissements apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -2810,7 +2786,7 @@ Historique des Dividendes apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -3414,7 +3390,7 @@ Annuel apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4162,7 +4138,7 @@ Série en cours apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -4170,7 +4146,7 @@ Série la plus longue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -6135,7 +6111,7 @@ Confirmer la suppresion de ce message système? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -6291,11 +6267,11 @@ Investissement apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6307,7 +6283,7 @@ Performance des Actifs en valeur absolue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -6315,7 +6291,7 @@ Performance des Actifs apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -6323,7 +6299,7 @@ Performance des devises en valeur absolue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -6331,7 +6307,7 @@ Performance des devises apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -6339,7 +6315,7 @@ Performance nette absolue apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -6347,7 +6323,7 @@ Performance nette apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -6455,7 +6431,7 @@ Collecter les données apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -7613,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7709,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7748,12 +7724,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7761,7 +7737,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index 0620c018c..4ab6c53a1 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -265,10 +265,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -299,11 +295,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -627,15 +619,7 @@ Vuoi davvero eliminare questo buono? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - Vuoi davvero eliminare questa valuta? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -643,7 +627,7 @@ Vuoi davvero svuotare la cache? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -651,7 +635,7 @@ Imposta il messaggio di sistema: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -698,14 +682,6 @@ 45 - - Exchange Rates - Tassi di cambio - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency Aggiungi valuta @@ -719,7 +695,7 @@ Messaggio di sistema apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -727,7 +703,7 @@ Imposta messaggio apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -735,7 +711,7 @@ Modalità di sola lettura apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -743,7 +719,7 @@ Buoni sconto apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -751,7 +727,7 @@ Aggiungi apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -763,7 +739,7 @@ Bilancio domestico apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -771,7 +747,7 @@ Svuota la cache apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -2143,7 +2119,7 @@ Cronologia degli investimenti apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -2151,7 +2127,7 @@ In alto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -2159,7 +2135,7 @@ In basso apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -2815,7 +2791,7 @@ Mensile apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -2979,7 +2955,7 @@ Evoluzione del portafoglio apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -3299,7 +3275,7 @@ Dividendi apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -3311,7 +3287,7 @@ Cronologia dei dividendi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -3327,7 +3303,7 @@ Registrazione utente apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -3415,7 +3391,7 @@ Annuale apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4163,7 +4139,7 @@ Serie attuale apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -4171,7 +4147,7 @@ Serie più lunga apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -6136,7 +6112,7 @@ Confermi di voler cancellare questo messaggio di sistema? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -6292,11 +6268,11 @@ Investimento apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6308,7 +6284,7 @@ Rendimento assoluto dell'Asset apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -6316,7 +6292,7 @@ Rendimento dell'Asset apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -6324,7 +6300,7 @@ Rendimento assoluto della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -6332,7 +6308,7 @@ Rendimento della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -6340,7 +6316,7 @@ Rendimento assoluto della Valuta apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -6348,7 +6324,7 @@ Rendimento Netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -6456,7 +6432,7 @@ Raccolta Dati apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -7614,7 +7590,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7710,7 +7686,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7749,12 +7725,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7762,7 +7738,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index 61832f7fa..13671956d 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -264,10 +264,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -298,11 +294,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -626,15 +618,7 @@ Wil je deze coupon echt verwijderen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - Wil je deze valuta echt verwijderen? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -642,7 +626,7 @@ Wil je echt de cache legen? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -650,7 +634,7 @@ Stel je systeemboodschap in: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -697,14 +681,6 @@ 45 - - Exchange Rates - Wisselkoersen - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency Valuta toevoegen @@ -718,7 +694,7 @@ Systeembericht apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -726,7 +702,7 @@ Bericht instellen apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -734,7 +710,7 @@ Alleen lezen apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -742,7 +718,7 @@ Coupons apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -750,7 +726,7 @@ Toevoegen apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -762,7 +738,7 @@ Huishouding apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -770,7 +746,7 @@ Cache legen apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -2142,7 +2118,7 @@ Tijdlijn investeringen apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -2150,7 +2126,7 @@ Winnaars apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -2158,7 +2134,7 @@ Verliezers apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -2814,7 +2790,7 @@ Maandelijks apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -2978,7 +2954,7 @@ Waardeontwikkeling van portefeuille apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -3298,7 +3274,7 @@ Dividend apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -3310,7 +3286,7 @@ Tijdlijn dividend apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -3326,7 +3302,7 @@ Account aanmaken apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -3414,7 +3390,7 @@ Jaarlijks apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4162,7 +4138,7 @@ Huidige reeks apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -4170,7 +4146,7 @@ Langste reeks apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -6135,7 +6111,7 @@ Do you really want to delete this system message? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -6291,11 +6267,11 @@ Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6307,7 +6283,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -6315,7 +6291,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -6323,7 +6299,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -6331,7 +6307,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -6339,7 +6315,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -6347,7 +6323,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -6455,7 +6431,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -7613,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7709,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7748,12 +7724,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7761,7 +7737,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index c071300bd..ac208d13a 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -1113,10 +1113,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -1147,11 +1143,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1791,15 +1783,7 @@ Czy naprawdę chcesz usunąć ten kupon? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - Czy naprawdę chcesz usunąć tę walutę? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -1807,7 +1791,7 @@ Czy naprawdę chcesz usunąć tę wiadomość systemową? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -1815,7 +1799,7 @@ Czy naprawdę chcesz wyczyścić pamięć podręczną? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -1823,7 +1807,7 @@ Proszę ustawić swoją wiadomość systemową: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -1858,14 +1842,6 @@ 28 - - Exchange Rates - Kursy Walut - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency Dodaj Walutę @@ -1879,7 +1855,7 @@ Rejestracja Użytkownika apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -1887,7 +1863,7 @@ Tryb Tylko do Odczytu apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -1895,7 +1871,7 @@ Wiadomość Systemowa apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -1903,7 +1879,7 @@ Ustaw Wiadomość apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -1911,7 +1887,7 @@ Kupony apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -1919,7 +1895,7 @@ Dodaj apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1931,7 +1907,7 @@ Konserwacja apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -1939,7 +1915,7 @@ Wyczyszczenie pamięci podręcznej apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -4663,7 +4639,7 @@ Dywidenda apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -4683,7 +4659,7 @@ Miesięcznie apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -4691,7 +4667,7 @@ Rocznie apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4707,7 +4683,7 @@ Największe wzrosty apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -4715,7 +4691,7 @@ Największy spadek apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -4723,7 +4699,7 @@ Rozwój portfela apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -4731,7 +4707,7 @@ Oś czasu inwestycji apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -4739,7 +4715,7 @@ Obecna passa apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -4747,7 +4723,7 @@ Najdłuższa passa apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -4755,7 +4731,7 @@ Oś czasu dywidend apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -6291,11 +6267,11 @@ Inwestycje apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6307,7 +6283,7 @@ Łączny wynik aktywów apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -6315,7 +6291,7 @@ Wyniki aktywów apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -6323,7 +6299,7 @@ Łączny wynik walut apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -6331,7 +6307,7 @@ Wynik walut apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -6339,7 +6315,7 @@ Łączna wartość netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -6347,7 +6323,7 @@ Wynik netto apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -6455,7 +6431,7 @@ Gromadzenie Danych apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -7613,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7709,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7748,12 +7724,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7761,7 +7737,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index 7ce4f9062..789ee6360 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -320,10 +320,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -354,11 +350,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -786,15 +778,7 @@ Deseja realmente eliminar este cupão? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - Deseja realmente excluir esta moeda? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -802,7 +786,7 @@ Deseja realmente limpar a cache? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -810,7 +794,7 @@ Por favor, defina a sua mensagem do sistema: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -829,14 +813,6 @@ 28 - - Exchange Rates - Taxas de Câmbio - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency Adicionar Moeda @@ -850,7 +826,7 @@ Mensagem de Sistema apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -858,7 +834,7 @@ Definir Mensagem apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -866,7 +842,7 @@ Modo Somente Leitura apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -874,7 +850,7 @@ Cupões apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -882,7 +858,7 @@ Adicionar apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -894,7 +870,7 @@ Manutenção apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -902,7 +878,7 @@ Limpar Cache apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -2646,7 +2622,7 @@ Mensalmente apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -2662,7 +2638,7 @@ Topo apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -2670,7 +2646,7 @@ Fundo apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -2678,7 +2654,7 @@ Evolução do Portefólio apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -2686,7 +2662,7 @@ Cronograma de Investimento apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -3258,7 +3234,7 @@ Registo do Utilizador apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -3366,7 +3342,7 @@ Dividendos apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -3378,7 +3354,7 @@ Cronograma de Dividendos apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -3414,7 +3390,7 @@ Anualmente apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4162,7 +4138,7 @@ Série Atual apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -4170,7 +4146,7 @@ Série mais Longa apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -6135,7 +6111,7 @@ Do you really want to delete this system message? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -6291,11 +6267,11 @@ Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6307,7 +6283,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -6315,7 +6291,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -6323,7 +6299,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -6331,7 +6307,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -6339,7 +6315,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -6347,7 +6323,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -6455,7 +6431,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -7613,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7709,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7748,12 +7724,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7761,7 +7737,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index 8c646d8b9..05e2566f8 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -1073,10 +1073,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -1107,11 +1103,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1695,15 +1687,7 @@ Önbelleği temizlemeyi gerçekten istiyor musunuz? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - Bu para birimini silmeyi gerçekten istiyor musunuz? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -1711,7 +1695,7 @@ Önbelleği temizlemeyi gerçekten istiyor musunuz apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -1719,7 +1703,7 @@ Lütfen sistem mesajınızı belirleyin: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -1746,14 +1730,6 @@ 28 - - Exchange Rates - Döviz Kurları - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency Para Birimi Ekle @@ -1783,7 +1759,7 @@ Kullanıcı Kaydı apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -1791,7 +1767,7 @@ Salt okunur mod apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -1799,7 +1775,7 @@ Sistem Mesajı apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -1807,7 +1783,7 @@ Mesaj Belirle apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -1815,7 +1791,7 @@ Kupon apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -1823,7 +1799,7 @@ Ekle apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1835,7 +1811,7 @@ Genel Ayarlar apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -1843,7 +1819,7 @@ Önbelleği temizle apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -4143,7 +4119,7 @@ Temettü apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -4163,7 +4139,7 @@ Aylık apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -4171,7 +4147,7 @@ Yıllık apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4187,7 +4163,7 @@ Üst apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -4195,7 +4171,7 @@ Alt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -4203,7 +4179,7 @@ Portföyün Gelişimi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -4211,7 +4187,7 @@ Yatırım Zaman Çizelgesi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -4219,7 +4195,7 @@ Güncel Seri apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -4227,7 +4203,7 @@ En Uzun Seri apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -4235,7 +4211,7 @@ Temettü Zaman Çizelgesi apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -6135,7 +6111,7 @@ Bu sistem mesajını silmeyi gerçekten istiyor musunuz? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -6291,11 +6267,11 @@ Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6307,7 +6283,7 @@ Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -6315,7 +6291,7 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -6323,7 +6299,7 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -6331,7 +6307,7 @@ Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -6339,7 +6315,7 @@ Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -6347,7 +6323,7 @@ Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -6455,7 +6431,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -7613,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7709,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7748,12 +7724,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7761,7 +7737,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.uk.xlf b/apps/client/src/locales/messages.uk.xlf index dba73fe29..94d3893ba 100644 --- a/apps/client/src/locales/messages.uk.xlf +++ b/apps/client/src/locales/messages.uk.xlf @@ -1201,10 +1201,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -1235,11 +1231,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1963,15 +1955,7 @@ Ви дійсно хочете видалити цей купон? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - Ви дійсно хочете видалити цю валюту? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -1979,7 +1963,7 @@ Ви дійсно хочете видалити це системне повідомлення? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -1987,7 +1971,7 @@ Ви дійсно хочете очистити кеш? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -1995,7 +1979,7 @@ Будь ласка, встановіть ваше системне повідомлення: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -2030,20 +2014,12 @@ 28 - - Exchange Rates - Обмінні курси - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - User Signup Реєстрація користувача apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -2051,7 +2027,7 @@ Режим лише для читання apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -2059,7 +2035,7 @@ Збір даних apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -2067,7 +2043,7 @@ Системне повідомлення apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -2075,7 +2051,7 @@ Встановити повідомлення apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -2083,7 +2059,7 @@ Купони apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -2091,7 +2067,7 @@ Додати apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -2103,7 +2079,7 @@ Прибирання apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -2111,7 +2087,7 @@ Очистити кеш apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -5339,7 +5315,7 @@ Дивіденди apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -5351,11 +5327,11 @@ Інвестиції apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -5367,7 +5343,7 @@ Щомісячно apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -5375,7 +5351,7 @@ Щорічно apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -5391,7 +5367,7 @@ Абсолютна прибутковість активів apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -5399,7 +5375,7 @@ Прибутковість активів apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -5407,7 +5383,7 @@ Абсолютна прибутковість валюти apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -5415,7 +5391,7 @@ Прибутковість валюти apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -5423,7 +5399,7 @@ Абсолютна чиста прибутковість apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -5431,7 +5407,7 @@ Чиста прибутковість apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -5439,7 +5415,7 @@ Топ apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -5447,7 +5423,7 @@ Низ apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -5455,7 +5431,7 @@ Еволюція портфеля apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -5463,7 +5439,7 @@ Інвестиційний графік apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -5471,7 +5447,7 @@ Поточна серія apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -5479,7 +5455,7 @@ Найдовша серія apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -5487,7 +5463,7 @@ Графік дивідендів apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -7621,7 +7597,7 @@ Запит AI скопійовано в буфер обміну apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7709,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7748,12 +7724,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7761,7 +7737,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index e767e2266..c1ec934be 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -1081,10 +1081,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -1114,11 +1110,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1708,35 +1700,28 @@ Do you really want to delete this coupon? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 Do you really want to delete this system message? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 Do you really want to flush the cache? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 Please set your system message: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -1767,13 +1752,6 @@ 28 - - Exchange Rates - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency @@ -1785,42 +1763,42 @@ User Signup apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 Read-only Mode apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 System Message apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 Set Message apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 Coupons apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 Add apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1831,14 +1809,14 @@ Housekeeping apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 Flush Cache apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -4284,7 +4262,7 @@ Dividend apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -4302,14 +4280,14 @@ Monthly apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 Yearly apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4323,49 +4301,49 @@ Top apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 Bottom apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 Portfolio Evolution apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 Investment Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 Current Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 Longest Streak apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 Dividend Timeline apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -5727,32 +5705,32 @@ Absolute Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 Absolute Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 Absolute Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 Investment apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -5763,21 +5741,21 @@ Asset Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 Net Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 Currency Performance apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -5901,7 +5879,7 @@ Data Gathering apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -6889,7 +6867,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -6973,7 +6951,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7012,7 +6990,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 @@ -7036,11 +7014,11 @@ 77 - + Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index cb1c62a50..6d6054df6 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -1122,10 +1122,6 @@ apps/client/src/app/components/admin-market-data/admin-market-data.html 231 - - apps/client/src/app/components/admin-overview/admin-overview.html - 78 - apps/client/src/app/components/admin-platform/admin-platform.component.html 92 @@ -1156,11 +1152,7 @@ apps/client/src/app/components/admin-overview/admin-overview.html - 89 - - - apps/client/src/app/components/admin-overview/admin-overview.html - 196 + 129 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1800,15 +1792,7 @@ 您确实要删除此优惠券吗? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 137 - - - - Do you really want to delete this currency? - 您真的要删除该货币吗? - - apps/client/src/app/components/admin-overview/admin-overview.component.ts - 150 + 134 @@ -1816,7 +1800,7 @@ 您真的要删除这条系统消息吗? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 163 + 147 @@ -1824,7 +1808,7 @@ 您真的要刷新缓存吗? apps/client/src/app/components/admin-overview/admin-overview.component.ts - 187 + 171 @@ -1832,7 +1816,7 @@ 请设置您的系统消息: apps/client/src/app/components/admin-overview/admin-overview.component.ts - 207 + 191 @@ -1867,14 +1851,6 @@ 28 - - Exchange Rates - 汇率 - - apps/client/src/app/components/admin-overview/admin-overview.html - 34 - - Add Currency 添加货币 @@ -1888,7 +1864,7 @@ 用户注册 apps/client/src/app/components/admin-overview/admin-overview.html - 101 + 34 @@ -1896,7 +1872,7 @@ 只读模式 apps/client/src/app/components/admin-overview/admin-overview.html - 115 + 48 @@ -1904,7 +1880,7 @@ 系统信息 apps/client/src/app/components/admin-overview/admin-overview.html - 139 + 72 @@ -1912,7 +1888,7 @@ 设置留言 apps/client/src/app/components/admin-overview/admin-overview.html - 161 + 94 @@ -1920,7 +1896,7 @@ 优惠券 apps/client/src/app/components/admin-overview/admin-overview.html - 169 + 102 @@ -1928,7 +1904,7 @@ 添加 apps/client/src/app/components/admin-overview/admin-overview.html - 229 + 162 libs/ui/src/lib/account-balances/account-balances.component.html @@ -1940,7 +1916,7 @@ 家政 apps/client/src/app/components/admin-overview/admin-overview.html - 237 + 170 @@ -1948,7 +1924,7 @@ 刷新缓存 apps/client/src/app/components/admin-overview/admin-overview.html - 241 + 174 @@ -4672,7 +4648,7 @@ 股息 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 42 + 51 libs/ui/src/lib/i18n.ts @@ -4692,7 +4668,7 @@ 每月 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 55 + 66 @@ -4700,7 +4676,7 @@ 每年 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 56 + 67 @@ -4716,7 +4692,7 @@ 顶部 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 215 + 239 @@ -4724,7 +4700,7 @@ 底部 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 264 + 288 @@ -4732,7 +4708,7 @@ 投资组合演变 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 317 + 341 @@ -4740,7 +4716,7 @@ 投资时间表 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 344 + 368 @@ -4748,7 +4724,7 @@ 当前连胜 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 365 + 389 @@ -4756,7 +4732,7 @@ 最长连续纪录 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 374 + 398 @@ -4764,7 +4740,7 @@ 股息时间表 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 401 + 425 @@ -6292,7 +6268,7 @@ 绝对货币表现 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 121 + 145 @@ -6300,7 +6276,7 @@ 绝对净性能 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 168 + 192 @@ -6308,7 +6284,7 @@ 绝对资产绩效 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 78 + 102 @@ -6316,11 +6292,11 @@ 投资 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 47 + 56 apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 61 + 72 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -6332,7 +6308,7 @@ 资产绩效 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 99 + 123 @@ -6340,7 +6316,7 @@ 净绩效 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 187 + 211 @@ -6348,7 +6324,7 @@ 货币表现 apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 145 + 169 @@ -6488,7 +6464,7 @@ 数据收集 apps/client/src/app/components/admin-overview/admin-overview.html - 127 + 60 @@ -7614,7 +7590,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 153 + 170 @@ -7710,7 +7686,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 154 + 171 @@ -7749,12 +7725,12 @@ 378 - + Copy portfolio data to clipboard for AI prompt Copy portfolio data to clipboard for AI prompt apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 28 + 42 @@ -7762,7 +7738,7 @@ Copy AI prompt to clipboard for analysis apps/client/src/app/pages/portfolio/analysis/analysis-page.html - 44 + 67 From 29b8d63951e8faaee63ab2faa683a1b8a9667cc1 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 15 Mar 2025 19:39:07 +0100 Subject: [PATCH 10/56] Release 2.146.0 (#4444) --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d08e8b7e7..b52353a35 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.146.0 - 2025-03-15 ### Changed diff --git a/package-lock.json b/package-lock.json index 305eb185d..a6e4d4dd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.145.1", + "version": "2.146.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.145.1", + "version": "2.146.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index d0b3e1644..eb3a7083d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.145.1", + "version": "2.146.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From 5783497a66eec1e83d34a69696e27fc889ce6ca3 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 16 Mar 2025 10:42:35 +0100 Subject: [PATCH 11/56] Feature/improve symbol validation in Yahoo Finance service (#4445) * Improve symbol validation * Update changelog --- CHANGELOG.md | 6 ++++++ .../data-enhancer/yahoo-finance/yahoo-finance.service.ts | 2 ++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b52353a35..950a4d65d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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 + +### Changed + +- Improved the symbol validation in the _Yahoo Finance_ service (get asset profiles) + ## 2.146.0 - 2025-03-15 ### Changed diff --git a/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts index 18d624098..78644726a 100644 --- a/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts @@ -170,6 +170,8 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface { symbol = quotes[0].symbol; } } catch {} + } else if (symbol?.includes('-')) { + throw new Error(`${symbol} is not valid`); } else { symbol = this.convertToYahooFinanceSymbol(symbol); } From 6cd79fab310fe348c2faeb8ba8c81793b1d6988d Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 16 Mar 2025 20:59:21 +0100 Subject: [PATCH 12/56] Feature/rename TWR to ROAI (#4453) * Rename TWR to ROAI --- .../calculator/portfolio-calculator.factory.ts | 8 ++++---- ...culator-baln-buy-and-sell-in-two-activities.spec.ts | 2 +- .../portfolio-calculator-baln-buy-and-sell.spec.ts | 2 +- .../portfolio-calculator-baln-buy.spec.ts | 2 +- ...io-calculator-btcusd-buy-and-sell-partially.spec.ts | 2 +- .../{twr => roai}/portfolio-calculator-fee.spec.ts | 2 +- .../portfolio-calculator-googl-buy.spec.ts | 2 +- .../{twr => roai}/portfolio-calculator-item.spec.ts | 2 +- .../portfolio-calculator-liability.spec.ts | 2 +- ...portfolio-calculator-msft-buy-with-dividend.spec.ts | 2 +- .../portfolio-calculator-no-orders.spec.ts | 2 +- ...olio-calculator-novn-buy-and-sell-partially.spec.ts | 2 +- .../portfolio-calculator-novn-buy-and-sell.spec.ts | 2 +- .../{twr => roai}/portfolio-calculator.spec.ts | 0 .../calculator/{twr => roai}/portfolio-calculator.ts | 2 +- apps/api/src/app/portfolio/portfolio.service.ts | 10 +++++----- .../portfolio-snapshot/portfolio-snapshot.processor.ts | 2 +- .../portfolio-summary/portfolio-summary.component.html | 4 ++-- 18 files changed, 25 insertions(+), 25 deletions(-) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts (96%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-baln-buy-and-sell.spec.ts (99%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-baln-buy.spec.ts (99%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts (99%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-fee.spec.ts (99%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-googl-buy.spec.ts (99%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-item.spec.ts (99%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-liability.spec.ts (98%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-msft-buy-with-dividend.spec.ts (99%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-no-orders.spec.ts (98%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-novn-buy-and-sell-partially.spec.ts (99%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator-novn-buy-and-sell.spec.ts (96%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator.spec.ts (100%) rename apps/api/src/app/portfolio/calculator/{twr => roai}/portfolio-calculator.ts (99%) diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts index 18738373e..70f27fe7b 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts @@ -10,11 +10,11 @@ import { Injectable } from '@nestjs/common'; import { MWRPortfolioCalculator } from './mwr/portfolio-calculator'; import { PortfolioCalculator } from './portfolio-calculator'; -import { TWRPortfolioCalculator } from './twr/portfolio-calculator'; +import { RoaiPortfolioCalculator } from './roai/portfolio-calculator'; export enum PerformanceCalculationType { MWR = 'MWR', // Money-Weighted Rate of Return - TWR = 'TWR' // Time-Weighted Rate of Return + ROAI = 'ROAI' // Return on Average Investment } @Injectable() @@ -56,8 +56,8 @@ export class PortfolioCalculatorFactory { portfolioSnapshotService: this.portfolioSnapshotService, redisCacheService: this.redisCacheService }); - case PerformanceCalculationType.TWR: - return new TWRPortfolioCalculator({ + case PerformanceCalculationType.ROAI: + return new RoaiPortfolioCalculator({ accountBalanceItems, activities, currency, diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts similarity index 96% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts index deb3cd72f..e157e2d26 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts @@ -137,7 +137,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts similarity index 99% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts index 7b4d53b2f..a1650ea82 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts @@ -122,7 +122,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts similarity index 99% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts index 002cbd5e9..63a4d77b4 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts @@ -107,7 +107,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts similarity index 99% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts index 640de3985..2853e3d87 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts @@ -136,7 +136,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-fee.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts similarity index 99% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-fee.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts index 6f030a73d..b96e4f540 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-fee.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts @@ -107,7 +107,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'USD', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts similarity index 99% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts index 4e25c17f7..b3793a5b4 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts @@ -120,7 +120,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-item.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts similarity index 99% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-item.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts index 7fc5c526d..d226fe6f8 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-item.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts @@ -107,7 +107,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'USD', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-liability.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts similarity index 98% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-liability.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts index 5fa90e94c..569212b9a 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-liability.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts @@ -107,7 +107,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'USD', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts similarity index 99% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts index 543985424..4c54ba7aa 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts @@ -135,7 +135,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'USD', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts similarity index 98% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts index 84898490f..77e3f6157 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-no-orders.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts @@ -84,7 +84,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities: [], - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts similarity index 99% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts index 37f22e2f6..84bcc5bc1 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts @@ -116,7 +116,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts similarity index 96% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts index caf196f54..937fd8b48 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts @@ -116,7 +116,7 @@ describe('PortfolioCalculator', () => { const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: 'CHF', userId: userDummyData.id }); diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts similarity index 100% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.spec.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.spec.ts diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts similarity index 99% rename from apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts rename to apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts index 059b85441..5b918fa03 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts @@ -15,7 +15,7 @@ import { Big } from 'big.js'; import { addMilliseconds, differenceInDays, format, isBefore } from 'date-fns'; import { cloneDeep, sortBy } from 'lodash'; -export class TWRPortfolioCalculator extends PortfolioCalculator { +export class RoaiPortfolioCalculator extends PortfolioCalculator { private chartDates: string[]; protected calculateOverallPerformance( diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index ce03c8024..e90ebd4ae 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -295,7 +295,7 @@ export class PortfolioService { activities, filters, userId, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: this.request.user.Settings.settings.baseCurrency }); @@ -372,7 +372,7 @@ export class PortfolioService { activities, filters, userId, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: userCurrency }); @@ -680,7 +680,7 @@ export class PortfolioService { const portfolioCalculator = this.calculatorFactory.createCalculator({ activities, userId, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: userCurrency }); @@ -950,7 +950,7 @@ export class PortfolioService { activities, filters, userId, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: this.request.user.Settings.settings.baseCurrency }); @@ -1116,7 +1116,7 @@ export class PortfolioService { activities, filters, userId, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: userCurrency }); diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts index 93b34cbdf..60c3cf695 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts @@ -68,7 +68,7 @@ export class PortfolioSnapshotProcessor { const portfolioCalculator = this.calculatorFactory.createCalculator({ accountBalanceItems, activities, - calculationType: PerformanceCalculationType.TWR, + calculationType: PerformanceCalculationType.ROAI, currency: job.data.userCurrency, filters: job.data.filters, userId: job.data.userId 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 2a8880225..1a52bd646 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 @@ -117,8 +117,8 @@ Net Performance (TWR)(ROAI)
From f17a95eb48ed76aa654cfcbe4b3e2adb761f3c9b Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 16 Mar 2025 20:59:49 +0100 Subject: [PATCH 13/56] Feature/refresh cryptocurrencies list 20250316 (#4449) * Update cryptocurrencies.json * Update changelog --- CHANGELOG.md | 1 + .../cryptocurrencies/cryptocurrencies.json | 684 ++++++++++++++++-- 2 files changed, 622 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 950a4d65d..080a65cf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved the symbol validation in the _Yahoo Finance_ service (get asset profiles) +- Refreshed the cryptocurrencies list ## 2.146.0 - 2025-03-15 diff --git a/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json b/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json index 7d414ef11..6ac25e5d9 100644 --- a/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json +++ b/apps/api/src/assets/cryptocurrencies/cryptocurrencies.json @@ -1,6 +1,7 @@ { "3": "The Three Musketeers", "7": "Lucky7", + "32": "Project 32", "42": "42 Coin", "47": "President Trump", "300": "300 token", @@ -16,15 +17,16 @@ "1717": "1717 Masonic Commemorative Token", "2015": "2015 coin", "2024": "2024", + "2025": "2025 TOKEN", "2049": "TOKEN 2049", "2192": "LERNITAS", "$MAID": "MaidCoin", - "$ROPE": "Rope", "$TREAM": "World Stream Finance", "00": "ZER0ZER0", "007": "007 coin", "0DOG": "Bitcoin Dogs", "0KN": "0 Knowledge Network", + "0LNETWORK": "0L Network", "0NE": "Stone", "0X0": "0x0.ai", "0X1": "0x1.tools: AI Multi-tool Plaform", @@ -42,6 +44,7 @@ "1-UP": "1-UP", "1000SATS": "SATS", "1000X": "1000x by Virtuals", + "101M": "101M", "10SET": "Tenset", "1ART": "ArtWallet", "1CAT": "Bitcoin Cats", @@ -52,6 +55,7 @@ "1FLR": "Flare Token", "1GOLD": "1irstGold", "1GUY": "1GUY", + "1HUB": "1HubAI", "1INCH": "1inch", "1IRST": "1irstcoin", "1MCT": "MicroCreditToken", @@ -93,9 +97,11 @@ "3CRV": "LP 3pool Curve", "3D3D": "3d3d", "3DES": "3DES", + "3DVANCE": "3D Vance", "3FT": "ThreeFold Token", "3KM": "3 Kingdoms Multiverse", "3P": "Web3Camp", + "3RDEYE": "3rd Eye", "3ULL": "3ULL Coin", "3ULLV1": "Playa3ull Games v1", "3XD": "3DChain", @@ -117,6 +123,7 @@ "50TRUMP": "50TRUMP", "50X": "50x.com", "5IRE": "5ire", + "69MINUTES": "69 Minutes", "77G": "GraphenTech", "7E": "7ELEVEN", "88MPH": "88mph", @@ -131,7 +138,9 @@ "A": "Alpha Token", "A1INCH": "1inch (Arbitrum Bridge)", "A2A": "A2A", + "A2I": "Arcana AI", "A4": "A4 Finance", + "A47": "AGENDA 47", "A4M": "AlienForm", "A51": "A51 Finance", "A5T": "Alpha5", @@ -139,6 +148,7 @@ "AA": "Alva", "AAA": "Moon Rabbit", "AAAHHM": "Plankton in Pain", + "AAAI": "AAAI_agent by Virtuals", "AAB": "AAX Token", "AABL": "Abble", "AAC": "Double-A Chain", @@ -233,7 +243,8 @@ "ADA": "Cardano", "ADAB": "Adab Solutions", "ADACASH": "ADACash", - "ADAI": "Aave DAI", + "ADAI": "Aave Interest bearing DAI", + "ADAIV1": "Aave DAI", "ADAM": "Adam Back", "ADANA": "Adanaspor Fan Token", "ADAO": "ADADao", @@ -264,6 +275,7 @@ "ADON": "Adonis", "ADP": "Adappter Token", "ADR": "Adroverse", + "ADRI": "AdRise", "ADRX": "Adrenaline Chain", "ADS": "Adshares", "ADT": "AdToken", @@ -322,6 +334,7 @@ "AGATA": "Agatech", "AGB": "Apes Go Bananas", "AGC": "Argocoin", + "AGEN": "Agent Krasnov", "AGENT": "AgentLayer", "AGENTFUN": "AgentFun.AI", "AGET": "Agetron", @@ -330,6 +343,7 @@ "AGF": "Augmented Finance", "AGG": "AGG", "AGI": "Delysium", + "AGIALPHA": "AGI ALPHA AGENT", "AGII": "AGII", "AGIL": "Agility LSD", "AGIV1": "SingularityNET v1", @@ -356,7 +370,11 @@ "AHT": "AhaToken", "AI": "Sleepless", "AI16Z": "ai16z", + "AI21X": "ai21x", + "AI23T": "23 Turtles", + "AI69SAKURA": "Sakura", "AIA": "AIA Chain", + "AIAGENT": "Aiagent.app", "AIAI": "All In AI", "AIAKITA": "AiAkita", "AIAT": "AI Analysis Token", @@ -373,6 +391,7 @@ "AICODE": "AI CODE", "AICORE": "AICORE", "AICRYPTO": "AI Crypto", + "AICRYPTOKEN": "AI Crypto Token", "AID": "AidCoin", "AIDA": "Ai-Da robot", "AIDI": "Aidi Inu", @@ -385,6 +404,7 @@ "AIDUS": "AIDUS Token", "AIE": "A.I.Earn", "AIEN": "AIENGLISH", + "AIEPK": "EpiK Protocol", "AIF": "AI FREEDOM TOKEN", "AIFLOKI": "AI Floki", "AIFUN": "AI Agent Layer", @@ -400,12 +420,15 @@ "AIMBOT": "AimBot AI", "AIMEE": "AIMEE", "AIMET": "AI Metaverse", + "AIMONICA": "Aimonica Brands", "AIMR": "MeromAI", "AIMS": "HighCastle Token", "AIMX": "Aimedis", + "AIMXV1": "Aimedis v1", "AIN": "AI Network", "AINA": "Ainastasia", "AINN": "AINN", + "AINTI": "AIntivirus", "AINU": "Ainu Token", "AION": "Aion", "AIONE": "AiONE", @@ -423,6 +446,7 @@ "AIRBTC": "AIRBTC", "AIRDROP": "AIRDROP2049", "AIRE": "Tokenaire", + "AIREVOLUTION": "AI Revolution Coin", "AIRI": "aiRight", "AIRIAN": "AIRian", "AIRT": "Aircraft", @@ -441,17 +465,24 @@ "AITK": "AITK", "AITN": "Artificial Intelligence Technology Network", "AITRA": "Aitra", + "AITRUMP": "AITRUMP", "AITT": "AITrading", "AIUS": "Arbius", + "AIV": "AIVeronica", + "AIVA": "AI Voice Agents", "AIVIA": "AI Virtual Agents", "AIWALLET": "AiWallet Token", + "AIWS": "AIWS", "AIX": "Aigang", "AIXBT": "aixbt by Virtuals", "AIXERC": "AI-X", + "AIXT": "AIXTerminal", "AJNA": "Ajna Protocol", "AJUN": "Ajuna Network", "AK12": "AK12", "AKA": "Akroma", + "AKAL": "AKA Liberty", + "AKASHA": "Akasha by Bloomverse", "AKI": "Aki Network", "AKIT": "Akita Inu", "AKITA": "Akita Inu", @@ -491,15 +522,18 @@ "ALF": "AlphaCoin", "ALG": "Algory", "ALGB": "Algebra", + "ALGERIA": "Algeria", "ALGO": "Algorand", "ALGOBLK": "AlgoBlocks", "ALGOW": "Algowave", "ALH": "AlloHash", "ALI": "Alethea Artificial Liquid Intelligence Token", "ALIAS": "Alias", + "ALIBABAAI": "Alibaba AI Agent", "ALIC": "AliCoin", "ALICE": "My Neighbor Alice", "ALICEA": "Alice AI", + "ALICEW": "Alice Weidel", "ALIEN": "AlienCoin", "ALIENPEP": "Alien Pepe", "ALIF": " ALIF COIN", @@ -521,6 +555,7 @@ "ALN": "Aluna", "ALNV1": "Aluna v1", "ALOHA": "Aloha", + "ALON": "Alon", "ALOT": "Dexalot", "ALP": "Alphacon", "ALPA": "Alpaca", @@ -575,6 +610,7 @@ "AMERI": "AMERICAN EAGLE", "AMERIC": "American True Hero", "AMERICA": "America", + "AMERICAI": "AMERICA AI Agent", "AMERICANCOIN": "AmericanCoin", "AMF": "AddMeFast", "AMG": "DeHeroGame Amazing Token", @@ -620,7 +656,8 @@ "ANDY": "ANDY", "ANDYB": "AndyBlast", "ANDYBNB": "Andy", - "ANDYBSC": "ANDY", + "ANDYBSC": "Andy BSC", + "ANDYBSCVIP": "ANDY", "ANDYMAN": "ANDYMAN", "ANDYSOL": "Andy on SOL", "ANEX": "AstroNexus", @@ -632,8 +669,10 @@ "ANI": "Anime Token", "ANIM": "Animalia", "ANIMA": "Realm Anima", - "ANIME": "Anime", + "ANIME": "Animecoin", "ANIMECOIN": "Animecoin", + "ANIMEONBASE": "Anime", + "ANITA": "Anita AI", "ANJ": "Aragon Court", "ANJI": "Anji", "ANK": "AlphaLink", @@ -644,6 +683,7 @@ "ANKRETH": "Ankr Staked ETH", "ANKRFTM": "Ankr Staked FTM", "ANKRMATIC": "Ankr Staked MATIC", + "ANLOG": "Analog", "ANML": "Animal Concerts", "ANN": "Annex Finance", "ANON": "ANON", @@ -674,6 +714,7 @@ "AOS": "AOS", "AOT": "Age of Tanks", "AP": "AppleSwap AI", + "AP3X": "Apex token", "APAD": "Anypad", "APC": "AlpaCoin", "APCG": "ALLPAYCOIN", @@ -682,6 +723,7 @@ "APED": "Baddest Alpha Ape Bundle", "APEDEV": "The dev is an Ape", "APEFUN": "Ape", + "APEMAN": "APEMAN", "APEPE": "Ape and Pepe", "APES": "APES", "APETARDIO": "Apetardio", @@ -858,6 +900,7 @@ "ASD": "AscendEX Token", "ASDEX": "AstraDEX", "ASEED": "aUSD SEED (Acala)", + "ASF": "Asymmetry Finance Token", "ASG": "Asgard", "ASGC": "ASG", "ASH": "ASH", @@ -867,6 +910,7 @@ "ASIMI": "ASIMI", "ASIX": "ASIX+", "ASK": "Permission Coin", + "ASKAI": "ASKAI", "ASKO": "Asko", "ASM": "Assemble Protocol", "ASMO": "AS Monaco Fan Token", @@ -893,6 +937,7 @@ "ASTRAFER": "Astrafer", "ASTRAFERV1": "Astrafer v1", "ASTRAL": "Astral", + "ASTRALAB": "Astra Labs", "ASTRO": "Astroport", "ASTROC": "Astroport Classic", "ASTROLION": "AstroLion", @@ -934,6 +979,7 @@ "ATLX": "Atlantis Loans Polygon", "ATM": "Atletico de Madrid Fan Token", "ATMA": "ATMA", + "ATMBSC": "ATM", "ATMC": "Autumncoin", "ATMCHAIN": "ATMChain", "ATMCOIN": "ATM", @@ -994,6 +1040,8 @@ "AUTHORSHIP": "Authorship", "AUTISM": "AUTISM", "AUTO": "Auto", + "AUTOMATIC": "Automatic Treasury Machine", + "AUTONO": "Autonomi", "AUTUMN": "Autumn", "AUVERSE": "AuroraVerse", "AUX": "Auxilium", @@ -1012,6 +1060,7 @@ "AVAV": "AVAV", "AVAV1": "AVA v1", "AVAX": "Avalanche", + "AVAXAI": "AIvalanche DeFAI Agents", "AVAXIOU": "Avalanche IOU", "AVB": "Autonomous Virtual Beings", "AVDO": "AvocadoCoin", @@ -1034,6 +1083,7 @@ "AVTM": "Aventis Metaverse", "AVXL": "Avaxlauncher", "AVXT": "Avaxtars Token", + "AWARE": "ChainAware.ai", "AWAX": "AWAX", "AWC": "Atomic Wallet Coin", "AWK": "Awkward Monkey Base", @@ -1077,6 +1127,7 @@ "AZA": "Kaliza", "AZART": "Azart", "AZBI": "AZBI CORE", + "AZER": "Azerop", "AZERO": "Aleph Zero", "AZIT": "Azit", "AZR": "Azure", @@ -1096,7 +1147,8 @@ "B2G": "Bitcoiin2Gen", "B2M": "Bit2Me", "B2X": "SegWit2x", - "B3": "B3 Coin", + "B3": "B3", + "B3COIN": "B3 Coin", "B3X": "Bnext Token", "B91": "B91", "BA": "BAHA", @@ -1117,6 +1169,9 @@ "BABYBOME": "Book of Baby Memes", "BABYBOMEOW": "Baby of BOMEOW", "BABYBONK": "Baby Bonk", + "BABYBROC": "Baby Broccoli", + "BABYBROCCOL": "Baby Broccoli", + "BABYBROCCOLI": "BabyBroccoli", "BABYBTC": "BABYBTC", "BABYC": "Baby Cat", "BABYCAT": "Baby Cat Coin", @@ -1130,6 +1185,7 @@ "BABYD": "Baby Dragon", "BABYDENG": "Baby Moo Deng", "BABYDOGE": "BabyDoge", + "BABYDOGE2": "Baby Doge 2.0", "BABYDOGEINU": "BABY DOGE INU", "BABYDOGEZILLA": "BabyDogeZilla", "BABYDRAGON": "Baby Dragon", @@ -1150,6 +1206,7 @@ "BABYJERRY": "Baby Jerry", "BABYJESUS": "BabyJesusCoin", "BABYKABOSU": "Baby Kabosu", + "BABYKEKIUS": "Baby Kekius Maximus", "BABYKITTY": "BabyKitty", "BABYLONG": "Baby Long", "BABYM": "BabyMAGA", @@ -1162,6 +1219,7 @@ "BABYMUSK": "Baby Musk", "BABYMYRO": "Babymyro", "BABYNEIRO": "Baby Neiro", + "BABYNEIROB": "Baby Neiro", "BABYOKX": "BABYOKX", "BABYP": "BabyPepe", "BABYPEIPEI": "Baby PeiPei", @@ -1179,6 +1237,7 @@ "BABYSHIRO": "Baby Shiro Neko", "BABYSHIV": "Baby Shiva", "BABYSLERF": "BabySlerf", + "BABYSNAKE": "Baby Snake BSC", "BABYSOL": "Baby Solana", "BABYSORA": "Baby Sora", "BABYSWEEP": "BabySweep", @@ -1198,6 +1257,7 @@ "BACON": "BaconDAO (BACON)", "BAD": "Bad Idea AI", "BADA": "Bad Alien Division", + "BADAI": "BAD Coin", "BADC": "BADCAT", "BADCAT": "Andy’s Alter Ego", "BADGER": "Badger DAO", @@ -1205,6 +1265,7 @@ "BAG": "Bag", "BAGS": "Basis Gold Share", "BAHAMAS": "Bahamas", + "BAHIA": "Esporte Clube Bahia Fan Token", "BAI": "BearAI", "BAICA": "Baica", "BAJU": "Bajun Network", @@ -1218,6 +1279,7 @@ "BAKT": "Backed Protocol", "BAL": "Balancer", "BALA": "Shambala", + "BALANCE": "Balance AI", "BALD": "Bald", "BALIN": "Balin Bank", "BALL": "Game 5 BALL", @@ -1229,9 +1291,11 @@ "BAMA": "BabyAMA", "BAMBIT": "BAMBIT", "BAMBOO": "BambooDeFi", + "BAMF": "BAMF", "BAMITCOIN": "Bamit", "BAN": "Comedian", "BANANA": "Banana Gun", + "BANANACHARITY": "BANANA", "BANANAF": "Banana For Scale", "BANANAS": "Monkey Peepo", "BANANO": "Banano", @@ -1319,6 +1383,7 @@ "BBCH": "Binance Wrapped BCH", "BBCT": "TraDove B2BCoin", "BBDC": "Block Beats Network", + "BBDOGITO": "BabyBullDogito", "BBDT": "BBD Token", "BBEER": "BABY BEERCOIN", "BBF": "Bubblefong", @@ -1335,6 +1400,7 @@ "BBP": "BiblePay", "BBR": "Boolberry", "BBRETT": "Baby Brett", + "BBROCCOLI": "Baby Broccoli", "BBS": "BBSCoin", "BBSOL": "Bybit Staked SOL", "BBT": "BitBook", @@ -1397,6 +1463,7 @@ "BDAY": "Birthday Cake", "BDB": "Big Data Block", "BDC": "BILLION•DOLLAR•CAT", + "BDCA": "BitDCA", "BDCC": "BDCC COIN", "BDCLBSC": "BorderCollieBSC", "BDG": "BitDegree", @@ -1419,8 +1486,10 @@ "BEAMMW": "Beam", "BEAN": "Bean", "BEANS": "Moonbeans", + "BEARIN": "Bear in Bathrobe", "BEAST": "MrBeast", "BEAT": "BEAT Token", + "BEATAI": "eBeat AI", "BEATLES": "JohnLennonC0IN", "BEATS": "Sol Beats", "BEBE": "BEBE", @@ -1433,6 +1502,7 @@ "BEE": "Herbee", "BEEF": "PepeBull", "BEEG": "Beeg Blue Whale", + "BEENZ": "BEENZ", "BEEP": "BEEP", "BEER": "BEERCOIN", "BEERUSCAT": "BeerusCat", @@ -1477,6 +1547,9 @@ "BEPE": "Blast Pepe", "BEPR": "Blockchain Euro Project", "BEPRO": "BEPRO Network", + "BERA": "Berachain", + "BERAETH": "Berachain Staked ETH", + "BERASTONE": "StakeStone Berachain Vault Token", "BERF": "BERF", "BERG": "Bloxberg", "BERN": "BERNcash", @@ -1531,11 +1604,15 @@ "BGBP": "Binance GBP Stable Coin", "BGBV1": "Bitget Token v1", "BGC": "Bee Token", + "BGCI": "Bloomberg Galaxy Crypto Index", + "BGEO": "BGEO", "BGG": "BGG Token", "BGLD": "Based Gold", "BGONE": "BigONE Token", "BGPT": "BlockGPT", + "BGR": "Bitgrit", "BGS": "Battle of Guardians Share", + "BGSC": "BugsCoin", "BGSOL": "Bitget SOL Staking", "BGUY": "The Big Guy", "BGVT": "Bit Game Verse Token", @@ -1543,7 +1620,8 @@ "BHAT": "BH Network", "BHAX": "Bithashex", "BHBD": "bHBD", - "BHC": "BillionHappiness", + "BHC": "Billion Happiness", + "BHCV1": "Billion Happiness v1", "BHEROES": "BombHeroes coin", "BHIG": "BuckHathCoin", "BHIRE": "BitHIRE", @@ -1554,6 +1632,7 @@ "BIAO": "Biaocoin", "BIB": "BIB Token", "BIBI": "BIBI", + "BIBI2025": "Bibi", "BIBL": "Biblecoin", "BIBO": "Bible of Memes", "BIC": "Bikercoins", @@ -1573,9 +1652,12 @@ "BIDZV1": "BIDZ Coin v1", "BIFI": "Beefy.Finance", "BIFIF": "BiFi", + "BIFIV1": "Beefy v1", "BIG": "Big Eyes", + "BIGBALLS": "Edward Coristine", "BIGBANGCORE": "BigBang Core", "BIGCOIN": "BigCoin", + "BIGFACTS": "BIGFACTS", "BIGFOOT": "BigFoot Town", "BIGHAN": "BighanCoin", "BIGLEZ": "THE BIG LEZ SHOW", @@ -1618,6 +1700,7 @@ "BIRB": "Birb", "BIRD": "Bird.Money", "BIRDCHAIN": "Birdchain", + "BIRDD": "BIRD DOG", "BIRDDOG": "Bird Dog", "BIRDO": "Bird Dog", "BIS": "Bismuth", @@ -1642,6 +1725,7 @@ "BITCNY": "bitCNY", "BITCO": "Bitcoin Black Credit Card", "BITCOINC": "Bitcoin Classic", + "BITCOINOTE": "BitcoiNote", "BITCOINP": "Bitcoin Private", "BITCOINV": "BitcoinV", "BITCONNECT": "BitConnect Coin", @@ -1680,7 +1764,9 @@ "BITVOLT": "BitVolt", "BITWORLD": "Bit World Token", "BITX": "BitScreener", - "BITZ": "Bitz Coin", + "BITXOXO": "Bitxoxo", + "BITZ": "MARBITZ", + "BITZBIZ": "Bitz Coin", "BIUT": "Bit Trust System", "BIVE": "BIZVERSE", "BIX": "BiboxCoin", @@ -1784,11 +1870,12 @@ "BLTV": "BLTV Token", "BLU": "BlueCoin", "BLUB": "BLUB", - "BLUE": "Blue Protocol", + "BLUE": "Bluefin", "BLUEBUTT": "BLUE BUTT CHEESE", "BLUEG": "Blue Guy", "BLUEM": "BlueMove", "BLUEN": "Blue Norva", + "BLUEPROTOCOL": "Blue Protocol", "BLUES": "Blueshift", "BLUESC": "BluesCrypto", "BLUESPARROW": "BlueSparrow Token", @@ -1838,6 +1925,7 @@ "BNA": "BananaTok", "BNANA": "Chimpion", "BNB": "Binance Coin", + "BNBAI": "BNB Agents", "BNBBONK": "BNB BONK", "BNBBUNNY": "BNB BUNNY", "BNBCAT": "BNBcat", @@ -1853,12 +1941,14 @@ "BNBLION": "BNB LION", "BNBOLYMPIC": "BNB OLYMPIC", "BNBP": "BNBPot", + "BNBPRINTER": "BNBPrinter", "BNBSNAKE": "BNB SNAKE", "BNBSONGOKU": "BNBsongoku", "BNBTC": "BNbitcoin", "BNBVEGETA": "BNB VEGETA", "BNBWHALES": "BNB Whales", "BNBX": "Stader BNBx", + "BNBXBT": "BNBXBT", "BNC": "Bifrost Native Coin", "BND": "Bened", "BNF": "BonFi", @@ -1884,6 +1974,7 @@ "BNU": "ByteNext", "BNUSD": "Balanced Dollars", "BNX": "BinaryX", + "BNXV1": "BinaryX v1", "BNY": "TaskBunny", "BOA": "BOSAGORA", "BOAI": "BOLICAI", @@ -1900,11 +1991,13 @@ "BOBER": "BOBER", "BOBFUN": "BOB", "BOBLS": "Boblles", + "BOBMARLEY": "Bob Marley Meme", "BOBO": "BOBO", "BOBOT": "Bobo The Bear", "BOBS": "Bob's Repair", "BOBT": "BOB Token", "BOBUKI": "Bobuki Neko", + "BOBY": "BOBY", "BOC": "BOCOIN", "BOCA": "BookOfPussyCats", "BOCAC": "BocaChica token", @@ -1936,6 +2029,7 @@ "BOLD": "Bold", "BOLI": "BolivarCoin", "BOLT": "Bolt", + "BOLTAI": "Bolt AI", "BOLTT": "BolttCoin", "BOM": "Book Of Matt Furie", "BOMA": "Book of Maga", @@ -1951,12 +2045,14 @@ "BOMES": "BOOK OF MEMES", "BOMET": "BOME TRUMP", "BOMK": "BOMK", + "BOMT": "Baby One More Time", "BON": "Bonpay", "BONA": "Bonafi", "BOND": "BarnBridge", "BONDAPPETIT": "BondAppetit", "BONDLY": "Bondly", "BONDLYV1": "Bondly Finance", + "BONDX": "BondX", "BONE": "Bone ShibaSwap", "BONES": "Moonshots Farm", "BONESCOIN": "BonesCoin", @@ -1986,7 +2082,9 @@ "BOO": "Spookyswap", "BOOB": "BooBank", "BOOE": "Book of Ethereum", + "BOOF": "Boofus by Virtuals", "BOOFI": "Boo Finance", + "BOOG": "BOOG base", "BOOK": "Solbook", "BOOKIE": "BookieBot", "BOOKO": "Book of Pets", @@ -2010,6 +2108,7 @@ "BORA": "BORA", "BORED": "Bored Museum", "BORG": "SwissBorg", + "BORGY": "BORGY", "BORING": "BoringDAO", "BORK": "Bork", "BORKIE": "Borkie", @@ -2034,6 +2133,7 @@ "BOTX": "BOTXCOIN", "BOU": "Boulle", "BOUNCE": "Bounce Token", + "BOUNTY": "ChainBounty", "BOUTS": "BoutsPro", "BOW": "Archer Swap", "BOWE": "Book of Whales", @@ -2088,6 +2188,7 @@ "BR": "BOHR", "BR34P": "BR34P", "BRACE": "Bitci Racing Token", + "BRAI": "Brain Frog", "BRAIN": "BrainCoin", "BRAINERS": "Brainers", "BRAINLET": "Brainlet", @@ -2149,6 +2250,14 @@ "BRNK": "Brank", "BRNX": "Bronix", "BRO": "Bro the cat", + "BROC": "Broccoli (broc.wtf)", + "BROCC": "Broccoli", + "BROCCO": "Broccoli (firstbroccoli.com)", + "BROCCOL": "Broccoli (broccolibsc.com)", + "BROCCOLI": "CZ'S Dog (broccoli.gg)", + "BROCCOLIBNB": "BROCCOLI (broccolibnb.xyz)", + "BROCCOLICZ": "Broccoli (broccoli_cz)", + "BROCCOLIVIP": "Broccoli(broccoli.vip)", "BROCK": "Bitrock", "BROGG": "Brett's Dog", "BROKE": "Broke Again", @@ -2178,6 +2287,7 @@ "BS": "BlackShadowCoin", "BSAFE": "BlockSafe", "BSAFU": "BlockSAFU", + "BSAI": "Bitcoin Silver AI", "BSATOSHI": "BabySatoshi", "BSB": "Based Street Bets", "BSC": "BSC Layer", @@ -2194,6 +2304,7 @@ "BSCV": "Bscview", "BSDETH": "Based ETH", "BSE": "base season", + "BSEN": "Baby Sen by Sentio", "BSEND": "BitSend", "BSFM": "BABY SAFEMOON", "BSG": "Baby Squid Game", @@ -2206,6 +2317,7 @@ "BSKT": "BasketCoin", "BSL": "BankSocial", "BSOL": "BlazeStake Staked SOL", + "BSOP": "Bsop", "BSOV": "BitcoinSoV", "BSP": "BallSwap", "BSPM": "Bitcoin Supreme", @@ -2243,6 +2355,7 @@ "BTCA": "BITCOIN ADDITIONAL", "BTCAB": "Bitcoin Avalanche Bridged", "BTCACT": "BITCOIN Act", + "BTCAI": "BTC AI Agent", "BTCAS": "BitcoinAsia", "BTCAT": "Bitcoin Cat", "BTCB": "Bitcoin BEP2", @@ -2264,7 +2377,7 @@ "BTCL": "BTC Lite", "BTCM": "BTCMoon", "BTCMT": "Minto", - "BTCN": "BitcoiNote", + "BTCN": "Bitcorn", "BTCNOW": "Blockchain Technology Co.", "BTCP": "Bitcoin Palladium", "BTCPAY": "Bitcoin Pay", @@ -2273,6 +2386,7 @@ "BTCRED": "Bitcoin Red", "BTCRY": "BitCrystal", "BTCS": "Bitcoin Scrypt", + "BTCSR": "BTC Strategic Reserve", "BTCST": "BTC Standard Hashrate Token", "BTCT": "Bitcoin Token", "BTCUS": "Bitcoinus", @@ -2293,6 +2407,7 @@ "BTL": "Bitlocus", "BTLC": "BitLuckCoin", "BTM": "Bytom", + "BTMETA": "BTCASH", "BTMG": "Bitcademy Football", "BTMI": "BitMiles", "BTMK": "BitMark", @@ -2316,6 +2431,7 @@ "BTRS": "Bitball Treasure", "BTRST": "Braintrust", "BTRU": "Biblical Truth", + "BTRUMP": "Baron Trump", "BTS": "Bitshares", "BTSC": "BTS Chain", "BTSE": "BTSE Token", @@ -2361,19 +2477,23 @@ "BUGG": "Bugg Inu", "BUGS": "Bugs Bunny", "BUIDL": "Starter.xyz", + "BUIL": "BUILD", "BUILD": "BuildAI", "BUILDIN": "Buildin Token", "BUILDTEAM": "BuildTeam", + "BUILT": "Built Different", "BUK": "CryptoBuk", "BUL": "bul", "BULDAK": "Buldak", "BULEI": "Bulei", - "BULL": "Bullieverse", + "BULL": "Tron Bull", + "BULLBEAR": "BullBear AI", "BULLC": "BuySell", "BULLF": "BULL FINANCE", "BULLI": "Bullish On Ethereum", + "BULLIEVERSE": "Bullieverse", "BULLINU": "Bull inu", - "BULLION": "BullionFX", + "BULLIONFX": "BullionFX", "BULLISH": "bullish", "BULLMOON": "Bull Moon", "BULLPEPE": "Bullpepe", @@ -2389,7 +2509,9 @@ "BUND": "Bund V2.0", "BUNDL": "Bundl Tools", "BUNI": "Bunicorn", - "BUNN": "Bunni", + "BUNKER": "BunkerCoin", + "BUNNI": "Bunni", + "BUNNIV1": "Timeless", "BUNNY": "Pancake Bunny", "BUNNYINU": "Bunny Inu", "BUNNYM": "BUNNY MEV BOT", @@ -2410,6 +2532,8 @@ "BUSY": "Busy DAO", "BUT": "BitUP Token", "BUTT": "Buttercat", + "BUTTCOIN": "The Next Bitcoin", + "BUTTHOLE": "Butthole Coin", "BUX": "BUX", "BUXCOIN": "Buxcoin", "BUY": "Burency", @@ -2500,6 +2624,7 @@ "CAI": "Cai Token", "CAID": "ClearAid", "CAIR": "Crypto-AI-Robo.com", + "CAIV": "CARVIS", "CAIX": "CAIx", "CAIZ": "Caizcoin", "CAKE": "PancakeSwap", @@ -2515,6 +2640,7 @@ "CAM": "Consumption Avatar Matrix", "CAMC": "Camcoin", "CAMEL": "The Camel", + "CAMINO": "Camino Network", "CAMLY": "Camly Coin", "CAMP": "Camp", "CAMT": "CAMELL", @@ -2538,8 +2664,10 @@ "CAPRICOIN": "CapriCoin", "CAPS": "Ternoa", "CAPT": "Bitcoin Captain", + "CAPTAINBNB": "CaptainBNB", "CAPTAINPLANET": "Captain Planet", "CAPY": "Capybara", + "CAPYBARA": "Capybara", "CAR": "CarBlock", "CARAT": "Carats Token", "CARBO": "CleanCarbon", @@ -2556,7 +2684,8 @@ "CAROL": "CAROLToken", "CARPE": "CarpeDiemCoin", "CARR": "Carnomaly", - "CARROT": "CarrotSwap", + "CARROT": "Carrot by Puffer", + "CARROTSWAP": "CarrotSwap", "CART": "CryptoArt.Ai", "CARTAXI": "CarTaxi", "CARTERCOIN": "CarterCoin", @@ -2619,6 +2748,7 @@ "CATSY": "CAT SYLVESTER", "CATT": "Catex", "CATTO": "Cat Token", + "CATTON": "Catton AI", "CATVAX": "Catvax", "CATVILLS": "Catvills Coin", "CATW": "Cat wif Hands", @@ -2739,6 +2869,7 @@ "CEODOGE": "CEO DOGE", "CERBER": "CERBEROGE", "CERE": "Cere Network", + "CEREB": "Cerebrum", "CERES": "Ceres", "CES": "swap.coffee", "CESC": "Crypto Escudo", @@ -2765,6 +2896,7 @@ "CFXT": "Chainflix", "CFun": "CFun", "CGA": "Cryptographic Anomaly", + "CGAI": "GDAI Agent", "CGAR": "CryptoGuards", "CGG": "Chain Guardians", "CGL": "Crypto Gladiator Shards", @@ -2774,8 +2906,11 @@ "CGPU": "CloudGPU", "CGS": "Crypto Gladiator Shards", "CGT": "Coin Gabbar Token", + "CGTV1": "Curio Governance", + "CGTV2": "Curio Gas Token", "CGU": "Crypto Gaming United", "CGV": "Cogito Finance", + "CGX": "Forkast", "CHA": "Charity Coin", "CHACHA": "Chacha", "CHAD": "Chad Coin", @@ -2828,7 +2963,8 @@ "CHEESEBALL": "Cheeseball the Wizard", "CHEESECOIN": "Cheesecoin", "CHEESUS": "Cheesus", - "CHEF": "Chefdotfun", + "CHEF": "CoinChef", + "CHEFDOTFUN": "Chefdotfun", "CHENG": "Chengshi", "CHEQ": "CHEQD Network", "CHER": "Cherry Network", @@ -2859,8 +2995,10 @@ "CHILDAI": "Singularity's Child gonzoai", "CHILI": "CHILI", "CHILL": "ChillPill", + "CHILLAX": "Chillax", "CHILLGUY": "Chill Guy", "CHIM": "Chimera", + "CHINA": "China Coin", "CHINAU": "Chinau", "CHINAZILLA": "ChinaZilla", "CHINGON": "Mexico Chingon", @@ -2893,9 +3031,11 @@ "CHOPPER": "Chopper Inu", "CHOPPY": "Choppy", "CHORIZO": "Chorizo", + "CHORUZ": "Choruz AI", "CHOW": "Chow Chow Finance", "CHOY": "Bok Choy", "CHP": "CoinPoker Token", + "CHPD": "Chirppad", "CHR": "Chroma", "CHRETT": "Chinese BRETT", "CHRISPUMP": "Christmas Pump", @@ -2968,11 +3108,13 @@ "CLAY": "Clay Nation", "CLB": "Cloudbric", "CLBR": "Colibri Protocol", + "CLBTC": "clBTC", "CLCT": "CollectCoin", "CLD": "Cloud", "CLDX": "Cloverdex", - "CLEAR": "Clear Water", + "CLEAR": "Everclear", "CLEARPOLL": "ClearPoll", + "CLEARWATER": "Clear Water", "CLEG": "Chain of Legends", "CLEO": "Cleo Tech", "CLEV": "CLever Token", @@ -3012,6 +3154,7 @@ "CLUB": "ClubCoin", "CLUD": "CludCoin", "CLUSTR": "Clustr Labs", + "CLUTCH": "Clutch", "CLV": "Clover Finance", "CLVA": "Clever DeFi", "CLVX": "Calvex", @@ -3086,6 +3229,8 @@ "COCK": "Shibacock", "COCO": "COCO COIN", "COCONUT": "Coconut", + "COCOR": "Cocoro", + "COCORO": "Cocoro", "COD": "Chief of Deswamp", "CODA": "CODA", "CODAI": "CODAI", @@ -3107,6 +3252,7 @@ "COGI": "COGI", "COGS": "Cogmento", "COI": "Coinnec", + "COINAI": "Coinbase AI Agent", "COINB": "Coinbidex", "COINBT": "CoinBot", "COINDEFI": "Coin", @@ -3122,10 +3268,12 @@ "COJ": "Cojam", "COK": "Cat Own Kimono", "COKE": "Cocaine Cowboy Shards", + "COKEONS": "Coke on Sol", "COL": "Clash of Lilliput", "COLA": "Cola", "COLL": "Collateral Pay", "COLLAR": "PolyPup Finance", + "COLLAT": "Collaterize", "COLLE": "Collective Care", "COLLEA": "Colle AI", "COLLECT": "CoinCollect", @@ -3152,6 +3300,7 @@ "COMT": "Community Token", "CONAN": "Conan", "CONC": "Concentrator", + "CONCHO": "Sapo Concho", "CONDENSATE": "Condensate", "CONDO": "CONDO", "CONE": "BitCone", @@ -3195,6 +3344,7 @@ "CORN": "CORN", "CORNELLA": "CORNELLA", "CORSI": "Cane Corso", + "CORTEX": "Cortex Protocol", "CORX": "CorionX", "COS": "Contentos", "COSHI": "CoShi Inu", @@ -3258,6 +3408,7 @@ "CPTN": "Captain Max", "CPU": "CPUcoin", "CPX": "Apex Token", + "CPXTB": "Coin Prediction Tool On Base", "CPY": "COPYTRACK", "CQST": "ConquestCoin", "CQT": "Covalent", @@ -3353,6 +3504,7 @@ "CRONA": "CronaSwap", "CRONK": "CRONK", "CROPPER": "CropperFinance", + "CROW": "cr0w by Virtuals", "CROWD": "CrowdCoin", "CROWDWIZ": "Crowdwiz", "CROWN": "Crown by Third Time Games", @@ -3387,16 +3539,21 @@ "CRYP": "CrypticCoin", "CRYPT": "CryptCoin", "CRYPTER": "Crypteriumcoin", + "CRYPTOA": "CryptoAI", + "CRYPTOAGENT": "CRYPTO AGENT TRUMP", "CRYPTOAI": "CryptoAI", "CRYPTOB": "Crypto Burger", "CRYPTOBEAST": "CryptoBeast", "CRYPTOBL": "CryptoBlades Kingdoms", + "CRYPTOBR": "Crypto Bro", "CRYPTOBULLION": "CryptoBullion", + "CRYPTODELIVERY": "Crypto Delivery", "CRYPTOE": "Cryptoenter", "CRYPTOEM": "Crypto Emperor Trump", "CRYPTOF": "CryptoFarmers", "CRYPTOH": "CryptoHunterTrading", "CRYPTOJ": "Crypto Journey", + "CRYPTOJESUS": "Crypto Jesus Trump", "CRYPTON": "CRYPTON", "CRYPTONITE": "Cryptonite", "CRYPTOOFFICIAL": "Crypto", @@ -3457,6 +3614,7 @@ "CTLX": "Cash Telex", "CTN": "Continuum Finance", "CTO": "BaseCTO", + "CTOAI": "ClustroAI", "CTOK": "Codyfight", "CTP": "Ctomorrow Platform", "CTPL": "Cultiplan", @@ -3475,9 +3633,10 @@ "CU": "Crypto Unicorns", "CUAN": "CuanSwap.com", "CUB": "Cub Finance", - "CUBE": "Cube Network", + "CUBE": "Somnium Space CUBEs", "CUBEAUTO": "Cube", "CUBEB": "CubeBase", + "CUBENETWORK": "Cube Network", "CUCCI": "Cat in Gucci", "CUCK": "Cuckadoodledoo", "CUDOS": "Cudos", @@ -3495,7 +3654,6 @@ "CURA": "Cura Network", "CURE": "Curecoin", "CURI": "Curium", - "CURIO": "Curio Governance", "CURLY": "Curly", "CURR": "Curry", "CURRY": "CurrySwap", @@ -3537,6 +3695,7 @@ "CWAR": "Cryowar Token", "CWBTC": "Compound Wrapped BTC", "CWD": "CROWD", + "CWDV1": "Linkflow", "CWEB": "Coinweb", "CWEX": "Crypto Wine Exchange", "CWIF": "catwifhat", @@ -3592,7 +3751,9 @@ "CYS": "BlooCYS", "CYT": "Cryptokenz", "CZ": "CHANGPENG ZHAO (changpengzhao.club)", + "CZBROCCOLI": "Cz Broccoli", "CZC": "Crazy Coin", + "CZDOG": "CZ Dog", "CZF": "CZodiac Farming Token", "CZGOAT": "CZ THE GOAT", "CZKING": "CZKING", @@ -3602,7 +3763,6 @@ "CZSHARES": "CZshares", "CZUSD": "CZUSD", "CZZ": "ClassZZ", - "D": "Denarius", "D11": "DeFi11", "D2O": "DAM Finance", "D2T": "Dash 2 Trade", @@ -3631,6 +3791,7 @@ "DAFI": "Dafi Protocol", "DAFT": "DaftCoin", "DAG": "Constellation", + "DAGESTAN": "Dagestan And Forget", "DAGO": "Dago Mining", "DAGS": "Dagcoin", "DAGT": "Digital Asset Guarantee Token", @@ -3643,6 +3804,7 @@ "DAIQ": "Daiquilibrium", "DAISY": "Daisy Launch Pad", "DAK": "dak", + "DAKU": "Der Daku", "DAL": "DAOLaunch", "DALI": "Dalichain", "DALMA": "Dalma Inu", @@ -3653,6 +3815,7 @@ "DAMOON": "Damoon Coin", "DAN": "Daneel", "DANA": "Ardana", + "DANCING": "Dancing Michi", "DANG": "Guangdang", "DANGEL": "dAngel Fund", "DANJ": "Danjuan Cat", @@ -3681,10 +3844,12 @@ "DARED": "Daredevil Dog", "DARICO": "Darico", "DARIK": "Darik", - "DARK": "Dark", + "DARK": "Dark Frontiers", + "DARKCOIN": "Dark", "DARKEN": "Dark Energy Crystals", "DARKMAGACOIN": "DARK MAGA", "DARKT": "Dark Trump", + "DARKTOKEN": "DarkToken", "DART": "dART Insurance", "DARX": "Bitdaric", "DAS": "DAS", @@ -3695,6 +3860,7 @@ "DASIAv": "DASIA", "DAT": "Datum", "DATA": "Streamr", + "DATAB": "Databot", "DATAO": "Data Ownership Protocol", "DATAWALLET": "DataWallet", "DATOM": "Drop Staked ATOM", @@ -3760,7 +3926,7 @@ "DCN": "Dentacoin", "DCNT": "Decanect", "DCNTR": "Decentrahub Coin", - "DCOIN": "Crypto Delivery", + "DCOIN": "Dogcoin", "DCR": "Decred", "DCRE": "DeltaCredits", "DCRN": "Decred-Next", @@ -3779,6 +3945,7 @@ "DDIM": "DuckDaoDime", "DDK": "DDKoin", "DDL": "Donocle", + "DDM": "DDM Deutsche Mark", "DDMT": "Dongdaemun Token", "DDN": "Den Domains", "DDOS": "disBalancer", @@ -3804,13 +3971,18 @@ "DEDE": "Dede", "DEDI": "Dedium", "DEDPRZ": "DEDPRZ", + "DEE": "Deep AI", "DEEBO": "Deebo the Bear", "DEED": "Deed (Ordinals)", "DEEM": "iShares MSCI Emerging Markets ETF Defichain", "DEEP": "DeepBook Protocol", "DEEPCLOUD": "DeepCloud AI", "DEEPG": "Deep Gold", + "DEEPSEARCH": "Grok 3 DeepSearch", + "DEEPSEEK": "Global DePIN Chain", + "DEEPSEEKR1": "DeepSeek R1", "DEER": "ToxicDeer Finance", + "DEERSEIZED": "Deer Seized by US Government", "DEEX": "DEEX", "DEEZ": "DEEZ NUTS", "DEFAI": "DeFAI", @@ -3859,6 +4031,7 @@ "DEMI": "DeMi", "DEMIR": "Adana Demirspor Token", "DEMOS": "DEMOS", + "DENARIUS": "Denarius", "DENT": "Dent", "DENTX": "DENTNet", "DEO": "Demeter", @@ -3890,6 +4063,7 @@ "DEUS": "DEUS Finance", "DEUSD": "Elixir deUSD", "DEV": "Deviant Coin", + "DEVAI": "DEV AI", "DEVCOIN": "DevCoin", "DEVE": "Develocity Finance", "DEVI": "DEVITA", @@ -3898,9 +4072,11 @@ "DEVVE": "Devve", "DEVX": "Developeo", "DEX": "DEX", + "DEX223": "DEX223", "DEXA": "DEXA COIN", "DEXC": "DexCoyote Legends", "DEXE": "DeXe", + "DEXEV1": "DeXe v1", "DEXG": "Dextoken Governance", "DEXIO": "Dexioprotocol", "DEXM": "Dexmex", @@ -4036,6 +4212,7 @@ "DISK": "Dark Lisk", "DISPEPE": "Disabled Pepe", "DISTR": "Distributed Autonomous Organization", + "DISTRIBUTE": "DISTRIBUTE", "DIT": "Ditcoin", "DITH": "Dither AI", "DIVA": "DIVA Protocol", @@ -4110,6 +4287,7 @@ "DNO": "Denaro", "DNODE": "DecentraNode", "DNOTES": "Dnotes", + "DNOW": "DuelNow", "DNS": "BitDNS", "DNT": "district0x", "DNTX": "DNAtix", @@ -4152,10 +4330,12 @@ "DOGE1SAT": "DOGE-1SATELLITE", "DOGE2": "Dogecoin 2.0", "DOGE20": "Doge 2.0", + "DOGEAI": "DOGEai", "DOGEB": "DogeBonk", "DOGEBNB": "DogeBNB", "DOGEC": "DogeCash", "DOGECAST": "Dogecast", + "DOGECAUCUS": "Doge Caucus", "DOGECEO": "Doge CEO", "DOGECO": "Dogecolony", "DOGECOIN": "Buff Doge Coin", @@ -4164,6 +4344,7 @@ "DOGED": "DogeCoinDark", "DOGEDAO": "DogeDao", "DOGEDASH": "Doge Dash", + "DOGEDI": "Doge Dividends", "DOGEFA": "DOGEFATHER", "DOGEFATHER": "Dogefather", "DOGEFORK": "DogeFork", @@ -4177,6 +4358,7 @@ "DOGEM": "Doge Matrix", "DOGEMETA": "Dogemetaverse", "DOGEMOB": "DOGEMOB", + "DOGEMOON": "DOGE TO MOON", "DOGENFT": "The Doge NFT", "DOGEP": "Doge Protocol", "DOGEPAY": "Doge Payment", @@ -4186,6 +4368,7 @@ "DOGERA": "Dogera", "DOGES": "Dogeswap", "DOGESWAP": "Dogeswap Token (HECO)", + "DOGETF": "DOGE ETF", "DOGETH": "EtherDoge", "DOGEVERSE": "DogeVerse", "DOGEWHALE": "Dogewhale", @@ -4208,13 +4391,16 @@ "DOGLAI": "Doglaikacoin", "DOGMI": "DOGMI", "DOGO": "DogemonGo", + "DOGONB": "Dog on Base", "DOGPAD": "DogPad Finance", + "DOGPU": "DogeGPU", "DOGRMY": "DogeArmy", "DOGS": "Dogs", "DOGSROCK": "Dogs Rock", "DOGSS": "DOGS SOL", "DOGSSO": "DOGS Solana", "DOGSWAG": "DogSwaghat", + "DOGUN": "Dogun", "DOGW": "DOGWIFHOOD", "DOGWIFHAT": "dogwifhat", "DOGWIFSEAL": "dogwifseal", @@ -4238,6 +4424,7 @@ "DONA": "DONASWAP", "DONAL": "Donald Pump", "DONALD": "DONALD TRUMP", + "DONALDP": "Donald Pump", "DONALDT": "Donald The Trump", "DONATION": "DonationCoin", "DONG": "DongCoin", @@ -4289,6 +4476,7 @@ "DPEX": "DPEX", "DPI": "DeFiPulse Index", "DPIE": "DeFiPie", + "DPIN": "DPIN", "DPLAT": "zbyte", "DPLN": "DePlan", "DPLTR": "Palantir Tokenized Stock Defichain", @@ -4350,6 +4538,7 @@ "DRT": "DomRaider", "DRUGS": "Big Pharmai", "DRV": "Derive", + "DRX": "DRX Token", "DRXNE": "Droxne", "DRZ": "Droidz", "DS": "DeStorage", @@ -4376,7 +4565,7 @@ "DSTR": "Dynamic Supply Tracker", "DSUN": "DsunDAO", "DSYNC": "Destra Network", - "DT": "DarkToken", + "DT": "Drift Zone", "DT1": "Dollar Token 1", "DTA": "Data", "DTB": "Databits", @@ -4425,6 +4614,7 @@ "DUET": "Duet Protocol", "DUG": "DUG", "DUGE": "DUGE", + "DUK": "DUKE COIN", "DUK+": "Dukascoin", "DUKE": "Duke Inu", "DUKO": "DUKO", @@ -4518,6 +4708,7 @@ "EA": "EagleCoin", "EAC": "Education Assessment Cult", "EADX": "EADX Token", + "EAFIN": "EAFIN", "EAG": "Emerging Assets Group", "EAGLE": "Eagle Token", "EAGS": "EagsCoin", @@ -4525,6 +4716,7 @@ "EARLY": "Early Risers", "EARLYF": "EarlyFans", "EARN": "EarnGuild", + "EARNB": "Earn BTC", "EARTH": "Earth Token", "EARTHCOIN": "EarthCoin", "EASYF": "EasyFeedback", @@ -4616,12 +4808,15 @@ "EDUCOIN": "EduCoin", "EDUM": "EDUM", "EDUX": "Edufex", + "EDWIN": "Edwin", "EDX": "Equilibrium", + "EEFS": "Eefs", "EER": "Ethereum eRush", "EETH": "ether fi", "EFBAI": "EuroFootball AI", "EFC": "Everton Fan Token", "EFCR": "EFLANCER", + "EFFECT": "Effect AI", "EFFT": "Effort Economy ", "EFI": "Efinity", "EFIL": "Ethereum Wrapped Filecoin", @@ -4721,6 +4916,7 @@ "ELON": "Dogelon Mars", "ELON2024": "ELON 2024(BSC)", "ELON404": "Elon404", + "ELON4AFD": "Elon for AfD", "ELONCAT": "ELON CAT COIN", "ELOND": "ELON DOGE", "ELONDOGE": "ELON DOGE", @@ -4748,6 +4944,7 @@ "ELUSKMON": "Elusk Mon", "ELV": "Elvantis", "ELVN": "11Minutes", + "ELX": "Elixir Network", "ELY": "Elysian", "ELYS": "Elys Network", "ELYSIUM": "Elysium", @@ -4785,6 +4982,7 @@ "EMU": "eMusic", "EMV": "Ethereum Movie Venture", "EMX": "EMX", + "EMYC": "E Money", "ENA": "Ethena", "ENC": "Encores Token", "ENCD": "Encircled", @@ -4812,6 +5010,7 @@ "ENQ": "Enecuum", "ENQAI": "enqAI", "ENRG": "EnergyCoin", + "ENRON": "Enron", "ENRX": "Enrex", "ENS": "Ethereum Name Service", "ENT": "Eternity", @@ -4909,8 +5108,10 @@ "ESGC": "ESG Chain", "ESH": "Switch", "ESHIB": "Euro Shiba Inu", + "ESM": "EL SALVADOR MEME", "ESN": "Ethersocial", "ESNC": "Galaxy Arena Metaverse", + "ESOL": "Earn Solana", "ESP": "Espers", "ESPL": "ESPL ARENA", "ESPR": "Espresso Bot", @@ -4972,6 +5173,7 @@ "ETHPOW": "ETHPoW", "ETHPR": "Ethereum Premium", "ETHPY": "Etherpay", + "ETHR": "Ethereal", "ETHS": "EthereumScrypt", "ETHSHIB": "Eth Shiba", "ETHV": "Ethverse", @@ -5017,6 +5219,7 @@ "EUROP": "Europa Coin", "EURQ": "Quantoz EURQ", "EURR": "StablR Euro", + "EURRV1": "StablR Euro v1", "EURS": "STASIS EURS", "EURT": "Euro Tether", "EURTV1": "Euro Tether v1", @@ -5027,6 +5230,7 @@ "EV": "EVAI", "EVA": "Evadore", "EVAI": "EVA Intelligence", + "EVAL": "Chromia's EVAL by Virtuals", "EVAN": "Evanesco Network", "EVAULT": "EthereumVault", "EVAV1": "Evadore v1", @@ -5094,6 +5298,7 @@ "EXOS": "Exobots", "EXP": "Expanse", "EXPAND": "Gems", + "EXPERT": "EXPERT_MONEY", "EXPO": "Exponential Capital", "EXRD": "Radix", "EXRN": "EXRNchain", @@ -5127,6 +5332,7 @@ "F9": "Falcon Nine", "FAB": "FABRK Token", "FABA": "Faba Invest", + "FABIENNE": "Fabienne", "FABRIC": "MetaFabric", "FAC": "Flying Avocado Cat", "FACE": "FaceDAO", @@ -5136,6 +5342,8 @@ "FACTORY": "ChainFactory", "FACTR": "Defactor", "FADO": "FADO Go", + "FAFO": "FAFO", + "FAFOSOL": "Fafo", "FAG": "PoorFag", "FAH": "Falcons", "FAI": "Freysa AI", @@ -5172,7 +5380,11 @@ "FARME": "Farmers Only", "FARMING": "Farming Bad", "FARMS": "Farmsent", + "FARTAI": "Fart AI", + "FARTBOY": "Fartboy", "FARTCOIN": "Fartcoin", + "FARTDEV": "Fart Dev", + "FARTING": "Farting Unicorn", "FAS": "fast construction coin", "FAST": "Fastswap", "FASTAI": "Fast And Ai", @@ -5316,6 +5528,7 @@ "FINS": "AutoShark DEX", "FINT": "FintraDao", "FINU": "Formula Inu", + "FINVESTA": "Finvesta", "FIO": "FIO Protocol", "FIONA": "Fiona", "FIONABSC": "Fiona", @@ -5336,6 +5549,7 @@ "FITFI": "Step App", "FITT": "Fitmint", "FIU": "beFITTER", + "FIUSD": "Sygnum FIUSD Liquidity Fund", "FIWA": "Defi Warrior", "FIX00": "FIX00", "FJB": "Freedom. Jobs. Business.", @@ -5389,7 +5603,9 @@ "FLO": "Flo", "FLOAT": "Float Protocol", "FLOCHI": "Flochi", + "FLOCK": "FLock.io", "FLOCKA": "Waka Flocka", + "FLOCKE": "Flockerz", "FLOKA": "FLOKA", "FLOKEI": "FLOKEI", "FLOKI": "Floki Inu", @@ -5412,6 +5628,8 @@ "FLORKY": "Florky", "FLOSHIDO": "FLOSHIDO INU", "FLOT": "FireLotto", + "FLOTUS47": "Melania Trump", + "FLOURI": "Flourishing AI", "FLOVI": "Flovi inu", "FLOVM": "FLOV MARKET", "FLOW": "Flow", @@ -5423,12 +5641,14 @@ "FLRBRG": "Floor Cheese Burger", "FLRS": "Flourish Coin", "FLS": "Flits", + "FLSH": "FlashWash", "FLT": "Fluence", "FLUFFI": "Fluffington", "FLUFFY": "FLUFFY", "FLUFFYS": "Fluffys", "FLUI": "Fluidity", "FLUID": "Fluid", + "FLUIDTRADE": "Fluid", "FLURRY": "Flurry Finance", "FLUT": "Flute", "FLUTTERCOIN": "FlutterCoin", @@ -5517,6 +5737,7 @@ "FORT": "Forta", "FORTH": "Ampleforth Governance Token", "FORTHB": "ForthBox", + "FORTKNOX": "Fort Knox", "FORTUNA": "Fortuna", "FORTUNE": "Fortune", "FORWARD": "Forward Protocol", @@ -5544,6 +5765,7 @@ "FPEPE": "Based Father Pepe", "FPFT": "Peruvian National Football Team Fan Token", "FPI": "Frax Price Index", + "FPIBANK": "FPIBANK", "FPIS": "Frax Price Index Share", "FQS": "FQSwap V2", "FR": "Freedom Reserve", @@ -5573,13 +5795,15 @@ "FREET": "FreeTrump", "FREL": "Freela", "FREN": "FREN", + "FRENC": "Frencoin", "FRENCH": "French On Base", "FRENS": "Farmer Friends", "FRESCO": "Fresco", "FRF": "France REV Finance", "FRGST": "Froggies Token", "FRGX": "FRGX", - "FRIC": "Frictionless", + "FRIC": "Fric", + "FRICTION": "Frictionless", "FRIEND": "Friend.tech", "FRIES": "Soltato FRIES", "FRIN": "Fringe Finance", @@ -5589,6 +5813,7 @@ "FRM": "Ferrum Network", "FRN": "Francs", "FRNT": "Final Frontier", + "FROC": "Based Froc", "FROG": "FrogSwap", "FROGB": "Frog Bsc", "FROGCEO": "Frog Ceo", @@ -5629,6 +5854,7 @@ "FSO": "FSociety", "FST": "Futureswap", "FSTC": "FastCoin", + "FSTR": "Fourth Star", "FSW": "Falconswap", "FT": "Fracton Protocol", "FTB": "Fit&Beat", @@ -5656,10 +5882,12 @@ "FTVT": "FashionTV Token", "FTW": "FutureWorks", "FTX": "FintruX", + "FTXAI": "FTX AI Agent", "FTXT": "FUTURAX", "FU": "FU Money", "FUBAO": "FUBAO", "FUCK": "Fuck Token", + "FUCKTRUMP": "FUCK TRUMP", "FUD": "FUD.finance", "FUEGO": "FUEGO", "FUEL": "Fuel Network", @@ -5669,6 +5897,7 @@ "FUJIN": "Fujinto", "FUKU": "FUKU-KUN", "FUL": "Fulcrom Finance", + "FULLSEND": "Fullsend Community Coin", "FUMO": "Alien Milady Fumo", "FUN": "FUN Token", "FUNASSYI": "Funassyi", @@ -5736,6 +5965,7 @@ "G1X": "GoldFinX", "G3": "GAM3S.GG", "G50": "G50", + "G7": "Game7", "G8C": "ONEG8.ONE", "G999": "G999", "GAC": "Green Art Coin", @@ -5915,6 +6145,7 @@ "GENX": "Genx Token", "GENXNET": "Genesis Network", "GENZ": "GENZ Token", + "GENZAI": "GENZAI", "GEO": "GeoCoin", "GEOD": "GEODNET", "GEODB": "GeoDB", @@ -5942,6 +6173,7 @@ "GFCS": "Global Funeral Care", "GFI": "Goldfinch", "GFLY": "BattleFly", + "GFM": "GoFundMeme", "GFN": "Graphene", "GFOX": "Galaxy Fox", "GFT": "Gifto", @@ -5954,6 +6186,7 @@ "GGC": "Global Game Coin", "GGCM": "Gold Guaranteed Coin", "GGG": "Good Games Guild", + "GGGG": "Good Game Gary Gensler", "GGH": "Green Grass Hopper", "GGM": "Monster Galaxy", "GGMT": "GG MetaGame", @@ -5993,8 +6226,10 @@ "GIFT": "GiftNet", "GIG": "GigaCoin", "GIGA": "Gigachad", + "GIGABRAIN": "Gigabrain by virtuals", "GIGACAT": "GIGACAT", "GIGACHAD": "GigaChad", + "GIGAG": "GIGAGEEK", "GIGASWAP": "GigaSwap", "GIGGLE": "Giggle Academy", "GIGS": "Climate101", @@ -6091,6 +6326,7 @@ "GMNT": "Gmining", "GMPD": "GamesPad", "GMR": "GAMER", + "GMRT": "Gamertag Token", "GMRV1": "GAMER v1", "GMRV2": "GAMER v2", "GMRX": "Gaimin", @@ -6132,6 +6368,7 @@ "GOC": "GoCrypto", "GOCHU": "Gochujangcoin", "GOD": "Bitcoin God", + "GODC": "Godcoin", "GODCAT": "GodcatExplodingKittens", "GODE": "Gode Chain", "GODEX": "GUARD OF DECENT", @@ -6146,9 +6383,11 @@ "GOGLZ": "GOGGLES", "GOGO": "GOGO Finance", "GOGU": "GOGU Coin", + "GOHOME": "GOHOME", "GOIN": "GOinfluencer", "GOJOCOIN": "Gojo Coin", - "GOKU": "Goku", + "GOKU": "Goku Super Saiyan", + "GOKUINU": "Goku (gokuinu.io)", "GOL": "GogolCoin", "GOLC": "GOLCOIN", "GOLD": "CyberDragon Gold", @@ -6226,7 +6465,9 @@ "GPN": "Gamepass Network", "GPO": "GoldPesa Option", "GPPT": "Pluto Project Coin", - "GPS": "Triffic", + "GPRO": "GoldPro", + "GPS": "GoPlus Security", + "GPSTOKEN": "GPS Token", "GPT": "QnA3.AI", "GPT4O": "GPT-4o", "GPTG": "GPT Guru", @@ -6243,6 +6484,7 @@ "GRAIL": "Camelot Token", "GRAIN": "Granary", "GRAM": "Gram", + "GRAND": "Grand Theft Ape", "GRANDCOIN": "GrandCoin", "GRANDMA": "Grandma", "GRAPE": "GrapeCoin", @@ -6279,6 +6521,7 @@ "GRIMEVO": "Grim EVO", "GRIMEX": "SpaceGrime", "GRIN": "Grin", + "GRIND": "Self Improving", "GRL": "Greelance", "GRLC": "Garlicoin", "GRM": "GridMaster", @@ -6290,6 +6533,8 @@ "GROGGO": "Groggo By Matt Furie", "GROK": "Grok", "GROK2": "GROK 2.0", + "GROK3": "Grok 3", + "GROKAI": "Grok AI Agent", "GROKBANK": "Grok Bank", "GROKBOY": "GrokBoy", "GROKCAT": "Grok Cat", @@ -6306,12 +6551,14 @@ "GROKOLAUS": "GROKolaus", "GROKQUEEN": "Grok Queen", "GROKSORAX": "GROKSORAX", + "GROKVANCE": "GROK VANCE", "GROKX": "GROKX", "GROKXAI": "Grok X Ai", "GRON": "Gron Digital", "GROOOOOK": "Groooook", "GROOVE": "GROOVE", - "GROW": "GrownCoin", + "GROW": "Grow Token", + "GROWNCOIN": "GrownCoin", "GROWTH": "GROWTH DeFi", "GROYPER": "Groyper", "GRP": "Grape", @@ -6370,6 +6617,7 @@ "GTSE": "Global Tourism Sharing Ecology", "GTTM": "Going To The Moon", "GTX": "GALLACTIC", + "GTY": "G-Agents AI", "GUA": "GUA", "GUAC": "Guacamole", "GUAN": "Guanciale by Virtuals", @@ -6379,6 +6627,7 @@ "GUARDAI": "GuardAI", "GUC": "Green Universe Coin", "GUCCI": "GUCCI", + "GUDTEK": "ai16zterminalfartARCzereLLMswarm", "GUE": "GuerillaCoin", "GUESS": "Peerguess", "GUGU": "gugu", @@ -6432,6 +6681,7 @@ "GZT": "Golden Zen Token", "GZX": "GreenZoneX", "Glo Dollar": "USDGLO", + "H1DR4": "H1DR4 by Virtuals", "H2O": "H2O Dao", "H2ON": "H2O Securities", "H3O": "Hydrominer", @@ -6488,6 +6738,7 @@ "HARR": "HARRIS DOGS", "HARRIS": "KAMALA HARRIS", "HARRISV": "Harris V Trump", + "HARRYBOLZ": "Harry Bolz", "HARRYP": "HarryPotterObamaSonic10Inu (ERC20)", "HARRYPO": "HarryPotterObamaPacMan8Inu", "HART": "HARA", @@ -6533,6 +6784,7 @@ "HBX": "Hyperbridge", "HBZ": "HBZ Coin", "HC": "HyperCash", + "HCAT": "Hover Cat", "HCC": "HappyCreatorCoin", "HCT": "HurricaneSwap Token", "HCXP": "HCX PAY", @@ -6551,6 +6803,7 @@ "HEALT": "Healthmedi", "HEART": "Humans", "HEARTBOUT": "HeartBout Pay", + "HEARTN": "Heart Number", "HEARTR": "Heart Rate", "HEAT": "Heat Ledger", "HEAVEN": "Heaven Token", @@ -6565,6 +6818,7 @@ "HEGG": "Hummingbird Egg", "HEGIC": "Hegic", "HEHE": "hehe", + "HEI": "Heima", "HEL": "Hello Puppy", "HELA": "Science Cult Mascot", "HELI": "Helion", @@ -6578,11 +6832,13 @@ "HEMAN": "HE-MAN", "HEMULE": "Hemule", "HENG": "HengCoin", + "HENL": "henlo", "HENLO": "Henlo", "HENLOV1": "Henlo v1", "HEP": "Health Potion", "HER": "Hero Node", "HERA": "Hero Arena", + "HERAF": "Hera Finance", "HERB": "HerbCoin", "HERBE": "Herbee", "HERME": "Hermes DAO", @@ -6592,11 +6848,13 @@ "HERO": "Metahero", "HEROC": "HEROcoin", "HEROES": "Dehero Community Token", + "HEROESAI": "HEROES AI", "HEROESC": "HeroesChained", "HEROI": "Heroic Saga Shiba", "HET": "HavEther", "HETA": "HetaChain", "HETH": "Huobi Ethereum", + "HEU": "Heurist AI", "HEWE": "Health & Wealth", "HEX": "HEX", "HEXC": "HexCoin", @@ -6650,7 +6908,9 @@ "HIMO": "Himo World", "HIMOONBIRDS": "hiMOONBIRDS", "HINA": "Hina Inu", - "HINT": "Hintchain", + "HINAGI": "Hinagi", + "HINT": "Hive Intelligence", + "HINTCH": "Hintchain", "HINU": "HajiIni", "HIOD": "hiOD", "HIODBS": "hiODBS", @@ -6679,6 +6939,7 @@ "HKFLOKI": "hong kong floki", "HKG": "Hacker Gold", "HKN": "Hacken", + "HKU5": "New Coronavirus", "HLC": "HalalChain", "HLD": "HyperLending", "HLDY": "HOLIDAY", @@ -6733,6 +6994,7 @@ "HODL": "HOdlcoin", "HOG": "Hog", "HOGE": "Hoge Finance", + "HOGONSOLANA": "HOG", "HOHOHO": "Santa Floki v2.0", "HOICHI": "Hoichi", "HOKA": "Hokkaido Inu", @@ -6741,9 +7003,11 @@ "HOLA": "Hola Token", "HOLD": "HOLD", "HOLDEX": "Holdex Finance", + "HOLDON4": "HoldOn4DearLife", "HOLY": "Holy Trinity", "HOM": "Homeety", "HOME": "OtterHome", + "HOMEBREW": "Homebrew Robotics Club", "HOMER": "Homer Simpson", "HOMERB": "Homer BSC", "HOMERO": "Homer Of Meme", @@ -6758,6 +7022,7 @@ "HONK": "Honk", "HONKLER": "Honkler", "HONOR": "HonorLand", + "HOODRAT": "Hoodrat Coin", "HOOF": "Metaderby Hoof", "HOOK": "Hooked Protocol", "HOOP": "Chibi Dinos", @@ -6781,6 +7046,7 @@ "HOTMOON": "HotMoon Token", "HOTN": "HotNow", "HOTT": "HOT Token", + "HOUND": "BaseHoundBot by Virtuals", "HOUSE": "Klaymore Stakehouse", "HOW": "HowInu", "HOWL": "Coyote", @@ -6834,7 +7100,7 @@ "HTM": "Hatom", "HTML": "HTML Coin", "HTMOON": "HTMOON", - "HTN": "Heart Number", + "HTN": "Hoosat Network", "HTO": "Heavenland HTO", "HTR": "Hathor", "HTT": "Hello Art", @@ -6851,6 +7117,7 @@ "HUGO": "Hugo Inu", "HUH": "HUH Token", "HUHCAT": "huhcat", + "HULEZHI": "HU LE ZHI", "HUM": "Humanscape", "HUMAI": "Humanoid AI", "HUMP": "Hump", @@ -6873,6 +7140,7 @@ "HVE2": "Uhive", "HVH": "HAVAH", "HVI": "Hungarian Vizsla Inu", + "HVLO": "Hivello", "HVN": "Hiveterminal Token", "HVNT": "HiveNet Token", "HVT": "HyperVerse", @@ -6948,6 +7216,7 @@ "ICE": "Ice Open Network", "ICEC": "IceCream", "ICECR": "Ice Cream Sandwich", + "ICECREAM": "IceCream AI", "ICELAND": "ICE LAND", "ICETH": "Interest Compounding ETH Index", "ICG": "Invest Club Global", @@ -7048,6 +7317,7 @@ "IMGZ": "Imigize", "IMI": "Influencer", "IML": "IMMLA", + "IMMIGRATION": "Immigration Customs Enforcement", "IMMO": "ImmortalDAO Finance", "IMMORTAL": "IMMORTAL.COM", "IMO": "IMO", @@ -7087,6 +7357,8 @@ "INDIAN": "Indian Call Center", "INDICOIN": "IndiCoin", "INDU": "INDU4.0", + "INDUSTRIAL": "Industrial", + "INDX": "CryptoIndex", "INDY": "Indigo Protocol", "INE": "IntelliShare", "INEDIBLE": "INEDIBLE", @@ -7101,6 +7373,7 @@ "INFINI": "Infinity Economics", "INFLR": "Inflr", "INFO": "Infomatix", + "INFR": "infraX", "INFRA": "Bware", "INFT": "Infinito", "INFTT": "iNFT Token", @@ -7126,7 +7399,6 @@ "INSP": "Inspect", "INSPI": "InspireAI", "INSR": "Insurabler", - "INST": "Instadapp", "INSTAMINE": "Instamine Nuggets", "INSTAR": "Insights Network", "INSUR": "InsurAce", @@ -7183,6 +7455,7 @@ "IOV": "Starname", "IOVT": "IOV", "IOWN": "iOWN Token", + "IP": "Story", "IP3": "Cripco", "IPAD": "Infinity Pad", "IPC": "IPChain", @@ -7227,11 +7500,13 @@ "ISKY": "Infinity Skies", "ISL": "IslaCoin", "ISLAMI": "ISLAMICOIN", + "ISLAND": "ISLAND Token", "ISLM": "Islamic Coin", "ISME": "Root Protocol", "ISP": "Ispolink", "ISR": "Insureum", "ISRG.CUR": "Intuitive Surgical, Inc.", + "ISSOU": "Risitas", "ISSP": "ISSP", "IST": "Inter Stable Token", "ISTEP": "iSTEP", @@ -7288,7 +7563,7 @@ "IZX": "IZX", "IZZY": "Izzy", "InBit": "PrepayWay", - "J": "JoinCoin", + "J": "Jambo", "J8T": "JET8", "J9BC": "J9CASINO", "JACK": "Jack Token", @@ -7296,8 +7571,12 @@ "JACY": "JACY", "JADE": "Jade Protocol", "JADEC": "Jade Currency", + "JAE": "JaeCoin", "JAGO": "Jagotrack", + "JAI": "Japanese Akita Inu", "JAIHO": "Jaiho Crypto", + "JAIHOZ": "Jaihoz by Virtuals", + "JAILSTOOL": "Stool Prisondente", "JAKE": "Jake The Dog", "JAM": "Tune.Fm", "JAN": "Storm Warfare", @@ -7305,6 +7584,7 @@ "JANET": "Janet", "JANI": "JANI", "JANRO": "Janro The Rat", + "JAPAN": "Japan Open Chain", "JAR": "Jarvis+", "JARED": "Jared From Subway", "JARY": "JeromeAndGary", @@ -7327,6 +7607,7 @@ "JDAI": "Dai (TON Bridge)", "JDC": "JustDatingSite", "JDO": "JINDO", + "JDV": "JD Vance", "JED": "JEDSTAR", "JEDALS": "Yoda Coin Swap", "JEET": "Jeet", @@ -7337,6 +7618,8 @@ "JEJUDOGE": "Jejudoge", "JELLI": "JELLI", "JELLY": "Jelly eSports", + "JELLYAI": "jelly ai agent", + "JELLYJELLY": "Jelly-My-Jelly", "JEM": "Jem", "JEN": "JEN COIN", "JENNER": "Caitlyn Jenner", @@ -7360,6 +7643,7 @@ "JFI": "JackPool.finance", "JFIN": "JFIN Coin", "JFIVE": "Jonny Five", + "JFOX": "JuniperFox AI", "JFP": "JUSTICE FOR PEANUT", "JGLP": "Jones GLP", "JGN": "Juggernaut", @@ -7397,6 +7681,7 @@ "JNY": "JNY", "JOB": "Jobchain", "JOBS": "JobsCoin", + "JOBSEEK": "JobSeek AI", "JOC": "Speed Star JOC", "JOE": "JOE", "JOEB": "Joe Biden", @@ -7406,6 +7691,7 @@ "JOGECO": "Jogecodog", "JOHM": "Johm lemmon", "JOHNNY": "Johnny The Bull", + "JOINCOIN": "JoinCoin", "JOINT": "Joint Ventures", "JOJO": "JOJO", "JOK": "JokInTheBox", @@ -7485,6 +7771,7 @@ "JWBTC": "Wrapped Bitcoin (TON Bridge)", "JWIF": "Jerrywifhat", "JWL": "Jewels", + "JYAI": "Jerry The Turtle By Matt Furie", "JYC": "Joe-Yo Coin", "K21": "K21", "K2G": "Kasko2go", @@ -7506,7 +7793,9 @@ "KAIK": "KAI KEN", "KAIKEN": "Kaiken Shiba", "KAILY": "Kailith", + "KAIM": "Kai Meme", "KAINET": "KAINET", + "KAITO": "KAITO", "KAKA": "KAKA NFT World", "KAKAXA": "KAKAXA", "KAKI": "Doge KaKi", @@ -7532,6 +7821,7 @@ "KANGAL": "Kangal", "KANGO": "KANGO", "KAP": "KAP Games", + "KAPPA": "Kappa", "KAPPY": "Kappy", "KAPU": "Kapu", "KAR": "Karura", @@ -7543,6 +7833,7 @@ "KARMAD": "Karma DAO", "KARRAT": "KARRAT", "KART": "Dragon Kart", + "KARUM": "Karum Coin", "KAS": "Kaspa", "KASBOT": "KASBOT THE GUARDIAN OF 𐤊ASPA", "KASHIN": "KASHIN", @@ -7561,7 +7852,6 @@ "KAVA": "Kava", "KAWA": "Kawakami Inu", "KAYI": "Kayı", - "KB3": "B3Coin", "KBC": "Karatgold coin", "KBD": "Kyberdyne", "KBOND": "Klondike Bond", @@ -7597,7 +7887,9 @@ "KEEP": "Keep Network", "KEES": "Korea Entertainment Education & Shopping", "KEI": "Keisuke Inu", + "KEIRA": "Keira", "KEK": "KekCoin", + "KEKARMY": "Kek", "KEKE": "KEK", "KEKEC": "THE BALKAN DWARF", "KEKIUS": "Kekius Maximus", @@ -7653,7 +7945,8 @@ "KICKS": "GetKicks", "KIDEN": "RoboKiden", "KIF": "KittenFinance", - "KIKI": "Kiki Flaminki", + "KIKI": "KIKICat", + "KIKIF": "Kiki Flaminki", "KIKO": "KIKO", "KILLA": "The Bitcoin Killa", "KILLER": "Fat Cat Killer", @@ -7662,6 +7955,7 @@ "KIM": "King Money", "KIMBO": "Kimbo", "KIMCHI": "KIMCHI.finance", + "KIMIAI": "Kimi AI Agent", "KIN": "Kin", "KIND": "Kind Ads", "KINE": "Kine Protocol", @@ -7692,6 +7986,7 @@ "KINK": "Kinka", "KINT": "Kintsugi", "KINU": "Kragger Inu", + "KIP": "KIP", "KIRA": "Kira the Injective Cat", "KIRBY": "Kirby Inu", "KIRBYCEO": "Kirby CEO", @@ -7772,6 +8067,7 @@ "KODACHI": "Kodachi Token", "KOGE": "BNB48 Club Token", "KOGECOIN": "KogeCoin.io", + "KOGIN": "Kogin by Virtuals", "KOI": "Koi", "KOII": "Koii", "KOIN": "Koinos", @@ -7919,6 +8215,7 @@ "KZEN": "Kaizen", "L": "L inu", "L1": "Lamina1", + "L1X": "Layer One X", "L2": "Leverj Gluon", "L2DAO": "Layer2DAO", "L3": "Layer3", @@ -7959,6 +8256,7 @@ "LANDV1": "Landshare v1", "LANDW": "LandWolf", "LANDWOLF": "LANDWOLF", + "LANDWOLFAVAX": "LANDWOLF (AVAX)", "LANDWOLFETH": "Landwolf", "LANDWU": "LandWu", "LANE": "LaneAxis", @@ -7989,7 +8287,7 @@ "LAWO": "Law Of Attraction", "LAX": "LAPO", "LAY3R": "AutoLayer", - "LAYER": "UniLayer", + "LAYER": "Solayer", "LAZ": "Lazarus", "LAZIO": "Lazio Fan Token", "LAZYCAT": "LAZYCAT", @@ -8020,6 +8318,7 @@ "LCR": "Lucro", "LCRO": "Liquid CRO", "LCS": "LocalCoinSwap", + "LCSH": "LC SHIB", "LCSN": "Lacostoken", "LCT": "LendConnect", "LCWP": "LiteCoinW Plus", @@ -8047,7 +8346,9 @@ "LEG": "Legia Warsaw Fan Token", "LEGEND": "Legend", "LEGION": "LEGION", + "LEGIT": "LEGIT", "LEGO": "Lego Coin", + "LEI": "Leia Games", "LEIA": "Leia", "LELE": "Lelecoin", "LEMC": "LemonChain", @@ -8076,6 +8377,7 @@ "LESLIE": "Leslie", "LESS": "Less Network", "LESSF": "LessFnGas", + "LESTE": "LESTER by Virtuals", "LESTER": "Litecoin Mascot", "LET": "LinkEye", "LETIT": "Letit", @@ -8093,7 +8395,7 @@ "LEXI": "LEXIT", "LEZ": "Peoplez", "LEZGI": "LEZGI Token", - "LF": "Linkflow", + "LF": "LF", "LFC": "BigLifeCoin", "LFDOG": "lifedog", "LFG": "Gamerse", @@ -8108,7 +8410,9 @@ "LGC": "LiveGreen Coin", "LGCY": "LGCY Network", "LGD": "Legends Cryptocurrency", + "LGG": "Let's Go Gambling", "LGNDX": "LegendX", + "LGNS": "Longinus", "LGO": "Legolas Exchange", "LGOLD": "LYFE GOLD", "LGOT": "LGO Token", @@ -8124,7 +8428,7 @@ "LIBERO": "Libero Financial", "LIBERTA": "The Libertarian Dog", "LIBFX": "Libfx", - "LIBRA": "0L Network", + "LIBRA": "Libra", "LIBRAP": "Libra Protocol", "LIBRE": "Libre", "LIC": "Ligercoin", @@ -8153,6 +8457,7 @@ "LILA": "LiquidLayer", "LILB": "Lil Brett", "LILFLOKI": "Lil Floki", + "LILO": "Lilo", "LILPUMP": "lilpump", "LILY": "LILY-The Gold Digger", "LIMBO": "Limbo", @@ -8221,6 +8526,7 @@ "LKY": "LuckyCoin", "LL": "LightLink", "LLAND": "Lyfe Land", + "LLD": "Liberland dollar", "LLG": "Loligo", "LLION": "Lydian Lion", "LLM": "Large Language Model Based", @@ -8272,6 +8578,7 @@ "LOE": "Legends of Elysium", "LOF": "Land of Fantasy", "LOFI": "LOFI", + "LOFIBUZZ": "LOFI", "LOG": "Wood Coin", "LOGO": "LOGOS", "LOGX": "LogX Network", @@ -8395,6 +8702,7 @@ "LTO": "LTO Network", "LTOV1": "LTO Network v1", "LTOV2": "LTO Network v2", + "LTP": "Listapie", "LTPC": "Lightpaycoin", "LTR": "LogiTron", "LTRBT": "Little Rabbit", @@ -8518,11 +8826,14 @@ "MAG": "Magnify Cash", "MAGA": "MAGA", "MAGA2024": "MAGA2024", + "MAGA47": "MAGA 47", "MAGAA": "MAGA AGAIN", + "MAGABRO": "M.A.G.A. Bro", "MAGAC": "MAGA CAT", "MAGACA": "MAGA CAT", "MAGACAT": "MAGACAT", "MAGADOGE": "MAGA DOGE", + "MAGAF": "MAGA FRENS", "MAGAHAT": "MAGA Hat", "MAGAIBA": "Magaiba", "MAGAN": "Maganomics On Solana", @@ -8539,6 +8850,7 @@ "MAGICV": "Magicverse", "MAGIK": "Magik Finance", "MAGN": "Magnate Finance", + "MAGNE": "Magnetix", "MAGNET": "Yield Magnet", "MAGNET6900": "MAGNET6900", "MAGNETWORK": "Magnet", @@ -8569,6 +8881,7 @@ "MAND": "Mandala Exchange Token", "MANDALA": "Mandala Exchange Token", "MANDOX": "MandoX", + "MANDY": "MANDY COIN", "MANE": "MANE", "MANEKI": "MANEKI", "MANGA": "Manga Token", @@ -8579,7 +8892,9 @@ "MANT": "Mantle USD", "MANTA": "Manta Network", "MANTLE": "Mantle", + "MANUSAI": "Manus AI Agent", "MANYU": "Little Manyu", + "MANYUDOG": "MANYU", "MAO": "Mao", "MAOW": "MAOW", "MAP": "MAP Protocol", @@ -8655,6 +8970,7 @@ "MAWA": "Kumala Herris", "MAWC": "Magawincat", "MAX": "Matr1x", + "MAXAIAGENT": "MAX", "MAXCOIN": "MaxCoin", "MAXETH": "Max on ETH", "MAXI": "Maximus", @@ -8663,6 +8979,7 @@ "MAXX": "MAXX Finance", "MAY": "Theresa May Coin", "MAYACOIN": "MayaCoin", + "MAYILONG": "Yi long ma", "MAYO": "Mr Mayonnaise the Cat", "MAYP": "Maya Preferred", "MAZC": "MyMazzu", @@ -8762,6 +9079,7 @@ "MDM": "Medium", "MDN": "Modicoin", "MDOGE": "First Dog In Mars", + "MDOGS": "Money Dogs", "MDR": "Mudra MDR", "MDS": "MediShares", "MDT": "Measurable Data Token", @@ -8820,6 +9138,7 @@ "MEM": "Memecoin", "MEMAGX": "Meta Masters Guild Games", "MEMD": "MemeDAO", + "MEMDEX": "Memdex100", "MEME": "Memecoin", "MEMEAI": "Meme Ai", "MEMECUP": "Meme Cup", @@ -8836,6 +9155,7 @@ "MEMESQUAD": "Meme Squad", "MEMET": "MEMETOON", "MEMETIC": "Memetic", + "MEMHASH": "Memhash", "MEMORYCOIN": "MemoryCoin", "MEN": "METAHUB FINANCE", "MENDI": "Mendi Finance", @@ -8854,6 +9174,7 @@ "MERCU": "Merculet", "MERCURY": "Mercury", "MEREDITH": "Taylor Swift's Cat MEREDITH", + "MERG": "Merge Token", "MERGE": "Merge", "MERI": "Merebel", "MERIDIAN": "Meridian Network LOCK", @@ -8870,6 +9191,7 @@ "META": "MetaDAO", "METAA": "META ARENA", "METABOT": "Robot Warriors", + "METABRAW": "Metabrawl", "METAC": "Metacoin", "METACA": "MetaCash", "METACAT": "MetaCat", @@ -8968,6 +9290,7 @@ "MHUNT": "MetaShooter", "MI": "XiaoMiCoin", "MIA": "MiamiCoin", + "MIAO": "MIAOCoin", "MIB": "Mobile Integrated Blockchain", "MIBO": "miBoodle", "MIBR": "MIBR Fan Token", @@ -8981,6 +9304,7 @@ "MIDAI": "Midway AI", "MIDAS": "Midas", "MIDASDOLLAR": "Midas Dollar Share", + "MIDLE": "Midle", "MIDN": "Midnight", "MIDNIGHT": "Midnight", "MIE": "MIE Network", @@ -9034,6 +9358,7 @@ "MINI": "mini", "MINIBNBTIGER": "MiniBNBTiger", "MINID": "Mini Donald", + "MINIDO": "MiniDoge", "MINIDOGE": "MiniDOGE", "MINIFOOTBALL": "Minifootball", "MINIMYRO": "Mini Myro", @@ -9051,6 +9376,7 @@ "MINTCOIN": "MintCoin", "MINTE": "Minter HUB", "MINTME": "MintMe.com Coin", + "MINTO": "The AI Mascot", "MINTYS": "MintySwap", "MINU": "Minu", "MINUTE": "MINUTE Vault (NFTX)", @@ -9070,7 +9396,9 @@ "MISHA": "Vitalik's Dog", "MISHKA": "Mishka Token", "MISS": "MISS", + "MISSK": "Miss Kaka", "MIST": "Mist", + "MISTCOIN": "MistCoin", "MISTE": "Mister Miggles", "MISTRAL": "Mistral AI", "MIT": "Galaxy Blitz", @@ -9147,7 +9475,7 @@ "MNB": "MoneyBag", "MNBR": "MN Bridge", "MNC": "MainCoin", - "MND": "Mound Token", + "MND": "Mind", "MNDCC": "Mondo Community Coin", "MNDE": "Marinade", "MNE": "Minereum", @@ -9224,6 +9552,7 @@ "MOGT": "MOG TRUMP", "MOGU": "Mogu", "MOGUL": "Mogul Productions", + "MOGULV1": "Mogul Productions v1", "MOGUT": "Mogutou", "MOGX": "Mogu", "MOH": "Medal of Honour", @@ -9231,6 +9560,7 @@ "MOIN": "MoinCoin", "MOJI": "Moji", "MOJO": "Mojocoin", + "MOJOB": "Mojo on Base", "MOK": "MocktailSwap", "MOL": "Molecule", "MOLA": "MoonLana", @@ -9242,6 +9572,7 @@ "MOMA": "Mochi Market", "MOMIJI": "MAGA Momiji", "MOMO": "MOMO 2.0", + "MOMO2025": "momo", "MON": "MON Protocol", "MONA": "MonaCoin", "MONAIZE": "Monaize", @@ -9337,6 +9668,7 @@ "MOTHER": "Mother Iggy", "MOTI": "Motion", "MOTO": "Motocoin", + "MOUND": "Mound Token", "MOUTAI": "Moutai", "MOV": "MovieCoin", "MOVD": "MOVE Network", @@ -9352,6 +9684,7 @@ "MOXIE": "Moxie", "MOYA": "MOYA", "MOZ": "Mozik", + "MOZA": "Mozaic", "MP": "Membership Placeholders", "MP3": "MP3", "MPAA": "MPAA", @@ -9376,6 +9709,7 @@ "MQL": "MiraQle", "MQST": "MonsterQuest", "MR": "Meta Ruffy", + "MRB": "MoonRabbits", "MRBASED": "MrBased", "MRBOB": "MR BOB COIN", "MRCH": "MerchDAO", @@ -9393,6 +9727,7 @@ "MRS": "Metars Genesis", "MRSA": "MrsaCoin", "MRSMIGGLES": "Mrs Miggles", + "MRST": "Mars Token", "MRT": "MinersReward", "MRUN": "Metarun", "MRV": "Macroverse", @@ -9440,6 +9775,7 @@ "MTGT": "MTG Token", "MTGX": "Montage Token", "MTH": "Monetha", + "MTHB": "MTHAIBAHT", "MTHD": "Method Finance", "MTHN": "MTH Network", "MTIK": "MatikaToken", @@ -9505,6 +9841,7 @@ "MUSICAI": "MusicAI", "MUSICOIN": "Musicoin", "MUSK": "Musk", + "MUSKAI": "Musk AI Agent", "MUSKMEME": "MUSK MEME", "MUSKVSZUCK": "Cage Match", "MUST": "MUST Protocol", @@ -9536,12 +9873,16 @@ "MWC": "MimbleWimbleCoin", "MWCC": "Metaworld", "MWD": "MEW WOOF DAO", + "MWETH": "Moonwell Flagship ETH (Morpho Vault)", + "MWH": "Melania Wif Hat", "MX": "MX Token", "MXC": "Machine Xchange Coin", "MXD": "Denarius", "MXGP": "MXGP Fan Token", "MXM": "Maximine", + "MXNA": "Machina", "MXNB": "MXNB", + "MXNBC": "Rekt Burgundy by Virtuals", "MXNT": "Tether MXNt", "MXRP": "Monsta XRP", "MXT": "MixTrust", @@ -9582,6 +9923,7 @@ "Medu": "Medusa", "N0031": "nYFI", "N1": "NFTify", + "N3": "Network3", "N3DR": "NeorderDAO ", "N64": "N64", "N7": "Number7", @@ -9639,9 +9981,11 @@ "NAWS": "NAWS.AI", "NAX": "NextDAO", "NAYM": "NAYM", + "NAYUTA": "Nayuta Coin", "NAZ": "NAZDAQ", "NAZA": "NAZA", "NAZAR": "NAZAR PROTOCOL", + "NAZIELON": "NAZI ELON", "NBABSC": "NBA BSC", "NBAI": "Nebula AI", "NBAR": "NOBAR", @@ -9658,7 +10002,7 @@ "NBS": "New BitShares", "NBT": "NanoByte", "NBXC": "Nibble", - "NC": "Nayuta Coin", + "NC": "Nodecoin", "NCA": "NeuroCrypto Ads", "NCASH": "Nucleus Vision", "NCAT": "Neuracat", @@ -9735,6 +10079,7 @@ "NERF": "Neural Radiance Field", "NERO": "Nero Token", "NERVE": "NERVE", + "NES": "Nest AI", "NESS": "Ness LAB", "NEST": "Nest Protocol", "NESTREE": "Nestree", @@ -9752,6 +10097,7 @@ "NETZ": "MainnetZ", "NETZ1": "NETZERO", "NEU": "Neumark", + "NEUR": "neur.sh", "NEURA": "Neurahub", "NEURAL": "NeuralAI", "NEURALINK": "Neuralink", @@ -9770,6 +10116,7 @@ "NEWB": "Newbium", "NEWBV1": "Newbium v1", "NEWC": "New Cat", + "NEWERASOL": "New Era AI", "NEWG": "NewGold", "NEWM": "NEWM", "NEWO": "New Order", @@ -9784,6 +10131,7 @@ "NEXAI": "NexAI", "NEXBOX": "NexBox", "NEXBT": "Native XBTPro Exchange Token", + "NEXEA": "NEXEA", "NEXG": "NexGami", "NEXM": "Nexum", "NEXMI": "NexMillionaires", @@ -9795,7 +10143,8 @@ "NEXTV1": "Connext Network", "NEXUSAI": "NexusAI", "NEXXO": "Nexxo", - "NEZHA": "NezhaToken", + "NEZHA": "NEZHA", + "NEZHATOKEN": "NezhaToken", "NFAI": "Not Financial Advice", "NFAIV1": "Not Financial Advice v1", "NFCR": "NFCore", @@ -9840,6 +10189,7 @@ "NHI": "Non Human Intelligence", "NHT": "Neighbourhoods", "NIAO": "NIAO", + "NIBBLES": "Nibbles", "NIBI": "Nibiru Chain", "NIC": "NewInvestCoin", "NICE": "Nice", @@ -9860,8 +10210,10 @@ "NIKO": "NikolAI", "NILE": "Nile", "NIM": "Nimiq", + "NIMBUS": "Nimbus AI", "NIMFA": "Nimfamoney", "NIN": "Next Innovation", + "NINA": "NINA", "NINJ": "Ninja Protocol", "NINJA": "Dog Wif Nunchucks", "NINJACAT": "NinjaCat", @@ -9883,6 +10235,7 @@ "NITO": "Nitroken", "NITRO": "Nitro League", "NITROE": "NitroEX", + "NITROFROG": "Nitro", "NITROG": "Nitro", "NIX": "NIX", "NIZA": "Niza Global", @@ -9922,7 +10275,7 @@ "NOBL": "NobleCoin", "NOBS": "No BS Crypto", "NOCHILL": "AVAX HAS NO CHILL", - "NODE": "Whole Network", + "NODE": "NodelyAI", "NODESYNAPSE": "NodeSynapse", "NODIDDY": "NODIDDY", "NODIS": "Nodis", @@ -9945,9 +10298,11 @@ "NOODS": "Noods", "NOOOO": "NOOOO", "NOOT": "NOOT (Ordinals)", + "NOPAIN": "No Pain No Gain", "NOR": "Noir", "NORA": "SnowCrash Token", "NORD": "Nord Finance", + "NORDO": "Greenland Rare Bear", "NORMIE": "Normie", "NORMUS": "NORMUS", "NOS": "Nosana", @@ -10060,6 +10415,7 @@ "NVC": "NovaCoin", "NVDX": "Nodvix", "NVG": "NightVerse Game", + "NVG8": "Navigate", "NVIR": "NvirWorld", "NVL": "Nevula", "NVOY": "Envoy", @@ -10114,11 +10470,13 @@ "OAK": "Acorn Collective", "OAS": "Oasis City", "OASC": "Oasis City", + "OASI": "Oasis Metaverse", "OASIS": "Oasis", "OAT": "OAT Network", "OATH": "OATH Protocol", "OAX": "Oax", "OB1INCH": "1inch (OmniBridge)", + "OBABYTRUMP": "Official Baby Trump", "OBEMA": "burek obema", "OBI": "Orbofi AI", "OBICOIN": "OBI Real Estate", @@ -10147,6 +10505,7 @@ "OCICAT": "OciCat", "OCL": "Oceanlab", "OCN": "Odyssey", + "OCNEST": "OcNest AI", "OCO": "Owners Casino Online", "OCP": "Omni Consumer Protocols", "OCPR": "OC Protocol", @@ -10157,8 +10516,9 @@ "OCTAVUS": "Octavus Prime", "OCTAX": "OctaX", "OCTI": "Oction", - "OCTO": "OctoFi", + "OCTO": "OctonetAI", "OCTOCOIN": "Octocoin", + "OCTOF": "OctoFi", "OCTOIN": "Octoin Coin", "OCW": "Online Cold Wallet", "OCX": "Original Crypto Coin", @@ -10180,8 +10540,14 @@ "OFCR": "CryptoPolice", "OFE": "Ofero", "OFF": "BlastOff", + "OFFI": "Official Elon Coin", + "OFFIC": "OFFICIAL SIMPSON", + "OFFICI": "OFFICIAL BARRON", + "OFFICIA": "Official Elon Coin", "OFN": "Openfabric AI", + "OFT": "ONFA", "OG": "OG Fan Token", + "OGC": "OGCommunity", "OGCINU": "The OG Cheems Inu", "OGD": "OLYMPIC GAMES DOGE", "OGGIE": "Oggie", @@ -10206,6 +10572,8 @@ "OHNOGG": "OHNHO (ohno.gg)", "OHO": "OHO", "OICOIN": "Osmium Investment Coin", + "OIIAOIIA": "spinning cat", + "OIK": "Space Nation", "OIL": "Oiler", "OILD": "OilWellCoin", "OILX": "OilX Token", @@ -10249,6 +10617,7 @@ "OMD": "OneMillionDollars", "OME": "o-mee", "OMEGA": "OMEGA", + "OMEGAX": "Ome‎gaX He‎alth", "OMG": "OMG Network", "OMGC": "OmiseGO Classic", "OMI": "ECOMI", @@ -10276,6 +10645,7 @@ "ONC": "One Cash", "ONCH": "OnchainPoints.xyz", "ONDO": "Ondo", + "ONDOAI": "Ondo DeFAI", "ONE": "Harmony", "ONES": "OneSwap DAO", "ONET": "ONE Token", @@ -10296,6 +10666,7 @@ "ONS": "One Share", "ONSTON": "Onston", "ONT": "Ontology", + "ONTACT": "OnTact", "ONUS": "ONUS", "ONX": "Onix", "OOE": "OpenOcean", @@ -10350,15 +10721,18 @@ "OPTIG": "Catgirl Optimus", "OPTIM": "Optimus X", "OPTIMOUSE": "Optimouse", + "OPTIO": "Optio", "OPTION": "OptionCoin", "OPU": "Opu Coin", "OPUL": "Opulous", "OPUS": "Opus", "OPV": "OpenLive NFT", "OPXVEVELO": "OpenX Locked Velo", - "ORA": "Oracolxor", + "ORA": "ORA Coin", "ORACLE": "Oracle AI", "ORACLECHAIN": "OracleChain", + "ORACLER": "Oracler", + "ORACOLXOR": "Oracolxor", "ORACUL": "Oracul Ai", "ORAI": "Oraichain Token", "ORAIX": "OraiDEX", @@ -10390,6 +10764,7 @@ "ORGT": "Organic Token", "ORI": "Origami", "ORIGIN": "Origin Foundation", + "ORIGINA": "Original Gangsters", "ORION": "Orion Money", "ORKL": "Orakler", "ORLY": "OrlyCoin", @@ -10425,6 +10800,7 @@ "OSL": "OSL AI", "OSMI": "OSMI", "OSMO": "Osmosis", + "OSOL": "OSOL", "OSQTH": "Opyn Squeeth", "OSS": "OSSChain", "OST": "OST", @@ -10543,6 +10919,7 @@ "PAPI": "Papi", "PAPO": "PAPO NINJA", "PAPPAY": "PAPPAY", + "PAPPLE": "Pineapple", "PAPU": "Papu Token", "PAPUSHA": "Papusha", "PAR": "Parachute", @@ -10571,6 +10948,7 @@ "PASG": "Passage", "PASL": "Pascal Lite", "PASS": "Blockpass", + "PASTERNAK": "Ben Pasternak", "PAT": "PATRON", "PATEK": "Silly Patek", "PATEX": "Patex", @@ -10672,6 +11050,7 @@ "PECL": "PECland", "PED": "PEDRO", "PEDRO": "Pedro The Raccoon", + "PEE": "peecoin", "PEEL": "Meta Apes", "PEENO": "Peeno", "PEEP": "Peepo", @@ -10704,6 +11083,7 @@ "PENGCOIN": "PENG", "PENGU": "Pudgy Penguins", "PENGUI": "Penguiana", + "PENGUIN": "Penguin", "PENGYX": "PengyX", "PENIS": "PenisGrow", "PENJ": "Penjamin Blinkerton", @@ -10755,6 +11135,7 @@ "PEPEMAGA": "Trump Pepe", "PEPEMO": "PepeMo", "PEPEMOON": "PEPEMOON", + "PEPEMUSK": "pepemusk", "PEPEOFSOL": "Pepe of Solana", "PEPEPI": "PEPEPi", "PEPER": "Baby Pepe", @@ -10836,6 +11217,7 @@ "PHAE": "Phaeton", "PHALA": "Phalanx", "PHAME": "PHAME", + "PHAR": "Pharaoh", "PHAUNTEM": "Phauntem", "PHB": "Phoenix Global [v2]", "PHBD": "Polygon HBD", @@ -10860,21 +11242,25 @@ "PHRYG": "PHRYGES", "PHRYGE": "PHRYGES", "PHRYGES": "The Phryges", + "PHRZ": "Pharaohs", "PHS": "PhilosophersStone", "PHT": "Photon Token", "PHTC": "Photochain", "PHTR": "Phuture", "PHUN": "PHUNWARE", "PHV": "PATHHIVE", - "PI": "Plian", + "PI": "Pi Network", "PIA": "Olympia AI", + "PIAI": "Pi Network AI", "PIAS": "PIAS", "PIB": "Pibble", - "PICA": "PicaArtMoney", + "PICA": "Picasso", + "PICAARTMONEY": "PicaArtMoney", "PICKL": "PICKLE", "PICKLE": "Pickle Finance", "PICO": "PicoGo", "PICOLO": "PICOLO", + "PIDOGE": "Pi Network Doge", "PIE": "Persistent Information Exchange", "PIERRE": "sacré bleu", "PIF": "Pepe Wif Hat", @@ -10893,12 +11279,15 @@ "PIKAM": "Pikamoon", "PIKE": "Pike Token", "PIKO": "Pinnako", + "PILLAR": "PillarFi", "PILOT": "Unipilot", "PIM": "PIM", - "PIN": "Pin", + "PIN": "PinLink", + "PINCHAIN": "Pin", "PINCHI": "Da Pinchi", "PINE": "Pine", "PINETWORKDEFI": "Pi Network DeFi", + "PINEYE": "PinEye", "PING": "CryptoPing", "PINK": "PINK - The Panther", "PINKCOIN": "PinkCoin", @@ -10929,6 +11318,7 @@ "PIST": "Pist Trust", "PIT": "Pitbull", "PITCH": "PITCH", + "PIUU": "PIXIU", "PIVN": "PIVN", "PIVX": "Private Instant Verified Transaction", "PIX": "Lampix", @@ -10987,6 +11377,7 @@ "PLG": "Pledgecamp", "PLGR": "Pledge Finance", "PLI": "Plugin", + "PLIAN": "Plian", "PLINK": "Chainlink (Polygon Portal)", "PLM": "Plasmonics", "PLMC": "Polimec", @@ -11076,6 +11467,7 @@ "POK": "Pokmonsters", "POKEGROK": "PokeGROK", "POKEM": "Pokemonio", + "POKEMO": "Pokemon", "POKEMON": "Pokemon", "POKER": "PokerCoin", "POKERFI": "PokerFi", @@ -11095,6 +11487,7 @@ "POLL": "Pollchain", "POLLUK": "Jasse Polluk", "POLLUX": "Pollux Coin", + "POLLY": "Polynetica", "POLNX": "eToro Polish Zloty", "POLO": "NftyPlay", "POLS": "Polkastarter", @@ -11141,6 +11534,7 @@ "POPE": "PopPepe", "POPECOIN": "Popecoin", "POPEPE": "POPEPE", + "POPG": "POPG", "POPGOAT": "Goatseus Poppimus", "POPK": "POPKON", "POPO": "popo", @@ -11172,6 +11566,8 @@ "POTATO": "Potato", "POTS": "Moonpot", "POTTER": "POTTER", + "POTUS": "President Trump", + "POTUS47": "Trump Coin", "POU": "Pou", "POUPE": "Poupe", "POUW": "Pouwifhat", @@ -11320,6 +11716,7 @@ "PTD": "Pilot", "PTERIA": "Pteria", "PTF": "PowerTrade Fuel", + "PTGC": "The Grays Currency", "PTH": "PlasticHero", "PTI": "Paytomat", "PTM": "Potentiam", @@ -11356,7 +11753,9 @@ "PUMP": "PUMP", "PUMPBTC": "pumpBTC", "PUMPFUNBAN": "Pump Fun Ban", + "PUMPIT": "BOGDANOFF", "PUMPTRUMP": "PUMP TRUMP", + "PUMPY": "WOW MOON LAMBO PUMPPPPPPY", "PUN": "Punkko", "PUNCH": "PUNCHWORD", "PUNDIX": "Pundi X", @@ -11378,7 +11777,9 @@ "PURA": "Pura", "PURE": "Puriever", "PUREALT": "Pure", + "PURP": "Purple Platform io", "PURPE": "Purple Pepe", + "PURPLEBTC": "Purple Bitcoin", "PURR": "Purr", "PURRC": "Purrcoin", "PURSE": "Pundi X PURSE", @@ -11391,6 +11792,7 @@ "PUSSY": "Pussy Financial", "PUSSYINBIO": "Pussy In Bio", "PUT": "PutinCoin", + "PUTIN": "Putin Meme", "PUUSH": "puush da button", "PUX": "pukkamex", "PVC": "PVC Meta", @@ -11400,8 +11802,10 @@ "PVU": "Plant vs Undead Token", "PWAR": "PolkaWar", "PWC": "PixelWorldCoin", + "PWEASE": "Pwease", "PWH": "pepewifhat", "PWINGS": "JetSwap pWings", + "PWOG": "Purple Fwog", "PWON": "Personal Wager", "PWR": "MaxxChain", "PWRC": "PWR Coin", @@ -11548,6 +11952,7 @@ "QWAN": "The QWAN", "QWARK": "Qwark", "QWC": "Qwertycoin", + "QWEN": "Qwen AI", "QWLA": "Qawalla", "QWT": "QoWatt", "QXC": "QuantumXC", @@ -11578,7 +11983,7 @@ "RAFT": "Raft", "RAGDOLL": "Ragdoll", "RAGE": "Rage Fan", - "RAI": "Rai Reflex Index", + "RAI": "Reploy", "RAID": "Raid Token", "RAIDER": "Crypto Raiders", "RAIF": "RAI Finance", @@ -11588,6 +11993,7 @@ "RAINC": "RainCheck", "RAINCO": "Rain Coin", "RAINI": "Rainicorn", + "RAIREFLEX": "Rai Reflex Index", "RAISE": "Raise Token", "RAIT": "Rabbitgame", "RAIZER": "RAIZER", @@ -11597,7 +12003,9 @@ "RALLY": "Trump Rally", "RAM": "Ramifi Protocol", "RAMA": "Ramestta", + "RAME": "Ramen", "RAMEN": "RamenSwap", + "RAMON": "Ramon", "RAMP": "RAMP", "RANKER": "RankerDao", "RAP": "Philosoraptor", @@ -11617,6 +12025,7 @@ "RAVELOUS": "Ravelous", "RAVEN": "Raven Protocol", "RAVENCOINC": "Ravencoin Classic", + "RAWDOG": "RawDog", "RAWG": "RAWG", "RAY": "Raydium", "RAYS": "Rays Network", @@ -11680,12 +12089,17 @@ "RDX": "Redux Protocol", "REA": "Realisto", "REACH": "/Reach", + "REACT": "Reactive Network", "REAL": "RealLink", + "REALESTATE": "RealEstate", "REALM": "Realm", "REALMS": "Realms of Ethernity", "REALP": "Real Pepe", "REALPLATFORM": "REAL", "REALTRACT": "RealTract", + "REALUSD": "Real USD", + "REALUSDV1": "Real USD v1", + "REALUSDV2": "Real USD v2", "REALY": "Realy Metaverse", "REAP": "ReapChain", "REAPER": "Grim Finance", @@ -11710,6 +12124,7 @@ "REDLC": "Redlight Chain", "REDLUNA": "Redluna", "REDN": "Reden", + "REDNOTE": "RedNote Xiaohongshu", "REDO": "Resistance Dog", "REDP": "Red Ponzi Gud", "REDPEPE": "Red Pepe", @@ -11725,7 +12140,9 @@ "REFLECTO": "Reflecto", "REFTOKEN": "RefToken", "REFUND": "Refund", + "REG": "RealToken Ecosystem Governance", "REGALCOIN": "Regalcoin", + "REGE": "Regent of the North Winds", "REGEN": "Regen Network", "REGENT": "REGENT COIN", "REGI": "Resistance Girl", @@ -11735,10 +12152,12 @@ "REIGN": "Reign of Terror", "REINDEER": "Reindeer", "REKT": "REKT", - "REKT2": "REKT 2.0", + "REKTV2": "REKT 2.0", + "REKTV3": "REKT v3 (rekt.game)", "REL": "Reliance", "RELAY": "Relay Token", "RELI": "Relite Finance", + "RELIGN": "RELIGN", "RELOADED": "Doge Reloaded", "RELVT": "Relevant", "REM": "REMME", @@ -11746,6 +12165,7 @@ "REME": "REME-Coin", "REMILIA": " Remilia", "REMIT": "BlockRemit", + "REMMETA": "Real Estate Metaverse", "REN": "REN", "RENA": "Warena", "RENBTC": "renBTC", @@ -11794,7 +12214,9 @@ "REVV": "REVV", "REW": "Review.Network", "REWARD": "Rewardable", + "REWARDS": "Solana Rewards", "REX": "Imbrex", + "REXBT": "rexbt by VIRTUALS", "REXHAT": "rexwifhat", "REZ": "Renzo", "RF": "Raido Financial", @@ -11831,17 +12253,20 @@ "RIC": "Riecoin", "RICE": "RiceFarm", "RICECOIN": "RiceCoin", - "RICH": "Richie", + "RICHIE": "Richie2.0", + "RICHIEV1": "Richie", "RICHOFME": "Rich Of Memes", "RICHR": "RichRabbit", "RICK": "Infinite Ricks", "RICKMORTY": "Rick And Morty", "RIDE": "Holoride", + "RIDECHAIN": "Ride Chain Coin", "RIDEMY": "Ride My Car", "RIF": "RIF Token", "RIF3": "MetaTariffv3", "RIFA": "Rifampicin", "RIFI": "Rikkei Finance", + "RIFT": "RIFT AI", "RIGEL": "Rigel Finance", "RIK": "RIKEZA", "RIL": "Rilcoin", @@ -11875,6 +12300,7 @@ "RITZ": "Ritz.Game", "RIVUS": "RivusDAO", "RIYA": "Etheriya", + "RIZ": "Rivalz Network", "RIZE": "Rizespor Token", "RIZO": "HahaYes", "RIZOLOL": "Rizo", @@ -11918,10 +12344,12 @@ "RNTB": "BitRent", "RNX": "ROONEX", "ROAD": "ROAD", + "ROAM": "Roam Token", "ROAR": "Alpha DEX", "ROARINGCAT": "Roaring Kitty", "ROB": "ROB", "ROBET": "RoBet", + "ROBI": "Robin Rug", "ROBIN": "Robin of Da Hood", "ROBINH": "ROBIN HOOD", "ROBO": "RoboHero", @@ -11964,6 +12392,9 @@ "ROOT": "The Root Network", "ROOTCOIN": "RootCoin", "ROOTS": "RootProject", + "ROP": "Redemption Of Pets", + "ROPE": "Rope Token", + "ROPELOL": "Rope", "ROPIRITO": "Ropirito", "ROS": "ROS Coin", "ROSA": "Rosa Inu", @@ -11975,6 +12406,7 @@ "ROSX": "Roseon", "ROT": "Rotten", "ROTTY": "ROTTYCOIN", + "ROUGE": "Rouge Studio", "ROUND": "RoundCoin", "ROUP": "Roup (Ordinals)", "ROUSH": "Roush Fenway Racing Fan Token", @@ -12101,6 +12533,7 @@ "RXD": "Radiant", "RXO": "RocketXRP Official", "RXT": "RIMAUNANGIS", + "RYAN": "OFFICIAL RYAN", "RYC": "RoyalCoin", "RYCN": "RoyalCoin 2.0", "RYD": "RYderOSHI", @@ -12112,6 +12545,7 @@ "RYU": "The Blue Dragon", "RYZ": "Anryze", "RZR": "RazorCoin", + "RZUSD": "RZUSD", "RedFlokiCEO": "Red Floki CEO", "S": "Sonic Labs", "S2K": "Sports 2K75", @@ -12119,6 +12553,7 @@ "S4F": "S4FE", "S8C": "S88 Coin", "SA": "Superalgos", + "SAAS": "SaaSGo", "SABAI": "Sabai Protocol", "SABLE": "Sable Finance", "SABR": "SABR Coin", @@ -12380,7 +12815,7 @@ "SEN": "Sentaro", "SENATE": "SENATE", "SENC": "Sentinel Chain", - "SEND": "Social Send", + "SEND": "Suilend", "SENDOR": "Sendor", "SENK": "Senk", "SENNO": "SENNO", @@ -12401,6 +12836,7 @@ "SEPA": "Secure Pad", "SEQ": "Sequence", "SER": "Secretum", + "SERAPH": "Seraph", "SERG": "Seiren Games Network", "SERO": "Super Zero", "SERP": "Shibarium Perpetuals", @@ -12477,6 +12913,7 @@ "SHARK": "Sharky", "SHARKC": "Shark Cat", "SHARKI": "Sharki", + "SHARKYSH": "Sharky Sharkx", "SHARP": "Sharp", "SHARPE": "Sharpe Capital", "SHAUN": "SHAUN INU", @@ -12484,6 +12921,7 @@ "SHC": "School Hack Coin", "SHD": "ShardingDAO", "SHDW": "Shadow Token", + "SHDX": "Shido DEX", "SHE": "Shine Chain", "SHEB": "SHEBOSHIS", "SHEEESH": "Secret Gem", @@ -12491,7 +12929,8 @@ "SHEESHA": "Sheesha Finance", "SHEGEN": "Aiwithdaddyissues", "SHEI": "SheikhSolana", - "SHELL": "Shell Token", + "SHELL": "MyShell", + "SHELLTOKEN": "Shell Token", "SHEN": "Shen", "SHEPE": "Shiba V Pepe", "SHERA": "Shera Tokens", @@ -12515,6 +12954,7 @@ "SHIBAMOM": "Shiba Mom", "SHIBAR": "Shibarium Name Service", "SHIBARMY": "Shib Army", + "SHIBAW": "Shiba $Wing", "SHIBAY": "Shiba Inu Pay", "SHIBAZILLA": "ShibaZilla2.0", "SHIBCAT": "SHIBCAT", @@ -12557,6 +12997,7 @@ "SHIP": "ShipChain", "SHIR": "SHIRO", "SHIRO": "Shiro Neko", + "SHIROSOL": "Shiro Neko (shirosol.online)", "SHIRYOINU": "Shiryo-Inu", "SHISHA": "Shisha Coin", "SHIT": "I will poop it NFT", @@ -12570,7 +13011,8 @@ "SHO": "Showcase Token", "SHOE": "ShoeFy", "SHOG": "SHOG", - "SHOGGOTH": "Shoggoth", + "SHOGGOTH": "Shoggoth (shoggoth.monster)", + "SHOGGOTHAI": "Shoggoth", "SHOKI": "Shoki", "SHON": "ShonToken", "SHOOT": "Mars Battle", @@ -12598,6 +13040,7 @@ "SHUFFLE": "SHUFFLE!", "SHVR": "Shivers", "SHX": "Stronghold Token", + "SHY": "Shytoshi Kusama", "SHYTCOIN": "ShytCoin", "SI": "Siren", "SIACLASSIC": "SiaClassic", @@ -12606,6 +13049,7 @@ "SIC": "Swisscoin", "SID": "Sid", "SIDE": "Side.xyz", + "SIDELINED": "Sidelined?", "SIDESHIFT": "SideShift Token", "SIDUS": "Sidus", "SIERRA": "Sierracoin", @@ -12631,6 +13075,7 @@ "SILVA": "Silva Token", "SILVER": "SILVER", "SILVERKRC": "Silver KRC-20", + "SILVERNOV": "Silvernova Token", "SILVERSTAND": "Silver Standard", "SILVERWAY": "Silverway", "SIM": "Simpson", @@ -12660,6 +13105,7 @@ "SIPHER": "Sipher", "SIPHON": "Siphon Life Spell", "SIR": "Sir", + "SIREN": "siren", "SIRIUS": "first reply", "SIS": "Symbiosis Finance", "SISA": "Strategic Investments in Significant Areas", @@ -12670,11 +13116,13 @@ "SIUUU": "Crustieno Renaldo", "SIV": "Sivasspor Token", "SIX": "SIX Network", + "SIXP": "Sixpack Miner", "SIXPACK": "SIXPACK", "SIZ": "Sizlux", "SIZE": "SIZE", "SJCX": "StorjCoin", "SKAI": "Skillful AI", + "SKAIN": "SKAINET", "SKB": "SkullBuzz", "SKBDI": "Skibidi Toilet", "SKC": "Skeincoin", @@ -12692,7 +13140,9 @@ "SKIN": "Skincoin", "SKING": "Solo King", "SKINS": "Coins & Skins", + "SKINUT": "Skimask Pnut", "SKIPUP": "SKI MASK PUP", + "SKITTEN": "Ski Mask Kitten", "SKL": "SKALE Network", "SKLAY": "sKLAY", "SKM": "Skrumble Network", @@ -12721,7 +13171,7 @@ "SLAP": "CatSlap", "SLAVI": "Slavi Coin", "SLB": "Solberg", - "SLC": "Solice", + "SLC": "Silencio", "SLCL": "Solcial", "SLEEP": "Sleep Ecosystem", "SLEEPEE": "SleepFuture", @@ -12755,7 +13205,7 @@ "SLRS": "Solrise Finance", "SLS": "SaluS", "SLST": "SmartLands", - "SLT": "Social Lending Network", + "SLT": "Salute", "SLUGDENG": "SLUG DENG", "SLUMBO": "SLUMBO", "SLVX": "eToro Silver", @@ -12828,7 +13278,9 @@ "SNAI": "SwarmNode.ai", "SNAIL": "SnailBrook", "SNAKE": "snake", + "SNAKEMOON": "Snakemoon", "SNAKES": "Snakes Game", + "SNAKT": "Sna-King Trump", "SNAP": "SnapEx", "SNAPCAT": "Snapcat", "SNB": "SynchroBitcoin", @@ -12855,6 +13307,7 @@ "SNM": "SONM", "SNMT": "Satoshi Nakamoto Token", "SNN": "SeChain", + "SNO": "Snow Leopard", "SNOB": "Snowball", "SNOLEX": "Snolex", "SNOOP": "SnoopDAO", @@ -12892,6 +13345,8 @@ "SOCC": "SocialCoin", "SOCCER": "SoccerInu", "SOCIAL": "Phavercoin", + "SOCIALLT": "Social Lending Network", + "SOCIALSEND": "Social Send", "SOCKS": "Unisocks", "SOCOLA": "SOCOLA INU", "SODA": "SODA Coin", @@ -12917,6 +13372,7 @@ "SOLAN": "Solana Beach", "SOLANAP": "Solana Poker", "SOLANAS": "Solana Swap", + "SOLANATREASURY": "Solana Treasury Machine", "SOLAPE": "SolAPE Token", "SOLAR": "Solar", "SOLARA": "Solara", @@ -12938,8 +13394,10 @@ "SOLETF": "SOL ETF", "SOLEX": "Solex Launchpad", "SOLFI": "SoliDefi", + "SOLFUN": "SolFun", "SOLGOAT": "SOLGOAT", "SOLGUN": "Solgun", + "SOLIC": "Solice", "SOLID": "Solidified", "SOLIDSEX": "SOLIDsex: Tokenized veSOLID", "SOLITO": "SOLITO", @@ -12967,6 +13425,8 @@ "SOLVBTC": "Solv Protocol SolvBTC", "SOLVBTCBBN": "Solv Protocol SolvBTC.BBN", "SOLVBTCCORE": "Solv Protocol SolvBTC.CORE", + "SOLVBTCENA": "SolvBTC Ethena", + "SOLVBTCJUP": "SolvBTC Jupiter", "SOLVE": "SOLVE", "SOLWIF": "Solwif", "SOLX": "SolarX", @@ -12977,14 +13437,13 @@ "SOM": "Souls of Meta", "SOMA": "Soma", "SOMM": "Sommelier", - "SOMNIUM": "Somnium Space CUBEs", "SOMPS": "SompsOnKas", "SON": "Simone", "SONAR": "SonarWatch", "SONG": "Song Coin", "SONGOKU": "SONGOKU", - "SONIC": "Sonic", "SONICO": "Sonic", + "SONICSONIC": "Sonic", "SONICWIF": "SonicWifHat", "SONNE": "Sonne Finance", "SONOF": "Son of Solana", @@ -13002,6 +13461,7 @@ "SORAI": "Sora AI", "SORAPORN": "Sora Porn", "SOSNOVKINO": "Sosnovkino", + "SOSO": "SoSoValue", "SOSWAP": "Solana Swap", "SOT": "Soccer Crypto", "SOTA": "SOTA Finance", @@ -13025,6 +13485,7 @@ "SPACE": "Spacelens", "SPACECOIN": "SpaceCoin", "SPACED": "SPACE DRAGON", + "SPACEM": "Spacem Token", "SPACEPI": "SpacePi", "SPAD": "SolPad", "SPAI": "Starship AI", @@ -13111,6 +13572,7 @@ "SPRING": "Spring", "SPRITZMOON": "SpritzMoon Crypto Token", "SPRKL": "Sparkle Loyalty", + "SPROUT": "Sprout", "SPRT": "Sportium", "SPRTS": "Sprouts", "SPRTZ": "SpritzCoin", @@ -13173,6 +13635,7 @@ "SSD": "Sonic Screw Driver Coin", "SSDX": "SpunkySDX", "SSE": "Soroosh Smart Ecosystem", + "SSEV1": "Soroosh Smart Ecosystem v1", "SSG": "Surviving Soldiers", "SSGT": "Safeswap", "SSH": "StreamSpace", @@ -13181,6 +13644,7 @@ "SSLX": "StarSlax", "SSNC": "SatoshiSync", "SSOL": "Solayer SOL", + "SSR": "SOL Strategic Reserve", "SSS": "StarSharks", "SSSSS": "Snake wif Hat", "SST": "SIMBA Storage Token", @@ -13209,10 +13673,12 @@ "STANDARD": "Stakeborg DAO", "STAPT": "Ditto Staked Aptos", "STAR": "FileStar", + "STAR10": "Ronaldinho Coin", "STARAMBA": "Staramba", "STARBASE": "Starbase", "STARC": "StarChain", "STARDOGE": "StarDOGE", + "STARGATEAI": "Stargate AI Agent", "STARL": "StarLink", "STARLAUNCH": "StarLaunch", "STARLY": "Starly", @@ -13280,6 +13746,7 @@ "STIMA": "STIMA", "STING": "Sting", "STINJ": "Stride Staked INJ", + "STITCH": "Stitch", "STIX": "STIX", "STJUNO": "Stride Staked JUNO", "STK": "STK Token", @@ -13301,6 +13768,7 @@ "STNK": "Stonks", "STO": "Save The Ocean", "STOC": "STO Cash", + "STOCK": "Digital Asset Stockpile", "STOG": "Stooges", "STOGE": "Stoner Doge Finance", "STOIC": "stoicDAO", @@ -13363,6 +13831,7 @@ "STUDENTC": "Student Coin", "STUFF": "STUFF.io", "STUMEE": "Stride Staked UMEE", + "STUPID": "StupidCoin", "STUSDT": "Staked USDT", "STV": "Sativa Coin", "STWEMIX": "Staked WEMIX", @@ -13374,11 +13843,13 @@ "STZETA": "ZetaEarn", "STZU": "Shihtzu Exchange Token", "SU": "Smol Su", + "SUAI": "SuiAI", "SUB": "Subsocial", "SUBAWU": "Subawu Token", "SUBF": "Super Best Friends", "SUBS": "Substratum Network", "SUCR": "Sucre", + "SUD": "Sudo Labs", "SUDO": "sudoswap", "SUGAR": "Sugar Exchange", "SUI": "Sui", @@ -13391,6 +13862,7 @@ "SUIMAN": "Suiman", "SUIMON": "Sui Monster", "SUIP": "SuiPad", + "SUIRWA": "Sui RWA", "SUISHIB": "SuiShiba", "SUITE": "Suite", "SUKI": "SUKI", @@ -13470,7 +13942,8 @@ "SWAGT": "Swag Token", "SWAI": "Safe Water AI", "SWAMP": "Swampy", - "SWAN": "Black Swan", + "SWAN": "Swan Chain", + "SWANSOL": "Black Swan", "SWAP": "Trustswap", "SWAPP": "SWAPP Protocol", "SWAPZ": "SWAPZ.app", @@ -13516,6 +13989,7 @@ "SWPR": "Swapr", "SWPRS": "Maid Sweepers", "SWRV": "Swerve", + "SWRX": "SwissRx Coin", "SWT": "Swarm City Token", "SWTH": "Carbon", "SWTS": "SWEETS", @@ -13540,6 +14014,7 @@ "SYLO": "Sylo", "SYLV": "Sylvester", "SYM": "SymVerse", + "SYMM": "Symmio", "SYMP": "Sympson AI", "SYN": "Synapse", "SYNC": "Syncus", @@ -13601,6 +14076,7 @@ "TANUKI": "Tanuki", "TANUPAD": "Tanuki Launchpad", "TAO": "Bittensor", + "TAOBOT": "tao.bot", "TAONU": "TAO INU", "TAOP": "TaoPad", "TAOTOOLS": "TAOTools", @@ -13608,6 +14084,7 @@ "TAPC": "Tap Coin", "TAPPINGCOIN": "TappingCoin", "TAPROOT": "Taproot Exchange", + "TAPS": "TapSwap", "TAPT": "Tortuga Staked Aptos", "TARA": "Taraxa", "TARAL": "TARALITY", @@ -13631,6 +14108,7 @@ "TAUR": "Marnotaur", "TAVA": "ALTAVA", "TAX": "MetaToll", + "TAXAD": "TAXAD", "TAXI": "Robotaxi", "TAXLESSTRUMP": "MAGA TAXLESS", "TBAC": "BlockAura", @@ -13647,6 +14125,7 @@ "TBFT": "Türkiye Basketbol Federasyon Token", "TBIS": "TBIS token", "TBL": "Tombola", + "TBR": "Tuebor", "TBRIDGE": "tBridge Token", "TBT": "T-BOT", "TBTC": "tBTC", @@ -13691,6 +14170,7 @@ "TEC": "TeCoin", "TECAR": "Tesla Cars", "TECH": "TechCoin", + "TECK": "Technet", "TECRA": "TecraCoin", "TED": "TED", "TEDDY": "Teddy Doge v2", @@ -13702,6 +14182,7 @@ "TEL": "Telcoin", "TELE": "Miracle Tele", "TELEBTC": "teleBTC", + "TELEPORT": "Teleport System Token", "TELL": "Tellurion", "TELLER": "Teller", "TELO": "Telo Meme Coin", @@ -13749,6 +14230,7 @@ "TETU": "TETU", "TEVA": "Tevaera", "TEW": "Trump in a memes world", + "TEX": "Terrax", "TF47": "Trump Force 47", "TFBX": "Truefeedback Token", "TFC": "The Freedom Coin", @@ -13772,6 +14254,7 @@ "THAVAGE": "Mike Tython", "THC": "The Hempcoin", "THD": "Trump Harris Debate", + "THE369": "The 369 code", "THE9": "THE9", "THEAICOIN": "AI", "THEB": "The Boys Club", @@ -13781,7 +14264,9 @@ "THECAT": "THECAT", "THECITADEL": "The Citadel", "THEDAO": "The DAO", + "THEDOGE": "The Dogefather", "THEF": "The Flash Currency", + "THEFACE": "FACE", "THEG": "The GameHub", "THEHARAMBE": "Harambe", "THEM": "The Meta DAO", @@ -13825,6 +14310,7 @@ "TIA": "Celestia", "TIANHE": "Tianhe", "TIC": "TrueInvestmentCoin", + "TICO": "Tico", "TIDAL": "Tidal Finance", "TIDDIES": "TIDDIES", "TIDE": "Tidalflats", @@ -13841,14 +14327,17 @@ "TIIM": "TriipMiles", "TIK": "ChronoBase", "TIKI": "Tiki Token", + "TIKTOK": "Tiktok", "TIKTOKEN": "TikToken", "TIM": "TIMTIM GAMES", "TIME": "Chrono.tech", + "TIMEFUN": "timefun", "TIMES": "DARKTIMES", "TIMI": "This Is My Iguana", "TIMICOIN": "Timicoin", "TIN": "Token IN", "TINC": "Tiny Coin", + "TIND": "Tinder Swindler", "TINKU": "TinkuCoin", "TINU": "Telegram Inu", "TINY": "TinyBits", @@ -13985,6 +14474,7 @@ "TORII": "Torii Finance", "TORN": "Tornado Cash", "TORO": "Toro Inoue", + "TOROSOL": "Toro", "TORSY": "TORSY", "TOS": "ThingsOperatingSystem", "TOSA": "TosaInu BSC", @@ -14032,6 +14522,7 @@ "TRADEBOT": "TradeBot", "TRADECHAIN": "Trade Chain", "TRADEX": "TradeX AI", + "TRAI": "Trackgood AI", "TRAID": "Traid", "TRAIMP": "TRUMP AI", "TRAIN": "Trump Train", @@ -14056,6 +14547,7 @@ "TRDS": "Traders Token", "TRDT": "Trident", "TREAT": "Treat", + "TREB": "Treble", "TRECENTO": "Trecento Blockchain Capital", "TREE": "Tree", "TREEB": "Retreeb", @@ -14124,6 +14616,7 @@ "TRUMP3": "Trump MP3", "TRUMP47": "47th President of the United States", "TRUMPA": "TRUMP AI", + "TRUMPAI": "Trump Maga AI", "TRUMPAMANIA": "TRUMPAMANIA", "TRUMPARMY": "Trump Army", "TRUMPBASE": "MAGA (magatrumponbase.tech)", @@ -14131,11 +14624,13 @@ "TRUMPC": "TrumpCat", "TRUMPCA": "Trump Card", "TRUMPCAT": "TRUMPCAT", + "TRUMPCATF": "Trump Cat Family", "TRUMPCATS": "Trump Golden Cat", "TRUMPCOIN": "TrumpCoin", "TRUMPDAO": "TRUMP DAO", "TRUMPDO": "TRUMP", "TRUMPDOGE": "Trump Doge", + "TRUMPDOGECOIN": "DOGE", "TRUMPE": "Trump Pepe", "TRUMPEPE": "Trump Pepe", "TRUMPER": "Trump Era", @@ -14149,11 +14644,13 @@ "TRUMPMA": "TRUMP MAGA SUPER", "TRUMPMAGA": "President Trump MAGA", "TRUMPONBASE": "TRUMP ON BASE", + "TRUMPPROJECT": "Trump Project 2025", "TRUMPS": "Trump SOL", "TRUMPSB": "TrumpsBags", "TRUMPSFIGHT": "TrumpsFight", "TRUMPSHIBA": "Trump Shiba", "TRUMPTECH": "Trump Tech", + "TRUMPTESLA": "Trump Tesla", "TRUMPTITANS": "TrumpTitans", "TRUMPVANCE": "Trump Vance 2024", "TRUMPX": "Trump X-Maga", @@ -14161,6 +14658,7 @@ "TRUNK": "Elephant Money", "TRUST": "TrustDAO", "TRUSTNFT": "TrustNFT", + "TRUT": "Truth", "TRUTH": "TruthGPT", "TRUTHFI": "Truthfi", "TRV": "TrustVerse", @@ -14190,7 +14688,9 @@ "TSLT": "Tamkin", "TSN": "Tsunami Exchange Token", "TSR": "Tesra", - "TST": "Teleport System Token", + "TST": "Test", + "TSTAI": "Test AI", + "TSTS": "Test", "TSUBASAUT": "TSUBASA Utility Token", "TSUGT": "Captain Tsubasa", "TSUJI": "Tsutsuji", @@ -14258,6 +14758,7 @@ "TX20": "Trex20", "TXA": "TXA", "TXAG": "tSILVER", + "TXAI": "TrumpX Ai", "TXAU": "tGOLD", "TXBIT": "Txbit Token", "TXC": "TEXITcoin", @@ -14289,7 +14790,8 @@ "UAT": "UltrAlpha", "UB": "UBit Token", "UBA": "Unbox.Art", - "UBC": "Ubcoin", + "UBC": "Universal Basic Compute", + "UBCOIN": "Ubcoin", "UBDN": "UBD Network", "UBEX": "Ubex", "UBI": "Universal Basic Income", @@ -14407,17 +14909,23 @@ "UNIDXV1": "UniDex v1", "UNIE": "Uniswap Protocol Token (Avalanche Bridge)", "UNIETH": "Universal ETH", + "UNIFI": "Unifi", "UNIFY": "Unify", + "UNIL": "UniLayer", "UNIM": "Unicorn Milk", "UNIO": "Unio Coin", "UNIQ": "Uniqredit", "UNIQUE": "Unique One", + "UNISD": "unified Stable Dollar", + "UNISDV1": "uniswap State Dollar", "UNISTAKE": "Unistake", "UNIT": "Universal Currency", "UNIT0": "UNIT0", "UNITARYSTATUS": "UnitaryStatus Dollar", "UNITED": "UnitedCoins", "UNITRADE": "UniTrade", + "UNITREEAI": "Unitree G1 AI", + "UNITREEDOG": "Unitree AI Robot Dog", "UNITS": "GameUnits", "UNITY": "SuperNET", "UNIVRS": "Universe", @@ -14493,6 +15001,7 @@ "USDCPO": "USD Coin (PoS) (Portal from Polygon)", "USDCSO": "USD Coin (Portal from Solana)", "USDD": "USDD", + "USDDV1": "USDD v1", "USDE": "Ethena USDe", "USDEBT": "USDEBT", "USDEX": "eToro US Dollar", @@ -14505,21 +15014,24 @@ "USDL": "Lift Dollar", "USDM": "Mountain Protocol", "USDMA": "USD mars", - "USDN": "Neutrino USD", + "USDN": "Neutral AI", "USDO": "USD Open Dollar", "USDP": "Pax Dollar", "USDPLUS": "Overnight.fi USD+", - "USDQ": "USDQ", - "USDR": "Real USD", + "USDQ": "Quantoz USDQ", + "USDQSTABLE": "USDQ", + "USDR": "StablR USD", "USDS": "Sky Dollar", "USDSB": "USDSB", "USDSTABLY": "StableUSD", "USDT": "Tether", + "USDT0": "USDT0", "USDTBASE": "USDT (Base)", "USDTV": "TetherTV", "USDTZ": "USDtez", "USDU": "Upper Dollar", "USDV": "Verified USD", + "USDW": "USD DWIN", "USDX": "USDX Stablecoin", "USDY": "Ondo US Dollar Yield", "USDZ": "Zedxion USDZ", @@ -14545,6 +15057,7 @@ "USTBL": "Spiko US T-Bills Money Market Fund", "USTC": "TerraClassicUSD", "USTCW": "TerraClassicUSD Wormhole", + "USTREAM": "Ustream Coin", "USTX": "UpStableToken", "USUAL": "Usual", "USUALX": "USUALx", @@ -14556,6 +15069,7 @@ "UTC": "UltraCoin", "UTG": "UltronGlow", "UTH": "Uther", + "UTHR": "Utherverse Xaeon", "UTHX": "Utherverse", "UTI": "Unicorn Technology International", "UTIL": "Utility Coin", @@ -14567,7 +15081,9 @@ "UTT": "United Traders Token", "UTU": "UTU Protocol", "UTX": "UTIX", + "UTYA": "Utya", "UTYAB": "Utya Black", + "UUC": "USA Unity Coin", "UUSD": "Utopia USD", "UUU": "U Network", "UVT": "UvToken", @@ -14600,6 +15116,7 @@ "VANA": "Vana", "VANCAT": "Vancat", "VANCE": "JD Vance", + "VANCEMEME": "Vance Meme", "VANF": "Van Fwogh", "VANRY": "Vanar Chain", "VANT": "Vanta Network", @@ -14609,6 +15126,7 @@ "VARA": "Vara Network", "VARIUS": "Varius", "VARK": "Aardvark", + "VATAN": "Vatan Token", "VATO": "vanitis", "VATR": "Vatra INU", "VATRENI": "Croatian FF Fan Token", @@ -14649,6 +15167,7 @@ "VEC": "VECTOR", "VEC2": "VectorCoin 2.0", "VECT": "Vectorium", + "VECTOR": "VectorChat.ai", "VEE": "BLOCKv", "VEED": "VEED", "VEEN": "LIVEEN", @@ -14707,13 +15226,15 @@ "VGX": "Voyager Token", "VHC": "Vault Hill City", "VI": "Vid", - "VIA": "ViaCoin", + "VIA": "Octavia AI", + "VIAC": "ViaCoin", "VIB": "Viberate", "VIBE": "VIBEHub", "VIBEA": "Vibe AI", "VIBLO": "VIBLO", "VIC": "Viction", "VICA": "ViCA Token", + "VICE": "VICE Token", "VICEX": "ViceToken", "VICS": "RoboF", "VICT": "Victory Impact Coin", @@ -14722,6 +15243,7 @@ "VIDA": "Vidiachange", "VIDEO": "Videocoin by Drakula", "VIDT": "VIDT Datalink", + "VIDTV1": "VIDT Datalink", "VIDY": "Vidy", "VIDYA": "Vidya", "VIDYX": "VidyX", @@ -14731,9 +15253,11 @@ "VIK": "VIKTAMA", "VIKITA": "VIKITA", "VIKKY": "VikkyToken", + "VILADY": "Vitalik Milady", "VIM": "VicMove", "VIN": "VinChain", "VINCI": "VINCI", + "VINE": "Vine Coin", "VINU": "Vita Inu", "VIOR": "ViorCoin", "VIP": "VIP Tokens", @@ -14757,6 +15281,7 @@ "VITAFAST": "Molecules of Korolchuk IP-NFT", "VITAL": "Vital Network", "VITALI": "Vitalik's Casper", + "VITALIK": "OFFICIAL VITALIK", "VITAMINS": "Vitamins", "VITARNA": "VitaRNA", "VITE": "VITE", @@ -14809,6 +15334,7 @@ "VOCO": "Provoco", "VODCAT": "VODKA CAT", "VODKA": "Vodka Token", + "VOID": "Nothing", "VOIP": "Voip Finance", "VOISE": "Voise", "VOL": "Volume Network", @@ -14820,6 +15346,7 @@ "VOLTOLD": "Volt Inu (Old)", "VOLTV1": "Volt Inu v1", "VOLTV2": "Volt Inu v2", + "VOLTX": "VolatilityX", "VOLTZ": "Voltz", "VOLX": "VolumeX", "VONE": "Vone", @@ -14900,6 +15427,7 @@ "VVAIFU": "Dasha", "VVI": "VV Coin", "VVS": "VVS Finance", + "VVV": "Venice Token", "VX": "ViteX Coin", "VXL": "Voxel X Network", "VXR": "Vox Royale", @@ -14911,6 +15439,8 @@ "VYFI": "VyFinance", "VYNC": "VYNK Chain", "VYPER": "VYPER.WIN", + "VYVO": "Vyvo AI", + "VZ": "Vault Zero", "VZT": "Vezt", "W": "Wormhole", "W1": "W1", @@ -14936,6 +15466,7 @@ "WAGG": "Waggle Network", "WAGIE": "Wagie", "WAGIEBOT": "Wagie Bot", + "WAGM": "WAGMI", "WAGMI": "Wagmi Coin", "WAGMIGAMES": "WAGMI Game", "WAGMIT": "Wagmi", @@ -14990,11 +15521,13 @@ "WAXS": "Axie Infinity Shards (Wormhole)", "WAY": "WayCoin", "WAZ": "MikeAI", + "WBAI": "Wrapped Balance AI", "WBAN": "Wrapped Banano", "WBB": "Wild Beast Coin", "WBBC": "Wibcoin", "WBC": "WorldBrain Coin", "WBCH": "Wrapped Bitcoin Cash", + "WBERA": "Wrapped Bera", "WBESC": "Wrapped BESC", "WBET": "Wavesbet", "WBETH": "Wrapped Beacon ETH", @@ -15081,6 +15614,7 @@ "WENLAMBO": "Wenlambo", "WEOS": "Wrapped EOS", "WEPC": "World Earn & Play Community", + "WEPE": "Wall Street Pepe", "WERK": "Werk Family", "WEST": "Waves Enterprise", "WET": "WeShow Token", @@ -15136,6 +15670,7 @@ "WHITE": "Whiteheart", "WHL": "WhaleCoin", "WHO": "Truwho", + "WHOLE": "Whole Network", "WHOREN": "elizabath whoren", "WHT": "Wrapped Huobi Token", "WHTETGRMOON": "WHITE TIGER MOON", @@ -15183,7 +15718,9 @@ "WINT": "WinToken", "WINTER": "Winter", "WINU": "Walter Inu", + "WINX": "WinX.io", "WIOTX": "Wrapped IoTeX", + "WIRE": "717ai by Virtuals", "WIRTUAL": "Wirtual", "WIS": "Experty Wisdom Token", "WISC": "WisdomCoin", @@ -15205,6 +15742,7 @@ "WKAVA": "Wrapped Kava", "WKC": "Wiki Cat", "WKD": "Wakanda Inu", + "WKEYDAO": "WebKey DAO", "WLD": "Worldcoin", "WLF": "Wolfs Group", "WLFI": "World Liberty Financial", @@ -15212,13 +15750,15 @@ "WLK": "Wolk", "WLKN": "Walken", "WLO": "WOLLO", + "WLSC": "WESTLAND SMART CITY", "WLTH": "Common Wealth", "WLUNA": "Wrapped LUNA Token", "WLUNC": "Wrapped LUNA Classic", "WLXT": "Wallex Token", "WMATIC": "Wrapped Matic", "WMB": "WatermelonBlock", - "WMC": "WMCoin", + "WMC": "Wrapped MistCoin", + "WMCOIN": "WMCoin", "WMEMO": "Wonderful Memories", "WMETIS": "Wrapped Metis", "WMF": "Whale Maker Fund", @@ -15257,9 +15797,10 @@ "WOJAK2": "Wojak 2.0 Coin", "WOJAKC": "Wojak Coin", "WOKB": "Wrapped OKB", + "WOKIE": "Wokie Plumpkin by Virtuals", "WOKT": "Wrapped OKT", "WOL": "World of Legends", - "WOLF": "LANDWOLF (AVAX)", + "WOLF": "Landwolf 0x67", "WOLFILAND": "Wolfiland", "WOLFOF": "Wolf of Wall Street", "WOLFP": "Wolfpack Coin", @@ -15312,6 +15853,7 @@ "WPR": "WePower", "WQT": "Work Quest", "WRC": "Worldcore", + "WREACT": "Wrapped REACT", "WRK": "BlockWRK", "WRKX": "NFT Workx", "WRLD": "NFT Worlds", @@ -15413,6 +15955,7 @@ "XAGX": "Silver Token", "XAH": "Xahau", "XAI": "Xai", + "XAIGAME": "xAI Game Studio", "XALGO": "Wrapped ALGO", "XALPHA": "XAlpha AI", "XAMP": "Antiample", @@ -15543,6 +16086,7 @@ "XHP": "XHYPE", "XHPV1": "XHYPE v1", "XHT": "HollaEx", + "XHUNT": "CryptoHunter World", "XHV": "Haven Protocol", "XI": "Xi", "XIASI": "Xiasi Inu", @@ -15617,6 +16161,7 @@ "XOV": "XOVBank", "XOX": "XOX Labs", "XOXNO": "XOXNO", + "XOXO": "XO Protocol", "XP": "Experience Points", "XPA": "XPA", "XPAT": "Bitnation Pangea", @@ -15628,6 +16173,7 @@ "XPET": "XPET token", "XPH": "PharmaCoin", "XPHX": "PhoenixCo Token", + "XPI": "XPi", "XPL": "Exclusive Platform", "XPLA": "XPLA", "XPLL": "ParallelChain", @@ -15708,6 +16254,7 @@ "XTECH": "X-TECH", "XTK": "xToken", "XTM": "TORUM", + "XTN": "Neutrino Index Token", "XTO": "Tao", "XTP": "Tap", "XTR": "Xtreme", @@ -15784,14 +16331,18 @@ "YDF": "Yieldification", "YDOGE": "Yorkie Doge", "YDR": "YDragon", + "YE": "Kanye West", + "YEAI": "YE AI Agent", "YEARN": "YearnTogether", "YEC": "Ycash", "YEE": "Yeeco", "YEED": "Yggdrash", "YEEHAW": "YEEHAW", + "YEET": "Yeet", "YEETI": "YEETI 液体", "YEFI": "YeFi", "YEL": "Yel.Finance", + "YELP": "Yelpro", "YEON": "Yeon", "YEPE": "Yellow Pepe", "YES": "YES Money", @@ -15799,6 +16350,7 @@ "YESP": "Yesports", "YESW": "Yes World", "YETI": "Yeti Finance", + "YETIUSD": "YUSD Stablecoin", "YETU": "Yetucoin", "YFARM": "YFARM Token", "YFBETA": "yfBeta", @@ -15842,6 +16394,7 @@ "YOC": "YoCoin", "YOCO": "YocoinYOCO", "YOD": "Year of the Dragon", + "YODA": "YODA", "YODE": "YodeSwap", "YOLO": "YoloNolo", "YOM": "YOM", @@ -15868,6 +16421,7 @@ "YSEC": "Yearn Secure", "YSR": "Ystar", "YTA": "YottaChain", + "YTC": "Yachtscoin", "YTJIA": "Jia Yueting", "YTN": "YENTEN", "YTS": "YetiSwap", @@ -15882,12 +16436,13 @@ "YUKIE": "Yukie", "YUKKY": "YUKKY", "YUKO": "YUKO", + "YULI": "Yuliverse", "YUM": "Yumerium", "YUMMI": "Yummi Universe", "YUMMY": "Yummy", "YUP": "Crowdholding", "YURI": "YURI", - "YUSD": "YUSD Stablecoin", + "YUSD": "YieldFi yToken", "YUSE": "Yuse Token", "YUSRA": "YUSRA", "YUSUF": "Yusuf Dikec Meme", @@ -15899,6 +16454,7 @@ "YYE": "YYE Energy", "YYFI": "YYFI.Protocol", "YYOLO": "yYOLO", + "YZY": "Yeezy", "Z3": "Z-Cubed", "ZABAKU": "Zabaku Inu", "ZACK": "Zack Morris", @@ -15924,6 +16480,7 @@ "ZBCN": "Zebec Network", "ZBIT": "zbit", "ZBU": "Zeebu", + "ZBUV1": "ZEEBU v1", "ZCC": "ZCC Coin", "ZCC1": "ZeroCarbon", "ZCD": "ZChains", @@ -15997,6 +16554,7 @@ "ZGEM": "GemSwap", "ZHC": "ZHC : Zero Hour Cash", "ZHOA": "Chengpang Zhoa", + "ZHOUKING": "ZhouKing", "ZIBU": "Zibu", "ZIG": "Zignaly", "ZIGAP": "ZIGAP", From 6036547cf54fd3e954ddc8e79256a189a8f8e9e7 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 17 Mar 2025 21:21:09 +0100 Subject: [PATCH 14/56] Feature/refactor portfolio calculator factory (#4454) * Refactor portfolio calculator factory --- .../calculator/mwr/portfolio-calculator.ts | 2 +- .../portfolio-calculator.factory.ts | 21 +++++++++++++--- .../calculator/twr/portfolio-calculator.ts | 24 +++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts diff --git a/apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts index e54f63422..fab15e6e7 100644 --- a/apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts @@ -5,7 +5,7 @@ import { } from '@ghostfolio/common/interfaces'; import { PortfolioSnapshot } from '@ghostfolio/common/models'; -export class MWRPortfolioCalculator extends PortfolioCalculator { +export class MwrPortfolioCalculator extends PortfolioCalculator { protected calculateOverallPerformance(): PortfolioSnapshot { throw new Error('Method not implemented.'); } diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts index 70f27fe7b..6cc5edeaf 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts @@ -8,13 +8,15 @@ import { Filter, HistoricalDataItem } from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; -import { MWRPortfolioCalculator } from './mwr/portfolio-calculator'; +import { MwrPortfolioCalculator } from './mwr/portfolio-calculator'; import { PortfolioCalculator } from './portfolio-calculator'; import { RoaiPortfolioCalculator } from './roai/portfolio-calculator'; +import { TwrPortfolioCalculator } from './twr/portfolio-calculator'; export enum PerformanceCalculationType { MWR = 'MWR', // Money-Weighted Rate of Return - ROAI = 'ROAI' // Return on Average Investment + ROAI = 'ROAI', // Return on Average Investment + TWR = 'TWR' // Time-Weighted Rate of Return } @Injectable() @@ -44,7 +46,7 @@ export class PortfolioCalculatorFactory { }): PortfolioCalculator { switch (calculationType) { case PerformanceCalculationType.MWR: - return new MWRPortfolioCalculator({ + return new MwrPortfolioCalculator({ accountBalanceItems, activities, currency, @@ -61,10 +63,23 @@ export class PortfolioCalculatorFactory { accountBalanceItems, activities, currency, + filters, + userId, + configurationService: this.configurationService, currentRateService: this.currentRateService, + exchangeRateDataService: this.exchangeRateDataService, + portfolioSnapshotService: this.portfolioSnapshotService, + redisCacheService: this.redisCacheService + }); + case PerformanceCalculationType.TWR: + return new TwrPortfolioCalculator({ + accountBalanceItems, + activities, + currency, filters, userId, configurationService: this.configurationService, + currentRateService: this.currentRateService, exchangeRateDataService: this.exchangeRateDataService, portfolioSnapshotService: this.portfolioSnapshotService, redisCacheService: this.redisCacheService diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts new file mode 100644 index 000000000..6499ca3db --- /dev/null +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts @@ -0,0 +1,24 @@ +import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; +import { + AssetProfileIdentifier, + SymbolMetrics +} from '@ghostfolio/common/interfaces'; +import { PortfolioSnapshot } from '@ghostfolio/common/models'; + +export class TwrPortfolioCalculator extends PortfolioCalculator { + protected calculateOverallPerformance(): PortfolioSnapshot { + throw new Error('Method not implemented.'); + } + + protected getSymbolMetrics({}: { + end: Date; + exchangeRates: { [dateString: string]: number }; + marketSymbolMap: { + [date: string]: { [symbol: string]: Big }; + }; + start: Date; + step?: number; + } & AssetProfileIdentifier): SymbolMetrics { + throw new Error('Method not implemented.'); + } +} From 51d55f74e922a8e294f56d202b381fb4c30403cc Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 17 Mar 2025 21:21:46 +0100 Subject: [PATCH 15/56] Feature/add missing lifecycle hook in historical market data editor dialog (#4456) * Add OnInit --- .../historical-market-data-editor-dialog.component.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 73f382c5e..c75b6f3ae 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 @@ -7,7 +7,8 @@ import { Component, CUSTOM_ELEMENTS_SCHEMA, Inject, - OnDestroy + OnDestroy, + OnInit } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; @@ -41,7 +42,9 @@ import { HistoricalMarketDataEditorDialogParams } from './interfaces/interfaces' styleUrls: ['./historical-market-data-editor-dialog.scss'], templateUrl: 'historical-market-data-editor-dialog.html' }) -export class GfHistoricalMarketDataEditorDialogComponent implements OnDestroy { +export class GfHistoricalMarketDataEditorDialogComponent + implements OnDestroy, OnInit +{ private unsubscribeSubject = new Subject(); public constructor( From 4db8c007f00340f425da83e6795b795b3682f67c Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 17 Mar 2025 22:27:22 +0100 Subject: [PATCH 16/56] Bugfix/fix activities import with account balances (#4446) * Fix import with account balances * Update changelog --- CHANGELOG.md | 4 ++++ .../src/app/import/create-account-with-balances.dto.ts | 10 ++++++++++ apps/api/src/app/import/import-data.dto.ts | 7 ++++--- .../src/lib/interfaces/account-balance.interface.ts | 4 ++++ libs/common/src/lib/interfaces/export.interface.ts | 4 +++- libs/common/src/lib/interfaces/index.ts | 2 ++ 6 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 apps/api/src/app/import/create-account-with-balances.dto.ts create mode 100644 libs/common/src/lib/interfaces/account-balance.interface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 080a65cf1..841ad6ac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the symbol validation in the _Yahoo Finance_ service (get asset profiles) - Refreshed the cryptocurrencies list +### Fixed + +- Fixed an issue in the activities import functionality related to the account balances + ## 2.146.0 - 2025-03-15 ### Changed diff --git a/apps/api/src/app/import/create-account-with-balances.dto.ts b/apps/api/src/app/import/create-account-with-balances.dto.ts new file mode 100644 index 000000000..fd4b8df48 --- /dev/null +++ b/apps/api/src/app/import/create-account-with-balances.dto.ts @@ -0,0 +1,10 @@ +import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; +import { AccountBalance } from '@ghostfolio/common/interfaces'; + +import { IsArray, IsOptional } from 'class-validator'; + +export class CreateAccountWithBalancesDto extends CreateAccountDto { + @IsArray() + @IsOptional() + balances?: AccountBalance; +} diff --git a/apps/api/src/app/import/import-data.dto.ts b/apps/api/src/app/import/import-data.dto.ts index 715766821..207c8152b 100644 --- a/apps/api/src/app/import/import-data.dto.ts +++ b/apps/api/src/app/import/import-data.dto.ts @@ -1,15 +1,16 @@ -import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { Type } from 'class-transformer'; import { IsArray, IsOptional, ValidateNested } from 'class-validator'; +import { CreateAccountWithBalancesDto } from './create-account-with-balances.dto'; + export class ImportDataDto { @IsOptional() @IsArray() - @Type(() => CreateAccountDto) + @Type(() => CreateAccountWithBalancesDto) @ValidateNested({ each: true }) - accounts: CreateAccountDto[]; + accounts: CreateAccountWithBalancesDto[]; @IsArray() @Type(() => CreateOrderDto) diff --git a/libs/common/src/lib/interfaces/account-balance.interface.ts b/libs/common/src/lib/interfaces/account-balance.interface.ts new file mode 100644 index 000000000..00fcf1e53 --- /dev/null +++ b/libs/common/src/lib/interfaces/account-balance.interface.ts @@ -0,0 +1,4 @@ +export interface AccountBalance { + date: string; + value: number; +} diff --git a/libs/common/src/lib/interfaces/export.interface.ts b/libs/common/src/lib/interfaces/export.interface.ts index 0772159e6..24e478779 100644 --- a/libs/common/src/lib/interfaces/export.interface.ts +++ b/libs/common/src/lib/interfaces/export.interface.ts @@ -1,8 +1,10 @@ import { Account, Order, Platform, Tag } from '@prisma/client'; +import { AccountBalance } from './account-balance.interface'; + export interface Export { accounts: (Omit & { - balances: { date: string; value: number }[]; + balances: AccountBalance[]; })[]; activities: (Omit< Order, diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 3dcbbb32a..5f8b8b5b1 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -1,4 +1,5 @@ import type { Access } from './access.interface'; +import type { AccountBalance } from './account-balance.interface'; import type { Accounts } from './accounts.interface'; import type { AdminData } from './admin-data.interface'; import type { AdminJobs } from './admin-jobs.interface'; @@ -68,6 +69,7 @@ import type { XRayRulesSettings } from './x-ray-rules-settings.interface'; export { Access, + AccountBalance, AccountBalancesResponse, Accounts, AdminData, From 235db72ade673018af0cdef56da7435d0fad291a Mon Sep 17 00:00:00 2001 From: Chang-Yen Tseng <10328757+Summon528@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:06:00 -0700 Subject: [PATCH 17/56] Bugfix/change client-side dates to be sent in UTC format (#4402) * Change client-side dates to be sent in UTC format * Update changelog --- CHANGELOG.md | 3 +++ apps/client/src/app/services/admin.service.ts | 17 ++--------------- apps/client/src/app/services/data.service.ts | 8 +++----- package-lock.json | 6 ++++++ package.json | 1 + 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 841ad6ac9..6651ff32e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed an issue in the activities import functionality related to the account balances +- Changed client-side dates to be sent in UTC format to ensure date consistency + - Benchmark endpoint + - Exchange rate endpoint ## 2.146.0 - 2025-03-15 diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 5d71a80f9..fea3924e9 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -8,7 +8,6 @@ import { PROPERTY_API_KEY_GHOSTFOLIO } from '@ghostfolio/common/config'; import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; -import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, AdminData, @@ -25,7 +24,6 @@ import { Injectable } from '@angular/core'; import { SortDirection } from '@angular/material/sort'; import { DataSource, MarketData, Platform } from '@prisma/client'; import { JobStatus } from 'bull'; -import { format } from 'date-fns'; import { switchMap } from 'rxjs'; import { environment } from '../../environments/environment'; @@ -186,19 +184,8 @@ export class AdminService { ); } - public gatherSymbol({ - dataSource, - date, - symbol - }: AssetProfileIdentifier & { - date?: Date; - }) { - let url = `/api/v1/admin/gather/${dataSource}/${symbol}`; - - if (date) { - url = `${url}/${format(date, DATE_FORMAT)}`; - } - + public gatherSymbol({ dataSource, symbol }: AssetProfileIdentifier) { + const url = `/api/v1/admin/gather/${dataSource}/${symbol}`; return this.http.post(url, {}); } diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index e8d4b7826..76f9c4867 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -57,6 +57,7 @@ import { translate } from '@ghostfolio/ui/i18n'; import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { SortDirection } from '@angular/material/sort'; +import { utc } from '@date-fns/utc'; import { AccountBalance, DataSource, @@ -281,7 +282,7 @@ export class DataService { symbol: string; }) { return this.http.get( - `/api/v1/exchange-rate/${symbol}/${format(date, DATE_FORMAT)}` + `/api/v1/exchange-rate/${symbol}/${format(date, DATE_FORMAT, { in: utc })}` ); } @@ -363,10 +364,7 @@ export class DataService { } return this.http.get( - `/api/v1/benchmarks/${dataSource}/${symbol}/${format( - startDate, - DATE_FORMAT - )}`, + `/api/v1/benchmarks/${dataSource}/${symbol}/${format(startDate, DATE_FORMAT, { in: utc })}`, { params } ); } diff --git a/package-lock.json b/package-lock.json index a6e4d4dd7..407107fbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@angular/router": "19.2.1", "@angular/service-worker": "19.2.1", "@codewithdan/observable-store": "2.2.15", + "@date-fns/utc": "2.1.0", "@dfinity/agent": "0.15.7", "@dfinity/auth-client": "0.15.7", "@dfinity/candid": "0.15.7", @@ -3113,6 +3114,11 @@ "dev": true, "license": "MIT" }, + "node_modules/@date-fns/utc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.0.tgz", + "integrity": "sha512-176grgAgU2U303rD2/vcOmNg0kGPbhzckuH1TEP2al7n0AQipZIy9P15usd2TKQCG1g+E1jX/ZVQSzs4sUDwgA==" + }, "node_modules/@dfinity/agent": { "version": "0.15.7", "resolved": "https://registry.npmjs.org/@dfinity/agent/-/agent-0.15.7.tgz", diff --git a/package.json b/package.json index eb3a7083d..89ca173ae 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@angular/router": "19.2.1", "@angular/service-worker": "19.2.1", "@codewithdan/observable-store": "2.2.15", + "@date-fns/utc": "2.1.0", "@dfinity/agent": "0.15.7", "@dfinity/auth-client": "0.15.7", "@dfinity/candid": "0.15.7", From efc0b1bf5a498f3d1206769976fd3d6a80964025 Mon Sep 17 00:00:00 2001 From: csehatt741 <77381875+csehatt741@users.noreply.github.com> Date: Tue, 18 Mar 2025 20:13:24 +0100 Subject: [PATCH 18/56] Feature/support filters in AI prompt API (#4431) * Support filters in AI prompt API * Update changelog --- CHANGELOG.md | 4 +++ .../api/src/app/endpoints/ai/ai.controller.ts | 27 +++++++++++++++++-- apps/api/src/app/endpoints/ai/ai.module.ts | 2 ++ apps/api/src/app/endpoints/ai/ai.service.ts | 4 +++ .../analysis/analysis-page.component.ts | 5 +++- apps/client/src/app/services/data.service.ts | 14 ++++++++-- 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6651ff32e..ea12f2ca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added support for filtering in the _Copy AI prompt to clipboard_ actions on the analysis page (experimental) + ### Changed - Improved the symbol validation in the _Yahoo Finance_ service (get asset profiles) diff --git a/apps/api/src/app/endpoints/ai/ai.controller.ts b/apps/api/src/app/endpoints/ai/ai.controller.ts index 910abbf96..980d5607c 100644 --- a/apps/api/src/app/endpoints/ai/ai.controller.ts +++ b/apps/api/src/app/endpoints/ai/ai.controller.ts @@ -1,5 +1,6 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { ApiService } from '@ghostfolio/api/services/api/api.service'; import { DEFAULT_CURRENCY, DEFAULT_LANGUAGE_CODE @@ -8,7 +9,14 @@ import { AiPromptResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; import type { AiPromptMode, RequestWithUser } from '@ghostfolio/common/types'; -import { Controller, Get, Inject, Param, UseGuards } from '@nestjs/common'; +import { + Controller, + Get, + Inject, + Param, + Query, + UseGuards +} from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; @@ -18,6 +26,7 @@ import { AiService } from './ai.service'; export class AiController { public constructor( private readonly aiService: AiService, + private readonly apiService: ApiService, @Inject(REQUEST) private readonly request: RequestWithUser ) {} @@ -25,9 +34,23 @@ export class AiController { @HasPermission(permissions.readAiPrompt) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async getPrompt( - @Param('mode') mode: AiPromptMode + @Param('mode') mode: AiPromptMode, + @Query('accounts') filterByAccounts?: string, + @Query('assetClasses') filterByAssetClasses?: string, + @Query('dataSource') filterByDataSource?: string, + @Query('symbol') filterBySymbol?: string, + @Query('tags') filterByTags?: string ): Promise { + const filters = this.apiService.buildFiltersFromQueryParams({ + filterByAccounts, + filterByAssetClasses, + filterByDataSource, + filterBySymbol, + filterByTags + }); + const prompt = await this.aiService.getPrompt({ + filters, mode, impersonationId: undefined, languageCode: diff --git a/apps/api/src/app/endpoints/ai/ai.module.ts b/apps/api/src/app/endpoints/ai/ai.module.ts index 5a30f3264..584f29956 100644 --- a/apps/api/src/app/endpoints/ai/ai.module.ts +++ b/apps/api/src/app/endpoints/ai/ai.module.ts @@ -7,6 +7,7 @@ import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.servic import { RulesService } from '@ghostfolio/api/app/portfolio/rules.service'; import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module'; +import { ApiModule } from '@ghostfolio/api/services/api/api.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; @@ -25,6 +26,7 @@ import { AiService } from './ai.service'; @Module({ controllers: [AiController], imports: [ + ApiModule, ConfigurationModule, DataProviderModule, ExchangeRateDataModule, diff --git a/apps/api/src/app/endpoints/ai/ai.service.ts b/apps/api/src/app/endpoints/ai/ai.service.ts index d9090d77c..8807e67bf 100644 --- a/apps/api/src/app/endpoints/ai/ai.service.ts +++ b/apps/api/src/app/endpoints/ai/ai.service.ts @@ -1,4 +1,5 @@ import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; +import { Filter } from '@ghostfolio/common/interfaces'; import type { AiPromptMode } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; @@ -8,12 +9,14 @@ export class AiService { public constructor(private readonly portfolioService: PortfolioService) {} public async getPrompt({ + filters, impersonationId, languageCode, mode, userCurrency, userId }: { + filters?: Filter[]; impersonationId: string; languageCode: string; mode: AiPromptMode; @@ -21,6 +24,7 @@ export class AiService { userId: string; }) { const { holdings } = await this.portfolioService.getDetails({ + filters, impersonationId, userId }); 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 4aee450e6..2bd3096d4 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 @@ -161,7 +161,10 @@ export class AnalysisPageComponent implements OnDestroy, OnInit { } this.dataService - .fetchPrompt(mode) + .fetchPrompt({ + mode, + filters: this.userService.getFilters() + }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(({ prompt }) => { this.clipboard.copy(prompt); diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 76f9c4867..4eba3fffa 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -653,8 +653,18 @@ export class DataService { return this.http.get('/api/v1/portfolio/report'); } - public fetchPrompt(mode: AiPromptMode) { - return this.http.get(`/api/v1/ai/prompt/${mode}`); + public fetchPrompt({ + filters, + mode + }: { + filters?: Filter[]; + mode: AiPromptMode; + }) { + const params = this.buildFiltersAsQueryParams({ filters }); + + return this.http.get(`/api/v1/ai/prompt/${mode}`, { + params + }); } public fetchPublicPortfolio(aAccessId: string) { From 82fe2590bf4118e091b30dfa8ae74fde3529a463 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:26:00 +0100 Subject: [PATCH 19/56] Feature/add Storybook link to development guide (#4448) * Add Storybook --- DEVELOPMENT.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index cea43095d..1c45aeca1 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -60,6 +60,10 @@ Remove permission in `UserService` using `without()` Use `@if (user?.settings?.isExperimentalFeatures) {}` in HTML template +## Component Library (_Storybook_) + +https://ghostfol.io/development/storybook + ## Git ### Rebase From ddc798928097d0df193504133cca210525b68d02 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 20:37:30 +0100 Subject: [PATCH 20/56] Feature/update locales (#4460) Co-authored-by: github-actions[bot] --- apps/client/src/locales/messages.ca.xlf | 4 ++-- apps/client/src/locales/messages.de.xlf | 4 ++-- apps/client/src/locales/messages.es.xlf | 4 ++-- apps/client/src/locales/messages.fr.xlf | 4 ++-- apps/client/src/locales/messages.it.xlf | 4 ++-- apps/client/src/locales/messages.nl.xlf | 4 ++-- apps/client/src/locales/messages.pl.xlf | 4 ++-- apps/client/src/locales/messages.pt.xlf | 4 ++-- apps/client/src/locales/messages.tr.xlf | 4 ++-- apps/client/src/locales/messages.uk.xlf | 4 ++-- apps/client/src/locales/messages.xlf | 4 ++-- apps/client/src/locales/messages.zh.xlf | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index 0f9de4906..f83df8770 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -7589,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7685,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index 223253bae..d8ee071f5 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -7589,7 +7589,7 @@ KI-Anweisung wurde in die Zwischenablage kopiert apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7685,7 +7685,7 @@ Öffne Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index a763a3902..5b9c5dbef 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -7590,7 +7590,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7686,7 +7686,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index 0e7b6b9aa..2b61e3ed1 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -7589,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7685,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index 4ab6c53a1..3d12719eb 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -7590,7 +7590,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7686,7 +7686,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index 13671956d..fa29621a5 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -7589,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7685,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index ac208d13a..e6e89741d 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -7589,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7685,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index 789ee6360..5676461ed 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -7589,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7685,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index 05e2566f8..f9e2bfd9b 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -7589,7 +7589,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7685,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.uk.xlf b/apps/client/src/locales/messages.uk.xlf index 94d3893ba..2c13d1ef7 100644 --- a/apps/client/src/locales/messages.uk.xlf +++ b/apps/client/src/locales/messages.uk.xlf @@ -7597,7 +7597,7 @@ Запит AI скопійовано в буфер обміну apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7685,7 +7685,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index c1ec934be..778195c24 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -6867,7 +6867,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -6951,7 +6951,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index 6d6054df6..5c1b6bc5b 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -7590,7 +7590,7 @@ AI prompt has been copied to the clipboard apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 170 + 173 @@ -7686,7 +7686,7 @@ Open Duck.ai apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts - 171 + 174 From 795e4582a8005b15ae0d788a45d6032b64ebe917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20=C5=81=C4=85giewka?= Date: Wed, 19 Mar 2025 21:10:19 +0100 Subject: [PATCH 21/56] Feature/replace lodash.uniq with Array.from + Set (#4387) * Replace lodash.uniq with Array.from + Set * Update chagnelog --- CHANGELOG.md | 1 + .../calculator/portfolio-calculator.ts | 4 ++-- .../src/app/portfolio/portfolio.service.ts | 20 ++++++++++--------- .../exchange-rate-data.service.ts | 4 ++-- .../create-asset-profile-dialog.component.ts | 5 +++-- .../user-account-settings.component.ts | 3 +-- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea12f2ca9..8f58b23f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved the symbol validation in the _Yahoo Finance_ service (get asset profiles) +- Refactored `lodash.uniq` with `Array.from(new Set(...))` - Refreshed the cryptocurrencies list ### Fixed diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 54d80a955..52d57230b 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -49,7 +49,7 @@ import { min, subDays } from 'date-fns'; -import { isNumber, sortBy, sum, uniq, uniqBy } from 'lodash'; +import { isNumber, sortBy, sum, uniqBy } from 'lodash'; export abstract class PortfolioCalculator { protected static readonly ENABLE_LOGGING = false; @@ -222,7 +222,7 @@ export abstract class PortfolioCalculator { const exchangeRatesByCurrency = await this.exchangeRateDataService.getExchangeRatesByCurrency({ - currencies: uniq(Object.values(currencies)), + currencies: Array.from(new Set(Object.values(currencies))), endDate: endOfDay(this.endDate), startDate: this.startDate, targetCurrency: this.currency diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index e90ebd4ae..a3d9e3c4a 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -82,7 +82,7 @@ import { parseISO, set } from 'date-fns'; -import { isEmpty, uniq } from 'lodash'; +import { isEmpty } from 'lodash'; import { PortfolioCalculator } from './calculator/portfolio-calculator'; import { @@ -2032,14 +2032,16 @@ export class PortfolioService { where: { id: filters[0].id } }); } else { - const accountIds = uniq( - activities - .filter(({ accountId }) => { - return accountId; - }) - .map(({ accountId }) => { - return accountId; - }) + const accountIds = Array.from( + new Set( + activities + .filter(({ accountId }) => { + return accountId; + }) + .map(({ accountId }) => { + return accountId; + }) + ) ); currentAccounts = await this.accountService.accounts({ 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 db95a3487..0a2d177ce 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 @@ -23,7 +23,7 @@ import { isToday, subDays } from 'date-fns'; -import { isNumber, uniq } from 'lodash'; +import { isNumber } from 'lodash'; import ms from 'ms'; @Injectable() @@ -515,7 +515,7 @@ export class ExchangeRateDataService { } } - return uniq(currencies).filter(Boolean).sort(); + return Array.from(new Set(currencies)).filter(Boolean).sort(); } private prepareCurrencyPairs(aCurrencies: string[]) { diff --git a/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts index b0f69fa5c..e84554577 100644 --- a/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts @@ -20,7 +20,6 @@ import { } from '@angular/forms'; import { MatDialogRef } from '@angular/material/dialog'; import { isISO4217CurrencyCode } from 'class-validator'; -import { uniq } from 'lodash'; import { Subject, takeUntil } from 'rxjs'; import { CreateAssetProfileDialogMode } from './interfaces/interfaces'; @@ -87,7 +86,9 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy { this.createAssetProfileForm.get('addCurrency').value as string ).toUpperCase(); - const currencies = uniq([...this.customCurrencies, currency]).sort(); + const currencies = Array.from( + new Set([...this.customCurrencies, currency]) + ).sort(); this.dataService .putAdminSetting(PROPERTY_CURRENCIES, { diff --git a/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts b/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts index ced617117..1ee23ff8a 100644 --- a/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts +++ b/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -24,7 +24,6 @@ import { FormBuilder, Validators } from '@angular/forms'; import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { MatSnackBar } from '@angular/material/snack-bar'; import { format, parseISO } from 'date-fns'; -import { uniq } from 'lodash'; import ms from 'ms'; import { EMPTY, Subject, throwError } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; @@ -108,7 +107,7 @@ export class UserAccountSettingsComponent implements OnDestroy, OnInit { ); this.locales.push(this.user.settings.locale); - this.locales = uniq(this.locales.sort()); + this.locales = Array.from(new Set(this.locales)).sort(); this.changeDetectorRef.markForCheck(); } From 1aa1960d45ec5d731a8351663d70154e6e50c2b7 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 20 Mar 2025 08:03:32 +0100 Subject: [PATCH 22/56] Feature/rename TWR to ROAI (#4464) * Rename TWR to ROAI --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 14477ea9a..ed82ac723 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Ghostfolio is for you if you are... - ✅ Create, update and delete transactions - ✅ Multi account management -- ✅ Portfolio performance: Time-weighted rate of return (TWR) for `Today`, `WTD`, `MTD`, `YTD`, `1Y`, `5Y`, `Max` +- ✅ Portfolio performance: Return on Average Investment (ROAI) for `Today`, `WTD`, `MTD`, `YTD`, `1Y`, `5Y`, `Max` - ✅ Various charts - ✅ Static analysis to identify potential risks in your portfolio - ✅ Import and export transactions From d8b305a343720cd07f618e64722ecf1119ea429e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 20 Mar 2025 08:04:02 +0100 Subject: [PATCH 23/56] Feature/add Storybook to sitemap.xml (#4452) * Add Storybook * Update changelog --- CHANGELOG.md | 1 + apps/api/src/assets/sitemap.xml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f58b23f8..4d24ce5d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support for filtering in the _Copy AI prompt to clipboard_ actions on the analysis page (experimental) +- Added the _Storybook_ path to the `sitemap.xml` file ### Changed diff --git a/apps/api/src/assets/sitemap.xml b/apps/api/src/assets/sitemap.xml index a422ff31a..fc1e89dba 100644 --- a/apps/api/src/assets/sitemap.xml +++ b/apps/api/src/assets/sitemap.xml @@ -92,6 +92,10 @@ https://ghostfol.io/de/ueber-uns/oss-friends ${currentDate}T00:00:00+00:00 + + https://ghostfol.io/development/storybook + ${currentDate}T00:00:00+00:00 + https://ghostfol.io/en ${currentDate}T00:00:00+00:00 From 150d97bd420c1a480b7658df34eed05be8aaa28c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 20:40:59 +0100 Subject: [PATCH 24/56] Feature/update locales (#4463) Co-authored-by: github-actions[bot] --- apps/client/src/locales/messages.ca.xlf | 10 +++++----- apps/client/src/locales/messages.de.xlf | 10 +++++----- apps/client/src/locales/messages.es.xlf | 10 +++++----- apps/client/src/locales/messages.fr.xlf | 10 +++++----- apps/client/src/locales/messages.it.xlf | 10 +++++----- apps/client/src/locales/messages.nl.xlf | 10 +++++----- apps/client/src/locales/messages.pl.xlf | 10 +++++----- apps/client/src/locales/messages.pt.xlf | 10 +++++----- apps/client/src/locales/messages.tr.xlf | 10 +++++----- apps/client/src/locales/messages.uk.xlf | 10 +++++----- apps/client/src/locales/messages.xlf | 10 +++++----- apps/client/src/locales/messages.zh.xlf | 10 +++++----- 12 files changed, 60 insertions(+), 60 deletions(-) diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index f83df8770..52a1a1dac 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -2475,7 +2475,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -3343,7 +3343,7 @@ Auto apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -3351,7 +3351,7 @@ Do you really want to close your Ghostfolio account? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -3359,7 +3359,7 @@ Do you really want to remove this sign in method? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -3367,7 +3367,7 @@ Oops! There was an error setting up biometric authentication. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index d8ee071f5..49495d26e 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -1022,7 +1022,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -1662,7 +1662,7 @@ Möchtest du diese Anmeldemethode wirklich löschen? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -2906,7 +2906,7 @@ Automatisch apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -6603,7 +6603,7 @@ Möchtest du dieses Ghostfolio Konto wirklich schliessen? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6651,7 +6651,7 @@ Ups! Beim Einrichten der biometrischen Authentifizierung ist ein Fehler aufgetreten. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index 5b9c5dbef..2997331e2 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -1023,7 +1023,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -1663,7 +1663,7 @@ ¿Estás seguro de eliminar este método de acceso? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -2907,7 +2907,7 @@ Automático apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -6604,7 +6604,7 @@ ¿Estás seguro de querer borrar tu cuenta de Ghostfolio? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6652,7 +6652,7 @@ Oops! There was an error setting up biometric authentication. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index 2b61e3ed1..3819bc98d 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -1334,7 +1334,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -1878,7 +1878,7 @@ Auto apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -1918,7 +1918,7 @@ Voulez-vous vraiment supprimer cette méthode de connexion ? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -6603,7 +6603,7 @@ Confirmer la suppresion de votre compte Ghostfolio ? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6651,7 +6651,7 @@ Oops! Une erreur s'est produite lors de la configuration de l'authentification biométrique. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index 3d12719eb..257f8f68a 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -1023,7 +1023,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -1663,7 +1663,7 @@ Vuoi davvero rimuovere questo metodo di accesso? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -2907,7 +2907,7 @@ Auto apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -6604,7 +6604,7 @@ Confermi di voler chiudere il tuo account Ghostfolio? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6652,7 +6652,7 @@ Ops! C'è stato un errore impostando l'autenticazione biometrica. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index fa29621a5..00341b16c 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -1022,7 +1022,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -1662,7 +1662,7 @@ Wil je deze aanmeldingsmethode echt verwijderen? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -2906,7 +2906,7 @@ Automatisch apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -6603,7 +6603,7 @@ Do you really want to close your Ghostfolio account? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6651,7 +6651,7 @@ Oops! There was an error setting up biometric authentication. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index e6e89741d..aaed78ea7 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -2287,7 +2287,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -3075,7 +3075,7 @@ Auto apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -3083,7 +3083,7 @@ Czy na pewno chcesz usunąć tą metode logowania? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -6603,7 +6603,7 @@ Czy na pewno chcesz zamknąć swoje konto Ghostfolio? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6651,7 +6651,7 @@ Ups! Wystąpił błąd podczas konfigurowania uwierzytelniania biometrycznego. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index 5676461ed..5fe949db4 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -1206,7 +1206,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -1862,7 +1862,7 @@ Auto apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -1902,7 +1902,7 @@ Deseja realmente remover este método de início de sessão? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -6603,7 +6603,7 @@ Do you really want to close your Ghostfolio account? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6651,7 +6651,7 @@ Oops! There was an error setting up biometric authentication. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index f9e2bfd9b..2e385d887 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -2135,7 +2135,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -4895,7 +4895,7 @@ Otomatik apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -4935,7 +4935,7 @@ Bu giriş yöntemini kaldırmayı gerçekten istiyor musunuz? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -6603,7 +6603,7 @@ Do you really want to close your Ghostfolio account? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6651,7 +6651,7 @@ Oops! There was an error setting up biometric authentication. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.uk.xlf b/apps/client/src/locales/messages.uk.xlf index 2c13d1ef7..b2dc82d73 100644 --- a/apps/client/src/locales/messages.uk.xlf +++ b/apps/client/src/locales/messages.uk.xlf @@ -2595,7 +2595,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -3591,7 +3591,7 @@ Автоматичний apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -3599,7 +3599,7 @@ Ви дійсно хочете закрити ваш обліковий запис Ghostfolio? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -3607,7 +3607,7 @@ Ви дійсно хочете вилучити цей спосіб входу? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -3615,7 +3615,7 @@ Упс! Виникла помилка під час налаштування біометричної автентифікації. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index 778195c24..417df8c83 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -2152,7 +2152,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -2864,14 +2864,14 @@ Auto apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 Do you really want to remove this sign in method? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -6009,7 +6009,7 @@ Do you really want to close your Ghostfolio account? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6044,7 +6044,7 @@ Oops! There was an error setting up biometric authentication. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index 5c1b6bc5b..ca4b21fe0 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -2296,7 +2296,7 @@ apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 160 + 159 @@ -3084,7 +3084,7 @@ 自动 apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 40 + 39 @@ -3092,7 +3092,7 @@ 您确实要删除此登录方法吗? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 249 + 248 @@ -6604,7 +6604,7 @@ Do you really want to close your Ghostfolio account? apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 175 + 174 @@ -6652,7 +6652,7 @@ Oops! There was an error setting up biometric authentication. apps/client/src/app/components/user-account-settings/user-account-settings.component.ts - 303 + 302 From 536b000ff926f57d36a9d1d7b0f938af7854a060 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 21 Mar 2025 20:41:37 +0100 Subject: [PATCH 25/56] Feature/improve Storybook story of fire calculator (#4451) * Add default value for fire wealth --- .../lib/fire-calculator/fire-calculator.component.stories.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.stories.ts b/libs/ui/src/lib/fire-calculator/fire-calculator.component.stories.ts index 0ad868ff9..e4cfa18ea 100644 --- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.stories.ts +++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.stories.ts @@ -45,7 +45,7 @@ type Story = StoryObj; export const Simple: Story = { args: { currency: 'USD', - fireWealth: 0, + fireWealth: 50000, locale: locale } }; From 198f73db004fc45cf1aca77a6d59acb632da0030 Mon Sep 17 00:00:00 2001 From: csehatt741 <77381875+csehatt741@users.noreply.github.com> Date: Fri, 21 Mar 2025 20:58:47 +0100 Subject: [PATCH 26/56] Feature/improve export by applying filters on accounts and tags (#4425) * Improve export by applying filters on accounts and tags * Update changelog --------- Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> --- CHANGELOG.md | 1 + apps/api/src/app/export/export.controller.ts | 3 +- apps/api/src/app/export/export.service.ts | 96 +++++++++++--------- 3 files changed, 58 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d24ce5d6..80f831119 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the export functionality by applying filters on accounts and tags - Improved the symbol validation in the _Yahoo Finance_ service (get asset profiles) - Refactored `lodash.uniq` with `Array.from(new Set(...))` - Refreshed the cryptocurrencies list diff --git a/apps/api/src/app/export/export.controller.ts b/apps/api/src/app/export/export.controller.ts index 551b3e489..d807132c9 100644 --- a/apps/api/src/app/export/export.controller.ts +++ b/apps/api/src/app/export/export.controller.ts @@ -21,10 +21,11 @@ export class ExportController { @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async export( @Query('accounts') filterByAccounts?: string, - @Query('activityIds') activityIds?: string[], + @Query('activityIds') filterByActivityIds?: string, @Query('assetClasses') filterByAssetClasses?: string, @Query('tags') filterByTags?: string ): Promise { + const activityIds = filterByActivityIds?.split(',') ?? []; const filters = this.apiService.buildFiltersFromQueryParams({ filterByAccounts, filterByAssetClasses, diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index 8b9d2c56c..f0449dc14 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -28,6 +28,22 @@ export class ExportService { }): Promise { const platformsMap: { [platformId: string]: Platform } = {}; + let { activities } = await this.orderService.getOrders({ + filters, + userCurrency, + userId, + includeDrafts: true, + sortColumn: 'date', + sortDirection: 'asc', + withExcludedAccounts: true + }); + + if (activityIds?.length > 0) { + activities = activities.filter(({ id }) => { + return activityIds.includes(id); + }); + } + const accounts = ( await this.accountService.accounts({ include: { @@ -39,57 +55,55 @@ export class ExportService { }, where: { userId } }) - ).map( - ({ - balance, - balances, - comment, - currency, - id, - isExcluded, - name, - Platform: platform, - platformId - }) => { - if (platformId) { - platformsMap[platformId] = platform; - } - - return { + ) + .filter(({ id }) => { + return activities.length > 0 + ? activities.some(({ accountId }) => { + return accountId === id; + }) + : true; + }) + .map( + ({ balance, - balances: balances.map(({ date, value }) => { - return { date: date.toISOString(), value }; - }), + balances, comment, currency, id, isExcluded, name, + Platform: platform, platformId - }; - } - ); - - let { activities } = await this.orderService.getOrders({ - filters, - userCurrency, - userId, - includeDrafts: true, - sortColumn: 'date', - sortDirection: 'asc', - withExcludedAccounts: true - }); + }) => { + if (platformId) { + platformsMap[platformId] = platform; + } - if (activityIds) { - activities = activities.filter((activity) => { - return activityIds.includes(activity.id); - }); - } + return { + balance, + balances: balances.map(({ date, value }) => { + return { date: date.toISOString(), value }; + }), + comment, + currency, + id, + isExcluded, + name, + platformId + }; + } + ); const tags = (await this.tagService.getTagsForUser(userId)) - .filter(({ isUsed }) => { - return isUsed; - }) + .filter( + ({ id, isUsed }) => + isUsed && + activities.some((activity) => { + return activity.tags.some(({ id: tagId }) => { + return tagId === id; + }); + }) + ) .map(({ id, name }) => { return { id, From a9c32248561322a22df8630f8598c4e16ff4f1f0 Mon Sep 17 00:00:00 2001 From: Ronnie Alsop <133896587+aRonnieAlsop@users.noreply.github.com> Date: Sat, 22 Mar 2025 01:36:45 -0700 Subject: [PATCH 27/56] Feature/add endpoint to localize site.webmanifest (#4450) * Add endpoint to localize site.webmanifest * Refactor rootUrl * Update changelog --------- Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> --- CHANGELOG.md | 1 + apps/api/src/app/app.module.ts | 2 + .../app/endpoints/assets/assets.controller.ts | 46 +++++++++++++++++++ .../src/app/endpoints/assets/assets.module.ts | 11 +++++ .../api/src/app/sitemap/sitemap.controller.ts | 8 ++-- .../src/assets/site.webmanifest | 4 +- apps/api/src/environments/environment.prod.ts | 3 ++ apps/api/src/environments/environment.ts | 3 ++ apps/api/src/main.ts | 10 ++-- .../middlewares/html-template.middleware.ts | 3 +- .../configuration/configuration.service.ts | 14 ++++-- apps/client/ngsw-config.json | 8 +--- apps/client/project.json | 3 -- apps/client/src/index.html | 5 +- libs/common/src/lib/config.ts | 3 +- 15 files changed, 96 insertions(+), 28 deletions(-) create mode 100644 apps/api/src/app/endpoints/assets/assets.controller.ts create mode 100644 apps/api/src/app/endpoints/assets/assets.module.ts rename apps/{client => api}/src/assets/site.webmanifest (92%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80f831119..83e0bec19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support for filtering in the _Copy AI prompt to clipboard_ actions on the analysis page (experimental) +- Added an endpoint to localize the `site.webmanifest` - Added the _Storybook_ path to the `sitemap.xml` file ### Changed diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 2a515bf43..99080e1e1 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -32,6 +32,7 @@ import { AuthModule } from './auth/auth.module'; import { CacheModule } from './cache/cache.module'; import { AiModule } from './endpoints/ai/ai.module'; import { ApiKeysModule } from './endpoints/api-keys/api-keys.module'; +import { AssetsModule } from './endpoints/assets/assets.module'; import { BenchmarksModule } from './endpoints/benchmarks/benchmarks.module'; import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module'; import { MarketDataModule } from './endpoints/market-data/market-data.module'; @@ -61,6 +62,7 @@ import { UserModule } from './user/user.module'; AiModule, ApiKeysModule, AssetModule, + AssetsModule, AuthDeviceModule, AuthModule, BenchmarksModule, diff --git a/apps/api/src/app/endpoints/assets/assets.controller.ts b/apps/api/src/app/endpoints/assets/assets.controller.ts new file mode 100644 index 000000000..1735cc594 --- /dev/null +++ b/apps/api/src/app/endpoints/assets/assets.controller.ts @@ -0,0 +1,46 @@ +import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; +import { interpolate } from '@ghostfolio/common/helper'; + +import { + Controller, + Get, + Param, + Res, + Version, + VERSION_NEUTRAL +} from '@nestjs/common'; +import { Response } from 'express'; +import { readFileSync } from 'fs'; +import { join } from 'path'; + +@Controller('assets') +export class AssetsController { + private webManifest = ''; + + public constructor( + public readonly configurationService: ConfigurationService + ) { + try { + this.webManifest = readFileSync( + join(__dirname, 'assets', 'site.webmanifest'), + 'utf8' + ); + } catch {} + } + + @Get('/:languageCode/site.webmanifest') + @Version(VERSION_NEUTRAL) + public getWebManifest( + @Param('languageCode') languageCode: string, + @Res() response: Response + ): void { + const rootUrl = this.configurationService.get('ROOT_URL'); + const webManifest = interpolate(this.webManifest, { + languageCode, + rootUrl + }); + + response.setHeader('Content-Type', 'application/json'); + response.send(webManifest); + } +} diff --git a/apps/api/src/app/endpoints/assets/assets.module.ts b/apps/api/src/app/endpoints/assets/assets.module.ts new file mode 100644 index 000000000..51d330e50 --- /dev/null +++ b/apps/api/src/app/endpoints/assets/assets.module.ts @@ -0,0 +1,11 @@ +import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; + +import { Module } from '@nestjs/common'; + +import { AssetsController } from './assets.controller'; + +@Module({ + controllers: [AssetsController], + providers: [ConfigurationService] +}) +export class AssetsModule {} diff --git a/apps/api/src/app/sitemap/sitemap.controller.ts b/apps/api/src/app/sitemap/sitemap.controller.ts index ea21906ef..aad5e39a1 100644 --- a/apps/api/src/app/sitemap/sitemap.controller.ts +++ b/apps/api/src/app/sitemap/sitemap.controller.ts @@ -9,8 +9,8 @@ import { personalFinanceTools } from '@ghostfolio/common/personal-finance-tools' import { Controller, Get, Res, VERSION_NEUTRAL, Version } from '@nestjs/common'; import { format } from 'date-fns'; import { Response } from 'express'; -import * as fs from 'fs'; -import * as path from 'path'; +import { readFileSync } from 'fs'; +import { join } from 'path'; @Controller('sitemap.xml') export class SitemapController { @@ -20,8 +20,8 @@ export class SitemapController { private readonly configurationService: ConfigurationService ) { try { - this.sitemapXml = fs.readFileSync( - path.join(__dirname, 'assets', 'sitemap.xml'), + this.sitemapXml = readFileSync( + join(__dirname, 'assets', 'sitemap.xml'), 'utf8' ); } catch {} diff --git a/apps/client/src/assets/site.webmanifest b/apps/api/src/assets/site.webmanifest similarity index 92% rename from apps/client/src/assets/site.webmanifest rename to apps/api/src/assets/site.webmanifest index 8f1eceefb..a28719625 100644 --- a/apps/client/src/assets/site.webmanifest +++ b/apps/api/src/assets/site.webmanifest @@ -25,7 +25,7 @@ "name": "Ghostfolio", "orientation": "portrait", "short_name": "Ghostfolio", - "start_url": "/en/", + "start_url": "/${languageCode}/", "theme_color": "#FFFFFF", - "url": "https://ghostfol.io" + "url": "${rootUrl}" } diff --git a/apps/api/src/environments/environment.prod.ts b/apps/api/src/environments/environment.prod.ts index 81b324963..6d4cbb4bf 100644 --- a/apps/api/src/environments/environment.prod.ts +++ b/apps/api/src/environments/environment.prod.ts @@ -1,4 +1,7 @@ +import { DEFAULT_HOST, DEFAULT_PORT } from '@ghostfolio/common/config'; + export const environment = { production: true, + rootUrl: `http://${DEFAULT_HOST}:${DEFAULT_PORT}`, version: `${require('../../../../package.json').version}` }; diff --git a/apps/api/src/environments/environment.ts b/apps/api/src/environments/environment.ts index c0ae2e7e5..054766460 100644 --- a/apps/api/src/environments/environment.ts +++ b/apps/api/src/environments/environment.ts @@ -1,4 +1,7 @@ +import { DEFAULT_HOST } from '@ghostfolio/common/config'; + export const environment = { production: false, + rootUrl: `https://${DEFAULT_HOST}:4200`, version: 'dev' }; diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 7cd5953b0..73502525c 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -1,4 +1,8 @@ -import { STORYBOOK_PATH } from '@ghostfolio/common/config'; +import { + DEFAULT_HOST, + DEFAULT_PORT, + STORYBOOK_PATH +} from '@ghostfolio/common/config'; import { Logger, @@ -75,8 +79,8 @@ async function bootstrap() { app.use(HtmlTemplateMiddleware); - const HOST = configService.get('HOST') || '0.0.0.0'; - const PORT = configService.get('PORT') || 3333; + const HOST = configService.get('HOST') || DEFAULT_HOST; + const PORT = configService.get('PORT') || DEFAULT_PORT; await app.listen(PORT, HOST, () => { logLogo(); diff --git a/apps/api/src/middlewares/html-template.middleware.ts b/apps/api/src/middlewares/html-template.middleware.ts index 256876952..403b09610 100644 --- a/apps/api/src/middlewares/html-template.middleware.ts +++ b/apps/api/src/middlewares/html-template.middleware.ts @@ -2,7 +2,6 @@ import { environment } from '@ghostfolio/api/environments/environment'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { DEFAULT_LANGUAGE_CODE, - DEFAULT_ROOT_URL, STORYBOOK_PATH, SUPPORTED_LANGUAGE_CODES } from '@ghostfolio/common/config'; @@ -126,7 +125,7 @@ export const HtmlTemplateMiddleware = async ( } const currentDate = format(new Date(), DATE_FORMAT); - const rootUrl = process.env.ROOT_URL || DEFAULT_ROOT_URL; + const rootUrl = process.env.ROOT_URL || environment.rootUrl; if ( path.startsWith('/api/') || diff --git a/apps/api/src/services/configuration/configuration.service.ts b/apps/api/src/services/configuration/configuration.service.ts index 3dfe5d5c5..473d909ee 100644 --- a/apps/api/src/services/configuration/configuration.service.ts +++ b/apps/api/src/services/configuration/configuration.service.ts @@ -1,11 +1,13 @@ +import { environment } from '@ghostfolio/api/environments/environment'; import { Environment } from '@ghostfolio/api/services/interfaces/environment.interface'; import { CACHE_TTL_NO_CACHE, + DEFAULT_HOST, + DEFAULT_PORT, DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY, DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY, DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY, - DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT, - DEFAULT_ROOT_URL + DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT } from '@ghostfolio/common/config'; import { Injectable } from '@nestjs/common'; @@ -49,11 +51,11 @@ export class ConfigurationService { GOOGLE_SHEETS_ACCOUNT: str({ default: '' }), GOOGLE_SHEETS_ID: str({ default: '' }), GOOGLE_SHEETS_PRIVATE_KEY: str({ default: '' }), - HOST: host({ default: '0.0.0.0' }), + HOST: host({ default: DEFAULT_HOST }), JWT_SECRET_KEY: str({}), MAX_ACTIVITIES_TO_IMPORT: num({ default: Number.MAX_SAFE_INTEGER }), MAX_CHART_ITEMS: num({ default: 365 }), - PORT: port({ default: 3333 }), + PORT: port({ default: DEFAULT_PORT }), PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY: num({ default: DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY }), @@ -71,7 +73,9 @@ export class ConfigurationService { REDIS_PASSWORD: str({ default: '' }), REDIS_PORT: port({ default: 6379 }), REQUEST_TIMEOUT: num({ default: ms('3 seconds') }), - ROOT_URL: url({ default: DEFAULT_ROOT_URL }), + ROOT_URL: url({ + default: environment.rootUrl + }), STRIPE_PUBLIC_KEY: str({ default: '' }), STRIPE_SECRET_KEY: str({ default: '' }), TWITTER_ACCESS_TOKEN: str({ default: 'dummyAccessToken' }), diff --git a/apps/client/ngsw-config.json b/apps/client/ngsw-config.json index c0f03a135..56e1cfd63 100644 --- a/apps/client/ngsw-config.json +++ b/apps/client/ngsw-config.json @@ -6,13 +6,7 @@ "name": "app", "installMode": "prefetch", "resources": { - "files": [ - "/favicon.ico", - "/index.html", - "/assets/site.webmanifest", - "/*.css", - "/*.js" - ] + "files": ["/favicon.ico", "/index.html", "/*.css", "/*.js"] } }, { diff --git a/apps/client/project.json b/apps/client/project.json index 160a27ea7..b2144d7b3 100644 --- a/apps/client/project.json +++ b/apps/client/project.json @@ -146,9 +146,6 @@ { "command": "shx cp apps/client/src/assets/robots.txt dist/apps/client" }, - { - "command": "shx cp apps/client/src/assets/site.webmanifest dist/apps/client" - }, { "command": "shx cp node_modules/ionicons/dist/index.js dist/apps/client" }, diff --git a/apps/client/src/index.html b/apps/client/src/index.html index 47f2c3d1a..e11bd1575 100644 --- a/apps/client/src/index.html +++ b/apps/client/src/index.html @@ -45,7 +45,10 @@ sizes="16x16" type="image/png" /> - + diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 696ca86d2..b8588fd0d 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -48,13 +48,14 @@ export const PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE_PRIORITY_LOW = export const DEFAULT_CURRENCY = 'USD'; export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy'; +export const DEFAULT_HOST = '0.0.0.0'; export const DEFAULT_LANGUAGE_CODE = 'en'; export const DEFAULT_PAGE_SIZE = 50; +export const DEFAULT_PORT = 3333; export const DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT = 30000; -export const DEFAULT_ROOT_URL = 'https://localhost:4200'; // USX is handled separately export const DERIVED_CURRENCIES = [ From 5ecdb5fb7a0b5f019946261075935a0d9094eabd Mon Sep 17 00:00:00 2001 From: Omer Faruk Gormel Date: Sat, 22 Mar 2025 10:20:30 +0100 Subject: [PATCH 28/56] Feature/improve language localization for tr 20250322 (#4467) * Update translations * Update changelog --- CHANGELOG.md | 1 + apps/client/src/locales/messages.tr.xlf | 74 ++++++++++++------------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83e0bec19..b5d72f986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the symbol validation in the _Yahoo Finance_ service (get asset profiles) - Refactored `lodash.uniq` with `Array.from(new Set(...))` - Refreshed the cryptocurrencies list +- Improved the language localization for Turkish (`tr`) ### Fixed diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index 2e385d887..6e3f5bb95 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -3556,7 +3556,7 @@ What our users are saying - What our users are saying + Kullanıcılarımızın görüşleri apps/client/src/app/pages/landing/landing-page.html 327 @@ -3620,7 +3620,7 @@ Are you ready? - Are you ready? + Hazır mısınız? apps/client/src/app/pages/landing/landing-page.html 431 @@ -6204,7 +6204,7 @@ Permission - Permission + Yetki apps/client/src/app/components/access-table/access-table.component.html 18 @@ -6216,7 +6216,7 @@ Restricted view - Restricted view + Kısıtlı görünüm apps/client/src/app/components/access-table/access-table.component.html 26 @@ -6228,7 +6228,7 @@ Oops! Could not grant access. - Oops! Could not grant access. + Hay Allah! Erişim izni verilemedi. apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts 91 @@ -6236,7 +6236,7 @@ Private - Private + Özel apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.html 24 @@ -6244,7 +6244,7 @@ Job Queue - Job Queue + İş Kuyruğu apps/client/src/app/pages/admin/admin-page-routing.module.ts 25 @@ -6264,7 +6264,7 @@ Investment - Investment + Yatırım apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts 56 @@ -6280,7 +6280,7 @@ Absolute Asset Performance - Absolute Asset Performance + Mutlak Varlık Performansı apps/client/src/app/pages/portfolio/analysis/analysis-page.html 102 @@ -6640,7 +6640,7 @@ Join now or check out the example account - Join now or check out the example account + Hemen katıl ya da örnek hesabı incele apps/client/src/app/pages/landing/landing-page.html 434 @@ -7128,7 +7128,7 @@ Oops! Invalid currency. - Oops! Invalid currency. + Hay Allah! Geçersiz para birimi. apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 49 @@ -7136,7 +7136,7 @@ This page has been archived. - This page has been archived. + Bu sayfa arşivlendi. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 14 @@ -7144,7 +7144,7 @@ is Open Source Software - is Open Source Software + , Açık Kaynak Kodlu Yazılımdır apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7152,7 +7152,7 @@ is not Open Source Software - is not Open Source Software + , Açık Kaynak Kodlu Yazılımdır apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7160,7 +7160,7 @@ is Open Source Software - is Open Source Software + , Açık Kaynak Kodlu Yazılımdır apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 156 @@ -7168,7 +7168,7 @@ is not Open Source Software - is not Open Source Software + , Açık Kaynak Kodlu Yazılım değildir apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 163 @@ -7578,7 +7578,7 @@ Please enter your Ghostfolio API key. - Please enter your Ghostfolio API key. + Lütfen Ghostfolio API anahtarınızı girin. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.component.ts 57 @@ -7586,7 +7586,7 @@ AI prompt has been copied to the clipboard - AI prompt has been copied to the clipboard + Yapay zeka istemi panoya kopyalandı apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts 173 @@ -7594,7 +7594,7 @@ Link has been copied to the clipboard - Link has been copied to the clipboard + Bağlantı panoya kopyalandı apps/client/src/app/components/access-table/access-table.component.ts 65 @@ -7602,7 +7602,7 @@ Early Access - Early Access + Erken Erişim apps/client/src/app/components/admin-settings/admin-settings.component.html 16 @@ -7666,7 +7666,7 @@ end of day - end of day + gün sonu apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 93 @@ -7674,7 +7674,7 @@ real-time - real-time + gerçek zamanlı apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 97 @@ -7682,7 +7682,7 @@ Open Duck.ai - Open Duck.ai + Duck.ai'yi aç apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts 174 @@ -7690,7 +7690,7 @@ Create - Create + Oluştur libs/ui/src/lib/tags-selector/tags-selector.component.html 50 @@ -7698,7 +7698,7 @@ Market Data - Market Data + Piyasa Verileri apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 374 @@ -7706,7 +7706,7 @@ Change - Change + Değişim libs/ui/src/lib/treemap-chart/treemap-chart.component.ts 365 @@ -7714,7 +7714,7 @@ Performance - Performance + Performans libs/ui/src/lib/treemap-chart/treemap-chart.component.ts 365 @@ -7726,7 +7726,7 @@ Copy portfolio data to clipboard for AI prompt - Copy portfolio data to clipboard for AI prompt + Yapay zeka istemi için portföy verilerini panoya kopyalayın apps/client/src/app/pages/portfolio/analysis/analysis-page.html 42 @@ -7734,7 +7734,7 @@ Copy AI prompt to clipboard for analysis - Copy AI prompt to clipboard for analysis + Yapay zeka istemini analiz için panoya kopyala apps/client/src/app/pages/portfolio/analysis/analysis-page.html 67 @@ -7742,7 +7742,7 @@ Armenia - Armenia + Ermenistan libs/ui/src/lib/i18n.ts 73 @@ -7750,7 +7750,7 @@ British Virgin Islands - British Virgin Islands + Britanya Virjin Adaları libs/ui/src/lib/i18n.ts 77 @@ -7758,7 +7758,7 @@ Singapore - Singapore + Singapur libs/ui/src/lib/i18n.ts 91 @@ -7766,7 +7766,7 @@ Terms and Conditions - Terms and Conditions + Hükümler ve Koşullar apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.html 10 @@ -7774,7 +7774,7 @@ Please keep your security token safe. If you lose it, you will not be able to recover your account. - Please keep your security token safe. If you lose it, you will not be able to recover your account. + Lütfen güvenlik tokenınızı güvende tutun. Kaybetmeniz halinde hesabınızı kurtarmanız mümkün olmayacaktır. apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.html 13 @@ -7782,7 +7782,7 @@ I understand that if I lose my security token, I cannot recover my account. - I understand that if I lose my security token, I cannot recover my account. + Güvenlik belirtecimi kaybedersem hesabımı kurtaramayacağımı anlıyorum. apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.html 23 @@ -7790,7 +7790,7 @@ Continue - Continue + Devam et apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.html 38 @@ -7798,7 +7798,7 @@ Here is your security token. It is only visible once, please store and keep it in a safe place. - Here is your security token. It is only visible once, please store and keep it in a safe place. + İşte güvenlik belirteciniz. Yalnızca bir kez görülebilir, lütfen saklayın ve güvenli bir yerde muhafaza edin. apps/client/src/app/pages/register/show-access-token-dialog/show-access-token-dialog.html 47 From 6c624fefc9bfb63ea61993c1b2b8816c83f959d4 Mon Sep 17 00:00:00 2001 From: Sayed Murtadha Ahmed Date: Sat, 22 Mar 2025 12:21:28 +0300 Subject: [PATCH 29/56] Feature/eliminate firstOrderDate in favor of dateOfFirstActivity in portfolio summary component (#4462) * Eliminate firstOrderDate in favor of dateOfFirstActivity in portfolio summary component * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/portfolio/portfolio.service.ts | 1 - apps/api/src/helper/object.helper.spec.ts | 2 -- .../portfolio-summary/portfolio-summary.component.ts | 4 ++-- apps/client/src/app/services/data.service.ts | 6 ------ .../src/lib/interfaces/portfolio-summary.interface.ts | 1 - 6 files changed, 3 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5d72f986..d0b69880f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the export functionality by applying filters on accounts and tags - Improved the symbol validation in the _Yahoo Finance_ service (get asset profiles) +- Eliminated `firstOrderDate` from the summary of the portfolio details endpoint in favor of using `dateOfFirstActivity` from the user endpoint - Refactored `lodash.uniq` with `Array.from(new Set(...))` - Refreshed the cryptocurrencies list - Improved the language localization for Turkish (`tr`) diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index a3d9e3c4a..c7eff27d3 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -1918,7 +1918,6 @@ export class PortfolioService { annualizedPerformancePercentWithCurrencyEffect, cash, excludedAccountsAndActivities, - firstOrderDate, netPerformance, netPerformancePercentage, netPerformancePercentageWithCurrencyEffect, diff --git a/apps/api/src/helper/object.helper.spec.ts b/apps/api/src/helper/object.helper.spec.ts index 85fb8f4eb..b0370fa3f 100644 --- a/apps/api/src/helper/object.helper.spec.ts +++ b/apps/api/src/helper/object.helper.spec.ts @@ -1519,7 +1519,6 @@ describe('redactAttributes', () => { annualizedPerformancePercentWithCurrencyEffect: 0.1694019484552876, cash: null, excludedAccountsAndActivities: null, - firstOrderDate: '2017-01-02T23:00:00.000Z', netPerformance: null, netPerformancePercentage: 2.3039314216696174, netPerformancePercentageWithCurrencyEffect: 2.3589806001456606, @@ -3023,7 +3022,6 @@ describe('redactAttributes', () => { annualizedPerformancePercentWithCurrencyEffect: 0.1694019484552876, cash: null, excludedAccountsAndActivities: null, - firstOrderDate: '2017-01-02T23:00:00.000Z', netPerformance: null, netPerformancePercentage: 2.3039314216696174, netPerformancePercentageWithCurrencyEffect: 2.3589806001456606, 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 25f7d9307..a44eacc93 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 @@ -40,8 +40,8 @@ export class PortfolioSummaryComponent implements OnChanges { public ngOnChanges() { if (this.summary) { - if (this.summary.firstOrderDate) { - this.timeInMarket = formatDistanceToNow(this.summary.firstOrderDate, { + if (this.user.dateOfFirstActivity) { + this.timeInMarket = formatDistanceToNow(this.user.dateOfFirstActivity, { locale: getDateFnsLocale(this.language) }); } else { diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 4eba3fffa..5ee49d63f 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -529,12 +529,6 @@ export class DataService { }) .pipe( map((response) => { - if (response.summary?.firstOrderDate) { - response.summary.firstOrderDate = parseISO( - response.summary.firstOrderDate - ); - } - if (response.holdings) { for (const symbol of Object.keys(response.holdings)) { response.holdings[symbol].assetClassLabel = translate( diff --git a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts index 5b27f4c7e..42496b228 100644 --- a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts +++ b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts @@ -16,7 +16,6 @@ export interface PortfolioSummary extends PortfolioPerformance { filteredValueInBaseCurrency?: number; filteredValueInPercentage?: number; fireWealth: number; - firstOrderDate: Date; grossPerformance: number; grossPerformanceWithCurrencyEffect: number; interest: number; From 4842c347a9c893b3629ce0749bb71243e6989129 Mon Sep 17 00:00:00 2001 From: csehatt741 <77381875+csehatt741@users.noreply.github.com> Date: Sat, 22 Mar 2025 10:56:01 +0100 Subject: [PATCH 30/56] Feature/generate new security token for user via admin control panel (#4458) * Generate new security token for user via admin control panel * Update changelog --------- Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> --- CHANGELOG.md | 1 + apps/api/src/app/auth/auth.service.ts | 8 ++-- apps/api/src/app/user/user.controller.ts | 35 ++++++++++++--- apps/api/src/app/user/user.service.ts | 35 +++++++++++---- .../admin-users/admin-users.component.ts | 45 +++++++++++++++---- .../components/admin-users/admin-users.html | 11 ++++- apps/client/src/app/services/data.service.ts | 8 ++++ .../src/app/services/user/user.service.ts | 4 +- libs/common/src/lib/interfaces/index.ts | 2 + .../access-token-response.interface.ts | 3 ++ 10 files changed, 123 insertions(+), 29 deletions(-) create mode 100644 libs/common/src/lib/interfaces/responses/access-token-response.interface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d0b69880f..8ec1fd9ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support for filtering in the _Copy AI prompt to clipboard_ actions on the analysis page (experimental) +- Added support for generating a new _Security Token_ via the users table of the admin control panel - Added an endpoint to localize the `site.webmanifest` - Added the _Storybook_ path to the `sitemap.xml` file diff --git a/apps/api/src/app/auth/auth.service.ts b/apps/api/src/app/auth/auth.service.ts index edfb22b6f..ceff492a0 100644 --- a/apps/api/src/app/auth/auth.service.ts +++ b/apps/api/src/app/auth/auth.service.ts @@ -20,10 +20,10 @@ export class AuthService { public async validateAnonymousLogin(accessToken: string): Promise { return new Promise(async (resolve, reject) => { try { - const hashedAccessToken = this.userService.createAccessToken( - accessToken, - this.configurationService.get('ACCESS_TOKEN_SALT') - ); + const hashedAccessToken = this.userService.createAccessToken({ + password: accessToken, + salt: this.configurationService.get('ACCESS_TOKEN_SALT') + }); const [user] = await this.userService.users({ where: { accessToken: hashedAccessToken } diff --git a/apps/api/src/app/user/user.controller.ts b/apps/api/src/app/user/user.controller.ts index 149e06285..868af505b 100644 --- a/apps/api/src/app/user/user.controller.ts +++ b/apps/api/src/app/user/user.controller.ts @@ -1,8 +1,13 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; +import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; -import { User, UserSettings } from '@ghostfolio/common/interfaces'; +import { + AccessTokenResponse, + User, + UserSettings +} from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import type { RequestWithUser } from '@ghostfolio/common/types'; @@ -36,6 +41,7 @@ export class UserController { public constructor( private readonly configurationService: ConfigurationService, private readonly jwtService: JwtService, + private readonly prismaService: PrismaService, private readonly propertyService: PropertyService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly userService: UserService @@ -47,10 +53,10 @@ export class UserController { public async deleteOwnUser( @Body() data: DeleteOwnUserDto ): Promise { - const hashedAccessToken = this.userService.createAccessToken( - data.accessToken, - this.configurationService.get('ACCESS_TOKEN_SALT') - ); + const hashedAccessToken = this.userService.createAccessToken({ + password: data.accessToken, + salt: this.configurationService.get('ACCESS_TOKEN_SALT') + }); const [user] = await this.userService.users({ where: { accessToken: hashedAccessToken, id: this.request.user.id } @@ -85,6 +91,25 @@ export class UserController { }); } + @HasPermission(permissions.accessAdminControl) + @Post(':id/access-token') + @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + public async generateAccessToken( + @Param('id') id: string + ): Promise { + const { accessToken, hashedAccessToken } = + this.userService.generateAccessToken({ + userId: id + }); + + await this.prismaService.user.update({ + data: { accessToken: hashedAccessToken }, + where: { id } + }); + + return { accessToken }; + } + @Get() @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async getUser( diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 40bc1b2b5..e9b8078b1 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -67,13 +67,33 @@ export class UserService { return this.prismaService.user.count(args); } - public createAccessToken(password: string, salt: string): string { + public createAccessToken({ + password, + salt + }: { + password: string; + salt: string; + }): string { const hash = createHmac('sha512', salt); hash.update(password); return hash.digest('hex'); } + public generateAccessToken({ userId }: { userId: string }) { + const accessToken = this.createAccessToken({ + password: userId, + salt: getRandomString(10) + }); + + const hashedAccessToken = this.createAccessToken({ + password: accessToken, + salt: this.configurationService.get('ACCESS_TOKEN_SALT') + }); + + return { accessToken, hashedAccessToken }; + } + public async getUser( { Account, id, permissions, Settings, subscription }: UserWithSettings, aLocale = locale @@ -433,7 +453,7 @@ export class UserService { data.provider = 'ANONYMOUS'; } - let user = await this.prismaService.user.create({ + const user = await this.prismaService.user.create({ data: { ...data, Account: { @@ -464,14 +484,11 @@ export class UserService { } if (data.provider === 'ANONYMOUS') { - const accessToken = this.createAccessToken(user.id, getRandomString(10)); - - const hashedAccessToken = this.createAccessToken( - accessToken, - this.configurationService.get('ACCESS_TOKEN_SALT') - ); + const { accessToken, hashedAccessToken } = this.generateAccessToken({ + userId: user.id + }); - user = await this.prismaService.user.update({ + await this.prismaService.user.update({ data: { accessToken: hashedAccessToken }, where: { id: user.id } }); diff --git a/apps/client/src/app/components/admin-users/admin-users.component.ts b/apps/client/src/app/components/admin-users/admin-users.component.ts index 50b8cb5f3..e1cd3102c 100644 --- a/apps/client/src/app/components/admin-users/admin-users.component.ts +++ b/apps/client/src/app/components/admin-users/admin-users.component.ts @@ -1,9 +1,4 @@ -import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; -import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; -import { AdminService } from '@ghostfolio/client/services/admin.service'; -import { DataService } from '@ghostfolio/client/services/data.service'; -import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; -import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { getDateFormatString, getEmojiFlag } from '@ghostfolio/common/helper'; import { AdminUsers, InfoItem, User } from '@ghostfolio/common/interfaces'; @@ -26,11 +21,18 @@ import { import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { ConfirmationDialogType } from '../../core/notification/confirmation-dialog/confirmation-dialog.type'; +import { NotificationService } from '../../core/notification/notification.service'; +import { AdminService } from '../../services/admin.service'; +import { DataService } from '../../services/data.service'; +import { ImpersonationStorageService } from '../../services/impersonation-storage.service'; +import { UserService } from '../../services/user/user.service'; + @Component({ selector: 'gf-admin-users', + standalone: false, styleUrls: ['./admin-users.scss'], - templateUrl: './admin-users.html', - standalone: false + templateUrl: './admin-users.html' }) export class AdminUsersComponent implements OnDestroy, OnInit { @ViewChild(MatPaginator) paginator: MatPaginator; @@ -55,6 +57,7 @@ export class AdminUsersComponent implements OnDestroy, OnInit { private dataService: DataService, private impersonationStorageService: ImpersonationStorageService, private notificationService: NotificationService, + private tokenStorageService: TokenStorageService, private userService: UserService ) { this.info = this.dataService.fetchInfo(); @@ -140,6 +143,32 @@ export class AdminUsersComponent implements OnDestroy, OnInit { }); } + public onGenerateAccessToken(aUserId: string) { + this.notificationService.confirm({ + confirmFn: () => { + this.dataService + .generateAccessToken(aUserId) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ accessToken }) => { + this.notificationService.alert({ + discardFn: () => { + if (aUserId === this.user.id) { + this.tokenStorageService.signOut(); + this.userService.remove(); + + document.location.href = `/${document.documentElement.lang}`; + } + }, + message: accessToken, + title: $localize`Security token` + }); + }); + }, + confirmType: ConfirmationDialogType.Warn, + title: $localize`Do you really want to generate a new security token for this user?` + }); + } + public onImpersonateUser(aId: string) { if (aId) { this.impersonationStorageService.setId(aId); diff --git a/apps/client/src/app/components/admin-users/admin-users.html b/apps/client/src/app/components/admin-users/admin-users.html index ca8ef0558..e8725e70c 100644 --- a/apps/client/src/app/components/admin-users/admin-users.html +++ b/apps/client/src/app/components/admin-users/admin-users.html @@ -239,8 +239,17 @@ Impersonate User -
} + +
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 a767a5796..9e8855d71 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 @@ -308,7 +308,7 @@
- + Terms and Conditions
From a437a1c72216fb80eafa612d3c69d51f7886b98f Mon Sep 17 00:00:00 2001 From: tobikugel Date: Mon, 17 Mar 2025 11:33:11 -0300 Subject: [PATCH 44/56] add symbol-autocomplete-comp, change assetProfileForm coverage, refactor endpoint --- apps/api/src/app/admin/admin.controller.ts | 11 +- apps/api/src/app/admin/admin.service.ts | 125 +++-- .../src/app/admin/update-asset-profile.dto.ts | 10 +- .../market-data/market-data.service.ts | 22 + .../symbol-profile/symbol-profile.service.ts | 89 +++- .../asset-profile-dialog.component.ts | 20 +- .../asset-profile-dialog.html | 498 ++++++++++-------- .../asset-profile-dialog.module.ts | 2 + 8 files changed, 489 insertions(+), 288 deletions(-) diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 409ce7160..5cc6caf61 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -338,11 +338,12 @@ export class AdminController { @Param('dataSource') dataSource: DataSource, @Param('symbol') symbol: string ): Promise { - return this.adminService.patchAssetProfileData({ - ...assetProfileData, - dataSource, - symbol - }); + return this.adminService.patchAssetProfileData( + { dataSource, symbol }, + { + ...assetProfileData + } + ); } @HasPermission(permissions.accessAdminControl) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index edb96a28e..502bf570e 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -463,61 +463,102 @@ export class AdminService { return { count, users }; } - public async patchAssetProfileData({ - assetClass, - assetSubClass, - comment, - countries, - currency, - dataSource, - holdings, - name, - scraperConfiguration, - sectors, - symbol, - symbolMapping, - url - }: AssetProfileIdentifier & Prisma.SymbolProfileUpdateInput) { - const symbolProfileOverrides = { - assetClass: assetClass as AssetClass, - assetSubClass: assetSubClass as AssetSubClass, - name: name as string, - url: url as string - }; - - const updatedSymbolProfile: AssetProfileIdentifier & - Prisma.SymbolProfileUpdateInput = { + public async patchAssetProfileData( + assetProfileIdentifier: AssetProfileIdentifier, + { + assetClass, + assetSubClass, comment, countries, currency, dataSource, holdings, + name, scraperConfiguration, sectors, symbol, symbolMapping, - ...(dataSource === 'MANUAL' - ? { assetClass, assetSubClass, name, url } - : { - SymbolProfileOverrides: { - upsert: { - create: symbolProfileOverrides, - update: symbolProfileOverrides - } - } - }) - }; + url + }: Prisma.SymbolProfileUpdateInput + ) { + if ( + symbol && + dataSource && + assetProfileIdentifier.symbol !== symbol && + assetProfileIdentifier.dataSource !== dataSource + ) { + await this.symbolProfileService.updateAssetProfileIdentifier( + assetProfileIdentifier, + { + dataSource: dataSource as DataSource, // TODO change + symbol: symbol as string + } + ); + + await this.marketDataService.updateAssetProfileIdentifier( + assetProfileIdentifier, + { + dataSource: dataSource as DataSource, + symbol: symbol as string + } + ); - await this.symbolProfileService.updateSymbolProfile(updatedSymbolProfile); + const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( + [ + { + dataSource: dataSource as DataSource, + symbol: symbol as string + } + ] + ); - const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles([ - { + return symbolProfile; + } else { + const symbolProfileOverrides = { + assetClass: assetClass as AssetClass, + assetSubClass: assetSubClass as AssetSubClass, + name: name as string, + url: url as string + }; + + const updatedSymbolProfile: Prisma.SymbolProfileUpdateInput = { + comment, + countries, + currency, dataSource, - symbol - } - ]); + holdings, + scraperConfiguration, + sectors, + symbol, + symbolMapping, + ...(dataSource === 'MANUAL' + ? { assetClass, assetSubClass, name, url } + : { + SymbolProfileOverrides: { + upsert: { + create: symbolProfileOverrides, + update: symbolProfileOverrides + } + } + }) + }; + + await this.symbolProfileService.updateSymbolProfile( + assetProfileIdentifier, + updatedSymbolProfile + ); - return symbolProfile; + const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( + [ + { + dataSource: dataSource as DataSource, + symbol: symbol as string + } + ] + ); + + return symbolProfile; + } } public async putSetting(key: string, value: string) { diff --git a/apps/api/src/app/admin/update-asset-profile.dto.ts b/apps/api/src/app/admin/update-asset-profile.dto.ts index 8c9ae220b..c9448e46f 100644 --- a/apps/api/src/app/admin/update-asset-profile.dto.ts +++ b/apps/api/src/app/admin/update-asset-profile.dto.ts @@ -1,6 +1,6 @@ import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; -import { AssetClass, AssetSubClass, Prisma } from '@prisma/client'; +import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client'; import { IsArray, IsEnum, @@ -35,6 +35,14 @@ export class UpdateAssetProfileDto { @IsOptional() name?: string; + @IsEnum(DataSource, { each: true }) + @IsOptional() + dataSource?: DataSource; + + @IsString() + @IsOptional() + symbol?: string; + @IsObject() @IsOptional() scraperConfiguration?: Prisma.InputJsonObject; diff --git a/apps/api/src/services/market-data/market-data.service.ts b/apps/api/src/services/market-data/market-data.service.ts index c0abdf04e..0cab0bcc0 100644 --- a/apps/api/src/services/market-data/market-data.service.ts +++ b/apps/api/src/services/market-data/market-data.service.ts @@ -110,6 +110,28 @@ export class MarketDataService { }); } + public async updateAssetProfileIdentifier( + oldAssetProfileIdentifier: AssetProfileIdentifier, + newAssetProfileIdentifier: AssetProfileIdentifier + ) { + return this.prismaService.marketData.updateMany({ + data: { + dataSource: newAssetProfileIdentifier.dataSource, + symbol: newAssetProfileIdentifier.symbol + }, + where: { + AND: [ + { + dataSource: oldAssetProfileIdentifier.dataSource + }, + { + symbol: oldAssetProfileIdentifier.symbol + } + ] + } + }); + } + public async updateMarketData(params: { data: { state: MarketDataState; diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index e9c568cef..65394eef5 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -126,23 +126,78 @@ export class SymbolProfileService { }); } - public updateSymbolProfile({ - assetClass, - assetSubClass, - comment, - countries, - currency, - dataSource, - holdings, - isActive, - name, - scraperConfiguration, - sectors, - symbol, - symbolMapping, - SymbolProfileOverrides, - url - }: AssetProfileIdentifier & Prisma.SymbolProfileUpdateInput) { + public async getSymbolProfilesByUserSubscription({ + withUserSubscription = false + }: { + withUserSubscription?: boolean; + }) { + return this.prismaService.symbolProfile.findMany({ + include: { + Order: { + include: { + User: true + } + } + }, + orderBy: [{ symbol: 'asc' }], + where: { + Order: withUserSubscription + ? { + some: { + User: { + Subscription: { some: { expiresAt: { gt: new Date() } } } + } + } + } + : { + every: { + User: { + Subscription: { none: { expiresAt: { gt: new Date() } } } + } + } + } + } + }); + } + + public updateAssetProfileIdentifier( + oldAssetProfileIdentifier: AssetProfileIdentifier, + newAssetProfileIdentifier: AssetProfileIdentifier + ) { + return this.prismaService.symbolProfile.update({ + data: { + dataSource: newAssetProfileIdentifier.dataSource, + symbol: newAssetProfileIdentifier.symbol + }, + where: { + dataSource_symbol: { + dataSource: oldAssetProfileIdentifier.dataSource, + symbol: oldAssetProfileIdentifier.symbol + } + } + }); + } + + public updateSymbolProfile( + { dataSource, symbol }: AssetProfileIdentifier, + { + assetClass, + assetSubClass, + comment, + countries, + currency, + //dataSource, + holdings, + isActive, + name, + scraperConfiguration, + sectors, + //symbol, + symbolMapping, + SymbolProfileOverrides, + url + }: Prisma.SymbolProfileUpdateInput + ) { return this.prismaService.symbolProfile.update({ data: { assetClass, 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 1467a1ba3..5b966ed74 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 @@ -55,6 +55,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { return { id: assetSubClass, label: translate(assetSubClass) }; }); public assetProfile: AdminMarketDataDetails['assetProfile']; + public assetProfileIdentifierForm; public assetProfileForm = this.formBuilder.group({ assetClass: new FormControl(undefined), assetSubClass: new FormControl(undefined), @@ -86,6 +87,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public ghostfolioScraperApiSymbolPrefix = ghostfolioScraperApiSymbolPrefix; public historicalDataItems: LineChartItem[]; public isBenchmark = false; + public isEditSymbolMode = false; public marketDataItems: MarketData[] = []; public modeValues = [ { @@ -269,7 +271,23 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } - public async onSubmit() { + public onSetEditSymboleMode() { + this.isEditSymbolMode = true; + + this.assetProfileForm.disable(); + + this.changeDetectorRef.markForCheck(); + } + + public onCancelEditSymboleMode() { + this.isEditSymbolMode = false; + + this.assetProfileForm.enable(); + + this.changeDetectorRef.markForCheck(); + } + + public async onSubmitAssetProfileForm() { let countries = []; let scraperConfiguration = {}; let sectors = []; diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 595ec28cb..a0c0153de 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -1,9 +1,4 @@ -
+

{{ assetProfile?.name ?? data.symbol }} @@ -91,21 +86,55 @@ />
-
- Symbol -
-
- Data Source -
+ @if (isEditSymbolMode) { +
+ + Name, symbol or ISIN + + + + +
+ } @else { +
+ Symbol +
+
+ Data Source +
+
+ +
+ }
Currency -
- - Name - - -
- @if (assetProfile?.dataSource === 'MANUAL') { +
- Currency - + Name +
- } -
- - Asset Class - - - @for (assetClass of assetClasses; track assetClass) { - {{ - assetClass.label - }} - } - - -
-
- - Asset Sub Class - - - @for (assetSubClass of assetSubClasses; track assetSubClass) { - {{ - assetSubClass.label - }} - } - - -
-
-
- Benchmark + @if (assetProfile?.dataSource === 'MANUAL') { +
+ + Currency + + +
+ } +
+ + Asset Class + + + @for (assetClass of assetClasses; track assetClass) { + {{ + assetClass.label + }} + } + +
-
-
- - Symbol Mapping - - -
- @if (assetProfile?.dataSource === 'MANUAL') { -
- - + + Asset Sub Class + + + @for (assetSubClass of assetSubClasses; track assetSubClass) { + {{ + assetSubClass.label + }} + } + + +
+
+
+ Benchmark - - Scraper Configuration - -
-
- - Default Market Price - - -
-
- - HTTP Request Headers - - -
-
- - Locale - - -
-
- - Mode - - @for (modeValue of modeValues; track modeValue) { - {{ - modeValue.viewValue - }} - } - - -
-
- - - Selector* - - - -
-
- - - Url* - - - -
-
- -
-
- - +
- } - @if (assetProfile?.dataSource === 'MANUAL') { -
+
- Sectors + Symbol Mapping
+ @if (assetProfile?.dataSource === 'MANUAL') { +
+ + + + Scraper Configuration + +
+
+ + Default Market Price + + +
+
+ + HTTP Request Headers + + +
+
+ + Locale + + +
+
+ + Mode + + @for (modeValue of modeValues; track modeValue) { + {{ + modeValue.viewValue + }} + } + + +
+
+ + + Selector* + + + +
+
+ + + Url* + + + +
+
+ +
+
+
+
+
+ } + @if (assetProfile?.dataSource === 'MANUAL') { +
+ + Sectors + + +
+
+ + Countries + + +
+ }
+ + Url + + @if (assetProfileForm.get('url').value) { + + } + +
+
- Countries + Note
- } -
- - Url - - @if (assetProfileForm.get('url').value) { - - } - -
-
- - Note - - -
+
@@ -439,4 +493,4 @@ Save
- +
diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts index 9b9876dbc..11818381f 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts @@ -4,6 +4,7 @@ import { GfCurrencySelectorComponent } from '@ghostfolio/ui/currency-selector'; import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor'; import { GfLineChartComponent } from '@ghostfolio/ui/line-chart'; import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart'; +import { GfSymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplete'; import { GfValueComponent } from '@ghostfolio/ui/value'; import { TextFieldModule } from '@angular/cdk/text-field'; @@ -30,6 +31,7 @@ import { AssetProfileDialog } from './asset-profile-dialog.component'; GfCurrencySelectorComponent, GfHistoricalMarketDataEditorComponent, GfLineChartComponent, + GfSymbolAutocompleteComponent, GfPortfolioProportionChartComponent, GfValueComponent, MatButtonModule, From d3fe2c0720e8e8d33c8bac78079324330dbc97cb Mon Sep 17 00:00:00 2001 From: tobikugel Date: Tue, 18 Mar 2025 10:43:15 -0300 Subject: [PATCH 45/56] feat: implements frontend logic --- apps/api/src/app/admin/admin.controller.ts | 9 ++-- apps/api/src/app/admin/admin.service.ts | 37 ++++++++----- .../asset-profile-dialog.component.ts | 52 ++++++++++++++++-- .../asset-profile-dialog.html | 54 ++++++++++++------- apps/client/src/app/services/admin.service.ts | 34 +++++++----- 5 files changed, 129 insertions(+), 57 deletions(-) diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 5cc6caf61..e2a29d451 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -338,12 +338,9 @@ export class AdminController { @Param('dataSource') dataSource: DataSource, @Param('symbol') symbol: string ): Promise { - return this.adminService.patchAssetProfileData( - { dataSource, symbol }, - { - ...assetProfileData - } - ); + return this.adminService.patchAssetProfileData(dataSource, symbol, { + ...assetProfileData + }); } @HasPermission(permissions.accessAdminControl) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 502bf570e..864f4ecda 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -464,42 +464,48 @@ export class AdminService { } public async patchAssetProfileData( - assetProfileIdentifier: AssetProfileIdentifier, + dataSource: DataSource, + symbol: string, { assetClass, assetSubClass, comment, countries, currency, - dataSource, + dataSource: newDataSource, holdings, name, scraperConfiguration, sectors, - symbol, + symbol: newSymbol, symbolMapping, url }: Prisma.SymbolProfileUpdateInput ) { if ( - symbol && - dataSource && - assetProfileIdentifier.symbol !== symbol && - assetProfileIdentifier.dataSource !== dataSource + newSymbol && + newDataSource && + (newSymbol !== symbol || newDataSource !== dataSource) ) { await this.symbolProfileService.updateAssetProfileIdentifier( - assetProfileIdentifier, { - dataSource: dataSource as DataSource, // TODO change - symbol: symbol as string + dataSource, + symbol + }, + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string } ); await this.marketDataService.updateAssetProfileIdentifier( - assetProfileIdentifier, { - dataSource: dataSource as DataSource, - symbol: symbol as string + dataSource, + symbol + }, + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string } ); @@ -544,7 +550,10 @@ export class AdminService { }; await this.symbolProfileService.updateSymbolProfile( - assetProfileIdentifier, + { + dataSource, + symbol + }, updatedSymbolProfile ); 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 5b966ed74..443f6a7aa 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 @@ -55,7 +55,12 @@ export class AssetProfileDialog implements OnDestroy, OnInit { return { id: assetSubClass, label: translate(assetSubClass) }; }); public assetProfile: AdminMarketDataDetails['assetProfile']; - public assetProfileIdentifierForm; + public assetProfileIdentifierForm = this.formBuilder.group({ + editedSearchSymbol: new FormControl( + { symbol: null, dataSource: null }, + [Validators.required] + ) + }); public assetProfileForm = this.formBuilder.group({ assetClass: new FormControl(undefined), assetSubClass: new FormControl(undefined), @@ -225,6 +230,14 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } + public get isSymbolEditable() { + return !this.assetProfileForm.dirty; + } + + public get isSymbolEditButtonInvisible() { + return this.assetProfile?.dataSource === 'MANUAL'; + } + public onClose() { this.dialogRef.close(); } @@ -284,9 +297,40 @@ export class AssetProfileDialog implements OnDestroy, OnInit { this.assetProfileForm.enable(); + this.assetProfileIdentifierForm.reset(); + this.changeDetectorRef.markForCheck(); } + public async onSubmitAssetProfileIdentifierForm() { + const assetProfileIdentifierData: UpdateAssetProfileDto = { + dataSource: + this.assetProfileIdentifierForm.get('editedSearchSymbol').value + .dataSource, + symbol: + this.assetProfileIdentifierForm.get('editedSearchSymbol').value.symbol + }; + + try { + await validateObjectForForm({ + classDto: UpdateAssetProfileDto, + form: this.assetProfileIdentifierForm, + object: assetProfileIdentifierData + }); + } catch (error) { + console.error(error); + return; + } + + this.adminService + .patchAssetProfile(this.data.dataSource, this.data.symbol, { + ...assetProfileIdentifierData + }) + .subscribe(() => { + this.initialize(); + }); + } + public async onSubmitAssetProfileForm() { let countries = []; let scraperConfiguration = {}; @@ -360,10 +404,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { } this.adminService - .patchAssetProfile({ - ...assetProfileData, - dataSource: this.data.dataSource, - symbol: this.data.symbol + .patchAssetProfile(this.data.dataSource, this.data.symbol, { + ...assetProfileData }) .subscribe(() => { this.initialize(); diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index a0c0153de..4c1fe9ed3 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -88,24 +88,39 @@
@if (isEditSymbolMode) {
- - Name, symbol or ISIN - - - - + + Name, symbol or ISIN + + + + +
} @else {
@@ -128,7 +143,10 @@ class="mx-1 no-min-width px-2" mat-button type="button" - [disabled]="assetProfileForm.dirty" + [disabled]="!isSymbolEditable" + [ngClass]="{ + 'd-none': isSymbolEditButtonInvisible + }" (click)="onSetEditSymboleMode()" > diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index fea3924e9..7a1e148db 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -203,20 +203,24 @@ export class AdminService { return this.http.get(url); } - public patchAssetProfile({ - assetClass, - assetSubClass, - comment, - countries, - currency, - dataSource, - name, - scraperConfiguration, - sectors, - symbol, - symbolMapping, - url - }: AssetProfileIdentifier & UpdateAssetProfileDto) { + public patchAssetProfile( + dataSource: DataSource, + symbol: string, + { + assetClass, + assetSubClass, + comment, + countries, + currency, + dataSource: newDataSource, + name, + scraperConfiguration, + sectors, + symbol: newSymbol, + symbolMapping, + url + }: UpdateAssetProfileDto + ) { return this.http.patch( `/api/v1/admin/profile-data/${dataSource}/${symbol}`, { @@ -226,6 +230,8 @@ export class AdminService { countries, currency, name, + newDataSource, + newSymbol, scraperConfiguration, sectors, symbolMapping, From 5ce488b49b488e05f624d9212801fd1a9413fda9 Mon Sep 17 00:00:00 2001 From: tobikugel Date: Thu, 20 Mar 2025 12:13:36 -0300 Subject: [PATCH 46/56] feat: implements form logic, adujsts backend admin service --- apps/api/src/app/admin/admin.service.ts | 74 +++++++++++++------ .../symbol-profile/symbol-profile.service.ts | 2 - .../asset-profile-dialog.component.ts | 46 +++++++++++- .../asset-profile-dialog.html | 3 +- apps/client/src/app/services/admin.service.ts | 4 +- 5 files changed, 99 insertions(+), 30 deletions(-) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 864f4ecda..ca2ed498d 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -32,7 +32,12 @@ import { import { Sector } from '@ghostfolio/common/interfaces/sector.interface'; import { MarketDataPreset } from '@ghostfolio/common/types'; -import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { + BadRequestException, + HttpException, + Injectable, + Logger +} from '@nestjs/common'; import { AssetClass, AssetSubClass, @@ -42,6 +47,7 @@ import { SymbolProfile } from '@prisma/client'; import { differenceInDays } from 'date-fns'; +import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { groupBy } from 'lodash'; @Injectable() @@ -487,38 +493,58 @@ export class AdminService { newDataSource && (newSymbol !== symbol || newDataSource !== dataSource) ) { - await this.symbolProfileService.updateAssetProfileIdentifier( - { - dataSource, - symbol - }, + const [profile] = await this.symbolProfileService.getSymbolProfiles([ { dataSource: newDataSource as DataSource, symbol: newSymbol as string } - ); + ]); - await this.marketDataService.updateAssetProfileIdentifier( - { - dataSource, - symbol - }, - { - dataSource: newDataSource as DataSource, - symbol: newSymbol as string - } - ); + if (profile) { + throw new HttpException( + getReasonPhrase(StatusCodes.CONFLICT), + StatusCodes.CONFLICT + ); + } - const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( - [ + try { + await this.symbolProfileService.updateAssetProfileIdentifier( { - dataSource: dataSource as DataSource, - symbol: symbol as string + dataSource, + symbol + }, + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string } - ] - ); + ); - return symbolProfile; + await this.marketDataService.updateAssetProfileIdentifier( + { + dataSource, + symbol + }, + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string + } + ); + + const [symbolProfile] = + await this.symbolProfileService.getSymbolProfiles([ + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string + } + ]); + + return symbolProfile; + } catch { + throw new HttpException( + getReasonPhrase(StatusCodes.BAD_REQUEST), + StatusCodes.BAD_REQUEST + ); + } } else { const symbolProfileOverrides = { assetClass: assetClass as AssetClass, diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index 65394eef5..c995b13d1 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -186,13 +186,11 @@ export class SymbolProfileService { comment, countries, currency, - //dataSource, holdings, isActive, name, scraperConfiguration, sectors, - //symbol, symbolMapping, SymbolProfileOverrides, url 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 443f6a7aa..7d925395f 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 @@ -19,13 +19,17 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + ElementRef, Inject, OnDestroy, OnInit, + ViewChild, signal } from '@angular/core'; import { FormBuilder, FormControl, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { Router } from '@angular/router'; import { AssetClass, AssetSubClass, @@ -33,6 +37,7 @@ import { SymbolProfile } from '@prisma/client'; import { format } from 'date-fns'; +import ms from 'ms'; import { EMPTY, Subject } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; @@ -47,6 +52,9 @@ import { AssetProfileDialogParams } from './interfaces/interfaces'; standalone: false }) export class AssetProfileDialog implements OnDestroy, OnInit { + @ViewChild('assetProfileFormElement') + assetProfileFormElement: ElementRef; + public assetProfileClass: string; public assetClasses = Object.keys(AssetClass).map((assetClass) => { return { id: assetClass, label: translate(assetClass) }; @@ -125,6 +133,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public dialogRef: MatDialogRef, private formBuilder: FormBuilder, private notificationService: NotificationService, + private router: Router, + private snackBar: MatSnackBar, private userService: UserService ) {} @@ -326,8 +336,23 @@ export class AssetProfileDialog implements OnDestroy, OnInit { .patchAssetProfile(this.data.dataSource, this.data.symbol, { ...assetProfileIdentifierData }) + .pipe( + catchError(() => { + this.snackBar.open('Conflict', undefined, { + duration: ms('3 seconds') + }); + + return EMPTY; + }), + takeUntil(this.unsubscribeSubject) + ) .subscribe(() => { - this.initialize(); + this.onOpenAssetProfileDialog({ + dataSource: assetProfileIdentifierData.dataSource, + symbol: assetProfileIdentifierData.symbol + }); + + this.dialogRef.close(); }); } @@ -482,4 +507,23 @@ export class AssetProfileDialog implements OnDestroy, OnInit { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } + + public onOpenAssetProfileDialog({ + dataSource, + symbol + }: AssetProfileIdentifier) { + this.router.navigate([], { + queryParams: { + dataSource, + symbol, + assetProfileDialog: true + } + }); + } + + public triggerIdentifierSubmit() { + if (this.assetProfileIdentifierForm) { + this.assetProfileFormElement.nativeElement.requestSubmit(); + } + } } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 4c1fe9ed3..b4effc127 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -250,6 +250,7 @@ }
Save diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 7a1e148db..e038f8164 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -230,8 +230,8 @@ export class AdminService { countries, currency, name, - newDataSource, - newSymbol, + dataSource: newDataSource, + symbol: newSymbol, scraperConfiguration, sectors, symbolMapping, From 1825a1f4b9cf1a92418d6334e82871ef8e1c9351 Mon Sep 17 00:00:00 2001 From: tobikugel Date: Fri, 21 Mar 2025 12:19:49 -0300 Subject: [PATCH 47/56] feat: frontend form logic and error handling --- .../admin-market-data.component.ts | 12 ++- .../asset-profile-dialog.component.ts | 74 +++++++++++++++---- .../asset-profile-dialog.html | 21 ++++-- .../symbol-autocomplete.component.ts | 3 + 4 files changed, 83 insertions(+), 27 deletions(-) diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts index 30ef457d2..bf067bd7f 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts @@ -389,9 +389,15 @@ export class AdminMarketDataComponent dialogRef .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(() => { - this.router.navigate(['.'], { relativeTo: this.route }); - }); + .subscribe( + (newAssetProfileIdentifer: AssetProfileIdentifier | undefined) => { + if (newAssetProfileIdentifer) { + this.onOpenAssetProfileDialog(newAssetProfileIdentifer); + } else { + this.router.navigate(['.'], { relativeTo: this.route }); + } + } + ); }); } 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 7d925395f..ea5fbd537 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 @@ -15,6 +15,7 @@ import { } from '@ghostfolio/common/interfaces'; import { translate } from '@ghostfolio/ui/i18n'; +import { HttpErrorResponse } from '@angular/common/http'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -26,7 +27,13 @@ import { ViewChild, signal } from '@angular/core'; -import { FormBuilder, FormControl, Validators } from '@angular/forms'; +import { + AbstractControl, + FormBuilder, + FormControl, + ValidationErrors, + Validators +} from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; @@ -63,12 +70,18 @@ export class AssetProfileDialog implements OnDestroy, OnInit { return { id: assetSubClass, label: translate(assetSubClass) }; }); public assetProfile: AdminMarketDataDetails['assetProfile']; - public assetProfileIdentifierForm = this.formBuilder.group({ - editedSearchSymbol: new FormControl( - { symbol: null, dataSource: null }, - [Validators.required] - ) - }); + public assetProfileIdentifierForm = this.formBuilder.group( + { + editedSearchSymbol: new FormControl( + { symbol: null, dataSource: null }, + [Validators.required] + ) + }, + { + validators: (control) => this.isNewSymbolValid(control) + } + ); + public assetProfileForm = this.formBuilder.group({ assetClass: new FormControl(undefined), assetSubClass: new FormControl(undefined), @@ -240,6 +253,21 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } + private isNewSymbolValid(control: AbstractControl): ValidationErrors { + const currentAssetProfileIdentifier: AssetProfileIdentifier | undefined = + control.get('editedSearchSymbol').value; + console.log(this.assetProfileIdentifierForm?.valid); + + if ( + currentAssetProfileIdentifier.dataSource === this.data?.dataSource && + currentAssetProfileIdentifier.symbol === this.data?.symbol + ) { + return { + isPreviousIdentifier: true + }; + } + } + public get isSymbolEditable() { return !this.assetProfileForm.dirty; } @@ -337,22 +365,36 @@ export class AssetProfileDialog implements OnDestroy, OnInit { ...assetProfileIdentifierData }) .pipe( - catchError(() => { - this.snackBar.open('Conflict', undefined, { - duration: ms('3 seconds') - }); + catchError((error: HttpErrorResponse) => { + if (error.status === 409) { + this.snackBar.open( + $localize`This symbol is already in use`, + undefined, + { + duration: ms('3 seconds') + } + ); + } else { + this.snackBar.open( + $localize`An error occurred while updating the symbol`, + undefined, + { + duration: ms('3 seconds') + } + ); + } return EMPTY; }), takeUntil(this.unsubscribeSubject) ) .subscribe(() => { - this.onOpenAssetProfileDialog({ + const newAssetProfileIdentifer = { dataSource: assetProfileIdentifierData.dataSource, symbol: assetProfileIdentifierData.symbol - }); + }; - this.dialogRef.close(); + this.dialogRef.close(newAssetProfileIdentifer); }); } @@ -521,8 +563,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } - public triggerIdentifierSubmit() { - if (this.assetProfileIdentifierForm) { + public triggerProfileFormSubmit() { + if (this.assetProfileForm) { this.assetProfileFormElement.nativeElement.requestSubmit(); } } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index b4effc127..ad58862d6 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -87,8 +87,9 @@
@if (isEditSymbolMode) { -
+
- + Name, symbol or ISIN Apply @@ -128,8 +134,9 @@ >Symbol
-
+
Data Source -
-
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 3c56c4748..f92635150 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts @@ -194,6 +194,9 @@ export class GfSymbolAutocompleteComponent } private validateRequired() { + console.log(`dataSource: ${super.value?.dataSource}`); + console.log(`symbol: ${super.value?.symbol}`); + const requiredCheck = super.required ? !super.value?.dataSource || !super.value?.symbol : false; From 6c84e7d9a817940c91e7bd93ad168ff4e0644f18 Mon Sep 17 00:00:00 2001 From: tobikugel Date: Fri, 21 Mar 2025 16:54:58 -0300 Subject: [PATCH 48/56] chore(refactor): change patch function arguments --- apps/api/src/app/admin/admin.controller.ts | 9 ++- apps/api/src/app/admin/admin.service.ts | 3 +- .../src/app/admin/update-asset-profile.dto.ts | 10 +-- .../asset-profile-dialog.component.ts | 65 +++++++++---------- .../asset-profile-dialog.html | 4 +- .../asset-profile-dialog.module.ts | 2 +- apps/client/src/app/services/admin.service.ts | 7 +- .../symbol-autocomplete.component.ts | 3 - 8 files changed, 49 insertions(+), 54 deletions(-) diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index e2a29d451..5cc6caf61 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -338,9 +338,12 @@ export class AdminController { @Param('dataSource') dataSource: DataSource, @Param('symbol') symbol: string ): Promise { - return this.adminService.patchAssetProfileData(dataSource, symbol, { - ...assetProfileData - }); + return this.adminService.patchAssetProfileData( + { dataSource, symbol }, + { + ...assetProfileData + } + ); } @HasPermission(permissions.accessAdminControl) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index ca2ed498d..9c1c8f109 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -470,8 +470,7 @@ export class AdminService { } public async patchAssetProfileData( - dataSource: DataSource, - symbol: string, + { dataSource, symbol }: AssetProfileIdentifier, { assetClass, assetSubClass, diff --git a/apps/api/src/app/admin/update-asset-profile.dto.ts b/apps/api/src/app/admin/update-asset-profile.dto.ts index c9448e46f..fbd2b3752 100644 --- a/apps/api/src/app/admin/update-asset-profile.dto.ts +++ b/apps/api/src/app/admin/update-asset-profile.dto.ts @@ -31,17 +31,13 @@ export class UpdateAssetProfileDto { @IsOptional() currency?: string; - @IsString() - @IsOptional() - name?: string; - @IsEnum(DataSource, { each: true }) @IsOptional() dataSource?: DataSource; @IsString() @IsOptional() - symbol?: string; + name?: string; @IsObject() @IsOptional() @@ -51,6 +47,10 @@ export class UpdateAssetProfileDto { @IsOptional() sectors?: Prisma.InputJsonArray; + @IsString() + @IsOptional() + symbol?: string; + @IsObject() @IsOptional() symbolMapping?: { 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 ea5fbd537..bc467f1b7 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 @@ -36,7 +36,6 @@ import { } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { Router } from '@angular/router'; import { AssetClass, AssetSubClass, @@ -146,7 +145,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public dialogRef: MatDialogRef, private formBuilder: FormBuilder, private notificationService: NotificationService, - private router: Router, private snackBar: MatSnackBar, private userService: UserService ) {} @@ -256,14 +254,13 @@ export class AssetProfileDialog implements OnDestroy, OnInit { private isNewSymbolValid(control: AbstractControl): ValidationErrors { const currentAssetProfileIdentifier: AssetProfileIdentifier | undefined = control.get('editedSearchSymbol').value; - console.log(this.assetProfileIdentifierForm?.valid); if ( currentAssetProfileIdentifier.dataSource === this.data?.dataSource && currentAssetProfileIdentifier.symbol === this.data?.symbol ) { return { - isPreviousIdentifier: true + equalsPreviousSymbol: true }; } } @@ -276,6 +273,16 @@ export class AssetProfileDialog implements OnDestroy, OnInit { return this.assetProfile?.dataSource === 'MANUAL'; } + public onCancelEditSymboleMode() { + this.isEditSymbolMode = false; + + this.assetProfileForm.enable(); + + this.assetProfileIdentifierForm.reset(); + + this.changeDetectorRef.markForCheck(); + } + public onClose() { this.dialogRef.close(); } @@ -330,16 +337,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit { this.changeDetectorRef.markForCheck(); } - public onCancelEditSymboleMode() { - this.isEditSymbolMode = false; - - this.assetProfileForm.enable(); - - this.assetProfileIdentifierForm.reset(); - - this.changeDetectorRef.markForCheck(); - } - public async onSubmitAssetProfileIdentifierForm() { const assetProfileIdentifierData: UpdateAssetProfileDto = { dataSource: @@ -357,13 +354,20 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } catch (error) { console.error(error); + return; } this.adminService - .patchAssetProfile(this.data.dataSource, this.data.symbol, { - ...assetProfileIdentifierData - }) + .patchAssetProfile( + { + dataSource: this.data.dataSource, + symbol: this.data.symbol + }, + { + ...assetProfileIdentifierData + } + ) .pipe( catchError((error: HttpErrorResponse) => { if (error.status === 409) { @@ -471,9 +475,15 @@ export class AssetProfileDialog implements OnDestroy, OnInit { } this.adminService - .patchAssetProfile(this.data.dataSource, this.data.symbol, { - ...assetProfileData - }) + .patchAssetProfile( + { + dataSource: this.data.dataSource, + symbol: this.data.symbol + }, + { + ...assetProfileData + } + ) .subscribe(() => { this.initialize(); }); @@ -550,20 +560,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { this.unsubscribeSubject.complete(); } - public onOpenAssetProfileDialog({ - dataSource, - symbol - }: AssetProfileIdentifier) { - this.router.navigate([], { - queryParams: { - dataSource, - symbol, - assetProfileDialog: true - } - }); - } - - public triggerProfileFormSubmit() { + public onTriggerSubmitAssetProfileForm() { if (this.assetProfileForm) { this.assetProfileFormElement.nativeElement.requestSubmit(); } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index ad58862d6..50e49df92 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -113,7 +113,7 @@ assetProfileIdentifierForm.hasError( 'invalidData', 'editedSearchSymbol' - ) || assetProfileIdentifierForm.hasError('isPreviousIdentifier') + ) || assetProfileIdentifierForm.hasError('equalsPreviousSymbol') " > Apply @@ -512,7 +512,7 @@ color="primary" mat-flat-button [disabled]="!(assetProfileForm.dirty && assetProfileForm.valid)" - (click)="triggerProfileFormSubmit()" + (click)="onTriggerSubmitAssetProfileForm()" > Save diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts index 11818381f..bef8c198e 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts @@ -31,8 +31,8 @@ import { AssetProfileDialog } from './asset-profile-dialog.component'; GfCurrencySelectorComponent, GfHistoricalMarketDataEditorComponent, GfLineChartComponent, - GfSymbolAutocompleteComponent, GfPortfolioProportionChartComponent, + GfSymbolAutocompleteComponent, GfValueComponent, MatButtonModule, MatCheckboxModule, diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index e038f8164..d03eb44e3 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -204,8 +204,7 @@ export class AdminService { } public patchAssetProfile( - dataSource: DataSource, - symbol: string, + { dataSource, symbol }: AssetProfileIdentifier, { assetClass, assetSubClass, @@ -229,11 +228,11 @@ export class AdminService { comment, countries, currency, - name, dataSource: newDataSource, - symbol: newSymbol, + name, scraperConfiguration, sectors, + symbol: newSymbol, symbolMapping, url } 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 f92635150..3c56c4748 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts @@ -194,9 +194,6 @@ export class GfSymbolAutocompleteComponent } private validateRequired() { - console.log(`dataSource: ${super.value?.dataSource}`); - console.log(`symbol: ${super.value?.symbol}`); - const requiredCheck = super.required ? !super.value?.dataSource || !super.value?.symbol : false; From 1f49a15dcd191a87048043732e4033c85cfbc659 Mon Sep 17 00:00:00 2001 From: tobikugel Date: Fri, 21 Mar 2025 17:10:56 -0300 Subject: [PATCH 49/56] chore(refactor): renaming variables --- .../asset-profile-dialog.component.ts | 18 ++++++++---------- .../asset-profile-dialog.html | 8 +++----- 2 files changed, 11 insertions(+), 15 deletions(-) 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 bc467f1b7..f7b56d4b4 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 @@ -71,7 +71,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public assetProfile: AdminMarketDataDetails['assetProfile']; public assetProfileIdentifierForm = this.formBuilder.group( { - editedSearchSymbol: new FormControl( + symbol: new FormControl( { symbol: null, dataSource: null }, [Validators.required] ) @@ -253,11 +253,11 @@ export class AssetProfileDialog implements OnDestroy, OnInit { private isNewSymbolValid(control: AbstractControl): ValidationErrors { const currentAssetProfileIdentifier: AssetProfileIdentifier | undefined = - control.get('editedSearchSymbol').value; + control.get('symbol').value; if ( - currentAssetProfileIdentifier.dataSource === this.data?.dataSource && - currentAssetProfileIdentifier.symbol === this.data?.symbol + currentAssetProfileIdentifier?.dataSource === this.data?.dataSource && + currentAssetProfileIdentifier?.symbol === this.data?.symbol ) { return { equalsPreviousSymbol: true @@ -340,10 +340,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public async onSubmitAssetProfileIdentifierForm() { const assetProfileIdentifierData: UpdateAssetProfileDto = { dataSource: - this.assetProfileIdentifierForm.get('editedSearchSymbol').value - .dataSource, - symbol: - this.assetProfileIdentifierForm.get('editedSearchSymbol').value.symbol + this.assetProfileIdentifierForm.get('symbol').value.dataSource, + symbol: this.assetProfileIdentifierForm.get('symbol').value.symbol }; try { @@ -372,7 +370,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { catchError((error: HttpErrorResponse) => { if (error.status === 409) { this.snackBar.open( - $localize`This symbol is already in use`, + $localize`This symbol is already in use.`, undefined, { duration: ms('3 seconds') @@ -380,7 +378,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { ); } else { this.snackBar.open( - $localize`An error occurred while updating the symbol`, + $localize`An error occurred while updating the symbol.`, undefined, { duration: ms('3 seconds') diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 50e49df92..86210c890 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -100,7 +100,7 @@ Name, symbol or ISIN @@ -110,10 +110,8 @@ mat-flat-button type="submit" [disabled]=" - assetProfileIdentifierForm.hasError( - 'invalidData', - 'editedSearchSymbol' - ) || assetProfileIdentifierForm.hasError('equalsPreviousSymbol') + assetProfileIdentifierForm.hasError('invalidData', 'symbol') || + assetProfileIdentifierForm.hasError('equalsPreviousSymbol') " > Apply From 0a7409b7157cfb9395e088f0eb0da1ea5090ce9b Mon Sep 17 00:00:00 2001 From: tobikugel Date: Sat, 22 Mar 2025 10:27:48 -0300 Subject: [PATCH 50/56] chore(refactor): change formating --- .../asset-profile-dialog.component.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 f7b56d4b4..33eaa346f 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 @@ -69,18 +69,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit { return { id: assetSubClass, label: translate(assetSubClass) }; }); public assetProfile: AdminMarketDataDetails['assetProfile']; - public assetProfileIdentifierForm = this.formBuilder.group( - { - symbol: new FormControl( - { symbol: null, dataSource: null }, - [Validators.required] - ) - }, - { - validators: (control) => this.isNewSymbolValid(control) - } - ); - public assetProfileForm = this.formBuilder.group({ assetClass: new FormControl(undefined), assetSubClass: new FormControl(undefined), @@ -103,6 +91,17 @@ export class AssetProfileDialog implements OnDestroy, OnInit { symbolMapping: '', url: '' }); + public assetProfileIdentifierForm = this.formBuilder.group( + { + symbol: new FormControl( + { symbol: null, dataSource: null }, + [Validators.required] + ) + }, + { + validators: (control) => this.isNewSymbolValid(control) + } + ); public assetProfileSubClass: string; public benchmarks: Partial[]; public countries: { @@ -555,6 +554,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public ngOnDestroy() { this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); } From 13f7570a4478ebb83c97b4f52325804c3e810c52 Mon Sep 17 00:00:00 2001 From: tobikugel Date: Sun, 23 Mar 2025 19:34:34 -0300 Subject: [PATCH 51/56] refactor: change variable names according to code review --- apps/api/src/app/admin/admin.controller.ts | 4 +- apps/api/src/app/admin/admin.service.ts | 70 ++++++++--------- .../market-data/market-data.service.ts | 10 +-- .../admin-market-data.component.ts | 6 +- .../asset-profile-dialog.component.ts | 75 ++++++++++--------- .../asset-profile-dialog.html | 29 ++++--- 6 files changed, 94 insertions(+), 100 deletions(-) diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 5cc6caf61..f9b158f5b 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -334,14 +334,14 @@ export class AdminController { @Patch('profile-data/:dataSource/:symbol') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async patchAssetProfileData( - @Body() assetProfileData: UpdateAssetProfileDto, + @Body() assetProfile: UpdateAssetProfileDto, @Param('dataSource') dataSource: DataSource, @Param('symbol') symbol: string ): Promise { return this.adminService.patchAssetProfileData( { dataSource, symbol }, { - ...assetProfileData + ...assetProfile } ); } diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 9c1c8f109..20a2d2f50 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -492,14 +492,14 @@ export class AdminService { newDataSource && (newSymbol !== symbol || newDataSource !== dataSource) ) { - const [profile] = await this.symbolProfileService.getSymbolProfiles([ + const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ { dataSource: newDataSource as DataSource, symbol: newSymbol as string } ]); - if (profile) { + if (assetProfile) { throw new HttpException( getReasonPhrase(StatusCodes.CONFLICT), StatusCodes.CONFLICT @@ -507,37 +507,35 @@ export class AdminService { } try { - await this.symbolProfileService.updateAssetProfileIdentifier( - { - dataSource, - symbol - }, - { - dataSource: newDataSource as DataSource, - symbol: newSymbol as string - } - ); - - await this.marketDataService.updateAssetProfileIdentifier( - { - dataSource, - symbol - }, - { - dataSource: newDataSource as DataSource, - symbol: newSymbol as string - } - ); - - const [symbolProfile] = - await this.symbolProfileService.getSymbolProfiles([ + Promise.all([ + await this.symbolProfileService.updateAssetProfileIdentifier( + { + dataSource, + symbol + }, + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string + } + ), + await this.marketDataService.updateAssetProfileIdentifier( + { + dataSource, + symbol + }, { dataSource: newDataSource as DataSource, symbol: newSymbol as string } - ]); + ) + ]); - return symbolProfile; + return this.symbolProfileService.getSymbolProfiles([ + { + dataSource: newDataSource as DataSource, + symbol: newSymbol as string + } + ])?.[0]; } catch { throw new HttpException( getReasonPhrase(StatusCodes.BAD_REQUEST), @@ -582,16 +580,12 @@ export class AdminService { updatedSymbolProfile ); - const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( - [ - { - dataSource: dataSource as DataSource, - symbol: symbol as string - } - ] - ); - - return symbolProfile; + return this.symbolProfileService.getSymbolProfiles([ + { + dataSource: dataSource as DataSource, + symbol: symbol as string + } + ])?.[0]; } } diff --git a/apps/api/src/services/market-data/market-data.service.ts b/apps/api/src/services/market-data/market-data.service.ts index 0cab0bcc0..390586b37 100644 --- a/apps/api/src/services/market-data/market-data.service.ts +++ b/apps/api/src/services/market-data/market-data.service.ts @@ -120,14 +120,8 @@ export class MarketDataService { symbol: newAssetProfileIdentifier.symbol }, where: { - AND: [ - { - dataSource: oldAssetProfileIdentifier.dataSource - }, - { - symbol: oldAssetProfileIdentifier.symbol - } - ] + dataSource: oldAssetProfileIdentifier.dataSource, + symbol: oldAssetProfileIdentifier.symbol } }); } diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts index bf067bd7f..5fe268142 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts @@ -390,9 +390,9 @@ export class AdminMarketDataComponent .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe( - (newAssetProfileIdentifer: AssetProfileIdentifier | undefined) => { - if (newAssetProfileIdentifer) { - this.onOpenAssetProfileDialog(newAssetProfileIdentifer); + (newAssetProfileIdentifier: AssetProfileIdentifier | undefined) => { + if (newAssetProfileIdentifier) { + this.onOpenAssetProfileDialog(newAssetProfileIdentifier); } else { this.router.navigate(['.'], { relativeTo: this.route }); } 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 33eaa346f..66790ce45 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 @@ -43,6 +43,7 @@ import { SymbolProfile } from '@prisma/client'; import { format } from 'date-fns'; +import { StatusCodes } from 'http-status-codes'; import ms from 'ms'; import { EMPTY, Subject } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; @@ -93,7 +94,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); public assetProfileIdentifierForm = this.formBuilder.group( { - symbol: new FormControl( + assetProfileIdentifier: new FormControl( { symbol: null, dataSource: null }, [Validators.required] ) @@ -111,7 +112,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public ghostfolioScraperApiSymbolPrefix = ghostfolioScraperApiSymbolPrefix; public historicalDataItems: LineChartItem[]; public isBenchmark = false; - public isEditSymbolMode = false; + public isEditAssetProfileIdentifierMode = false; public marketDataItems: MarketData[] = []; public modeValues = [ { @@ -250,30 +251,16 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } - private isNewSymbolValid(control: AbstractControl): ValidationErrors { - const currentAssetProfileIdentifier: AssetProfileIdentifier | undefined = - control.get('symbol').value; - - if ( - currentAssetProfileIdentifier?.dataSource === this.data?.dataSource && - currentAssetProfileIdentifier?.symbol === this.data?.symbol - ) { - return { - equalsPreviousSymbol: true - }; - } - } - - public get isSymbolEditable() { + public get isAssetProfileIdentifierEditable() { return !this.assetProfileForm.dirty; } - public get isSymbolEditButtonInvisible() { - return this.assetProfile?.dataSource === 'MANUAL'; + public get isAssetProfileIdentifierEditButtonVisible() { + return this.assetProfile?.dataSource !== 'MANUAL'; } - public onCancelEditSymboleMode() { - this.isEditSymbolMode = false; + public onCancelEditAssetProfileIdentifierMode() { + this.isEditAssetProfileIdentifierMode = false; this.assetProfileForm.enable(); @@ -328,8 +315,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { }); } - public onSetEditSymboleMode() { - this.isEditSymbolMode = true; + public onSetEditAssetProfileIdentifierMode() { + this.isEditAssetProfileIdentifierMode = true; this.assetProfileForm.disable(); @@ -337,17 +324,18 @@ export class AssetProfileDialog implements OnDestroy, OnInit { } public async onSubmitAssetProfileIdentifierForm() { - const assetProfileIdentifierData: UpdateAssetProfileDto = { - dataSource: - this.assetProfileIdentifierForm.get('symbol').value.dataSource, - symbol: this.assetProfileIdentifierForm.get('symbol').value.symbol + const assetProfileIdentifier: UpdateAssetProfileDto = { + dataSource: this.assetProfileIdentifierForm.get('assetProfileIdentifier') + .value.dataSource, + symbol: this.assetProfileIdentifierForm.get('assetProfileIdentifier') + .value.symbol }; try { await validateObjectForForm({ classDto: UpdateAssetProfileDto, form: this.assetProfileIdentifierForm, - object: assetProfileIdentifierData + object: assetProfileIdentifier }); } catch (error) { console.error(error); @@ -362,14 +350,14 @@ export class AssetProfileDialog implements OnDestroy, OnInit { symbol: this.data.symbol }, { - ...assetProfileIdentifierData + ...assetProfileIdentifier } ) .pipe( catchError((error: HttpErrorResponse) => { - if (error.status === 409) { + if (error.status === StatusCodes.CONFLICT) { this.snackBar.open( - $localize`This symbol is already in use.`, + $localize`${assetProfileIdentifier.symbol} (${assetProfileIdentifier.dataSource}) is already in use.`, undefined, { duration: ms('3 seconds') @@ -377,7 +365,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { ); } else { this.snackBar.open( - $localize`An error occurred while updating the symbol.`, + $localize`An error occurred while updating to ${assetProfileIdentifier.symbol} (${assetProfileIdentifier.dataSource}).`, undefined, { duration: ms('3 seconds') @@ -390,12 +378,12 @@ export class AssetProfileDialog implements OnDestroy, OnInit { takeUntil(this.unsubscribeSubject) ) .subscribe(() => { - const newAssetProfileIdentifer = { - dataSource: assetProfileIdentifierData.dataSource, - symbol: assetProfileIdentifierData.symbol + const newAssetProfileIdentifier = { + dataSource: assetProfileIdentifier.dataSource, + symbol: assetProfileIdentifier.symbol }; - this.dialogRef.close(newAssetProfileIdentifer); + this.dialogRef.close(newAssetProfileIdentifier); }); } @@ -554,7 +542,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public ngOnDestroy() { this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); } @@ -563,4 +550,18 @@ export class AssetProfileDialog implements OnDestroy, OnInit { this.assetProfileFormElement.nativeElement.requestSubmit(); } } + + private isNewSymbolValid(control: AbstractControl): ValidationErrors { + const currentAssetProfileIdentifier: AssetProfileIdentifier | undefined = + control.get('assetProfileIdentifier').value; + + if ( + currentAssetProfileIdentifier?.dataSource === this.data?.dataSource && + currentAssetProfileIdentifier?.symbol === this.data?.symbol + ) { + return { + equalsPreviousProfileIdentifier: true + }; + } + } } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 86210c890..4628c42e1 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -86,7 +86,7 @@ />
- @if (isEditSymbolMode) { + @if (isEditAssetProfileIdentifierMode) {
Name, symbol or ISIN @@ -110,19 +110,24 @@ mat-flat-button type="submit" [disabled]=" - assetProfileIdentifierForm.hasError('invalidData', 'symbol') || - assetProfileIdentifierForm.hasError('equalsPreviousSymbol') + assetProfileIdentifierForm.hasError( + 'invalidData', + 'assetProfileIdentifier' + ) || + assetProfileIdentifierForm.hasError( + 'equalsPreviousProfileIdentifier' + ) " > - Apply + Apply
@@ -132,7 +137,7 @@ >Symbol
-
+
@@ -307,7 +312,7 @@ color="primary" i18n [checked]="isBenchmark" - [disabled]="isEditSymbolMode" + [disabled]="isEditAssetProfileIdentifierMode" (change)=" isBenchmark ? onUnsetBenchmark({ From 947535eb61b36b50475cd128b570c97a5197ee6f Mon Sep 17 00:00:00 2001 From: tobikugel Date: Mon, 24 Mar 2025 08:49:00 -0300 Subject: [PATCH 52/56] fix: DataSource not found during build process --- apps/api/src/app/admin/admin.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 20a2d2f50..c9c6d5be9 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -494,7 +494,7 @@ export class AdminService { ) { const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ { - dataSource: newDataSource as DataSource, + dataSource: newDataSource as keyof typeof DataSource, symbol: newSymbol as string } ]); @@ -514,7 +514,7 @@ export class AdminService { symbol }, { - dataSource: newDataSource as DataSource, + dataSource: newDataSource as keyof typeof DataSource, symbol: newSymbol as string } ), @@ -524,7 +524,7 @@ export class AdminService { symbol }, { - dataSource: newDataSource as DataSource, + dataSource: newDataSource as keyof typeof DataSource, symbol: newSymbol as string } ) @@ -532,7 +532,7 @@ export class AdminService { return this.symbolProfileService.getSymbolProfiles([ { - dataSource: newDataSource as DataSource, + dataSource: newDataSource as keyof typeof DataSource, symbol: newSymbol as string } ])?.[0]; @@ -582,7 +582,7 @@ export class AdminService { return this.symbolProfileService.getSymbolProfiles([ { - dataSource: dataSource as DataSource, + dataSource: dataSource as keyof typeof DataSource, symbol: symbol as string } ])?.[0]; From 050a6534980aeef0a18733e514bc9b074ab5d0a6 Mon Sep 17 00:00:00 2001 From: tobikugel Date: Mon, 24 Mar 2025 09:03:34 -0300 Subject: [PATCH 53/56] Revert "fix: DataSource not found during build process" This reverts commit 4b8625f694f24de7913dd1c8c0b129f80be0b572. --- apps/api/src/app/admin/admin.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index c9c6d5be9..20a2d2f50 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -494,7 +494,7 @@ export class AdminService { ) { const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ { - dataSource: newDataSource as keyof typeof DataSource, + dataSource: newDataSource as DataSource, symbol: newSymbol as string } ]); @@ -514,7 +514,7 @@ export class AdminService { symbol }, { - dataSource: newDataSource as keyof typeof DataSource, + dataSource: newDataSource as DataSource, symbol: newSymbol as string } ), @@ -524,7 +524,7 @@ export class AdminService { symbol }, { - dataSource: newDataSource as keyof typeof DataSource, + dataSource: newDataSource as DataSource, symbol: newSymbol as string } ) @@ -532,7 +532,7 @@ export class AdminService { return this.symbolProfileService.getSymbolProfiles([ { - dataSource: newDataSource as keyof typeof DataSource, + dataSource: newDataSource as DataSource, symbol: newSymbol as string } ])?.[0]; @@ -582,7 +582,7 @@ export class AdminService { return this.symbolProfileService.getSymbolProfiles([ { - dataSource: dataSource as keyof typeof DataSource, + dataSource: dataSource as DataSource, symbol: symbol as string } ])?.[0]; From fe496e820e72d4a56d23f2b95940fa034aefb63c Mon Sep 17 00:00:00 2001 From: tobikugel Date: Tue, 25 Mar 2025 21:12:31 -0300 Subject: [PATCH 54/56] fix: build process --- apps/api/src/app/admin/admin.controller.ts | 4 +--- apps/api/src/app/admin/admin.service.ts | 8 ++++---- .../asset-profile-dialog.component.ts | 12 ++++-------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index f9b158f5b..2df1d98ae 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -340,9 +340,7 @@ export class AdminController { ): Promise { return this.adminService.patchAssetProfileData( { dataSource, symbol }, - { - ...assetProfile - } + assetProfile ); } diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 20a2d2f50..179d2ac25 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -494,7 +494,7 @@ export class AdminService { ) { const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ { - dataSource: newDataSource as DataSource, + dataSource: DataSource[newDataSource.toString()], symbol: newSymbol as string } ]); @@ -514,7 +514,7 @@ export class AdminService { symbol }, { - dataSource: newDataSource as DataSource, + dataSource: DataSource[newDataSource.toString()], symbol: newSymbol as string } ), @@ -524,7 +524,7 @@ export class AdminService { symbol }, { - dataSource: newDataSource as DataSource, + dataSource: DataSource[newDataSource.toString()], symbol: newSymbol as string } ) @@ -532,7 +532,7 @@ export class AdminService { return this.symbolProfileService.getSymbolProfiles([ { - dataSource: newDataSource as DataSource, + dataSource: DataSource[newDataSource.toString()], symbol: newSymbol as string } ])?.[0]; 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 66790ce45..dcaf9b3af 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 @@ -349,9 +349,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { dataSource: this.data.dataSource, symbol: this.data.symbol }, - { - ...assetProfileIdentifier - } + assetProfileIdentifier ) .pipe( catchError((error: HttpErrorResponse) => { @@ -435,7 +433,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { ); } catch {} - const assetProfileData: UpdateAssetProfileDto = { + const assetProfile: UpdateAssetProfileDto = { countries, scraperConfiguration, sectors, @@ -452,7 +450,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { await validateObjectForForm({ classDto: UpdateAssetProfileDto, form: this.assetProfileForm, - object: assetProfileData + object: assetProfile }); } catch (error) { console.error(error); @@ -465,9 +463,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit { dataSource: this.data.dataSource, symbol: this.data.symbol }, - { - ...assetProfileData - } + assetProfile ) .subscribe(() => { this.initialize(); From 5d79f7c9cf83417e93d474868035701bfe081c4d Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:06:28 +0100 Subject: [PATCH 55/56] Clean up --- .../symbol-profile/symbol-profile.service.ts | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index c995b13d1..e934a6324 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -126,40 +126,6 @@ export class SymbolProfileService { }); } - public async getSymbolProfilesByUserSubscription({ - withUserSubscription = false - }: { - withUserSubscription?: boolean; - }) { - return this.prismaService.symbolProfile.findMany({ - include: { - Order: { - include: { - User: true - } - } - }, - orderBy: [{ symbol: 'asc' }], - where: { - Order: withUserSubscription - ? { - some: { - User: { - Subscription: { some: { expiresAt: { gt: new Date() } } } - } - } - } - : { - every: { - User: { - Subscription: { none: { expiresAt: { gt: new Date() } } } - } - } - } - } - }); - } - public updateAssetProfileIdentifier( oldAssetProfileIdentifier: AssetProfileIdentifier, newAssetProfileIdentifier: AssetProfileIdentifier From c69a0315a66fc552beb91666fbfb45029314523c Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:12:42 +0100 Subject: [PATCH 56/56] Add DataSource --- apps/api/src/app/admin/admin.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 179d2ac25..d73e2b878 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -41,6 +41,7 @@ import { import { AssetClass, AssetSubClass, + DataSource, Prisma, PrismaClient, Property,