|
@ -56,12 +56,14 @@ import { |
|
|
parse, |
|
|
parse, |
|
|
parseISO, |
|
|
parseISO, |
|
|
setDayOfYear, |
|
|
setDayOfYear, |
|
|
|
|
|
startOfDay, |
|
|
subDays, |
|
|
subDays, |
|
|
subYears |
|
|
subYears |
|
|
} from 'date-fns'; |
|
|
} from 'date-fns'; |
|
|
import { isEmpty } from 'lodash'; |
|
|
import { isEmpty } from 'lodash'; |
|
|
|
|
|
|
|
|
import { |
|
|
import { |
|
|
|
|
|
HistoricalDataContainer, |
|
|
HistoricalDataItem, |
|
|
HistoricalDataItem, |
|
|
PortfolioPositionDetail |
|
|
PortfolioPositionDetail |
|
|
} from './interfaces/portfolio-position-detail.interface'; |
|
|
} from './interfaces/portfolio-position-detail.interface'; |
|
@ -164,7 +166,7 @@ export class PortfolioService { |
|
|
public async getChart( |
|
|
public async getChart( |
|
|
aImpersonationId: string, |
|
|
aImpersonationId: string, |
|
|
aDateRange: DateRange = 'max' |
|
|
aDateRange: DateRange = 'max' |
|
|
): Promise<HistoricalDataItem[]> { |
|
|
): Promise<HistoricalDataContainer> { |
|
|
const userId = await this.getUserId(aImpersonationId, this.request.user.id); |
|
|
const userId = await this.getUserId(aImpersonationId, this.request.user.id); |
|
|
|
|
|
|
|
|
const portfolioCalculator = new PortfolioCalculator( |
|
|
const portfolioCalculator = new PortfolioCalculator( |
|
@ -175,14 +177,21 @@ export class PortfolioService { |
|
|
const { transactionPoints } = await this.getTransactionPoints({ userId }); |
|
|
const { transactionPoints } = await this.getTransactionPoints({ userId }); |
|
|
portfolioCalculator.setTransactionPoints(transactionPoints); |
|
|
portfolioCalculator.setTransactionPoints(transactionPoints); |
|
|
if (transactionPoints.length === 0) { |
|
|
if (transactionPoints.length === 0) { |
|
|
return []; |
|
|
return { |
|
|
|
|
|
isAllTimeHigh: false, |
|
|
|
|
|
isAllTimeLow: false, |
|
|
|
|
|
items: [] |
|
|
|
|
|
}; |
|
|
} |
|
|
} |
|
|
let portfolioStart = parse( |
|
|
let portfolioStart = parse( |
|
|
transactionPoints[0].date, |
|
|
transactionPoints[0].date, |
|
|
DATE_FORMAT, |
|
|
DATE_FORMAT, |
|
|
new Date() |
|
|
new Date() |
|
|
); |
|
|
); |
|
|
portfolioStart = this.getStartDate(aDateRange, portfolioStart); |
|
|
|
|
|
|
|
|
// Get start date for the full portfolio because of because of the
|
|
|
|
|
|
// min and max calculation
|
|
|
|
|
|
portfolioStart = this.getStartDate('max', portfolioStart); |
|
|
|
|
|
|
|
|
const timelineSpecification: TimelineSpecification[] = [ |
|
|
const timelineSpecification: TimelineSpecification[] = [ |
|
|
{ |
|
|
{ |
|
@ -191,18 +200,52 @@ export class PortfolioService { |
|
|
} |
|
|
} |
|
|
]; |
|
|
]; |
|
|
|
|
|
|
|
|
const timeline = await portfolioCalculator.calculateTimeline( |
|
|
const timelineInfo = await portfolioCalculator.calculateTimeline( |
|
|
timelineSpecification, |
|
|
timelineSpecification, |
|
|
format(new Date(), DATE_FORMAT) |
|
|
format(new Date(), DATE_FORMAT) |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
return timeline |
|
|
const timeline = timelineInfo.timelinePeriods; |
|
|
|
|
|
|
|
|
|
|
|
const items = timeline |
|
|
.filter((timelineItem) => timelineItem !== null) |
|
|
.filter((timelineItem) => timelineItem !== null) |
|
|
.map((timelineItem) => ({ |
|
|
.map((timelineItem) => ({ |
|
|
date: timelineItem.date, |
|
|
date: timelineItem.date, |
|
|
marketPrice: timelineItem.value, |
|
|
marketPrice: timelineItem.value, |
|
|
value: timelineItem.netPerformance.toNumber() |
|
|
value: timelineItem.netPerformance.toNumber() |
|
|
})); |
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
let lastItem = null; |
|
|
|
|
|
if (timeline.length > 0) { |
|
|
|
|
|
lastItem = timeline[timeline.length - 1]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let isAllTimeHigh = timelineInfo.maxNetPerformance?.eq( |
|
|
|
|
|
lastItem?.netPerformance |
|
|
|
|
|
); |
|
|
|
|
|
let isAllTimeLow = timelineInfo.minNetPerformance?.eq( |
|
|
|
|
|
lastItem?.netPerformance |
|
|
|
|
|
); |
|
|
|
|
|
if (isAllTimeHigh && isAllTimeLow) { |
|
|
|
|
|
isAllTimeHigh = false; |
|
|
|
|
|
isAllTimeLow = false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
portfolioStart = startOfDay( |
|
|
|
|
|
this.getStartDate( |
|
|
|
|
|
aDateRange, |
|
|
|
|
|
parse(transactionPoints[0].date, DATE_FORMAT, new Date()) |
|
|
|
|
|
) |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
|
isAllTimeHigh, |
|
|
|
|
|
isAllTimeLow, |
|
|
|
|
|
items: items.filter((item) => { |
|
|
|
|
|
// Filter items of date range
|
|
|
|
|
|
return !isAfter(portfolioStart, parseDate(item.date)); |
|
|
|
|
|
}) |
|
|
|
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public async getDetails( |
|
|
public async getDetails( |
|
@ -639,7 +682,9 @@ export class PortfolioService { |
|
|
currentGrossPerformancePercent: 0, |
|
|
currentGrossPerformancePercent: 0, |
|
|
currentNetPerformance: 0, |
|
|
currentNetPerformance: 0, |
|
|
currentNetPerformancePercent: 0, |
|
|
currentNetPerformancePercent: 0, |
|
|
currentValue: 0 |
|
|
currentValue: 0, |
|
|
|
|
|
isAllTimeHigh: false, |
|
|
|
|
|
isAllTimeLow: false |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
@ -672,7 +717,9 @@ export class PortfolioService { |
|
|
currentGrossPerformancePercent, |
|
|
currentGrossPerformancePercent, |
|
|
currentNetPerformance, |
|
|
currentNetPerformance, |
|
|
currentNetPerformancePercent, |
|
|
currentNetPerformancePercent, |
|
|
currentValue |
|
|
currentValue, |
|
|
|
|
|
isAllTimeHigh: true, // TODO
|
|
|
|
|
|
isAllTimeLow: false // TODO
|
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|