Merge branch 'main' of gitea.suda.codes:giteauser/ghostfolio-mirror
This commit is contained in:
commit
633995c9c8
@ -5,12 +5,16 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 2.106.0-alpha.1 - 2024-08-24
|
||||
## 2.106.0-beta.1 - 2024-08-25
|
||||
|
||||
### Changed
|
||||
|
||||
- Reworked the portfolio calculator
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed an issue in the view mode toggle of the holdings tab on the home page (experimental)
|
||||
|
||||
## 2.105.0 - 2024-08-21
|
||||
|
||||
### Added
|
||||
|
@ -519,6 +519,23 @@ export class OrderService {
|
||||
return { activities, count };
|
||||
}
|
||||
|
||||
public async getOrdersForPortfolioCalculator({
|
||||
filters,
|
||||
userCurrency,
|
||||
userId
|
||||
}: {
|
||||
filters?: Filter[];
|
||||
userCurrency: string;
|
||||
userId: string;
|
||||
}) {
|
||||
return this.getOrders({
|
||||
filters,
|
||||
userCurrency,
|
||||
userId,
|
||||
withExcludedAccounts: false // TODO
|
||||
});
|
||||
}
|
||||
|
||||
public async getStatisticsByCurrency(
|
||||
currency: EnhancedSymbolProfile['currency']
|
||||
): Promise<{
|
||||
|
@ -713,10 +713,14 @@ export abstract class PortfolioCalculator {
|
||||
netPerformanceWithCurrencyEffect:
|
||||
netPerformanceWithCurrencyEffectSinceStartDate,
|
||||
netPerformanceInPercentage:
|
||||
netPerformanceSinceStartDate / timeWeightedInvestmentValue,
|
||||
timeWeightedInvestmentValue === 0
|
||||
? 0
|
||||
: netPerformanceSinceStartDate / timeWeightedInvestmentValue,
|
||||
netPerformanceInPercentageWithCurrencyEffect:
|
||||
netPerformanceWithCurrencyEffectSinceStartDate /
|
||||
timeWeightedInvestmentValue,
|
||||
timeWeightedInvestmentValue === 0
|
||||
? 0
|
||||
: netPerformanceWithCurrencyEffectSinceStartDate /
|
||||
timeWeightedInvestmentValue,
|
||||
// TODO: Add net worth with valuables
|
||||
// netWorth: totalCurrentValueWithCurrencyEffect
|
||||
// .plus(totalAccountBalanceWithCurrencyEffect)
|
||||
|
@ -247,13 +247,12 @@ export class PortfolioService {
|
||||
|
||||
const { endDate, startDate } = getIntervalFromDateRange(dateRange);
|
||||
|
||||
const { activities } = await this.orderService.getOrders({
|
||||
filters,
|
||||
userId,
|
||||
includeDrafts: true,
|
||||
types: ['BUY', 'SELL'],
|
||||
userCurrency: this.getUserCurrency()
|
||||
});
|
||||
const { activities } =
|
||||
await this.orderService.getOrdersForPortfolioCalculator({
|
||||
filters,
|
||||
userId,
|
||||
userCurrency: this.getUserCurrency()
|
||||
});
|
||||
|
||||
if (activities.length === 0) {
|
||||
return {
|
||||
@ -332,12 +331,12 @@ export class PortfolioService {
|
||||
(user.Settings?.settings as UserSettings)?.emergencyFund ?? 0
|
||||
);
|
||||
|
||||
const { activities } = await this.orderService.getOrders({
|
||||
filters,
|
||||
userCurrency,
|
||||
userId,
|
||||
withExcludedAccounts
|
||||
});
|
||||
const { activities } =
|
||||
await this.orderService.getOrdersForPortfolioCalculator({
|
||||
filters,
|
||||
userCurrency,
|
||||
userId
|
||||
});
|
||||
|
||||
const portfolioCalculator = this.calculatorFactory.createCalculator({
|
||||
activities,
|
||||
@ -597,11 +596,11 @@ export class PortfolioService {
|
||||
const user = await this.userService.user({ id: userId });
|
||||
const userCurrency = this.getUserCurrency(user);
|
||||
|
||||
const { activities } = await this.orderService.getOrders({
|
||||
userCurrency,
|
||||
userId,
|
||||
withExcludedAccounts: true
|
||||
});
|
||||
const { activities } =
|
||||
await this.orderService.getOrdersForPortfolioCalculator({
|
||||
userCurrency,
|
||||
userId
|
||||
});
|
||||
|
||||
const orders = activities.filter(({ SymbolProfile }) => {
|
||||
return (
|
||||
@ -906,14 +905,12 @@ export class PortfolioService {
|
||||
const userId = await this.getUserId(impersonationId, this.request.user.id);
|
||||
const user = await this.userService.user({ id: userId });
|
||||
|
||||
const { endDate } = getIntervalFromDateRange(dateRange);
|
||||
|
||||
const { activities } = await this.orderService.getOrders({
|
||||
endDate,
|
||||
filters,
|
||||
userId,
|
||||
userCurrency: this.getUserCurrency()
|
||||
});
|
||||
const { activities } =
|
||||
await this.orderService.getOrdersForPortfolioCalculator({
|
||||
filters,
|
||||
userId,
|
||||
userCurrency: this.getUserCurrency()
|
||||
});
|
||||
|
||||
if (activities?.length <= 0) {
|
||||
return {
|
||||
@ -1085,15 +1082,12 @@ export class PortfolioService {
|
||||
)
|
||||
);
|
||||
|
||||
const { endDate, startDate } = getIntervalFromDateRange(dateRange);
|
||||
|
||||
const { activities } = await this.orderService.getOrders({
|
||||
endDate,
|
||||
filters,
|
||||
userCurrency,
|
||||
userId,
|
||||
withExcludedAccounts
|
||||
});
|
||||
const { activities } =
|
||||
await this.orderService.getOrdersForPortfolioCalculator({
|
||||
filters,
|
||||
userCurrency,
|
||||
userId
|
||||
});
|
||||
|
||||
if (accountBalanceItems?.length <= 0 && activities?.length <= 0) {
|
||||
return {
|
||||
@ -1126,6 +1120,8 @@ export class PortfolioService {
|
||||
const { errors, hasErrors, historicalData } =
|
||||
await portfolioCalculator.getSnapshot();
|
||||
|
||||
const { endDate, startDate } = getIntervalFromDateRange(dateRange);
|
||||
|
||||
const { chart } = await portfolioCalculator.getPerformance({
|
||||
end: endDate,
|
||||
start: startDate
|
||||
@ -1175,10 +1171,11 @@ export class PortfolioService {
|
||||
const user = await this.userService.user({ id: userId });
|
||||
const userCurrency = this.getUserCurrency(user);
|
||||
|
||||
const { activities } = await this.orderService.getOrders({
|
||||
userCurrency,
|
||||
userId
|
||||
});
|
||||
const { activities } =
|
||||
await this.orderService.getOrdersForPortfolioCalculator({
|
||||
userCurrency,
|
||||
userId
|
||||
});
|
||||
|
||||
const portfolioCalculator = this.calculatorFactory.createCalculator({
|
||||
activities,
|
||||
|
@ -161,10 +161,8 @@ export class HeaderComponent implements OnChanges {
|
||||
.putUserSetting({ dateRange })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe();
|
||||
});
|
||||
@ -191,10 +189,8 @@ export class HeaderComponent implements OnChanges {
|
||||
.putUserSetting(userSetting)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe();
|
||||
});
|
||||
|
@ -18,7 +18,7 @@ import { FormControl } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||
import { Subject } from 'rxjs';
|
||||
import { skip, takeUntil } from 'rxjs/operators';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'gf-home-holdings',
|
||||
@ -87,20 +87,14 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit {
|
||||
});
|
||||
|
||||
this.viewModeFormControl.valueChanges
|
||||
.pipe(
|
||||
// Skip inizialization: "new FormControl"
|
||||
skip(1),
|
||||
takeUntil(this.unsubscribeSubject)
|
||||
)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((holdingsViewMode) => {
|
||||
this.dataService
|
||||
.putUserSetting({ holdingsViewMode })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
@ -144,7 +138,7 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
private initialize() {
|
||||
this.viewModeFormControl.disable();
|
||||
this.viewModeFormControl.disable({ emitEvent: false });
|
||||
|
||||
if (
|
||||
this.hasPermissionToAccessHoldingsChart &&
|
||||
|
@ -73,10 +73,8 @@ export class HomeSummaryComponent implements OnDestroy, OnInit {
|
||||
.putUserSetting({ emergencyFund })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
|
@ -125,10 +125,8 @@ export class UserAccountSettingsComponent implements OnDestroy, OnInit {
|
||||
.putUserSetting({ [aKey]: aValue })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
@ -180,10 +178,8 @@ export class UserAccountSettingsComponent implements OnDestroy, OnInit {
|
||||
.putUserSetting({ isExperimentalFeatures: aEvent.checked })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
@ -218,10 +214,8 @@ export class UserAccountSettingsComponent implements OnDestroy, OnInit {
|
||||
.putUserSetting({ isRestrictedView: aEvent.checked })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
@ -259,10 +253,8 @@ export class UserAccountSettingsComponent implements OnDestroy, OnInit {
|
||||
.putUserSetting({ viewMode: aEvent.checked === true ? 'ZEN' : 'DEFAULT' })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
|
@ -143,10 +143,8 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
.putUserSetting({ benchmark: symbolProfileId })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
|
@ -103,10 +103,8 @@ export class FirePageComponent implements OnDestroy, OnInit {
|
||||
.putUserSetting({ annualInterestRate })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
@ -124,10 +122,8 @@ export class FirePageComponent implements OnDestroy, OnInit {
|
||||
})
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
@ -153,10 +149,8 @@ export class FirePageComponent implements OnDestroy, OnInit {
|
||||
.putUserSetting({ savingsRate })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
@ -174,10 +168,8 @@ export class FirePageComponent implements OnDestroy, OnInit {
|
||||
})
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.userService.remove();
|
||||
|
||||
this.userService
|
||||
.get()
|
||||
.get(true)
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
|
@ -466,11 +466,9 @@
|
||||
[ngClass]="{
|
||||
'cursor-pointer':
|
||||
hasPermissionToOpenDetails &&
|
||||
!row.isDraft &&
|
||||
row.type !== 'FEE' &&
|
||||
row.type !== 'INTEREST' &&
|
||||
row.type !== 'ITEM' &&
|
||||
row.type !== 'LIABILITY'
|
||||
row.Account?.isExcluded !== true &&
|
||||
row.isDraft === false &&
|
||||
['BUY', 'DIVIDEND', 'SELL'].includes(row.type)
|
||||
}"
|
||||
(click)="onClickActivity(row)"
|
||||
></tr>
|
||||
|
@ -199,11 +199,9 @@ export class GfActivitiesTableComponent
|
||||
}
|
||||
} else if (
|
||||
this.hasPermissionToOpenDetails &&
|
||||
!activity.isDraft &&
|
||||
activity.type !== 'FEE' &&
|
||||
activity.type !== 'INTEREST' &&
|
||||
activity.type !== 'ITEM' &&
|
||||
activity.type !== 'LIABILITY'
|
||||
activity.Account?.isExcluded !== true &&
|
||||
activity.isDraft === false &&
|
||||
['BUY', 'DIVIDEND', 'SELL'].includes(activity.type)
|
||||
) {
|
||||
this.onOpenPositionDialog({
|
||||
dataSource: activity.SymbolProfile.dataSource,
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ghostfolio",
|
||||
"version": "2.106.0-alpha.1",
|
||||
"version": "2.106.0-beta.1",
|
||||
"homepage": "https://ghostfol.io",
|
||||
"license": "AGPL-3.0",
|
||||
"repository": "https://github.com/ghostfolio/ghostfolio",
|
||||
|
Loading…
x
Reference in New Issue
Block a user