|
|
@ -1,7 +1,7 @@ |
|
|
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; |
|
|
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; |
|
|
import { HoldingPerformance } from '@ghostfolio/api/app/portfolio/interfaces/holding-performance.interface'; |
|
|
import { HoldingPerformance } from '@ghostfolio/api/app/portfolio/interfaces/holding-performance.interface'; |
|
|
|
|
|
import { PortfolioCalculatorActivityItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-activity-item.interface'; |
|
|
import { PortfolioCalculatorHolding } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-holding.interface'; |
|
|
import { PortfolioCalculatorHolding } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-calculator-holding.interface'; |
|
|
import { PortfolioOrderItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order-item.interface'; |
|
|
|
|
|
import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; |
|
|
import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; |
|
|
import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; |
|
|
import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; |
|
|
import { |
|
|
import { |
|
|
@ -183,12 +183,12 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
symbol |
|
|
symbol |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
let orders: PortfolioOrderItem[] = |
|
|
let activities: PortfolioCalculatorActivityItem[] = |
|
|
this.activitiesByAssetProfileIdentifier[assetProfileIdentifier] ?? []; |
|
|
this.activitiesByAssetProfileIdentifier[assetProfileIdentifier] ?? []; |
|
|
|
|
|
|
|
|
const isCash = orders[0]?.assetProfile?.assetSubClass === 'CASH'; |
|
|
const isCash = activities[0]?.assetProfile?.assetSubClass === 'CASH'; |
|
|
|
|
|
|
|
|
if (orders.length <= 0) { |
|
|
if (activities.length <= 0) { |
|
|
return this.getEmptyHoldingPerformance(); |
|
|
return this.getEmptyHoldingPerformance(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -200,9 +200,9 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
totalDividendInBaseCurrency, |
|
|
totalDividendInBaseCurrency, |
|
|
totalInterestInBaseCurrency, |
|
|
totalInterestInBaseCurrency, |
|
|
totalLiabilitiesInBaseCurrency |
|
|
totalLiabilitiesInBaseCurrency |
|
|
} = this.getTotalsFromActivities({ exchangeRates, orders }); |
|
|
} = this.getTotalsFromActivities({ activities, exchangeRates }); |
|
|
|
|
|
|
|
|
const dateOfFirstActivity = parseDate(orders[0].date); |
|
|
const dateOfFirstActivity = parseDate(activities[0].date); |
|
|
|
|
|
|
|
|
const endDateString = format(end, DATE_FORMAT); |
|
|
const endDateString = format(end, DATE_FORMAT); |
|
|
const startDateString = format(start, DATE_FORMAT); |
|
|
const startDateString = format(start, DATE_FORMAT); |
|
|
@ -211,9 +211,9 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
marketSymbolMap[startDateString]?.[assetProfileIdentifier]; |
|
|
marketSymbolMap[startDateString]?.[assetProfileIdentifier]; |
|
|
|
|
|
|
|
|
const unitPriceAtEndDate = this.getUnitPriceAtEndDate({ |
|
|
const unitPriceAtEndDate = this.getUnitPriceAtEndDate({ |
|
|
|
|
|
activities, |
|
|
dataSource, |
|
|
dataSource, |
|
|
isCash, |
|
|
isCash, |
|
|
orders, |
|
|
|
|
|
marketPriceAtEndDate: |
|
|
marketPriceAtEndDate: |
|
|
marketSymbolMap[endDateString]?.[assetProfileIdentifier] |
|
|
marketSymbolMap[endDateString]?.[assetProfileIdentifier] |
|
|
}); |
|
|
}); |
|
|
@ -225,7 +225,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
// A missing market price can only affect the quantity which is held. The
|
|
|
// A missing market price can only affect the quantity which is held. The
|
|
|
// dividends, the interest and the liabilities do not hold any quantity
|
|
|
// dividends, the interest and the liabilities do not hold any quantity
|
|
|
// and are therefore not in error.
|
|
|
// and are therefore not in error.
|
|
|
const hasActivitiesWithQuantity = orders.some(({ type }) => { |
|
|
const hasActivitiesWithQuantity = activities.some(({ type }) => { |
|
|
return ['BUY', 'SELL'].includes(type); |
|
|
return ['BUY', 'SELL'].includes(type); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
@ -239,11 +239,11 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
orders = this.getOrdersWithMarketPrices({ |
|
|
activities = this.getActivitiesWithMarketPrices({ |
|
|
|
|
|
activities, |
|
|
chartDates, |
|
|
chartDates, |
|
|
endDateString, |
|
|
endDateString, |
|
|
marketSymbolMap, |
|
|
marketSymbolMap, |
|
|
orders, |
|
|
|
|
|
startDateString, |
|
|
startDateString, |
|
|
unitPriceAtEndDate, |
|
|
unitPriceAtEndDate, |
|
|
unitPriceAtStartDate, |
|
|
unitPriceAtStartDate, |
|
|
@ -254,11 +254,11 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const indexOfStartOrder = orders.findIndex(({ itemType }) => { |
|
|
const indexOfStartActivity = activities.findIndex(({ itemType }) => { |
|
|
return itemType === 'start'; |
|
|
return itemType === 'start'; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const indexOfEndOrder = orders.findIndex(({ itemType }) => { |
|
|
const indexOfEndActivity = activities.findIndex(({ itemType }) => { |
|
|
return itemType === 'end'; |
|
|
return itemType === 'end'; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
@ -266,56 +266,59 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
let sumOfTimeWeightedInvestments = new Big(0); |
|
|
let sumOfTimeWeightedInvestments = new Big(0); |
|
|
let sumOfTimeWeightedInvestmentsWithCurrencyEffect = new Big(0); |
|
|
let sumOfTimeWeightedInvestmentsWithCurrencyEffect = new Big(0); |
|
|
|
|
|
|
|
|
for (let i = 0; i < orders.length; i += 1) { |
|
|
for (let i = 0; i < activities.length; i += 1) { |
|
|
const order = orders[i]; |
|
|
const activity = activities[i]; |
|
|
|
|
|
|
|
|
if (PortfolioCalculator.ENABLE_LOGGING) { |
|
|
if (PortfolioCalculator.ENABLE_LOGGING) { |
|
|
console.log(); |
|
|
console.log(); |
|
|
console.log(); |
|
|
console.log(); |
|
|
console.log( |
|
|
console.log( |
|
|
i + 1, |
|
|
i + 1, |
|
|
order.date, |
|
|
activity.date, |
|
|
order.type, |
|
|
activity.type, |
|
|
order.itemType ? `(${order.itemType})` : '' |
|
|
activity.itemType ? `(${activity.itemType})` : '' |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const exchangeRateAtOrderDate = exchangeRates[order.date]; |
|
|
const exchangeRateAtActivityDate = exchangeRates[activity.date]; |
|
|
|
|
|
|
|
|
if (order.itemType === 'start') { |
|
|
if (activity.itemType === 'start') { |
|
|
// Take the unit price of the order as the market price if there are no
|
|
|
// Take the unit price of the activity as the market price if there are no
|
|
|
// orders of this symbol before the start date
|
|
|
// activities of this symbol before the start date
|
|
|
order.unitPrice = |
|
|
activity.unitPrice = |
|
|
indexOfStartOrder === 0 |
|
|
indexOfStartActivity === 0 |
|
|
? orders[i + 1]?.unitPrice |
|
|
? activities[i + 1]?.unitPrice |
|
|
: unitPriceAtStartDate; |
|
|
: unitPriceAtStartDate; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (order.fee) { |
|
|
if (activity.fee) { |
|
|
order.feeInBaseCurrency = order.fee.mul(currentExchangeRate ?? 1); |
|
|
activity.feeInBaseCurrency = activity.fee.mul(currentExchangeRate ?? 1); |
|
|
order.feeInBaseCurrencyWithCurrencyEffect = order.fee.mul( |
|
|
activity.feeInBaseCurrencyWithCurrencyEffect = activity.fee.mul( |
|
|
exchangeRateAtOrderDate ?? 1 |
|
|
exchangeRateAtActivityDate ?? 1 |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const unitPrice = ['BUY', 'SELL'].includes(order.type) |
|
|
const unitPrice = ['BUY', 'SELL'].includes(activity.type) |
|
|
? order.unitPrice |
|
|
? activity.unitPrice |
|
|
: order.unitPriceFromMarketData; |
|
|
: activity.unitPriceFromMarketData; |
|
|
|
|
|
|
|
|
if (unitPrice) { |
|
|
if (unitPrice) { |
|
|
order.unitPriceInBaseCurrency = unitPrice.mul(currentExchangeRate ?? 1); |
|
|
activity.unitPriceInBaseCurrency = unitPrice.mul( |
|
|
|
|
|
currentExchangeRate ?? 1 |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
order.unitPriceInBaseCurrencyWithCurrencyEffect = unitPrice.mul( |
|
|
activity.unitPriceInBaseCurrencyWithCurrencyEffect = unitPrice.mul( |
|
|
exchangeRateAtOrderDate ?? 1 |
|
|
exchangeRateAtActivityDate ?? 1 |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const marketPriceInBaseCurrency = |
|
|
const marketPriceInBaseCurrency = |
|
|
order.unitPriceFromMarketData?.mul(currentExchangeRate ?? 1) ?? |
|
|
activity.unitPriceFromMarketData?.mul(currentExchangeRate ?? 1) ?? |
|
|
new Big(0); |
|
|
new Big(0); |
|
|
const marketPriceInBaseCurrencyWithCurrencyEffect = |
|
|
const marketPriceInBaseCurrencyWithCurrencyEffect = |
|
|
order.unitPriceFromMarketData?.mul(exchangeRateAtOrderDate ?? 1) ?? |
|
|
activity.unitPriceFromMarketData?.mul( |
|
|
new Big(0); |
|
|
exchangeRateAtActivityDate ?? 1 |
|
|
|
|
|
) ?? new Big(0); |
|
|
|
|
|
|
|
|
const valueOfInvestmentBeforeTransaction = totalQuantity.mul( |
|
|
const valueOfInvestmentBeforeTransaction = totalQuantity.mul( |
|
|
marketPriceInBaseCurrency |
|
|
marketPriceInBaseCurrency |
|
|
@ -324,7 +327,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
const valueOfInvestmentBeforeTransactionWithCurrencyEffect = |
|
|
const valueOfInvestmentBeforeTransactionWithCurrencyEffect = |
|
|
totalQuantity.mul(marketPriceInBaseCurrencyWithCurrencyEffect); |
|
|
totalQuantity.mul(marketPriceInBaseCurrencyWithCurrencyEffect); |
|
|
|
|
|
|
|
|
if (!investmentAtStartDate && i >= indexOfStartOrder) { |
|
|
if (!investmentAtStartDate && i >= indexOfStartActivity) { |
|
|
investmentAtStartDate = totalInvestment ?? new Big(0); |
|
|
investmentAtStartDate = totalInvestment ?? new Big(0); |
|
|
|
|
|
|
|
|
investmentAtStartDateWithCurrencyEffect = |
|
|
investmentAtStartDateWithCurrencyEffect = |
|
|
@ -339,17 +342,17 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
let transactionInvestment = new Big(0); |
|
|
let transactionInvestment = new Big(0); |
|
|
let transactionInvestmentWithCurrencyEffect = new Big(0); |
|
|
let transactionInvestmentWithCurrencyEffect = new Big(0); |
|
|
|
|
|
|
|
|
if (order.type === 'BUY') { |
|
|
if (activity.type === 'BUY') { |
|
|
transactionInvestment = order.quantity |
|
|
transactionInvestment = activity.quantity |
|
|
.mul(order.unitPriceInBaseCurrency) |
|
|
.mul(activity.unitPriceInBaseCurrency) |
|
|
.mul(getFactor(order.type)); |
|
|
.mul(getFactor(activity.type)); |
|
|
|
|
|
|
|
|
transactionInvestmentWithCurrencyEffect = order.quantity |
|
|
transactionInvestmentWithCurrencyEffect = activity.quantity |
|
|
.mul(order.unitPriceInBaseCurrencyWithCurrencyEffect) |
|
|
.mul(activity.unitPriceInBaseCurrencyWithCurrencyEffect) |
|
|
.mul(getFactor(order.type)); |
|
|
.mul(getFactor(activity.type)); |
|
|
|
|
|
|
|
|
totalQuantityFromBuyTransactions = |
|
|
totalQuantityFromBuyTransactions = |
|
|
totalQuantityFromBuyTransactions.plus(order.quantity); |
|
|
totalQuantityFromBuyTransactions.plus(activity.quantity); |
|
|
|
|
|
|
|
|
totalInvestmentFromBuyTransactions = |
|
|
totalInvestmentFromBuyTransactions = |
|
|
totalInvestmentFromBuyTransactions.plus(transactionInvestment); |
|
|
totalInvestmentFromBuyTransactions.plus(transactionInvestment); |
|
|
@ -358,9 +361,9 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
totalInvestmentFromBuyTransactionsWithCurrencyEffect.plus( |
|
|
totalInvestmentFromBuyTransactionsWithCurrencyEffect.plus( |
|
|
transactionInvestmentWithCurrencyEffect |
|
|
transactionInvestmentWithCurrencyEffect |
|
|
); |
|
|
); |
|
|
} else if (order.type === 'SELL') { |
|
|
} else if (activity.type === 'SELL') { |
|
|
if (totalQuantity.gt(0)) { |
|
|
if (totalQuantity.gt(0)) { |
|
|
const remainingQuantity = totalQuantity.minus(order.quantity); |
|
|
const remainingQuantity = totalQuantity.minus(activity.quantity); |
|
|
|
|
|
|
|
|
transactionInvestment = totalInvestment |
|
|
transactionInvestment = totalInvestment |
|
|
.mul(remainingQuantity) |
|
|
.mul(remainingQuantity) |
|
|
@ -376,7 +379,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (PortfolioCalculator.ENABLE_LOGGING) { |
|
|
if (PortfolioCalculator.ENABLE_LOGGING) { |
|
|
console.log('order.quantity', order.quantity.toNumber()); |
|
|
console.log('activity.quantity', activity.quantity.toNumber()); |
|
|
console.log('transactionInvestment', transactionInvestment.toNumber()); |
|
|
console.log('transactionInvestment', transactionInvestment.toNumber()); |
|
|
|
|
|
|
|
|
console.log( |
|
|
console.log( |
|
|
@ -397,9 +400,9 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
transactionInvestmentWithCurrencyEffect |
|
|
transactionInvestmentWithCurrencyEffect |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
if (i >= indexOfStartOrder && !initialValue) { |
|
|
if (i >= indexOfStartActivity && !initialValue) { |
|
|
if ( |
|
|
if ( |
|
|
i === indexOfStartOrder && |
|
|
i === indexOfStartActivity && |
|
|
!valueOfInvestmentBeforeTransaction.eq(0) |
|
|
!valueOfInvestmentBeforeTransaction.eq(0) |
|
|
) { |
|
|
) { |
|
|
initialValue = valueOfInvestmentBeforeTransaction; |
|
|
initialValue = valueOfInvestmentBeforeTransaction; |
|
|
@ -408,14 +411,14 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fees = fees.plus(order.feeInBaseCurrency ?? 0); |
|
|
fees = fees.plus(activity.feeInBaseCurrency ?? 0); |
|
|
|
|
|
|
|
|
feesWithCurrencyEffect = feesWithCurrencyEffect.plus( |
|
|
feesWithCurrencyEffect = feesWithCurrencyEffect.plus( |
|
|
order.feeInBaseCurrencyWithCurrencyEffect ?? 0 |
|
|
activity.feeInBaseCurrencyWithCurrencyEffect ?? 0 |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
totalQuantity = totalQuantity.plus( |
|
|
totalQuantity = totalQuantity.plus( |
|
|
order.quantity.mul(getFactor(order.type)) |
|
|
activity.quantity.mul(getFactor(activity.type)) |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
const valueOfInvestment = totalQuantity.mul(marketPriceInBaseCurrency); |
|
|
const valueOfInvestment = totalQuantity.mul(marketPriceInBaseCurrency); |
|
|
@ -425,17 +428,17 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
const grossPerformanceFromSell = |
|
|
const grossPerformanceFromSell = |
|
|
order.type === 'SELL' |
|
|
activity.type === 'SELL' |
|
|
? order.unitPriceInBaseCurrency |
|
|
? activity.unitPriceInBaseCurrency |
|
|
.minus(lastAveragePrice) |
|
|
.minus(lastAveragePrice) |
|
|
.mul(order.quantity) |
|
|
.mul(activity.quantity) |
|
|
: new Big(0); |
|
|
: new Big(0); |
|
|
|
|
|
|
|
|
const grossPerformanceFromSellWithCurrencyEffect = |
|
|
const grossPerformanceFromSellWithCurrencyEffect = |
|
|
order.type === 'SELL' |
|
|
activity.type === 'SELL' |
|
|
? order.unitPriceInBaseCurrencyWithCurrencyEffect |
|
|
? activity.unitPriceInBaseCurrencyWithCurrencyEffect |
|
|
.minus(lastAveragePriceWithCurrencyEffect) |
|
|
.minus(lastAveragePriceWithCurrencyEffect) |
|
|
.mul(order.quantity) |
|
|
.mul(activity.quantity) |
|
|
: new Big(0); |
|
|
: new Big(0); |
|
|
|
|
|
|
|
|
grossPerformanceFromSells = grossPerformanceFromSells.plus( |
|
|
grossPerformanceFromSells = grossPerformanceFromSells.plus( |
|
|
@ -493,7 +496,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
grossPerformanceWithCurrencyEffect = |
|
|
grossPerformanceWithCurrencyEffect = |
|
|
newGrossPerformanceWithCurrencyEffect; |
|
|
newGrossPerformanceWithCurrencyEffect; |
|
|
|
|
|
|
|
|
if (order.itemType === 'start') { |
|
|
if (activity.itemType === 'start') { |
|
|
feesAtStartDate = fees; |
|
|
feesAtStartDate = fees; |
|
|
feesAtStartDateWithCurrencyEffect = feesWithCurrencyEffect; |
|
|
feesAtStartDateWithCurrencyEffect = feesWithCurrencyEffect; |
|
|
grossPerformanceAtStartDate = grossPerformance; |
|
|
grossPerformanceAtStartDate = grossPerformance; |
|
|
@ -502,36 +505,36 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
grossPerformanceWithCurrencyEffect; |
|
|
grossPerformanceWithCurrencyEffect; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (i > indexOfStartOrder) { |
|
|
if (i > indexOfStartActivity) { |
|
|
// 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 ( |
|
|
if ( |
|
|
valueOfInvestmentBeforeTransaction.gt(0) && |
|
|
valueOfInvestmentBeforeTransaction.gt(0) && |
|
|
['BUY', 'SELL'].includes(order.type) |
|
|
['BUY', 'SELL'].includes(activity.type) |
|
|
) { |
|
|
) { |
|
|
// Calculate the number of days since the previous order
|
|
|
// Calculate the number of days since the previous activity
|
|
|
const orderDate = new Date(order.date); |
|
|
const activityDate = new Date(activity.date); |
|
|
const previousOrderDate = new Date(orders[i - 1].date); |
|
|
const previousActivityDate = new Date(activities[i - 1].date); |
|
|
|
|
|
|
|
|
let daysSinceLastOrder = differenceInDays( |
|
|
let daysSinceLastActivity = differenceInDays( |
|
|
orderDate, |
|
|
activityDate, |
|
|
previousOrderDate |
|
|
previousActivityDate |
|
|
); |
|
|
); |
|
|
if (daysSinceLastOrder <= 0) { |
|
|
if (daysSinceLastActivity <= 0) { |
|
|
// The time between two activities on the same day is unknown
|
|
|
// The time between two activities on the same day is unknown
|
|
|
// -> Set it to the smallest floating point number greater than 0
|
|
|
// -> Set it to the smallest floating point number greater than 0
|
|
|
daysSinceLastOrder = Number.EPSILON; |
|
|
daysSinceLastActivity = Number.EPSILON; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Sum up the total investment days since the start date to calculate
|
|
|
// Sum up the total investment days since the start date to calculate
|
|
|
// the time weighted investment
|
|
|
// the time weighted investment
|
|
|
totalInvestmentDays += daysSinceLastOrder; |
|
|
totalInvestmentDays += daysSinceLastActivity; |
|
|
|
|
|
|
|
|
sumOfTimeWeightedInvestments = sumOfTimeWeightedInvestments.add( |
|
|
sumOfTimeWeightedInvestments = sumOfTimeWeightedInvestments.add( |
|
|
valueAtStartDate |
|
|
valueAtStartDate |
|
|
.minus(investmentAtStartDate) |
|
|
.minus(investmentAtStartDate) |
|
|
.plus(totalInvestmentBeforeTransaction) |
|
|
.plus(totalInvestmentBeforeTransaction) |
|
|
.mul(daysSinceLastOrder) |
|
|
.mul(daysSinceLastActivity) |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
sumOfTimeWeightedInvestmentsWithCurrencyEffect = |
|
|
sumOfTimeWeightedInvestmentsWithCurrencyEffect = |
|
|
@ -539,45 +542,45 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
valueAtStartDateWithCurrencyEffect |
|
|
valueAtStartDateWithCurrencyEffect |
|
|
.minus(investmentAtStartDateWithCurrencyEffect) |
|
|
.minus(investmentAtStartDateWithCurrencyEffect) |
|
|
.plus(totalInvestmentBeforeTransactionWithCurrencyEffect) |
|
|
.plus(totalInvestmentBeforeTransactionWithCurrencyEffect) |
|
|
.mul(daysSinceLastOrder) |
|
|
.mul(daysSinceLastActivity) |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
currentValues[order.date] = valueOfInvestment; |
|
|
currentValues[activity.date] = valueOfInvestment; |
|
|
|
|
|
|
|
|
currentValuesWithCurrencyEffect[order.date] = |
|
|
currentValuesWithCurrencyEffect[activity.date] = |
|
|
valueOfInvestmentWithCurrencyEffect; |
|
|
valueOfInvestmentWithCurrencyEffect; |
|
|
|
|
|
|
|
|
netPerformanceValues[order.date] = grossPerformance |
|
|
netPerformanceValues[activity.date] = grossPerformance |
|
|
.minus(grossPerformanceAtStartDate) |
|
|
.minus(grossPerformanceAtStartDate) |
|
|
.minus(fees.minus(feesAtStartDate)); |
|
|
.minus(fees.minus(feesAtStartDate)); |
|
|
|
|
|
|
|
|
netPerformanceValuesWithCurrencyEffect[order.date] = |
|
|
netPerformanceValuesWithCurrencyEffect[activity.date] = |
|
|
grossPerformanceWithCurrencyEffect |
|
|
grossPerformanceWithCurrencyEffect |
|
|
.minus(grossPerformanceAtStartDateWithCurrencyEffect) |
|
|
.minus(grossPerformanceAtStartDateWithCurrencyEffect) |
|
|
.minus( |
|
|
.minus( |
|
|
feesWithCurrencyEffect.minus(feesAtStartDateWithCurrencyEffect) |
|
|
feesWithCurrencyEffect.minus(feesAtStartDateWithCurrencyEffect) |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
investmentValuesAccumulated[order.date] = totalInvestment; |
|
|
investmentValuesAccumulated[activity.date] = totalInvestment; |
|
|
|
|
|
|
|
|
investmentValuesAccumulatedWithCurrencyEffect[order.date] = |
|
|
investmentValuesAccumulatedWithCurrencyEffect[activity.date] = |
|
|
totalInvestmentWithCurrencyEffect; |
|
|
totalInvestmentWithCurrencyEffect; |
|
|
|
|
|
|
|
|
investmentValuesWithCurrencyEffect[order.date] = ( |
|
|
investmentValuesWithCurrencyEffect[activity.date] = ( |
|
|
investmentValuesWithCurrencyEffect[order.date] ?? new Big(0) |
|
|
investmentValuesWithCurrencyEffect[activity.date] ?? new Big(0) |
|
|
).add(transactionInvestmentWithCurrencyEffect); |
|
|
).add(transactionInvestmentWithCurrencyEffect); |
|
|
|
|
|
|
|
|
// If duration is effectively zero (first day), use the actual investment as the base.
|
|
|
// If duration is effectively zero (first day), use the actual investment as the base.
|
|
|
// Otherwise, use the calculated time-weighted average.
|
|
|
// Otherwise, use the calculated time-weighted average.
|
|
|
timeWeightedInvestmentValues[order.date] = |
|
|
timeWeightedInvestmentValues[activity.date] = |
|
|
totalInvestmentDays > Number.EPSILON |
|
|
totalInvestmentDays > Number.EPSILON |
|
|
? sumOfTimeWeightedInvestments.div(totalInvestmentDays) |
|
|
? sumOfTimeWeightedInvestments.div(totalInvestmentDays) |
|
|
: totalInvestment.gt(0) |
|
|
: totalInvestment.gt(0) |
|
|
? totalInvestment |
|
|
? totalInvestment |
|
|
: new Big(0); |
|
|
: new Big(0); |
|
|
|
|
|
|
|
|
timeWeightedInvestmentValuesWithCurrencyEffect[order.date] = |
|
|
timeWeightedInvestmentValuesWithCurrencyEffect[activity.date] = |
|
|
totalInvestmentDays > Number.EPSILON |
|
|
totalInvestmentDays > Number.EPSILON |
|
|
? sumOfTimeWeightedInvestmentsWithCurrencyEffect.div( |
|
|
? sumOfTimeWeightedInvestmentsWithCurrencyEffect.div( |
|
|
totalInvestmentDays |
|
|
totalInvestmentDays |
|
|
@ -608,7 +611,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (i === indexOfEndOrder) { |
|
|
if (i === indexOfEndActivity) { |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -768,7 +771,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
console.log( |
|
|
console.log( |
|
|
` |
|
|
` |
|
|
${symbol} |
|
|
${symbol} |
|
|
Unit price: ${orders[indexOfStartOrder].unitPrice.toFixed( |
|
|
Unit price: ${activities[indexOfStartActivity].unitPrice.toFixed( |
|
|
2 |
|
|
2 |
|
|
)} -> ${unitPriceAtEndDate.toFixed(2)} |
|
|
)} -> ${unitPriceAtEndDate.toFixed(2)} |
|
|
Total investment: ${totalInvestment.toFixed(2)} |
|
|
Total investment: ${totalInvestment.toFixed(2)} |
|
|
|