Feature/simplify benchmark configuration (#1248)

* Simplify benchmark configuration

* Update changelog
pull/1249/head
Thomas Kaul 2 years ago committed by GitHub
parent 0fcfa6c1bd
commit e320aa91f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Simplified the configuration of the benchmarks: `symbolProfileId` instead of `dataSource` and `symbol`
- Upgraded `yahoo-finance2` from version `2.3.3` to `2.3.6`
### Fixed

@ -12,6 +12,7 @@ import {
UniqueAsset
} from '@ghostfolio/common/interfaces';
import { Injectable } from '@nestjs/common';
import { SymbolProfile } from '@prisma/client';
import Big from 'big.js';
import { format } from 'date-fns';
import ms from 'ms';
@ -55,25 +56,25 @@ export class BenchmarkService {
} catch {}
}
const benchmarkAssets: UniqueAsset[] =
((await this.propertyService.getByKey(
PROPERTY_BENCHMARKS
)) as UniqueAsset[]) ?? [];
const benchmarkAssetProfiles = await this.getBenchmarkAssetProfiles();
const promises: Promise<number>[] = [];
const [quotes, assetProfiles] = await Promise.all([
this.dataProviderService.getQuotes(benchmarkAssets),
this.symbolProfileService.getSymbolProfiles(benchmarkAssets)
]);
const quotes = await this.dataProviderService.getQuotes(
benchmarkAssetProfiles.map(({ dataSource, symbol }) => {
return { dataSource, symbol };
})
);
for (const benchmarkAsset of benchmarkAssets) {
promises.push(this.marketDataService.getMax(benchmarkAsset));
for (const { dataSource, symbol } of benchmarkAssetProfiles) {
promises.push(this.marketDataService.getMax({ dataSource, symbol }));
}
const allTimeHighs = await Promise.all(promises);
benchmarks = allTimeHighs.map((allTimeHigh, index) => {
const { marketPrice } = quotes[benchmarkAssets[index].symbol] ?? {};
const { marketPrice } =
quotes[benchmarkAssetProfiles[index].symbol] ?? {};
let performancePercentFromAllTimeHigh = 0;
@ -88,12 +89,7 @@ export class BenchmarkService {
marketCondition: this.getMarketCondition(
performancePercentFromAllTimeHigh
),
name: assetProfiles.find(({ dataSource, symbol }) => {
return (
dataSource === benchmarkAssets[index].dataSource &&
symbol === benchmarkAssets[index].symbol
);
})?.name,
name: benchmarkAssetProfiles[index].name,
performances: {
allTimeHigh: {
performancePercent: performancePercentFromAllTimeHigh
@ -111,19 +107,23 @@ export class BenchmarkService {
return benchmarks;
}
public async getBenchmarkAssetProfiles(): Promise<UniqueAsset[]> {
const benchmarkAssets: UniqueAsset[] =
((await this.propertyService.getByKey(
PROPERTY_BENCHMARKS
)) as UniqueAsset[]) ?? [];
public async getBenchmarkAssetProfiles(): Promise<Partial<SymbolProfile>[]> {
const symbolProfileIds: string[] = (
((await this.propertyService.getByKey(PROPERTY_BENCHMARKS)) as {
symbolProfileId: string;
}[]) ?? []
).map(({ symbolProfileId }) => {
return symbolProfileId;
});
const assetProfiles = await this.symbolProfileService.getSymbolProfiles(
benchmarkAssets
);
const assetProfiles =
await this.symbolProfileService.getSymbolProfilesByIds(symbolProfileIds);
return assetProfiles.map(({ dataSource, symbol }) => {
return assetProfiles.map(({ dataSource, id, name, symbol }) => {
return {
dataSource,
id,
name,
symbol
};
});

@ -1,10 +1,8 @@
import { UniqueAsset } from '@ghostfolio/common/interfaces';
import type { DateRange, ViewMode } from '@ghostfolio/common/types';
import {
IsBoolean,
IsIn,
IsNumber,
IsObject,
IsOptional,
IsString
} from 'class-validator';
@ -14,9 +12,9 @@ export class UpdateUserSettingDto {
@IsString()
baseCurrency?: string;
@IsObject()
@IsString()
@IsOptional()
benchmark?: UniqueAsset;
benchmark?: string;
@IsIn(<DateRange[]>['1d', '1y', '5y', 'max', 'ytd'])
@IsOptional()

@ -102,10 +102,10 @@ export class UserController {
public async updateUserSetting(@Body() data: UpdateUserSettingDto) {
if (
size(data) === 1 &&
data.dateRange &&
(data.benchmark || data.dateRange) &&
this.request.user.role === 'DEMO'
) {
// Allow date range change for demo user
// Allow benchmark or date range change for demo user
} else if (
!hasPermission(
this.request.user.permissions,

@ -64,6 +64,23 @@ export class SymbolProfileService {
.then((symbolProfiles) => this.getSymbols(symbolProfiles));
}
public async getSymbolProfilesByIds(
symbolProfileIds: string[]
): Promise<EnhancedSymbolProfile[]> {
return this.prismaService.symbolProfile
.findMany({
include: { SymbolProfileOverrides: true },
where: {
id: {
in: symbolProfileIds.map((symbolProfileId) => {
return symbolProfileId;
})
}
}
})
.then((symbolProfiles) => this.getSymbols(symbolProfiles));
}
/**
* @deprecated
*/

@ -14,14 +14,13 @@
<mat-label i18n>Compare with...</mat-label>
<mat-select
name="benchmark"
[compareWith]="compareUniqueAssets"
[value]="benchmark"
(selectionChange)="onChangeBenchmark($event.value)"
>
<mat-option
*ngFor="let currentBenchmark of benchmarks"
[value]="currentBenchmark"
>{{ currentBenchmark.symbol }}</mat-option
*ngFor="let symbolProfile of benchmarks"
[value]="symbolProfile.id"
>{{ symbolProfile.name }}</mat-option
>
</mat-select>
</mat-form-field>

@ -39,6 +39,7 @@ import {
Tooltip
} from 'chart.js';
import annotationPlugin from 'chartjs-plugin-annotation';
import { SymbolProfile } from '@prisma/client';
@Component({
selector: 'gf-benchmark-comparator',
@ -48,14 +49,14 @@ import annotationPlugin from 'chartjs-plugin-annotation';
})
export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
@Input() benchmarkDataItems: LineChartItem[] = [];
@Input() benchmark: UniqueAsset;
@Input() benchmarks: UniqueAsset[];
@Input() benchmark: string;
@Input() benchmarks: Partial<SymbolProfile>[];
@Input() daysInMarket: number;
@Input() locale: string;
@Input() performanceDataItems: LineChartItem[];
@Input() user: User;
@Output() benchmarkChanged = new EventEmitter<UniqueAsset>();
@Output() benchmarkChanged = new EventEmitter<string>();
@Output() dateRangeChanged = new EventEmitter<DateRange>();
@ViewChild('chartCanvas') chartCanvas;
@ -85,18 +86,8 @@ export class BenchmarkComparatorComponent implements OnChanges, OnDestroy {
}
}
public compareUniqueAssets(
uniqueAsset1: UniqueAsset,
uniqueAsset2: UniqueAsset
) {
return (
uniqueAsset1?.dataSource === uniqueAsset2?.dataSource &&
uniqueAsset1?.symbol === uniqueAsset2?.symbol
);
}
public onChangeBenchmark(benchmark: UniqueAsset) {
this.benchmarkChanged.next(benchmark);
public onChangeBenchmark(symbolProfileId: string) {
this.benchmarkChanged.next(symbolProfileId);
}
public onChangeDateRange(dateRange: DateRange) {

@ -5,11 +5,11 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
import {
HistoricalDataItem,
Position,
UniqueAsset,
User
} from '@ghostfolio/common/interfaces';
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
import { DateRange, GroupBy, ToggleOption } from '@ghostfolio/common/types';
import { SymbolProfile } from '@prisma/client';
import { differenceInDays } from 'date-fns';
import { sortBy } from 'lodash';
import { DeviceDetectorService } from 'ngx-device-detector';
@ -24,7 +24,7 @@ import { takeUntil } from 'rxjs/operators';
})
export class AnalysisPageComponent implements OnDestroy, OnInit {
public benchmarkDataItems: HistoricalDataItem[] = [];
public benchmarks: UniqueAsset[];
public benchmarks: Partial<SymbolProfile>[];
public bottom3: Position[];
public daysInMarket: number;
public deviceType: string;
@ -75,9 +75,9 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
});
}
public onChangeBenchmark(benchmark: UniqueAsset) {
public onChangeBenchmark(symbolProfileId: string) {
this.dataService
.putUserSetting({ benchmark })
.putUserSetting({ benchmark: symbolProfileId })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.userService.remove();
@ -179,9 +179,15 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
private updateBenchmarkDataItems() {
if (this.user.settings.benchmark) {
const { dataSource, symbol } =
this.benchmarks.find(({ id }) => {
return id === this.user.settings.benchmark;
}) ?? {};
this.dataService
.fetchBenchmarkBySymbol({
...this.user.settings.benchmark,
dataSource,
symbol,
startDate: this.firstOrderDate
})
.pipe(takeUntil(this.unsubscribeSubject))

@ -1,12 +1,11 @@
import { Tag } from '@prisma/client';
import { SymbolProfile, Tag } from '@prisma/client';
import { Statistics } from './statistics.interface';
import { Subscription } from './subscription.interface';
import { UniqueAsset } from './unique-asset.interface';
export interface InfoItem {
baseCurrency: string;
benchmarks: UniqueAsset[];
benchmarks: Partial<SymbolProfile>[];
currencies: string[];
demoAuthToken: string;
fearAndGreedDataSource?: string;

@ -1,10 +1,8 @@
import { DateRange, ViewMode } from '@ghostfolio/common/types';
import { UniqueAsset } from './unique-asset.interface';
export interface UserSettings {
baseCurrency?: string;
benchmark?: UniqueAsset;
benchmark?: string;
dateRange?: DateRange;
emergencyFund?: number;
isExperimentalFeatures?: boolean;

Loading…
Cancel
Save