Browse Source

feat: add filters for chart, perf, positions, summary

pull/1290/head
Zakaria YAHI 3 years ago
parent
commit
06941046be
  1. 84
      apps/api/src/app/portfolio/portfolio.controller.ts
  2. 33
      apps/api/src/app/portfolio/portfolio.service.ts
  3. 4
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  4. 4
      apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts
  5. 177
      apps/client/src/app/services/data.service.ts

84
apps/api/src/app/portfolio/portfolio.controller.ts

@ -65,11 +65,19 @@ export class PortfolioController {
@UseGuards(AuthGuard('jwt'))
public async getChart(
@Headers('impersonation-id') impersonationId: string,
@Query('range') range
@Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string,
@Query('range') range?: DateRange,
@Query('tags') filterByTags?: string
): Promise<PortfolioChart> {
const historicalDataContainer = await this.portfolioService.getChart(
impersonationId,
range
range,
this.buildFiltersFromQueries(
filterByAccounts,
filterByAssetClasses,
filterByTags
)
);
let chartData = historicalDataContainer.items;
@ -278,11 +286,19 @@ export class PortfolioController {
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getPerformance(
@Headers('impersonation-id') impersonationId: string,
@Query('range') range
@Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string,
@Query('range') range?: DateRange,
@Query('tags') filterByTags?: string
): Promise<PortfolioPerformanceResponse> {
const performanceInformation = await this.portfolioService.getPerformance(
impersonationId,
range
range,
this.buildFiltersFromQueries(
filterByAccounts,
filterByAssetClasses,
filterByTags
)
);
if (
@ -333,11 +349,19 @@ export class PortfolioController {
@UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getPositions(
@Headers('impersonation-id') impersonationId: string,
@Query('range') range
@Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string,
@Query('range') range?: DateRange,
@Query('tags') filterByTags?: string
): Promise<PortfolioPositions> {
const result = await this.portfolioService.getPositions(
impersonationId,
range
range,
this.buildFiltersFromQueries(
filterByAccounts,
filterByAssetClasses,
filterByTags
)
);
if (
@ -423,7 +447,10 @@ export class PortfolioController {
@Get('summary')
@UseGuards(AuthGuard('jwt'))
public async getSummary(
@Headers('impersonation-id') impersonationId
@Headers('impersonation-id') impersonationId,
@Query('accounts') filterByAccounts?: string,
@Query('assetClasses') filterByAssetClasses?: string,
@Query('tags') filterByTags?: string
): Promise<PortfolioSummary> {
if (
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') &&
@ -435,7 +462,15 @@ export class PortfolioController {
);
}
let summary = await this.portfolioService.getSummary(impersonationId);
const filters = this.buildFiltersFromQueries(
filterByAccounts,
filterByAssetClasses,
filterByTags
);
let summary = await this.portfolioService.getSummary(
impersonationId,
filters
);
if (
impersonationId ||
@ -516,4 +551,37 @@ export class PortfolioController {
return await this.portfolioService.getReport(impersonationId);
}
private buildFiltersFromQueries(
filterByAccounts?: string,
filterByAssetClasses?: string,
filterByTags?: string
) {
const accountIds = filterByAccounts?.split(',') ?? [];
const assetClasses = filterByAssetClasses?.split(',') ?? [];
const tagIds = filterByTags?.split(',') ?? [];
const filters: Filter[] = [
...accountIds.map((accountId) => {
return <Filter>{
id: accountId,
type: 'ACCOUNT'
};
}),
...assetClasses.map((assetClass) => {
return <Filter>{
id: assetClass,
type: 'ASSET_CLASS'
};
}),
...tagIds.map((tagId) => {
return <Filter>{
id: tagId,
type: 'TAG'
};
})
];
return filters;
}
}

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

@ -268,13 +268,15 @@ export class PortfolioService {
public async getChart(
aImpersonationId: string,
aDateRange: DateRange = 'max'
aDateRange: DateRange = 'max',
aFilters?: Filter[]
): Promise<HistoricalDataContainer> {
const userId = await this.getUserId(aImpersonationId, this.request.user.id);
const { portfolioOrders, transactionPoints } =
await this.getTransactionPoints({
userId
userId,
filters: aFilters
});
const portfolioCalculator = new PortfolioCalculator({
@ -845,13 +847,15 @@ export class PortfolioService {
public async getPositions(
aImpersonationId: string,
aDateRange: DateRange = 'max'
aDateRange: DateRange = 'max',
aFilters?: Filter[]
): Promise<{ hasErrors: boolean; positions: Position[] }> {
const userId = await this.getUserId(aImpersonationId, this.request.user.id);
const { portfolioOrders, transactionPoints } =
await this.getTransactionPoints({
userId
userId,
filters: aFilters
});
const portfolioCalculator = new PortfolioCalculator({
@ -921,12 +925,14 @@ export class PortfolioService {
public async getPerformance(
aImpersonationId: string,
aDateRange: DateRange = 'max'
aDateRange: DateRange = 'max',
aFilters?: Filter[]
): Promise<PortfolioPerformanceResponse> {
const userId = await this.getUserId(aImpersonationId, this.request.user.id);
const { portfolioOrders, transactionPoints } =
await this.getTransactionPoints({
filters: aFilters,
userId
});
@ -1181,20 +1187,29 @@ export class PortfolioService {
};
}
public async getSummary(aImpersonationId: string): Promise<PortfolioSummary> {
public async getSummary(
aImpersonationId: string,
aFilters?: Filter[]
): Promise<PortfolioSummary> {
const userCurrency = this.request.user.Settings.settings.baseCurrency;
const userId = await this.getUserId(aImpersonationId, this.request.user.id);
const user = await this.userService.user({ id: userId });
const performanceInformation = await this.getPerformance(aImpersonationId);
const performanceInformation = await this.getPerformance(
aImpersonationId,
undefined,
aFilters
);
const { balanceInBaseCurrency } = await this.accountService.getCashDetails({
userId,
currency: userCurrency
currency: userCurrency,
filters: aFilters
});
const orders = await this.orderService.getOrders({
userCurrency,
userId
userId,
filters: aFilters
});
const dividend = this.getDividend(orders).toNumber();
const emergencyFund = new Big(

4
apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

@ -136,9 +136,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
? $localize`Filter by account or tag...`
: '';
return this.dataService.fetchPortfolioDetails({
filters: this.activeFilters
});
return this.dataService.fetchPortfolioDetails(this.activeFilters);
}),
takeUntil(this.unsubscribeSubject)
)

4
apps/client/src/app/pages/portfolio/holdings/holdings-page.component.ts

@ -87,9 +87,7 @@ export class HoldingsPageComponent implements OnDestroy, OnInit {
? $localize`Filter by account or tag...`
: '';
return this.dataService.fetchPortfolioDetails({
filters: this.activeFilters
});
return this.dataService.fetchPortfolioDetails(this.activeFilters);
}),
takeUntil(this.unsubscribeSubject)
)

177
apps/client/src/app/services/data.service.ts

@ -75,52 +75,13 @@ export class DataService {
}: {
filters?: Filter[];
}): Observable<Activities> {
let params = new HttpParams();
if (filters?.length > 0) {
const {
ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass,
TAG: filtersByTag
} = groupBy(filters, (filter) => {
return filter.type;
});
if (filtersByAccount) {
params = params.append(
'accounts',
filtersByAccount
.map(({ id }) => {
return id;
})
.join(',')
);
return this.http
.get<any>('/api/v1/order', {
params: {
...this.buildParamsFilter(filters)
}
if (filtersByAssetClass) {
params = params.append(
'assetClasses',
filtersByAssetClass
.map(({ id }) => {
return id;
})
.join(',')
);
}
if (filtersByTag) {
params = params.append(
'tags',
filtersByTag
.map(({ id }) => {
return id;
})
.join(',')
);
}
}
return this.http.get<any>('/api/v1/order', { params }).pipe(
.pipe(
map(({ activities }) => {
for (const activity of activities) {
activity.createdAt = parseISO(activity.createdAt);
@ -202,9 +163,20 @@ export class DataService {
return this.http.get<BenchmarkResponse>('/api/v1/benchmark');
}
public fetchChart({ range, version }: { range: DateRange; version: number }) {
public fetchChart({
range,
version,
filters
}: {
range: DateRange;
version: number;
filters?: Filter[];
}) {
return this.http.get<PortfolioChart>(`/api/v${version}/portfolio/chart`, {
params: { range }
params: {
range,
...this.buildParamsFilter(filters)
}
});
}
@ -283,12 +255,14 @@ export class DataService {
}
public fetchPositions({
range
range,
filters
}: {
range: DateRange;
filters?: Filter[];
}): Observable<PortfolioPositions> {
return this.http.get<PortfolioPositions>('/api/v1/portfolio/positions', {
params: { range }
params: { range, ...this.buildParamsFilter(filters) }
});
}
@ -302,67 +276,24 @@ export class DataService {
);
}
public fetchPortfolioDetails({ filters }: { filters?: Filter[] }) {
let params = new HttpParams();
if (filters?.length > 0) {
const {
ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass,
TAG: filtersByTag
} = groupBy(filters, (filter) => {
return filter.type;
});
if (filtersByAccount) {
params = params.append(
'accounts',
filtersByAccount
.map(({ id }) => {
return id;
})
.join(',')
);
}
if (filtersByAssetClass) {
params = params.append(
'assetClasses',
filtersByAssetClass
.map(({ id }) => {
return id;
})
.join(',')
);
}
if (filtersByTag) {
params = params.append(
'tags',
filtersByTag
.map(({ id }) => {
return id;
})
.join(',')
);
}
}
public fetchPortfolioDetails(filters?: Filter[]) {
return this.http.get<PortfolioDetails>('/api/v1/portfolio/details', {
params
params: { ...this.buildParamsFilter(filters) }
});
}
public fetchPortfolioPerformance({
range,
version
version,
filters
}: {
range: DateRange;
version: number;
filters?: Filter[];
}) {
return this.http.get<PortfolioPerformanceResponse>(
`/api/v${version}/portfolio/performance`,
{ params: { range } }
{ params: { range, ...this.buildParamsFilter(filters) } }
);
}
@ -376,8 +307,14 @@ export class DataService {
return this.http.get<PortfolioReport>('/api/v1/portfolio/report');
}
public fetchPortfolioSummary(): Observable<PortfolioSummary> {
return this.http.get<any>('/api/v1/portfolio/summary').pipe(
public fetchPortfolioSummary(
filters?: Filter[]
): Observable<PortfolioSummary> {
return this.http
.get<any>('/api/v1/portfolio/summary', {
params: { ...this.buildParamsFilter(filters) }
})
.pipe(
map((summary) => {
if (summary.firstOrderDate) {
summary.firstOrderDate = parseISO(summary.firstOrderDate);
@ -456,4 +393,46 @@ export class DataService {
couponCode
});
}
private buildParamsFilter(filters: Filter[]) {
const paramsFilter: {
accounts?: string;
assetClasses?: string;
tags?: string;
} = {};
if (filters?.length > 0) {
const {
ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass,
TAG: filtersByTag
} = groupBy(filters, (filter) => {
return filter.type;
});
if (filtersByAccount) {
paramsFilter.accounts = filtersByAccount
.map(({ id }) => {
return id;
})
.join(',');
}
if (filtersByAssetClass) {
paramsFilter.assetClasses = filtersByAssetClass
.map(({ id }) => {
return id;
})
.join(',');
}
if (filtersByTag) {
paramsFilter.tags = filtersByTag
.map(({ id }) => {
return id;
})
.join(',');
}
}
return paramsFilter;
}
}

Loading…
Cancel
Save