Merge branch 'main' of github.com:ghostfolio/ghostfolio
All checks were successful
Docker image CD / build_and_push (push) Successful in 40m57s

This commit is contained in:
sudacode 2025-03-01 12:36:25 -08:00
commit b4656322c7
14 changed files with 55 additions and 40 deletions

View File

@ -7,12 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Changed
- Improved the symbol lookup in the _Trackinsight_ data enhancer for asset profile data
### Fixed
- Handled an exception in the benchmark service related to unnamed asset profiles
## 2.142.0 - 2025-02-28
### Added ### Added
- Extended the export functionality by the platforms - Extended the export functionality by the platforms
- Extended the portfolio snapshot in the portfolio calculator by the `createdAt` timestamp
- Extended the _Trackinsight_ data enhancer for asset profile data by `cusip` - Extended the _Trackinsight_ data enhancer for asset profile data by `cusip`
- Added _Storybook_ to the build process - Added _Storybook_ to the build process
### Changed
- Upgraded `eslint` dependencies
## 2.141.0 - 2025-02-25 ## 2.141.0 - 2025-02-25
### Added ### Added

View File

@ -57,7 +57,7 @@ export class PublicController {
} }
const [ const [
{ holdings, markets }, { createdAt, holdings, markets },
{ performance: performance1d }, { performance: performance1d },
{ performance: performanceMax }, { performance: performanceMax },
{ performance: performanceYtd } { performance: performanceYtd }
@ -81,6 +81,7 @@ export class PublicController {
}); });
const publicPortfolioResponse: PublicPortfolioResponse = { const publicPortfolioResponse: PublicPortfolioResponse = {
createdAt,
hasDetails, hasDetails,
markets, markets,
alias: access.alias, alias: access.alias,

View File

@ -176,6 +176,7 @@ export abstract class PortfolioCalculator {
if (!transactionPoints.length) { if (!transactionPoints.length) {
return { return {
activitiesCount: 0, activitiesCount: 0,
createdAt: new Date(),
currentValueInBaseCurrency: new Big(0), currentValueInBaseCurrency: new Big(0),
errors: [], errors: [],
hasErrors: false, hasErrors: false,

View File

@ -104,6 +104,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
activitiesCount: this.activities.filter(({ type }) => { activitiesCount: this.activities.filter(({ type }) => {
return ['BUY', 'SELL'].includes(type); return ['BUY', 'SELL'].includes(type);
}).length, }).length,
createdAt: new Date(),
errors: [], errors: [],
historicalData: [], historicalData: [],
totalLiabilitiesWithCurrencyEffect: new Big(0), totalLiabilitiesWithCurrencyEffect: new Big(0),

View File

@ -105,6 +105,7 @@ export class PortfolioController {
const { const {
accounts, accounts,
createdAt,
hasErrors, hasErrors,
holdings, holdings,
markets, markets,
@ -233,6 +234,7 @@ export class PortfolioController {
return { return {
accounts, accounts,
createdAt,
hasError, hasError,
holdings, holdings,
platforms, platforms,

View File

@ -376,7 +376,7 @@ export class PortfolioService {
currency: userCurrency currency: userCurrency
}); });
const { currentValueInBaseCurrency, hasErrors, positions } = const { createdAt, currentValueInBaseCurrency, hasErrors, positions } =
await portfolioCalculator.getSnapshot(); await portfolioCalculator.getSnapshot();
const cashDetails = await this.accountService.getCashDetails({ const cashDetails = await this.accountService.getCashDetails({
@ -617,6 +617,7 @@ export class PortfolioService {
return { return {
accounts, accounts,
createdAt,
hasErrors, hasErrors,
holdings, holdings,
markets, markets,

View File

@ -133,7 +133,9 @@ export class BenchmarkService {
symbol symbol
}; };
}) })
.sort((a, b) => a.name.localeCompare(b.name)); .sort((a, b) => {
return a.name?.localeCompare(b?.name) ?? 0;
});
} }
public async addBenchmark({ public async addBenchmark({

View File

@ -192,7 +192,10 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
.then((jsonRes) => { .then((jsonRes) => {
if ( if (
jsonRes['results']?.['count'] === 1 || jsonRes['results']?.['count'] === 1 ||
jsonRes['results']?.['docs']?.[0]?.['ticker'] === symbol // Allow exact match
jsonRes['results']?.['docs']?.[0]?.['ticker'] === symbol ||
// Allow EXCHANGE:SYMBOL
jsonRes['results']?.['docs']?.[0]?.['ticker']?.endsWith(`:${symbol}`)
) { ) {
return jsonRes['results']['docs'][0]['ticker']; return jsonRes['results']['docs'][0]['ticker'];
} }

View File

@ -260,6 +260,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
this.platforms = {}; this.platforms = {};
this.portfolioDetails = { this.portfolioDetails = {
accounts: {}, accounts: {},
createdAt: undefined,
holdings: {}, holdings: {},
platforms: {}, platforms: {},
summary: undefined summary: undefined

View File

@ -14,6 +14,7 @@ export interface PortfolioDetails {
valueInPercentage?: number; valueInPercentage?: number;
}; };
}; };
createdAt: Date;
holdings: { [symbol: string]: PortfolioPosition }; holdings: { [symbol: string]: PortfolioPosition };
markets?: { markets?: {
[key in Market]: { [key in Market]: {

View File

@ -32,6 +32,7 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 {
} }
interface PublicPortfolioResponseV1 { interface PublicPortfolioResponseV1 {
createdAt: Date;
performance: { performance: {
'1d': { '1d': {
relativeChange: number; relativeChange: number;

View File

@ -11,6 +11,8 @@ import { Transform, Type } from 'class-transformer';
export class PortfolioSnapshot { export class PortfolioSnapshot {
activitiesCount: number; activitiesCount: number;
createdAt: Date;
@Transform(transformToBig, { toClassOnly: true }) @Transform(transformToBig, { toClassOnly: true })
@Type(() => Big) @Type(() => Big)
currentValueInBaseCurrency: Big; currentValueInBaseCurrency: Big;

50
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.141.0", "version": "2.142.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.141.0", "version": "2.142.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
@ -141,9 +141,9 @@
"cypress": "6.2.1", "cypress": "6.2.1",
"eslint": "9.18.0", "eslint": "9.18.0",
"eslint-config-prettier": "9.1.0", "eslint-config-prettier": "9.1.0",
"eslint-plugin-cypress": "3.2.0", "eslint-plugin-cypress": "4.1.0",
"eslint-plugin-import": "2.31.0", "eslint-plugin-import": "2.31.0",
"eslint-plugin-storybook": "0.10.2", "eslint-plugin-storybook": "0.11.2",
"husky": "9.1.7", "husky": "9.1.7",
"jest": "29.7.0", "jest": "29.7.0",
"jest-environment-jsdom": "29.7.0", "jest-environment-jsdom": "29.7.0",
@ -17087,42 +17087,26 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/eslint-plugin-cypress": { "node_modules/eslint-plugin-cypress": {
"version": "3.2.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-3.2.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-4.1.0.tgz",
"integrity": "sha512-HaxMz6BoU4ay+K4WrG9ZJC1NdX06FqSlAwtRDStjM0ORFT7zCNPNuRJ+kUPc17Rt2AMUBSqeD9L0zTR3uZhPpw==", "integrity": "sha512-JhqkMY02mw74USwK9OFhectx3YSj6Co1NgWBxlGdKvlqiAp9vdEuQqt33DKGQFvvGS/NWtduuhWXWNnU29xDSg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"globals": "^13.20.0" "globals": "^15.11.0"
}, },
"peerDependencies": { "peerDependencies": {
"eslint": ">=7" "eslint": ">=9"
} }
}, },
"node_modules/eslint-plugin-cypress/node_modules/globals": { "node_modules/eslint-plugin-cypress/node_modules/globals": {
"version": "13.24.0", "version": "15.14.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": {
"type-fest": "^0.20.2"
},
"engines": { "engines": {
"node": ">=8" "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint-plugin-cypress/node_modules/type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
@ -17216,9 +17200,9 @@
} }
}, },
"node_modules/eslint-plugin-storybook": { "node_modules/eslint-plugin-storybook": {
"version": "0.10.2", "version": "0.11.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.10.2.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.11.2.tgz",
"integrity": "sha512-iOrFJfyLgRLIbWDLbbs3J4yrknvIB+uiZ8KGqGOEXTL7/BGuBMZLiIU9Q4Pm/VYJrHN4JqmMtwCSrAemHL2nFg==", "integrity": "sha512-0Z4DUklJrC+GHjCRXa7PYfPzWC15DaVnwaOYenpgXiCEijXPZkLKCms+rHhtoRcWccP7Z8DpOOaP1gc3P9oOwg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -17230,7 +17214,7 @@
"node": ">= 18" "node": ">= 18"
}, },
"peerDependencies": { "peerDependencies": {
"eslint": ">=6" "eslint": ">=8"
} }
}, },
"node_modules/eslint-scope": { "node_modules/eslint-scope": {

View File

@ -1,6 +1,6 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.141.0", "version": "2.142.0",
"homepage": "https://ghostfol.io", "homepage": "https://ghostfol.io",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio", "repository": "https://github.com/ghostfolio/ghostfolio",
@ -187,9 +187,9 @@
"cypress": "6.2.1", "cypress": "6.2.1",
"eslint": "9.18.0", "eslint": "9.18.0",
"eslint-config-prettier": "9.1.0", "eslint-config-prettier": "9.1.0",
"eslint-plugin-cypress": "3.2.0", "eslint-plugin-cypress": "4.1.0",
"eslint-plugin-import": "2.31.0", "eslint-plugin-import": "2.31.0",
"eslint-plugin-storybook": "0.10.2", "eslint-plugin-storybook": "0.11.2",
"husky": "9.1.7", "husky": "9.1.7",
"jest": "29.7.0", "jest": "29.7.0",
"jest-environment-jsdom": "29.7.0", "jest-environment-jsdom": "29.7.0",