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