Feature/add developed vs emerging markets calculation (#767)
* Add allocations by market * Update changelog
This commit is contained in:
parent
2b4319454d
commit
b4848be914
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added the calculation for developed vs. emerging markets to the allocations page
|
||||||
- Added a hover effect to the page tabs
|
- Added a hover effect to the page tabs
|
||||||
- Extended the feature overview page by _Bonds_ and _Emergency Fund_
|
- Extended the feature overview page by _Bonds_ and _Emergency Fund_
|
||||||
|
|
||||||
|
@ -13,8 +13,9 @@ export class PortfolioServiceStrategy {
|
|||||||
@Inject(REQUEST) private readonly request: RequestWithUser
|
@Inject(REQUEST) private readonly request: RequestWithUser
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public get() {
|
public get(newCalculationEngine?: boolean) {
|
||||||
if (
|
if (
|
||||||
|
newCalculationEngine ||
|
||||||
this.request.user?.Settings?.settings?.['isNewCalculationEngine'] === true
|
this.request.user?.Settings?.settings?.['isNewCalculationEngine'] === true
|
||||||
) {
|
) {
|
||||||
return this.portfolioServiceNew;
|
return this.portfolioServiceNew;
|
||||||
|
@ -120,7 +120,7 @@ export class PortfolioController {
|
|||||||
|
|
||||||
const { accounts, holdings, hasErrors } =
|
const { accounts, holdings, hasErrors } =
|
||||||
await this.portfolioServiceStrategy
|
await this.portfolioServiceStrategy
|
||||||
.get()
|
.get(true)
|
||||||
.getDetails(impersonationId, this.request.user.id, range);
|
.getDetails(impersonationId, this.request.user.id, range);
|
||||||
|
|
||||||
if (hasErrors || hasNotDefinedValuesInObject(holdings)) {
|
if (hasErrors || hasNotDefinedValuesInObject(holdings)) {
|
||||||
@ -277,7 +277,7 @@ export class PortfolioController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { holdings } = await this.portfolioServiceStrategy
|
const { holdings } = await this.portfolioServiceStrategy
|
||||||
.get()
|
.get(true)
|
||||||
.getDetails(access.userId, access.userId);
|
.getDetails(access.userId, access.userId);
|
||||||
|
|
||||||
const portfolioPublicDetails: PortfolioPublicDetails = {
|
const portfolioPublicDetails: PortfolioPublicDetails = {
|
||||||
@ -304,6 +304,7 @@ export class PortfolioController {
|
|||||||
allocationCurrent: portfolioPosition.allocationCurrent,
|
allocationCurrent: portfolioPosition.allocationCurrent,
|
||||||
countries: hasDetails ? portfolioPosition.countries : [],
|
countries: hasDetails ? portfolioPosition.countries : [],
|
||||||
currency: portfolioPosition.currency,
|
currency: portfolioPosition.currency,
|
||||||
|
markets: portfolioPosition.markets,
|
||||||
name: portfolioPosition.name,
|
name: portfolioPosition.name,
|
||||||
sectors: hasDetails ? portfolioPosition.sectors : [],
|
sectors: hasDetails ? portfolioPosition.sectors : [],
|
||||||
value: portfolioPosition.value / totalValue
|
value: portfolioPosition.value / totalValue
|
||||||
|
@ -40,6 +40,7 @@ import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.in
|
|||||||
import type {
|
import type {
|
||||||
AccountWithValue,
|
AccountWithValue,
|
||||||
DateRange,
|
DateRange,
|
||||||
|
Market,
|
||||||
OrderWithAccount,
|
OrderWithAccount,
|
||||||
RequestWithUser
|
RequestWithUser
|
||||||
} from '@ghostfolio/common/types';
|
} from '@ghostfolio/common/types';
|
||||||
@ -71,6 +72,9 @@ import {
|
|||||||
import { PortfolioCalculatorNew } from './portfolio-calculator-new';
|
import { PortfolioCalculatorNew } from './portfolio-calculator-new';
|
||||||
import { RulesService } from './rules.service';
|
import { RulesService } from './rules.service';
|
||||||
|
|
||||||
|
const developedMarkets = require('../../assets/countries/developed-markets.json');
|
||||||
|
const emergingMarkets = require('../../assets/countries/emerging-markets.json');
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PortfolioServiceNew {
|
export class PortfolioServiceNew {
|
||||||
public constructor(
|
public constructor(
|
||||||
@ -380,7 +384,31 @@ export class PortfolioServiceNew {
|
|||||||
const value = item.quantity.mul(item.marketPrice);
|
const value = item.quantity.mul(item.marketPrice);
|
||||||
const symbolProfile = symbolProfileMap[item.symbol];
|
const symbolProfile = symbolProfileMap[item.symbol];
|
||||||
const dataProviderResponse = dataProviderResponses[item.symbol];
|
const dataProviderResponse = dataProviderResponses[item.symbol];
|
||||||
|
|
||||||
|
const markets: { [key in Market]: number } = {
|
||||||
|
developedMarkets: 0,
|
||||||
|
emergingMarkets: 0,
|
||||||
|
otherMarkets: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const country of symbolProfile.countries) {
|
||||||
|
if (developedMarkets.includes(country.code)) {
|
||||||
|
markets.developedMarkets = new Big(markets.developedMarkets)
|
||||||
|
.plus(country.weight)
|
||||||
|
.toNumber();
|
||||||
|
} else if (emergingMarkets.includes(country.code)) {
|
||||||
|
markets.emergingMarkets = new Big(markets.emergingMarkets)
|
||||||
|
.plus(country.weight)
|
||||||
|
.toNumber();
|
||||||
|
} else {
|
||||||
|
markets.otherMarkets = new Big(markets.otherMarkets)
|
||||||
|
.plus(country.weight)
|
||||||
|
.toNumber();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
holdings[item.symbol] = {
|
holdings[item.symbol] = {
|
||||||
|
markets,
|
||||||
allocationCurrent: value.div(totalValue).toNumber(),
|
allocationCurrent: value.div(totalValue).toNumber(),
|
||||||
allocationInvestment: item.investment.div(totalInvestment).toNumber(),
|
allocationInvestment: item.investment.div(totalInvestment).toNumber(),
|
||||||
assetClass: symbolProfile.assetClass,
|
assetClass: symbolProfile.assetClass,
|
||||||
|
26
apps/api/src/assets/countries/developed-markets.json
Normal file
26
apps/api/src/assets/countries/developed-markets.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[
|
||||||
|
"AT",
|
||||||
|
"AU",
|
||||||
|
"BE",
|
||||||
|
"CA",
|
||||||
|
"CH",
|
||||||
|
"DE",
|
||||||
|
"DK",
|
||||||
|
"ES",
|
||||||
|
"FI",
|
||||||
|
"FR",
|
||||||
|
"GB",
|
||||||
|
"HK",
|
||||||
|
"IE",
|
||||||
|
"IL",
|
||||||
|
"IT",
|
||||||
|
"JP",
|
||||||
|
"LU",
|
||||||
|
"NL",
|
||||||
|
"NO",
|
||||||
|
"NZ",
|
||||||
|
"PT",
|
||||||
|
"SE",
|
||||||
|
"SG",
|
||||||
|
"US"
|
||||||
|
]
|
28
apps/api/src/assets/countries/emerging-markets.json
Normal file
28
apps/api/src/assets/countries/emerging-markets.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[
|
||||||
|
"AE",
|
||||||
|
"BR",
|
||||||
|
"CL",
|
||||||
|
"CN",
|
||||||
|
"CO",
|
||||||
|
"CY",
|
||||||
|
"CZ",
|
||||||
|
"EG",
|
||||||
|
"GR",
|
||||||
|
"HK",
|
||||||
|
"HU",
|
||||||
|
"ID",
|
||||||
|
"IN",
|
||||||
|
"KR",
|
||||||
|
"KW",
|
||||||
|
"MX",
|
||||||
|
"MY",
|
||||||
|
"PE",
|
||||||
|
"PH",
|
||||||
|
"PL",
|
||||||
|
"QA",
|
||||||
|
"SA",
|
||||||
|
"TH",
|
||||||
|
"TR",
|
||||||
|
"TW",
|
||||||
|
"ZA"
|
||||||
|
]
|
@ -14,7 +14,7 @@ import {
|
|||||||
User
|
User
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||||
import { ToggleOption } from '@ghostfolio/common/types';
|
import { Market, ToggleOption } from '@ghostfolio/common/types';
|
||||||
import { Account, AssetClass, DataSource } from '@prisma/client';
|
import { Account, AssetClass, DataSource } from '@prisma/client';
|
||||||
import { DeviceDetectorService } from 'ngx-device-detector';
|
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||||
import { Subject, Subscription } from 'rxjs';
|
import { Subject, Subscription } from 'rxjs';
|
||||||
@ -42,6 +42,9 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
|||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
public hasImpersonationId: boolean;
|
public hasImpersonationId: boolean;
|
||||||
public hasPermissionToCreateOrder: boolean;
|
public hasPermissionToCreateOrder: boolean;
|
||||||
|
public markets: {
|
||||||
|
[key in Market]: { name: string; value: number };
|
||||||
|
};
|
||||||
public period = 'current';
|
public period = 'current';
|
||||||
public periodOptions: ToggleOption[] = [
|
public periodOptions: ToggleOption[] = [
|
||||||
{ label: 'Initial', value: 'original' },
|
{ label: 'Initial', value: 'original' },
|
||||||
@ -160,6 +163,20 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
|||||||
value: 0
|
value: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
this.markets = {
|
||||||
|
developedMarkets: {
|
||||||
|
name: 'developedMarkets',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
emergingMarkets: {
|
||||||
|
name: 'emergingMarkets',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
otherMarkets: {
|
||||||
|
name: 'otherMarkets',
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
this.positions = {};
|
this.positions = {};
|
||||||
this.positionsArray = [];
|
this.positionsArray = [];
|
||||||
this.sectors = {
|
this.sectors = {
|
||||||
@ -219,6 +236,16 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
|||||||
// Prepare analysis data by continents, countries and sectors except for cash
|
// Prepare analysis data by continents, countries and sectors except for cash
|
||||||
|
|
||||||
if (position.countries.length > 0) {
|
if (position.countries.length > 0) {
|
||||||
|
this.markets.developedMarkets.value +=
|
||||||
|
position.markets.developedMarkets *
|
||||||
|
(aPeriod === 'original' ? position.investment : position.value);
|
||||||
|
this.markets.emergingMarkets.value +=
|
||||||
|
position.markets.emergingMarkets *
|
||||||
|
(aPeriod === 'original' ? position.investment : position.value);
|
||||||
|
this.markets.otherMarkets.value +=
|
||||||
|
position.markets.otherMarkets *
|
||||||
|
(aPeriod === 'original' ? position.investment : position.value);
|
||||||
|
|
||||||
for (const country of position.countries) {
|
for (const country of position.countries) {
|
||||||
const { code, continent, name, weight } = country;
|
const { code, continent, name, weight } = country;
|
||||||
|
|
||||||
@ -294,6 +321,18 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const marketsTotal =
|
||||||
|
this.markets.developedMarkets.value +
|
||||||
|
this.markets.emergingMarkets.value +
|
||||||
|
this.markets.otherMarkets.value;
|
||||||
|
|
||||||
|
this.markets.developedMarkets.value =
|
||||||
|
this.markets.developedMarkets.value / marketsTotal;
|
||||||
|
this.markets.emergingMarkets.value =
|
||||||
|
this.markets.emergingMarkets.value / marketsTotal;
|
||||||
|
this.markets.otherMarkets.value =
|
||||||
|
this.markets.otherMarkets.value / marketsTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public onChangePeriod(aValue: string) {
|
public onChangePeriod(aValue: string) {
|
||||||
|
@ -190,6 +190,32 @@
|
|||||||
[countries]="countries"
|
[countries]="countries"
|
||||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||||
></gf-world-map-chart>
|
></gf-world-map-chart>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-md-4 my-2">
|
||||||
|
<gf-value
|
||||||
|
label="Developed Markets"
|
||||||
|
size="large"
|
||||||
|
[isPercent]="true"
|
||||||
|
[value]="markets?.developedMarkets?.value"
|
||||||
|
></gf-value>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-12 col-md-4 my-2">
|
||||||
|
<gf-value
|
||||||
|
label="Emerging Markets"
|
||||||
|
size="large"
|
||||||
|
[isPercent]="true"
|
||||||
|
[value]="markets?.emergingMarkets?.value"
|
||||||
|
></gf-value>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-12 col-md-4 my-2">
|
||||||
|
<gf-value
|
||||||
|
label="Other Markets"
|
||||||
|
size="large"
|
||||||
|
[isPercent]="true"
|
||||||
|
[value]="markets?.otherMarkets?.value"
|
||||||
|
></gf-value>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,6 +5,7 @@ import { GfPositionsTableModule } from '@ghostfolio/client/components/positions-
|
|||||||
import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module';
|
import { GfToggleModule } from '@ghostfolio/client/components/toggle/toggle.module';
|
||||||
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
|
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
|
||||||
import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module';
|
import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module';
|
||||||
|
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||||
|
|
||||||
import { AllocationsPageRoutingModule } from './allocations-page-routing.module';
|
import { AllocationsPageRoutingModule } from './allocations-page-routing.module';
|
||||||
import { AllocationsPageComponent } from './allocations-page.component';
|
import { AllocationsPageComponent } from './allocations-page.component';
|
||||||
@ -19,6 +20,7 @@ import { AllocationsPageComponent } from './allocations-page.component';
|
|||||||
GfPositionsTableModule,
|
GfPositionsTableModule,
|
||||||
GfToggleModule,
|
GfToggleModule,
|
||||||
GfWorldMapChartModule,
|
GfWorldMapChartModule,
|
||||||
|
GfValueModule,
|
||||||
MatCardModule
|
MatCardModule
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
PortfolioPosition,
|
PortfolioPosition,
|
||||||
PortfolioPublicDetails
|
PortfolioPublicDetails
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
|
import { Market } from '@ghostfolio/common/types';
|
||||||
import { StatusCodes } from 'http-status-codes';
|
import { StatusCodes } from 'http-status-codes';
|
||||||
import { DeviceDetectorService } from 'ngx-device-detector';
|
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||||
import { EMPTY, Subject } from 'rxjs';
|
import { EMPTY, Subject } from 'rxjs';
|
||||||
@ -26,6 +27,9 @@ export class PublicPageComponent implements OnInit {
|
|||||||
[code: string]: { name: string; value: number };
|
[code: string]: { name: string; value: number };
|
||||||
};
|
};
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
|
public markets: {
|
||||||
|
[key in Market]: { name: string; value: number };
|
||||||
|
};
|
||||||
public portfolioPublicDetails: PortfolioPublicDetails;
|
public portfolioPublicDetails: PortfolioPublicDetails;
|
||||||
public positions: {
|
public positions: {
|
||||||
[symbol: string]: Pick<PortfolioPosition, 'currency' | 'name' | 'value'>;
|
[symbol: string]: Pick<PortfolioPosition, 'currency' | 'name' | 'value'>;
|
||||||
@ -96,6 +100,20 @@ export class PublicPageComponent implements OnInit {
|
|||||||
value: 0
|
value: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
this.markets = {
|
||||||
|
developedMarkets: {
|
||||||
|
name: 'developedMarkets',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
emergingMarkets: {
|
||||||
|
name: 'emergingMarkets',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
otherMarkets: {
|
||||||
|
name: 'otherMarkets',
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
this.positions = {};
|
this.positions = {};
|
||||||
this.sectors = {
|
this.sectors = {
|
||||||
[UNKNOWN_KEY]: {
|
[UNKNOWN_KEY]: {
|
||||||
@ -123,6 +141,13 @@ export class PublicPageComponent implements OnInit {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (position.countries.length > 0) {
|
if (position.countries.length > 0) {
|
||||||
|
this.markets.developedMarkets.value +=
|
||||||
|
position.markets.developedMarkets * position.value;
|
||||||
|
this.markets.emergingMarkets.value +=
|
||||||
|
position.markets.emergingMarkets * position.value;
|
||||||
|
this.markets.otherMarkets.value +=
|
||||||
|
position.markets.otherMarkets * position.value;
|
||||||
|
|
||||||
for (const country of position.countries) {
|
for (const country of position.countries) {
|
||||||
const { code, continent, name, weight } = country;
|
const { code, continent, name, weight } = country;
|
||||||
|
|
||||||
@ -176,6 +201,18 @@ export class PublicPageComponent implements OnInit {
|
|||||||
value: position.value
|
value: position.value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const marketsTotal =
|
||||||
|
this.markets.developedMarkets.value +
|
||||||
|
this.markets.emergingMarkets.value +
|
||||||
|
this.markets.otherMarkets.value;
|
||||||
|
|
||||||
|
this.markets.developedMarkets.value =
|
||||||
|
this.markets.developedMarkets.value / marketsTotal;
|
||||||
|
this.markets.emergingMarkets.value =
|
||||||
|
this.markets.emergingMarkets.value / marketsTotal;
|
||||||
|
this.markets.otherMarkets.value =
|
||||||
|
this.markets.otherMarkets.value / marketsTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
|
@ -79,12 +79,38 @@
|
|||||||
[countries]="countries"
|
[countries]="countries"
|
||||||
[isInPercent]="true"
|
[isInPercent]="true"
|
||||||
></gf-world-map-chart>
|
></gf-world-map-chart>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-md-4 my-2">
|
||||||
|
<gf-value
|
||||||
|
label="Developed Markets"
|
||||||
|
size="large"
|
||||||
|
[isPercent]="true"
|
||||||
|
[value]="markets?.developedMarkets?.value"
|
||||||
|
></gf-value>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-12 col-md-4 my-2">
|
||||||
|
<gf-value
|
||||||
|
label="Emerging Markets"
|
||||||
|
size="large"
|
||||||
|
[isPercent]="true"
|
||||||
|
[value]="markets?.emergingMarkets?.value"
|
||||||
|
></gf-value>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-12 col-md-4 my-2">
|
||||||
|
<gf-value
|
||||||
|
label="Other Markets"
|
||||||
|
size="large"
|
||||||
|
[isPercent]="true"
|
||||||
|
[value]="markets?.otherMarkets?.value"
|
||||||
|
></gf-value>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row my-5">
|
<div class="row my-5">
|
||||||
<div class="col-md-8 offset-md-2">
|
<div class="col-md-10 offset-md-1">
|
||||||
<h2 class="h4 mb-1 text-center">
|
<h2 class="h4 mb-1 text-center">
|
||||||
Would you like to <strong>refine</strong> your
|
Would you like to <strong>refine</strong> your
|
||||||
<strong>personal investment strategy</strong>?
|
<strong>personal investment strategy</strong>?
|
||||||
|
@ -4,6 +4,7 @@ import { MatButtonModule } from '@angular/material/button';
|
|||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
|
import { GfWorldMapChartModule } from '@ghostfolio/client/components/world-map-chart/world-map-chart.module';
|
||||||
import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module';
|
import { GfPortfolioProportionChartModule } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.module';
|
||||||
|
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||||
|
|
||||||
import { PublicPageRoutingModule } from './public-page-routing.module';
|
import { PublicPageRoutingModule } from './public-page-routing.module';
|
||||||
import { PublicPageComponent } from './public-page.component';
|
import { PublicPageComponent } from './public-page.component';
|
||||||
@ -14,6 +15,7 @@ import { PublicPageComponent } from './public-page.component';
|
|||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
GfPortfolioProportionChartModule,
|
GfPortfolioProportionChartModule,
|
||||||
|
GfValueModule,
|
||||||
GfWorldMapChartModule,
|
GfWorldMapChartModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
|
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
|
import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
|
||||||
|
|
||||||
|
import { Market } from '../types';
|
||||||
import { Country } from './country.interface';
|
import { Country } from './country.interface';
|
||||||
import { Sector } from './sector.interface';
|
import { Sector } from './sector.interface';
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ export interface PortfolioPosition {
|
|||||||
marketChange?: number;
|
marketChange?: number;
|
||||||
marketChangePercent?: number;
|
marketChangePercent?: number;
|
||||||
marketPrice: number;
|
marketPrice: number;
|
||||||
|
markets?: { [key in Market]: number };
|
||||||
marketState: MarketState;
|
marketState: MarketState;
|
||||||
name: string;
|
name: string;
|
||||||
netPerformance: number;
|
netPerformance: number;
|
||||||
|
@ -8,6 +8,7 @@ export interface PortfolioPublicDetails {
|
|||||||
| 'allocationCurrent'
|
| 'allocationCurrent'
|
||||||
| 'countries'
|
| 'countries'
|
||||||
| 'currency'
|
| 'currency'
|
||||||
|
| 'markets'
|
||||||
| 'name'
|
| 'name'
|
||||||
| 'sectors'
|
| 'sectors'
|
||||||
| 'value'
|
| 'value'
|
||||||
|
@ -2,6 +2,7 @@ import type { AccessWithGranteeUser } from './access-with-grantee-user.type';
|
|||||||
import { AccountWithValue } from './account-with-value.type';
|
import { AccountWithValue } from './account-with-value.type';
|
||||||
import type { DateRange } from './date-range.type';
|
import type { DateRange } from './date-range.type';
|
||||||
import type { Granularity } from './granularity.type';
|
import type { Granularity } from './granularity.type';
|
||||||
|
import { Market } from './market.type';
|
||||||
import type { OrderWithAccount } from './order-with-account.type';
|
import type { OrderWithAccount } from './order-with-account.type';
|
||||||
import type { RequestWithUser } from './request-with-user.type';
|
import type { RequestWithUser } from './request-with-user.type';
|
||||||
import { ToggleOption } from './toggle-option.type';
|
import { ToggleOption } from './toggle-option.type';
|
||||||
@ -11,6 +12,7 @@ export type {
|
|||||||
AccountWithValue,
|
AccountWithValue,
|
||||||
DateRange,
|
DateRange,
|
||||||
Granularity,
|
Granularity,
|
||||||
|
Market,
|
||||||
OrderWithAccount,
|
OrderWithAccount,
|
||||||
RequestWithUser,
|
RequestWithUser,
|
||||||
ToggleOption
|
ToggleOption
|
||||||
|
1
libs/common/src/lib/types/market.type.ts
Normal file
1
libs/common/src/lib/types/market.type.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type Market = 'developedMarkets' | 'emergingMarkets' | 'otherMarkets';
|
@ -43,9 +43,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<small *ngIf="label">
|
<ng-container *ngIf="label">
|
||||||
{{ label }}
|
<div *ngIf="size === 'large'">
|
||||||
</small>
|
{{ label }}
|
||||||
|
</div>
|
||||||
|
<small *ngIf="size !== 'large'">
|
||||||
|
{{ label }}
|
||||||
|
</small>
|
||||||
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ngx-skeleton-loader
|
<ngx-skeleton-loader
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
OnChanges
|
OnChanges
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
|
import { DEFAULT_DATE_FORMAT, locale } from '@ghostfolio/common/config';
|
||||||
import { format, isDate, parseISO } from 'date-fns';
|
import { format, isDate, parseISO } from 'date-fns';
|
||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash';
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ export class ValueComponent implements OnChanges {
|
|||||||
@Input() isCurrency = false;
|
@Input() isCurrency = false;
|
||||||
@Input() isPercent = false;
|
@Input() isPercent = false;
|
||||||
@Input() label = '';
|
@Input() label = '';
|
||||||
@Input() locale = '';
|
@Input() locale = locale;
|
||||||
@Input() position = '';
|
@Input() position = '';
|
||||||
@Input() precision: number | undefined;
|
@Input() precision: number | undefined;
|
||||||
@Input() size: 'large' | 'medium' | 'small' = 'small';
|
@Input() size: 'large' | 'medium' | 'small' = 'small';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user