parent
1050bfa098
commit
3ec4a73b35
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Changed
|
||||
|
||||
- Improved the symbol conversion for _Yahoo Finance_: Support for _Solana USD_ (`SOL1-USD`)
|
||||
- Improved the tooltips of the allocations page
|
||||
- Upgraded `envalid` from version `7.1.0` to `7.2.1`
|
||||
|
||||
## 1.57.0 - 29.09.2021
|
||||
|
@ -18,6 +18,7 @@ import svgMap from 'svgmap';
|
||||
export class WorldMapChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
@Input() baseCurrency: string;
|
||||
@Input() countries: { [code: string]: { name: string; value: number } };
|
||||
@Input() isInPercent = false;
|
||||
|
||||
public isLoading = true;
|
||||
public svgMapElement;
|
||||
@ -41,6 +42,20 @@ export class WorldMapChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
private initialize() {
|
||||
if (this.isInPercent) {
|
||||
// Convert value of countries to percentage
|
||||
let sum = 0;
|
||||
Object.keys(this.countries).map((country) => {
|
||||
sum += this.countries[country].value;
|
||||
});
|
||||
|
||||
Object.keys(this.countries).map((country) => {
|
||||
this.countries[country].value = Number(
|
||||
((this.countries[country].value * 100) / sum).toFixed(2)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
this.svgMapElement = new svgMap({
|
||||
colorMax: '#22bdb9',
|
||||
colorMin: '#c3f1f0',
|
||||
@ -49,7 +64,7 @@ export class WorldMapChartComponent implements OnChanges, OnDestroy, OnInit {
|
||||
applyData: 'value',
|
||||
data: {
|
||||
value: {
|
||||
format: `{0} ${this.baseCurrency}`
|
||||
format: this.isInPercent ? `{0}%` : `{0} ${this.baseCurrency}`
|
||||
}
|
||||
},
|
||||
values: this.countries
|
||||
|
@ -37,13 +37,23 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
||||
{ label: 'Current', value: 'current' }
|
||||
];
|
||||
public portfolioDetails: PortfolioDetails;
|
||||
public positions: { [symbol: string]: any };
|
||||
public positions: {
|
||||
[symbol: string]: Pick<
|
||||
PortfolioPosition,
|
||||
| 'assetClass'
|
||||
| 'assetSubClass'
|
||||
| 'currency'
|
||||
| 'exchange'
|
||||
| 'name'
|
||||
| 'value'
|
||||
>;
|
||||
};
|
||||
public positionsArray: PortfolioPosition[];
|
||||
public sectors: {
|
||||
[name: string]: { name: string; value: number };
|
||||
};
|
||||
public symbols: {
|
||||
[name: string]: { name: string; value: number };
|
||||
[name: string]: { name: string; symbol: string; value: number };
|
||||
};
|
||||
|
||||
public user: User;
|
||||
@ -121,6 +131,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
||||
this.symbols = {
|
||||
[UNKNOWN_KEY]: {
|
||||
name: UNKNOWN_KEY,
|
||||
symbol: UNKNOWN_KEY,
|
||||
value: 0
|
||||
}
|
||||
};
|
||||
@ -137,15 +148,29 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
||||
for (const [symbol, position] of Object.entries(
|
||||
this.portfolioDetails.holdings
|
||||
)) {
|
||||
let value = 0;
|
||||
|
||||
if (aPeriod === 'original') {
|
||||
if (this.hasImpersonationId) {
|
||||
value = position.allocationInvestment;
|
||||
} else {
|
||||
value = position.investment;
|
||||
}
|
||||
} else {
|
||||
if (this.hasImpersonationId) {
|
||||
value = position.allocationCurrent;
|
||||
} else {
|
||||
value = position.value;
|
||||
}
|
||||
}
|
||||
|
||||
this.positions[symbol] = {
|
||||
value,
|
||||
assetClass: position.assetClass,
|
||||
assetSubClass: position.assetSubClass,
|
||||
currency: position.currency,
|
||||
exchange: position.exchange,
|
||||
value:
|
||||
aPeriod === 'original'
|
||||
? position.allocationInvestment
|
||||
: position.allocationCurrent
|
||||
name: position.name
|
||||
};
|
||||
this.positionsArray.push(position);
|
||||
|
||||
@ -221,7 +246,8 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
|
||||
|
||||
if (position.assetClass === AssetClass.EQUITY) {
|
||||
this.symbols[symbol] = {
|
||||
name: symbol,
|
||||
symbol,
|
||||
name: position.name,
|
||||
value: aPeriod === 'original' ? position.investment : position.value
|
||||
};
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
<mat-card-content>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isInPercent]="hasImpersonationId"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[keys]="['name']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[positions]="accounts"
|
||||
@ -43,7 +43,7 @@
|
||||
<mat-card-content>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isInPercent]="true"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[keys]="['assetClass', 'assetSubClass']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[positions]="positions"
|
||||
@ -67,7 +67,7 @@
|
||||
<mat-card-content>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isInPercent]="true"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[keys]="['currency']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[positions]="positions"
|
||||
@ -90,8 +90,8 @@
|
||||
<gf-portfolio-proportion-chart
|
||||
class="mx-auto"
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isInPercent]="false"
|
||||
[keys]="['name']"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[keys]="['symbol']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[positions]="symbols"
|
||||
[showLabels]="deviceType !== 'mobile'"
|
||||
@ -113,7 +113,7 @@
|
||||
<mat-card-content>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isInPercent]="false"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[keys]="['name']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[maxItems]="10"
|
||||
@ -138,7 +138,7 @@
|
||||
<mat-card-content>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isInPercent]="false"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[keys]="['name']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[positions]="continents"
|
||||
@ -161,7 +161,7 @@
|
||||
<gf-portfolio-proportion-chart
|
||||
[keys]="['name']"
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[isInPercent]="false"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[locale]="user?.settings?.locale"
|
||||
[maxItems]="10"
|
||||
[positions]="countries"
|
||||
@ -186,6 +186,7 @@
|
||||
<gf-world-map-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[countries]="countries"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
></gf-world-map-chart>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
@ -35,7 +35,10 @@ export class PortfolioProportionChartComponent
|
||||
@Input() maxItems?: number;
|
||||
@Input() showLabels = false;
|
||||
@Input() positions: {
|
||||
[symbol: string]: Pick<PortfolioPosition, 'type'> & { value: number };
|
||||
[symbol: string]: Pick<PortfolioPosition, 'type'> & {
|
||||
name: string;
|
||||
value: number;
|
||||
};
|
||||
} = {};
|
||||
|
||||
@ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>;
|
||||
@ -80,6 +83,7 @@ export class PortfolioProportionChartComponent
|
||||
const chartData: {
|
||||
[symbol: string]: {
|
||||
color?: string;
|
||||
name: string;
|
||||
subCategory: { [symbol: string]: { value: number } };
|
||||
value: number;
|
||||
};
|
||||
@ -106,6 +110,7 @@ export class PortfolioProportionChartComponent
|
||||
}
|
||||
} else {
|
||||
chartData[this.positions[symbol][this.keys[0]]] = {
|
||||
name: this.positions[symbol].name,
|
||||
subCategory: {},
|
||||
value: this.positions[symbol].value
|
||||
};
|
||||
@ -123,6 +128,7 @@ export class PortfolioProportionChartComponent
|
||||
chartData[UNKNOWN_KEY].value += this.positions[symbol].value;
|
||||
} else {
|
||||
chartData[UNKNOWN_KEY] = {
|
||||
name: this.positions[symbol].name,
|
||||
subCategory: this.keys[1]
|
||||
? { [this.keys[1]]: { value: 0 } }
|
||||
: undefined,
|
||||
@ -152,7 +158,7 @@ export class PortfolioProportionChartComponent
|
||||
if (!unknownItem) {
|
||||
const index = chartDataSorted.push([
|
||||
UNKNOWN_KEY,
|
||||
{ subCategory: {}, value: 0 }
|
||||
{ name: UNKNOWN_KEY, subCategory: {}, value: 0 }
|
||||
]);
|
||||
unknownItem = chartDataSorted[index];
|
||||
}
|
||||
@ -160,6 +166,7 @@ export class PortfolioProportionChartComponent
|
||||
rest.forEach((restItem) => {
|
||||
if (unknownItem?.[1]) {
|
||||
unknownItem[1] = {
|
||||
name: UNKNOWN_KEY,
|
||||
subCategory: {},
|
||||
value: unknownItem[1].value + restItem[1].value
|
||||
};
|
||||
@ -278,17 +285,29 @@ export class PortfolioProportionChartComponent
|
||||
const labelIndex =
|
||||
(data.datasets[context.datasetIndex - 1]?.data?.length ??
|
||||
0) + context.dataIndex;
|
||||
const label = context.chart.data.labels?.[labelIndex] ?? '';
|
||||
const symbol =
|
||||
context.chart.data.labels?.[labelIndex] ?? '';
|
||||
|
||||
const name = this.positions[<string>symbol]?.name;
|
||||
|
||||
let sum = 0;
|
||||
context.dataset.data.map((item) => {
|
||||
sum += item;
|
||||
});
|
||||
|
||||
const percentage = (context.parsed * 100) / sum;
|
||||
|
||||
if (this.isInPercent) {
|
||||
const value = 100 * <number>context.raw;
|
||||
return `${label} (${value.toFixed(2)}%)`;
|
||||
return `${name ?? symbol} (${percentage.toFixed(2)}%)`;
|
||||
} else {
|
||||
const value = <number>context.raw;
|
||||
return `${label} (${value.toLocaleString(this.locale, {
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2
|
||||
})} ${this.baseCurrency})`;
|
||||
return `${name ?? symbol}: ${value.toLocaleString(
|
||||
this.locale,
|
||||
{
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2
|
||||
}
|
||||
)} ${this.baseCurrency} (${percentage.toFixed(2)}%)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user