|
@ -22,6 +22,7 @@ import { |
|
|
format, |
|
|
format, |
|
|
isBefore, |
|
|
isBefore, |
|
|
isSameDay, |
|
|
isSameDay, |
|
|
|
|
|
max, |
|
|
subDays |
|
|
subDays |
|
|
} from 'date-fns'; |
|
|
} from 'date-fns'; |
|
|
import { cloneDeep, first, isNumber, last, sortBy, uniq } from 'lodash'; |
|
|
import { cloneDeep, first, isNumber, last, sortBy, uniq } from 'lodash'; |
|
@ -70,6 +71,7 @@ export class PortfolioCalculator { |
|
|
|
|
|
|
|
|
let lastDate: string = null; |
|
|
let lastDate: string = null; |
|
|
let lastTransactionPoint: TransactionPoint = null; |
|
|
let lastTransactionPoint: TransactionPoint = null; |
|
|
|
|
|
|
|
|
for (const order of this.orders) { |
|
|
for (const order of this.orders) { |
|
|
const currentDate = order.date; |
|
|
const currentDate = order.date; |
|
|
|
|
|
|
|
@ -77,33 +79,32 @@ export class PortfolioCalculator { |
|
|
const oldAccumulatedSymbol = symbols[order.symbol]; |
|
|
const oldAccumulatedSymbol = symbols[order.symbol]; |
|
|
|
|
|
|
|
|
const factor = getFactor(order.type); |
|
|
const factor = getFactor(order.type); |
|
|
const unitPrice = new Big(order.unitPrice); |
|
|
|
|
|
if (oldAccumulatedSymbol) { |
|
|
if (oldAccumulatedSymbol) { |
|
|
|
|
|
let investment = oldAccumulatedSymbol.investment; |
|
|
|
|
|
|
|
|
const newQuantity = order.quantity |
|
|
const newQuantity = order.quantity |
|
|
.mul(factor) |
|
|
.mul(factor) |
|
|
.plus(oldAccumulatedSymbol.quantity); |
|
|
.plus(oldAccumulatedSymbol.quantity); |
|
|
|
|
|
|
|
|
let investment = new Big(0); |
|
|
if (order.type === 'BUY') { |
|
|
|
|
|
investment = oldAccumulatedSymbol.investment.plus( |
|
|
if (newQuantity.gt(0)) { |
|
|
order.quantity.mul(order.unitPrice) |
|
|
if (order.type === 'BUY') { |
|
|
); |
|
|
investment = oldAccumulatedSymbol.investment.plus( |
|
|
} else if (order.type === 'SELL') { |
|
|
order.quantity.mul(unitPrice) |
|
|
investment = oldAccumulatedSymbol.investment.minus( |
|
|
); |
|
|
order.quantity.mul(oldAccumulatedSymbol.averagePrice) |
|
|
} else if (order.type === 'SELL') { |
|
|
); |
|
|
const averagePrice = oldAccumulatedSymbol.investment.div( |
|
|
|
|
|
oldAccumulatedSymbol.quantity |
|
|
|
|
|
); |
|
|
|
|
|
investment = oldAccumulatedSymbol.investment.minus( |
|
|
|
|
|
order.quantity.mul(averagePrice) |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
currentTransactionPointItem = { |
|
|
currentTransactionPointItem = { |
|
|
investment, |
|
|
investment, |
|
|
|
|
|
averagePrice: newQuantity.gt(0) |
|
|
|
|
|
? investment.div(newQuantity) |
|
|
|
|
|
: new Big(0), |
|
|
currency: order.currency, |
|
|
currency: order.currency, |
|
|
dataSource: order.dataSource, |
|
|
dataSource: order.dataSource, |
|
|
|
|
|
dividend: new Big(0), |
|
|
fee: order.fee.plus(oldAccumulatedSymbol.fee), |
|
|
fee: order.fee.plus(oldAccumulatedSymbol.fee), |
|
|
firstBuyDate: oldAccumulatedSymbol.firstBuyDate, |
|
|
firstBuyDate: oldAccumulatedSymbol.firstBuyDate, |
|
|
quantity: newQuantity, |
|
|
quantity: newQuantity, |
|
@ -113,11 +114,13 @@ export class PortfolioCalculator { |
|
|
}; |
|
|
}; |
|
|
} else { |
|
|
} else { |
|
|
currentTransactionPointItem = { |
|
|
currentTransactionPointItem = { |
|
|
|
|
|
averagePrice: order.unitPrice, |
|
|
currency: order.currency, |
|
|
currency: order.currency, |
|
|
dataSource: order.dataSource, |
|
|
dataSource: order.dataSource, |
|
|
|
|
|
dividend: new Big(0), |
|
|
fee: order.fee, |
|
|
fee: order.fee, |
|
|
firstBuyDate: order.date, |
|
|
firstBuyDate: order.date, |
|
|
investment: unitPrice.mul(order.quantity).mul(factor), |
|
|
investment: order.unitPrice.mul(order.quantity).mul(factor), |
|
|
quantity: order.quantity.mul(factor), |
|
|
quantity: order.quantity.mul(factor), |
|
|
symbol: order.symbol, |
|
|
symbol: order.symbol, |
|
|
tags: order.tags, |
|
|
tags: order.tags, |
|
@ -128,22 +131,28 @@ export class PortfolioCalculator { |
|
|
symbols[order.symbol] = currentTransactionPointItem; |
|
|
symbols[order.symbol] = currentTransactionPointItem; |
|
|
|
|
|
|
|
|
const items = lastTransactionPoint?.items ?? []; |
|
|
const items = lastTransactionPoint?.items ?? []; |
|
|
|
|
|
|
|
|
const newItems = items.filter( |
|
|
const newItems = items.filter( |
|
|
(transactionPointItem) => transactionPointItem.symbol !== order.symbol |
|
|
(transactionPointItem) => transactionPointItem.symbol !== order.symbol |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
newItems.push(currentTransactionPointItem); |
|
|
newItems.push(currentTransactionPointItem); |
|
|
|
|
|
|
|
|
newItems.sort((a, b) => { |
|
|
newItems.sort((a, b) => { |
|
|
return a.symbol?.localeCompare(b.symbol); |
|
|
return a.symbol?.localeCompare(b.symbol); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
if (lastDate !== currentDate || lastTransactionPoint === null) { |
|
|
if (lastDate !== currentDate || lastTransactionPoint === null) { |
|
|
lastTransactionPoint = { |
|
|
lastTransactionPoint = { |
|
|
date: currentDate, |
|
|
date: currentDate, |
|
|
items: newItems |
|
|
items: newItems |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.transactionPoints.push(lastTransactionPoint); |
|
|
this.transactionPoints.push(lastTransactionPoint); |
|
|
} else { |
|
|
} else { |
|
|
lastTransactionPoint.items = newItems; |
|
|
lastTransactionPoint.items = newItems; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
lastDate = currentDate; |
|
|
lastDate = currentDate; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -441,16 +450,27 @@ export class PortfolioCalculator { |
|
|
|
|
|
|
|
|
public async getCurrentPositions( |
|
|
public async getCurrentPositions( |
|
|
start: Date, |
|
|
start: Date, |
|
|
end = new Date(Date.now()) |
|
|
end?: Date |
|
|
): Promise<CurrentPositions> { |
|
|
): Promise<CurrentPositions> { |
|
|
const transactionPointsBeforeEndDate = |
|
|
const lastTransactionPoint = last(this.transactionPoints); |
|
|
this.transactionPoints?.filter((transactionPoint) => { |
|
|
|
|
|
return isBefore(parseDate(transactionPoint.date), end); |
|
|
|
|
|
}) ?? []; |
|
|
|
|
|
|
|
|
|
|
|
if (!transactionPointsBeforeEndDate.length) { |
|
|
let endDate = end; |
|
|
|
|
|
|
|
|
|
|
|
if (!endDate) { |
|
|
|
|
|
endDate = new Date(Date.now()); |
|
|
|
|
|
|
|
|
|
|
|
if (lastTransactionPoint) { |
|
|
|
|
|
endDate = max([endDate, parseDate(lastTransactionPoint.date)]); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const transactionPoints = this.transactionPoints?.filter(({ date }) => { |
|
|
|
|
|
return isBefore(parseDate(date), endDate); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (!transactionPoints.length) { |
|
|
return { |
|
|
return { |
|
|
currentValue: new Big(0), |
|
|
currentValueInBaseCurrency: new Big(0), |
|
|
grossPerformance: new Big(0), |
|
|
grossPerformance: new Big(0), |
|
|
grossPerformancePercentage: new Big(0), |
|
|
grossPerformancePercentage: new Big(0), |
|
|
grossPerformancePercentageWithCurrencyEffect: new Big(0), |
|
|
grossPerformancePercentageWithCurrencyEffect: new Big(0), |
|
@ -465,41 +485,40 @@ export class PortfolioCalculator { |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const lastTransactionPoint = |
|
|
|
|
|
transactionPointsBeforeEndDate[transactionPointsBeforeEndDate.length - 1]; |
|
|
|
|
|
|
|
|
|
|
|
const currencies: { [symbol: string]: string } = {}; |
|
|
const currencies: { [symbol: string]: string } = {}; |
|
|
const dataGatheringItems: IDataGatheringItem[] = []; |
|
|
const dataGatheringItems: IDataGatheringItem[] = []; |
|
|
let dates: Date[] = []; |
|
|
let dates: Date[] = []; |
|
|
let firstIndex = transactionPointsBeforeEndDate.length; |
|
|
let firstIndex = transactionPoints.length; |
|
|
let firstTransactionPoint: TransactionPoint = null; |
|
|
let firstTransactionPoint: TransactionPoint = null; |
|
|
|
|
|
|
|
|
dates.push(resetHours(start)); |
|
|
dates.push(resetHours(start)); |
|
|
for (const item of transactionPointsBeforeEndDate[firstIndex - 1].items) { |
|
|
|
|
|
|
|
|
for (const { currency, dataSource, symbol } of transactionPoints[ |
|
|
|
|
|
firstIndex - 1 |
|
|
|
|
|
].items) { |
|
|
dataGatheringItems.push({ |
|
|
dataGatheringItems.push({ |
|
|
dataSource: item.dataSource, |
|
|
dataSource, |
|
|
symbol: item.symbol |
|
|
symbol |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
currencies[item.symbol] = item.currency; |
|
|
currencies[symbol] = currency; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for (let i = 0; i < transactionPointsBeforeEndDate.length; i++) { |
|
|
for (let i = 0; i < transactionPoints.length; i++) { |
|
|
if ( |
|
|
if ( |
|
|
!isBefore(parseDate(transactionPointsBeforeEndDate[i].date), start) && |
|
|
!isBefore(parseDate(transactionPoints[i].date), start) && |
|
|
firstTransactionPoint === null |
|
|
firstTransactionPoint === null |
|
|
) { |
|
|
) { |
|
|
firstTransactionPoint = transactionPointsBeforeEndDate[i]; |
|
|
firstTransactionPoint = transactionPoints[i]; |
|
|
firstIndex = i; |
|
|
firstIndex = i; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (firstTransactionPoint !== null) { |
|
|
if (firstTransactionPoint !== null) { |
|
|
dates.push( |
|
|
dates.push(resetHours(parseDate(transactionPoints[i].date))); |
|
|
resetHours(parseDate(transactionPointsBeforeEndDate[i].date)) |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
dates.push(resetHours(end)); |
|
|
dates.push(resetHours(endDate)); |
|
|
|
|
|
|
|
|
// Add dates of last week for fallback
|
|
|
// Add dates of last week for fallback
|
|
|
dates.push(subDays(resetHours(new Date()), 7)); |
|
|
dates.push(subDays(resetHours(new Date()), 7)); |
|
@ -526,7 +545,7 @@ export class PortfolioCalculator { |
|
|
let exchangeRatesByCurrency = |
|
|
let exchangeRatesByCurrency = |
|
|
await this.exchangeRateDataService.getExchangeRatesByCurrency({ |
|
|
await this.exchangeRateDataService.getExchangeRatesByCurrency({ |
|
|
currencies: uniq(Object.values(currencies)), |
|
|
currencies: uniq(Object.values(currencies)), |
|
|
endDate: endOfDay(end), |
|
|
endDate: endOfDay(endDate), |
|
|
startDate: parseDate(this.transactionPoints?.[0]?.date), |
|
|
startDate: parseDate(this.transactionPoints?.[0]?.date), |
|
|
targetCurrency: this.currency |
|
|
targetCurrency: this.currency |
|
|
}); |
|
|
}); |
|
@ -562,7 +581,7 @@ export class PortfolioCalculator { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const endDateString = format(end, DATE_FORMAT); |
|
|
const endDateString = format(endDate, DATE_FORMAT); |
|
|
|
|
|
|
|
|
if (firstIndex > 0) { |
|
|
if (firstIndex > 0) { |
|
|
firstIndex--; |
|
|
firstIndex--; |
|
@ -574,15 +593,17 @@ export class PortfolioCalculator { |
|
|
const errors: ResponseError['errors'] = []; |
|
|
const errors: ResponseError['errors'] = []; |
|
|
|
|
|
|
|
|
for (const item of lastTransactionPoint.items) { |
|
|
for (const item of lastTransactionPoint.items) { |
|
|
const marketPriceInBaseCurrency = marketSymbolMap[endDateString]?.[ |
|
|
const marketPriceInBaseCurrency = ( |
|
|
item.symbol |
|
|
marketSymbolMap[endDateString]?.[item.symbol] ?? item.averagePrice |
|
|
]?.mul( |
|
|
).mul( |
|
|
exchangeRatesByCurrency[`${item.currency}${this.currency}`]?.[ |
|
|
exchangeRatesByCurrency[`${item.currency}${this.currency}`]?.[ |
|
|
endDateString |
|
|
endDateString |
|
|
] |
|
|
] |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
const { |
|
|
const { |
|
|
|
|
|
dividend, |
|
|
|
|
|
dividendInBaseCurrency, |
|
|
grossPerformance, |
|
|
grossPerformance, |
|
|
grossPerformancePercentage, |
|
|
grossPerformancePercentage, |
|
|
grossPerformancePercentageWithCurrencyEffect, |
|
|
grossPerformancePercentageWithCurrencyEffect, |
|
@ -597,9 +618,9 @@ export class PortfolioCalculator { |
|
|
totalInvestment, |
|
|
totalInvestment, |
|
|
totalInvestmentWithCurrencyEffect |
|
|
totalInvestmentWithCurrencyEffect |
|
|
} = this.getSymbolMetrics({ |
|
|
} = this.getSymbolMetrics({ |
|
|
end, |
|
|
|
|
|
marketSymbolMap, |
|
|
marketSymbolMap, |
|
|
start, |
|
|
start, |
|
|
|
|
|
end: endDate, |
|
|
exchangeRates: |
|
|
exchangeRates: |
|
|
exchangeRatesByCurrency[`${item.currency}${this.currency}`], |
|
|
exchangeRatesByCurrency[`${item.currency}${this.currency}`], |
|
|
symbol: item.symbol |
|
|
symbol: item.symbol |
|
@ -608,11 +629,11 @@ export class PortfolioCalculator { |
|
|
hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors; |
|
|
hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors; |
|
|
|
|
|
|
|
|
positions.push({ |
|
|
positions.push({ |
|
|
|
|
|
dividend, |
|
|
|
|
|
dividendInBaseCurrency, |
|
|
timeWeightedInvestment, |
|
|
timeWeightedInvestment, |
|
|
timeWeightedInvestmentWithCurrencyEffect, |
|
|
timeWeightedInvestmentWithCurrencyEffect, |
|
|
averagePrice: item.quantity.eq(0) |
|
|
averagePrice: item.averagePrice, |
|
|
? new Big(0) |
|
|
|
|
|
: item.investment.div(item.quantity), |
|
|
|
|
|
currency: item.currency, |
|
|
currency: item.currency, |
|
|
dataSource: item.dataSource, |
|
|
dataSource: item.dataSource, |
|
|
fee: item.fee, |
|
|
fee: item.fee, |
|
@ -646,7 +667,10 @@ export class PortfolioCalculator { |
|
|
quantity: item.quantity, |
|
|
quantity: item.quantity, |
|
|
symbol: item.symbol, |
|
|
symbol: item.symbol, |
|
|
tags: item.tags, |
|
|
tags: item.tags, |
|
|
transactionCount: item.transactionCount |
|
|
transactionCount: item.transactionCount, |
|
|
|
|
|
valueInBaseCurrency: new Big(marketPriceInBaseCurrency).mul( |
|
|
|
|
|
item.quantity |
|
|
|
|
|
) |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
if ( |
|
|
if ( |
|
@ -715,7 +739,7 @@ export class PortfolioCalculator { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private calculateOverallPerformance(positions: TimelinePosition[]) { |
|
|
private calculateOverallPerformance(positions: TimelinePosition[]) { |
|
|
let currentValue = new Big(0); |
|
|
let currentValueInBaseCurrency = new Big(0); |
|
|
let grossPerformance = new Big(0); |
|
|
let grossPerformance = new Big(0); |
|
|
let grossPerformanceWithCurrencyEffect = new Big(0); |
|
|
let grossPerformanceWithCurrencyEffect = new Big(0); |
|
|
let hasErrors = false; |
|
|
let hasErrors = false; |
|
@ -727,14 +751,9 @@ export class PortfolioCalculator { |
|
|
let totalTimeWeightedInvestmentWithCurrencyEffect = new Big(0); |
|
|
let totalTimeWeightedInvestmentWithCurrencyEffect = new Big(0); |
|
|
|
|
|
|
|
|
for (const currentPosition of positions) { |
|
|
for (const currentPosition of positions) { |
|
|
if ( |
|
|
if (currentPosition.valueInBaseCurrency) { |
|
|
currentPosition.investment && |
|
|
currentValueInBaseCurrency = currentValueInBaseCurrency.plus( |
|
|
currentPosition.marketPriceInBaseCurrency |
|
|
currentPosition.valueInBaseCurrency |
|
|
) { |
|
|
|
|
|
currentValue = currentValue.plus( |
|
|
|
|
|
new Big(currentPosition.marketPriceInBaseCurrency).mul( |
|
|
|
|
|
currentPosition.quantity |
|
|
|
|
|
) |
|
|
|
|
|
); |
|
|
); |
|
|
} else { |
|
|
} else { |
|
|
hasErrors = true; |
|
|
hasErrors = true; |
|
@ -791,7 +810,7 @@ export class PortfolioCalculator { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
return { |
|
|
currentValue, |
|
|
currentValueInBaseCurrency, |
|
|
grossPerformance, |
|
|
grossPerformance, |
|
|
grossPerformanceWithCurrencyEffect, |
|
|
grossPerformanceWithCurrencyEffect, |
|
|
hasErrors, |
|
|
hasErrors, |
|
@ -842,6 +861,8 @@ export class PortfolioCalculator { |
|
|
const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)]; |
|
|
const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)]; |
|
|
const currentValues: { [date: string]: Big } = {}; |
|
|
const currentValues: { [date: string]: Big } = {}; |
|
|
const currentValuesWithCurrencyEffect: { [date: string]: Big } = {}; |
|
|
const currentValuesWithCurrencyEffect: { [date: string]: Big } = {}; |
|
|
|
|
|
let dividend = new Big(0); |
|
|
|
|
|
let dividendInBaseCurrency = new Big(0); |
|
|
let fees = new Big(0); |
|
|
let fees = new Big(0); |
|
|
let feesAtStartDate = new Big(0); |
|
|
let feesAtStartDate = new Big(0); |
|
|
let feesAtStartDateWithCurrencyEffect = new Big(0); |
|
|
let feesAtStartDateWithCurrencyEffect = new Big(0); |
|
@ -894,6 +915,8 @@ export class PortfolioCalculator { |
|
|
return { |
|
|
return { |
|
|
currentValues: {}, |
|
|
currentValues: {}, |
|
|
currentValuesWithCurrencyEffect: {}, |
|
|
currentValuesWithCurrencyEffect: {}, |
|
|
|
|
|
dividend: new Big(0), |
|
|
|
|
|
dividendInBaseCurrency: new Big(0), |
|
|
grossPerformance: new Big(0), |
|
|
grossPerformance: new Big(0), |
|
|
grossPerformancePercentage: new Big(0), |
|
|
grossPerformancePercentage: new Big(0), |
|
|
grossPerformancePercentageWithCurrencyEffect: new Big(0), |
|
|
grossPerformancePercentageWithCurrencyEffect: new Big(0), |
|
@ -934,6 +957,8 @@ export class PortfolioCalculator { |
|
|
return { |
|
|
return { |
|
|
currentValues: {}, |
|
|
currentValues: {}, |
|
|
currentValuesWithCurrencyEffect: {}, |
|
|
currentValuesWithCurrencyEffect: {}, |
|
|
|
|
|
dividend: new Big(0), |
|
|
|
|
|
dividendInBaseCurrency: new Big(0), |
|
|
grossPerformance: new Big(0), |
|
|
grossPerformance: new Big(0), |
|
|
grossPerformancePercentage: new Big(0), |
|
|
grossPerformancePercentage: new Big(0), |
|
|
grossPerformancePercentageWithCurrencyEffect: new Big(0), |
|
|
grossPerformancePercentageWithCurrencyEffect: new Big(0), |
|
@ -1024,28 +1049,26 @@ export class PortfolioCalculator { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Sort orders so that the start and end placeholder order are at the right
|
|
|
// Sort orders so that the start and end placeholder order are at the correct
|
|
|
// position
|
|
|
// position
|
|
|
orders = sortBy(orders, (order) => { |
|
|
orders = sortBy(orders, ({ date, itemType }) => { |
|
|
let sortIndex = new Date(order.date); |
|
|
let sortIndex = new Date(date); |
|
|
|
|
|
|
|
|
if (order.itemType === 'start') { |
|
|
|
|
|
sortIndex = addMilliseconds(sortIndex, -1); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (order.itemType === 'end') { |
|
|
if (itemType === 'end') { |
|
|
sortIndex = addMilliseconds(sortIndex, 1); |
|
|
sortIndex = addMilliseconds(sortIndex, 1); |
|
|
|
|
|
} else if (itemType === 'start') { |
|
|
|
|
|
sortIndex = addMilliseconds(sortIndex, -1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return sortIndex.getTime(); |
|
|
return sortIndex.getTime(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const indexOfStartOrder = orders.findIndex((order) => { |
|
|
const indexOfStartOrder = orders.findIndex(({ itemType }) => { |
|
|
return order.itemType === 'start'; |
|
|
return itemType === 'start'; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const indexOfEndOrder = orders.findIndex((order) => { |
|
|
const indexOfEndOrder = orders.findIndex(({ itemType }) => { |
|
|
return order.itemType === 'end'; |
|
|
return itemType === 'end'; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
let totalInvestmentDays = 0; |
|
|
let totalInvestmentDays = 0; |
|
@ -1108,29 +1131,29 @@ export class PortfolioCalculator { |
|
|
valueOfInvestmentBeforeTransactionWithCurrencyEffect; |
|
|
valueOfInvestmentBeforeTransactionWithCurrencyEffect; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const transactionInvestment = |
|
|
let transactionInvestment = new Big(0); |
|
|
order.type === 'BUY' |
|
|
let transactionInvestmentWithCurrencyEffect = new Big(0); |
|
|
? order.quantity |
|
|
|
|
|
.mul(order.unitPriceInBaseCurrency) |
|
|
if (order.type === 'BUY') { |
|
|
.mul(getFactor(order.type)) |
|
|
transactionInvestment = order.quantity |
|
|
: totalUnits.gt(0) |
|
|
.mul(order.unitPriceInBaseCurrency) |
|
|
? totalInvestment |
|
|
.mul(getFactor(order.type)); |
|
|
.div(totalUnits) |
|
|
transactionInvestmentWithCurrencyEffect = order.quantity |
|
|
.mul(order.quantity) |
|
|
.mul(order.unitPriceInBaseCurrencyWithCurrencyEffect) |
|
|
.mul(getFactor(order.type)) |
|
|
.mul(getFactor(order.type)); |
|
|
: new Big(0); |
|
|
} else if (order.type === 'SELL') { |
|
|
|
|
|
if (totalUnits.gt(0)) { |
|
|
const transactionInvestmentWithCurrencyEffect = |
|
|
transactionInvestment = totalInvestment |
|
|
order.type === 'BUY' |
|
|
.div(totalUnits) |
|
|
? order.quantity |
|
|
.mul(order.quantity) |
|
|
.mul(order.unitPriceInBaseCurrencyWithCurrencyEffect) |
|
|
.mul(getFactor(order.type)); |
|
|
.mul(getFactor(order.type)) |
|
|
transactionInvestmentWithCurrencyEffect = |
|
|
: totalUnits.gt(0) |
|
|
totalInvestmentWithCurrencyEffect |
|
|
? totalInvestmentWithCurrencyEffect |
|
|
.div(totalUnits) |
|
|
.div(totalUnits) |
|
|
.mul(order.quantity) |
|
|
.mul(order.quantity) |
|
|
.mul(getFactor(order.type)); |
|
|
.mul(getFactor(order.type)) |
|
|
} |
|
|
: new Big(0); |
|
|
} |
|
|
|
|
|
|
|
|
if (PortfolioCalculator.ENABLE_LOGGING) { |
|
|
if (PortfolioCalculator.ENABLE_LOGGING) { |
|
|
console.log('totalInvestment', totalInvestment.toNumber()); |
|
|
console.log('totalInvestment', totalInvestment.toNumber()); |
|
@ -1186,6 +1209,13 @@ export class PortfolioCalculator { |
|
|
|
|
|
|
|
|
totalUnits = totalUnits.plus(order.quantity.mul(getFactor(order.type))); |
|
|
totalUnits = totalUnits.plus(order.quantity.mul(getFactor(order.type))); |
|
|
|
|
|
|
|
|
|
|
|
if (order.type === 'DIVIDEND') { |
|
|
|
|
|
dividend = dividend.plus(order.quantity.mul(order.unitPrice)); |
|
|
|
|
|
dividendInBaseCurrency = dividendInBaseCurrency.plus( |
|
|
|
|
|
dividend.mul(exchangeRateAtOrderDate ?? 1) |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const valueOfInvestment = totalUnits.mul(order.unitPriceInBaseCurrency); |
|
|
const valueOfInvestment = totalUnits.mul(order.unitPriceInBaseCurrency); |
|
|
|
|
|
|
|
|
const valueOfInvestmentWithCurrencyEffect = totalUnits.mul( |
|
|
const valueOfInvestmentWithCurrencyEffect = totalUnits.mul( |
|
@ -1277,7 +1307,7 @@ export class PortfolioCalculator { |
|
|
grossPerformanceWithCurrencyEffect; |
|
|
grossPerformanceWithCurrencyEffect; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (i > indexOfStartOrder) { |
|
|
if (i > indexOfStartOrder && ['BUY', 'SELL'].includes(order.type)) { |
|
|
// Only consider periods with an investment for the calculation of
|
|
|
// Only consider periods with an investment for the calculation of
|
|
|
// the time weighted investment
|
|
|
// the time weighted investment
|
|
|
if (valueOfInvestmentBeforeTransaction.gt(0)) { |
|
|
if (valueOfInvestmentBeforeTransaction.gt(0)) { |
|
@ -1471,6 +1501,7 @@ export class PortfolioCalculator { |
|
|
Time weighted investment with currency effect: ${timeWeightedAverageInvestmentBetweenStartAndEndDateWithCurrencyEffect.toFixed( |
|
|
Time weighted investment with currency effect: ${timeWeightedAverageInvestmentBetweenStartAndEndDateWithCurrencyEffect.toFixed( |
|
|
2 |
|
|
2 |
|
|
)} |
|
|
)} |
|
|
|
|
|
Total dividend: ${dividend.toFixed(2)} |
|
|
Gross performance: ${totalGrossPerformance.toFixed( |
|
|
Gross performance: ${totalGrossPerformance.toFixed( |
|
|
2 |
|
|
2 |
|
|
)} / ${grossPerformancePercentage.mul(100).toFixed(2)}% |
|
|
)} / ${grossPerformancePercentage.mul(100).toFixed(2)}% |
|
@ -1495,6 +1526,8 @@ export class PortfolioCalculator { |
|
|
return { |
|
|
return { |
|
|
currentValues, |
|
|
currentValues, |
|
|
currentValuesWithCurrencyEffect, |
|
|
currentValuesWithCurrencyEffect, |
|
|
|
|
|
dividend, |
|
|
|
|
|
dividendInBaseCurrency, |
|
|
grossPerformancePercentage, |
|
|
grossPerformancePercentage, |
|
|
grossPerformancePercentageWithCurrencyEffect, |
|
|
grossPerformancePercentageWithCurrencyEffect, |
|
|
initialValue, |
|
|
initialValue, |
|
|