|
@ -45,6 +45,7 @@ import type { |
|
|
AccountWithValue, |
|
|
AccountWithValue, |
|
|
DateRange, |
|
|
DateRange, |
|
|
GroupBy, |
|
|
GroupBy, |
|
|
|
|
|
Market, |
|
|
RequestWithUser, |
|
|
RequestWithUser, |
|
|
UserWithSettings |
|
|
UserWithSettings |
|
|
} from '@ghostfolio/common/types'; |
|
|
} from '@ghostfolio/common/types'; |
|
@ -73,7 +74,7 @@ import { |
|
|
parseISO, |
|
|
parseISO, |
|
|
set |
|
|
set |
|
|
} from 'date-fns'; |
|
|
} from 'date-fns'; |
|
|
import { isEmpty, last, uniq } from 'lodash'; |
|
|
import { isEmpty, isNumber, last, uniq } from 'lodash'; |
|
|
|
|
|
|
|
|
import { PortfolioCalculator } from './calculator/portfolio-calculator'; |
|
|
import { PortfolioCalculator } from './calculator/portfolio-calculator'; |
|
|
import { |
|
|
import { |
|
@ -581,6 +582,17 @@ export class PortfolioService { |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let markets: { |
|
|
|
|
|
[key in Market]: { |
|
|
|
|
|
name: string; |
|
|
|
|
|
value: number; |
|
|
|
|
|
}; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if (withMarkets) { |
|
|
|
|
|
markets = this.getAggregatedMarkets(holdings); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
let summary: PortfolioSummary; |
|
|
let summary: PortfolioSummary; |
|
|
|
|
|
|
|
|
if (withSummary) { |
|
|
if (withSummary) { |
|
@ -602,6 +614,7 @@ export class PortfolioService { |
|
|
accounts, |
|
|
accounts, |
|
|
hasErrors, |
|
|
hasErrors, |
|
|
holdings, |
|
|
holdings, |
|
|
|
|
|
markets, |
|
|
platforms, |
|
|
platforms, |
|
|
summary |
|
|
summary |
|
|
}; |
|
|
}; |
|
@ -1232,6 +1245,62 @@ export class PortfolioService { |
|
|
await this.orderService.assignTags({ dataSource, symbol, tags, userId }); |
|
|
await this.orderService.assignTags({ dataSource, symbol, tags, userId }); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private getAggregatedMarkets(holdings: { |
|
|
|
|
|
[symbol: string]: PortfolioPosition; |
|
|
|
|
|
}): { |
|
|
|
|
|
[key in Market]: { name: string; value: number }; |
|
|
|
|
|
} { |
|
|
|
|
|
const markets = { |
|
|
|
|
|
[UNKNOWN_KEY]: { |
|
|
|
|
|
name: UNKNOWN_KEY, |
|
|
|
|
|
value: 0 |
|
|
|
|
|
}, |
|
|
|
|
|
developedMarkets: { |
|
|
|
|
|
name: 'developedMarkets', |
|
|
|
|
|
value: 0 |
|
|
|
|
|
}, |
|
|
|
|
|
emergingMarkets: { |
|
|
|
|
|
name: 'emergingMarkets', |
|
|
|
|
|
value: 0 |
|
|
|
|
|
}, |
|
|
|
|
|
otherMarkets: { |
|
|
|
|
|
name: 'otherMarkets', |
|
|
|
|
|
value: 0 |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
for (const [symbol, position] of Object.entries(holdings)) { |
|
|
|
|
|
const value = position.valueInBaseCurrency; |
|
|
|
|
|
|
|
|
|
|
|
if (position.assetClass !== AssetClass.LIQUIDITY) { |
|
|
|
|
|
if (position.countries.length > 0) { |
|
|
|
|
|
markets.developedMarkets.value += |
|
|
|
|
|
position.markets.developedMarkets * value; |
|
|
|
|
|
markets.emergingMarkets.value += |
|
|
|
|
|
position.markets.emergingMarkets * value; |
|
|
|
|
|
markets.otherMarkets.value += position.markets.otherMarkets * value; |
|
|
|
|
|
} else { |
|
|
|
|
|
markets[UNKNOWN_KEY].value += value; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const marketsTotal = |
|
|
|
|
|
markets.developedMarkets.value + |
|
|
|
|
|
markets.emergingMarkets.value + |
|
|
|
|
|
markets.otherMarkets.value + |
|
|
|
|
|
markets[UNKNOWN_KEY].value; |
|
|
|
|
|
|
|
|
|
|
|
markets.developedMarkets.value = |
|
|
|
|
|
markets.developedMarkets.value / marketsTotal; |
|
|
|
|
|
markets.emergingMarkets.value = |
|
|
|
|
|
markets.emergingMarkets.value / marketsTotal; |
|
|
|
|
|
markets.otherMarkets.value = markets.otherMarkets.value / marketsTotal; |
|
|
|
|
|
markets[UNKNOWN_KEY].value = markets[UNKNOWN_KEY].value / marketsTotal; |
|
|
|
|
|
|
|
|
|
|
|
return markets; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private async getCashPositions({ |
|
|
private async getCashPositions({ |
|
|
cashDetails, |
|
|
cashDetails, |
|
|
userCurrency, |
|
|
userCurrency, |
|
|