|
|
@ -37,7 +37,7 @@ import { |
|
|
|
PortfolioInvestments, |
|
|
|
PortfolioPerformanceResponse, |
|
|
|
PortfolioPosition, |
|
|
|
PortfolioReport, |
|
|
|
PortfolioReportResponse, |
|
|
|
PortfolioSummary, |
|
|
|
Position, |
|
|
|
UserSettings |
|
|
@ -1162,7 +1162,9 @@ export class PortfolioService { |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
public async getReport(impersonationId: string): Promise<PortfolioReport> { |
|
|
|
public async getReport( |
|
|
|
impersonationId: string |
|
|
|
): Promise<PortfolioReportResponse> { |
|
|
|
const userId = await this.getUserId(impersonationId, this.request.user.id); |
|
|
|
const userSettings = this.request.user.Settings.settings as UserSettings; |
|
|
|
|
|
|
@ -1179,79 +1181,79 @@ export class PortfolioService { |
|
|
|
}) |
|
|
|
).toNumber(); |
|
|
|
|
|
|
|
return { |
|
|
|
rules: { |
|
|
|
accountClusterRisk: |
|
|
|
summary.ordersCount > 0 |
|
|
|
? await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new AccountClusterRiskCurrentInvestment( |
|
|
|
this.exchangeRateDataService, |
|
|
|
accounts |
|
|
|
), |
|
|
|
new AccountClusterRiskSingleAccount( |
|
|
|
this.exchangeRateDataService, |
|
|
|
accounts |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
) |
|
|
|
: undefined, |
|
|
|
economicMarketClusterRisk: |
|
|
|
summary.ordersCount > 0 |
|
|
|
? await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new EconomicMarketClusterRiskDevelopedMarkets( |
|
|
|
this.exchangeRateDataService, |
|
|
|
marketsTotalInBaseCurrency, |
|
|
|
markets.developedMarkets.valueInBaseCurrency |
|
|
|
), |
|
|
|
new EconomicMarketClusterRiskEmergingMarkets( |
|
|
|
this.exchangeRateDataService, |
|
|
|
marketsTotalInBaseCurrency, |
|
|
|
markets.emergingMarkets.valueInBaseCurrency |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
) |
|
|
|
: undefined, |
|
|
|
currencyClusterRisk: |
|
|
|
summary.ordersCount > 0 |
|
|
|
? await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new CurrencyClusterRiskBaseCurrencyCurrentInvestment( |
|
|
|
this.exchangeRateDataService, |
|
|
|
Object.values(holdings) |
|
|
|
), |
|
|
|
new CurrencyClusterRiskCurrentInvestment( |
|
|
|
this.exchangeRateDataService, |
|
|
|
Object.values(holdings) |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
) |
|
|
|
: undefined, |
|
|
|
emergencyFund: await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new EmergencyFundSetup( |
|
|
|
this.exchangeRateDataService, |
|
|
|
userSettings.emergencyFund |
|
|
|
const rules: PortfolioReportResponse['rules'] = { |
|
|
|
accountClusterRisk: |
|
|
|
summary.ordersCount > 0 |
|
|
|
? await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new AccountClusterRiskCurrentInvestment( |
|
|
|
this.exchangeRateDataService, |
|
|
|
accounts |
|
|
|
), |
|
|
|
new AccountClusterRiskSingleAccount( |
|
|
|
this.exchangeRateDataService, |
|
|
|
accounts |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
), |
|
|
|
fees: await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new FeeRatioInitialInvestment( |
|
|
|
this.exchangeRateDataService, |
|
|
|
summary.committedFunds, |
|
|
|
summary.fees |
|
|
|
: undefined, |
|
|
|
economicMarketClusterRisk: |
|
|
|
summary.ordersCount > 0 |
|
|
|
? await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new EconomicMarketClusterRiskDevelopedMarkets( |
|
|
|
this.exchangeRateDataService, |
|
|
|
marketsTotalInBaseCurrency, |
|
|
|
markets.developedMarkets.valueInBaseCurrency |
|
|
|
), |
|
|
|
new EconomicMarketClusterRiskEmergingMarkets( |
|
|
|
this.exchangeRateDataService, |
|
|
|
marketsTotalInBaseCurrency, |
|
|
|
markets.emergingMarkets.valueInBaseCurrency |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
) |
|
|
|
} |
|
|
|
: undefined, |
|
|
|
currencyClusterRisk: |
|
|
|
summary.ordersCount > 0 |
|
|
|
? await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new CurrencyClusterRiskBaseCurrencyCurrentInvestment( |
|
|
|
this.exchangeRateDataService, |
|
|
|
Object.values(holdings) |
|
|
|
), |
|
|
|
new CurrencyClusterRiskCurrentInvestment( |
|
|
|
this.exchangeRateDataService, |
|
|
|
Object.values(holdings) |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
) |
|
|
|
: undefined, |
|
|
|
emergencyFund: await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new EmergencyFundSetup( |
|
|
|
this.exchangeRateDataService, |
|
|
|
userSettings.emergencyFund |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
), |
|
|
|
fees: await this.rulesService.evaluate( |
|
|
|
[ |
|
|
|
new FeeRatioInitialInvestment( |
|
|
|
this.exchangeRateDataService, |
|
|
|
summary.committedFunds, |
|
|
|
summary.fees |
|
|
|
) |
|
|
|
], |
|
|
|
userSettings |
|
|
|
) |
|
|
|
}; |
|
|
|
|
|
|
|
return { rules, statistics: this.getReportStatistics(rules) }; |
|
|
|
} |
|
|
|
|
|
|
|
public async updateTags({ |
|
|
@ -1670,6 +1672,24 @@ export class PortfolioService { |
|
|
|
return { markets, marketsAdvanced }; |
|
|
|
} |
|
|
|
|
|
|
|
private getReportStatistics( |
|
|
|
evaluatedRules: PortfolioReportResponse['rules'] |
|
|
|
): PortfolioReportResponse['statistics'] { |
|
|
|
const rulesActiveCount = Object.values(evaluatedRules) |
|
|
|
.flat() |
|
|
|
.filter(({ isActive }) => { |
|
|
|
return isActive === true; |
|
|
|
}).length; |
|
|
|
|
|
|
|
const rulesFulfilledCount = Object.values(evaluatedRules) |
|
|
|
.flat() |
|
|
|
.filter(({ value }) => { |
|
|
|
return value === true; |
|
|
|
}).length; |
|
|
|
|
|
|
|
return { rulesActiveCount, rulesFulfilledCount }; |
|
|
|
} |
|
|
|
|
|
|
|
private getStreaks({ |
|
|
|
investments, |
|
|
|
savingsRate |
|
|
|