diff --git a/CHANGELOG.md b/CHANGELOG.md index 652c2a94..0af5d858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,18 +5,22 @@ 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.137.1 - 2025-02-01 ### Added - Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (North America) +- Added support for ETF sector data in the _Yahoo Finance_ data enhancer ### Changed +- Extracted the scraper configuration to a sub form in the asset profile details dialog of the admin control - Migrated the database seeding to _TypeScript_ +- Improved the language localization for German (`de`) - Upgraded `@trivago/prettier-plugin-sort-imports` from version `4.3.0` to `5.2.1` - Upgraded `bull` from version `4.16.4` to `4.16.5` - Upgraded `ng-extract-i18n-merge` from version `2.13.1` to `2.14.1` +- Upgraded `prisma` from version `6.2.1` to `6.3.0` ### Fixed 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 b71e3e31..18d62409 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 @@ -197,7 +197,7 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface { assetProfile.price.symbol ); - if (assetSubClass === AssetSubClass.MUTUALFUND) { + if (['ETF', 'MUTUALFUND'].includes(assetSubClass)) { response.sectors = []; for (const sectorWeighting of assetProfile.topHoldings @@ -207,7 +207,7 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface { } } } else if ( - assetSubClass === AssetSubClass.STOCK && + assetSubClass === 'STOCK' && assetProfile.summaryProfile?.country ) { // Add country if asset is stock and country available diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss index 7057aad8..a9e13578 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss @@ -7,5 +7,21 @@ gf-line-chart { aspect-ratio: 16/9; } + + .mat-expansion-panel { + --mat-expansion-container-background-color: transparent; + + ::ng-deep { + .mat-expansion-panel-body { + padding: 0; + } + } + + .mat-expansion-panel-header { + &:hover { + --mat-expansion-header-hover-state-layer-color: transparent; + } + } + } } } 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 a144bae8..bb19ad96 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 @@ -21,7 +21,8 @@ import { Component, Inject, OnDestroy, - OnInit + OnInit, + signal } from '@angular/core'; import { FormBuilder, FormControl, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @@ -64,7 +65,14 @@ export class AssetProfileDialog implements OnDestroy, OnInit { csvString: '' }), name: ['', Validators.required], - scraperConfiguration: '', + scraperConfiguration: this.formBuilder.group({ + defaultMarketPrice: null, + headers: JSON.stringify({}), + locale: '', + mode: '', + selector: '', + url: '' + }), sectors: '', symbolMapping: '', url: '' @@ -79,6 +87,11 @@ export class AssetProfileDialog implements OnDestroy, OnInit { public historicalDataItems: LineChartItem[]; public isBenchmark = false; public marketDataItems: MarketData[] = []; + public modeValues = [ + { value: 'lazy', viewValue: $localize`Lazy` }, + { value: 'instant', viewValue: $localize`Instant` } + ]; + public scraperConfiguationIsExpanded = signal(false); public sectors: { [name: string]: { name: string; value: number }; }; @@ -181,9 +194,18 @@ export class AssetProfileDialog implements OnDestroy, OnInit { csvString: AssetProfileDialog.HISTORICAL_DATA_TEMPLATE }, name: this.assetProfile.name ?? this.assetProfile.symbol, - scraperConfiguration: JSON.stringify( - this.assetProfile?.scraperConfiguration ?? {} - ), + scraperConfiguration: { + defaultMarketPrice: + this.assetProfile?.scraperConfiguration?.defaultMarketPrice ?? + null, + headers: JSON.stringify( + this.assetProfile?.scraperConfiguration?.headers ?? {} + ), + locale: this.assetProfile?.scraperConfiguration?.locale ?? '', + mode: this.assetProfile?.scraperConfiguration?.mode ?? 'lazy', + selector: this.assetProfile?.scraperConfiguration?.selector ?? '', + url: this.assetProfile?.scraperConfiguration?.url ?? '' + }, sectors: JSON.stringify(this.assetProfile?.sectors ?? []), symbolMapping: JSON.stringify(this.assetProfile?.symbolMapping ?? {}), url: this.assetProfile?.url ?? '' @@ -252,9 +274,31 @@ export class AssetProfileDialog implements OnDestroy, OnInit { } catch {} try { - scraperConfiguration = JSON.parse( - this.assetProfileForm.get('scraperConfiguration').value - ); + scraperConfiguration = { + defaultMarketPrice: + this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'defaultMarketPrice' + ].value, + headers: JSON.parse( + this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'headers' + ].value + ), + locale: + this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'locale' + ].value, + mode: this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'mode' + ].value, + selector: + this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'selector' + ].value, + url: this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'url' + ].value + }; } catch {} try { @@ -306,8 +350,31 @@ export class AssetProfileDialog implements OnDestroy, OnInit { this.adminService .testMarketData({ dataSource: this.data.dataSource, - scraperConfiguration: this.assetProfileForm.get('scraperConfiguration') - .value, + scraperConfiguration: JSON.stringify({ + defaultMarketPrice: + this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'defaultMarketPrice' + ].value, + headers: JSON.parse( + this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'headers' + ].value + ), + locale: + this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'locale' + ].value, + mode: this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'mode' + ].value, + selector: + this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'selector' + ].value, + url: this.assetProfileForm.controls['scraperConfiguration'].controls[ + 'url' + ].value + }), symbol: this.data.symbol }) .pipe( 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 eeb43e93..595ec28c 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 @@ -278,31 +278,106 @@ @if (assetProfile?.dataSource === 'MANUAL') { -