|
|
@ -3,7 +3,7 @@ import { PortfolioCalculatorPosition } from '@ghostfolio/api/app/portfolio/inter |
|
|
import { PortfolioOrderItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order-item.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 { DATE_FORMAT } from '@ghostfolio/common/helper'; |
|
|
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; |
|
|
import { |
|
|
import { |
|
|
AssetProfileIdentifier, |
|
|
AssetProfileIdentifier, |
|
|
SymbolMetrics |
|
|
SymbolMetrics |
|
|
@ -21,7 +21,7 @@ import { |
|
|
isBefore, |
|
|
isBefore, |
|
|
isThisYear |
|
|
isThisYear |
|
|
} from 'date-fns'; |
|
|
} from 'date-fns'; |
|
|
import { cloneDeep, sortBy } from 'lodash'; |
|
|
import { sortBy } from 'lodash'; |
|
|
|
|
|
|
|
|
export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
private chartDates: string[]; |
|
|
private chartDates: string[]; |
|
|
@ -192,12 +192,13 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
let valueAtStartDate: Big; |
|
|
let valueAtStartDate: Big; |
|
|
let valueAtStartDateWithCurrencyEffect: Big; |
|
|
let valueAtStartDateWithCurrencyEffect: Big; |
|
|
|
|
|
|
|
|
// Deep clone as the items are enriched below and the originals are shared
|
|
|
// Copy the items as they are enriched below. A shallow copy is sufficient
|
|
|
let orders: PortfolioOrderItem[] = cloneDeep( |
|
|
// because only top-level properties are written.
|
|
|
this.activities.filter((activities) => { |
|
|
let orders: PortfolioOrderItem[] = ( |
|
|
return activities.assetProfile.symbol === symbol; |
|
|
this.activitiesBySymbol[symbol] ?? [] |
|
|
}) |
|
|
).map((activity) => { |
|
|
); |
|
|
return { ...activity }; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
const isCash = orders[0]?.assetProfile?.assetSubClass === 'CASH'; |
|
|
const isCash = orders[0]?.assetProfile?.assetSubClass === 'CASH'; |
|
|
|
|
|
|
|
|
@ -268,7 +269,8 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const dateOfFirstTransaction = new Date(orders[0].date); |
|
|
const dateStringOfFirstActivity = orders[0].date; |
|
|
|
|
|
const dateOfFirstActivity = parseDate(dateStringOfFirstActivity); |
|
|
|
|
|
|
|
|
const endDateString = format(end, DATE_FORMAT); |
|
|
const endDateString = format(end, DATE_FORMAT); |
|
|
const startDateString = format(start, DATE_FORMAT); |
|
|
const startDateString = format(start, DATE_FORMAT); |
|
|
@ -276,7 +278,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
const unitPriceAtStartDate = marketSymbolMap[startDateString]?.[symbol]; |
|
|
const unitPriceAtStartDate = marketSymbolMap[startDateString]?.[symbol]; |
|
|
let unitPriceAtEndDate = marketSymbolMap[endDateString]?.[symbol]; |
|
|
let unitPriceAtEndDate = marketSymbolMap[endDateString]?.[symbol]; |
|
|
|
|
|
|
|
|
let latestActivity = orders.at(-1); |
|
|
const latestActivity = orders.at(-1); |
|
|
|
|
|
|
|
|
if ( |
|
|
if ( |
|
|
dataSource === 'MANUAL' && |
|
|
dataSource === 'MANUAL' && |
|
|
@ -293,7 +295,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
|
|
|
|
|
|
if ( |
|
|
if ( |
|
|
!unitPriceAtEndDate || |
|
|
!unitPriceAtEndDate || |
|
|
(!unitPriceAtStartDate && isBefore(dateOfFirstTransaction, start)) |
|
|
(!unitPriceAtStartDate && isBefore(dateOfFirstActivity, start)) |
|
|
) { |
|
|
) { |
|
|
// 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
|
|
|
@ -367,7 +369,9 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
unitPrice: unitPriceAtEndDate |
|
|
unitPrice: unitPriceAtEndDate |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
let lastUnitPrice: Big; |
|
|
// Fall back to the unit price at the end date for the chart dates before
|
|
|
|
|
|
// the first known market price of the symbol
|
|
|
|
|
|
let lastUnitPrice = unitPriceAtEndDate; |
|
|
|
|
|
|
|
|
const ordersByDate: { [date: string]: PortfolioOrderItem[] } = {}; |
|
|
const ordersByDate: { [date: string]: PortfolioOrderItem[] } = {}; |
|
|
|
|
|
|
|
|
@ -393,23 +397,24 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
marketSymbolMap[dateString]?.[symbol] ?? lastUnitPrice; |
|
|
marketSymbolMap[dateString]?.[symbol] ?? lastUnitPrice; |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
orders.push({ |
|
|
const unitPrice = |
|
|
assetProfile, |
|
|
marketSymbolMap[dateString]?.[symbol] ?? lastUnitPrice; |
|
|
date: dateString, |
|
|
|
|
|
fee: new Big(0), |
|
|
if (dateString >= dateStringOfFirstActivity) { |
|
|
feeInBaseCurrency: new Big(0), |
|
|
orders.push({ |
|
|
quantity: new Big(0), |
|
|
assetProfile, |
|
|
type: 'BUY', |
|
|
unitPrice, |
|
|
unitPrice: marketSymbolMap[dateString]?.[symbol] ?? lastUnitPrice, |
|
|
date: dateString, |
|
|
unitPriceFromMarketData: |
|
|
fee: new Big(0), |
|
|
marketSymbolMap[dateString]?.[symbol] ?? lastUnitPrice |
|
|
feeInBaseCurrency: new Big(0), |
|
|
}); |
|
|
quantity: new Big(0), |
|
|
} |
|
|
type: 'BUY', |
|
|
|
|
|
unitPriceFromMarketData: unitPrice |
|
|
latestActivity = orders.at(-1); |
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
lastUnitPrice = |
|
|
lastUnitPrice = unitPrice; |
|
|
latestActivity.unitPriceFromMarketData ?? latestActivity.unitPrice; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Sort orders so that the start and end placeholder order are at the correct
|
|
|
// Sort orders so that the start and end placeholder order are at the correct
|
|
|
|