Browse Source

Merge branch 'main' into feature/pastInvestments

pull/3146/head
Thomas Kaul 1 year ago
committed by GitHub
parent
commit
090802ecc7
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 25
      .env.dev
  2. 3
      .env.example
  3. 13
      CHANGELOG.md
  4. 2
      README.md
  5. 2
      apps/api/src/app/order/order.service.ts
  6. 32
      apps/api/src/app/portfolio/portfolio-calculator.ts
  7. 21
      apps/api/src/app/portfolio/portfolio.service.ts
  8. 3
      apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts
  9. 2
      apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/interfaces/interfaces.ts
  10. 4
      apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.component.ts
  11. 2
      apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.html
  12. 7
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  13. 6
      apps/client/src/app/components/admin-platform/admin-platform.component.html
  14. 6
      apps/client/src/app/components/admin-tag/admin-tag.component.html
  15. 37
      apps/client/src/app/pages/faq/overview/faq-overview-page.html
  16. 22
      apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts
  17. 9
      apps/client/src/app/services/admin.service.ts
  18. 4
      libs/common/src/lib/interfaces/symbol-metrics.interface.ts

25
.env.dev

@ -0,0 +1,25 @@
COMPOSE_PROJECT_NAME=ghostfolio-development
# CACHE
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=<INSERT_REDIS_PASSWORD>
# POSTGRES
POSTGRES_DB=ghostfolio-db
POSTGRES_USER=user
POSTGRES_PASSWORD=<INSERT_POSTGRES_PASSWORD>
# VARIOUS
ACCESS_TOKEN_SALT=<INSERT_RANDOM_STRING>
DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?connect_timeout=300&sslmode=prefer
JWT_SECRET_KEY=<INSERT_RANDOM_STRING>
# DEVELOPMENT
# Nx 18 enables using plugins to infer targets by default
# This is disabled for existing workspaces to maintain compatibility
# For more info, see: https://nx.dev/concepts/inferred-tasks
NX_ADD_PLUGINS=false
NX_NATIVE_COMMAND_RUNNER=false

3
.env.example

@ -1,4 +1,4 @@
COMPOSE_PROJECT_NAME=ghostfolio-development COMPOSE_PROJECT_NAME=ghostfolio
# CACHE # CACHE
REDIS_HOST=localhost REDIS_HOST=localhost
@ -10,6 +10,7 @@ POSTGRES_DB=ghostfolio-db
POSTGRES_USER=user POSTGRES_USER=user
POSTGRES_PASSWORD=<INSERT_POSTGRES_PASSWORD> POSTGRES_PASSWORD=<INSERT_POSTGRES_PASSWORD>
# VARIOUS
ACCESS_TOKEN_SALT=<INSERT_RANDOM_STRING> ACCESS_TOKEN_SALT=<INSERT_RANDOM_STRING>
DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?connect_timeout=300&sslmode=prefer DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?connect_timeout=300&sslmode=prefer
JWT_SECRET_KEY=<INSERT_RANDOM_STRING> JWT_SECRET_KEY=<INSERT_RANDOM_STRING>

13
CHANGELOG.md

@ -10,6 +10,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Added a toggle to switch between active and closed holdings on the portfolio holdings page - Added a toggle to switch between active and closed holdings on the portfolio holdings page
- Added support to update the cash balance of an account when adding a fee activity
- Added support to update the cash balance of an account when adding an interest activity
- Extended the content of the _General_ section by the product roadmap on the Frequently Asked Questions (FAQ) page
### Changed
- Improved the usability of the platform management in the admin control panel
- Improved the usability of the tag management in the admin control panel
### Fixed
- Fixed an issue in the dividend calculation of the portfolio holdings
- Fixed the date conversion of the import of historical market data in the admin control panel
## 2.63.2 - 2024-03-12 ## 2.63.2 - 2024-03-12

2
README.md

@ -154,7 +154,7 @@ Ghostfolio is available for various home server systems, including [CasaOS](http
- [Node.js](https://nodejs.org/en/download) (version 18+) - [Node.js](https://nodejs.org/en/download) (version 18+)
- [Yarn](https://yarnpkg.com/en/docs/install) - [Yarn](https://yarnpkg.com/en/docs/install)
- Create a local copy of this Git repository (clone) - Create a local copy of this Git repository (clone)
- Copy the file `.env.example` to `.env` and populate it with your data (`cp .env.example .env`) - Copy the file `.env.dev` to `.env` and populate it with your data (`cp .env.dev .env`)
### Setup ### Setup

2
apps/api/src/app/order/order.service.ts

@ -148,7 +148,7 @@ export class OrderService {
.plus(data.fee) .plus(data.fee)
.toNumber(); .toNumber();
if (data.type === 'BUY') { if (['BUY', 'FEE'].includes(data.type)) {
amount = new Big(amount).mul(-1).toNumber(); amount = new Big(amount).mul(-1).toNumber();
} }

32
apps/api/src/app/portfolio/portfolio-calculator.ts

@ -602,8 +602,6 @@ export class PortfolioCalculator {
); );
const { const {
dividend,
dividendInBaseCurrency,
grossPerformance, grossPerformance,
grossPerformancePercentage, grossPerformancePercentage,
grossPerformancePercentageWithCurrencyEffect, grossPerformancePercentageWithCurrencyEffect,
@ -615,6 +613,8 @@ export class PortfolioCalculator {
netPerformanceWithCurrencyEffect, netPerformanceWithCurrencyEffect,
timeWeightedInvestment, timeWeightedInvestment,
timeWeightedInvestmentWithCurrencyEffect, timeWeightedInvestmentWithCurrencyEffect,
totalDividend,
totalDividendInBaseCurrency,
totalInvestment, totalInvestment,
totalInvestmentWithCurrencyEffect totalInvestmentWithCurrencyEffect
} = this.getSymbolMetrics({ } = this.getSymbolMetrics({
@ -629,8 +629,8 @@ export class PortfolioCalculator {
hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors; hasAnySymbolMetricsErrors = hasAnySymbolMetricsErrors || hasErrors;
positions.push({ positions.push({
dividend, dividend: totalDividend,
dividendInBaseCurrency, dividendInBaseCurrency: totalDividendInBaseCurrency,
timeWeightedInvestment, timeWeightedInvestment,
timeWeightedInvestmentWithCurrencyEffect, timeWeightedInvestmentWithCurrencyEffect,
averagePrice: item.averagePrice, averagePrice: item.averagePrice,
@ -861,8 +861,6 @@ export class PortfolioCalculator {
const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)]; const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)];
const currentValues: { [date: string]: Big } = {}; const currentValues: { [date: string]: Big } = {};
const currentValuesWithCurrencyEffect: { [date: string]: Big } = {}; const currentValuesWithCurrencyEffect: { [date: string]: Big } = {};
let dividend = new Big(0);
let dividendInBaseCurrency = new Big(0);
let fees = new Big(0); let fees = new Big(0);
let feesAtStartDate = new Big(0); let feesAtStartDate = new Big(0);
let feesAtStartDateWithCurrencyEffect = new Big(0); let feesAtStartDateWithCurrencyEffect = new Big(0);
@ -892,6 +890,8 @@ export class PortfolioCalculator {
[date: string]: Big; [date: string]: Big;
} = {}; } = {};
let totalDividend = new Big(0);
let totalDividendInBaseCurrency = new Big(0);
let totalInvestment = new Big(0); let totalInvestment = new Big(0);
let totalInvestmentFromBuyTransactions = new Big(0); let totalInvestmentFromBuyTransactions = new Big(0);
let totalInvestmentFromBuyTransactionsWithCurrencyEffect = new Big(0); let totalInvestmentFromBuyTransactionsWithCurrencyEffect = new Big(0);
@ -912,8 +912,6 @@ export class PortfolioCalculator {
return { return {
currentValues: {}, currentValues: {},
currentValuesWithCurrencyEffect: {}, currentValuesWithCurrencyEffect: {},
dividend: new Big(0),
dividendInBaseCurrency: new Big(0),
grossPerformance: new Big(0), grossPerformance: new Big(0),
grossPerformancePercentage: new Big(0), grossPerformancePercentage: new Big(0),
grossPerformancePercentageWithCurrencyEffect: new Big(0), grossPerformancePercentageWithCurrencyEffect: new Big(0),
@ -934,6 +932,8 @@ export class PortfolioCalculator {
timeWeightedInvestmentValues: {}, timeWeightedInvestmentValues: {},
timeWeightedInvestmentValuesWithCurrencyEffect: {}, timeWeightedInvestmentValuesWithCurrencyEffect: {},
timeWeightedInvestmentWithCurrencyEffect: new Big(0), timeWeightedInvestmentWithCurrencyEffect: new Big(0),
totalDividend: new Big(0),
totalDividendInBaseCurrency: new Big(0),
totalInvestment: new Big(0), totalInvestment: new Big(0),
totalInvestmentWithCurrencyEffect: new Big(0) totalInvestmentWithCurrencyEffect: new Big(0)
}; };
@ -954,8 +954,6 @@ export class PortfolioCalculator {
return { return {
currentValues: {}, currentValues: {},
currentValuesWithCurrencyEffect: {}, currentValuesWithCurrencyEffect: {},
dividend: new Big(0),
dividendInBaseCurrency: new Big(0),
grossPerformance: new Big(0), grossPerformance: new Big(0),
grossPerformancePercentage: new Big(0), grossPerformancePercentage: new Big(0),
grossPerformancePercentageWithCurrencyEffect: new Big(0), grossPerformancePercentageWithCurrencyEffect: new Big(0),
@ -976,6 +974,8 @@ export class PortfolioCalculator {
timeWeightedInvestmentValues: {}, timeWeightedInvestmentValues: {},
timeWeightedInvestmentValuesWithCurrencyEffect: {}, timeWeightedInvestmentValuesWithCurrencyEffect: {},
timeWeightedInvestmentWithCurrencyEffect: new Big(0), timeWeightedInvestmentWithCurrencyEffect: new Big(0),
totalDividend: new Big(0),
totalDividendInBaseCurrency: new Big(0),
totalInvestment: new Big(0), totalInvestment: new Big(0),
totalInvestmentWithCurrencyEffect: new Big(0) totalInvestmentWithCurrencyEffect: new Big(0)
}; };
@ -1219,8 +1219,10 @@ export class PortfolioCalculator {
totalUnits = totalUnits.plus(order.quantity.mul(getFactor(order.type))); totalUnits = totalUnits.plus(order.quantity.mul(getFactor(order.type)));
if (order.type === 'DIVIDEND') { if (order.type === 'DIVIDEND') {
dividend = dividend.plus(order.quantity.mul(order.unitPrice)); const dividend = order.quantity.mul(order.unitPrice);
dividendInBaseCurrency = dividendInBaseCurrency.plus(
totalDividend = totalDividend.plus(dividend);
totalDividendInBaseCurrency = totalDividendInBaseCurrency.plus(
dividend.mul(exchangeRateAtOrderDate ?? 1) dividend.mul(exchangeRateAtOrderDate ?? 1)
); );
} }
@ -1495,7 +1497,7 @@ export class PortfolioCalculator {
Time weighted investment with currency effect: ${timeWeightedAverageInvestmentBetweenStartAndEndDateWithCurrencyEffect.toFixed( Time weighted investment with currency effect: ${timeWeightedAverageInvestmentBetweenStartAndEndDateWithCurrencyEffect.toFixed(
2 2
)} )}
Total dividend: ${dividend.toFixed(2)} Total dividend: ${totalDividend.toFixed(2)}
Gross performance: ${totalGrossPerformance.toFixed( Gross performance: ${totalGrossPerformance.toFixed(
2 2
)} / ${grossPerformancePercentage.mul(100).toFixed(2)}% )} / ${grossPerformancePercentage.mul(100).toFixed(2)}%
@ -1520,8 +1522,6 @@ export class PortfolioCalculator {
return { return {
currentValues, currentValues,
currentValuesWithCurrencyEffect, currentValuesWithCurrencyEffect,
dividend,
dividendInBaseCurrency,
grossPerformancePercentage, grossPerformancePercentage,
grossPerformancePercentageWithCurrencyEffect, grossPerformancePercentageWithCurrencyEffect,
initialValue, initialValue,
@ -1535,6 +1535,8 @@ export class PortfolioCalculator {
netPerformanceValuesWithCurrencyEffect, netPerformanceValuesWithCurrencyEffect,
timeWeightedInvestmentValues, timeWeightedInvestmentValues,
timeWeightedInvestmentValuesWithCurrencyEffect, timeWeightedInvestmentValuesWithCurrencyEffect,
totalDividend,
totalDividendInBaseCurrency,
totalInvestment, totalInvestment,
totalInvestmentWithCurrencyEffect, totalInvestmentWithCurrencyEffect,
grossPerformance: totalGrossPerformance, grossPerformance: totalGrossPerformance,

21
apps/api/src/app/portfolio/portfolio.service.ts

@ -294,10 +294,9 @@ export class PortfolioService {
const { items } = await this.getChart({ const { items } = await this.getChart({
dateRange, dateRange,
impersonationId, impersonationId,
portfolioOrders, portfolioCalculator,
transactionPoints, transactionPoints,
userId, userId,
userCurrency: this.request.user.Settings.settings.baseCurrency,
withDataDecimation: false withDataDecimation: false
}); });
@ -1229,9 +1228,8 @@ export class PortfolioService {
const { items } = await this.getChart({ const { items } = await this.getChart({
dateRange, dateRange,
impersonationId, impersonationId,
portfolioOrders, portfolioCalculator,
transactionPoints, transactionPoints,
userCurrency,
userId userId
}); });
@ -1456,17 +1454,15 @@ export class PortfolioService {
private async getChart({ private async getChart({
dateRange = 'max', dateRange = 'max',
impersonationId, impersonationId,
portfolioOrders, portfolioCalculator,
transactionPoints, transactionPoints,
userCurrency,
userId, userId,
withDataDecimation = true withDataDecimation = true
}: { }: {
dateRange?: DateRange; dateRange?: DateRange;
impersonationId: string; impersonationId: string;
portfolioOrders: PortfolioOrder[]; portfolioCalculator: PortfolioCalculator;
transactionPoints: TransactionPoint[]; transactionPoints: TransactionPoint[];
userCurrency: string;
userId: string; userId: string;
withDataDecimation?: boolean; withDataDecimation?: boolean;
}): Promise<HistoricalDataContainer> { }): Promise<HistoricalDataContainer> {
@ -1480,15 +1476,6 @@ export class PortfolioService {
userId = await this.getUserId(impersonationId, userId); userId = await this.getUserId(impersonationId, userId);
const portfolioCalculator = new PortfolioCalculator({
currency: userCurrency,
currentRateService: this.currentRateService,
exchangeRateDataService: this.exchangeRateDataService,
orders: portfolioOrders
});
portfolioCalculator.setTransactionPoints(transactionPoints);
const endDate = new Date(); const endDate = new Date();
const portfolioStart = parseDate(transactionPoints[0].date); const portfolioStart = parseDate(transactionPoints[0].date);

3
apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts

@ -155,15 +155,14 @@ export class AdminMarketDataDetailComponent implements OnChanges, OnInit {
day: string; day: string;
yearMonth: string; yearMonth: string;
}) { }) {
const date = parseISO(`${yearMonth}-${day}`);
const marketPrice = this.marketDataByMonth[yearMonth]?.[day]?.marketPrice; const marketPrice = this.marketDataByMonth[yearMonth]?.[day]?.marketPrice;
const dialogRef = this.dialog.open(MarketDataDetailDialog, { const dialogRef = this.dialog.open(MarketDataDetailDialog, {
data: <MarketDataDetailDialogParams>{ data: <MarketDataDetailDialogParams>{
date,
marketPrice, marketPrice,
currency: this.currency, currency: this.currency,
dataSource: this.dataSource, dataSource: this.dataSource,
dateString: `${yearMonth}-${day}`,
symbol: this.symbol, symbol: this.symbol,
user: this.user user: this.user
}, },

2
apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/interfaces/interfaces.ts

@ -5,7 +5,7 @@ import { DataSource } from '@prisma/client';
export interface MarketDataDetailDialogParams { export interface MarketDataDetailDialogParams {
currency: string; currency: string;
dataSource: DataSource; dataSource: DataSource;
date: Date; dateString: string;
marketPrice: number; marketPrice: number;
symbol: string; symbol: string;
user: User; user: User;

4
apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.component.ts

@ -45,7 +45,7 @@ export class MarketDataDetailDialog implements OnDestroy {
this.adminService this.adminService
.fetchSymbolForDate({ .fetchSymbolForDate({
dataSource: this.data.dataSource, dataSource: this.data.dataSource,
date: this.data.date, dateString: this.data.dateString,
symbol: this.data.symbol symbol: this.data.symbol
}) })
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
@ -63,7 +63,7 @@ export class MarketDataDetailDialog implements OnDestroy {
marketData: { marketData: {
marketData: [ marketData: [
{ {
date: this.data.date.toISOString(), date: this.data.dateString,
marketPrice: this.data.marketPrice marketPrice: this.data.marketPrice
} }
] ]

2
apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.html

@ -9,7 +9,7 @@
matInput matInput
name="date" name="date"
[matDatepicker]="date" [matDatepicker]="date"
[(ngModel)]="data.date" [(ngModel)]="data.dateString"
/> />
<mat-datepicker-toggle class="mr-2" matSuffix [for]="date"> <mat-datepicker-toggle class="mr-2" matSuffix [for]="date">
<ion-icon <ion-icon

7
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -1,4 +1,5 @@
import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto'; import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto';
import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto';
import { AdminService } from '@ghostfolio/client/services/admin.service'; import { AdminService } from '@ghostfolio/client/services/admin.service';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
@ -195,15 +196,13 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
header: true, header: true,
skipEmptyLines: true skipEmptyLines: true
} }
).data; ).data as UpdateMarketDataDto[];
this.adminService this.adminService
.postMarketData({ .postMarketData({
dataSource: this.data.dataSource, dataSource: this.data.dataSource,
marketData: { marketData: {
marketData: marketData.map(({ date, marketPrice }) => { marketData
return { marketPrice, date: parseDate(date).toISOString() };
})
}, },
symbol: this.data.symbol symbol: this.data.symbol
}) })

6
apps/client/src/app/components/admin-platform/admin-platform.component.html

@ -91,7 +91,11 @@
<span i18n>Edit</span> <span i18n>Edit</span>
</span> </span>
</button> </button>
<button mat-menu-item (click)="onDeletePlatform(element.id)"> <button
mat-menu-item
[disabled]="element.accountCount > 0"
(click)="onDeletePlatform(element.id)"
>
<span class="align-items-center d-flex"> <span class="align-items-center d-flex">
<ion-icon class="mr-2" name="trash-outline" /> <ion-icon class="mr-2" name="trash-outline" />
<span i18n>Delete</span> <span i18n>Delete</span>

6
apps/client/src/app/components/admin-tag/admin-tag.component.html

@ -71,7 +71,11 @@
<span i18n>Edit</span> <span i18n>Edit</span>
</span> </span>
</button> </button>
<button mat-menu-item (click)="onDeleteTag(element.id)"> <button
mat-menu-item
[disabled]="element.activityCount > 0"
(click)="onDeleteTag(element.id)"
>
<span class="align-items-center d-flex"> <span class="align-items-center d-flex">
<ion-icon class="mr-2" name="trash-outline" /> <ion-icon class="mr-2" name="trash-outline" />
<span i18n>Delete</span> <span i18n>Delete</span>

37
apps/client/src/app/pages/faq/overview/faq-overview-page.html

@ -33,10 +33,8 @@
</mat-card> </mat-card>
<mat-card appearance="outlined" class="mb-3"> <mat-card appearance="outlined" class="mb-3">
<mat-card-header> <mat-card-header>
<mat-card-title <mat-card-title>What else is included in Ghostfolio?</mat-card-title>
>What else is included in Ghostfolio?</mat-card-title </mat-card-header>
></mat-card-header
>
<mat-card-content> <mat-card-content>
Please find a feature overview to manage your wealth Please find a feature overview to manage your wealth
<a [routerLink]="routerLinkFeatures">here</a>. <a [routerLink]="routerLinkFeatures">here</a>.
@ -44,10 +42,8 @@
</mat-card> </mat-card>
<mat-card appearance="outlined" class="mb-3"> <mat-card appearance="outlined" class="mb-3">
<mat-card-header> <mat-card-header>
<mat-card-title <mat-card-title>Can I use Ghostfolio anonymously?</mat-card-title>
>Can I use Ghostfolio anonymously?</mat-card-title </mat-card-header>
></mat-card-header
>
<mat-card-content> <mat-card-content>
Yes, the authentication system via security token enables you to sign Yes, the authentication system via security token enables you to sign
in securely and anonymously to Ghostfolio. There is no need for an in securely and anonymously to Ghostfolio. There is no need for an
@ -56,10 +52,8 @@
</mat-card> </mat-card>
<mat-card appearance="outlined" class="mb-3"> <mat-card appearance="outlined" class="mb-3">
<mat-card-header> <mat-card-header>
<mat-card-title <mat-card-title>How can Ghostfolio be free?</mat-card-title>
>How can Ghostfolio be free?</mat-card-title </mat-card-header>
></mat-card-header
>
<mat-card-content <mat-card-content
>This project is driven by the efforts of contributors from around the >This project is driven by the efforts of contributors from around the
world. The world. The
@ -75,8 +69,8 @@
<mat-card-header> <mat-card-header>
<mat-card-title <mat-card-title
>Do you monetize or sell my financial data?</mat-card-title >Do you monetize or sell my financial data?</mat-card-title
></mat-card-header >
> </mat-card-header>
<mat-card-content <mat-card-content
>No, we value your privacy. We do not sell or share your financial >No, we value your privacy. We do not sell or share your financial
data with any third parties.</mat-card-content data with any third parties.</mat-card-content
@ -84,10 +78,8 @@
</mat-card> </mat-card>
<mat-card appearance="outlined" class="mb-3"> <mat-card appearance="outlined" class="mb-3">
<mat-card-header> <mat-card-header>
<mat-card-title <mat-card-title>What is your business model?</mat-card-title>
>What is your business model?</mat-card-title </mat-card-header>
></mat-card-header
>
<mat-card-content <mat-card-content
>By offering >By offering
<a href="https://ghostfol.io/en/pricing">Ghostfolio Premium</a>, a <a href="https://ghostfol.io/en/pricing">Ghostfolio Premium</a>, a
@ -96,6 +88,15 @@
users.</mat-card-content users.</mat-card-content
> >
</mat-card> </mat-card>
<mat-card appearance="outlined" class="mb-3">
<mat-card-header>
<mat-card-title>What is your product roadmap?</mat-card-title>
</mat-card-header>
<mat-card-content
>At this time, we do not have a public roadmap
available.</mat-card-content
>
</mat-card>
<mat-card appearance="outlined" class="mb-3"> <mat-card appearance="outlined" class="mb-3">
<mat-card-header> <mat-card-header>
<mat-card-title <mat-card-title

22
apps/client/src/app/pages/portfolio/activities/create-or-update-activity-dialog/create-or-update-activity-dialog.component.ts

@ -260,6 +260,17 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
this.activityForm.controls['currency'].setValue(currency); this.activityForm.controls['currency'].setValue(currency);
this.activityForm.controls['currencyOfFee'].setValue(currency); this.activityForm.controls['currencyOfFee'].setValue(currency);
this.activityForm.controls['currencyOfUnitPrice'].setValue(currency); this.activityForm.controls['currencyOfUnitPrice'].setValue(currency);
if (['FEE', 'INTEREST'].includes(type)) {
if (this.activityForm.controls['accountId'].value) {
this.activityForm.controls['updateAccountBalance'].enable();
} else {
this.activityForm.controls['updateAccountBalance'].disable();
this.activityForm.controls['updateAccountBalance'].setValue(
false
);
}
}
} }
} }
); );
@ -374,8 +385,15 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
this.activityForm.controls['unitPriceInCustomCurrency'].setValue(0); this.activityForm.controls['unitPriceInCustomCurrency'].setValue(0);
} }
this.activityForm.controls['updateAccountBalance'].disable(); if (
this.activityForm.controls['updateAccountBalance'].setValue(false); ['FEE', 'INTEREST'].includes(type) &&
this.activityForm.controls['accountId'].value
) {
this.activityForm.controls['updateAccountBalance'].enable();
} else {
this.activityForm.controls['updateAccountBalance'].disable();
this.activityForm.controls['updateAccountBalance'].setValue(false);
}
} else { } else {
this.activityForm.controls['accountId'].setValidators( this.activityForm.controls['accountId'].setValidators(
Validators.required Validators.required

9
apps/client/src/app/services/admin.service.ts

@ -188,17 +188,14 @@ export class AdminService {
public fetchSymbolForDate({ public fetchSymbolForDate({
dataSource, dataSource,
date, dateString,
symbol symbol
}: { }: {
dataSource: DataSource; dataSource: DataSource;
date: Date; dateString: string;
symbol: string; symbol: string;
}) { }) {
const url = `/api/v1/symbol/${dataSource}/${symbol}/${format( const url = `/api/v1/symbol/${dataSource}/${symbol}/${dateString}`;
date,
DATE_FORMAT
)}`;
return this.http.get<IDataProviderHistoricalResponse>(url); return this.http.get<IDataProviderHistoricalResponse>(url);
} }

4
libs/common/src/lib/interfaces/symbol-metrics.interface.ts

@ -7,8 +7,6 @@ export interface SymbolMetrics {
currentValuesWithCurrencyEffect: { currentValuesWithCurrencyEffect: {
[date: string]: Big; [date: string]: Big;
}; };
dividend: Big;
dividendInBaseCurrency: Big;
grossPerformance: Big; grossPerformance: Big;
grossPerformancePercentage: Big; grossPerformancePercentage: Big;
grossPerformancePercentageWithCurrencyEffect: Big; grossPerformancePercentageWithCurrencyEffect: Big;
@ -41,6 +39,8 @@ export interface SymbolMetrics {
[date: string]: Big; [date: string]: Big;
}; };
timeWeightedInvestmentWithCurrencyEffect: Big; timeWeightedInvestmentWithCurrencyEffect: Big;
totalDividend: Big;
totalDividendInBaseCurrency: Big;
totalInvestment: Big; totalInvestment: Big;
totalInvestmentWithCurrencyEffect: Big; totalInvestmentWithCurrencyEffect: Big;
} }

Loading…
Cancel
Save