@ -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 calculateOverallGross Performance (
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 ) ;
}
}