Browse Source

Merge remote-tracking branch 'origin/main' into feature/enable-strict-null-checks-in-ui

pull/6264/head
KenTandrian 3 days ago
parent
commit
32e6c068f5
  1. 17
      CHANGELOG.md
  2. 6
      apps/api/src/app/account/account.service.ts
  3. 1
      apps/api/src/app/admin/admin.service.ts
  4. 7
      apps/api/src/app/endpoints/market-data/market-data.controller.ts
  5. 6
      apps/api/src/app/endpoints/market-data/market-data.module.ts
  6. 7
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  7. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts
  8. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts
  9. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts
  10. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts
  11. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts
  12. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  13. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts
  14. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
  15. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts
  16. 3
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts
  17. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  18. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts
  19. 1
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts
  20. 3
      apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts
  21. 8
      apps/api/src/app/portfolio/portfolio.service.ts
  22. 1
      apps/api/src/app/subscription/subscription.service.ts
  23. 23
      apps/api/src/app/user/user.controller.ts
  24. 4
      apps/api/src/app/user/user.module.ts
  25. 51
      apps/api/src/app/user/user.service.ts
  26. 155
      apps/api/src/assets/cryptocurrencies/cryptocurrencies.json
  27. 48
      apps/api/src/helper/object.helper.spec.ts
  28. 28
      apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts
  29. 8
      apps/api/src/services/tag/tag.service.ts
  30. 6
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
  31. 2
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html
  32. 4
      libs/common/src/lib/interfaces/admin-data.interface.ts
  33. 4
      libs/common/src/lib/interfaces/portfolio-position.interface.ts
  34. 3
      libs/common/src/lib/interfaces/responses/accounts-response.interface.ts
  35. 3
      libs/common/src/lib/interfaces/responses/create-stripe-checkout-session-response.interface.ts
  36. 3
      libs/common/src/lib/models/timeline-position.ts
  37. 4
      libs/common/src/lib/types/account-with-value.type.ts
  38. 6
      libs/ui/src/lib/accounts-table/accounts-table.component.html
  39. 6
      libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts
  40. 7
      libs/ui/src/lib/mocks/holdings.ts
  41. 47
      libs/ui/src/lib/value/value.component.ts
  42. 4
      package-lock.json
  43. 2
      package.json

17
CHANGELOG.md

@ -9,8 +9,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Removed the `transactionCount` in the portfolio calculator and service
- Refreshed the cryptocurrencies list
### Fixed
- Fixed the accounts of the assistant for the impersonation mode
- Fixed the tags of the assistant for the impersonation mode
## 2.236.0 - 2026-02-05
### Changed
- Removed the deprecated `transactionCount` in the endpoint `GET api/v1/admin`
- Upgraded `stripe` from version `20.1.0` to `20.3.0`
### Fixed
- Fixed an exception when fetching the top holdings for ETF and mutual fund assets from _Yahoo Finance_
## 2.235.0 - 2026-02-03
### Added

6
apps/api/src/app/account/account.service.ts

@ -150,15 +150,15 @@ export class AccountService {
});
return accounts.map((account) => {
let transactionCount = 0;
let activitiesCount = 0;
for (const { isDraft } of account.activities) {
if (!isDraft) {
transactionCount += 1;
activitiesCount += 1;
}
}
const result = { ...account, transactionCount };
const result = { ...account, activitiesCount };
delete result.activities;

1
apps/api/src/app/admin/admin.service.ts

@ -186,7 +186,6 @@ export class AdminService {
dataProviders,
settings,
userCount,
transactionCount: activitiesCount,
version: environment.version
};
}

7
apps/api/src/app/endpoints/market-data/market-data.controller.ts

@ -2,6 +2,8 @@ import { AdminService } from '@ghostfolio/api/app/admin/admin.service';
import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service';
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import {
@ -28,7 +30,8 @@ import {
Param,
Post,
Query,
UseGuards
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
@ -86,6 +89,8 @@ export class MarketDataController {
@Get(':dataSource/:symbol')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(TransformDataSourceInRequestInterceptor)
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getMarketDataBySymbol(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string

6
apps/api/src/app/endpoints/market-data/market-data.module.ts

@ -1,5 +1,7 @@
import { AdminModule } from '@ghostfolio/api/app/admin/admin.module';
import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module';
import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module';
import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module';
import { MarketDataModule as MarketDataServiceModule } from '@ghostfolio/api/services/market-data/market-data.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
@ -13,7 +15,9 @@ import { MarketDataController } from './market-data.controller';
AdminModule,
MarketDataServiceModule,
SymbolModule,
SymbolProfileModule
SymbolProfileModule,
TransformDataSourceInRequestModule,
TransformDataSourceInResponseModule
]
})
export class MarketDataModule {}

7
apps/api/src/app/portfolio/calculator/portfolio-calculator.ts

@ -445,7 +445,6 @@ export abstract class PortfolioCalculator {
quantity: item.quantity,
symbol: item.symbol,
tags: item.tags,
transactionCount: item.transactionCount,
valueInBaseCurrency: new Big(marketPriceInBaseCurrency).mul(
item.quantity
)
@ -1005,8 +1004,7 @@ export abstract class PortfolioCalculator {
oldAccumulatedSymbol.feeInBaseCurrency.plus(feeInBaseCurrency),
includeInHoldings: oldAccumulatedSymbol.includeInHoldings,
quantity: newQuantity,
tags: oldAccumulatedSymbol.tags.concat(tags),
transactionCount: oldAccumulatedSymbol.transactionCount + 1
tags: oldAccumulatedSymbol.tags.concat(tags)
};
} else {
currentTransactionPointItem = {
@ -1024,8 +1022,7 @@ export abstract class PortfolioCalculator {
dividend: new Big(0),
includeInHoldings: INVESTMENT_ACTIVITY_TYPES.includes(type),
investment: unitPrice.mul(quantity).mul(factor),
quantity: quantity.mul(factor),
transactionCount: 1
quantity: quantity.mul(factor)
};
}

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts

@ -178,7 +178,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'474.93846153846153846154'
),
transactionCount: 2,
valueInBaseCurrency: new Big('595.6')
}
],

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts

@ -192,7 +192,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'285.80000000000000396627'
),
transactionCount: 3,
valueInBaseCurrency: new Big('0')
}
],

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts

@ -176,7 +176,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('285.8'),
timeWeightedInvestmentWithCurrencyEffect: new Big('285.8'),
transactionCount: 2,
valueInBaseCurrency: new Big('0')
}
],

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts

@ -172,7 +172,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('273.2'),
timeWeightedInvestmentWithCurrencyEffect: new Big('273.2'),
transactionCount: 1,
valueInBaseCurrency: new Big('297.8')
}
],

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts

@ -227,7 +227,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('44558.42'),
timeWeightedInvestmentWithCurrencyEffect: new Big('44558.42'),
transactionCount: 1,
valueInBaseCurrency: new Big('43099.7')
}
],

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts

@ -194,7 +194,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'636.79389574611155533947'
),
transactionCount: 2,
valueInBaseCurrency: new Big('13298.425356')
}
],

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts

@ -227,7 +227,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('44558.42'),
timeWeightedInvestmentWithCurrencyEffect: new Big('44558.42'),
transactionCount: 1,
valueInBaseCurrency: new Big('43099.7')
}
],

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts

@ -276,7 +276,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'852.45231607629427792916'
),
transactionCount: 2,
valueInBaseCurrency: new Big(1820)
});

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts

@ -172,7 +172,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('89.12').mul(0.8854),
timeWeightedInvestmentWithCurrencyEffect: new Big('82.329056'),
transactionCount: 1,
valueInBaseCurrency: new Big('103.10483')
}
],

3
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts

@ -162,8 +162,7 @@ describe('PortfolioCalculator', () => {
},
quantity: new Big('1'),
symbol: 'MSFT',
tags: [],
transactionCount: 2
tags: []
}
],
totalFeesWithCurrencyEffect: new Big('19'),

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts

@ -174,7 +174,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'145.10285714285714285714'
),
transactionCount: 2,
valueInBaseCurrency: new Big('87.8')
}
],

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts

@ -225,7 +225,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('151.6'),
timeWeightedInvestmentWithCurrencyEffect: new Big('151.6'),
transactionCount: 2,
valueInBaseCurrency: new Big('0')
}
],

1
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts

@ -146,7 +146,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('500000'),
timeWeightedInvestmentWithCurrencyEffect: new Big('500000'),
transactionCount: 1,
valueInBaseCurrency: new Big('500000')
}
],

3
apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts

@ -17,7 +17,4 @@ export interface TransactionPointSymbol {
skipErrors: boolean;
symbol: string;
tags?: Tag[];
/** @deprecated use activitiesCount instead */
transactionCount: number;
}

8
apps/api/src/app/portfolio/portfolio.service.ts

@ -233,7 +233,6 @@ export class PortfolioService {
account.currency,
userCurrency
),
transactionCount: activitiesCount,
value: this.exchangeRateDataService.toCurrency(
valueInBaseCurrency,
userCurrency,
@ -284,7 +283,6 @@ export class PortfolioService {
let totalDividendInBaseCurrency = new Big(0);
let totalInterestInBaseCurrency = new Big(0);
let totalValueInBaseCurrency = new Big(0);
let transactionCount = 0;
for (const account of accounts) {
activitiesCount += account.activitiesCount;
@ -301,8 +299,6 @@ export class PortfolioService {
totalValueInBaseCurrency = totalValueInBaseCurrency.plus(
account.valueInBaseCurrency
);
transactionCount += account.transactionCount;
}
for (const account of accounts) {
@ -317,7 +313,6 @@ export class PortfolioService {
return {
accounts,
activitiesCount,
transactionCount,
totalBalanceInBaseCurrency: totalBalanceInBaseCurrency.toNumber(),
totalDividendInBaseCurrency: totalDividendInBaseCurrency.toNumber(),
totalInterestInBaseCurrency: totalInterestInBaseCurrency.toNumber(),
@ -591,7 +586,6 @@ export class PortfolioService {
quantity,
symbol,
tags,
transactionCount,
valueInBaseCurrency
} of positions) {
if (isFilteredByClosedHoldings === true) {
@ -625,7 +619,6 @@ export class PortfolioService {
marketPrice,
symbol,
tags,
transactionCount,
allocationInPercentage: filteredValueInBaseCurrency.eq(0)
? 0
: valueInBaseCurrency.div(filteredValueInBaseCurrency).toNumber(),
@ -1696,7 +1689,6 @@ export class PortfolioService {
sectors: [],
symbol: currency,
tags: [],
transactionCount: 0,
valueInBaseCurrency: balance
};
}

1
apps/api/src/app/subscription/subscription.service.ts

@ -100,7 +100,6 @@ export class SubscriptionService {
);
return {
sessionId: session.id,
sessionUrl: session.url
};
}

23
apps/api/src/app/user/user.controller.ts

@ -1,8 +1,11 @@
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { RedactValuesInResponseInterceptor } from '@ghostfolio/api/interceptors/redact-values-in-response/redact-values-in-response.interceptor';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config';
import {
DeleteOwnUserDto,
UpdateOwnAccessTokenDto,
@ -28,7 +31,8 @@ import {
Param,
Post,
Put,
UseGuards
UseGuards,
UseInterceptors
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
@ -43,6 +47,7 @@ import { UserService } from './user.service';
export class UserController {
public constructor(
private readonly configurationService: ConfigurationService,
private readonly impersonationService: ImpersonationService,
private readonly jwtService: JwtService,
private readonly prismaService: PrismaService,
private readonly propertyService: PropertyService,
@ -107,13 +112,19 @@ export class UserController {
@Get()
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(RedactValuesInResponseInterceptor)
public async getUser(
@Headers('accept-language') acceptLanguage: string
@Headers('accept-language') acceptLanguage: string,
@Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string
): Promise<User> {
return this.userService.getUser(
this.request.user,
acceptLanguage?.split(',')?.[0]
);
const impersonationUserId =
await this.impersonationService.validateImpersonationId(impersonationId);
return this.userService.getUser({
impersonationUserId,
locale: acceptLanguage?.split(',')?.[0],
user: this.request.user
});
}
@Post()

4
apps/api/src/app/user/user.module.ts

@ -1,7 +1,9 @@
import { OrderModule } from '@ghostfolio/api/app/order/order.module';
import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module';
import { RedactValuesInResponseModule } from '@ghostfolio/api/interceptors/redact-values-in-response/redact-values-in-response.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { I18nModule } from '@ghostfolio/api/services/i18n/i18n.module';
import { ImpersonationModule } from '@ghostfolio/api/services/impersonation/impersonation.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { PropertyModule } from '@ghostfolio/api/services/property/property.module';
import { TagModule } from '@ghostfolio/api/services/tag/tag.module';
@ -18,6 +20,7 @@ import { UserService } from './user.service';
imports: [
ConfigurationModule,
I18nModule,
ImpersonationModule,
JwtModule.register({
secret: process.env.JWT_SECRET_KEY,
signOptions: { expiresIn: '30 days' }
@ -25,6 +28,7 @@ import { UserService } from './user.service';
OrderModule,
PrismaModule,
PropertyModule,
RedactValuesInResponseModule,
SubscriptionModule,
TagModule
],

51
apps/api/src/app/user/user.service.ts

@ -30,7 +30,7 @@ import {
PROPERTY_IS_READ_ONLY_MODE,
PROPERTY_SYSTEM_MESSAGE,
TAG_ID_EXCLUDE_FROM_ANALYSIS,
locale
locale as defaultLocale
} from '@ghostfolio/common/config';
import {
User as IUser,
@ -49,7 +49,7 @@ import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Prisma, Role, User } from '@prisma/client';
import { differenceInDays, subDays } from 'date-fns';
import { sortBy, without } from 'lodash';
import { without } from 'lodash';
import { createHmac } from 'node:crypto';
@Injectable()
@ -96,10 +96,17 @@ export class UserService {
return { accessToken, hashedAccessToken };
}
public async getUser(
{ accounts, id, permissions, settings, subscription }: UserWithSettings,
aLocale = locale
): Promise<IUser> {
public async getUser({
impersonationUserId,
locale = defaultLocale,
user
}: {
impersonationUserId: string;
locale?: string;
user: UserWithSettings;
}): Promise<IUser> {
const { id, permissions, settings, subscription } = user;
const userData = await Promise.all([
this.prismaService.access.findMany({
include: {
@ -108,22 +115,31 @@ export class UserService {
orderBy: { alias: 'asc' },
where: { granteeUserId: id }
}),
this.prismaService.account.findMany({
orderBy: {
name: 'asc'
},
where: {
userId: impersonationUserId || user.id
}
}),
this.prismaService.order.count({
where: { userId: id }
where: { userId: impersonationUserId || user.id }
}),
this.prismaService.order.findFirst({
orderBy: {
date: 'asc'
},
where: { userId: id }
where: { userId: impersonationUserId || user.id }
}),
this.tagService.getTagsForUser(id)
this.tagService.getTagsForUser(impersonationUserId || user.id)
]);
const access = userData[0];
const activitiesCount = userData[1];
const firstActivity = userData[2];
let tags = userData[3].filter((tag) => {
const accounts = userData[1];
const activitiesCount = userData[2];
const firstActivity = userData[3];
let tags = userData[4].filter((tag) => {
return tag.id !== TAG_ID_EXCLUDE_FROM_ANALYSIS;
});
@ -146,7 +162,6 @@ export class UserService {
}
return {
accounts,
activitiesCount,
id,
permissions,
@ -160,10 +175,13 @@ export class UserService {
permissions: accessItem.permissions
};
}),
accounts: accounts.sort((a, b) => {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
}),
dateOfFirstActivity: firstActivity?.date ?? new Date(),
settings: {
...(settings.settings as UserSettings),
locale: (settings.settings as UserSettings)?.locale ?? aLocale
locale: (settings.settings as UserSettings)?.locale ?? locale
}
};
}
@ -516,9 +534,10 @@ export class UserService {
currentPermissions.push(permissions.impersonateAllUsers);
}
user.accounts = sortBy(user.accounts, ({ name }) => {
return name.toLowerCase();
user.accounts = user.accounts.sort((a, b) => {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});
user.permissions = currentPermissions.sort();
return user;

155
apps/api/src/assets/cryptocurrencies/cryptocurrencies.json

@ -4,6 +4,7 @@
"4": "4",
"7": "Lucky7",
"8": "8",
"21": "2131KOBUSHIDE",
"32": "Project 32",
"42": "Semantic Layer",
"47": "President Trump",
@ -20,6 +21,7 @@
"1337": "EliteCoin",
"1717": "1717 Masonic Commemorative Token",
"2015": "2015 coin",
"2016": "2016 coin",
"2024": "2024",
"2025": "2025 TOKEN",
"2026": "2026",
@ -116,6 +118,7 @@
"3DES": "3DES",
"3DVANCE": "3D Vance",
"3FT": "ThreeFold Token",
"3KDS": "3KDS",
"3KM": "3 Kingdoms Multiverse",
"3P": "Web3Camp",
"3RDEYE": "3rd Eye",
@ -149,6 +152,7 @@
"7E": "7ELEVEN",
"88MPH": "88mph",
"8BIT": "8BIT Coin",
"8BITCOIN": "8-Bit COIN",
"8BT": "8 Circuit Studios",
"8LNDS": "8Lends",
"8PAY": "8Pay",
@ -179,6 +183,7 @@
"AAC": "Double-A Chain",
"AAG": "AAG Ventures",
"AAI": "AutoAir AI",
"AALON": "American Airlines Group (Ondo Tokenized)",
"AAPLON": "Apple (Ondo Tokenized)",
"AAPLX": "Apple xStock",
"AAPX": "AMPnet",
@ -713,6 +718,7 @@
"AMBRX": "Amber xStock",
"AMBT": "AMBT Token",
"AMC": "AI Meta Coin",
"AMCON": "AMC Entertainment (Ondo Tokenized)",
"AMDC": "Allmedi Coin",
"AMDG": "AMDG",
"AMDX": "AMD xStock",
@ -933,6 +939,7 @@
"ARBI": "Arbipad",
"ARBINU": "ArbInu",
"ARBIT": "Arbit Coin",
"ARBITROVE": "Arbitrove Governance Token",
"ARBP": "ARB Protocol",
"ARBS": "Arbswap",
"ARBT": "ARBITRAGE",
@ -1101,6 +1108,7 @@
"ASTA": "ASTA",
"ASTER": "Aster",
"ASTERINU": "Aster INU",
"ASTHERUSUSDF": "Astherus USDF",
"ASTO": "Altered State Token",
"ASTON": "Aston",
"ASTONV": "Aston Villa Fan Token",
@ -1171,6 +1179,7 @@
"ATON": "Further Network",
"ATOPLUS": "ATO+",
"ATOR": "ATOR Protocol",
"ATOS": "Atoshi",
"ATOZ": "Race Kingdom",
"ATP": "Atlas Protocol",
"ATPAY": "AtPay",
@ -1364,7 +1373,6 @@
"BABI": "Babylons",
"BABL": "Babylon Finance",
"BABY": "Babylon",
"BABY4": "Baby 4",
"BABYANDY": "Baby Andy",
"BABYASTER": "Baby Aster",
"BABYB": "Baby Bali",
@ -2007,7 +2015,8 @@
"BIPC": "BipCoin",
"BIPX": "Bispex",
"BIR": "Birake",
"BIRB": "Birb",
"BIRB": "Moonbirds",
"BIRBV1": "Birb",
"BIRD": "BIRD",
"BIRDCHAIN": "Birdchain",
"BIRDD": "BIRD DOG",
@ -2048,6 +2057,7 @@
"BITCOINCONFI": "Bitcoin Confidential",
"BITCOINOTE": "BitcoiNote",
"BITCOINP": "Bitcoin Private",
"BITCOINSCRYPT": "Bitcoin Scrypt",
"BITCOINV": "BitcoinV",
"BITCONNECT": "BitConnect Coin",
"BITCORE": "BitCore",
@ -2181,6 +2191,7 @@
"BLOCKASSET": "Blockasset",
"BLOCKB": "Block Browser",
"BLOCKBID": "Blockbid",
"BLOCKCHAINTRADED": "Blockchain Traded Fund",
"BLOCKF": "Block Farm Club",
"BLOCKG": "BlockGames",
"BLOCKIFY": "Blockify.Games",
@ -2808,7 +2819,7 @@
"BTCR": "BitCurrency",
"BTCRED": "Bitcoin Red",
"BTCRY": "BitCrystal",
"BTCS": "Bitcoin Scrypt",
"BTCS": "BTCs",
"BTCSR": "BTC Strategic Reserve",
"BTCST": "BTC Standard Hashrate Token",
"BTCTOKEN": "Bitcoin Token",
@ -2823,9 +2834,10 @@
"BTELEGRAM": "BetterTelegram Token",
"BTEV1": "Betero v1",
"BTEX": "BTEX",
"BTF": "Blockchain Traded Fund",
"BTF": "Bitfinity Network",
"BTFA": "Banana Task Force Ape",
"BTG": "Bitcoin Gold",
"BTGON": "B2Gold (Ondo Tokenized)",
"BTH": "Bithereum",
"BTK": "Bostoken",
"BTL": "Bitlocus",
@ -2973,9 +2985,11 @@
"BUSY": "Busy DAO",
"BUT": "Bucket Token",
"BUTT": "Buttercat",
"BUTTC": "Buttcoin",
"BUTTCOIN": "The Next Bitcoin",
"BUTTHOLE": "Butthole Coin",
"BUTTPLUG": "fartcoin killer",
"BUTWHY": "ButWhy",
"BUX": "BUX",
"BUXCOIN": "Buxcoin",
"BUY": "Burency",
@ -3007,6 +3021,7 @@
"BXA": "Blockchain Exchange Alliance",
"BXBT": "BoxBet",
"BXC": "BonusCloud",
"BXE": "Banxchange",
"BXF": "BlackFort Token",
"BXH": "BXH",
"BXK": "Bitbook Gambling",
@ -3230,6 +3245,7 @@
"CATW": "Cat wif Hands",
"CATWARRIOR": "Cat warrior",
"CATWIF": "CatWifHat",
"CATWIFM": "catwifmask",
"CATX": "CAT.trade Protocol",
"CATZ": "CatzCoin",
"CAU": "Canxium",
@ -3613,6 +3629,8 @@
"CLASH": "GeorgePlaysClashRoyale",
"CLASHUB": "Clashub",
"CLASS": "Class Coin",
"CLAWD": "clawd.atg.eth",
"CLAWNCH": "CLAWNCH",
"CLAY": "Clayton",
"CLAYN": "Clay Nation",
"CLB": "Cloudbric",
@ -3637,7 +3655,8 @@
"CLIN": "Clinicoin",
"CLINK": "cLINK",
"CLINT": "Clinton",
"CLIPPY": "CLIPPY",
"CLIPPY": "Clippy",
"CLIPPYETH": "CLIPPY",
"CLIPS": "Clips",
"CLIQ": "DefiCliq",
"CLIST": "Chainlist",
@ -3867,6 +3886,7 @@
"COPIUM": "Copium",
"COPPER": "COPPER",
"COPS": "Cops Finance",
"COPXON": "Global X Copper Miners ETF (Ondo Tokenized)",
"COPYCAT": "Copycat Finance",
"COQ": "Coq Inu",
"COR": "Coreto",
@ -3944,6 +3964,7 @@
"CPLO": "Cpollo",
"CPM": "Crypto Pump Meme",
"CPN": "CompuCoin",
"CPNGON": "Coupang (Ondo Tokenized)",
"CPO": "Cryptopolis",
"CPOO": "Cockapoo",
"CPOOL": "Clearpool",
@ -3976,6 +3997,7 @@
"CRAPPY": "CrappyBird",
"CRASH": "Solana Crash",
"CRASHBOYS": "CRASHBOYS",
"CRAT": "CratD2C",
"CRAVE": "CraveCoin",
"CRAYRABBIT": "CrazyRabbit",
"CRAZ": "CRAZY FLOKI",
@ -4411,6 +4433,7 @@
"DANJ": "Danjuan Cat",
"DANK": "DarkKush",
"DANKDOGE": "Dank Doge",
"DANKDOGEAI": "DankDogeAI",
"DANNY": "Degen Danny",
"DAO": "DAO Maker",
"DAO1": "DAO1",
@ -4897,6 +4920,7 @@
"DLXV": "Delta-X",
"DLY": "Daily Finance",
"DLYCOP": "Daily COP",
"DM": "Dumb Money",
"DMA": "Dragoma",
"DMAGA": "Dark MAGA",
"DMAIL": "DMAIL Network",
@ -5096,7 +5120,8 @@
"DONKEY": "donkey",
"DONNIEFIN": "Donnie Finance",
"DONS": "The Dons",
"DONT": "Donald Trump (dont.cash)",
"DONT": "DisclaimerCoin",
"DONTCASH": "DONT",
"DONU": "Donu",
"DONUT": "Donut",
"DONUTS": "The Simpsons",
@ -5451,6 +5476,7 @@
"ECET": "Evercraft Ecotechnologies",
"ECG": "EcoSmart",
"ECH": "EthereCash",
"ECHELON": "Echelon Token",
"ECHO": "Echo",
"ECHOBOT": "ECHO BOT",
"ECHOD": "EchoDEX",
@ -5569,6 +5595,7 @@
"EHASH": "EHash",
"EHIVE": "eHive",
"EHRT": "Eight Hours Token",
"EICOIN": "EICOIN",
"EIFI": "EIFI FINANCE",
"EIGEN": "EigenLayer",
"EIGENP": "Eigenpie",
@ -5899,6 +5926,7 @@
"ETHEREM": "Etherempires",
"ETHEREUMMEME": "Solana Ethereum Meme",
"ETHEREUMP": "ETHEREUMPLUS",
"ETHEREUMSCRYPT": "EthereumScrypt",
"ETHERINC": "EtherInc",
"ETHERKING": "Ether Kingdoms Token",
"ETHERNITY": "Ethernity Chain",
@ -5921,7 +5949,7 @@
"ETHPR": "Ethereum Premium",
"ETHPY": "Etherpay",
"ETHR": "Ethereal",
"ETHS": "EthereumScrypt",
"ETHS": "Ethscriptions",
"ETHSHIB": "Eth Shiba",
"ETHV": "Ethverse",
"ETHW": "Ethereum PoW",
@ -6096,6 +6124,7 @@
"F2C": "Ftribe Fighters",
"F2K": "Farm2Kitchen",
"F3": "Friend3",
"F5": "F5-promoT5",
"F7": "Five7",
"F9": "Falcon Nine",
"FAB": "FABRK Token",
@ -6283,6 +6312,7 @@
"FIC": "Filecash",
"FID": "Fidira",
"FIDA": "Bonfida",
"FIDD": "Fidelity Digital Dollar",
"FIDLE": "Fidlecoin",
"FIDO": "FIDO",
"FIDU": "Fidu",
@ -6672,6 +6702,7 @@
"FRR": "Frontrow",
"FRSP": "Forkspot",
"FRST": "FirstCoin",
"FRT": "FORT Token",
"FRTC": "FART COIN",
"FRTN": "EbisusBay Fortune",
"FRTS": "Fruits",
@ -7211,6 +7242,7 @@
"GMDP": "GMD Protocol",
"GME": "GameStop",
"GMEE": "GAMEE",
"GMEON": "GameStop (Ondo Tokenized)",
"GMEPEPE": "GAMESTOP PEPE",
"GMETHERFRENS": "GM",
"GMETRUMP": "GME TRUMP",
@ -7289,6 +7321,7 @@
"GOFINDXR": "Gofind XR",
"GOFX": "GooseFX",
"GOG": "Guild of Guardians",
"GOGE": "GOLD DOGE",
"GOGLZ": "GOGGLES",
"GOGLZV1": "GOGGLES v1",
"GOGO": "GOGO Finance",
@ -7399,6 +7432,7 @@
"GPU": "Node AI",
"GPUCOIN": "GPU Coin",
"GPUINU": "GPU Inu",
"GPUNET": "GPUnet",
"GPX": "GPEX",
"GQ": "Galactic Quadrant",
"GR": "GROM",
@ -7537,6 +7571,7 @@
"GTAN": "Giant Token",
"GTAVI": "GTAVI",
"GTBOT": "Gaming-T-Bot",
"GTBTC": "Gate Wrapped BTC",
"GTC": "Gitcoin",
"GTCC": "GTC COIN",
"GTCOIN": "Game Tree",
@ -7599,6 +7634,7 @@
"GVT": "Genesis Vision",
"GW": "Gyrowin",
"GWD": "GreenWorld",
"GWEI": "ETHGas",
"GWGW": "GoWrap",
"GWT": "Galaxy War",
"GX": "GameX",
@ -7915,7 +7951,7 @@
"HLG": "Holograph",
"HLINK": "Chainlink (Harmony One Bridge)",
"HLM": "Helium",
"HLN": "Holonus",
"HLN": "Ēnosys",
"HLO": "Halo",
"HLOV1": "Halo v1",
"HLP": "Purpose Coin",
@ -7980,6 +8016,7 @@
"HOLDON4": "HoldOn4DearLife",
"HOLDS": "Holdstation",
"HOLO": "Holoworld",
"HOLON": "Holonus",
"HOLY": "Holy Trinity",
"HOM": "Homeety",
"HOME": "Home",
@ -8199,6 +8236,7 @@
"IAI": "inheritance Art",
"IAM": "IAME Identity",
"IAOMIN": "Yao Ming",
"IAUON": "iShares Gold Trust (Ondo Tokenized)",
"IB": "Iron Bank",
"IBANK": "iBankCoin",
"IBAT": "Battle Infinity",
@ -8357,7 +8395,7 @@
"IMS": "Independent Money System",
"IMST": "Imsmart",
"IMT": "Immortal Token",
"IMU": "imusify",
"IMUSIFY": "imusify",
"IMVR": "ImmVRse",
"IMX": "Immutable X",
"IN": "INFINIT",
@ -8522,6 +8560,7 @@
"IRA": "Diligence",
"IRC": "IRIS",
"IRENA": "Irena Coin Apps",
"IRENON": "IREN (Ondo Tokenized)",
"IRIS": "IRIS Network",
"IRISTOKEN": "Iris Ecosystem",
"IRL": "IrishCoin",
@ -8595,6 +8634,7 @@
"IVZ": "InvisibleCoin",
"IW": "iWallet",
"IWFT": "İstanbul Wild Cats",
"IWMON": "iShares Russell 2000 ETF (Ondo Tokenized)",
"IWT": "IwToken",
"IX": "X-Block",
"IXC": "IXcoin",
@ -8698,6 +8738,7 @@
"JETCOIN": "Jetcoin",
"JETFUEL": "Jetfuel Finance",
"JETTON": "JetTon Game",
"JETUSD": "JETUSD",
"JEUR": "Jarvis Synthetic Euro",
"JEW": "Shekel",
"JEWEL": "DeFi Kingdoms",
@ -8847,6 +8888,7 @@
"JWBTC": "Wrapped Bitcoin (TON Bridge)",
"JWIF": "Jerrywifhat",
"JWL": "Jewels",
"JWT": "JW Token",
"JYAI": "Jerry The Turtle By Matt Furie",
"JYC": "Joe-Yo Coin",
"K": "Sidekick",
@ -8855,7 +8897,13 @@
"KAAI": "KanzzAI",
"KAAS": "KAASY.AI",
"KAB": "KABOSU",
"KABOSU": "Kabosu Family",
"KABOSU": "X Meme Dog",
"KABOSUCOIN": "Kabosu",
"KABOSUCOM": "Kabosu",
"KABOSUFAMILY": "Kabosu Family",
"KABOSUTOKEN": "Kabosu",
"KABOSUTOKENETH": "KABOSU",
"KABUTO": "Kabuto",
"KABY": "Kaby Arena",
"KAC": "KACO Finance",
"KACY": "markkacy",
@ -9155,6 +9203,7 @@
"KNDC": "KanadeCoin",
"KNDM": "Kingdom",
"KNDX": "Kondux",
"KNEKTED": "Knekted",
"KNFT": "KStarNFT",
"KNG": "BetKings",
"KNGN": "KingN Coin",
@ -9167,7 +9216,7 @@
"KNOW": "KNOW",
"KNOX": "KnoxDAO",
"KNS": "Kenshi",
"KNT": "Knekted",
"KNT": "KayakNet",
"KNTO": "Kento",
"KNTQ": "Kinetiq Governance Token",
"KNU": "Keanu",
@ -9670,7 +9719,7 @@
"LITTLEGUY": "just a little guy",
"LITTLEMANYU": "Little Manyu",
"LIV": "LiviaCoin",
"LIVE": "TRONbetLive",
"LIVE": "SecondLive",
"LIVENCOIN": "LivenPay",
"LIVESEY": "Dr. Livesey",
"LIVESTARS": "Live Stars",
@ -10008,6 +10057,7 @@
"M0": "M by M^0",
"M1": "SupplyShock",
"M2O": "M2O Token",
"M3H": "MehVerseCoin",
"M3M3": "M3M3",
"M87": "MESSIER",
"MA": "Mind-AI",
@ -10026,6 +10076,8 @@
"MADOG": "MarvelDoge",
"MADP": "Mad Penguin",
"MADPEPE": "Mad Pepe",
"MADU": "Nicolas Maduro",
"MADURO": "MADURO",
"MAECENAS": "Maecenas",
"MAEP": "Maester Protocol",
"MAF": "MetaMAFIA",
@ -10115,7 +10167,8 @@
"MANUSAI": "Manus AI Agent",
"MANYU": "Manyu",
"MANYUDOG": "MANYU",
"MAO": "Mao",
"MAO": "MAO",
"MAOMEME": "Mao",
"MAOW": "MAOW",
"MAP": "MAP Protocol",
"MAPC": "MapCoin",
@ -10125,6 +10178,7 @@
"MAPS": "MAPS",
"MAPU": "MatchAwards Platform Utility Token",
"MAR3": "Mar3 AI",
"MARAON": "MARA Holdings (Ondo Tokenized)",
"MARCO": "MELEGA",
"MARCUS": "Marcus Cesar Inu",
"MARE": "Mare Finance",
@ -10179,7 +10233,7 @@
"MASTERMIX": "Master MIX Token",
"MASTERTRADER": "MasterTraderCoin",
"MASYA": "MASYA",
"MAT": "My Master Wa",
"MAT": "Matchain",
"MATA": "Ninneko",
"MATAR": "MATAR AI",
"MATCH": "Matching Game",
@ -10397,6 +10451,7 @@
"MEMEAI": "Meme Ai",
"MEMEBRC": "MEME",
"MEMECOIN": "just memecoin",
"MEMECOINDAOAI": "MemeCoinDAO",
"MEMECUP": "Meme Cup",
"MEMEETF": "Meme ETF",
"MEMEFI": "MemeFi",
@ -10408,7 +10463,7 @@
"MEMEMUSK": "MEME MUSK",
"MEMENTO": "MEMENTO•MORI (Runes)",
"MEMERUNE": "MEME•ECONOMICS",
"MEMES": "MemeCoinDAO",
"MEMES": "memes will continue",
"MEMESAI": "Memes AI",
"MEMESQUAD": "Meme Squad",
"MEMET": "MEMETOON",
@ -10596,7 +10651,7 @@
"MIININGNFT": "MiningNFT",
"MIKE": "Mike",
"MIKS": "MIKS COIN",
"MIL": "Milllionaire Coin",
"MIL": "Mil",
"MILA": "MILADY MEME TOKEN",
"MILC": "Micro Licensing Coin",
"MILE": "milestoneBased",
@ -10609,6 +10664,7 @@
"MILLI": "Million",
"MILLIM": "Millimeter",
"MILLIMV1": "Millimeter v1",
"MILLLIONAIRECOIN": "Milllionaire Coin",
"MILLY": "milly",
"MILO": "Milo Inu",
"MILOCEO": "Milo CEO",
@ -10879,6 +10935,7 @@
"MOLK": "Mobilink Token",
"MOLLARS": "MollarsToken",
"MOLLY": "Molly",
"MOLT": "Moltbook",
"MOM": "Mother of Memes",
"MOMA": "Mochi Market",
"MOMIJI": "MAGA Momiji",
@ -10894,6 +10951,7 @@
"MONAV": "Monavale",
"MONB": "MonbaseCoin",
"MONDO": "mondo",
"MONEROAI": "Monero AI",
"MONEROCHAN": "Monerochan",
"MONET": "Claude Monet Memeory Coin",
"MONETA": "Moneta",
@ -11084,6 +11142,7 @@
"MSCT": "MUSE ENT NFT",
"MSD": "MSD",
"MSFT": "Microsoft 6900",
"MSFTON": "Microsoft (Ondo Tokenized)",
"MSFTX": "Microsoft xStock",
"MSG": "MsgSender",
"MSGO": "MetaSetGO",
@ -11107,6 +11166,7 @@
"MSTRX": "MicroStrategy xStock",
"MSU": "MetaSoccer",
"MSUSHI": "Sushi (Multichain)",
"MSVP": "MetaSoilVerseProtocol",
"MSWAP": "MoneySwap",
"MT": "Mint Token",
"MTA": "Meta",
@ -11262,10 +11322,12 @@
"MYL": "MyLottoCoin",
"MYLINX": "Linx",
"MYLO": "MYLOCAT",
"MYMASTERWAR": "My Master Wa",
"MYNE": "ITSMYNE",
"MYO": "Mycro",
"MYOBU": "Myōbu",
"MYRA": "Mytheria",
"MYRC": "MYRC",
"MYRE": "Myre",
"MYRIA": "Myria",
"MYRO": "Myro",
@ -11586,6 +11648,7 @@
"NIC": "NewInvestCoin",
"NICE": "Nice",
"NICEC": "NiceCoin",
"NIETZSCHEAN": "Nietzschean Penguin",
"NIF": "Unifty",
"NIFT": "Niftify",
"NIFTSY": "Envelop",
@ -11617,6 +11680,7 @@
"NINU": "Nvidia Inu",
"NIOB": "Niob Finance",
"NIOCTIB": "nioctiB",
"NIOON": "NIO (Ondo Tokenized)",
"NIOX": "Autonio",
"NIOXV1": "Autonio v1",
"NIOXV2": "Autonio v2",
@ -11750,6 +11814,7 @@
"NRCH": "EnreachDAO",
"NRFB": "NuriFootBall",
"NRG": "Energi",
"NRGE": "New Resources Generation Energy",
"NRGY": "NRGY Defi",
"NRK": "Nordek",
"NRM": "Neuromachine",
@ -12308,6 +12373,7 @@
"OWB": "OWB",
"OWC": "Oduwa",
"OWD": "Owlstand",
"OWL": "Owlto",
"OWLTOKEN": "OWL Token",
"OWN": "OTHERWORLD",
"OWNDATA": "OWNDATA",
@ -12342,6 +12408,7 @@
"P202": "Project 202",
"P2P": "Sentinel",
"P2PS": "P2P Solutions Foundation",
"P2PV1": "Sentinel",
"P33L": "THE P33L",
"P3D": "3DPass",
"P404": "Potion 404",
@ -12593,6 +12660,7 @@
"PENDY": "Pendy",
"PENG": "Peng",
"PENGCOIN": "PENG",
"PENGO": "Petro Penguins",
"PENGU": "Pudgy Penguins",
"PENGUAI": "PENGU AI",
"PENGUI": "Penguiana",
@ -12718,6 +12786,7 @@
"PEW": "pepe in a memes world",
"PEX": "Pexcoin",
"PF": "Purple Frog",
"PFEON": "Pfizer (Ondo Tokenized)",
"PFEX": "Pfizer xStock",
"PFF": "PumpFunFloki",
"PFI": "PrimeFinance",
@ -12815,6 +12884,7 @@
"PIKAM": "Pikamoon",
"PIKE": "Pike Token",
"PIKO": "Pinnako",
"PIKZ": "PIKZ",
"PILLAR": "PillarFi",
"PILOT": "Unipilot",
"PIM": "PIM",
@ -12952,6 +13022,7 @@
"PLSX": "PulseX",
"PLT": "Poollotto.finance",
"PLTC": "PlatonCoin",
"PLTRON": "Palantir Technologies (Ondo Tokenized)",
"PLTRX": "Palantir xStock",
"PLTX": "PlutusX",
"PLTXYZ": "Add.xyz",
@ -13199,6 +13270,7 @@
"PRESI": "Turbo Trump",
"PRESID": "President Ron DeSantis",
"PRESIDEN": "President Elon",
"PRESSX": "PressX",
"PRFT": "Proof Suite Token",
"PRG": "Paragon",
"PRI": "PRIVATEUM INITIATIVE",
@ -13431,6 +13503,7 @@
"PYME": "PymeDAO",
"PYN": "Paynetic",
"PYP": "PayPro",
"PYPLON": "PayPal (Ondo Tokenized)",
"PYQ": "PolyQuity",
"PYR": "Vulcan Forged",
"PYRAM": "Pyram Token",
@ -13467,6 +13540,7 @@
"QBX": "qiibee foundation",
"QBZ": "QUEENBEE",
"QC": "Qcash",
"QCAD": "QCAD",
"QCH": "QChi",
"QCN": "Quazar Coin",
"QCO": "Qravity",
@ -13538,6 +13612,7 @@
"QUA": "Quantum Tech",
"QUAC": "QUACK",
"QUACK": "Rich Quack",
"QUADRANS": "QuadransToken",
"QUAI": "Quai Network",
"QUAIN": "QUAIN",
"QUAM": "Quam Network",
@ -13614,6 +13689,7 @@
"RAIF": "RAI Finance",
"RAIIN": "Raiin",
"RAIL": "Railgun",
"RAILS": "Rails Token",
"RAIN": "Rain",
"RAINBOW": "Rainbow Token",
"RAINC": "RainCheck",
@ -13744,6 +13820,7 @@
"REALYN": "Real",
"REAP": "ReapChain",
"REAPER": "Grim Finance",
"REAT": "REAT",
"REAU": "Vira-lata Finance",
"REBD": "REBORN",
"REBL": "REBL",
@ -14557,7 +14634,7 @@
"SENSOR": "Sensor Protocol",
"SENSOV1": "SENSO v1",
"SENSUS": "Sensus",
"SENT": "Sentinel",
"SENT": "Sentient",
"SENTAI": "SentAI",
"SENTI": "Sentinel Bot Ai",
"SENTIS": "Sentism AI Token",
@ -14626,6 +14703,7 @@
"SGB": "Songbird",
"SGDX": "eToro Singapore Dollar",
"SGE": "Society of Galactic Exploration",
"SGI": "SmartGolfToken",
"SGLY": "Singularity",
"SGN": "Signals Network",
"SGO": "SafuuGO",
@ -15151,6 +15229,7 @@
"SOETH": "Wrapped Ethereum (Sollet)",
"SOFAC": "SofaCat",
"SOFI": "RAI Finance",
"SOFION": "SoFi Technologies (Ondo Tokenized)",
"SOFTCO": "SOFT COQ INU",
"SOFTT": "Wrapped FTT (Sollet)",
"SOGNI": "Sogni AI",
@ -15208,6 +15287,7 @@
"SOLIDSEX": "SOLIDsex: Tokenized veSOLID",
"SOLINK": "Wrapped Chainlink (Sollet)",
"SOLITO": "SOLITO",
"SOLKABOSU": "Kabosu",
"SOLKIT": "Solana Kit",
"SOLLY": "Solly",
"SOLM": "SolMix",
@ -15432,6 +15512,7 @@
"SQG": "Squid Token",
"SQGROW": "SquidGrow",
"SQL": "Squall Coin",
"SQQQON": "ProShares UltraPro Short QQQ (Ondo Tokenized)",
"SQR": "Magic Square",
"SQRL": "Squirrel Swap",
"SQT": "SubQuery Network",
@ -15519,6 +15600,7 @@
"STAK": "Jigstack",
"STAKE": "xDai Chain",
"STAKEDETH": "StakeHound Staked Ether",
"STAKERDAOWXTZ": "Wrapped Tezos",
"STALIN": "StalinCoin",
"STAMP": "SafePost",
"STAN": "Stank Memes",
@ -16561,7 +16643,7 @@
"TRGI": "The Real Golden Inu",
"TRHUB": "Tradehub",
"TRI": "Triangles Coin",
"TRIA": "Triaconta",
"TRIA": "TRIA",
"TRIAS": "Trias",
"TRIBE": "Tribe",
"TRIBETOKEN": "TribeToken",
@ -16596,6 +16678,7 @@
"TROLLMODE": "TROLL MODE",
"TROLLRUN": "TROLL",
"TROLLS": "trolls in a memes world",
"TRONBETLIVE": "TRONbetLive",
"TRONDOG": "TronDog",
"TRONI": "Tron Inu",
"TRONP": "Donald Tronp",
@ -16603,7 +16686,7 @@
"TROP": "Interop",
"TROPPY": "TROPPY",
"TROSS": "Trossard",
"TROVE": "Arbitrove Governance Token",
"TROVE": "TROVE",
"TROY": "Troy",
"TRP": "Tronipay",
"TRR": "Terran Coin",
@ -17048,7 +17131,8 @@
"USA": "Based USA",
"USACOIN": "American Coin",
"USAGIBNB": "U",
"USAT": "USAT",
"USAT": "Tether America USD",
"USATINC": "USAT",
"USBT": "Universal Blockchain",
"USC": "Ultimate Secure Cash",
"USCC": "USC",
@ -17087,7 +17171,8 @@
"USDGLOBI": "Globiance USD Stablecoin",
"USDGV1": "USDG v1",
"USDGV2": "USDG",
"USDH": "USDH Hubble Stablecoin",
"USDH": "USDH",
"USDHHUBBLE": "USDH Hubble Stablecoin",
"USDHL": "Hyper USD",
"USDI": "Interest Protocol USDi",
"USDJ": "USDJ",
@ -17182,8 +17267,9 @@
"UUU": "U Network",
"UVT": "UvToken",
"UW3S": "Utility Web3Shot",
"UWU": "UwU Lend",
"UWU": "Unlimited Wealth Utility",
"UWUCOIN": "uwu",
"UWULEND": "UwU Lend",
"UX": "Umee",
"UXLINK": "UXLINK",
"UXLINKV1": "UXLINK v1",
@ -17542,6 +17628,7 @@
"VSYS": "V Systems",
"VT": "Virtual Tourist",
"VTC": "Vertcoin",
"VTCN": "Versatize Coin",
"VTG": "Victory Gem",
"VTHO": "VeChainThor",
"VTIX": "Vanguard xStock",
@ -17587,6 +17674,7 @@
"VYPER": "VYPER.WIN",
"VYVO": "Vyvo AI",
"VZ": "Vault Zero",
"VZON": "Verizon (Ondo Tokenized)",
"VZT": "Vezt",
"W": "Wormhole",
"W1": "W1",
@ -17644,7 +17732,7 @@
"WANNA": "Wanna Bot",
"WANUSDT": "wanUSDT",
"WAP": "Wet Ass Pussy",
"WAR": "WeStarter",
"WAR": "WAR",
"WARP": "WarpCoin",
"WARPED": "Warped Games",
"WARPIE": "Warpie",
@ -17713,7 +17801,7 @@
"WCFGV1": "Wrapped Centrifuge",
"WCFX": "Wrapped Conflux",
"WCG": "World Crypto Gold",
"WCHZ": "Chiliz (Portal Bridge)",
"WCHZ": "Wrapped Chiliz",
"WCKB": "Wrapped Nervos Network",
"WCOIN": "WCoin",
"WCORE": "Wrapped Core",
@ -17786,6 +17874,7 @@
"WERK": "Werk Family",
"WESHOWTOKEN": "WeShow Token",
"WEST": "Waves Enterprise",
"WESTARTER": "WeStarter",
"WET": "HumidiFi Token",
"WETH": "WETH",
"WETHV1": "WETH v1",
@ -17830,6 +17919,7 @@
"WHATSONPIC": "WhatsOnPic",
"WHBAR": "Wrapped HBAR",
"WHC": "Whales Club",
"WHCHZ": "Chiliz (Portal Bridge)",
"WHEAT": "Wheat Token",
"WHEE": "WHEE (Ordinals)",
"WHEEL": "Wheelers",
@ -18118,6 +18208,7 @@
"WUF": "WUFFI",
"WUK": "WUKONG",
"WUKONG": "Sun Wukong",
"WULFON": "Terawulf (Ondo Tokenized)",
"WULFY": "Wulfy",
"WUM": "Unicorn Meat",
"WUSD": "Worldwide USD",
@ -18143,7 +18234,6 @@
"WXPL": "Wrapped XPL",
"WXRP": "Wrapped XRP",
"WXT": "WXT",
"WXTZ": "Wrapped Tezos",
"WYAC": "Woman Yelling At Cat",
"WYN": "Wynn",
"WYNN": "Anita Max Wynn",
@ -18186,6 +18276,7 @@
"XAS": "Asch",
"XAT": "ShareAt",
"XAUC": "XauCoin",
"XAUH": "Herculis Gold Coin",
"XAUM": "Matrixdock Gold",
"XAUR": "Xaurum",
"XAUT": "Tether Gold",
@ -18778,7 +18869,8 @@
"ZEBU": "ZEBU",
"ZEC": "ZCash",
"ZECD": "ZCashDarkCoin",
"ZED": "ZedCoins",
"ZED": "ZED Token",
"ZEDCOIN": "ZedCoin",
"ZEDD": "ZedDex",
"ZEDTOKEN": "Zed Token",
"ZEDX": "ZEDX Сoin",
@ -18932,6 +19024,7 @@
"ZOOM": "ZoomCoin",
"ZOOMER": "Zoomer Coin",
"ZOON": "CryptoZoon",
"ZOOSTORY": "ZOO",
"ZOOT": "Zoo Token",
"ZOOTOPIA": "Zootopia",
"ZORA": "Zora",
@ -19009,5 +19102,13 @@
"vXDEFI": "vXDEFI",
"wsOHM": "Wrapped Staked Olympus",
"修仙": "修仙",
"币安人生": "币安人生"
"分红狗头": "分红狗头",
"哭哭马": "哭哭马",
"安": "安",
"币安人生": "币安人生",
"恶俗企鹅": "恶俗企鹅",
"我踏马来了": "我踏马来了",
"老子": "老子",
"雪球": "雪球",
"黑马": "黑马"
}

48
apps/api/src/helper/object.helper.spec.ts

@ -111,6 +111,7 @@ describe('redactAttributes', () => {
hasError: false,
holdings: {
'AAPL.US': {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -130,7 +131,6 @@ describe('redactAttributes', () => {
marketPrice: 220.79,
symbol: 'AAPL.US',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.044900865255793135,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -163,6 +163,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.0694356974830054
},
'ALV.DE': {
activitiesCount: 2,
currency: 'EUR',
markets: {
UNKNOWN: 0,
@ -182,7 +183,6 @@ describe('redactAttributes', () => {
marketPrice: 296.5,
symbol: 'ALV.DE',
tags: [],
transactionCount: 2,
allocationInPercentage: 0.026912563036519527,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -210,6 +210,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.04161818652826481
},
AMZN: {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -229,7 +230,6 @@ describe('redactAttributes', () => {
marketPrice: 187.99,
symbol: 'AMZN',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.07646101417126275,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -262,6 +262,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.11824101426541227
},
bitcoin: {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 36985.0332704,
@ -287,7 +288,6 @@ describe('redactAttributes', () => {
userId: null
}
],
transactionCount: 1,
allocationInPercentage: 0.15042891393226654,
assetClass: 'LIQUIDITY',
assetSubClass: 'CRYPTOCURRENCY',
@ -313,6 +313,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.232626620912395
},
BONDORA_GO_AND_GROW: {
activitiesCount: 5,
currency: 'EUR',
markets: {
UNKNOWN: 2231.644722160232,
@ -338,7 +339,6 @@ describe('redactAttributes', () => {
userId: null
}
],
transactionCount: 5,
allocationInPercentage: 0.009076749759365777,
assetClass: 'FIXED_INCOME',
assetSubClass: 'BOND',
@ -364,6 +364,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.014036487867880205
},
FRANKLY95P: {
activitiesCount: 6,
currency: 'CHF',
markets: {
UNKNOWN: 0,
@ -389,7 +390,6 @@ describe('redactAttributes', () => {
userId: null
}
],
transactionCount: 6,
allocationInPercentage: 0.09095764645669335,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@ -488,6 +488,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.14065892911313693
},
MSFT: {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -507,7 +508,6 @@ describe('redactAttributes', () => {
marketPrice: 428.02,
symbol: 'MSFT',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.05222646409742627,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -540,6 +540,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.08076416659271518
},
TSLA: {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -559,7 +560,6 @@ describe('redactAttributes', () => {
marketPrice: 260.46,
symbol: 'TSLA',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.1589050142378352,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -592,6 +592,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.2457342510950259
},
VTI: {
activitiesCount: 5,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -611,7 +612,6 @@ describe('redactAttributes', () => {
marketPrice: 282.05,
symbol: 'VTI',
tags: [],
transactionCount: 5,
allocationInPercentage: 0.057358979326040366,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@ -764,6 +764,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.08870120238725339
},
'VWRL.SW': {
activitiesCount: 5,
currency: 'CHF',
markets: {
UNKNOWN: 0,
@ -783,7 +784,6 @@ describe('redactAttributes', () => {
marketPrice: 117.62,
symbol: 'VWRL.SW',
tags: [],
transactionCount: 5,
allocationInPercentage: 0.09386983901959013,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@ -1172,6 +1172,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.145162408515095
},
'XDWD.DE': {
activitiesCount: 1,
currency: 'EUR',
markets: {
UNKNOWN: 0,
@ -1191,7 +1192,6 @@ describe('redactAttributes', () => {
marketPrice: 105.72,
symbol: 'XDWD.DE',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.03598477442100562,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@ -1450,6 +1450,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.055647656152211074
},
USD: {
activitiesCount: 0,
currency: 'USD',
allocationInPercentage: 0.20291717628620132,
assetClass: 'LIQUIDITY',
@ -1472,7 +1473,6 @@ describe('redactAttributes', () => {
sectors: [],
symbol: 'USD',
tags: [],
transactionCount: 0,
valueInBaseCurrency: 49890,
valueInPercentage: 0.3137956381563603
}
@ -1615,6 +1615,7 @@ describe('redactAttributes', () => {
hasError: false,
holdings: {
'AAPL.US': {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -1634,7 +1635,6 @@ describe('redactAttributes', () => {
marketPrice: 220.79,
symbol: 'AAPL.US',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.044900865255793135,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -1667,6 +1667,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.0694356974830054
},
'ALV.DE': {
activitiesCount: 2,
currency: 'EUR',
markets: {
UNKNOWN: 0,
@ -1686,7 +1687,6 @@ describe('redactAttributes', () => {
marketPrice: 296.5,
symbol: 'ALV.DE',
tags: [],
transactionCount: 2,
allocationInPercentage: 0.026912563036519527,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -1714,6 +1714,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.04161818652826481
},
AMZN: {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -1733,7 +1734,6 @@ describe('redactAttributes', () => {
marketPrice: 187.99,
symbol: 'AMZN',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.07646101417126275,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -1766,6 +1766,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.11824101426541227
},
bitcoin: {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 36985.0332704,
@ -1791,7 +1792,6 @@ describe('redactAttributes', () => {
userId: null
}
],
transactionCount: 1,
allocationInPercentage: 0.15042891393226654,
assetClass: 'LIQUIDITY',
assetSubClass: 'CRYPTOCURRENCY',
@ -1817,6 +1817,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.232626620912395
},
BONDORA_GO_AND_GROW: {
activitiesCount: 5,
currency: 'EUR',
markets: {
UNKNOWN: 2231.644722160232,
@ -1842,7 +1843,6 @@ describe('redactAttributes', () => {
userId: null
}
],
transactionCount: 5,
allocationInPercentage: 0.009076749759365777,
assetClass: 'FIXED_INCOME',
assetSubClass: 'BOND',
@ -1868,6 +1868,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.014036487867880205
},
FRANKLY95P: {
activitiesCount: 6,
currency: 'CHF',
markets: {
UNKNOWN: 0,
@ -1893,7 +1894,6 @@ describe('redactAttributes', () => {
userId: null
}
],
transactionCount: 6,
allocationInPercentage: 0.09095764645669335,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@ -1972,6 +1972,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.14065892911313693
},
MSFT: {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -1991,7 +1992,6 @@ describe('redactAttributes', () => {
marketPrice: 428.02,
symbol: 'MSFT',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.05222646409742627,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -2024,6 +2024,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.08076416659271518
},
TSLA: {
activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -2043,7 +2044,6 @@ describe('redactAttributes', () => {
marketPrice: 260.46,
symbol: 'TSLA',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.1589050142378352,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@ -2076,6 +2076,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.2457342510950259
},
VTI: {
activitiesCount: 5,
currency: 'USD',
markets: {
UNKNOWN: 0,
@ -2095,7 +2096,6 @@ describe('redactAttributes', () => {
marketPrice: 282.05,
symbol: 'VTI',
tags: [],
transactionCount: 5,
allocationInPercentage: 0.057358979326040366,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@ -2248,6 +2248,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.08870120238725339
},
'VWRL.SW': {
activitiesCount: 5,
currency: 'CHF',
markets: {
UNKNOWN: 0,
@ -2267,7 +2268,6 @@ describe('redactAttributes', () => {
marketPrice: 117.62,
symbol: 'VWRL.SW',
tags: [],
transactionCount: 5,
allocationInPercentage: 0.09386983901959013,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@ -2648,6 +2648,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.145162408515095
},
'XDWD.DE': {
activitiesCount: 1,
currency: 'EUR',
markets: {
UNKNOWN: 0,
@ -2667,7 +2668,6 @@ describe('redactAttributes', () => {
marketPrice: 105.72,
symbol: 'XDWD.DE',
tags: [],
transactionCount: 1,
allocationInPercentage: 0.03598477442100562,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@ -2926,6 +2926,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.055647656152211074
},
USD: {
activitiesCount: 0,
currency: 'USD',
allocationInPercentage: 0.20291717628620132,
assetClass: 'LIQUIDITY',
@ -2948,7 +2949,6 @@ describe('redactAttributes', () => {
sectors: [],
symbol: 'USD',
tags: [],
transactionCount: 0,
valueInBaseCurrency: null,
valueInPercentage: 0.3137956381563603
}

28
apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts

@ -206,26 +206,26 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
);
if (['ETF', 'MUTUALFUND'].includes(assetSubClass)) {
response.sectors = [];
for (const sectorWeighting of assetProfile.topHoldings
?.sectorWeightings ?? []) {
for (const [sector, weight] of Object.entries(sectorWeighting)) {
response.sectors.push({
name: this.parseSector(sector),
weight: weight as number
});
}
}
response.holdings = assetProfile.topHoldings.holdings.map(
response.holdings =
assetProfile.topHoldings?.holdings?.map(
({ holdingName, holdingPercent }) => {
return {
name: this.formatName({ longName: holdingName }),
weight: holdingPercent
};
}
);
) ?? [];
response.sectors = (
assetProfile.topHoldings?.sectorWeightings ?? []
).flatMap((sectorWeighting) => {
return Object.entries(sectorWeighting).map(([sector, weight]) => {
return {
name: this.parseSector(sector),
weight: weight as number
};
});
});
} else if (
assetSubClass === 'STOCK' &&
assetProfile.summaryProfile?.country

8
apps/api/src/services/tag/tag.service.ts

@ -75,12 +75,16 @@ export class TagService {
}
});
return tags.map(({ _count, id, name, userId }) => ({
return tags
.map(({ _count, id, name, userId }) => ({
id,
name,
userId,
isUsed: _count.activities > 0
}));
}))
.sort((a, b) => {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});
}
public async getTagsWithActivityCount() {

6
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts

@ -80,6 +80,7 @@ import { AccountDetailDialogParams } from './interfaces/interfaces';
export class GfAccountDetailDialogComponent implements OnDestroy, OnInit {
public accountBalances: AccountBalancesResponse['balances'];
public activities: OrderWithAccount[];
public activitiesCount: number;
public balance: number;
public balancePrecision = 2;
public currency: string;
@ -100,7 +101,6 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit {
public sortColumn = 'date';
public sortDirection: SortDirection = 'desc';
public totalItems: number;
public transactionCount: number;
public user: User;
public valueInBaseCurrency: number;
@ -215,16 +215,17 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit {
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(
({
activitiesCount,
balance,
currency,
dividendInBaseCurrency,
interestInBaseCurrency,
name,
platform,
transactionCount,
value,
valueInBaseCurrency
}) => {
this.activitiesCount = activitiesCount;
this.balance = balance;
if (
@ -270,7 +271,6 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit {
this.name = name;
this.platformName = platform?.name ?? '-';
this.transactionCount = transactionCount;
this.valueInBaseCurrency = valueInBaseCurrency;
this.changeDetectorRef.markForCheck();

2
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html

@ -82,7 +82,7 @@
>
</div>
<div class="col-6 mb-3">
<gf-value i18n size="medium" [value]="transactionCount"
<gf-value i18n size="medium" [value]="activitiesCount"
>Activities</gf-value
>
</div>

4
libs/common/src/lib/interfaces/admin-data.interface.ts

@ -7,10 +7,6 @@ export interface AdminData {
useForExchangeRates: boolean;
})[];
settings: { [key: string]: boolean | object | string | string[] };
/** @deprecated use activitiesCount instead */
transactionCount: number;
userCount: number;
version: string;
}

4
libs/common/src/lib/interfaces/portfolio-position.interface.ts

@ -39,10 +39,6 @@ export interface PortfolioPosition {
sectors: Sector[];
symbol: string;
tags?: Tag[];
/** @deprecated use activitiesCount instead */
transactionCount: number;
type?: string;
url?: string;
valueInBaseCurrency?: number;

3
libs/common/src/lib/interfaces/responses/accounts-response.interface.ts

@ -7,7 +7,4 @@ export interface AccountsResponse {
totalDividendInBaseCurrency: number;
totalInterestInBaseCurrency: number;
totalValueInBaseCurrency: number;
/** @deprecated use activitiesCount instead */
transactionCount: number;
}

3
libs/common/src/lib/interfaces/responses/create-stripe-checkout-session-response.interface.ts

@ -1,6 +1,3 @@
export interface CreateStripeCheckoutSessionResponse {
/** @deprecated */
sessionId: string;
sessionUrl: string;
}

3
libs/common/src/lib/models/timeline-position.ts

@ -93,9 +93,6 @@ export class TimelinePosition {
@Type(() => Big)
timeWeightedInvestmentWithCurrencyEffect: Big;
/** @deprecated use activitiesCount instead */
transactionCount: number;
@Transform(transformToBig, { toClassOnly: true })
@Type(() => Big)
valueInBaseCurrency: Big;

4
libs/common/src/lib/types/account-with-value.type.ts

@ -7,10 +7,6 @@ export type AccountWithValue = AccountModel & {
dividendInBaseCurrency: number;
interestInBaseCurrency: number;
platform?: Platform;
/** @deprecated use activitiesCount instead */
transactionCount: number;
value: number;
valueInBaseCurrency: number;
};

6
libs/ui/src/lib/accounts-table/accounts-table.component.html

@ -120,13 +120,13 @@
*matHeaderCellDef
class="justify-content-end px-1"
mat-header-cell
mat-sort-header="transactionCount"
mat-sort-header="activitiesCount"
>
<span class="d-block d-sm-none">#</span>
<span class="d-none d-sm-block" i18n>Activities</span>
</th>
<td *matCellDef="let element" class="px-1 text-right" mat-cell>
{{ element.transactionCount }}
{{ element.activitiesCount }}
</td>
<td *matFooterCellDef class="px-1 text-right" mat-footer-cell>
{{ activitiesCount }}
@ -323,7 +323,7 @@
<hr class="m-0" />
<button
mat-menu-item
[disabled]="element.transactionCount > 0"
[disabled]="element.activitiesCount > 0"
(click)="onDeleteAccount(element.id)"
>
<span class="align-items-center d-flex">

6
libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts

@ -16,6 +16,7 @@ import { GfAccountsTableComponent } from './accounts-table.component';
const accounts = [
{
activitiesCount: 0,
allocationInPercentage: null,
balance: 278,
balanceInBaseCurrency: 278,
@ -31,13 +32,13 @@ const accounts = [
url: 'https://www.coinbase.com'
},
platformId: '8dc24b88-bb92-4152-af25-fe6a31643e26',
transactionCount: 0,
updatedAt: new Date('2025-06-01T06:52:49.063Z'),
userId: '081aa387-487d-4438-83a4-3060eb2a016e',
value: 278,
valueInBaseCurrency: 278
},
{
activitiesCount: 0,
allocationInPercentage: null,
balance: 12000,
balanceInBaseCurrency: 12000,
@ -53,13 +54,13 @@ const accounts = [
url: 'https://www.jpmorgan.com'
},
platformId: '43e8fcd1-5b79-4100-b678-d2229bd1660d',
transactionCount: 0,
updatedAt: new Date('2025-06-01T06:48:53.055Z'),
userId: '081aa387-487d-4438-83a4-3060eb2a016e',
value: 12000,
valueInBaseCurrency: 12000
},
{
activitiesCount: 12,
allocationInPercentage: null,
balance: 150.2,
balanceInBaseCurrency: 150.2,
@ -75,7 +76,6 @@ const accounts = [
url: 'https://interactivebrokers.com'
},
platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737',
transactionCount: 12,
valueInBaseCurrency: 95693.70321466809,
updatedAt: new Date('2025-06-01T06:53:10.569Z'),
userId: '081aa387-487d-4438-83a4-3060eb2a016e',

7
libs/ui/src/lib/mocks/holdings.ts

@ -41,7 +41,6 @@ export const holdings: PortfolioPosition[] = [
],
symbol: 'AAPL',
tags: [],
transactionCount: 1,
url: 'https://www.apple.com',
valueInBaseCurrency: 12230
},
@ -85,7 +84,6 @@ export const holdings: PortfolioPosition[] = [
],
symbol: 'ALV.DE',
tags: [],
transactionCount: 2,
url: 'https://www.allianz.com',
valueInBaseCurrency: 6763.224181360202
},
@ -129,7 +127,6 @@ export const holdings: PortfolioPosition[] = [
],
symbol: 'AMZN',
tags: [],
transactionCount: 1,
url: 'https://www.aboutamazon.com',
valueInBaseCurrency: 22868
},
@ -161,7 +158,6 @@ export const holdings: PortfolioPosition[] = [
sectors: [],
symbol: 'bitcoin',
tags: [],
transactionCount: 1,
url: undefined,
valueInBaseCurrency: 54666.7898248
},
@ -205,7 +201,6 @@ export const holdings: PortfolioPosition[] = [
],
symbol: 'MSFT',
tags: [],
transactionCount: 1,
url: 'https://www.microsoft.com',
valueInBaseCurrency: 12252.9
},
@ -249,7 +244,6 @@ export const holdings: PortfolioPosition[] = [
],
symbol: 'TSLA',
tags: [],
transactionCount: 1,
url: 'https://www.tesla.com',
valueInBaseCurrency: 53376
},
@ -293,7 +287,6 @@ export const holdings: PortfolioPosition[] = [
],
symbol: 'VTI',
tags: [],
transactionCount: 5,
url: 'https://www.vanguard.com',
valueInBaseCurrency: 15092
}

47
libs/ui/src/lib/value/value.component.ts

@ -6,7 +6,9 @@ import {
ChangeDetectionStrategy,
Component,
Input,
OnChanges
OnChanges,
computed,
input
} from '@angular/core';
import { IonIcon } from '@ionic/angular/standalone';
import { isNumber } from 'lodash';
@ -30,7 +32,6 @@ export class GfValueComponent implements OnChanges {
@Input() isPercent = false;
@Input() locale: string;
@Input() position = '';
@Input() precision?: number;
@Input() size: 'large' | 'medium' | 'small' = 'small';
@Input() subLabel = '';
@Input() unit = '';
@ -42,12 +43,20 @@ export class GfValueComponent implements OnChanges {
public isString = false;
public useAbsoluteValue = false;
get hasPrecision(): boolean {
return (
this.precision !== undefined &&
this.precision !== null &&
this.precision >= 0
);
public readonly precision = input<number>();
private readonly formatOptions = computed<Intl.NumberFormatOptions>(() => {
const digits = this.hasPrecision ? this.precision() : 2;
return {
maximumFractionDigits: digits,
minimumFractionDigits: digits
};
});
private get hasPrecision() {
const precision = this.precision();
return precision !== undefined && precision >= 0;
}
public ngOnChanges() {
@ -59,21 +68,19 @@ export class GfValueComponent implements OnChanges {
this.isString = false;
this.absoluteValue = Math.abs(this.value);
const options = this.getFormatOptions();
if (this.colorizeSign) {
if (this.isCurrency) {
try {
this.formattedValue = this.absoluteValue.toLocaleString(
this.locale,
options
this.formatOptions()
);
} catch {}
} else if (this.isPercent) {
try {
this.formattedValue = (this.absoluteValue * 100).toLocaleString(
this.locale,
options
this.formatOptions()
);
} catch {}
}
@ -81,21 +88,21 @@ export class GfValueComponent implements OnChanges {
try {
this.formattedValue = this.value?.toLocaleString(
this.locale,
options
this.formatOptions()
);
} catch {}
} else if (this.isPercent) {
try {
this.formattedValue = (this.value * 100).toLocaleString(
this.locale,
options
this.formatOptions()
);
} catch {}
} else if (this.hasPrecision) {
try {
this.formattedValue = this.value?.toLocaleString(
this.locale,
options
this.formatOptions()
);
} catch {}
} else {
@ -130,22 +137,12 @@ export class GfValueComponent implements OnChanges {
}
}
private getFormatOptions(): Intl.NumberFormatOptions {
const digits = this.hasPrecision ? this.precision : 2;
return {
maximumFractionDigits: digits,
minimumFractionDigits: digits
};
}
private initializeVariables() {
this.absoluteValue = 0;
this.formattedValue = '';
this.isNumber = false;
this.isString = false;
this.locale = this.locale || getLocale();
this.precision = this.hasPrecision ? this.precision : undefined;
this.useAbsoluteValue = false;
}
}

4
package-lock.json

@ -1,12 +1,12 @@
{
"name": "ghostfolio",
"version": "2.235.0",
"version": "2.236.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghostfolio",
"version": "2.235.0",
"version": "2.236.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {

2
package.json

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "2.235.0",
"version": "2.236.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",

Loading…
Cancel
Save