Feature/support unlimited currencies (#387)

* Support unlimited currencies

* Update changelog
pull/389/head
Thomas Kaul 3 years ago committed by GitHub
parent 508a48f4c3
commit dcee651098
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -15,12 +15,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Changed the navigation to always show the portfolio page
- Migrated the data type of currencies from `enum` to `string` in the database
- Supported unlimited currencies (instead of `CHF`, `EUR`, `GBP` and `USD`)
### Fixed
- Hid the actions from the accounts table in the _Presenter View_
- Hid the actions from the transactions table in the _Presenter View_
### Todo
- Apply data migration (`yarn prisma migrate deploy`)
## 1.55.0 - 20.09.2021
### Changed

@ -1,7 +1,7 @@
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { Injectable } from '@nestjs/common';
import { Account, Currency, Order, Platform, Prisma } from '@prisma/client';
import { Account, Order, Platform, Prisma } from '@prisma/client';
import { CashDetails } from './interfaces/cash-details.interface';
@ -95,7 +95,7 @@ export class AccountService {
public async getCashDetails(
aUserId: string,
aCurrency: Currency
aCurrency: string
): Promise<CashDetails> {
let totalCashBalance = 0;

@ -1,4 +1,4 @@
import { AccountType, Currency } from '@prisma/client';
import { AccountType } from '@prisma/client';
import { IsNumber, IsString, ValidateIf } from 'class-validator';
export class CreateAccountDto {
@ -9,7 +9,7 @@ export class CreateAccountDto {
balance: number;
@IsString()
currency: Currency;
currency: string;
@IsString()
name: string;

@ -1,4 +1,4 @@
import { AccountType, Currency } from '@prisma/client';
import { AccountType } from '@prisma/client';
import { IsNumber, IsString, ValidateIf } from 'class-validator';
export class UpdateAccountDto {
@ -9,7 +9,7 @@ export class UpdateAccountDto {
balance: number;
@IsString()
currency: Currency;
currency: string;
@IsString()
id: string;

@ -3,9 +3,9 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.ser
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { baseCurrency } from '@ghostfolio/common/config';
import { AdminData } from '@ghostfolio/common/interfaces';
import { Injectable } from '@nestjs/common';
import { Currency } from '@prisma/client';
import { differenceInDays } from 'date-fns';
@Injectable()
@ -20,53 +20,22 @@ export class AdminService {
public async get(): Promise<AdminData> {
return {
exchangeRates: [
{
label1: Currency.EUR,
label2: Currency.CHF,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.EUR,
Currency.CHF
)
},
{
label1: Currency.GBP,
label2: Currency.CHF,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.GBP,
Currency.CHF
)
},
{
label1: Currency.USD,
label2: Currency.CHF,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.USD,
Currency.CHF
)
},
{
label1: Currency.USD,
label2: Currency.EUR,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.USD,
Currency.EUR
)
},
{
label1: Currency.USD,
label2: Currency.GBP,
value: await this.exchangeRateDataService.toCurrency(
1,
Currency.USD,
Currency.GBP
)
}
],
exchangeRates: this.exchangeRateDataService
.getCurrencies()
.filter((currency) => {
return currency !== baseCurrency;
})
.map((currency) => {
return {
label1: baseCurrency,
label2: currency,
value: this.exchangeRateDataService.toCurrency(
1,
baseCurrency,
currency
)
};
}),
lastDataGathering: await this.getLastDataGathering(),
transactionCount: await this.prismaService.order.count(),
userCount: await this.prismaService.user.count(),

@ -7,13 +7,14 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data
import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service';
import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service';
import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data.module';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { Module } from '@nestjs/common';
import { CacheController } from './cache.controller';
@Module({
imports: [RedisCacheModule],
imports: [ExchangeRateDataModule, RedisCacheModule],
controllers: [CacheController],
providers: [
AlphaVantageService,

@ -1,9 +1,9 @@
import { Currency, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString, ValidateIf } from 'class-validator';
import { Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString } from 'class-validator';
export class CreateOrderDto {
@IsString()
currency: Currency;
currency: string;
@IsISO8601()
date: string;

@ -1,6 +1,4 @@
import { Currency } from '@prisma/client';
export interface Data {
currency: Currency;
currency: string;
value: number;
}

@ -1,4 +1,3 @@
import { CacheService } from '@ghostfolio/api/app/cache/cache.service';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering.module';
@ -18,6 +17,6 @@ import { ExportService } from './export.service';
RedisCacheModule
],
controllers: [ExportController],
providers: [CacheService, ExportService]
providers: [ExportService]
})
export class ExportModule {}

@ -5,6 +5,7 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data
import { GhostfolioScraperApiService } from '@ghostfolio/api/services/data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service';
import { RakutenRapidApiService } from '@ghostfolio/api/services/data-provider/rakuten-rapid-api/rakuten-rapid-api.service';
import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data.module';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
@ -14,6 +15,7 @@ import { InfoService } from './info.service';
@Module({
imports: [
ExchangeRateDataModule,
JwtModule.register({
secret: process.env.JWT_SECRET_KEY,
signOptions: { expiresIn: '30 days' }

@ -1,12 +1,12 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { InfoItem } from '@ghostfolio/common/interfaces';
import { Subscription } from '@ghostfolio/common/interfaces/subscription.interface';
import { permissions } from '@ghostfolio/common/permissions';
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Currency } from '@prisma/client';
import * as bent from 'bent';
import { subDays } from 'date-fns';
@ -16,6 +16,7 @@ export class InfoService {
public constructor(
private readonly configurationService: ConfigurationService,
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly dataGatheringService: DataGatheringService,
private readonly jwtService: JwtService,
private readonly prismaService: PrismaService
@ -56,7 +57,7 @@ export class InfoService {
...info,
globalPermissions,
platforms,
currencies: Object.values(Currency),
currencies: this.exchangeRateDataService.getCurrencies(),
demoAuthToken: this.getDemoAuthToken(),
lastDataGathering: await this.getLastDataGathering(),
statistics: await this.getStatistics(),

@ -1,4 +1,4 @@
import { Currency, DataSource, Type } from '@prisma/client';
import { DataSource, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString } from 'class-validator';
export class CreateOrderDto {
@ -6,7 +6,7 @@ export class CreateOrderDto {
accountId: string;
@IsString()
currency: Currency;
currency: string;
@IsString()
dataSource: DataSource;

@ -1,12 +1,12 @@
import { Currency, DataSource, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString, ValidateIf } from 'class-validator';
import { DataSource, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString } from 'class-validator';
export class UpdateOrderDto {
@IsString()
accountId: string;
@IsString()
currency: Currency;
currency: string;
@IsString()
dataSource: DataSource;

@ -1,6 +1,6 @@
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { Currency, DataSource, MarketData } from '@prisma/client';
import { DataSource, MarketData } from '@prisma/client';
import { CurrentRateService } from './current-rate.service';
import { MarketDataService } from './market-data.service';
@ -80,7 +80,7 @@ describe('CurrentRateService', () => {
null,
null
);
exchangeRateDataService = new ExchangeRateDataService(null);
exchangeRateDataService = new ExchangeRateDataService(null, null);
marketDataService = new MarketDataService(null);
await exchangeRateDataService.initialize();
@ -95,10 +95,10 @@ describe('CurrentRateService', () => {
it('getValue', async () => {
expect(
await currentRateService.getValue({
currency: Currency.USD,
currency: 'USD',
date: new Date(Date.UTC(2020, 0, 1, 0, 0, 0)),
symbol: 'AMZN',
userCurrency: Currency.CHF
userCurrency: 'CHF'
})
).toMatchObject({
marketPrice: 1847.839966
@ -108,13 +108,13 @@ describe('CurrentRateService', () => {
it('getValues', async () => {
expect(
await currentRateService.getValues({
currencies: { AMZN: Currency.USD },
currencies: { AMZN: 'USD' },
dataGatheringItems: [{ dataSource: DataSource.YAHOO, symbol: 'AMZN' }],
dateQuery: {
lt: new Date(Date.UTC(2020, 0, 2, 0, 0, 0)),
gte: new Date(Date.UTC(2020, 0, 1, 0, 0, 0))
},
userCurrency: Currency.CHF
userCurrency: 'CHF'
})
).toMatchObject([
{

@ -1,8 +1,6 @@
import { Currency } from '@prisma/client';
export interface GetValueParams {
currency: Currency;
currency: string;
date: Date;
symbol: string;
userCurrency: Currency;
userCurrency: string;
}

@ -1,11 +1,10 @@
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { Currency } from '@prisma/client';
import { DateQuery } from './date-query.interface';
export interface GetValuesParams {
currencies: { [symbol: string]: Currency };
currencies: { [symbol: string]: string };
dataGatheringItems: IDataGatheringItem[];
dateQuery: DateQuery;
userCurrency: Currency;
userCurrency: string;
}

@ -1,9 +1,9 @@
import { OrderType } from '@ghostfolio/api/models/order-type';
import { Currency, DataSource } from '@prisma/client';
import { DataSource } from '@prisma/client';
import Big from 'big.js';
export interface PortfolioOrder {
currency: Currency;
currency: string;
date: string;
dataSource: DataSource;
fee: Big;

@ -1,8 +1,6 @@
import { Currency } from '@prisma/client';
export interface PortfolioPositionDetail {
averagePrice: number;
currency: Currency;
currency: string;
firstBuyDate: string;
grossPerformance: number;
grossPerformancePercent: number;

@ -1,8 +1,8 @@
import { Currency, DataSource } from '@prisma/client';
import { DataSource } from '@prisma/client';
import Big from 'big.js';
export interface TransactionPointSymbol {
currency: Currency;
currency: string;
dataSource: DataSource;
fee: Big;
firstBuyDate: string;

@ -1,7 +1,6 @@
import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service';
import { OrderType } from '@ghostfolio/api/models/order-type';
import { parseDate, resetHours } from '@ghostfolio/common/helper';
import { Currency, DataSource } from '@prisma/client';
import { DataSource } from '@prisma/client';
import Big from 'big.js';
import {
addDays,
@ -134,7 +133,7 @@ describe('PortfolioCalculator', () => {
it('with orders of only one symbol', () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.computeTransactionPoints(ordersVTI);
const portfolioItemsAtTransactionPoints =
@ -148,7 +147,7 @@ describe('PortfolioCalculator', () => {
it('with orders of only one symbol and a fee', () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
const orders: PortfolioOrder[] = [
{
@ -158,7 +157,7 @@ describe('PortfolioCalculator', () => {
symbol: 'VTI',
type: OrderType.Buy,
unitPrice: new Big('144.38'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big('5')
},
@ -169,7 +168,7 @@ describe('PortfolioCalculator', () => {
symbol: 'VTI',
type: OrderType.Buy,
unitPrice: new Big('147.99'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big('10')
},
@ -180,7 +179,7 @@ describe('PortfolioCalculator', () => {
symbol: 'VTI',
type: OrderType.Sell,
unitPrice: new Big('151.41'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big('5')
}
@ -198,7 +197,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
transactionCount: 1,
fee: new Big('5')
@ -213,7 +212,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
transactionCount: 2,
fee: new Big('15')
@ -228,7 +227,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'VTI',
investment: new Big('652.55'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
transactionCount: 3,
fee: new Big('20')
@ -241,7 +240,7 @@ describe('PortfolioCalculator', () => {
it('with orders of two different symbols and a fee', () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
const orders: PortfolioOrder[] = [
{
@ -251,7 +250,7 @@ describe('PortfolioCalculator', () => {
symbol: 'VTI',
type: OrderType.Buy,
unitPrice: new Big('144.38'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big('5')
},
@ -262,7 +261,7 @@ describe('PortfolioCalculator', () => {
symbol: 'VTX',
type: OrderType.Buy,
unitPrice: new Big('147.99'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big('10')
},
@ -273,7 +272,7 @@ describe('PortfolioCalculator', () => {
symbol: 'VTI',
type: OrderType.Sell,
unitPrice: new Big('151.41'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big('5')
}
@ -291,7 +290,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
transactionCount: 1,
fee: new Big('5')
@ -306,7 +305,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
transactionCount: 1,
fee: new Big('5')
@ -316,7 +315,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTX',
investment: new Big('1479.9'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-08-03',
transactionCount: 1,
fee: new Big('10')
@ -331,7 +330,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'VTI',
investment: new Big('686.75'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
transactionCount: 2,
fee: new Big('10')
@ -341,7 +340,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTX',
investment: new Big('1479.9'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-08-03',
transactionCount: 1,
fee: new Big('10')
@ -355,7 +354,7 @@ describe('PortfolioCalculator', () => {
const orders: PortfolioOrder[] = [
...ordersVTI,
{
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: '2021-02-01',
name: 'Vanguard Total Stock Market Index Fund ETF Shares',
@ -368,7 +367,7 @@ describe('PortfolioCalculator', () => {
];
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.computeTransactionPoints(orders);
const portfolioItemsAtTransactionPoints =
@ -379,7 +378,7 @@ describe('PortfolioCalculator', () => {
date: '2019-02-01',
items: [
{
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
investment: new Big('1443.8'),
@ -394,7 +393,7 @@ describe('PortfolioCalculator', () => {
date: '2019-08-03',
items: [
{
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
investment: new Big('2923.7'),
@ -409,7 +408,7 @@ describe('PortfolioCalculator', () => {
date: '2020-02-02',
items: [
{
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
investment: new Big('652.55'),
@ -424,7 +423,7 @@ describe('PortfolioCalculator', () => {
date: '2021-02-01',
items: [
{
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
investment: new Big('6627.05'),
@ -439,7 +438,7 @@ describe('PortfolioCalculator', () => {
date: '2021-08-01',
items: [
{
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
investment: new Big('8403.95'),
@ -457,7 +456,7 @@ describe('PortfolioCalculator', () => {
const orders: PortfolioOrder[] = [
...ordersVTI,
{
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: '2019-09-01',
name: 'Amazon.com, Inc.',
@ -470,7 +469,7 @@ describe('PortfolioCalculator', () => {
];
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.computeTransactionPoints(orders);
const portfolioItemsAtTransactionPoints =
@ -485,7 +484,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
fee: new Big(0),
transactionCount: 1
@ -500,7 +499,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
fee: new Big(0),
transactionCount: 2
@ -515,7 +514,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'AMZN',
investment: new Big('10109.95'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-09-01',
fee: new Big(0),
transactionCount: 1
@ -525,7 +524,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
fee: new Big(0),
transactionCount: 2
@ -540,7 +539,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'AMZN',
investment: new Big('10109.95'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-09-01',
fee: new Big(0),
transactionCount: 1
@ -550,7 +549,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'VTI',
investment: new Big('652.55'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
fee: new Big(0),
transactionCount: 3
@ -565,7 +564,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'AMZN',
investment: new Big('10109.95'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-09-01',
fee: new Big(0),
transactionCount: 1
@ -575,7 +574,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('15'),
symbol: 'VTI',
investment: new Big('2684.05'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
fee: new Big(0),
transactionCount: 4
@ -590,7 +589,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'AMZN',
investment: new Big('10109.95'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-09-01',
fee: new Big(0),
transactionCount: 1
@ -600,7 +599,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('25'),
symbol: 'VTI',
investment: new Big('4460.95'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2019-02-01',
fee: new Big(0),
transactionCount: 5
@ -620,7 +619,7 @@ describe('PortfolioCalculator', () => {
symbol: 'AMZN',
type: OrderType.Buy,
unitPrice: new Big('2021.99'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
},
@ -631,14 +630,14 @@ describe('PortfolioCalculator', () => {
symbol: 'AMZN',
type: OrderType.Sell,
unitPrice: new Big('2412.23'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
}
];
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.computeTransactionPoints(orders);
const portfolioItemsAtTransactionPoints =
@ -652,7 +651,7 @@ describe('PortfolioCalculator', () => {
it('with mixed symbols', () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.computeTransactionPoints(ordersMixedSymbols);
const portfolioItemsAtTransactionPoints =
@ -667,7 +666,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('50'),
symbol: 'TSLA',
investment: new Big('2148.5'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2017-01-03',
fee: new Big(0),
transactionCount: 1
@ -682,7 +681,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('0.5614682'),
symbol: 'BTCUSD',
investment: new Big('1999.9999999999998659756'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2017-07-01',
fee: new Big(0),
transactionCount: 1
@ -692,7 +691,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('50'),
symbol: 'TSLA',
investment: new Big('2148.5'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2017-01-03',
fee: new Big(0),
transactionCount: 1
@ -707,7 +706,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'AMZN',
investment: new Big('10109.95'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2018-09-01',
fee: new Big(0),
transactionCount: 1
@ -717,7 +716,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('0.5614682'),
symbol: 'BTCUSD',
investment: new Big('1999.9999999999998659756'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2017-07-01',
fee: new Big(0),
transactionCount: 1
@ -727,7 +726,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('50'),
symbol: 'TSLA',
investment: new Big('2148.5'),
currency: Currency.USD,
currency: 'USD',
firstBuyDate: '2017-01-03',
fee: new Big(0),
transactionCount: 1
@ -742,7 +741,7 @@ describe('PortfolioCalculator', () => {
it('with single TSLA and early start', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints(orderTslaTransactionPoint);
@ -782,7 +781,7 @@ describe('PortfolioCalculator', () => {
it('with single TSLA and buy day start', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints(orderTslaTransactionPoint);
@ -822,7 +821,7 @@ describe('PortfolioCalculator', () => {
it('with single TSLA and late start', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints(orderTslaTransactionPoint);
@ -862,7 +861,7 @@ describe('PortfolioCalculator', () => {
it('with VTI only', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
@ -905,7 +904,7 @@ describe('PortfolioCalculator', () => {
it('with buy and sell', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints(transactionPointsBuyAndSell);
@ -959,7 +958,7 @@ describe('PortfolioCalculator', () => {
it('with buy, sell, buy', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints([
{
@ -969,7 +968,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'VTI',
investment: new Big('805.9'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-09-01',
fee: new Big(0),
@ -984,7 +983,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('0'),
symbol: 'VTI',
investment: new Big('0'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-09-01',
fee: new Big(0),
@ -999,7 +998,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'VTI',
investment: new Big('1013.9'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-09-01',
fee: new Big(0),
@ -1047,7 +1046,7 @@ describe('PortfolioCalculator', () => {
it('with performance since Jan 1st, 2020', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
const transactionPoints: TransactionPoint[] = [
{
@ -1057,7 +1056,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -1072,7 +1071,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -1130,7 +1129,7 @@ describe('PortfolioCalculator', () => {
it('with net performance since Jan 1st, 2020 - include fees', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
const transactionPoints: TransactionPoint[] = [
{
@ -1140,7 +1139,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(50),
@ -1155,7 +1154,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(100),
@ -1223,7 +1222,7 @@ describe('PortfolioCalculator', () => {
it('with net performance since Feb 1st, 2019 - include fees', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
const transactionPoints: TransactionPoint[] = [
{
@ -1233,7 +1232,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(50),
@ -1248,7 +1247,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(100),
@ -1311,7 +1310,7 @@ describe('PortfolioCalculator', () => {
it('with TWR example from Investopedia: Scenario 1', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints([
{
@ -1321,7 +1320,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('1000000'), // 1 million
symbol: 'MFA', // Mutual Fund A
investment: new Big('1000000'), // 1 million
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2010-12-31',
fee: new Big(0),
@ -1336,7 +1335,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('1086022.689344541'), // 1,000,000 + 100,000 / 1.162484
symbol: 'MFA', // Mutual Fund A
investment: new Big('1100000'), // 1,000,000 + 100,000
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2010-12-31',
fee: new Big(0),
@ -1388,7 +1387,7 @@ describe('PortfolioCalculator', () => {
it('with example from chsoft.ch: Performance of a Combination of Investments', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.CHF
'CHF'
);
portfolioCalculator.setTransactionPoints([
{
@ -1398,7 +1397,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('200'),
symbol: 'SPA', // Sub Portfolio A
investment: new Big('200'),
currency: Currency.CHF,
currency: 'CHF',
dataSource: DataSource.YAHOO,
firstBuyDate: '2012-12-31',
fee: new Big(0),
@ -1408,7 +1407,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('300'),
symbol: 'SPB', // Sub Portfolio B
investment: new Big('300'),
currency: Currency.CHF,
currency: 'CHF',
dataSource: DataSource.YAHOO,
firstBuyDate: '2012-12-31',
fee: new Big(0),
@ -1423,7 +1422,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('200'),
symbol: 'SPA', // Sub Portfolio A
investment: new Big('200'),
currency: Currency.CHF,
currency: 'CHF',
dataSource: DataSource.YAHOO,
firstBuyDate: '2012-12-31',
fee: new Big(0),
@ -1433,7 +1432,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('300'),
symbol: 'SPB', // Sub Portfolio B
investment: new Big('300'),
currency: Currency.CHF,
currency: 'CHF',
dataSource: DataSource.YAHOO,
firstBuyDate: '2012-12-31',
fee: new Big(0),
@ -1494,7 +1493,7 @@ describe('PortfolioCalculator', () => {
it('with yearly', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
const timelineSpecification: TimelineSpecification[] = [
@ -1537,7 +1536,7 @@ describe('PortfolioCalculator', () => {
it('with yearly and fees', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
const transactionPoints: TransactionPoint[] = [
{
@ -1547,7 +1546,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(50),
@ -1562,7 +1561,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(100),
@ -1577,7 +1576,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'VTI',
investment: new Big('652.55'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(150),
@ -1592,7 +1591,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('15'),
symbol: 'VTI',
investment: new Big('2684.05'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(200),
@ -1607,7 +1606,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('25'),
symbol: 'VTI',
investment: new Big('4460.95'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(250),
@ -1657,7 +1656,7 @@ describe('PortfolioCalculator', () => {
it('with monthly', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
const timelineSpecification: TimelineSpecification[] = [
@ -1889,7 +1888,7 @@ describe('PortfolioCalculator', () => {
it('with yearly and monthly mixed', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
const timelineSpecification: TimelineSpecification[] = [
@ -1971,7 +1970,7 @@ describe('PortfolioCalculator', () => {
it('with all mixed', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
const timelineSpecification: TimelineSpecification[] = [
@ -2262,7 +2261,7 @@ describe('PortfolioCalculator', () => {
it('with mixed portfolio', async () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
portfolioCalculator.setTransactionPoints([
{
@ -2272,7 +2271,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('5'),
symbol: 'AMZN',
investment: new Big('10109.95'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2282,7 +2281,7 @@ describe('PortfolioCalculator', () => {
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2325,7 +2324,7 @@ describe('PortfolioCalculator', () => {
describe('annualized performance percentage', () => {
const portfolioCalculator = new PortfolioCalculator(
currentRateService,
Currency.USD
'USD'
);
it('Get annualized performance', async () => {
@ -2391,7 +2390,7 @@ const ordersMixedSymbols: PortfolioOrder[] = [
symbol: 'TSLA',
type: OrderType.Buy,
unitPrice: new Big('42.97'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
},
@ -2402,7 +2401,7 @@ const ordersMixedSymbols: PortfolioOrder[] = [
symbol: 'BTCUSD',
type: OrderType.Buy,
unitPrice: new Big('3562.089535970158'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
},
@ -2413,7 +2412,7 @@ const ordersMixedSymbols: PortfolioOrder[] = [
symbol: 'AMZN',
type: OrderType.Buy,
unitPrice: new Big('2021.99'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
}
@ -2427,7 +2426,7 @@ const ordersVTI: PortfolioOrder[] = [
symbol: 'VTI',
type: OrderType.Buy,
unitPrice: new Big('144.38'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
},
@ -2438,7 +2437,7 @@ const ordersVTI: PortfolioOrder[] = [
symbol: 'VTI',
type: OrderType.Buy,
unitPrice: new Big('147.99'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
},
@ -2449,7 +2448,7 @@ const ordersVTI: PortfolioOrder[] = [
symbol: 'VTI',
type: OrderType.Sell,
unitPrice: new Big('151.41'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
},
@ -2460,7 +2459,7 @@ const ordersVTI: PortfolioOrder[] = [
symbol: 'VTI',
type: OrderType.Buy,
unitPrice: new Big('177.69'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
},
@ -2471,7 +2470,7 @@ const ordersVTI: PortfolioOrder[] = [
symbol: 'VTI',
type: OrderType.Buy,
unitPrice: new Big('203.15'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
fee: new Big(0)
}
@ -2485,7 +2484,7 @@ const orderTslaTransactionPoint: TransactionPoint[] = [
quantity: new Big('1'),
symbol: 'TSLA',
investment: new Big('719.46'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2021-01-01',
fee: new Big(0),
@ -2503,7 +2502,7 @@ const ordersVTITransactionPoints: TransactionPoint[] = [
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2518,7 +2517,7 @@ const ordersVTITransactionPoints: TransactionPoint[] = [
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2533,7 +2532,7 @@ const ordersVTITransactionPoints: TransactionPoint[] = [
quantity: new Big('5'),
symbol: 'VTI',
investment: new Big('652.55'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2548,7 +2547,7 @@ const ordersVTITransactionPoints: TransactionPoint[] = [
quantity: new Big('15'),
symbol: 'VTI',
investment: new Big('2684.05'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2563,7 +2562,7 @@ const ordersVTITransactionPoints: TransactionPoint[] = [
quantity: new Big('25'),
symbol: 'VTI',
investment: new Big('4460.95'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2581,7 +2580,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('10'),
symbol: 'VTI',
investment: new Big('1443.8'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2596,7 +2595,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2611,7 +2610,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('5'),
symbol: 'AMZN',
investment: new Big('10109.95'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-09-01',
fee: new Big(0),
@ -2621,7 +2620,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('20'),
symbol: 'VTI',
investment: new Big('2923.7'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2636,7 +2635,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('5'),
symbol: 'AMZN',
investment: new Big('10109.95'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-09-01',
fee: new Big(0),
@ -2646,7 +2645,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('5'),
symbol: 'VTI',
investment: new Big('652.55'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2661,7 +2660,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('0'),
symbol: 'AMZN',
investment: new Big('0'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-09-01',
fee: new Big(0),
@ -2671,7 +2670,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('5'),
symbol: 'VTI',
investment: new Big('652.55'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2686,7 +2685,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('0'),
symbol: 'AMZN',
investment: new Big('0'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-09-01',
fee: new Big(0),
@ -2696,7 +2695,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('15'),
symbol: 'VTI',
investment: new Big('2684.05'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),
@ -2711,7 +2710,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('0'),
symbol: 'AMZN',
investment: new Big('0'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-09-01',
fee: new Big(0),
@ -2721,7 +2720,7 @@ const transactionPointsBuyAndSell: TransactionPoint[] = [
quantity: new Big('25'),
symbol: 'VTI',
investment: new Big('4460.95'),
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
firstBuyDate: '2019-02-01',
fee: new Big(0),

@ -2,7 +2,6 @@ import { OrderType } from '@ghostfolio/api/models/order-type';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper';
import { TimelinePosition } from '@ghostfolio/common/interfaces';
import { Currency, DataSource } from '@prisma/client';
import Big from 'big.js';
import {
addDays,
@ -35,7 +34,7 @@ export class PortfolioCalculator {
public constructor(
private currentRateService: CurrentRateService,
private currency: Currency
private currency: string
) {}
public computeTransactionPoints(orders: PortfolioOrder[]) {
@ -157,7 +156,7 @@ export class PortfolioCalculator {
let firstIndex = this.transactionPoints.length;
const dates = [];
const dataGatheringItems: IDataGatheringItem[] = [];
const currencies: { [symbol: string]: Currency } = {};
const currencies: { [symbol: string]: string } = {};
dates.push(resetHours(start));
for (const item of this.transactionPoints[firstIndex - 1].items) {
@ -521,7 +520,7 @@ export class PortfolioCalculator {
[date: string]: { [symbol: string]: Big };
} = {};
if (j >= 0) {
const currencies: { [name: string]: Currency } = {};
const currencies: { [name: string]: string } = {};
const dataGatheringItems: IDataGatheringItem[] = [];
for (const item of this.transactionPoints[j].items) {

@ -39,15 +39,9 @@ import type {
} from '@ghostfolio/common/types';
import { Inject, Injectable } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import {
AssetClass,
Currency,
DataSource,
Type as TypeOfOrder
} from '@prisma/client';
import { AssetClass, DataSource, Type as TypeOfOrder } from '@prisma/client';
import Big from 'big.js';
import {
differenceInDays,
endOfToday,
format,
isAfter,
@ -59,7 +53,7 @@ import {
subDays,
subYears
} from 'date-fns';
import { isEmpty, isNumber } from 'lodash';
import { isEmpty } from 'lodash';
import {
HistoricalDataItem,
@ -775,7 +769,7 @@ export class PortfolioService {
assetClass: AssetClass.CASH,
assetSubClass: AssetClass.CASH,
countries: [],
currency: Currency.CHF,
currency: 'CHF',
grossPerformance: 0,
grossPerformancePercent: 0,
investment: cashValue.toNumber(),
@ -865,7 +859,7 @@ export class PortfolioService {
private async getAccounts(
orders: OrderWithAccount[],
portfolioItemsNow: { [p: string]: TimelinePosition },
userCurrency: Currency,
userCurrency: string,
userId: string
) {
const accounts: PortfolioDetails['accounts'] = {};
@ -938,7 +932,7 @@ export class PortfolioService {
private getTotalByType(
orders: OrderWithAccount[],
currency: Currency,
currency: string,
type: TypeOfOrder
) {
return orders

@ -1,7 +1,6 @@
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { Rule } from '@ghostfolio/api/models/rule';
import { Injectable } from '@nestjs/common';
import { Currency } from '@prisma/client';
@Injectable()
export class RulesService {
@ -9,7 +8,7 @@ export class RulesService {
public async evaluate<T extends RuleSettings>(
aRules: Rule<T>[],
aUserSettings: { baseCurrency: Currency }
aUserSettings: { baseCurrency: string }
) {
return aRules
.filter((rule) => {

@ -1,7 +1,7 @@
import { Currency, DataSource } from '@prisma/client';
import { DataSource } from '@prisma/client';
export interface LookupItem {
currency: Currency;
currency: string;
dataSource: DataSource;
name: string;
symbol: string;

@ -1,7 +1,7 @@
import { Currency, DataSource } from '@prisma/client';
import { DataSource } from '@prisma/client';
export interface SymbolItem {
currency: Currency;
currency: string;
dataSource: DataSource;
marketPrice: number;
}

@ -2,7 +2,7 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { Injectable } from '@nestjs/common';
import { Currency, DataSource } from '@prisma/client';
import { DataSource } from '@prisma/client';
import { LookupItem } from './interfaces/lookup-item.interface';
import { SymbolItem } from './interfaces/symbol-item.interface';
@ -20,8 +20,8 @@ export class SymbolService {
if (dataGatheringItem.dataSource && marketPrice) {
return {
currency,
marketPrice,
currency: <Currency>(<unknown>currency),
dataSource: dataGatheringItem.dataSource
};
}

@ -1,7 +1,7 @@
import { Currency, ViewMode } from '@prisma/client';
import { ViewMode } from '@prisma/client';
export interface UserSettingsParams {
currency?: Currency;
currency?: string;
userId: string;
viewMode?: ViewMode;
}

@ -1,9 +1,9 @@
import { Currency, ViewMode } from '@prisma/client';
import { ViewMode } from '@prisma/client';
import { IsString } from 'class-validator';
export class UpdateUserSettingsDto {
@IsString()
baseCurrency: Currency;
baseCurrency: string;
@IsString()
viewMode: ViewMode;

@ -1,12 +1,12 @@
import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service';
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { locale } from '@ghostfolio/common/config';
import { baseCurrency, locale } from '@ghostfolio/common/config';
import { User as IUser, UserWithSettings } from '@ghostfolio/common/interfaces';
import { getPermissions, permissions } from '@ghostfolio/common/permissions';
import { SubscriptionType } from '@ghostfolio/common/types/subscription.type';
import { Injectable } from '@nestjs/common';
import { Currency, Prisma, Provider, User, ViewMode } from '@prisma/client';
import { Prisma, Provider, User, ViewMode } from '@prisma/client';
import { UserSettingsParams } from './interfaces/user-settings-params.interface';
import { UserSettings } from './interfaces/user-settings.interface';
@ -15,7 +15,7 @@ const crypto = require('crypto');
@Injectable()
export class UserService {
public static DEFAULT_CURRENCY = Currency.USD;
public static DEFAULT_CURRENCY = 'USD';
public constructor(
private readonly configurationService: ConfigurationService,
@ -144,9 +144,15 @@ export class UserService {
...data,
Account: {
create: {
currency: baseCurrency,
isDefault: true,
name: 'Default Account'
}
},
Settings: {
create: {
currency: baseCurrency
}
}
}
});

@ -1,5 +1,3 @@
import { Currency } from '@prisma/client';
export interface UserSettings {
baseCurrency: Currency;
baseCurrency: string;
}

@ -1,4 +1,4 @@
import { Account, Currency, SymbolProfile } from '@prisma/client';
import { Account, SymbolProfile } from '@prisma/client';
import { v4 as uuidv4 } from 'uuid';
import { IOrder } from '../services/interfaces/interfaces';
@ -6,7 +6,7 @@ import { OrderType } from './order-type';
export class Order {
private account: Account;
private currency: Currency;
private currency: string;
private fee: number;
private date: string;
private id: string;

@ -3,7 +3,6 @@ import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.in
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { groupBy } from '@ghostfolio/common/helper';
import { TimelinePosition } from '@ghostfolio/common/interfaces';
import { Currency } from '@prisma/client';
import { EvaluationResult } from './interfaces/evaluation-result.interface';
import { RuleInterface } from './interfaces/rule.interface';
@ -29,7 +28,7 @@ export abstract class Rule<T extends RuleSettings> implements RuleInterface<T> {
public groupCurrentPositionsByAttribute(
positions: TimelinePosition[],
attribute: keyof TimelinePosition,
baseCurrency: Currency
baseCurrency: string
) {
return Array.from(groupBy(attribute, positions).entries()).map(
([attributeValue, objs]) => ({

@ -2,8 +2,6 @@ import { CurrentPositions } from '@ghostfolio/api/app/portfolio/interfaces/curre
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { Currency } from '@prisma/client';
import { Rule } from '../../rule';
@ -69,5 +67,5 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Setti
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
baseCurrency: string;
}

@ -1,7 +1,6 @@
import { CurrentPositions } from '@ghostfolio/api/app/portfolio/interfaces/current-positions.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { Currency } from '@prisma/client';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule';
@ -69,5 +68,5 @@ export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule<Setti
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
baseCurrency: string;
}

@ -1,7 +1,6 @@
import { CurrentPositions } from '@ghostfolio/api/app/portfolio/interfaces/current-positions.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { Currency } from '@prisma/client';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule';
@ -69,6 +68,6 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
baseCurrency: string;
threshold: number;
}

@ -1,7 +1,6 @@
import { CurrentPositions } from '@ghostfolio/api/app/portfolio/interfaces/current-positions.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { Currency } from '@prisma/client';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule';
@ -69,6 +68,6 @@ export class CurrencyClusterRiskInitialInvestment extends Rule<Settings> {
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
baseCurrency: string;
threshold: number;
}

@ -1,6 +1,5 @@
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { Currency } from '@prisma/client';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule';
@ -46,6 +45,6 @@ export class FeeRatioInitialInvestment extends Rule<Settings> {
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
baseCurrency: string;
threshold: number;
}

@ -4,8 +4,15 @@ import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-
import { PrismaModule } from '@ghostfolio/api/services/prisma.module';
import { Module } from '@nestjs/common';
import { ExchangeRateDataModule } from './exchange-rate-data.module';
@Module({
imports: [ConfigurationModule, DataProviderModule, PrismaModule],
imports: [
ConfigurationModule,
DataProviderModule,
ExchangeRateDataModule,
PrismaModule
],
providers: [DataGatheringService],
exports: [DataGatheringService]
})

@ -1,6 +1,5 @@
import {
benchmarks,
currencyPairs,
ghostfolioFearAndGreedIndexSymbol
} from '@ghostfolio/common/config';
import { DATE_FORMAT, getUtc, resetHours } from '@ghostfolio/common/helper';
@ -19,6 +18,7 @@ import {
import { ConfigurationService } from './configuration.service';
import { DataProviderService } from './data-provider/data-provider.service';
import { GhostfolioScraperApiService } from './data-provider/ghostfolio-scraper-api/ghostfolio-scraper-api.service';
import { ExchangeRateDataService } from './exchange-rate-data.service';
import { IDataGatheringItem } from './interfaces/interfaces';
import { PrismaService } from './prisma.service';
@ -27,6 +27,7 @@ export class DataGatheringService {
public constructor(
private readonly configurationService: ConfigurationService,
private readonly dataProviderService: DataProviderService,
private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly ghostfolioScraperApi: GhostfolioScraperApiService,
private readonly prismaService: PrismaService
) {}
@ -230,6 +231,8 @@ export class DataGatheringService {
}
}
await this.exchangeRateDataService.initialize();
if (hasError) {
throw '';
}
@ -316,15 +319,15 @@ export class DataGatheringService {
};
});
const currencyPairsToGather = currencyPairs.map(
({ dataSource, symbol }) => {
const currencyPairsToGather = this.exchangeRateDataService
.getCurrencyPairs()
.map(({ dataSource, symbol }) => {
return {
dataSource,
symbol,
date: startDate
};
}
);
});
const customSymbolsToGather =
await this.ghostfolioScraperApi.getCustomSymbolsToGather(startDate);
@ -343,15 +346,15 @@ export class DataGatheringService {
const customSymbolsToGather =
await this.ghostfolioScraperApi.getCustomSymbolsToGather(startDate);
const currencyPairsToGather = currencyPairs.map(
({ dataSource, symbol }) => {
const currencyPairsToGather = this.exchangeRateDataService
.getCurrencyPairs()
.map(({ dataSource, symbol }) => {
return {
dataSource,
symbol,
date: startDate
};
}
);
});
const symbolProfilesToGather =
await this.prismaService.symbolProfile.findMany({

@ -1,7 +1,5 @@
import { Currency } from '@prisma/client';
export interface ScraperConfig {
currency: Currency;
currency: string;
selector: string;
symbol: string;
url: string;

@ -1,19 +1,9 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
import {
DATE_FORMAT,
isCrypto,
isCurrency,
parseCurrency
} from '@ghostfolio/common/helper';
import { DATE_FORMAT, isCrypto, isCurrency } from '@ghostfolio/common/helper';
import { Granularity } from '@ghostfolio/common/types';
import { Injectable } from '@nestjs/common';
import {
AssetClass,
AssetSubClass,
Currency,
DataSource
} from '@prisma/client';
import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
import * as bent from 'bent';
import Big from 'big.js';
import { countries } from 'countries-list';
@ -68,7 +58,7 @@ export class YahooFinanceService implements DataProviderInterface {
response[symbol] = {
assetClass,
assetSubClass,
currency: parseCurrency(value.price?.currency),
currency: value.price?.currency,
dataSource: DataSource.YAHOO,
exchange: this.parseExchange(value.price?.exchangeName),
marketState:
@ -81,7 +71,7 @@ export class YahooFinanceService implements DataProviderInterface {
if (value.price?.currency === 'GBp') {
// Convert GBp (pence) to GBP
response[symbol].currency = Currency.GBP;
response[symbol].currency = 'GBP';
response[symbol].marketPrice = new Big(
value.price?.regularMarketPrice ?? 0
)
@ -200,7 +190,7 @@ export class YahooFinanceService implements DataProviderInterface {
.filter(({ quoteType, symbol }) => {
if (quoteType === 'CRYPTOCURRENCY') {
// Only allow cryptocurrencies in USD
return symbol.includes(Currency.USD);
return symbol.includes('USD');
}
return true;

@ -2,8 +2,10 @@ import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { Module } from '@nestjs/common';
import { PrismaModule } from './prisma.module';
@Module({
imports: [DataProviderModule],
imports: [DataProviderModule, PrismaModule],
providers: [ExchangeRateDataService],
exports: [ExchangeRateDataService]
})

@ -1,27 +1,45 @@
import { currencyPairs } from '@ghostfolio/common/config';
import { baseCurrency } from '@ghostfolio/common/config';
import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
import { Injectable } from '@nestjs/common';
import { Currency, DataSource } from '@prisma/client';
import { DataSource } from '@prisma/client';
import { format } from 'date-fns';
import { isEmpty, isNumber } from 'lodash';
import { isEmpty, isNumber, uniq } from 'lodash';
import { DataProviderService } from './data-provider/data-provider.service';
import { IDataGatheringItem } from './interfaces/interfaces';
import { PrismaService } from './prisma.service';
@Injectable()
export class ExchangeRateDataService {
private currencies: string[] = [];
private currencyPairs: IDataGatheringItem[] = [];
private exchangeRates: { [currencyPair: string]: number } = {};
public constructor(private dataProviderService: DataProviderService) {
public constructor(
private readonly dataProviderService: DataProviderService,
private readonly prismaService: PrismaService
) {
this.initialize();
}
public getCurrencies() {
return this.currencies?.length > 0 ? this.currencies : [baseCurrency];
}
public getCurrencyPairs() {
return this.currencyPairs;
}
public async initialize() {
this.currencies = await this.prepareCurrencies();
this.currencyPairs = [];
this.exchangeRates = {};
for (const { currency1, currency2, dataSource } of currencyPairs) {
for (const {
currency1,
currency2,
dataSource
} of this.prepareCurrencyPairs(this.currencies)) {
this.addCurrencyPairs({ currency1, currency2, dataSource });
}
@ -77,8 +95,8 @@ export class ExchangeRateDataService {
if (!this.exchangeRates[symbol]) {
// Not found, calculate indirectly via USD
this.exchangeRates[symbol] =
resultExtended[`${currency1}${Currency.USD}`]?.[date]?.marketPrice *
resultExtended[`${Currency.USD}${currency2}`]?.[date]?.marketPrice;
resultExtended[`${currency1}${'USD'}`]?.[date]?.marketPrice *
resultExtended[`${'USD'}${currency2}`]?.[date]?.marketPrice;
// Calculate the opposite direction
this.exchangeRates[`${currency2}${currency1}`] =
@ -89,10 +107,14 @@ export class ExchangeRateDataService {
public toCurrency(
aValue: number,
aFromCurrency: Currency,
aToCurrency: Currency
aFromCurrency: string,
aToCurrency: string
) {
if (isNaN(this.exchangeRates[`${Currency.USD}${Currency.CHF}`])) {
const hasNaN = Object.values(this.exchangeRates).some((exchangeRate) => {
return isNaN(exchangeRate);
});
if (hasNaN) {
// Reinitialize if data is not loaded correctly
this.initialize();
}
@ -104,8 +126,8 @@ export class ExchangeRateDataService {
factor = this.exchangeRates[`${aFromCurrency}${aToCurrency}`];
} else {
// Calculate indirectly via USD
const factor1 = this.exchangeRates[`${aFromCurrency}${Currency.USD}`];
const factor2 = this.exchangeRates[`${Currency.USD}${aToCurrency}`];
const factor1 = this.exchangeRates[`${aFromCurrency}${'USD'}`];
const factor2 = this.exchangeRates[`${'USD'}${aToCurrency}`];
factor = factor1 * factor2;
@ -129,8 +151,8 @@ export class ExchangeRateDataService {
currency2,
dataSource
}: {
currency1: Currency;
currency2: Currency;
currency1: string;
currency2: string;
dataSource: DataSource;
}) {
this.currencyPairs.push({
@ -142,4 +164,49 @@ export class ExchangeRateDataService {
symbol: `${currency2}${currency1}`
});
}
private async prepareCurrencies(): Promise<string[]> {
const currencies: string[] = [];
const settings = await this.prismaService.settings.findMany({
distinct: ['currency'],
orderBy: [{ currency: 'asc' }],
select: { currency: true }
});
settings.forEach((settingsItem) => {
if (settingsItem.currency) {
currencies.push(settingsItem.currency);
}
});
const symbolProfiles = await this.prismaService.symbolProfile.findMany({
distinct: ['currency'],
orderBy: [{ currency: 'asc' }],
select: { currency: true }
});
symbolProfiles.forEach((symbolProfile) => {
if (symbolProfile.currency) {
currencies.push(symbolProfile.currency);
}
});
return uniq(currencies).sort();
}
private prepareCurrencyPairs(aCurrencies: string[]) {
return aCurrencies
.filter((currency) => {
return currency !== baseCurrency;
})
.map((currency) => {
return {
currency1: baseCurrency,
currency2: currency,
dataSource: DataSource.YAHOO,
symbol: `${baseCurrency}${currency}`
};
});
}
}

@ -2,7 +2,6 @@ import {
Account,
AssetClass,
AssetSubClass,
Currency,
DataSource,
SymbolProfile
} from '@prisma/client';
@ -17,7 +16,7 @@ export const MarketState = {
export interface IOrder {
account: Account;
currency: Currency;
currency: string;
date: string;
fee: number;
id?: string;
@ -38,7 +37,7 @@ export interface IDataProviderResponse {
assetClass?: AssetClass;
assetSubClass?: AssetSubClass;
countries?: { code: string; weight: number }[];
currency: Currency;
currency: string;
dataSource: DataSource;
exchange?: string;
marketChange?: number;

@ -1,17 +1,12 @@
import { Country } from '@ghostfolio/common/interfaces/country.interface';
import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
import {
AssetClass,
AssetSubClass,
Currency,
DataSource
} from '@prisma/client';
import { AssetClass, AssetSubClass, DataSource } from '@prisma/client';
export interface EnhancedSymbolProfile {
assetClass: AssetClass;
assetSubClass: AssetSubClass;
createdAt: Date;
currency: Currency | null;
currency: string | null;
dataSource: DataSource;
id: string;
name: string | null;

@ -8,7 +8,6 @@ import {
ViewChild
} from '@angular/core';
import { PortfolioPerformance } from '@ghostfolio/common/interfaces';
import { Currency } from '@prisma/client';
import { CountUp } from 'countup.js';
import { isNumber } from 'lodash';
@ -19,7 +18,7 @@ import { isNumber } from 'lodash';
styleUrls: ['./portfolio-performance.component.scss']
})
export class PortfolioPerformanceComponent implements OnChanges, OnInit {
@Input() baseCurrency: Currency;
@Input() baseCurrency: string;
@Input() isLoading: boolean;
@Input() locale: string;
@Input() performance: PortfolioPerformance;

@ -6,7 +6,6 @@ import {
OnInit
} from '@angular/core';
import { PortfolioSummary } from '@ghostfolio/common/interfaces';
import { Currency } from '@prisma/client';
import { formatDistanceToNow } from 'date-fns';
@Component({
@ -16,7 +15,7 @@ import { formatDistanceToNow } from 'date-fns';
styleUrls: ['./portfolio-summary.component.scss']
})
export class PortfolioSummaryComponent implements OnChanges, OnInit {
@Input() baseCurrency: Currency;
@Input() baseCurrency: string;
@Input() isLoading: boolean;
@Input() locale: string;
@Input() summary: PortfolioSummary;

@ -7,7 +7,6 @@ import {
OnDestroy,
OnInit
} from '@angular/core';
import { Currency } from '@prisma/client';
import svgMap from 'svgmap';
@Component({
@ -17,7 +16,7 @@ import svgMap from 'svgmap';
styleUrls: ['./world-map-chart.component.scss']
})
export class WorldMapChartComponent implements OnChanges, OnDestroy, OnInit {
@Input() baseCurrency: Currency;
@Input() baseCurrency: string;
@Input() countries: { [code: string]: { name: string; value: number } };
public isLoading = true;

@ -15,7 +15,6 @@ import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { DEFAULT_DATE_FORMAT, baseCurrency } from '@ghostfolio/common/config';
import { Access, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { Currency } from '@prisma/client';
import { StripeService } from 'ngx-stripe';
import { EMPTY, Subject } from 'rxjs';
import { catchError, switchMap, takeUntil } from 'rxjs/operators';
@ -33,7 +32,7 @@ export class AccountPageComponent implements OnDestroy, OnInit {
public baseCurrency = baseCurrency;
public coupon: number;
public couponId: string;
public currencies: Currency[] = [];
public currencies: string[] = [];
public defaultDateFormat = DEFAULT_DATE_FORMAT;
public hasPermissionForSubscription: boolean;
public hasPermissionToUpdateViewMode: boolean;

@ -1,12 +1,10 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Inject,
OnDestroy
} from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Currency } from '@prisma/client';
import { Subject } from 'rxjs';
import { DataService } from '../../../services/data.service';
@ -20,13 +18,12 @@ import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces';
templateUrl: 'create-or-update-account-dialog.html'
})
export class CreateOrUpdateAccountDialog implements OnDestroy {
public currencies: Currency[] = [];
public currencies: string[] = [];
public platforms: { id: string; name: string }[];
private unsubscribeSubject = new Subject<void>();
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService,
public dialogRef: MatDialogRef<CreateOrUpdateAccountDialog>,
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateAccountDialogParams

@ -11,7 +11,6 @@ import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { DataService } from '@ghostfolio/client/services/data.service';
import { Currency } from '@prisma/client';
import { isString } from 'lodash';
import { EMPTY, Observable, Subject } from 'rxjs';
import {
@ -35,7 +34,7 @@ import { CreateOrUpdateTransactionDialogParams } from './interfaces/interfaces';
export class CreateOrUpdateTransactionDialog implements OnDestroy {
@ViewChild('autocomplete') autocomplete;
public currencies: Currency[] = [];
public currencies: string[] = [];
public currentMarketPrice = null;
public filteredLookupItems: LookupItem[];
public filteredLookupItemsObservable: Observable<LookupItem[]>;

@ -1,31 +1,12 @@
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { Currency } from '@prisma/client';
import { DataSource } from '@prisma/client';
export const baseCurrency = Currency.USD;
export const baseCurrency = 'USD';
export const benchmarks: Partial<IDataGatheringItem>[] = [
{ dataSource: DataSource.YAHOO, symbol: 'VOO' }
];
export const currencyPairs: Partial<
IDataGatheringItem & {
currency1: Currency;
currency2: Currency;
}
>[] = (Object.keys(Currency) as Array<keyof typeof Currency>)
.filter((currency) => {
return currency !== Currency.USD;
})
.map((currency) => {
return {
currency1: Currency.USD,
currency2: Currency[currency],
dataSource: DataSource.YAHOO,
symbol: `${Currency.USD}${Currency[currency]}`
};
});
export const ghostfolioScraperApiSymbolPrefix = '_GF_';
export const ghostfolioCashSymbol = `${ghostfolioScraperApiSymbolPrefix}CASH`;
export const ghostfolioFearAndGreedIndexSymbol = `${ghostfolioScraperApiSymbolPrefix}FEAR_AND_GREED_INDEX`;

@ -1,4 +1,3 @@
import { Currency } from '@prisma/client';
import { getDate, getMonth, getYear, parse, subDays } from 'date-fns';
import { ghostfolioScraperApiSymbolPrefix } from './config';
@ -87,9 +86,9 @@ export function isCrypto(aSymbol = '') {
export function isCurrency(aSymbol = '') {
return (
(aSymbol.includes(Currency.CHF) ||
aSymbol.includes(Currency.EUR) ||
aSymbol.includes(Currency.USD)) &&
(aSymbol.includes('CHF') ||
aSymbol.includes('EUR') ||
aSymbol.includes('USD')) &&
aSymbol.length >= 6
);
}
@ -102,10 +101,6 @@ export function isRakutenRapidApiSymbol(aSymbol = '') {
return aSymbol === 'GF.FEAR_AND_GREED_INDEX';
}
export function parseCurrency(aCurrency: string): Currency {
return Currency[aCurrency];
}
export function resetHours(aDate: Date) {
const year = getYear(aDate);
const month = getMonth(aDate);

@ -1,10 +1,8 @@
import { Currency } from '@prisma/client';
import { Statistics } from './statistics.interface';
import { Subscription } from './subscription.interface';
export interface InfoItem {
currencies: Currency[];
currencies: string[];
demoAuthToken: string;
globalPermissions: string[];
lastDataGathering?: Date;

@ -1,5 +1,5 @@
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
import { AssetClass, AssetSubClass, Currency } from '@prisma/client';
import { AssetClass, AssetSubClass } from '@prisma/client';
import { Country } from './country.interface';
import { Sector } from './sector.interface';
@ -10,7 +10,7 @@ export interface PortfolioPosition {
assetClass?: AssetClass;
assetSubClass?: AssetSubClass | 'CASH';
countries: Country[];
currency: Currency;
currency: string;
exchange?: string;
grossPerformance: number;
grossPerformancePercent: number;

@ -1,10 +1,10 @@
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
import { AssetClass, Currency } from '@prisma/client';
import { AssetClass } from '@prisma/client';
export interface Position {
assetClass: AssetClass;
averagePrice: number;
currency: Currency;
currency: string;
firstBuyDate: string;
grossPerformance?: number;
grossPerformancePercentage?: number;

@ -1,9 +1,9 @@
import { Currency, DataSource } from '@prisma/client';
import { DataSource } from '@prisma/client';
import Big from 'big.js';
export interface TimelinePosition {
averagePrice: Big;
currency: Currency;
currency: string;
dataSource: DataSource;
firstBuyDate: string;
grossPerformance: Big;

@ -1,7 +1,7 @@
import { Currency, ViewMode } from '@prisma/client';
import { ViewMode } from '@prisma/client';
export interface UserSettings {
baseCurrency?: Currency;
baseCurrency?: string;
isRestrictedView?: boolean;
locale: string;
viewMode?: ViewMode;

@ -11,7 +11,6 @@ import {
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
import { getTextColor } from '@ghostfolio/common/helper';
import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { Currency } from '@prisma/client';
import { Tooltip } from 'chart.js';
import { LinearScale } from 'chart.js';
import { ArcElement } from 'chart.js';
@ -29,7 +28,7 @@ import * as Color from 'color';
export class PortfolioProportionChartComponent
implements AfterViewInit, OnChanges, OnDestroy
{
@Input() baseCurrency: Currency;
@Input() baseCurrency: string;
@Input() isInPercent = false;
@Input() keys: string[] = [];
@Input() locale = '';

@ -0,0 +1,14 @@
-- AlterTable
ALTER TABLE "Account" ALTER COLUMN "currency" TYPE TEXT;
-- AlterTable
ALTER TABLE "Order" ALTER COLUMN "currency" TYPE TEXT;
-- AlterTable
ALTER TABLE "Settings" ALTER COLUMN "currency" TYPE TEXT;
-- AlterTable
ALTER TABLE "SymbolProfile" ALTER COLUMN "currency" TYPE TEXT;
-- DropEnum
DROP TYPE "Currency" CASCADE;

@ -28,7 +28,7 @@ model Account {
accountType AccountType @default(SECURITIES)
balance Float @default(0)
createdAt DateTime @default(now())
currency Currency @default(USD)
currency String?
id String @default(uuid())
isDefault Boolean @default(false)
name String?
@ -77,7 +77,7 @@ model Order {
accountId String?
accountUserId String?
createdAt DateTime @default(now())
currency Currency?
currency String?
dataSource DataSource
date DateTime
fee Float
@ -109,7 +109,7 @@ model Property {
}
model Settings {
currency Currency?
currency String?
settings Json?
updatedAt DateTime @updatedAt
viewMode ViewMode?
@ -122,7 +122,7 @@ model SymbolProfile {
assetSubClass AssetSubClass?
countries Json?
createdAt DateTime @default(now())
currency Currency?
currency String?
dataSource DataSource
id String @id @default(uuid())
name String?
@ -182,13 +182,6 @@ enum AssetSubClass {
STOCK
}
enum Currency {
CHF
EUR
GBP
USD
}
enum DataSource {
ALPHA_VANTAGE
GHOSTFOLIO

@ -1,6 +1,5 @@
import {
AccountType,
Currency,
DataSource,
PrismaClient,
Role,
@ -88,7 +87,7 @@ async function main() {
{
accountType: AccountType.SECURITIES,
balance: 0,
currency: Currency.USD,
currency: 'USD',
id: 'f4425b66-9ba9-4ac4-93d7-fdf9a145e8cb',
isDefault: true,
name: 'Default Account'
@ -112,7 +111,7 @@ async function main() {
{
accountType: AccountType.SECURITIES,
balance: 0,
currency: Currency.USD,
currency: 'USD',
id: 'd804de69-0429-42dc-b6ca-b308fd7dd926',
name: 'Coinbase Account',
platformId: platformCoinbase.id
@ -120,7 +119,7 @@ async function main() {
{
accountType: AccountType.SECURITIES,
balance: 0,
currency: Currency.EUR,
currency: 'EUR',
id: '65cfb79d-b6c7-4591-9d46-73426bc62094',
name: 'DEGIRO Account',
platformId: platformDegiro.id
@ -128,7 +127,7 @@ async function main() {
{
accountType: AccountType.SECURITIES,
balance: 0,
currency: Currency.USD,
currency: 'USD',
id: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
isDefault: true,
name: 'Interactive Brokers Account',
@ -201,7 +200,7 @@ async function main() {
{
accountId: '65cfb79d-b6c7-4591-9d46-73426bc62094',
accountUserId: userDemo.id,
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2017, 0, 3, 0, 0, 0)),
fee: 30,
@ -216,7 +215,7 @@ async function main() {
{
accountId: 'd804de69-0429-42dc-b6ca-b308fd7dd926',
accountUserId: userDemo.id,
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2017, 7, 16, 0, 0, 0)),
fee: 29.9,
@ -231,7 +230,7 @@ async function main() {
{
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id,
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2018, 9, 1, 0, 0, 0)),
fee: 80.79,
@ -246,7 +245,7 @@ async function main() {
{
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id,
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2019, 2, 1, 0, 0, 0)),
fee: 19.9,
@ -261,7 +260,7 @@ async function main() {
{
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id,
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2019, 8, 3, 0, 0, 0)),
fee: 19.9,
@ -276,7 +275,7 @@ async function main() {
{
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id,
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2020, 2, 2, 0, 0, 0)),
fee: 19.9,
@ -291,7 +290,7 @@ async function main() {
{
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id,
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2020, 8, 1, 0, 0, 0)),
fee: 19.9,
@ -306,7 +305,7 @@ async function main() {
{
accountId: '480269ce-e12a-4fd1-ac88-c4b0ff3f899c',
accountUserId: userDemo.id,
currency: Currency.USD,
currency: 'USD',
dataSource: DataSource.YAHOO,
date: new Date(Date.UTC(2020, 2, 1, 0, 0, 0)),
fee: 19.9,

Loading…
Cancel
Save