Feature/remove deprecated environment variable base currency (#2255)
* Remove the deprecated environment variable BASE_CURRENCY * Update changelog
This commit is contained in:
parent
48ba8f936b
commit
bc33e5f147
@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- **Breaking Change**: Removed the deprecated environment variable `BASE_CURRENCY`
|
||||||
- Improved the validation in the activities import
|
- Improved the validation in the activities import
|
||||||
- Deactivated _Internet Identity_ as a social login provider for the account registration
|
- Deactivated _Internet Identity_ as a social login provider for the account registration
|
||||||
- Improved the language localization for German (`de`)
|
- Improved the language localization for German (`de`)
|
||||||
|
@ -6,7 +6,10 @@ import { MarketDataService } from '@ghostfolio/api/services/market-data/market-d
|
|||||||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
||||||
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
||||||
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
||||||
import { PROPERTY_CURRENCIES } from '@ghostfolio/common/config';
|
import {
|
||||||
|
DEFAULT_CURRENCY,
|
||||||
|
PROPERTY_CURRENCIES
|
||||||
|
} from '@ghostfolio/common/config';
|
||||||
import {
|
import {
|
||||||
AdminData,
|
AdminData,
|
||||||
AdminMarketData,
|
AdminMarketData,
|
||||||
@ -23,8 +26,6 @@ import { groupBy } from 'lodash';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminService {
|
export class AdminService {
|
||||||
private baseCurrency: string;
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly configurationService: ConfigurationService,
|
private readonly configurationService: ConfigurationService,
|
||||||
private readonly dataProviderService: DataProviderService,
|
private readonly dataProviderService: DataProviderService,
|
||||||
@ -34,9 +35,7 @@ export class AdminService {
|
|||||||
private readonly propertyService: PropertyService,
|
private readonly propertyService: PropertyService,
|
||||||
private readonly subscriptionService: SubscriptionService,
|
private readonly subscriptionService: SubscriptionService,
|
||||||
private readonly symbolProfileService: SymbolProfileService
|
private readonly symbolProfileService: SymbolProfileService
|
||||||
) {
|
) {}
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
}
|
|
||||||
|
|
||||||
public async addAssetProfile({
|
public async addAssetProfile({
|
||||||
dataSource,
|
dataSource,
|
||||||
@ -80,15 +79,15 @@ export class AdminService {
|
|||||||
exchangeRates: this.exchangeRateDataService
|
exchangeRates: this.exchangeRateDataService
|
||||||
.getCurrencies()
|
.getCurrencies()
|
||||||
.filter((currency) => {
|
.filter((currency) => {
|
||||||
return currency !== this.baseCurrency;
|
return currency !== DEFAULT_CURRENCY;
|
||||||
})
|
})
|
||||||
.map((currency) => {
|
.map((currency) => {
|
||||||
return {
|
return {
|
||||||
label1: this.baseCurrency,
|
label1: DEFAULT_CURRENCY,
|
||||||
label2: currency,
|
label2: currency,
|
||||||
value: this.exchangeRateDataService.toCurrency(
|
value: this.exchangeRateDataService.toCurrency(
|
||||||
1,
|
1,
|
||||||
this.baseCurrency,
|
DEFAULT_CURRENCY,
|
||||||
currency
|
currency
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
|
|||||||
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
||||||
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
|
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
|
||||||
import {
|
import {
|
||||||
|
DEFAULT_CURRENCY,
|
||||||
PROPERTY_BETTER_UPTIME_MONITOR_ID,
|
PROPERTY_BETTER_UPTIME_MONITOR_ID,
|
||||||
PROPERTY_COUNTRIES_OF_SUBSCRIBERS,
|
PROPERTY_COUNTRIES_OF_SUBSCRIBERS,
|
||||||
PROPERTY_DEMO_USER_ID,
|
PROPERTY_DEMO_USER_ID,
|
||||||
@ -139,7 +140,7 @@ export class InfoService {
|
|||||||
subscriptions,
|
subscriptions,
|
||||||
systemMessage,
|
systemMessage,
|
||||||
tags,
|
tags,
|
||||||
baseCurrency: this.configurationService.get('BASE_CURRENCY'),
|
baseCurrency: DEFAULT_CURRENCY,
|
||||||
currencies: this.exchangeRateDataService.getCurrencies()
|
currencies: this.exchangeRateDataService.getCurrencies()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,6 @@ describe('CurrentRateService', () => {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
marketDataService = new MarketDataService(null);
|
marketDataService = new MarketDataService(null);
|
||||||
|
@ -10,7 +10,10 @@ import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interc
|
|||||||
import { ApiService } from '@ghostfolio/api/services/api/api.service';
|
import { ApiService } from '@ghostfolio/api/services/api/api.service';
|
||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
||||||
import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config';
|
import {
|
||||||
|
DEFAULT_CURRENCY,
|
||||||
|
HEADER_KEY_IMPERSONATION
|
||||||
|
} from '@ghostfolio/common/config';
|
||||||
import {
|
import {
|
||||||
PortfolioDetails,
|
PortfolioDetails,
|
||||||
PortfolioDividends,
|
PortfolioDividends,
|
||||||
@ -47,8 +50,6 @@ import { PortfolioService } from './portfolio.service';
|
|||||||
|
|
||||||
@Controller('portfolio')
|
@Controller('portfolio')
|
||||||
export class PortfolioController {
|
export class PortfolioController {
|
||||||
private baseCurrency: string;
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly accessService: AccessService,
|
private readonly accessService: AccessService,
|
||||||
private readonly apiService: ApiService,
|
private readonly apiService: ApiService,
|
||||||
@ -57,9 +58,7 @@ export class PortfolioController {
|
|||||||
private readonly portfolioService: PortfolioService,
|
private readonly portfolioService: PortfolioService,
|
||||||
@Inject(REQUEST) private readonly request: RequestWithUser,
|
@Inject(REQUEST) private readonly request: RequestWithUser,
|
||||||
private readonly userService: UserService
|
private readonly userService: UserService
|
||||||
) {
|
) {}
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get('details')
|
@Get('details')
|
||||||
@UseGuards(AuthGuard('jwt'))
|
@UseGuards(AuthGuard('jwt'))
|
||||||
@ -442,8 +441,7 @@ export class PortfolioController {
|
|||||||
return this.exchangeRateDataService.toCurrency(
|
return this.exchangeRateDataService.toCurrency(
|
||||||
portfolioPosition.quantity * portfolioPosition.marketPrice,
|
portfolioPosition.quantity * portfolioPosition.marketPrice,
|
||||||
portfolioPosition.currency,
|
portfolioPosition.currency,
|
||||||
this.request.user?.Settings?.settings.baseCurrency ??
|
this.request.user?.Settings?.settings.baseCurrency ?? DEFAULT_CURRENCY
|
||||||
this.baseCurrency
|
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.reduce((a, b) => a + b, 0);
|
.reduce((a, b) => a + b, 0);
|
||||||
|
@ -11,12 +11,12 @@ import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/ac
|
|||||||
import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-current-investment';
|
import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-current-investment';
|
||||||
import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment';
|
import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment';
|
||||||
import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment';
|
import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment';
|
||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
|
||||||
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
||||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
|
||||||
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service';
|
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service';
|
||||||
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
||||||
import {
|
import {
|
||||||
|
DEFAULT_CURRENCY,
|
||||||
EMERGENCY_FUND_TAG_ID,
|
EMERGENCY_FUND_TAG_ID,
|
||||||
MAX_CHART_ITEMS,
|
MAX_CHART_ITEMS,
|
||||||
UNKNOWN_KEY
|
UNKNOWN_KEY
|
||||||
@ -90,11 +90,8 @@ const europeMarkets = require('../../assets/countries/europe-markets.json');
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PortfolioService {
|
export class PortfolioService {
|
||||||
private baseCurrency: string;
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly accountService: AccountService,
|
private readonly accountService: AccountService,
|
||||||
private readonly configurationService: ConfigurationService,
|
|
||||||
private readonly currentRateService: CurrentRateService,
|
private readonly currentRateService: CurrentRateService,
|
||||||
private readonly dataProviderService: DataProviderService,
|
private readonly dataProviderService: DataProviderService,
|
||||||
private readonly exchangeRateDataService: ExchangeRateDataService,
|
private readonly exchangeRateDataService: ExchangeRateDataService,
|
||||||
@ -104,9 +101,7 @@ export class PortfolioService {
|
|||||||
private readonly rulesService: RulesService,
|
private readonly rulesService: RulesService,
|
||||||
private readonly symbolProfileService: SymbolProfileService,
|
private readonly symbolProfileService: SymbolProfileService,
|
||||||
private readonly userService: UserService
|
private readonly userService: UserService
|
||||||
) {
|
) {}
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getAccounts({
|
public async getAccounts({
|
||||||
filters,
|
filters,
|
||||||
@ -1768,7 +1763,7 @@ export class PortfolioService {
|
|||||||
portfolioOrders: PortfolioOrder[];
|
portfolioOrders: PortfolioOrder[];
|
||||||
}> {
|
}> {
|
||||||
const userCurrency =
|
const userCurrency =
|
||||||
this.request.user?.Settings?.settings.baseCurrency ?? this.baseCurrency;
|
this.request.user?.Settings?.settings.baseCurrency ?? DEFAULT_CURRENCY;
|
||||||
|
|
||||||
const orders = await this.orderService.getOrders({
|
const orders = await this.orderService.getOrders({
|
||||||
filters,
|
filters,
|
||||||
@ -1990,7 +1985,7 @@ export class PortfolioService {
|
|||||||
return (
|
return (
|
||||||
aUser.Settings?.settings.baseCurrency ??
|
aUser.Settings?.settings.baseCurrency ??
|
||||||
this.request.user?.Settings?.settings.baseCurrency ??
|
this.request.user?.Settings?.settings.baseCurrency ??
|
||||||
this.baseCurrency
|
DEFAULT_CURRENCY
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,17 +25,13 @@ const crypto = require('crypto');
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
private baseCurrency: string;
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly configurationService: ConfigurationService,
|
private readonly configurationService: ConfigurationService,
|
||||||
private readonly prismaService: PrismaService,
|
private readonly prismaService: PrismaService,
|
||||||
private readonly propertyService: PropertyService,
|
private readonly propertyService: PropertyService,
|
||||||
private readonly subscriptionService: SubscriptionService,
|
private readonly subscriptionService: SubscriptionService,
|
||||||
private readonly tagService: TagService
|
private readonly tagService: TagService
|
||||||
) {
|
) {}
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
}
|
|
||||||
|
|
||||||
public async count(args?: Prisma.UserCountArgs) {
|
public async count(args?: Prisma.UserCountArgs) {
|
||||||
return this.prismaService.user.count(args);
|
return this.prismaService.user.count(args);
|
||||||
@ -271,7 +267,7 @@ export class UserService {
|
|||||||
...data,
|
...data,
|
||||||
Account: {
|
Account: {
|
||||||
create: {
|
create: {
|
||||||
currency: this.baseCurrency,
|
currency: DEFAULT_CURRENCY,
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
name: 'Default Account'
|
name: 'Default Account'
|
||||||
}
|
}
|
||||||
@ -279,7 +275,7 @@ export class UserService {
|
|||||||
Settings: {
|
Settings: {
|
||||||
create: {
|
create: {
|
||||||
settings: {
|
settings: {
|
||||||
currency: this.baseCurrency
|
currency: DEFAULT_CURRENCY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ async function bootstrap() {
|
|||||||
|
|
||||||
app.use(HtmlTemplateMiddleware);
|
app.use(HtmlTemplateMiddleware);
|
||||||
|
|
||||||
const BASE_CURRENCY = configService.get<string>('BASE_CURRENCY');
|
|
||||||
const HOST = configService.get<string>('HOST') || '0.0.0.0';
|
const HOST = configService.get<string>('HOST') || '0.0.0.0';
|
||||||
const PORT = configService.get<number>('PORT') || 3333;
|
const PORT = configService.get<number>('PORT') || 3333;
|
||||||
|
|
||||||
@ -63,15 +62,6 @@ async function bootstrap() {
|
|||||||
logLogo();
|
logLogo();
|
||||||
Logger.log(`Listening at http://${HOST}:${PORT}`);
|
Logger.log(`Listening at http://${HOST}:${PORT}`);
|
||||||
Logger.log('');
|
Logger.log('');
|
||||||
|
|
||||||
if (BASE_CURRENCY) {
|
|
||||||
Logger.warn(
|
|
||||||
`The environment variable "BASE_CURRENCY" is deprecated and will be removed in Ghostfolio 2.0.`
|
|
||||||
);
|
|
||||||
Logger.warn(
|
|
||||||
'Please use the currency converter in the activity dialog instead.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Environment } from '@ghostfolio/api/services/interfaces/environment.interface';
|
import { Environment } from '@ghostfolio/api/services/interfaces/environment.interface';
|
||||||
import { DEFAULT_CURRENCY, DEFAULT_ROOT_URL } from '@ghostfolio/common/config';
|
import { DEFAULT_ROOT_URL } from '@ghostfolio/common/config';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { DataSource } from '@prisma/client';
|
import { DataSource } from '@prisma/client';
|
||||||
import { bool, cleanEnv, host, json, num, port, str } from 'envalid';
|
import { bool, cleanEnv, host, json, num, port, str } from 'envalid';
|
||||||
@ -12,10 +12,6 @@ export class ConfigurationService {
|
|||||||
this.environmentConfiguration = cleanEnv(process.env, {
|
this.environmentConfiguration = cleanEnv(process.env, {
|
||||||
ACCESS_TOKEN_SALT: str(),
|
ACCESS_TOKEN_SALT: str(),
|
||||||
ALPHA_VANTAGE_API_KEY: str({ default: '' }),
|
ALPHA_VANTAGE_API_KEY: str({ default: '' }),
|
||||||
BASE_CURRENCY: str({
|
|
||||||
choices: ['AUD', 'CAD', 'CNY', 'EUR', 'GBP', 'JPY', 'RUB', 'USD'],
|
|
||||||
default: DEFAULT_CURRENCY
|
|
||||||
}),
|
|
||||||
BETTER_UPTIME_API_KEY: str({ default: '' }),
|
BETTER_UPTIME_API_KEY: str({ default: '' }),
|
||||||
CACHE_QUOTES_TTL: num({ default: 1 }),
|
CACHE_QUOTES_TTL: num({ default: 1 }),
|
||||||
CACHE_TTL: num({ default: 1 }),
|
CACHE_TTL: num({ default: 1 }),
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
|
||||||
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
||||||
import {
|
import {
|
||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
|
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
|
||||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||||
import { DataProviderInfo } from '@ghostfolio/common/interfaces';
|
import { DataProviderInfo } from '@ghostfolio/common/interfaces';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
@ -20,14 +20,9 @@ import got from 'got';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CoinGeckoService implements DataProviderInterface {
|
export class CoinGeckoService implements DataProviderInterface {
|
||||||
private baseCurrency: string;
|
|
||||||
private readonly URL = 'https://api.coingecko.com/api/v3';
|
private readonly URL = 'https://api.coingecko.com/api/v3';
|
||||||
|
|
||||||
public constructor(
|
public constructor() {}
|
||||||
private readonly configurationService: ConfigurationService
|
|
||||||
) {
|
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
}
|
|
||||||
|
|
||||||
public canHandle(symbol: string) {
|
public canHandle(symbol: string) {
|
||||||
return true;
|
return true;
|
||||||
@ -39,7 +34,7 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
const response: Partial<SymbolProfile> = {
|
const response: Partial<SymbolProfile> = {
|
||||||
assetClass: AssetClass.CASH,
|
assetClass: AssetClass.CASH,
|
||||||
assetSubClass: AssetSubClass.CRYPTOCURRENCY,
|
assetSubClass: AssetSubClass.CRYPTOCURRENCY,
|
||||||
currency: this.baseCurrency,
|
currency: DEFAULT_CURRENCY,
|
||||||
dataSource: this.getName(),
|
dataSource: this.getName(),
|
||||||
symbol: aSymbol
|
symbol: aSymbol
|
||||||
};
|
};
|
||||||
@ -81,7 +76,7 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
const { prices } = await got(
|
const { prices } = await got(
|
||||||
`${
|
`${
|
||||||
this.URL
|
this.URL
|
||||||
}/coins/${aSymbol}/market_chart/range?vs_currency=${this.baseCurrency.toLowerCase()}&from=${getUnixTime(
|
}/coins/${aSymbol}/market_chart/range?vs_currency=${DEFAULT_CURRENCY.toLowerCase()}&from=${getUnixTime(
|
||||||
from
|
from
|
||||||
)}&to=${getUnixTime(to)}`
|
)}&to=${getUnixTime(to)}`
|
||||||
).json<any>();
|
).json<any>();
|
||||||
@ -130,16 +125,16 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
const response = await got(
|
const response = await got(
|
||||||
`${this.URL}/simple/price?ids=${aSymbols.join(
|
`${this.URL}/simple/price?ids=${aSymbols.join(
|
||||||
','
|
','
|
||||||
)}&vs_currencies=${this.baseCurrency.toLowerCase()}`
|
)}&vs_currencies=${DEFAULT_CURRENCY.toLowerCase()}`
|
||||||
).json<any>();
|
).json<any>();
|
||||||
|
|
||||||
for (const symbol in response) {
|
for (const symbol in response) {
|
||||||
if (Object.prototype.hasOwnProperty.call(response, symbol)) {
|
if (Object.prototype.hasOwnProperty.call(response, symbol)) {
|
||||||
results[symbol] = {
|
results[symbol] = {
|
||||||
currency: this.baseCurrency,
|
currency: DEFAULT_CURRENCY,
|
||||||
dataProviderInfo: this.getDataProviderInfo(),
|
dataProviderInfo: this.getDataProviderInfo(),
|
||||||
dataSource: DataSource.COINGECKO,
|
dataSource: DataSource.COINGECKO,
|
||||||
marketPrice: response[symbol][this.baseCurrency.toLowerCase()],
|
marketPrice: response[symbol][DEFAULT_CURRENCY.toLowerCase()],
|
||||||
marketState: 'open'
|
marketState: 'open'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -175,7 +170,7 @@ export class CoinGeckoService implements DataProviderInterface {
|
|||||||
symbol,
|
symbol,
|
||||||
assetClass: AssetClass.CASH,
|
assetClass: AssetClass.CASH,
|
||||||
assetSubClass: AssetSubClass.CRYPTOCURRENCY,
|
assetSubClass: AssetSubClass.CRYPTOCURRENCY,
|
||||||
currency: this.baseCurrency,
|
currency: DEFAULT_CURRENCY,
|
||||||
dataSource: this.getName()
|
dataSource: this.getName()
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
|
||||||
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
||||||
|
|
||||||
import { YahooFinanceDataEnhancerService } from './yahoo-finance.service';
|
import { YahooFinanceDataEnhancerService } from './yahoo-finance.service';
|
||||||
@ -26,16 +25,13 @@ jest.mock(
|
|||||||
);
|
);
|
||||||
|
|
||||||
describe('YahooFinanceDataEnhancerService', () => {
|
describe('YahooFinanceDataEnhancerService', () => {
|
||||||
let configurationService: ConfigurationService;
|
|
||||||
let cryptocurrencyService: CryptocurrencyService;
|
let cryptocurrencyService: CryptocurrencyService;
|
||||||
let yahooFinanceDataEnhancerService: YahooFinanceDataEnhancerService;
|
let yahooFinanceDataEnhancerService: YahooFinanceDataEnhancerService;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
configurationService = new ConfigurationService();
|
|
||||||
cryptocurrencyService = new CryptocurrencyService();
|
cryptocurrencyService = new CryptocurrencyService();
|
||||||
|
|
||||||
yahooFinanceDataEnhancerService = new YahooFinanceDataEnhancerService(
|
yahooFinanceDataEnhancerService = new YahooFinanceDataEnhancerService(
|
||||||
configurationService,
|
|
||||||
cryptocurrencyService
|
cryptocurrencyService
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
|
||||||
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
||||||
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
|
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
|
||||||
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
|
import { DEFAULT_CURRENCY, UNKNOWN_KEY } from '@ghostfolio/common/config';
|
||||||
import { isCurrency } from '@ghostfolio/common/helper';
|
import { isCurrency } from '@ghostfolio/common/helper';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
@ -17,23 +16,18 @@ import type { Price } from 'yahoo-finance2/dist/esm/src/modules/quoteSummary-ifa
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
||||||
private baseCurrency: string;
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly configurationService: ConfigurationService,
|
|
||||||
private readonly cryptocurrencyService: CryptocurrencyService
|
private readonly cryptocurrencyService: CryptocurrencyService
|
||||||
) {
|
) {}
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
}
|
|
||||||
|
|
||||||
public convertFromYahooFinanceSymbol(aYahooFinanceSymbol: string) {
|
public convertFromYahooFinanceSymbol(aYahooFinanceSymbol: string) {
|
||||||
let symbol = aYahooFinanceSymbol.replace(
|
let symbol = aYahooFinanceSymbol.replace(
|
||||||
new RegExp(`-${this.baseCurrency}$`),
|
new RegExp(`-${DEFAULT_CURRENCY}$`),
|
||||||
this.baseCurrency
|
DEFAULT_CURRENCY
|
||||||
);
|
);
|
||||||
|
|
||||||
if (symbol.includes('=X') && !symbol.includes(this.baseCurrency)) {
|
if (symbol.includes('=X') && !symbol.includes(DEFAULT_CURRENCY)) {
|
||||||
symbol = `${this.baseCurrency}${symbol}`;
|
symbol = `${DEFAULT_CURRENCY}${symbol}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return symbol.replace('=X', '');
|
return symbol.replace('=X', '');
|
||||||
@ -48,21 +42,18 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
|||||||
*/
|
*/
|
||||||
public convertToYahooFinanceSymbol(aSymbol: string) {
|
public convertToYahooFinanceSymbol(aSymbol: string) {
|
||||||
if (
|
if (
|
||||||
aSymbol.includes(this.baseCurrency) &&
|
aSymbol.includes(DEFAULT_CURRENCY) &&
|
||||||
aSymbol.length > this.baseCurrency.length
|
aSymbol.length > DEFAULT_CURRENCY.length
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
isCurrency(
|
isCurrency(
|
||||||
aSymbol.substring(0, aSymbol.length - this.baseCurrency.length)
|
aSymbol.substring(0, aSymbol.length - DEFAULT_CURRENCY.length)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return `${aSymbol}=X`;
|
return `${aSymbol}=X`;
|
||||||
} else if (
|
} else if (
|
||||||
this.cryptocurrencyService.isCryptocurrency(
|
this.cryptocurrencyService.isCryptocurrency(
|
||||||
aSymbol.replace(
|
aSymbol.replace(new RegExp(`-${DEFAULT_CURRENCY}$`), DEFAULT_CURRENCY)
|
||||||
new RegExp(`-${this.baseCurrency}$`),
|
|
||||||
this.baseCurrency
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// Add a dash before the last three characters
|
// Add a dash before the last three characters
|
||||||
@ -70,8 +61,8 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
|||||||
// DOGEUSD -> DOGE-USD
|
// DOGEUSD -> DOGE-USD
|
||||||
// SOL1USD -> SOL1-USD
|
// SOL1USD -> SOL1-USD
|
||||||
return aSymbol.replace(
|
return aSymbol.replace(
|
||||||
new RegExp(`-?${this.baseCurrency}$`),
|
new RegExp(`-?${DEFAULT_CURRENCY}$`),
|
||||||
`-${this.baseCurrency}`
|
`-${DEFAULT_CURRENCY}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,10 @@ import {
|
|||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config';
|
import {
|
||||||
|
DEFAULT_CURRENCY,
|
||||||
|
DEFAULT_REQUEST_TIMEOUT
|
||||||
|
} from '@ghostfolio/common/config';
|
||||||
import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
@ -22,14 +25,12 @@ import got from 'got';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class EodHistoricalDataService implements DataProviderInterface {
|
export class EodHistoricalDataService implements DataProviderInterface {
|
||||||
private apiKey: string;
|
private apiKey: string;
|
||||||
private baseCurrency: string;
|
|
||||||
private readonly URL = 'https://eodhistoricaldata.com/api';
|
private readonly URL = 'https://eodhistoricaldata.com/api';
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly configurationService: ConfigurationService
|
private readonly configurationService: ConfigurationService
|
||||||
) {
|
) {
|
||||||
this.apiKey = this.configurationService.get('EOD_HISTORICAL_DATA_API_KEY');
|
this.apiKey = this.configurationService.get('EOD_HISTORICAL_DATA_API_KEY');
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public canHandle(symbol: string) {
|
public canHandle(symbol: string) {
|
||||||
@ -175,7 +176,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
})?.currency;
|
})?.currency;
|
||||||
|
|
||||||
result[this.convertFromEodSymbol(code)] = {
|
result[this.convertFromEodSymbol(code)] = {
|
||||||
currency: currency ?? this.baseCurrency,
|
currency: currency ?? DEFAULT_CURRENCY,
|
||||||
dataSource: DataSource.EOD_HISTORICAL_DATA,
|
dataSource: DataSource.EOD_HISTORICAL_DATA,
|
||||||
marketPrice: close,
|
marketPrice: close,
|
||||||
marketState: isToday(new Date(timestamp * 1000)) ? 'open' : 'closed'
|
marketState: isToday(new Date(timestamp * 1000)) ? 'open' : 'closed'
|
||||||
@ -186,24 +187,24 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response[`${this.baseCurrency}GBP`]) {
|
if (response[`${DEFAULT_CURRENCY}GBP`]) {
|
||||||
response[`${this.baseCurrency}GBp`] = {
|
response[`${DEFAULT_CURRENCY}GBp`] = {
|
||||||
...response[`${this.baseCurrency}GBP`],
|
...response[`${DEFAULT_CURRENCY}GBP`],
|
||||||
currency: `${this.baseCurrency}GBp`,
|
currency: `${DEFAULT_CURRENCY}GBp`,
|
||||||
marketPrice: this.getConvertedValue({
|
marketPrice: this.getConvertedValue({
|
||||||
symbol: `${this.baseCurrency}GBp`,
|
symbol: `${DEFAULT_CURRENCY}GBp`,
|
||||||
value: response[`${this.baseCurrency}GBP`].marketPrice
|
value: response[`${DEFAULT_CURRENCY}GBP`].marketPrice
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response[`${this.baseCurrency}ILS`]) {
|
if (response[`${DEFAULT_CURRENCY}ILS`]) {
|
||||||
response[`${this.baseCurrency}ILA`] = {
|
response[`${DEFAULT_CURRENCY}ILA`] = {
|
||||||
...response[`${this.baseCurrency}ILS`],
|
...response[`${DEFAULT_CURRENCY}ILS`],
|
||||||
currency: `${this.baseCurrency}ILA`,
|
currency: `${DEFAULT_CURRENCY}ILA`,
|
||||||
marketPrice: this.getConvertedValue({
|
marketPrice: this.getConvertedValue({
|
||||||
symbol: `${this.baseCurrency}ILA`,
|
symbol: `${DEFAULT_CURRENCY}ILA`,
|
||||||
value: response[`${this.baseCurrency}ILS`].marketPrice
|
value: response[`${DEFAULT_CURRENCY}ILS`].marketPrice
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -272,7 +273,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
if (symbol.endsWith('.FOREX')) {
|
if (symbol.endsWith('.FOREX')) {
|
||||||
symbol = symbol.replace('GBX', 'GBp');
|
symbol = symbol.replace('GBX', 'GBp');
|
||||||
symbol = symbol.replace('.FOREX', '');
|
symbol = symbol.replace('.FOREX', '');
|
||||||
symbol = `${this.baseCurrency}${symbol}`;
|
symbol = `${DEFAULT_CURRENCY}${symbol}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return symbol;
|
return symbol;
|
||||||
@ -285,17 +286,17 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
*/
|
*/
|
||||||
private convertToEodSymbol(aSymbol: string) {
|
private convertToEodSymbol(aSymbol: string) {
|
||||||
if (
|
if (
|
||||||
aSymbol.startsWith(this.baseCurrency) &&
|
aSymbol.startsWith(DEFAULT_CURRENCY) &&
|
||||||
aSymbol.length > this.baseCurrency.length
|
aSymbol.length > DEFAULT_CURRENCY.length
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
isCurrency(
|
isCurrency(
|
||||||
aSymbol.substring(0, aSymbol.length - this.baseCurrency.length)
|
aSymbol.substring(0, aSymbol.length - DEFAULT_CURRENCY.length)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return `${aSymbol
|
return `${aSymbol
|
||||||
.replace('GBp', 'GBX')
|
.replace('GBp', 'GBX')
|
||||||
.replace(this.baseCurrency, '')}.FOREX`;
|
.replace(DEFAULT_CURRENCY, '')}.FOREX`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,10 +310,10 @@ export class EodHistoricalDataService implements DataProviderInterface {
|
|||||||
symbol: string;
|
symbol: string;
|
||||||
value: number;
|
value: number;
|
||||||
}) {
|
}) {
|
||||||
if (symbol === `${this.baseCurrency}GBp`) {
|
if (symbol === `${DEFAULT_CURRENCY}GBp`) {
|
||||||
// Convert GPB to GBp (pence)
|
// Convert GPB to GBp (pence)
|
||||||
return new Big(value).mul(100).toNumber();
|
return new Big(value).mul(100).toNumber();
|
||||||
} else if (symbol === `${this.baseCurrency}ILA`) {
|
} else if (symbol === `${DEFAULT_CURRENCY}ILA`) {
|
||||||
// Convert ILS to ILA
|
// Convert ILS to ILA
|
||||||
return new Big(value).mul(100).toNumber();
|
return new Big(value).mul(100).toNumber();
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
|
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
|
||||||
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
||||||
import { DataProviderInfo } from '@ghostfolio/common/interfaces';
|
import { DataProviderInfo } from '@ghostfolio/common/interfaces';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
@ -16,7 +17,6 @@ import got from 'got';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class FinancialModelingPrepService implements DataProviderInterface {
|
export class FinancialModelingPrepService implements DataProviderInterface {
|
||||||
private apiKey: string;
|
private apiKey: string;
|
||||||
private baseCurrency: string;
|
|
||||||
private readonly URL = 'https://financialmodelingprep.com/api/v3';
|
private readonly URL = 'https://financialmodelingprep.com/api/v3';
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
@ -25,7 +25,6 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
|||||||
this.apiKey = this.configurationService.get(
|
this.apiKey = this.configurationService.get(
|
||||||
'FINANCIAL_MODELING_PREP_API_KEY'
|
'FINANCIAL_MODELING_PREP_API_KEY'
|
||||||
);
|
);
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public canHandle(symbol: string) {
|
public canHandle(symbol: string) {
|
||||||
@ -117,7 +116,7 @@ export class FinancialModelingPrepService implements DataProviderInterface {
|
|||||||
|
|
||||||
for (const { price, symbol } of response) {
|
for (const { price, symbol } of response) {
|
||||||
results[symbol] = {
|
results[symbol] = {
|
||||||
currency: this.baseCurrency,
|
currency: DEFAULT_CURRENCY,
|
||||||
dataProviderInfo: this.getDataProviderInfo(),
|
dataProviderInfo: this.getDataProviderInfo(),
|
||||||
dataSource: DataSource.FINANCIAL_MODELING_PREP,
|
dataSource: DataSource.FINANCIAL_MODELING_PREP,
|
||||||
marketPrice: price,
|
marketPrice: price,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
|
||||||
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
||||||
import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service';
|
import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service';
|
||||||
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
|
||||||
@ -7,6 +6,7 @@ import {
|
|||||||
IDataProviderHistoricalResponse,
|
IDataProviderHistoricalResponse,
|
||||||
IDataProviderResponse
|
IDataProviderResponse
|
||||||
} from '@ghostfolio/api/services/interfaces/interfaces';
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
|
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config';
|
||||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
@ -18,15 +18,10 @@ import { Quote } from 'yahoo-finance2/dist/esm/src/modules/quote';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class YahooFinanceService implements DataProviderInterface {
|
export class YahooFinanceService implements DataProviderInterface {
|
||||||
private baseCurrency: string;
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly configurationService: ConfigurationService,
|
|
||||||
private readonly cryptocurrencyService: CryptocurrencyService,
|
private readonly cryptocurrencyService: CryptocurrencyService,
|
||||||
private readonly yahooFinanceDataEnhancerService: YahooFinanceDataEnhancerService
|
private readonly yahooFinanceDataEnhancerService: YahooFinanceDataEnhancerService
|
||||||
) {
|
) {}
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
}
|
|
||||||
|
|
||||||
public canHandle(symbol: string) {
|
public canHandle(symbol: string) {
|
||||||
return true;
|
return true;
|
||||||
@ -212,50 +207,50 @@ export class YahooFinanceService implements DataProviderInterface {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
symbol === `${this.baseCurrency}GBP` &&
|
symbol === `${DEFAULT_CURRENCY}GBP` &&
|
||||||
yahooFinanceSymbols.includes(`${this.baseCurrency}GBp=X`)
|
yahooFinanceSymbols.includes(`${DEFAULT_CURRENCY}GBp=X`)
|
||||||
) {
|
) {
|
||||||
// Convert GPB to GBp (pence)
|
// Convert GPB to GBp (pence)
|
||||||
response[`${this.baseCurrency}GBp`] = {
|
response[`${DEFAULT_CURRENCY}GBp`] = {
|
||||||
...response[symbol],
|
...response[symbol],
|
||||||
currency: 'GBp',
|
currency: 'GBp',
|
||||||
marketPrice: this.getConvertedValue({
|
marketPrice: this.getConvertedValue({
|
||||||
symbol: `${this.baseCurrency}GBp`,
|
symbol: `${DEFAULT_CURRENCY}GBp`,
|
||||||
value: response[symbol].marketPrice
|
value: response[symbol].marketPrice
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
} else if (
|
} else if (
|
||||||
symbol === `${this.baseCurrency}ILS` &&
|
symbol === `${DEFAULT_CURRENCY}ILS` &&
|
||||||
yahooFinanceSymbols.includes(`${this.baseCurrency}ILA=X`)
|
yahooFinanceSymbols.includes(`${DEFAULT_CURRENCY}ILA=X`)
|
||||||
) {
|
) {
|
||||||
// Convert ILS to ILA
|
// Convert ILS to ILA
|
||||||
response[`${this.baseCurrency}ILA`] = {
|
response[`${DEFAULT_CURRENCY}ILA`] = {
|
||||||
...response[symbol],
|
...response[symbol],
|
||||||
currency: 'ILA',
|
currency: 'ILA',
|
||||||
marketPrice: this.getConvertedValue({
|
marketPrice: this.getConvertedValue({
|
||||||
symbol: `${this.baseCurrency}ILA`,
|
symbol: `${DEFAULT_CURRENCY}ILA`,
|
||||||
value: response[symbol].marketPrice
|
value: response[symbol].marketPrice
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
} else if (
|
} else if (
|
||||||
symbol === `${this.baseCurrency}ZAR` &&
|
symbol === `${DEFAULT_CURRENCY}ZAR` &&
|
||||||
yahooFinanceSymbols.includes(`${this.baseCurrency}ZAc=X`)
|
yahooFinanceSymbols.includes(`${DEFAULT_CURRENCY}ZAc=X`)
|
||||||
) {
|
) {
|
||||||
// Convert ZAR to ZAc (cents)
|
// Convert ZAR to ZAc (cents)
|
||||||
response[`${this.baseCurrency}ZAc`] = {
|
response[`${DEFAULT_CURRENCY}ZAc`] = {
|
||||||
...response[symbol],
|
...response[symbol],
|
||||||
currency: 'ZAc',
|
currency: 'ZAc',
|
||||||
marketPrice: this.getConvertedValue({
|
marketPrice: this.getConvertedValue({
|
||||||
symbol: `${this.baseCurrency}ZAc`,
|
symbol: `${DEFAULT_CURRENCY}ZAc`,
|
||||||
value: response[symbol].marketPrice
|
value: response[symbol].marketPrice
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (yahooFinanceSymbols.includes(`${this.baseCurrency}USX=X`)) {
|
if (yahooFinanceSymbols.includes(`${DEFAULT_CURRENCY}USX=X`)) {
|
||||||
// Convert USD to USX (cent)
|
// Convert USD to USX (cent)
|
||||||
response[`${this.baseCurrency}USX`] = {
|
response[`${DEFAULT_CURRENCY}USX`] = {
|
||||||
currency: 'USX',
|
currency: 'USX',
|
||||||
dataSource: this.getName(),
|
dataSource: this.getName(),
|
||||||
marketPrice: new Big(1).mul(100).toNumber(),
|
marketPrice: new Big(1).mul(100).toNumber(),
|
||||||
@ -303,8 +298,8 @@ export class YahooFinanceService implements DataProviderInterface {
|
|||||||
(quoteType === 'CRYPTOCURRENCY' &&
|
(quoteType === 'CRYPTOCURRENCY' &&
|
||||||
this.cryptocurrencyService.isCryptocurrency(
|
this.cryptocurrencyService.isCryptocurrency(
|
||||||
symbol.replace(
|
symbol.replace(
|
||||||
new RegExp(`-${this.baseCurrency}$`),
|
new RegExp(`-${DEFAULT_CURRENCY}$`),
|
||||||
this.baseCurrency
|
DEFAULT_CURRENCY
|
||||||
)
|
)
|
||||||
)) ||
|
)) ||
|
||||||
quoteTypes.includes(quoteType)
|
quoteTypes.includes(quoteType)
|
||||||
@ -314,7 +309,7 @@ export class YahooFinanceService implements DataProviderInterface {
|
|||||||
if (quoteType === 'CRYPTOCURRENCY') {
|
if (quoteType === 'CRYPTOCURRENCY') {
|
||||||
// Only allow cryptocurrencies in base currency to avoid having redundancy in the database.
|
// Only allow cryptocurrencies in base currency to avoid having redundancy in the database.
|
||||||
// Transactions need to be converted manually to the base currency before
|
// Transactions need to be converted manually to the base currency before
|
||||||
return symbol.includes(this.baseCurrency);
|
return symbol.includes(DEFAULT_CURRENCY);
|
||||||
} else if (quoteType === 'FUTURE') {
|
} else if (quoteType === 'FUTURE') {
|
||||||
// Allow GC=F, but not MGC=F
|
// Allow GC=F, but not MGC=F
|
||||||
return symbol.length === 4;
|
return symbol.length === 4;
|
||||||
@ -373,13 +368,13 @@ export class YahooFinanceService implements DataProviderInterface {
|
|||||||
symbol: string;
|
symbol: string;
|
||||||
value: number;
|
value: number;
|
||||||
}) {
|
}) {
|
||||||
if (symbol === `${this.baseCurrency}GBp`) {
|
if (symbol === `${DEFAULT_CURRENCY}GBp`) {
|
||||||
// Convert GPB to GBp (pence)
|
// Convert GPB to GBp (pence)
|
||||||
return new Big(value).mul(100).toNumber();
|
return new Big(value).mul(100).toNumber();
|
||||||
} else if (symbol === `${this.baseCurrency}ILA`) {
|
} else if (symbol === `${DEFAULT_CURRENCY}ILA`) {
|
||||||
// Convert ILS to ILA
|
// Convert ILS to ILA
|
||||||
return new Big(value).mul(100).toNumber();
|
return new Big(value).mul(100).toNumber();
|
||||||
} else if (symbol === `${this.baseCurrency}ZAc`) {
|
} else if (symbol === `${DEFAULT_CURRENCY}ZAc`) {
|
||||||
// Convert ZAR to ZAc (cents)
|
// Convert ZAR to ZAc (cents)
|
||||||
return new Big(value).mul(100).toNumber();
|
return new Big(value).mul(100).toNumber();
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
|
||||||
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
||||||
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
|
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
||||||
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
||||||
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
||||||
import { PROPERTY_CURRENCIES } from '@ghostfolio/common/config';
|
import {
|
||||||
|
DEFAULT_CURRENCY,
|
||||||
|
PROPERTY_CURRENCIES
|
||||||
|
} from '@ghostfolio/common/config';
|
||||||
import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { format, isToday } from 'date-fns';
|
import { format, isToday } from 'date-fns';
|
||||||
@ -12,13 +14,11 @@ import { isNumber, uniq } from 'lodash';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExchangeRateDataService {
|
export class ExchangeRateDataService {
|
||||||
private baseCurrency: string;
|
|
||||||
private currencies: string[] = [];
|
private currencies: string[] = [];
|
||||||
private currencyPairs: IDataGatheringItem[] = [];
|
private currencyPairs: IDataGatheringItem[] = [];
|
||||||
private exchangeRates: { [currencyPair: string]: number } = {};
|
private exchangeRates: { [currencyPair: string]: number } = {};
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly configurationService: ConfigurationService,
|
|
||||||
private readonly dataProviderService: DataProviderService,
|
private readonly dataProviderService: DataProviderService,
|
||||||
private readonly marketDataService: MarketDataService,
|
private readonly marketDataService: MarketDataService,
|
||||||
private readonly prismaService: PrismaService,
|
private readonly prismaService: PrismaService,
|
||||||
@ -26,7 +26,7 @@ export class ExchangeRateDataService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public getCurrencies() {
|
public getCurrencies() {
|
||||||
return this.currencies?.length > 0 ? this.currencies : [this.baseCurrency];
|
return this.currencies?.length > 0 ? this.currencies : [DEFAULT_CURRENCY];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCurrencyPairs() {
|
public getCurrencyPairs() {
|
||||||
@ -43,7 +43,6 @@ export class ExchangeRateDataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async initialize() {
|
public async initialize() {
|
||||||
this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
|
|
||||||
this.currencies = await this.prepareCurrencies();
|
this.currencies = await this.prepareCurrencies();
|
||||||
this.currencyPairs = [];
|
this.currencyPairs = [];
|
||||||
this.exchangeRates = {};
|
this.exchangeRates = {};
|
||||||
@ -113,9 +112,9 @@ export class ExchangeRateDataService {
|
|||||||
if (!this.exchangeRates[symbol]) {
|
if (!this.exchangeRates[symbol]) {
|
||||||
// Not found, calculate indirectly via base currency
|
// Not found, calculate indirectly via base currency
|
||||||
this.exchangeRates[symbol] =
|
this.exchangeRates[symbol] =
|
||||||
resultExtended[`${currency1}${this.baseCurrency}`]?.[date]
|
resultExtended[`${currency1}${DEFAULT_CURRENCY}`]?.[date]
|
||||||
?.marketPrice *
|
?.marketPrice *
|
||||||
resultExtended[`${this.baseCurrency}${currency2}`]?.[date]
|
resultExtended[`${DEFAULT_CURRENCY}${currency2}`]?.[date]
|
||||||
?.marketPrice;
|
?.marketPrice;
|
||||||
|
|
||||||
// Calculate the opposite direction
|
// Calculate the opposite direction
|
||||||
@ -144,9 +143,8 @@ export class ExchangeRateDataService {
|
|||||||
} else {
|
} else {
|
||||||
// Calculate indirectly via base currency
|
// Calculate indirectly via base currency
|
||||||
const factor1 =
|
const factor1 =
|
||||||
this.exchangeRates[`${aFromCurrency}${this.baseCurrency}`];
|
this.exchangeRates[`${aFromCurrency}${DEFAULT_CURRENCY}`];
|
||||||
const factor2 =
|
const factor2 = this.exchangeRates[`${DEFAULT_CURRENCY}${aToCurrency}`];
|
||||||
this.exchangeRates[`${this.baseCurrency}${aToCurrency}`];
|
|
||||||
|
|
||||||
factor = factor1 * factor2;
|
factor = factor1 * factor2;
|
||||||
|
|
||||||
@ -204,28 +202,28 @@ export class ExchangeRateDataService {
|
|||||||
let marketPriceBaseCurrencyToCurrency: number;
|
let marketPriceBaseCurrencyToCurrency: number;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.baseCurrency === aFromCurrency) {
|
if (aFromCurrency === DEFAULT_CURRENCY) {
|
||||||
marketPriceBaseCurrencyFromCurrency = 1;
|
marketPriceBaseCurrencyFromCurrency = 1;
|
||||||
} else {
|
} else {
|
||||||
marketPriceBaseCurrencyFromCurrency = (
|
marketPriceBaseCurrencyFromCurrency = (
|
||||||
await this.marketDataService.get({
|
await this.marketDataService.get({
|
||||||
dataSource,
|
dataSource,
|
||||||
date: aDate,
|
date: aDate,
|
||||||
symbol: `${this.baseCurrency}${aFromCurrency}`
|
symbol: `${DEFAULT_CURRENCY}${aFromCurrency}`
|
||||||
})
|
})
|
||||||
)?.marketPrice;
|
)?.marketPrice;
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.baseCurrency === aToCurrency) {
|
if (aToCurrency === DEFAULT_CURRENCY) {
|
||||||
marketPriceBaseCurrencyToCurrency = 1;
|
marketPriceBaseCurrencyToCurrency = 1;
|
||||||
} else {
|
} else {
|
||||||
marketPriceBaseCurrencyToCurrency = (
|
marketPriceBaseCurrencyToCurrency = (
|
||||||
await this.marketDataService.get({
|
await this.marketDataService.get({
|
||||||
dataSource,
|
dataSource,
|
||||||
date: aDate,
|
date: aDate,
|
||||||
symbol: `${this.baseCurrency}${aToCurrency}`
|
symbol: `${DEFAULT_CURRENCY}${aToCurrency}`
|
||||||
})
|
})
|
||||||
)?.marketPrice;
|
)?.marketPrice;
|
||||||
}
|
}
|
||||||
@ -295,14 +293,14 @@ export class ExchangeRateDataService {
|
|||||||
private prepareCurrencyPairs(aCurrencies: string[]) {
|
private prepareCurrencyPairs(aCurrencies: string[]) {
|
||||||
return aCurrencies
|
return aCurrencies
|
||||||
.filter((currency) => {
|
.filter((currency) => {
|
||||||
return currency !== this.baseCurrency;
|
return currency !== DEFAULT_CURRENCY;
|
||||||
})
|
})
|
||||||
.map((currency) => {
|
.map((currency) => {
|
||||||
return {
|
return {
|
||||||
currency1: this.baseCurrency,
|
currency1: DEFAULT_CURRENCY,
|
||||||
currency2: currency,
|
currency2: currency,
|
||||||
dataSource: this.dataProviderService.getDataSourceForExchangeRates(),
|
dataSource: this.dataProviderService.getDataSourceForExchangeRates(),
|
||||||
symbol: `${this.baseCurrency}${currency}`
|
symbol: `${DEFAULT_CURRENCY}${currency}`
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { CleanedEnvAccessors } from 'envalid';
|
|||||||
export interface Environment extends CleanedEnvAccessors {
|
export interface Environment extends CleanedEnvAccessors {
|
||||||
ACCESS_TOKEN_SALT: string;
|
ACCESS_TOKEN_SALT: string;
|
||||||
ALPHA_VANTAGE_API_KEY: string;
|
ALPHA_VANTAGE_API_KEY: string;
|
||||||
BASE_CURRENCY: string;
|
|
||||||
BETTER_UPTIME_API_KEY: string;
|
BETTER_UPTIME_API_KEY: string;
|
||||||
CACHE_QUOTES_TTL: number;
|
CACHE_QUOTES_TTL: number;
|
||||||
CACHE_TTL: number;
|
CACHE_TTL: number;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user