Browse Source

net performance for current positions (#330)

* implement fees for transaction points #324

* add net performance to current positions #324

* add net performance to calculate timeline #324

* make timeline fee accumulated by default #324

* Update changelog

Co-authored-by: Valentin Zickner <github@zickner.ch>
Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
pull/347/head
Valentin Zickner 3 years ago
committed by GitHub
parent
commit
48ab862bb6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      CHANGELOG.md
  2. 2
      apps/api/src/app/portfolio/interfaces/current-positions.interface.ts
  3. 1
      apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts
  4. 2
      apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts
  5. 1
      apps/api/src/app/portfolio/interfaces/timeline-period.interface.ts
  6. 1
      apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts
  7. 745
      apps/api/src/app/portfolio/portfolio-calculator.spec.ts
  8. 82
      apps/api/src/app/portfolio/portfolio-calculator.ts
  9. 38
      apps/api/src/app/portfolio/portfolio.service.ts
  10. 4
      apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html
  11. 2
      apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts
  12. 63
      apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
  13. 6
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts
  14. 4
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html
  15. 6
      apps/client/src/app/components/position/position.component.html
  16. 2
      apps/client/src/app/components/positions-table/positions-table.component.html
  17. 2
      libs/common/src/lib/interfaces/portfolio-performance.interface.ts
  18. 2
      libs/common/src/lib/interfaces/portfolio-position.interface.ts
  19. 2
      libs/common/src/lib/interfaces/position.interface.ts
  20. 2
      libs/common/src/lib/interfaces/timeline-position.interface.ts

5
CHANGELOG.md

@ -16,6 +16,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added a story for the trend indicator component - Added a story for the trend indicator component
- Added a story for the value component - Added a story for the value component
### Changed
- Switched from gross to net performance
- Restructured the portfolio summary tab on the home page (fees and net performance)
## 1.45.0 - 04.09.2021 ## 1.45.0 - 04.09.2021
### Added ### Added

2
apps/api/src/app/portfolio/interfaces/current-positions.interface.ts

@ -6,6 +6,8 @@ export interface CurrentPositions {
positions: TimelinePosition[]; positions: TimelinePosition[];
grossPerformance: Big; grossPerformance: Big;
grossPerformancePercentage: Big; grossPerformancePercentage: Big;
netPerformance: Big;
netPerformancePercentage: Big;
currentValue: Big; currentValue: Big;
totalInvestment: Big; totalInvestment: Big;
} }

1
apps/api/src/app/portfolio/interfaces/portfolio-order.interface.ts

@ -5,6 +5,7 @@ import Big from 'big.js';
export interface PortfolioOrder { export interface PortfolioOrder {
currency: Currency; currency: Currency;
date: string; date: string;
fee: Big;
name: string; name: string;
quantity: Big; quantity: Big;
symbol: string; symbol: string;

2
apps/api/src/app/portfolio/interfaces/portfolio-position-detail.interface.ts

@ -11,6 +11,8 @@ export interface PortfolioPositionDetail {
marketPrice: number; marketPrice: number;
maxPrice: number; maxPrice: number;
minPrice: number; minPrice: number;
netPerformance: number;
netPerformancePercent: number;
quantity: number; quantity: number;
symbol: string; symbol: string;
transactionCount: number; transactionCount: number;

1
apps/api/src/app/portfolio/interfaces/timeline-period.interface.ts

@ -4,5 +4,6 @@ export interface TimelinePeriod {
date: string; date: string;
grossPerformance: Big; grossPerformance: Big;
investment: Big; investment: Big;
netPerformance: Big;
value: Big; value: Big;
} }

1
apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts

@ -3,6 +3,7 @@ import Big from 'big.js';
export interface TransactionPointSymbol { export interface TransactionPointSymbol {
currency: Currency; currency: Currency;
fee: Big;
firstBuyDate: string; firstBuyDate: string;
investment: Big; investment: Big;
quantity: Big; quantity: Big;

745
apps/api/src/app/portfolio/portfolio-calculator.spec.ts

File diff suppressed because it is too large

82
apps/api/src/app/portfolio/portfolio-calculator.ts

@ -58,6 +58,7 @@ export class PortfolioCalculator {
.plus(oldAccumulatedSymbol.quantity); .plus(oldAccumulatedSymbol.quantity);
currentTransactionPointItem = { currentTransactionPointItem = {
currency: order.currency, currency: order.currency,
fee: order.fee.plus(oldAccumulatedSymbol.fee),
firstBuyDate: oldAccumulatedSymbol.firstBuyDate, firstBuyDate: oldAccumulatedSymbol.firstBuyDate,
investment: newQuantity.eq(0) investment: newQuantity.eq(0)
? new Big(0) ? new Big(0)
@ -72,6 +73,7 @@ export class PortfolioCalculator {
} else { } else {
currentTransactionPointItem = { currentTransactionPointItem = {
currency: order.currency, currency: order.currency,
fee: order.fee,
firstBuyDate: order.date, firstBuyDate: order.date,
investment: unitPrice.mul(order.quantity).mul(factor), investment: unitPrice.mul(order.quantity).mul(factor),
quantity: order.quantity.mul(factor), quantity: order.quantity.mul(factor),
@ -112,11 +114,13 @@ export class PortfolioCalculator {
public async getCurrentPositions(start: Date): Promise<CurrentPositions> { public async getCurrentPositions(start: Date): Promise<CurrentPositions> {
if (!this.transactionPoints?.length) { if (!this.transactionPoints?.length) {
return { return {
currentValue: new Big(0),
hasErrors: false, hasErrors: false,
positions: [],
grossPerformance: new Big(0), grossPerformance: new Big(0),
grossPerformancePercentage: new Big(0), grossPerformancePercentage: new Big(0),
currentValue: new Big(0), netPerformance: new Big(0),
netPerformancePercentage: new Big(0),
positions: [],
totalInvestment: new Big(0) totalInvestment: new Big(0)
}; };
} }
@ -181,7 +185,9 @@ export class PortfolioCalculator {
const startString = format(start, DATE_FORMAT); const startString = format(start, DATE_FORMAT);
const holdingPeriodReturns: { [symbol: string]: Big } = {}; const holdingPeriodReturns: { [symbol: string]: Big } = {};
const netHoldingPeriodReturns: { [symbol: string]: Big } = {};
const grossPerformance: { [symbol: string]: Big } = {}; const grossPerformance: { [symbol: string]: Big } = {};
const netPerformance: { [symbol: string]: Big } = {};
const todayString = format(today, DATE_FORMAT); const todayString = format(today, DATE_FORMAT);
if (firstIndex > 0) { if (firstIndex > 0) {
@ -190,6 +196,7 @@ export class PortfolioCalculator {
const invalidSymbols = []; const invalidSymbols = [];
const lastInvestments: { [symbol: string]: Big } = {}; const lastInvestments: { [symbol: string]: Big } = {};
const lastQuantities: { [symbol: string]: Big } = {}; const lastQuantities: { [symbol: string]: Big } = {};
const lastFees: { [symbol: string]: Big } = {};
const initialValues: { [symbol: string]: Big } = {}; const initialValues: { [symbol: string]: Big } = {};
for (let i = firstIndex; i < this.transactionPoints.length; i++) { for (let i = firstIndex; i < this.transactionPoints.length; i++) {
@ -202,10 +209,6 @@ export class PortfolioCalculator {
const items = this.transactionPoints[i].items; const items = this.transactionPoints[i].items;
for (const item of items) { for (const item of items) {
let oldHoldingPeriodReturn = holdingPeriodReturns[item.symbol];
if (!oldHoldingPeriodReturn) {
oldHoldingPeriodReturn = new Big(1);
}
if (!marketSymbolMap[nextDate]?.[item.symbol]) { if (!marketSymbolMap[nextDate]?.[item.symbol]) {
invalidSymbols.push(item.symbol); invalidSymbols.push(item.symbol);
hasErrors = true; hasErrors = true;
@ -224,6 +227,13 @@ export class PortfolioCalculator {
const itemValue = marketSymbolMap[currentDate]?.[item.symbol]; const itemValue = marketSymbolMap[currentDate]?.[item.symbol];
let initialValue = itemValue?.mul(lastQuantity); let initialValue = itemValue?.mul(lastQuantity);
let investedValue = itemValue?.mul(item.quantity); let investedValue = itemValue?.mul(item.quantity);
const isFirstOrderAndIsStartBeforeCurrentDate =
i === firstIndex &&
isBefore(parseDate(this.transactionPoints[i].date), start);
const lastFee: Big = lastFees[item.symbol] ?? new Big(0);
const fee = isFirstOrderAndIsStartBeforeCurrentDate
? new Big(0)
: item.fee.minus(lastFee);
if (!isAfter(parseDate(currentDate), parseDate(item.firstBuyDate))) { if (!isAfter(parseDate(currentDate), parseDate(item.firstBuyDate))) {
initialValue = item.investment; initialValue = item.investment;
investedValue = item.investment; investedValue = item.investment;
@ -247,18 +257,26 @@ export class PortfolioCalculator {
); );
const holdingPeriodReturn = endValue.div(initialValue.plus(cashFlow)); const holdingPeriodReturn = endValue.div(initialValue.plus(cashFlow));
holdingPeriodReturns[item.symbol] = holdingPeriodReturns[item.symbol] = (
oldHoldingPeriodReturn.mul(holdingPeriodReturn); holdingPeriodReturns[item.symbol] ?? new Big(1)
let oldGrossPerformance = grossPerformance[item.symbol]; ).mul(holdingPeriodReturn);
if (!oldGrossPerformance) { grossPerformance[item.symbol] = (
oldGrossPerformance = new Big(0); grossPerformance[item.symbol] ?? new Big(0)
} ).plus(endValue.minus(investedValue));
const currentPerformance = endValue.minus(investedValue);
grossPerformance[item.symbol] = const netHoldingPeriodReturn = endValue.div(
oldGrossPerformance.plus(currentPerformance); initialValue.plus(cashFlow).plus(fee)
);
netHoldingPeriodReturns[item.symbol] = (
netHoldingPeriodReturns[item.symbol] ?? new Big(1)
).mul(netHoldingPeriodReturn);
netPerformance[item.symbol] = (
netPerformance[item.symbol] ?? new Big(0)
).plus(endValue.minus(investedValue).minus(fee));
} }
lastInvestments[item.symbol] = item.investment; lastInvestments[item.symbol] = item.investment;
lastQuantities[item.symbol] = item.quantity; lastQuantities[item.symbol] = item.quantity;
lastFees[item.symbol] = item.fee;
} }
} }
@ -282,15 +300,17 @@ export class PortfolioCalculator {
: null, : null,
investment: item.investment, investment: item.investment,
marketPrice: marketValue?.toNumber() ?? null, marketPrice: marketValue?.toNumber() ?? null,
netPerformance: isValid ? netPerformance[item.symbol] ?? null : null,
netPerformancePercentage:
isValid && netHoldingPeriodReturns[item.symbol]
? netHoldingPeriodReturns[item.symbol].minus(1)
: null,
quantity: item.quantity, quantity: item.quantity,
symbol: item.symbol, symbol: item.symbol,
transactionCount: item.transactionCount transactionCount: item.transactionCount
}); });
} }
const overall = this.calculateOverallGrossPerformance( const overall = this.calculateOverallPerformance(positions, initialValues);
positions,
initialValues
);
return { return {
...overall, ...overall,
@ -378,7 +398,7 @@ export class PortfolioCalculator {
return flatten(timelinePeriods); return flatten(timelinePeriods);
} }
private calculateOverallGrossPerformance( private calculateOverallPerformance(
positions: TimelinePosition[], positions: TimelinePosition[],
initialValues: { [p: string]: Big } initialValues: { [p: string]: Big }
) { ) {
@ -387,6 +407,8 @@ export class PortfolioCalculator {
let totalInvestment = new Big(0); let totalInvestment = new Big(0);
let grossPerformance = new Big(0); let grossPerformance = new Big(0);
let grossPerformancePercentage = new Big(0); let grossPerformancePercentage = new Big(0);
let netPerformance = new Big(0);
let netPerformancePercentage = new Big(0);
let completeInitialValue = new Big(0); let completeInitialValue = new Big(0);
for (const currentPosition of positions) { for (const currentPosition of positions) {
if (currentPosition.marketPrice) { if (currentPosition.marketPrice) {
@ -401,6 +423,7 @@ export class PortfolioCalculator {
grossPerformance = grossPerformance.plus( grossPerformance = grossPerformance.plus(
currentPosition.grossPerformance currentPosition.grossPerformance
); );
netPerformance = netPerformance.plus(currentPosition.netPerformance);
} else if (!currentPosition.quantity.eq(0)) { } else if (!currentPosition.quantity.eq(0)) {
hasErrors = true; hasErrors = true;
} }
@ -414,6 +437,9 @@ export class PortfolioCalculator {
grossPerformancePercentage = grossPerformancePercentage.plus( grossPerformancePercentage = grossPerformancePercentage.plus(
currentPosition.grossPerformancePercentage.mul(currentInitialValue) currentPosition.grossPerformancePercentage.mul(currentInitialValue)
); );
netPerformancePercentage = netPerformancePercentage.plus(
currentPosition.netPerformancePercentage.mul(currentInitialValue)
);
} else if (!currentPosition.quantity.eq(0)) { } else if (!currentPosition.quantity.eq(0)) {
console.error( console.error(
`Initial value is missing for symbol ${currentPosition.symbol}` `Initial value is missing for symbol ${currentPosition.symbol}`
@ -425,6 +451,8 @@ export class PortfolioCalculator {
if (!completeInitialValue.eq(0)) { if (!completeInitialValue.eq(0)) {
grossPerformancePercentage = grossPerformancePercentage =
grossPerformancePercentage.div(completeInitialValue); grossPerformancePercentage.div(completeInitialValue);
netPerformancePercentage =
netPerformancePercentage.div(completeInitialValue);
} }
return { return {
@ -432,6 +460,8 @@ export class PortfolioCalculator {
grossPerformance, grossPerformance,
grossPerformancePercentage, grossPerformancePercentage,
hasErrors, hasErrors,
netPerformance,
netPerformancePercentage,
totalInvestment totalInvestment
}; };
} }
@ -442,6 +472,7 @@ export class PortfolioCalculator {
endDate: Date endDate: Date
): Promise<TimelinePeriod[]> { ): Promise<TimelinePeriod[]> {
let investment: Big = new Big(0); let investment: Big = new Big(0);
let fees: Big = new Big(0);
const marketSymbolMap: { const marketSymbolMap: {
[date: string]: { [symbol: string]: Big }; [date: string]: { [symbol: string]: Big };
@ -454,6 +485,7 @@ export class PortfolioCalculator {
currencies[item.symbol] = item.currency; currencies[item.symbol] = item.currency;
symbols.push(item.symbol); symbols.push(item.symbol);
investment = investment.add(item.investment); investment = investment.add(item.investment);
fees = fees.add(item.fee);
} }
let marketSymbols: GetValueObject[] = []; let marketSymbols: GetValueObject[] = [];
@ -490,7 +522,7 @@ export class PortfolioCalculator {
} }
} }
const results = []; const results: TimelinePeriod[] = [];
for ( for (
let currentDate = startDate; let currentDate = startDate;
isBefore(currentDate, endDate); isBefore(currentDate, endDate);
@ -513,11 +545,13 @@ export class PortfolioCalculator {
} }
} }
if (!invalid) { if (!invalid) {
const grossPerformance = value.minus(investment);
const result = { const result = {
date: currentDateAsString, grossPerformance,
grossPerformance: value.minus(investment),
investment, investment,
value value,
date: currentDateAsString,
netPerformance: grossPerformance.minus(fees)
}; };
results.push(result); results.push(result);
} }

38
apps/api/src/app/portfolio/portfolio.service.ts

@ -147,7 +147,7 @@ export class PortfolioService {
.map((timelineItem) => ({ .map((timelineItem) => ({
date: timelineItem.date, date: timelineItem.date,
marketPrice: timelineItem.value, marketPrice: timelineItem.value,
value: timelineItem.grossPerformance.toNumber() value: timelineItem.netPerformance.toNumber()
})); }));
} }
@ -233,6 +233,8 @@ export class PortfolioService {
marketPrice: item.marketPrice, marketPrice: item.marketPrice,
marketState: dataProviderResponse.marketState, marketState: dataProviderResponse.marketState,
name: symbolProfile.name, name: symbolProfile.name,
netPerformance: item.netPerformance?.toNumber() ?? 0,
netPerformancePercent: item.netPerformancePercentage?.toNumber() ?? 0,
quantity: item.quantity.toNumber(), quantity: item.quantity.toNumber(),
sectors: symbolProfile.sectors, sectors: symbolProfile.sectors,
symbol: item.symbol, symbol: item.symbol,
@ -280,6 +282,8 @@ export class PortfolioService {
marketPrice: undefined, marketPrice: undefined,
maxPrice: undefined, maxPrice: undefined,
minPrice: undefined, minPrice: undefined,
netPerformance: undefined,
netPerformancePercent: undefined,
quantity: undefined, quantity: undefined,
symbol: aSymbol, symbol: aSymbol,
transactionCount: undefined transactionCount: undefined
@ -291,6 +295,7 @@ export class PortfolioService {
const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({ const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({
currency: order.currency, currency: order.currency,
date: format(order.date, DATE_FORMAT), date: format(order.date, DATE_FORMAT),
fee: new Big(order.fee),
name: order.SymbolProfile?.name, name: order.SymbolProfile?.name,
quantity: new Big(order.quantity), quantity: new Big(order.quantity),
symbol: order.symbol, symbol: order.symbol,
@ -324,7 +329,7 @@ export class PortfolioService {
transactionCount transactionCount
} = position; } = position;
// Convert investment and gross performance to currency of user // Convert investment, gross and net performance to currency of user
const userCurrency = this.request.user.Settings.currency; const userCurrency = this.request.user.Settings.currency;
const investment = this.exchangeRateDataService.toCurrency( const investment = this.exchangeRateDataService.toCurrency(
position.investment.toNumber(), position.investment.toNumber(),
@ -336,6 +341,11 @@ export class PortfolioService {
currency, currency,
userCurrency userCurrency
); );
const netPerformance = this.exchangeRateDataService.toCurrency(
position.netPerformance.toNumber(),
currency,
userCurrency
);
const historicalData = await this.dataProviderService.getHistorical( const historicalData = await this.dataProviderService.getHistorical(
[aSymbol], [aSymbol],
@ -397,10 +407,12 @@ export class PortfolioService {
marketPrice, marketPrice,
maxPrice, maxPrice,
minPrice, minPrice,
netPerformance,
transactionCount, transactionCount,
averagePrice: averagePrice.toNumber(), averagePrice: averagePrice.toNumber(),
grossPerformancePercent: position.grossPerformancePercentage.toNumber(), grossPerformancePercent: position.grossPerformancePercentage.toNumber(),
historicalData: historicalDataArray, historicalData: historicalDataArray,
netPerformancePercent: position.netPerformancePercentage.toNumber(),
quantity: quantity.toNumber(), quantity: quantity.toNumber(),
symbol: aSymbol symbol: aSymbol
}; };
@ -450,6 +462,8 @@ export class PortfolioService {
grossPerformancePercent: undefined, grossPerformancePercent: undefined,
historicalData: historicalDataArray, historicalData: historicalDataArray,
investment: 0, investment: 0,
netPerformance: undefined,
netPerformancePercent: undefined,
quantity: 0, quantity: 0,
symbol: aSymbol, symbol: aSymbol,
transactionCount: undefined transactionCount: undefined
@ -513,6 +527,9 @@ export class PortfolioService {
investment: new Big(position.investment).toNumber(), investment: new Big(position.investment).toNumber(),
marketState: dataProviderResponses[position.symbol].marketState, marketState: dataProviderResponses[position.symbol].marketState,
name: symbolProfileMap[position.symbol].name, name: symbolProfileMap[position.symbol].name,
netPerformance: position.netPerformance?.toNumber() ?? null,
netPerformancePercentage:
position.netPerformancePercentage?.toNumber() ?? null,
quantity: new Big(position.quantity).toNumber() quantity: new Big(position.quantity).toNumber()
}; };
}) })
@ -538,6 +555,8 @@ export class PortfolioService {
performance: { performance: {
currentGrossPerformance: 0, currentGrossPerformance: 0,
currentGrossPerformancePercent: 0, currentGrossPerformancePercent: 0,
currentNetPerformance: 0,
currentNetPerformancePercent: 0,
currentValue: 0 currentValue: 0
} }
}; };
@ -557,11 +576,17 @@ export class PortfolioService {
currentPositions.grossPerformance.toNumber(); currentPositions.grossPerformance.toNumber();
const currentGrossPerformancePercent = const currentGrossPerformancePercent =
currentPositions.grossPerformancePercentage.toNumber(); currentPositions.grossPerformancePercentage.toNumber();
const currentNetPerformance = currentPositions.netPerformance.toNumber();
const currentNetPerformancePercent =
currentPositions.netPerformancePercentage.toNumber();
return { return {
hasErrors: currentPositions.hasErrors || hasErrors, hasErrors: currentPositions.hasErrors || hasErrors,
performance: { performance: {
currentGrossPerformance, currentGrossPerformance,
currentGrossPerformancePercent, currentGrossPerformancePercent,
currentNetPerformance,
currentNetPerformancePercent,
currentValue: currentValue currentValue: currentValue
} }
}; };
@ -732,6 +757,8 @@ export class PortfolioService {
marketPrice: 0, marketPrice: 0,
marketState: MarketState.open, marketState: MarketState.open,
name: 'Cash', name: 'Cash',
netPerformance: 0,
netPerformancePercent: 0,
quantity: 0, quantity: 0,
sectors: [], sectors: [],
symbol: ghostfolioCashSymbol, symbol: ghostfolioCashSymbol,
@ -778,6 +805,13 @@ export class PortfolioService {
const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({ const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({
currency: order.currency, currency: order.currency,
date: format(order.date, DATE_FORMAT), date: format(order.date, DATE_FORMAT),
fee: new Big(
this.exchangeRateDataService.toCurrency(
order.fee,
order.currency,
userCurrency
)
),
name: order.SymbolProfile?.name, name: order.SymbolProfile?.name,
quantity: new Big(order.quantity), quantity: new Big(order.quantity),
symbol: order.symbol, symbol: order.symbol,

4
apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html

@ -37,7 +37,7 @@
[colorizeSign]="true" [colorizeSign]="true"
[isCurrency]="true" [isCurrency]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : performance?.currentGrossPerformance" [value]="isLoading ? undefined : performance?.currentNetPerformance"
></gf-value> ></gf-value>
</div> </div>
<div class="col"> <div class="col">
@ -46,7 +46,7 @@
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[value]=" [value]="
isLoading ? undefined : performance?.currentGrossPerformancePercent isLoading ? undefined : performance?.currentNetPerformancePercent
" "
></gf-value> ></gf-value>
</div> </div>

2
apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts

@ -52,7 +52,7 @@ export class PortfolioPerformanceComponent implements OnChanges, OnInit {
new CountUp( new CountUp(
'value', 'value',
this.performance?.currentGrossPerformancePercent * 100, this.performance?.currentNetPerformancePercent * 100,
{ {
decimalPlaces: 2, decimalPlaces: 2,
duration: 0.75, duration: 0.75,

63
apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html

@ -9,23 +9,6 @@
<div class="row"> <div class="row">
<div class="col"><hr /></div> <div class="col"><hr /></div>
</div> </div>
<div class="row px-3">
<div class="d-flex flex-grow-1" i18n>
Fees for {{ summary?.ordersCount }} {summary?.ordersCount, plural, =1
{order} other {orders}}
</div>
<div class="d-flex justify-content-end">
<gf-value
class="justify-content-end"
[currency]="baseCurrency"
[locale]="locale"
[value]="isLoading ? undefined : summary?.fees"
></gf-value>
</div>
</div>
<div class="row">
<div class="col"><hr /></div>
</div>
<div class="row px-3 py-1"> <div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Buy</div> <div class="d-flex flex-grow-1" i18n>Buy</div>
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
@ -66,7 +49,7 @@
</div> </div>
</div> </div>
<div class="row px-3 py-1"> <div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Absolute Performance</div> <div class="d-flex flex-grow-1" i18n>Absolute Gross Performance</div>
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
@ -77,7 +60,7 @@
</div> </div>
</div> </div>
<div class="row px-3 py-1"> <div class="row px-3 py-1">
<div class="d-flex flex-grow-1 ml-3" i18n>Performance (TWR)</div> <div class="d-flex flex-grow-1 ml-3" i18n>Gross Performance (TWR)</div>
<div class="d-flex flex-column flex-wrap justify-content-end"> <div class="d-flex flex-column flex-wrap justify-content-end">
<gf-value <gf-value
class="justify-content-end" class="justify-content-end"
@ -91,6 +74,48 @@
></gf-value> ></gf-value>
</div> </div>
</div> </div>
<div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>
Fees for {{ summary?.ordersCount }} {summary?.ordersCount, plural, =1
{order} other {orders}}
</div>
<div class="d-flex justify-content-end">
<span *ngIf="summary?.fees || summary?.fees === 0" class="mr-1">-</span>
<gf-value
class="justify-content-end"
[currency]="baseCurrency"
[locale]="locale"
[value]="isLoading ? undefined : summary?.fees"
></gf-value>
</div>
</div>
<div class="row">
<div class="col"><hr /></div>
</div>
<div class="row px-3 py-1">
<div class="d-flex flex-grow-1" i18n>Absolute Net Performance</div>
<div class="d-flex justify-content-end">
<gf-value
class="justify-content-end"
[currency]="baseCurrency"
[locale]="locale"
[value]="isLoading ? undefined : summary?.currentNetPerformance"
></gf-value>
</div>
</div>
<div class="row px-3 py-1">
<div class="d-flex flex-grow-1 ml-3" i18n>Net Performance (TWR)</div>
<div class="d-flex flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end"
position="end"
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
[value]="isLoading ? undefined : summary?.currentNetPerformancePercent"
></gf-value>
</div>
</div>
<div class="row"> <div class="row">
<div class="col"><hr /></div> <div class="col"><hr /></div>
</div> </div>

6
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts

@ -34,6 +34,8 @@ export class PositionDetailDialog implements OnDestroy {
public marketPrice: number; public marketPrice: number;
public maxPrice: number; public maxPrice: number;
public minPrice: number; public minPrice: number;
public netPerformance: number;
public netPerformancePercent: number;
public quantity: number; public quantity: number;
public transactionCount: number; public transactionCount: number;
@ -60,6 +62,8 @@ export class PositionDetailDialog implements OnDestroy {
marketPrice, marketPrice,
maxPrice, maxPrice,
minPrice, minPrice,
netPerformance,
netPerformancePercent,
quantity, quantity,
transactionCount transactionCount
}) => { }) => {
@ -86,6 +90,8 @@ export class PositionDetailDialog implements OnDestroy {
this.marketPrice = marketPrice; this.marketPrice = marketPrice;
this.maxPrice = maxPrice; this.maxPrice = maxPrice;
this.minPrice = minPrice; this.minPrice = minPrice;
this.netPerformance = netPerformance;
this.netPerformancePercent = netPerformancePercent;
this.quantity = quantity; this.quantity = quantity;
this.transactionCount = transactionCount; this.transactionCount = transactionCount;

4
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html

@ -25,7 +25,7 @@
[colorizeSign]="true" [colorizeSign]="true"
[currency]="data.baseCurrency" [currency]="data.baseCurrency"
[locale]="data.locale" [locale]="data.locale"
[value]="grossPerformance" [value]="netPerformance"
></gf-value> ></gf-value>
</div> </div>
<div class="col-6 mb-3"> <div class="col-6 mb-3">
@ -35,7 +35,7 @@
[colorizeSign]="true" [colorizeSign]="true"
[isPercent]="true" [isPercent]="true"
[locale]="data.locale" [locale]="data.locale"
[value]="grossPerformancePercent" [value]="netPerformancePercent"
></gf-value> ></gf-value>
</div> </div>
<div class="col-6 mb-3"> <div class="col-6 mb-3">

6
apps/client/src/app/components/position/position.component.html

@ -11,7 +11,7 @@
[isLoading]="isLoading" [isLoading]="isLoading"
[marketState]="position?.marketState" [marketState]="position?.marketState"
[range]="range" [range]="range"
[value]="position?.grossPerformancePercentage" [value]="position?.netPerformancePercentage"
></gf-trend-indicator> ></gf-trend-indicator>
</div> </div>
<div *ngIf="isLoading" class="flex-grow-1"> <div *ngIf="isLoading" class="flex-grow-1">
@ -47,13 +47,13 @@
[colorizeSign]="true" [colorizeSign]="true"
[currency]="baseCurrency" [currency]="baseCurrency"
[locale]="locale" [locale]="locale"
[value]="position?.grossPerformance" [value]="position?.netPerformance"
></gf-value> ></gf-value>
<gf-value <gf-value
[colorizeSign]="true" [colorizeSign]="true"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[value]="position?.grossPerformancePercentage" [value]="position?.netPerformancePercentage"
></gf-value> ></gf-value>
</div> </div>
</div> </div>

2
apps/client/src/app/components/positions-table/positions-table.component.html

@ -30,7 +30,7 @@
[colorizeSign]="true" [colorizeSign]="true"
[isPercent]="true" [isPercent]="true"
[locale]="locale" [locale]="locale"
[value]="isLoading ? undefined : element.grossPerformancePercent" [value]="isLoading ? undefined : element.netPerformancePercent"
></gf-value> ></gf-value>
</div> </div>
</td> </td>

2
libs/common/src/lib/interfaces/portfolio-performance.interface.ts

@ -1,5 +1,7 @@
export interface PortfolioPerformance { export interface PortfolioPerformance {
currentGrossPerformance: number; currentGrossPerformance: number;
currentGrossPerformancePercent: number; currentGrossPerformancePercent: number;
currentNetPerformance: number;
currentNetPerformancePercent: number;
currentValue: number; currentValue: number;
} }

2
libs/common/src/lib/interfaces/portfolio-position.interface.ts

@ -20,6 +20,8 @@ export interface PortfolioPosition {
marketPrice: number; marketPrice: number;
marketState: MarketState; marketState: MarketState;
name: string; name: string;
netPerformance: number;
netPerformancePercent: number;
quantity: number; quantity: number;
sectors: Sector[]; sectors: Sector[];
transactionCount: number; transactionCount: number;

2
libs/common/src/lib/interfaces/position.interface.ts

@ -13,6 +13,8 @@ export interface Position {
marketPrice?: number; marketPrice?: number;
marketState?: MarketState; marketState?: MarketState;
name?: string; name?: string;
netPerformance?: number;
netPerformancePercentage?: number;
quantity: number; quantity: number;
symbol: string; symbol: string;
transactionCount: number; transactionCount: number;

2
libs/common/src/lib/interfaces/timeline-position.interface.ts

@ -9,6 +9,8 @@ export interface TimelinePosition {
grossPerformancePercentage: Big; grossPerformancePercentage: Big;
investment: Big; investment: Big;
marketPrice: number; marketPrice: number;
netPerformance: Big;
netPerformancePercentage: Big;
quantity: Big; quantity: Big;
symbol: string; symbol: string;
transactionCount: number; transactionCount: number;

Loading…
Cancel
Save