|
@ -3,13 +3,23 @@ import { |
|
|
getFactor, |
|
|
getFactor, |
|
|
getInterval |
|
|
getInterval |
|
|
} from '@ghostfolio/api/helper/portfolio.helper'; |
|
|
} from '@ghostfolio/api/helper/portfolio.helper'; |
|
|
|
|
|
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; |
|
|
import { MAX_CHART_ITEMS } from '@ghostfolio/common/config'; |
|
|
import { MAX_CHART_ITEMS } from '@ghostfolio/common/config'; |
|
|
import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper'; |
|
|
import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper'; |
|
|
import { HistoricalDataItem } from '@ghostfolio/common/interfaces'; |
|
|
import { HistoricalDataItem } from '@ghostfolio/common/interfaces'; |
|
|
import { DateRange } from '@ghostfolio/common/types'; |
|
|
import { DateRange } from '@ghostfolio/common/types'; |
|
|
|
|
|
|
|
|
import { Big } from 'big.js'; |
|
|
import { Big } from 'big.js'; |
|
|
import { addDays, differenceInDays, eachDayOfInterval, format } from 'date-fns'; |
|
|
import { |
|
|
|
|
|
addDays, |
|
|
|
|
|
differenceInDays, |
|
|
|
|
|
eachDayOfInterval, |
|
|
|
|
|
format, |
|
|
|
|
|
isAfter, |
|
|
|
|
|
isBefore, |
|
|
|
|
|
isEqual, |
|
|
|
|
|
subDays |
|
|
|
|
|
} from 'date-fns'; |
|
|
|
|
|
|
|
|
import { PortfolioOrder } from '../../interfaces/portfolio-order.interface'; |
|
|
import { PortfolioOrder } from '../../interfaces/portfolio-order.interface'; |
|
|
import { TWRPortfolioCalculator } from '../twr/portfolio-calculator'; |
|
|
import { TWRPortfolioCalculator } from '../twr/portfolio-calculator'; |
|
@ -78,13 +88,18 @@ export class CPRPortfolioCalculator extends TWRPortfolioCalculator { |
|
|
start: Date; |
|
|
start: Date; |
|
|
step?: number; |
|
|
step?: number; |
|
|
}): Promise<HistoricalDataItem[]> { |
|
|
}): Promise<HistoricalDataItem[]> { |
|
|
const timelineHoldings = this.getHoldings(start, end); |
|
|
let marketMapTask = this.computeMarketMap({ in: [start, end] }); |
|
|
|
|
|
const timelineHoldings = await this.getHoldings(start, end); |
|
|
|
|
|
|
|
|
const calculationDates = Object.keys(timelineHoldings) |
|
|
const calculationDates = Object.keys(timelineHoldings) |
|
|
.filter((date) => { |
|
|
.filter((date) => { |
|
|
let parsed = parseDate(date); |
|
|
let parsed = parseDate(date); |
|
|
parsed >= start && parsed <= end; |
|
|
return ( |
|
|
|
|
|
isAfter(parsed, subDays(start, 1)) && |
|
|
|
|
|
isBefore(parsed, addDays(end, 1)) |
|
|
|
|
|
); |
|
|
}) |
|
|
}) |
|
|
.sort(); |
|
|
.sort((a, b) => parseDate(a).getTime() - parseDate(b).getTime()); |
|
|
let data: HistoricalDataItem[] = []; |
|
|
let data: HistoricalDataItem[] = []; |
|
|
const startString = format(start, DATE_FORMAT); |
|
|
const startString = format(start, DATE_FORMAT); |
|
|
|
|
|
|
|
@ -103,6 +118,8 @@ export class CPRPortfolioCalculator extends TWRPortfolioCalculator { |
|
|
valueWithCurrencyEffect: 0 |
|
|
valueWithCurrencyEffect: 0 |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
await marketMapTask; |
|
|
|
|
|
|
|
|
let totalInvestment = Object.keys(timelineHoldings[startString]).reduce( |
|
|
let totalInvestment = Object.keys(timelineHoldings[startString]).reduce( |
|
|
(sum, holding) => { |
|
|
(sum, holding) => { |
|
|
return sum.plus( |
|
|
return sum.plus( |
|
@ -231,11 +248,15 @@ export class CPRPortfolioCalculator extends TWRPortfolioCalculator { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@LogPerformance |
|
|
@LogPerformance |
|
|
private getHoldings(start: Date, end: Date) { |
|
|
private async getHoldings(start: Date, end: Date) { |
|
|
if ( |
|
|
if ( |
|
|
this.holdings && |
|
|
this.holdings && |
|
|
Object.keys(this.holdings).some((h) => parseDate(h) >= end) && |
|
|
Object.keys(this.holdings).some((h) => |
|
|
Object.keys(this.holdings).some((h) => parseDate(h) <= start) |
|
|
isAfter(parseDate(h), subDays(end, 1)) |
|
|
|
|
|
) && |
|
|
|
|
|
Object.keys(this.holdings).some((h) => |
|
|
|
|
|
isBefore(parseDate(h), addDays(start, 1)) |
|
|
|
|
|
) |
|
|
) { |
|
|
) { |
|
|
return this.holdings; |
|
|
return this.holdings; |
|
|
} |
|
|
} |
|
@ -245,7 +266,7 @@ export class CPRPortfolioCalculator extends TWRPortfolioCalculator { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@LogPerformance |
|
|
@LogPerformance |
|
|
private computeHoldings(start: Date, end: Date) { |
|
|
private async computeHoldings(start: Date, end: Date) { |
|
|
const investmentByDate = this.getInvestmentByDate(); |
|
|
const investmentByDate = this.getInvestmentByDate(); |
|
|
const transactionDates = Object.keys(investmentByDate).sort(); |
|
|
const transactionDates = Object.keys(investmentByDate).sort(); |
|
|
let dates = eachDayOfInterval({ start, end }, { step: 1 }) |
|
|
let dates = eachDayOfInterval({ start, end }, { step: 1 }) |
|
@ -320,4 +341,40 @@ export class CPRPortfolioCalculator extends TWRPortfolioCalculator { |
|
|
return groupedByDate; |
|
|
return groupedByDate; |
|
|
}, {}); |
|
|
}, {}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@LogPerformance |
|
|
|
|
|
private async computeMarketMap(dateQuery: { in: Date[] }) { |
|
|
|
|
|
const dataGatheringItems: IDataGatheringItem[] = this.activities.map( |
|
|
|
|
|
(activity) => { |
|
|
|
|
|
return { |
|
|
|
|
|
symbol: activity.SymbolProfile.symbol, |
|
|
|
|
|
dataSource: activity.SymbolProfile.dataSource |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
); |
|
|
|
|
|
const { values: marketSymbols } = await this.currentRateService.getValues({ |
|
|
|
|
|
dataGatheringItems, |
|
|
|
|
|
dateQuery |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const marketSymbolMap: { |
|
|
|
|
|
[date: string]: { [symbol: string]: Big }; |
|
|
|
|
|
} = {}; |
|
|
|
|
|
|
|
|
|
|
|
for (const marketSymbol of marketSymbols) { |
|
|
|
|
|
const date = format(marketSymbol.date, DATE_FORMAT); |
|
|
|
|
|
|
|
|
|
|
|
if (!marketSymbolMap[date]) { |
|
|
|
|
|
marketSymbolMap[date] = {}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (marketSymbol.marketPrice) { |
|
|
|
|
|
marketSymbolMap[date][marketSymbol.symbol] = new Big( |
|
|
|
|
|
marketSymbol.marketPrice |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.marketMap = marketSymbolMap; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|