|
|
|
@ -1,4 +1,3 @@ |
|
|
|
import { ActivitiesService } from '@ghostfolio/api/app/activities/activities.service'; |
|
|
|
import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; |
|
|
|
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; |
|
|
|
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; |
|
|
|
@ -18,13 +17,11 @@ import { |
|
|
|
} from '@nestjs/common'; |
|
|
|
import { REQUEST } from '@nestjs/core'; |
|
|
|
import { AuthGuard } from '@nestjs/passport'; |
|
|
|
import { Type as ActivityType } from '@prisma/client'; |
|
|
|
import { Big } from 'big.js'; |
|
|
|
|
|
|
|
@Controller('api') |
|
|
|
export class ApiController { |
|
|
|
public constructor( |
|
|
|
private readonly activitiesService: ActivitiesService, |
|
|
|
private readonly portfolioService: PortfolioService, |
|
|
|
@Inject(REQUEST) private readonly request: { user: UserWithApiAccess } |
|
|
|
) {} |
|
|
|
@ -37,65 +34,14 @@ export class ApiController { |
|
|
|
|
|
|
|
const { filters } = (apiAccess.settings ?? {}) as AccessSettings; |
|
|
|
|
|
|
|
const [ |
|
|
|
{ createdAt, holdings, markets }, |
|
|
|
{ performance: performance1d }, |
|
|
|
{ performance: performanceMax }, |
|
|
|
{ performance: performanceYtd } |
|
|
|
] = await Promise.all([ |
|
|
|
this.portfolioService.getDetails({ |
|
|
|
filters, |
|
|
|
impersonationId: apiAccess.userId, |
|
|
|
userId: user.id, |
|
|
|
withMarkets: true |
|
|
|
}), |
|
|
|
...['1d', 'max', 'ytd'].map((dateRange) => { |
|
|
|
return this.portfolioService.getPerformance({ |
|
|
|
dateRange, |
|
|
|
const { createdAt, holdings, latestActivities, markets, performance } = |
|
|
|
await this.portfolioService.getPortfolioOverview({ |
|
|
|
filters, |
|
|
|
impersonationId: undefined, |
|
|
|
userId: user.id |
|
|
|
}); |
|
|
|
}) |
|
|
|
]); |
|
|
|
|
|
|
|
const { activities } = await this.activitiesService.getActivities({ |
|
|
|
filters, |
|
|
|
sortColumn: 'date', |
|
|
|
sortDirection: 'desc', |
|
|
|
take: 10, |
|
|
|
types: [ActivityType.BUY, ActivityType.SELL], |
|
|
|
userCurrency: user.settings.settings.baseCurrency ?? DEFAULT_CURRENCY, |
|
|
|
userId: user.id, |
|
|
|
withExcludedAccountsAndActivities: false |
|
|
|
userId: user.id |
|
|
|
}); |
|
|
|
|
|
|
|
const latestActivities = activities.map( |
|
|
|
({ |
|
|
|
currency, |
|
|
|
date, |
|
|
|
fee, |
|
|
|
quantity, |
|
|
|
SymbolProfile, |
|
|
|
type, |
|
|
|
unitPrice, |
|
|
|
value, |
|
|
|
valueInBaseCurrency |
|
|
|
}) => { |
|
|
|
return { |
|
|
|
currency, |
|
|
|
date, |
|
|
|
fee, |
|
|
|
quantity, |
|
|
|
SymbolProfile, |
|
|
|
type, |
|
|
|
unitPrice, |
|
|
|
value, |
|
|
|
valueInBaseCurrency |
|
|
|
}; |
|
|
|
} |
|
|
|
); |
|
|
|
|
|
|
|
const totalValueInBaseCurrency = getSum( |
|
|
|
Object.values(holdings).map(({ valueInBaseCurrency }) => { |
|
|
|
return new Big(valueInBaseCurrency ?? 0); |
|
|
|
@ -106,29 +52,21 @@ export class ApiController { |
|
|
|
createdAt, |
|
|
|
latestActivities, |
|
|
|
markets, |
|
|
|
performance, |
|
|
|
totalValueInBaseCurrency, |
|
|
|
alias: apiAccess.alias, |
|
|
|
holdings: {}, |
|
|
|
performance: { |
|
|
|
'1d': { |
|
|
|
relativeChange: |
|
|
|
performance1d.netPerformancePercentageWithCurrencyEffect |
|
|
|
}, |
|
|
|
max: { |
|
|
|
relativeChange: |
|
|
|
performanceMax.netPerformancePercentageWithCurrencyEffect |
|
|
|
}, |
|
|
|
ytd: { |
|
|
|
relativeChange: |
|
|
|
performanceYtd.netPerformancePercentageWithCurrencyEffect |
|
|
|
} |
|
|
|
} |
|
|
|
holdings: {} |
|
|
|
}; |
|
|
|
|
|
|
|
for (const [symbol, portfolioPosition] of Object.entries(holdings)) { |
|
|
|
const allocationInPercentage = |
|
|
|
totalValueInBaseCurrency > 0 |
|
|
|
? (portfolioPosition.valueInBaseCurrency ?? 0) / |
|
|
|
totalValueInBaseCurrency |
|
|
|
: 0; |
|
|
|
|
|
|
|
apiPortfolioResponse.holdings[symbol] = { |
|
|
|
allocationInPercentage: |
|
|
|
portfolioPosition.valueInBaseCurrency / totalValueInBaseCurrency, |
|
|
|
allocationInPercentage, |
|
|
|
assetProfile: portfolioPosition.assetProfile, |
|
|
|
dateOfFirstActivity: portfolioPosition.dateOfFirstActivity, |
|
|
|
markets: portfolioPosition.markets, |
|
|
|
@ -136,8 +74,7 @@ export class ApiController { |
|
|
|
portfolioPosition.netPerformancePercentWithCurrencyEffect, |
|
|
|
quantity: portfolioPosition.quantity, |
|
|
|
valueInBaseCurrency: portfolioPosition.valueInBaseCurrency, |
|
|
|
valueInPercentage: |
|
|
|
portfolioPosition.valueInBaseCurrency / totalValueInBaseCurrency |
|
|
|
valueInPercentage: allocationInPercentage |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
|