Feature/respect cash balance in analysis (#203)
* Respect cash balance in in analysis * Update changelog
This commit is contained in:
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Respected the cash balance on the analysis page
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed rendering of currency and platform in dialogs (account and transaction)
|
- Fixed rendering of currency and platform in dialogs (account and transaction)
|
||||||
|
@ -4,6 +4,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { Account, Currency, Order, Prisma } from '@prisma/client';
|
import { Account, Currency, Order, Prisma } from '@prisma/client';
|
||||||
|
|
||||||
import { RedisCacheService } from '../redis-cache/redis-cache.service';
|
import { RedisCacheService } from '../redis-cache/redis-cache.service';
|
||||||
|
import { CashDetails } from './interfaces/cash-details.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AccountService {
|
export class AccountService {
|
||||||
@ -55,24 +56,6 @@ export class AccountService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async calculateCashBalance(aUserId: string, aCurrency: Currency) {
|
|
||||||
let totalCashBalance = 0;
|
|
||||||
|
|
||||||
const accounts = await this.accounts({
|
|
||||||
where: { userId: aUserId }
|
|
||||||
});
|
|
||||||
|
|
||||||
accounts.forEach((account) => {
|
|
||||||
totalCashBalance += this.exchangeRateDataService.toCurrency(
|
|
||||||
account.balance,
|
|
||||||
account.currency,
|
|
||||||
aCurrency
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return totalCashBalance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async createAccount(
|
public async createAccount(
|
||||||
data: Prisma.AccountCreateInput,
|
data: Prisma.AccountCreateInput,
|
||||||
aUserId: string
|
aUserId: string
|
||||||
@ -93,6 +76,27 @@ export class AccountService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getCashDetails(
|
||||||
|
aUserId: string,
|
||||||
|
aCurrency: Currency
|
||||||
|
): Promise<CashDetails> {
|
||||||
|
let totalCashBalance = 0;
|
||||||
|
|
||||||
|
const accounts = await this.accounts({
|
||||||
|
where: { userId: aUserId }
|
||||||
|
});
|
||||||
|
|
||||||
|
accounts.forEach((account) => {
|
||||||
|
totalCashBalance += this.exchangeRateDataService.toCurrency(
|
||||||
|
account.balance,
|
||||||
|
account.currency,
|
||||||
|
aCurrency
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { accounts, balance: totalCashBalance };
|
||||||
|
}
|
||||||
|
|
||||||
public async updateAccount(
|
public async updateAccount(
|
||||||
params: {
|
params: {
|
||||||
where: Prisma.AccountWhereUniqueInput;
|
where: Prisma.AccountWhereUniqueInput;
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
import { Account } from '@prisma/client';
|
||||||
|
|
||||||
|
export interface CashDetails {
|
||||||
|
accounts: Account[];
|
||||||
|
balance: number;
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
import { AccountService } from '@ghostfolio/api/app/account/account.service';
|
||||||
|
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
|
||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
|
||||||
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
|
||||||
import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service';
|
import { AlphaVantageService } from '@ghostfolio/api/services/data-provider/alpha-vantage/alpha-vantage.service';
|
||||||
@ -13,9 +15,10 @@ import { ExperimentalController } from './experimental.controller';
|
|||||||
import { ExperimentalService } from './experimental.service';
|
import { ExperimentalService } from './experimental.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [RedisCacheModule],
|
||||||
controllers: [ExperimentalController],
|
controllers: [ExperimentalController],
|
||||||
providers: [
|
providers: [
|
||||||
|
AccountService,
|
||||||
AlphaVantageService,
|
AlphaVantageService,
|
||||||
ConfigurationService,
|
ConfigurationService,
|
||||||
DataProviderService,
|
DataProviderService,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { AccountService } from '@ghostfolio/api/app/account/account.service';
|
||||||
import { Portfolio } from '@ghostfolio/api/models/portfolio';
|
import { Portfolio } from '@ghostfolio/api/models/portfolio';
|
||||||
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
|
||||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
|
||||||
@ -14,6 +15,7 @@ import { Data } from './interfaces/data.interface';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExperimentalService {
|
export class ExperimentalService {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
private readonly accountService: AccountService,
|
||||||
private readonly dataProviderService: DataProviderService,
|
private readonly dataProviderService: DataProviderService,
|
||||||
private readonly exchangeRateDataService: ExchangeRateDataService,
|
private readonly exchangeRateDataService: ExchangeRateDataService,
|
||||||
private prisma: PrismaService,
|
private prisma: PrismaService,
|
||||||
@ -52,6 +54,7 @@ export class ExperimentalService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const portfolio = new Portfolio(
|
const portfolio = new Portfolio(
|
||||||
|
this.accountService,
|
||||||
this.dataProviderService,
|
this.dataProviderService,
|
||||||
this.exchangeRateDataService,
|
this.exchangeRateDataService,
|
||||||
this.rulesService
|
this.rulesService
|
||||||
|
@ -70,6 +70,7 @@ export class PortfolioService {
|
|||||||
JSON.parse(stringifiedPortfolio);
|
JSON.parse(stringifiedPortfolio);
|
||||||
|
|
||||||
portfolio = new Portfolio(
|
portfolio = new Portfolio(
|
||||||
|
this.accountService,
|
||||||
this.dataProviderService,
|
this.dataProviderService,
|
||||||
this.exchangeRateDataService,
|
this.exchangeRateDataService,
|
||||||
this.rulesService
|
this.rulesService
|
||||||
@ -86,6 +87,7 @@ export class PortfolioService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
portfolio = new Portfolio(
|
portfolio = new Portfolio(
|
||||||
|
this.accountService,
|
||||||
this.dataProviderService,
|
this.dataProviderService,
|
||||||
this.exchangeRateDataService,
|
this.exchangeRateDataService,
|
||||||
this.rulesService
|
this.rulesService
|
||||||
@ -194,7 +196,7 @@ export class PortfolioService {
|
|||||||
impersonationUserId || this.request.user.id
|
impersonationUserId || this.request.user.id
|
||||||
);
|
);
|
||||||
|
|
||||||
const cash = await this.accountService.calculateCashBalance(
|
const { balance } = await this.accountService.getCashDetails(
|
||||||
impersonationUserId || this.request.user.id,
|
impersonationUserId || this.request.user.id,
|
||||||
this.request.user.Settings.currency
|
this.request.user.Settings.currency
|
||||||
);
|
);
|
||||||
@ -202,9 +204,9 @@ export class PortfolioService {
|
|||||||
const fees = portfolio.getFees();
|
const fees = portfolio.getFees();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cash,
|
|
||||||
committedFunds,
|
committedFunds,
|
||||||
fees,
|
fees,
|
||||||
|
cash: balance,
|
||||||
ordersCount: portfolio.getOrders().length,
|
ordersCount: portfolio.getOrders().length,
|
||||||
totalBuy: portfolio.getTotalBuy(),
|
totalBuy: portfolio.getTotalBuy(),
|
||||||
totalSell: portfolio.getTotalSell()
|
totalSell: portfolio.getTotalSell()
|
||||||
@ -350,13 +352,13 @@ export class PortfolioService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
averagePrice: undefined,
|
averagePrice: undefined,
|
||||||
currency: currentData[aSymbol].currency,
|
currency: currentData[aSymbol]?.currency,
|
||||||
firstBuyDate: undefined,
|
firstBuyDate: undefined,
|
||||||
grossPerformance: undefined,
|
grossPerformance: undefined,
|
||||||
grossPerformancePercent: undefined,
|
grossPerformancePercent: undefined,
|
||||||
historicalData: historicalDataArray,
|
historicalData: historicalDataArray,
|
||||||
investment: undefined,
|
investment: undefined,
|
||||||
marketPrice: currentData[aSymbol].marketPrice,
|
marketPrice: currentData[aSymbol]?.marketPrice,
|
||||||
maxPrice: undefined,
|
maxPrice: undefined,
|
||||||
minPrice: undefined,
|
minPrice: undefined,
|
||||||
quantity: undefined,
|
quantity: undefined,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { AccountService } from '@ghostfolio/api/app/account/account.service';
|
||||||
import { UNKNOWN_KEY, baseCurrency } from '@ghostfolio/common/config';
|
import { UNKNOWN_KEY, baseCurrency } from '@ghostfolio/common/config';
|
||||||
import { getUtc, getYesterday } from '@ghostfolio/common/helper';
|
import { getUtc, getYesterday } from '@ghostfolio/common/helper';
|
||||||
import {
|
import {
|
||||||
@ -16,6 +17,16 @@ import { MarketState } from '../services/interfaces/interfaces';
|
|||||||
import { RulesService } from '../services/rules.service';
|
import { RulesService } from '../services/rules.service';
|
||||||
import { Portfolio } from './portfolio';
|
import { Portfolio } from './portfolio';
|
||||||
|
|
||||||
|
jest.mock('../app/account/account.service', () => {
|
||||||
|
return {
|
||||||
|
AccountService: jest.fn().mockImplementation(() => {
|
||||||
|
return {
|
||||||
|
getCashDetails: () => Promise.resolve({ accounts: [], balance: 0 })
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('../services/data-provider.service', () => {
|
jest.mock('../services/data-provider.service', () => {
|
||||||
return {
|
return {
|
||||||
DataProviderService: jest.fn().mockImplementation(() => {
|
DataProviderService: jest.fn().mockImplementation(() => {
|
||||||
@ -81,12 +92,14 @@ const DEFAULT_ACCOUNT_ID = '693a834b-eb89-42c9-ae47-35196c25d269';
|
|||||||
const USER_ID = 'ca6ce867-5d31-495a-bce9-5942bbca9237';
|
const USER_ID = 'ca6ce867-5d31-495a-bce9-5942bbca9237';
|
||||||
|
|
||||||
describe('Portfolio', () => {
|
describe('Portfolio', () => {
|
||||||
|
let accountService: AccountService;
|
||||||
let dataProviderService: DataProviderService;
|
let dataProviderService: DataProviderService;
|
||||||
let exchangeRateDataService: ExchangeRateDataService;
|
let exchangeRateDataService: ExchangeRateDataService;
|
||||||
let portfolio: Portfolio;
|
let portfolio: Portfolio;
|
||||||
let rulesService: RulesService;
|
let rulesService: RulesService;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
accountService = new AccountService(null, null, null);
|
||||||
dataProviderService = new DataProviderService(
|
dataProviderService = new DataProviderService(
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -101,6 +114,7 @@ describe('Portfolio', () => {
|
|||||||
await exchangeRateDataService.initialize();
|
await exchangeRateDataService.initialize();
|
||||||
|
|
||||||
portfolio = new Portfolio(
|
portfolio = new Portfolio(
|
||||||
|
accountService,
|
||||||
dataProviderService,
|
dataProviderService,
|
||||||
exchangeRateDataService,
|
exchangeRateDataService,
|
||||||
rulesService
|
rulesService
|
||||||
@ -147,12 +161,52 @@ describe('Portfolio', () => {
|
|||||||
|
|
||||||
it('should return empty details', async () => {
|
it('should return empty details', async () => {
|
||||||
const details = await portfolio.getDetails('1d');
|
const details = await portfolio.getDetails('1d');
|
||||||
expect(details).toEqual({});
|
expect(details).toMatchObject({
|
||||||
|
_GF_CASH: {
|
||||||
|
accounts: {},
|
||||||
|
allocationCurrent: NaN, // TODO
|
||||||
|
allocationInvestment: NaN, // TODO
|
||||||
|
countries: [],
|
||||||
|
currency: 'CHF',
|
||||||
|
grossPerformance: 0,
|
||||||
|
grossPerformancePercent: 0,
|
||||||
|
investment: 0,
|
||||||
|
marketPrice: 0,
|
||||||
|
marketState: 'open',
|
||||||
|
name: 'Cash',
|
||||||
|
quantity: 0,
|
||||||
|
sectors: [],
|
||||||
|
symbol: '_GF_CASH',
|
||||||
|
transactionCount: 0,
|
||||||
|
type: 'Cash',
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return empty details', async () => {
|
it('should return empty details', async () => {
|
||||||
const details = await portfolio.getDetails('max');
|
const details = await portfolio.getDetails('max');
|
||||||
expect(details).toEqual({});
|
expect(details).toMatchObject({
|
||||||
|
_GF_CASH: {
|
||||||
|
accounts: {},
|
||||||
|
allocationCurrent: NaN, // TODO
|
||||||
|
allocationInvestment: NaN, // TODO
|
||||||
|
countries: [],
|
||||||
|
currency: 'CHF',
|
||||||
|
grossPerformance: 0,
|
||||||
|
grossPerformancePercent: 0,
|
||||||
|
investment: 0,
|
||||||
|
marketPrice: 0,
|
||||||
|
marketState: 'open',
|
||||||
|
name: 'Cash',
|
||||||
|
quantity: 0,
|
||||||
|
sectors: [],
|
||||||
|
symbol: '_GF_CASH',
|
||||||
|
transactionCount: 0,
|
||||||
|
type: 'Cash',
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return zero performance for 1d', async () => {
|
it('should return zero performance for 1d', async () => {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
|
import { AccountService } from '@ghostfolio/api/app/account/account.service';
|
||||||
|
import { CashDetails } from '@ghostfolio/api/app/account/interfaces/cash-details.interface';
|
||||||
|
import { UNKNOWN_KEY, ghostfolioCashSymbol } from '@ghostfolio/common/config';
|
||||||
import { getToday, getYesterday, resetHours } from '@ghostfolio/common/helper';
|
import { getToday, getYesterday, resetHours } from '@ghostfolio/common/helper';
|
||||||
import {
|
import {
|
||||||
PortfolioItem,
|
PortfolioItem,
|
||||||
@ -11,7 +13,7 @@ import {
|
|||||||
import { Country } from '@ghostfolio/common/interfaces/country.interface';
|
import { Country } from '@ghostfolio/common/interfaces/country.interface';
|
||||||
import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
|
import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
|
||||||
import { DateRange, OrderWithAccount } from '@ghostfolio/common/types';
|
import { DateRange, OrderWithAccount } from '@ghostfolio/common/types';
|
||||||
import { Prisma } from '@prisma/client';
|
import { Currency, Prisma } from '@prisma/client';
|
||||||
import { continents, countries } from 'countries-list';
|
import { continents, countries } from 'countries-list';
|
||||||
import {
|
import {
|
||||||
add,
|
add,
|
||||||
@ -34,7 +36,7 @@ import * as roundTo from 'round-to';
|
|||||||
|
|
||||||
import { DataProviderService } from '../services/data-provider.service';
|
import { DataProviderService } from '../services/data-provider.service';
|
||||||
import { ExchangeRateDataService } from '../services/exchange-rate-data.service';
|
import { ExchangeRateDataService } from '../services/exchange-rate-data.service';
|
||||||
import { IOrder } from '../services/interfaces/interfaces';
|
import { IOrder, MarketState, Type } from '../services/interfaces/interfaces';
|
||||||
import { RulesService } from '../services/rules.service';
|
import { RulesService } from '../services/rules.service';
|
||||||
import { PortfolioInterface } from './interfaces/portfolio.interface';
|
import { PortfolioInterface } from './interfaces/portfolio.interface';
|
||||||
import { Order } from './order';
|
import { Order } from './order';
|
||||||
@ -54,6 +56,7 @@ export class Portfolio implements PortfolioInterface {
|
|||||||
private user: UserWithSettings;
|
private user: UserWithSettings;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
private accountService: AccountService,
|
||||||
private dataProviderService: DataProviderService,
|
private dataProviderService: DataProviderService,
|
||||||
private exchangeRateDataService: ExchangeRateDataService,
|
private exchangeRateDataService: ExchangeRateDataService,
|
||||||
private rulesService: RulesService
|
private rulesService: RulesService
|
||||||
@ -232,10 +235,14 @@ export class Portfolio implements PortfolioInterface {
|
|||||||
|
|
||||||
const [portfolioItemsNow] = await this.get(new Date());
|
const [portfolioItemsNow] = await this.get(new Date());
|
||||||
|
|
||||||
const investment = this.getInvestment(new Date());
|
const cashDetails = await this.accountService.getCashDetails(
|
||||||
|
this.user.id,
|
||||||
|
this.user.Settings.currency
|
||||||
|
);
|
||||||
|
const investment = this.getInvestment(new Date()) + cashDetails.balance;
|
||||||
const portfolioItems = this.get(new Date());
|
const portfolioItems = this.get(new Date());
|
||||||
const symbols = this.getSymbols(new Date());
|
const symbols = this.getSymbols(new Date());
|
||||||
const value = this.getValue();
|
const value = this.getValue() + cashDetails.balance;
|
||||||
|
|
||||||
const details: { [symbol: string]: PortfolioPosition } = {};
|
const details: { [symbol: string]: PortfolioPosition } = {};
|
||||||
|
|
||||||
@ -372,6 +379,12 @@ export class Portfolio implements PortfolioInterface {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
details[ghostfolioCashSymbol] = await this.getCashPosition({
|
||||||
|
cashDetails,
|
||||||
|
investment,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,6 +657,46 @@ export class Portfolio implements PortfolioInterface {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getCashPosition({
|
||||||
|
cashDetails,
|
||||||
|
investment,
|
||||||
|
value
|
||||||
|
}: {
|
||||||
|
cashDetails: CashDetails;
|
||||||
|
investment: number;
|
||||||
|
value: number;
|
||||||
|
}) {
|
||||||
|
const accounts = {};
|
||||||
|
const cashValue = cashDetails.balance;
|
||||||
|
|
||||||
|
cashDetails.accounts.forEach((account) => {
|
||||||
|
accounts[account.name] = {
|
||||||
|
current: account.balance,
|
||||||
|
original: account.balance
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
accounts,
|
||||||
|
allocationCurrent: cashValue / value,
|
||||||
|
allocationInvestment: cashValue / investment,
|
||||||
|
countries: [],
|
||||||
|
currency: Currency.CHF,
|
||||||
|
grossPerformance: 0,
|
||||||
|
grossPerformancePercent: 0,
|
||||||
|
investment: cashValue,
|
||||||
|
marketPrice: 0,
|
||||||
|
marketState: MarketState.open,
|
||||||
|
name: Type.Cash,
|
||||||
|
quantity: 0,
|
||||||
|
sectors: [],
|
||||||
|
symbol: ghostfolioCashSymbol,
|
||||||
|
type: Type.Cash,
|
||||||
|
transactionCount: 0,
|
||||||
|
value: cashValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Refactor
|
* TODO: Refactor
|
||||||
*/
|
*/
|
||||||
|
@ -10,6 +10,7 @@ export const MarketState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Type = {
|
export const Type = {
|
||||||
|
Cash: 'Cash',
|
||||||
Cryptocurrency: 'Cryptocurrency',
|
Cryptocurrency: 'Cryptocurrency',
|
||||||
ETF: 'ETF',
|
ETF: 'ETF',
|
||||||
Stock: 'Stock',
|
Stock: 'Stock',
|
||||||
|
@ -82,7 +82,13 @@
|
|||||||
<tr
|
<tr
|
||||||
*matRowDef="let row; columns: displayedColumns"
|
*matRowDef="let row; columns: displayedColumns"
|
||||||
mat-row
|
mat-row
|
||||||
(click)="onOpenPositionDialog({ symbol: row.symbol, title: row.name })"
|
[ngClass]="{
|
||||||
|
'cursor-pointer': !this.ignoreTypes.includes(row.type)
|
||||||
|
}"
|
||||||
|
(click)="
|
||||||
|
!this.ignoreTypes.includes(row.type) &&
|
||||||
|
onOpenPositionDialog({ symbol: row.symbol, title: row.name })
|
||||||
|
"
|
||||||
></tr>
|
></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mat-row {
|
.mat-row {
|
||||||
cursor: pointer;
|
&.cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import { MatPaginator } from '@angular/material/paginator';
|
|||||||
import { MatSort } from '@angular/material/sort';
|
import { MatSort } from '@angular/material/sort';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Type } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { PortfolioPosition } from '@ghostfolio/common/interfaces';
|
import { PortfolioPosition } from '@ghostfolio/common/interfaces';
|
||||||
import { Order as OrderModel } from '@prisma/client';
|
import { Order as OrderModel } from '@prisma/client';
|
||||||
import { Subject, Subscription } from 'rxjs';
|
import { Subject, Subscription } from 'rxjs';
|
||||||
@ -42,6 +43,7 @@ export class PositionsTableComponent implements OnChanges, OnDestroy, OnInit {
|
|||||||
public dataSource: MatTableDataSource<PortfolioPosition> =
|
public dataSource: MatTableDataSource<PortfolioPosition> =
|
||||||
new MatTableDataSource();
|
new MatTableDataSource();
|
||||||
public displayedColumns = [];
|
public displayedColumns = [];
|
||||||
|
public ignoreTypes = [Type.Cash];
|
||||||
public isLoading = true;
|
public isLoading = true;
|
||||||
public pageSize = 7;
|
public pageSize = 7;
|
||||||
public routeQueryParams: Subscription;
|
public routeQueryParams: Subscription;
|
||||||
|
@ -5,7 +5,10 @@ import {
|
|||||||
OnChanges,
|
OnChanges,
|
||||||
OnInit
|
OnInit
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
|
import {
|
||||||
|
MarketState,
|
||||||
|
Type
|
||||||
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { PortfolioPosition } from '@ghostfolio/common/interfaces/portfolio-position.interface';
|
import { PortfolioPosition } from '@ghostfolio/common/interfaces/portfolio-position.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -25,6 +28,8 @@ export class PositionsComponent implements OnChanges, OnInit {
|
|||||||
public positionsRest: PortfolioPosition[] = [];
|
public positionsRest: PortfolioPosition[] = [];
|
||||||
public positionsWithPriority: PortfolioPosition[] = [];
|
public positionsWithPriority: PortfolioPosition[] = [];
|
||||||
|
|
||||||
|
private ignoreTypes = [Type.Cash];
|
||||||
|
|
||||||
public constructor() {}
|
public constructor() {}
|
||||||
|
|
||||||
public ngOnInit() {}
|
public ngOnInit() {}
|
||||||
@ -41,6 +46,10 @@ export class PositionsComponent implements OnChanges, OnInit {
|
|||||||
this.positionsWithPriority = [];
|
this.positionsWithPriority = [];
|
||||||
|
|
||||||
for (const [, portfolioPosition] of Object.entries(this.positions)) {
|
for (const [, portfolioPosition] of Object.entries(this.positions)) {
|
||||||
|
if (this.ignoreTypes.includes(portfolioPosition.type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
portfolioPosition.marketState === MarketState.open ||
|
portfolioPosition.marketState === MarketState.open ||
|
||||||
this.range !== '1d'
|
this.range !== '1d'
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
PortfolioPosition,
|
PortfolioPosition,
|
||||||
User
|
User
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
|
|
||||||
import { DeviceDetectorService } from 'ngx-device-detector';
|
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
@ -30,12 +29,12 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
|||||||
[code: string]: { name: string; value: number };
|
[code: string]: { name: string; value: number };
|
||||||
};
|
};
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
|
public hasImpersonationId: boolean;
|
||||||
public period = 'current';
|
public period = 'current';
|
||||||
public periodOptions: ToggleOption[] = [
|
public periodOptions: ToggleOption[] = [
|
||||||
{ label: 'Initial', value: 'original' },
|
{ label: 'Initial', value: 'original' },
|
||||||
{ label: 'Current', value: 'current' }
|
{ label: 'Current', value: 'current' }
|
||||||
];
|
];
|
||||||
public hasImpersonationId: boolean;
|
|
||||||
public portfolioItems: PortfolioItem[];
|
public portfolioItems: PortfolioItem[];
|
||||||
public portfolioPositions: { [symbol: string]: PortfolioPosition };
|
public portfolioPositions: { [symbol: string]: PortfolioPosition };
|
||||||
public positions: { [symbol: string]: any };
|
public positions: { [symbol: string]: any };
|
||||||
|
@ -15,6 +15,7 @@ export const currencyPairs: Partial<IDataGatheringItem>[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const ghostfolioScraperApiSymbolPrefix = '_GF_';
|
export const ghostfolioScraperApiSymbolPrefix = '_GF_';
|
||||||
|
export const ghostfolioCashSymbol = `${ghostfolioScraperApiSymbolPrefix}CASH`;
|
||||||
|
|
||||||
export const locale = 'de-CH';
|
export const locale = 'de-CH';
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user