|
|
|
@ -106,16 +106,19 @@ export class PortfolioCalculator {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getCurrentPositions(start: Date): Promise<{
|
|
|
|
|
[symbol: string]: TimelinePosition;
|
|
|
|
|
hasErrors: boolean;
|
|
|
|
|
positions: TimelinePosition[];
|
|
|
|
|
}> {
|
|
|
|
|
if (!this.transactionPoints?.length) {
|
|
|
|
|
return {};
|
|
|
|
|
return {
|
|
|
|
|
hasErrors: false,
|
|
|
|
|
positions: []
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const lastTransactionPoint =
|
|
|
|
|
this.transactionPoints[this.transactionPoints.length - 1];
|
|
|
|
|
|
|
|
|
|
const result: { [symbol: string]: TimelinePosition } = {};
|
|
|
|
|
// use Date.now() to use the mock for today
|
|
|
|
|
const today = new Date(Date.now());
|
|
|
|
|
|
|
|
|
@ -171,6 +174,7 @@ export class PortfolioCalculator {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let hasErrors = false;
|
|
|
|
|
const startString = format(start, DATE_FORMAT);
|
|
|
|
|
|
|
|
|
|
const holdingPeriodReturns: { [symbol: string]: Big } = {};
|
|
|
|
@ -178,12 +182,14 @@ export class PortfolioCalculator {
|
|
|
|
|
let todayString = format(today, DATE_FORMAT);
|
|
|
|
|
// in case no symbols are there for today, use yesterday
|
|
|
|
|
if (!marketSymbolMap[todayString]) {
|
|
|
|
|
hasErrors = true;
|
|
|
|
|
todayString = format(subDays(today, 1), DATE_FORMAT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (firstIndex > 0) {
|
|
|
|
|
firstIndex--;
|
|
|
|
|
}
|
|
|
|
|
const invalidSymbols = [];
|
|
|
|
|
for (let i = firstIndex; i < this.transactionPoints.length; i++) {
|
|
|
|
|
const currentDate =
|
|
|
|
|
i === firstIndex ? startString : this.transactionPoints[i].date;
|
|
|
|
@ -198,6 +204,22 @@ export class PortfolioCalculator {
|
|
|
|
|
if (!oldHoldingPeriodReturn) {
|
|
|
|
|
oldHoldingPeriodReturn = new Big(1);
|
|
|
|
|
}
|
|
|
|
|
if (!marketSymbolMap[nextDate]?.[item.symbol]) {
|
|
|
|
|
invalidSymbols.push(item.symbol);
|
|
|
|
|
hasErrors = true;
|
|
|
|
|
console.error(
|
|
|
|
|
`Missing value for symbol ${item.symbol} at ${nextDate}`
|
|
|
|
|
);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!marketSymbolMap[currentDate]?.[item.symbol]) {
|
|
|
|
|
invalidSymbols.push(item.symbol);
|
|
|
|
|
hasErrors = true;
|
|
|
|
|
console.error(
|
|
|
|
|
`Missing value for symbol ${item.symbol} at ${currentDate}`
|
|
|
|
|
);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
holdingPeriodReturns[item.symbol] = oldHoldingPeriodReturn.mul(
|
|
|
|
|
marketSymbolMap[nextDate][item.symbol].div(
|
|
|
|
|
marketSymbolMap[currentDate][item.symbol]
|
|
|
|
@ -215,26 +237,31 @@ export class PortfolioCalculator {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const positions: TimelinePosition[] = [];
|
|
|
|
|
for (const item of lastTransactionPoint.items) {
|
|
|
|
|
const marketValue = marketSymbolMap[todayString][item.symbol];
|
|
|
|
|
result[item.symbol] = {
|
|
|
|
|
const marketValue = marketSymbolMap[todayString]?.[item.symbol];
|
|
|
|
|
const isValid = invalidSymbols.indexOf(item.symbol) === -1;
|
|
|
|
|
positions.push({
|
|
|
|
|
averagePrice: item.investment.div(item.quantity),
|
|
|
|
|
currency: item.currency,
|
|
|
|
|
firstBuyDate: item.firstBuyDate,
|
|
|
|
|
grossPerformance: grossPerformance[item.symbol] ?? null,
|
|
|
|
|
grossPerformancePercentage: holdingPeriodReturns[item.symbol]
|
|
|
|
|
? holdingPeriodReturns[item.symbol].minus(1)
|
|
|
|
|
grossPerformance: isValid
|
|
|
|
|
? grossPerformance[item.symbol] ?? null
|
|
|
|
|
: null,
|
|
|
|
|
grossPerformancePercentage:
|
|
|
|
|
isValid && holdingPeriodReturns[item.symbol]
|
|
|
|
|
? holdingPeriodReturns[item.symbol].minus(1)
|
|
|
|
|
: null,
|
|
|
|
|
investment: item.investment,
|
|
|
|
|
marketPrice: marketValue.toNumber(),
|
|
|
|
|
marketPrice: marketValue?.toNumber() ?? null,
|
|
|
|
|
name: item.name,
|
|
|
|
|
quantity: item.quantity,
|
|
|
|
|
symbol: item.symbol,
|
|
|
|
|
transactionCount: item.transactionCount
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
return { hasErrors, positions };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async calculateTimeline(
|
|
|
|
|