|
|
@ -7,7 +7,6 @@ import {
|
|
|
|
MarketState,
|
|
|
|
MarketState,
|
|
|
|
Type
|
|
|
|
Type
|
|
|
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
|
|
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
|
|
|
import { resetHours } from '@ghostfolio/common/helper';
|
|
|
|
|
|
|
|
import { TimelinePosition } from '@ghostfolio/common/interfaces';
|
|
|
|
import { TimelinePosition } from '@ghostfolio/common/interfaces';
|
|
|
|
import { Currency } from '@prisma/client';
|
|
|
|
import { Currency } from '@prisma/client';
|
|
|
|
import Big from 'big.js';
|
|
|
|
import Big from 'big.js';
|
|
|
@ -21,9 +20,11 @@ import {
|
|
|
|
isBefore,
|
|
|
|
isBefore,
|
|
|
|
max,
|
|
|
|
max,
|
|
|
|
min,
|
|
|
|
min,
|
|
|
|
parse
|
|
|
|
parse,
|
|
|
|
|
|
|
|
subDays
|
|
|
|
} from 'date-fns';
|
|
|
|
} from 'date-fns';
|
|
|
|
import { flatten } from 'lodash';
|
|
|
|
import { flatten } from 'lodash';
|
|
|
|
|
|
|
|
import { resetHours } from '@ghostfolio/common/helper';
|
|
|
|
|
|
|
|
|
|
|
|
const DATE_FORMAT = 'yyyy-MM-dd';
|
|
|
|
const DATE_FORMAT = 'yyyy-MM-dd';
|
|
|
|
|
|
|
|
|
|
|
@ -127,16 +128,19 @@ export class PortfolioCalculator {
|
|
|
|
this.transactionPoints[this.transactionPoints.length - 1];
|
|
|
|
this.transactionPoints[this.transactionPoints.length - 1];
|
|
|
|
|
|
|
|
|
|
|
|
const result: { [symbol: string]: TimelinePosition } = {};
|
|
|
|
const result: { [symbol: string]: TimelinePosition } = {};
|
|
|
|
|
|
|
|
const marketValues = await this.getMarketValues(
|
|
|
|
|
|
|
|
lastTransactionPoint,
|
|
|
|
|
|
|
|
resetHours(subDays(new Date(), 3)),
|
|
|
|
|
|
|
|
endOfDay(new Date())
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
for (const item of lastTransactionPoint.items) {
|
|
|
|
for (const item of lastTransactionPoint.items) {
|
|
|
|
const marketValue = await this.currentRateService.getValue({
|
|
|
|
const marketValue = marketValues[item.symbol];
|
|
|
|
date: new Date(),
|
|
|
|
const grossPerformance = marketValue
|
|
|
|
symbol: item.symbol,
|
|
|
|
? new Big(marketValue.marketPrice)
|
|
|
|
currency: item.currency,
|
|
|
|
|
|
|
|
userCurrency: this.currency
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
const grossPerformance = new Big(marketValue.marketPrice)
|
|
|
|
|
|
|
|
.mul(item.quantity)
|
|
|
|
.mul(item.quantity)
|
|
|
|
.minus(item.investment);
|
|
|
|
.minus(item.investment)
|
|
|
|
|
|
|
|
: null;
|
|
|
|
result[item.symbol] = {
|
|
|
|
result[item.symbol] = {
|
|
|
|
averagePrice: item.investment.div(item.quantity),
|
|
|
|
averagePrice: item.investment.div(item.quantity),
|
|
|
|
currency: item.currency,
|
|
|
|
currency: item.currency,
|
|
|
@ -145,10 +149,12 @@ export class PortfolioCalculator {
|
|
|
|
quantity: item.quantity,
|
|
|
|
quantity: item.quantity,
|
|
|
|
symbol: item.symbol,
|
|
|
|
symbol: item.symbol,
|
|
|
|
investment: item.investment,
|
|
|
|
investment: item.investment,
|
|
|
|
marketPrice: marketValue.marketPrice,
|
|
|
|
marketPrice: marketValue?.marketPrice,
|
|
|
|
transactionCount: item.transactionCount,
|
|
|
|
transactionCount: item.transactionCount,
|
|
|
|
grossPerformance,
|
|
|
|
grossPerformance,
|
|
|
|
grossPerformancePercentage: grossPerformance.div(item.investment),
|
|
|
|
grossPerformancePercentage: marketValue
|
|
|
|
|
|
|
|
? grossPerformance.div(item.investment)
|
|
|
|
|
|
|
|
: null,
|
|
|
|
url: '', // TODO
|
|
|
|
url: '', // TODO
|
|
|
|
name: '', // TODO,
|
|
|
|
name: '', // TODO,
|
|
|
|
type: Type.Unknown // TODO
|
|
|
|
type: Type.Unknown // TODO
|
|
|
@ -228,6 +234,33 @@ export class PortfolioCalculator {
|
|
|
|
return flatten(timelinePeriods);
|
|
|
|
return flatten(timelinePeriods);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private async getMarketValues(
|
|
|
|
|
|
|
|
transactionPoint: TransactionPoint,
|
|
|
|
|
|
|
|
dateRangeStart: Date,
|
|
|
|
|
|
|
|
dateRangeEnd: Date
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
const symbols: string[] = [];
|
|
|
|
|
|
|
|
const currencies: { [symbol: string]: Currency } = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const item of transactionPoint.items) {
|
|
|
|
|
|
|
|
symbols.push(item.symbol);
|
|
|
|
|
|
|
|
currencies[item.symbol] = item.currency;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const values = await this.currentRateService.getValues({
|
|
|
|
|
|
|
|
dateRangeStart,
|
|
|
|
|
|
|
|
dateRangeEnd,
|
|
|
|
|
|
|
|
symbols,
|
|
|
|
|
|
|
|
currencies,
|
|
|
|
|
|
|
|
userCurrency: this.currency
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const marketValues: { [symbol: string]: GetValueObject } = {};
|
|
|
|
|
|
|
|
for (const value of values) {
|
|
|
|
|
|
|
|
marketValues[value.symbol] = value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return marketValues;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async getTimePeriodForDate(
|
|
|
|
private async getTimePeriodForDate(
|
|
|
|
j: number,
|
|
|
|
j: number,
|
|
|
|
startDate: Date,
|
|
|
|
startDate: Date,
|
|
|
|