Browse Source

fix: @typescript-eslint/consistent-indexed-object-style

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
pull/3986/head
dw-0 10 months ago
parent
commit
271c6d2c40
  1. 3
      .eslintrc.json
  2. 1
      CHANGELOG.md
  3. 5
      apps/api/src/app/account-balance/account-balance.service.ts
  4. 4
      apps/api/src/app/admin/update-asset-profile.dto.ts
  5. 6
      apps/api/src/app/import/import.service.ts
  6. 6
      apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts
  7. 89
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  8. 52
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts
  9. 6
      apps/api/src/app/portfolio/portfolio.service.ts
  10. 9
      apps/api/src/app/symbol/symbol.service.ts
  11. 2
      apps/api/src/helper/object.helper.ts
  12. 2
      apps/api/src/middlewares/html-template.middleware.ts
  13. 4
      apps/api/src/models/interfaces/portfolio.interface.ts
  14. 9
      apps/api/src/models/rules/account-cluster-risk/current-investment.ts
  15. 30
      apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts
  16. 44
      apps/api/src/services/data-provider/coingecko/coingecko.service.ts
  17. 85
      apps/api/src/services/data-provider/data-provider.service.ts
  18. 49
      apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts
  19. 44
      apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts
  20. 46
      apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts
  21. 30
      apps/api/src/services/data-provider/interfaces/data-provider.interface.ts
  22. 48
      apps/api/src/services/data-provider/manual/manual.service.ts
  23. 35
      apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts
  24. 50
      apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts
  25. 29
      apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts
  26. 2
      apps/api/src/services/i18n/i18n.service.ts
  27. 4
      apps/api/src/services/property/property.service.ts
  28. 2
      apps/api/src/services/queues/data-gathering/data-gathering.service.ts
  29. 6
      apps/api/src/services/symbol-profile/symbol-profile.service.ts
  30. 11
      apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts
  31. 8
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  32. 8
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
  33. 2
      apps/client/src/app/components/world-map-chart/world-map-chart.component.ts
  34. 4
      apps/client/src/app/pages/landing/landing-page.component.ts
  35. 63
      apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts
  36. 31
      apps/client/src/app/pages/public/public-page.component.ts
  37. 8
      libs/common/src/lib/class-transformer.ts
  38. 2
      libs/common/src/lib/interfaces/admin-data.interface.ts
  39. 2
      libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts
  40. 20
      libs/common/src/lib/interfaces/portfolio-details.interface.ts
  41. 2
      libs/common/src/lib/interfaces/portfolio-item.interface.ts
  42. 2
      libs/common/src/lib/interfaces/portfolio-report.interface.ts
  43. 9
      libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts
  44. 2
      libs/common/src/lib/interfaces/scraper-configuration.interface.ts
  45. 38
      libs/common/src/lib/interfaces/symbol-metrics.interface.ts
  46. 4
      libs/common/src/lib/models/timeline-position.ts
  47. 2
      libs/ui/src/lib/assistant/assistant.component.ts
  48. 24
      libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts

3
.eslintrc.json

@ -142,8 +142,7 @@
// The following rules are part of @typescript-eslint/stylistic-type-checked // The following rules are part of @typescript-eslint/stylistic-type-checked
// and can be remove once solved // and can be remove once solved
"@typescript-eslint/prefer-nullish-coalescing": "warn", // TODO: Requires strictNullChecks: true "@typescript-eslint/prefer-nullish-coalescing": "warn" // TODO: Requires strictNullChecks: true
"@typescript-eslint/consistent-indexed-object-style": "warn"
} }
} }
], ],

1
CHANGELOG.md

@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Switched the `consistent-type-assertions` rule from `warn` to `error` in the `eslint` configuration - Switched the `consistent-type-assertions` rule from `warn` to `error` in the `eslint` configuration
- Switched the `prefer-optional-chain` rule from `warn` to `error` in the `eslint` configuration - Switched the `prefer-optional-chain` rule from `warn` to `error` in the `eslint` configuration
- Switched the `consistent-generic-constructors` rule from `warn` to `error` in the `eslint` configuration - Switched the `consistent-generic-constructors` rule from `warn` to `error` in the `eslint` configuration
- Switched the `consistent-indexed-object-style` rule from `warn` to `error` in the `eslint` configuration
### Fixed ### Fixed

5
apps/api/src/app/account-balance/account-balance.service.ts

@ -110,9 +110,8 @@ export class AccountBalanceService {
userId, userId,
withExcludedAccounts: false // TODO withExcludedAccounts: false // TODO
}); });
const accumulatedBalancesByDate: { [date: string]: HistoricalDataItem } = const accumulatedBalancesByDate: Record<string, HistoricalDataItem> = {};
{}; const lastBalancesByAccount: Record<string, Big> = {};
const lastBalancesByAccount: { [accountId: string]: Big } = {};
for (const { accountId, date, valueInBaseCurrency } of balances) { for (const { accountId, date, valueInBaseCurrency } of balances) {
const formattedDate = format(date, DATE_FORMAT); const formattedDate = format(date, DATE_FORMAT);

4
apps/api/src/app/admin/update-asset-profile.dto.ts

@ -45,9 +45,7 @@ export class UpdateAssetProfileDto {
@IsObject() @IsObject()
@IsOptional() @IsOptional()
symbolMapping?: { symbolMapping?: Record<string, string>;
[dataProvider: string]: string;
};
@IsOptional() @IsOptional()
@IsUrl({ @IsUrl({

6
apps/api/src/app/import/import.service.ts

@ -158,7 +158,7 @@ export class ImportService {
maxActivitiesToImport: number; maxActivitiesToImport: number;
user: UserWithSettings; user: UserWithSettings;
}): Promise<Activity[]> { }): Promise<Activity[]> {
const accountIdMapping: { [oldAccountId: string]: string } = {}; const accountIdMapping: Record<string, string> = {};
const userCurrency = user.Settings.settings.baseCurrency; const userCurrency = user.Settings.settings.baseCurrency;
if (!isDryRun && accountsDto?.length) { if (!isDryRun && accountsDto?.length) {
@ -579,9 +579,7 @@ export class ImportService {
throw new Error(`Too many activities (${maxActivitiesToImport} at most)`); throw new Error(`Too many activities (${maxActivitiesToImport} at most)`);
} }
const assetProfiles: { const assetProfiles: Record<string, Partial<SymbolProfile>> = {};
[assetProfileIdentifier: string]: Partial<SymbolProfile>;
} = {};
for (const [ for (const [
index, index,

6
apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts

@ -12,10 +12,8 @@ export class MWRPortfolioCalculator extends PortfolioCalculator {
protected getSymbolMetrics({}: { protected getSymbolMetrics({}: {
end: Date; end: Date;
exchangeRates: { [dateString: string]: number }; exchangeRates: Record<string, number>;
marketSymbolMap: { marketSymbolMap: Record<string, Record<string, Big>>;
[date: string]: { [symbol: string]: Big };
};
start: Date; start: Date;
step?: number; step?: number;
} & AssetProfileIdentifier): SymbolMetrics { } & AssetProfileIdentifier): SymbolMetrics {

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

@ -1,16 +1,3 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service';
import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface';
import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface';
import { TransactionPointSymbol } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point-symbol.interface';
import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface';
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
import { getFactor } from '@ghostfolio/api/helper/portfolio.helper';
import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper';
import { import {
PORTFOLIO_SNAPSHOT_PROCESS_JOB_NAME, PORTFOLIO_SNAPSHOT_PROCESS_JOB_NAME,
@ -51,6 +38,20 @@ import {
} from 'date-fns'; } from 'date-fns';
import { first, isNumber, last, sortBy, sum, uniq, uniqBy } from 'lodash'; import { first, isNumber, last, sortBy, sum, uniq, uniqBy } from 'lodash';
import { getFactor } from '../../../helper/portfolio.helper';
import { LogPerformance } from '../../../interceptors/performance-logging/performance-logging.interceptor';
import { ConfigurationService } from '../../../services/configuration/configuration.service';
import { ExchangeRateDataService } from '../../../services/exchange-rate-data/exchange-rate-data.service';
import { IDataGatheringItem } from '../../../services/interfaces/interfaces';
import { PortfolioSnapshotService } from '../../../services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { Activity } from '../../order/interfaces/activities.interface';
import { RedisCacheService } from '../../redis-cache/redis-cache.service';
import { CurrentRateService } from '../current-rate.service';
import { PortfolioOrder } from '../interfaces/portfolio-order.interface';
import { PortfolioSnapshotValue } from '../interfaces/snapshot-value.interface';
import { TransactionPointSymbol } from '../interfaces/transaction-point-symbol.interface';
import { TransactionPoint } from '../interfaces/transaction-point.interface';
export abstract class PortfolioCalculator { export abstract class PortfolioCalculator {
protected static readonly ENABLE_LOGGING = false; protected static readonly ENABLE_LOGGING = false;
@ -188,7 +189,7 @@ export abstract class PortfolioCalculator {
}; };
} }
const currencies: { [symbol: string]: string } = {}; const currencies: Record<string, string> = {};
const dataGatheringItems: IDataGatheringItem[] = []; const dataGatheringItems: IDataGatheringItem[] = [];
let firstIndex = transactionPoints.length; let firstIndex = transactionPoints.length;
let firstTransactionPoint: TransactionPoint = null; let firstTransactionPoint: TransactionPoint = null;
@ -239,9 +240,7 @@ export abstract class PortfolioCalculator {
this.dataProviderInfos = dataProviderInfos; this.dataProviderInfos = dataProviderInfos;
const marketSymbolMap: { const marketSymbolMap: Record<string, Record<string, Big>> = {};
[date: string]: { [symbol: string]: Big };
} = {};
for (const marketSymbol of marketSymbols) { for (const marketSymbol of marketSymbols) {
const date = format(marketSymbol.date, DATE_FORMAT); const date = format(marketSymbol.date, DATE_FORMAT);
@ -290,8 +289,9 @@ export abstract class PortfolioCalculator {
const errors: ResponseError['errors'] = []; const errors: ResponseError['errors'] = [];
const accumulatedValuesByDate: { const accumulatedValuesByDate: Record<
[date: string]: { string,
{
investmentValueWithCurrencyEffect: Big; investmentValueWithCurrencyEffect: Big;
totalAccountBalanceWithCurrencyEffect: Big; totalAccountBalanceWithCurrencyEffect: Big;
totalCurrentValue: Big; totalCurrentValue: Big;
@ -302,22 +302,23 @@ export abstract class PortfolioCalculator {
totalNetPerformanceValueWithCurrencyEffect: Big; totalNetPerformanceValueWithCurrencyEffect: Big;
totalTimeWeightedInvestmentValue: Big; totalTimeWeightedInvestmentValue: Big;
totalTimeWeightedInvestmentValueWithCurrencyEffect: Big; totalTimeWeightedInvestmentValueWithCurrencyEffect: Big;
}; }
} = {}; > = {};
const valuesBySymbol: { const valuesBySymbol: Record<
[symbol: string]: { string,
currentValues: { [date: string]: Big }; {
currentValuesWithCurrencyEffect: { [date: string]: Big }; currentValues: Record<string, Big>;
investmentValuesAccumulated: { [date: string]: Big }; currentValuesWithCurrencyEffect: Record<string, Big>;
investmentValuesAccumulatedWithCurrencyEffect: { [date: string]: Big }; investmentValuesAccumulated: Record<string, Big>;
investmentValuesWithCurrencyEffect: { [date: string]: Big }; investmentValuesAccumulatedWithCurrencyEffect: Record<string, Big>;
netPerformanceValues: { [date: string]: Big }; investmentValuesWithCurrencyEffect: Record<string, Big>;
netPerformanceValuesWithCurrencyEffect: { [date: string]: Big }; netPerformanceValues: Record<string, Big>;
timeWeightedInvestmentValues: { [date: string]: Big }; netPerformanceValuesWithCurrencyEffect: Record<string, Big>;
timeWeightedInvestmentValuesWithCurrencyEffect: { [date: string]: Big }; timeWeightedInvestmentValues: Record<string, Big>;
}; timeWeightedInvestmentValuesWithCurrencyEffect: Record<string, Big>;
} = {}; }
> = {};
for (const item of lastTransactionPoint.items) { for (const item of lastTransactionPoint.items) {
const feeInBaseCurrency = item.fee.mul( const feeInBaseCurrency = item.fee.mul(
@ -461,10 +462,10 @@ export abstract class PortfolioCalculator {
return map; return map;
}, },
{} as { [date: string]: Big } {} as Record<string, Big>
); );
const accountBalanceMap: { [date: string]: Big } = {}; const accountBalanceMap: Record<string, Big> = {};
let lastKnownBalance = new Big(0); let lastKnownBalance = new Big(0);
@ -670,7 +671,7 @@ export abstract class PortfolioCalculator {
data: HistoricalDataItem[]; data: HistoricalDataItem[];
groupBy: GroupBy; groupBy: GroupBy;
}): InvestmentItem[] { }): InvestmentItem[] {
const groupedData: { [dateGroup: string]: Big } = {}; const groupedData: Record<string, Big> = {};
for (const { date, investmentValueWithCurrencyEffect } of data) { for (const { date, investmentValueWithCurrencyEffect } of data) {
const dateGroup = const dateGroup =
@ -802,12 +803,10 @@ export abstract class PortfolioCalculator {
start, start,
symbol symbol
}: { }: {
chartDateMap: { [date: string]: boolean }; chartDateMap: Record<string, boolean>;
end: Date; end: Date;
exchangeRates: { [dateString: string]: number }; exchangeRates: Record<string, number>;
marketSymbolMap: { marketSymbolMap: Record<string, Record<string, Big>>;
[date: string]: { [symbol: string]: Big };
};
start: Date; start: Date;
} & AssetProfileIdentifier): SymbolMetrics; } & AssetProfileIdentifier): SymbolMetrics;
@ -829,7 +828,7 @@ export abstract class PortfolioCalculator {
endDate: Date; endDate: Date;
startDate: Date; startDate: Date;
step: number; step: number;
}): { [date: string]: true } { }): Record<string, true> {
// Create a map of all relevant chart dates: // Create a map of all relevant chart dates:
// 1. Add transaction point dates // 1. Add transaction point dates
const chartDateMap = this.transactionPoints.reduce((result, { date }) => { const chartDateMap = this.transactionPoints.reduce((result, { date }) => {
@ -892,7 +891,7 @@ export abstract class PortfolioCalculator {
@LogPerformance @LogPerformance
private computeTransactionPoints() { private computeTransactionPoints() {
this.transactionPoints = []; this.transactionPoints = [];
const symbols: { [symbol: string]: TransactionPointSymbol } = {}; const symbols: Record<string, TransactionPointSymbol> = {};
let lastDate: string = null; let lastDate: string = null;
let lastTransactionPoint: TransactionPoint = null; let lastTransactionPoint: TransactionPoint = null;

52
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts

@ -1,6 +1,3 @@
import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator';
import { PortfolioOrderItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order-item.interface';
import { getFactor } from '@ghostfolio/api/helper/portfolio.helper';
import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper';
import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DATE_FORMAT } from '@ghostfolio/common/helper';
import { import {
@ -15,6 +12,10 @@ import { Big } from 'big.js';
import { addMilliseconds, differenceInDays, format, isBefore } from 'date-fns'; import { addMilliseconds, differenceInDays, format, isBefore } from 'date-fns';
import { cloneDeep, first, last, sortBy } from 'lodash'; import { cloneDeep, first, last, sortBy } from 'lodash';
import { getFactor } from '../../../../helper/portfolio.helper';
import { PortfolioOrderItem } from '../../interfaces/portfolio-order-item.interface';
import { PortfolioCalculator } from '../portfolio-calculator';
export class TWRPortfolioCalculator extends PortfolioCalculator { export class TWRPortfolioCalculator extends PortfolioCalculator {
private chartDates: string[]; private chartDates: string[];
@ -116,17 +117,15 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
start, start,
symbol symbol
}: { }: {
chartDateMap?: { [date: string]: boolean }; chartDateMap?: Record<string, boolean>;
end: Date; end: Date;
exchangeRates: { [dateString: string]: number }; exchangeRates: Record<string, number>;
marketSymbolMap: { marketSymbolMap: Record<string, Record<string, Big>>;
[date: string]: { [symbol: string]: Big };
};
start: Date; start: Date;
} & AssetProfileIdentifier): SymbolMetrics { } & AssetProfileIdentifier): SymbolMetrics {
const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)]; const currentExchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)];
const currentValues: { [date: string]: Big } = {}; const currentValues: Record<string, Big> = {};
const currentValuesWithCurrencyEffect: { [date: string]: Big } = {}; const currentValuesWithCurrencyEffect: Record<string, Big> = {};
let fees = new Big(0); let fees = new Big(0);
let feesAtStartDate = new Big(0); let feesAtStartDate = new Big(0);
let feesAtStartDateWithCurrencyEffect = new Big(0); let feesAtStartDateWithCurrencyEffect = new Big(0);
@ -141,20 +140,18 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
let initialValueWithCurrencyEffect: Big; let initialValueWithCurrencyEffect: Big;
let investmentAtStartDate: Big; let investmentAtStartDate: Big;
let investmentAtStartDateWithCurrencyEffect: Big; let investmentAtStartDateWithCurrencyEffect: Big;
const investmentValuesAccumulated: { [date: string]: Big } = {}; const investmentValuesAccumulated: Record<string, Big> = {};
const investmentValuesAccumulatedWithCurrencyEffect: { const investmentValuesAccumulatedWithCurrencyEffect: Record<string, Big> =
[date: string]: Big; {};
} = {}; const investmentValuesWithCurrencyEffect: Record<string, Big> = {};
const investmentValuesWithCurrencyEffect: { [date: string]: Big } = {};
let lastAveragePrice = new Big(0); let lastAveragePrice = new Big(0);
let lastAveragePriceWithCurrencyEffect = new Big(0); let lastAveragePriceWithCurrencyEffect = new Big(0);
const netPerformanceValues: { [date: string]: Big } = {}; const netPerformanceValues: Record<string, Big> = {};
const netPerformanceValuesWithCurrencyEffect: { [date: string]: Big } = {}; const netPerformanceValuesWithCurrencyEffect: Record<string, Big> = {};
const timeWeightedInvestmentValues: { [date: string]: Big } = {}; const timeWeightedInvestmentValues: Record<string, Big> = {};
const timeWeightedInvestmentValuesWithCurrencyEffect: { const timeWeightedInvestmentValuesWithCurrencyEffect: Record<string, Big> =
[date: string]: Big; {};
} = {};
const totalAccountBalanceInBaseCurrency = new Big(0); const totalAccountBalanceInBaseCurrency = new Big(0);
let totalDividend = new Big(0); let totalDividend = new Big(0);
@ -301,7 +298,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
let lastUnitPrice: Big; let lastUnitPrice: Big;
const ordersByDate: { [date: string]: PortfolioOrderItem[] } = {}; const ordersByDate: Record<string, PortfolioOrderItem[]> = {};
for (const order of orders) { for (const order of orders) {
ordersByDate[order.date] = ordersByDate[order.date] ?? []; ordersByDate[order.date] = ordersByDate[order.date] ?? [];
@ -788,13 +785,12 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
) )
: new Big(0); : new Big(0);
const netPerformancePercentageWithCurrencyEffectMap: { const netPerformancePercentageWithCurrencyEffectMap: Record<
[key: DateRange]: Big; DateRange,
} = {}; Big
> = {};
const netPerformanceWithCurrencyEffectMap: { const netPerformanceWithCurrencyEffectMap: Record<DateRange, Big> = {};
[key: DateRange]: Big;
} = {};
for (const dateRange of [ for (const dateRange of [
'1d', '1d',

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

@ -423,12 +423,12 @@ export class PortfolioService {
const symbolProfiles = const symbolProfiles =
await this.symbolProfileService.getSymbolProfiles(dataGatheringItems); await this.symbolProfileService.getSymbolProfiles(dataGatheringItems);
const symbolProfileMap: { [symbol: string]: EnhancedSymbolProfile } = {}; const symbolProfileMap: Record<string, EnhancedSymbolProfile> = {};
for (const symbolProfile of symbolProfiles) { for (const symbolProfile of symbolProfiles) {
symbolProfileMap[symbolProfile.symbol] = symbolProfile; symbolProfileMap[symbolProfile.symbol] = symbolProfile;
} }
const portfolioItemsNow: { [symbol: string]: TimelinePosition } = {}; const portfolioItemsNow: Record<string, TimelinePosition> = {};
for (const position of positions) { for (const position of positions) {
portfolioItemsNow[position.symbol] = position; portfolioItemsNow[position.symbol] = position;
} }
@ -969,7 +969,7 @@ export class PortfolioService {
) )
]); ]);
const symbolProfileMap: { [symbol: string]: EnhancedSymbolProfile } = {}; const symbolProfileMap: Record<string, EnhancedSymbolProfile> = {};
for (const symbolProfile of symbolProfiles) { for (const symbolProfile of symbolProfiles) {
symbolProfileMap[symbolProfile.symbol] = symbolProfile; symbolProfileMap[symbolProfile.symbol] = symbolProfile;

9
apps/api/src/app/symbol/symbol.service.ts

@ -74,11 +74,10 @@ export class SymbolService {
date = new Date(), date = new Date(),
symbol symbol
}: IDataGatheringItem): Promise<IDataProviderHistoricalResponse> { }: IDataGatheringItem): Promise<IDataProviderHistoricalResponse> {
let historicalData: { let historicalData: Record<
[symbol: string]: { string,
[date: string]: IDataProviderHistoricalResponse; Record<string, IDataProviderHistoricalResponse>
}; > = {
} = {
[symbol]: {} [symbol]: {}
}; };

2
apps/api/src/helper/object.helper.ts

@ -38,7 +38,7 @@ export function redactAttributes({
}: { }: {
isFirstRun?: boolean; isFirstRun?: boolean;
object: any; object: any;
options: { attribute: string; valueMap: { [key: string]: any } }[]; options: { attribute: string; valueMap: Record<string, any> }[];
}): any { }): any {
if (!object || !options?.length) { if (!object || !options?.length) {
return object; return object;

2
apps/api/src/middlewares/html-template.middleware.ts

@ -14,7 +14,7 @@ import { join } from 'path';
const i18nService = new I18nService(); const i18nService = new I18nService();
let indexHtmlMap: { [languageCode: string]: string } = {}; let indexHtmlMap: Record<string, string> = {};
const title = 'Ghostfolio'; const title = 'Ghostfolio';

4
apps/api/src/models/interfaces/portfolio.interface.ts

@ -7,9 +7,7 @@ export interface PortfolioInterface {
getFees(): number; getFees(): number;
getPositions(aDate: Date): { getPositions(aDate: Date): Record<string, Position>;
[symbol: string]: Position;
};
getSymbols(aDate?: Date): string[]; getSymbols(aDate?: Date): string[];

9
apps/api/src/models/rules/account-cluster-risk/current-investment.ts

@ -23,11 +23,12 @@ export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
} }
public evaluate(ruleSettings: Settings) { public evaluate(ruleSettings: Settings) {
const accounts: { const accounts: Record<
[symbol: string]: Pick<PortfolioPosition, 'name'> & { string,
Pick<PortfolioPosition, 'name'> & {
investment: number; investment: number;
}; }
} = {}; > = {};
for (const [accountId, account] of Object.entries(this.accounts)) { for (const [accountId, account] of Object.entries(this.accounts)) {
accounts[accountId] = { accounts[accountId] = {

30
apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts

@ -64,20 +64,20 @@ export class AlphaVantageService implements DataProviderInterface {
from, from,
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; Record<string, Record<string, IDataProviderHistoricalResponse>>
}> { > {
try { try {
const historicalData: { const historicalData: Record<string, IAlphaVantageHistoricalResponse[]> =
[symbol: string]: IAlphaVantageHistoricalResponse[]; await this.alphaVantage.crypto.daily(
} = await this.alphaVantage.crypto.daily( symbol.substring(0, symbol.length - 3).toLowerCase(),
symbol.substring(0, symbol.length - 3).toLowerCase(), 'usd'
'usd' );
);
const response: { const response: Record<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; string,
} = {}; Record<string, IDataProviderHistoricalResponse>
> = {};
response[symbol] = {}; response[symbol] = {};
@ -109,9 +109,9 @@ export class AlphaVantageService implements DataProviderInterface {
return DataSource.ALPHA_VANTAGE; return DataSource.ALPHA_VANTAGE;
} }
public async getQuotes({}: GetQuotesParams): Promise<{ public async getQuotes({}: GetQuotesParams): Promise<
[symbol: string]: IDataProviderResponse; Record<string, IDataProviderResponse>
}> { > {
return {}; return {};
} }

44
apps/api/src/services/data-provider/coingecko/coingecko.service.ts

@ -1,16 +1,3 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; 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';
@ -25,6 +12,20 @@ import {
import { format, fromUnixTime, getUnixTime } from 'date-fns'; import { format, fromUnixTime, getUnixTime } from 'date-fns';
import got, { Headers } from 'got'; import got, { Headers } from 'got';
import { LookupItem } from '../../../app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '../../configuration/configuration.service';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '../../interfaces/interfaces';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '../interfaces/data-provider.interface';
@Injectable() @Injectable()
export class CoinGeckoService implements DataProviderInterface { export class CoinGeckoService implements DataProviderInterface {
private readonly apiUrl: string; private readonly apiUrl: string;
@ -111,9 +112,9 @@ export class CoinGeckoService implements DataProviderInterface {
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; Record<string, Record<string, IDataProviderHistoricalResponse>>
}> { > {
try { try {
const abortController = new AbortController(); const abortController = new AbortController();
@ -134,9 +135,10 @@ export class CoinGeckoService implements DataProviderInterface {
} }
).json<any>(); ).json<any>();
const result: { const result: Record<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; string,
} = { Record<string, IDataProviderHistoricalResponse>
> = {
[symbol]: {} [symbol]: {}
}; };
@ -168,8 +170,8 @@ export class CoinGeckoService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<Record<string, IDataProviderResponse>> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: Record<string, IDataProviderResponse> = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

85
apps/api/src/services/data-provider/data-provider.service.ts

@ -1,14 +1,3 @@
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
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 {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
DERIVED_CURRENCIES, DERIVED_CURRENCIES,
@ -30,9 +19,21 @@ import { eachDayOfInterval, format, isValid } from 'date-fns';
import { groupBy, isEmpty, isNumber, uniqWith } from 'lodash'; import { groupBy, isEmpty, isNumber, uniqWith } from 'lodash';
import ms from 'ms'; import ms from 'ms';
import { RedisCacheService } from '../../app/redis-cache/redis-cache.service';
import { LookupItem } from '../../app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '../configuration/configuration.service';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '../interfaces/interfaces';
import { MarketDataService } from '../market-data/market-data.service';
import { PrismaService } from '../prisma/prisma.service';
import { PropertyService } from '../property/property.service';
import { DataProviderInterface } from './interfaces/data-provider.interface';
@Injectable() @Injectable()
export class DataProviderService { export class DataProviderService {
private dataProviderMapping: { [dataProviderName: string]: string }; private dataProviderMapping: Record<string, string>;
public constructor( public constructor(
private readonly configurationService: ConfigurationService, private readonly configurationService: ConfigurationService,
@ -48,9 +49,9 @@ export class DataProviderService {
public async initialize() { public async initialize() {
this.dataProviderMapping = this.dataProviderMapping =
((await this.propertyService.getByKey(PROPERTY_DATA_SOURCE_MAPPING)) as { ((await this.propertyService.getByKey(
[dataProviderName: string]: string; PROPERTY_DATA_SOURCE_MAPPING
}) ?? {}; )) as Record<string, string>) ?? {};
} }
public async checkQuote(dataSource: DataSource) { public async checkQuote(dataSource: DataSource) {
@ -75,12 +76,10 @@ export class DataProviderService {
return false; return false;
} }
public async getAssetProfiles(items: AssetProfileIdentifier[]): Promise<{ public async getAssetProfiles(
[symbol: string]: Partial<SymbolProfile>; items: AssetProfileIdentifier[]
}> { ): Promise<Record<string, Partial<SymbolProfile>>> {
const response: { const response: Record<string, Partial<SymbolProfile>> = {};
[symbol: string]: Partial<SymbolProfile>;
} = {};
const itemsGroupedByDataSource = groupBy(items, ({ dataSource }) => { const itemsGroupedByDataSource = groupBy(items, ({ dataSource }) => {
return dataSource; return dataSource;
@ -177,12 +176,11 @@ export class DataProviderService {
aGranularity: Granularity = 'month', aGranularity: Granularity = 'month',
from: Date, from: Date,
to: Date to: Date
): Promise<{ ): Promise<Record<string, Record<string, IDataProviderHistoricalResponse>>> {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; let response: Record<
}> { string,
let response: { Record<string, IDataProviderHistoricalResponse>
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; > = {};
} = {};
if (isEmpty(aItems) || !isValid(from) || !isValid(to)) { if (isEmpty(aItems) || !isValid(from) || !isValid(to)) {
return response; return response;
@ -246,9 +244,7 @@ export class DataProviderService {
dataGatheringItems: AssetProfileIdentifier[]; dataGatheringItems: AssetProfileIdentifier[];
from: Date; from: Date;
to: Date; to: Date;
}): Promise<{ }): Promise<Record<string, Record<string, IDataProviderHistoricalResponse>>> {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
}> {
for (const { currency, rootCurrency } of DERIVED_CURRENCIES) { for (const { currency, rootCurrency } of DERIVED_CURRENCIES) {
if ( if (
this.hasCurrency({ this.hasCurrency({
@ -272,21 +268,20 @@ export class DataProviderService {
return obj1.dataSource === obj2.dataSource && obj1.symbol === obj2.symbol; return obj1.dataSource === obj2.dataSource && obj1.symbol === obj2.symbol;
}); });
const result: { const result: Record<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; string,
} = {}; Record<string, IDataProviderHistoricalResponse>
> = {};
const promises: Promise<{ const promises: Promise<{
data: { [date: string]: IDataProviderHistoricalResponse }; data: Record<string, IDataProviderHistoricalResponse>;
symbol: string; symbol: string;
}>[] = []; }>[] = [];
for (const { dataSource, symbol } of dataGatheringItems) { for (const { dataSource, symbol } of dataGatheringItems) {
const dataProvider = this.getDataProvider(dataSource); const dataProvider = this.getDataProvider(dataSource);
if (dataProvider.canHandle(symbol)) { if (dataProvider.canHandle(symbol)) {
if (symbol === `${DEFAULT_CURRENCY}USX`) { if (symbol === `${DEFAULT_CURRENCY}USX`) {
const data: { const data: Record<string, IDataProviderHistoricalResponse> = {};
[date: string]: IDataProviderHistoricalResponse;
} = {};
for (const date of eachDayOfInterval({ end: to, start: from })) { for (const date of eachDayOfInterval({ end: to, start: from })) {
data[format(date, DATE_FORMAT)] = { marketPrice: 100 }; data[format(date, DATE_FORMAT)] = { marketPrice: 100 };
@ -354,12 +349,8 @@ export class DataProviderService {
requestTimeout?: number; requestTimeout?: number;
useCache?: boolean; useCache?: boolean;
user?: UserWithSettings; user?: UserWithSettings;
}): Promise<{ }): Promise<Record<string, IDataProviderResponse>> {
[symbol: string]: IDataProviderResponse; const response: Record<string, IDataProviderResponse> = {};
}> {
const response: {
[symbol: string]: IDataProviderResponse;
} = {};
const startTimeTotal = performance.now(); const startTimeTotal = performance.now();
if ( if (
@ -651,9 +642,7 @@ export class DataProviderService {
factor factor
}: { }: {
allData: { allData: {
data: { data: Record<string, IDataProviderHistoricalResponse>;
[date: string]: IDataProviderHistoricalResponse;
};
symbol: string; symbol: string;
}[]; }[];
currency: string; currency: string;
@ -663,9 +652,7 @@ export class DataProviderService {
return symbol === currency; return symbol === currency;
})?.data; })?.data;
const data: { const data: Record<string, IDataProviderHistoricalResponse> = {};
[date: string]: IDataProviderHistoricalResponse;
} = {};
for (const date in rootData) { for (const date in rootData) {
if (isNumber(rootData[date].marketPrice)) { if (isNumber(rootData[date].marketPrice)) {

49
apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts

@ -1,17 +1,3 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
REPLACE_NAME_PARTS REPLACE_NAME_PARTS
@ -31,6 +17,21 @@ import { addDays, format, isSameDay, isToday } from 'date-fns';
import got from 'got'; import got from 'got';
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
import { LookupItem } from '../../../app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '../../configuration/configuration.service';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '../../interfaces/interfaces';
import { SymbolProfileService } from '../../symbol-profile/symbol-profile.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '../interfaces/data-provider.interface';
@Injectable() @Injectable()
export class EodHistoricalDataService implements DataProviderInterface { export class EodHistoricalDataService implements DataProviderInterface {
private apiKey: string; private apiKey: string;
@ -78,9 +79,9 @@ export class EodHistoricalDataService implements DataProviderInterface {
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbol, symbol,
to to
}: GetDividendsParams): Promise<{ }: GetDividendsParams): Promise<
[date: string]: IDataProviderHistoricalResponse; Record<string, IDataProviderHistoricalResponse>
}> { > {
symbol = this.convertToEodSymbol(symbol); symbol = this.convertToEodSymbol(symbol);
if (isSameDay(from, to)) { if (isSameDay(from, to)) {
@ -90,9 +91,7 @@ export class EodHistoricalDataService implements DataProviderInterface {
try { try {
const abortController = new AbortController(); const abortController = new AbortController();
const response: { const response: Record<string, IDataProviderHistoricalResponse> = {};
[date: string]: IDataProviderHistoricalResponse;
} = {};
setTimeout(() => { setTimeout(() => {
abortController.abort(); abortController.abort();
@ -137,9 +136,9 @@ export class EodHistoricalDataService implements DataProviderInterface {
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; Record<string, Record<string, IDataProviderHistoricalResponse>>
}> { > {
symbol = this.convertToEodSymbol(symbol); symbol = this.convertToEodSymbol(symbol);
try { try {
@ -202,8 +201,8 @@ export class EodHistoricalDataService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<Record<string, IDataProviderResponse>> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: Record<string, IDataProviderResponse> = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

44
apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts

@ -1,16 +1,3 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; 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';
@ -20,6 +7,20 @@ import { DataSource, SymbolProfile } from '@prisma/client';
import { format, isAfter, isBefore, isSameDay } from 'date-fns'; import { format, isAfter, isBefore, isSameDay } from 'date-fns';
import got from 'got'; import got from 'got';
import { LookupItem } from '../../../app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '../../configuration/configuration.service';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '../../interfaces/interfaces';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '../interfaces/data-provider.interface';
@Injectable() @Injectable()
export class FinancialModelingPrepService implements DataProviderInterface { export class FinancialModelingPrepService implements DataProviderInterface {
private apiKey: string; private apiKey: string;
@ -65,9 +66,9 @@ export class FinancialModelingPrepService implements DataProviderInterface {
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; Record<string, Record<string, IDataProviderHistoricalResponse>>
}> { > {
try { try {
const abortController = new AbortController(); const abortController = new AbortController();
@ -83,9 +84,10 @@ export class FinancialModelingPrepService implements DataProviderInterface {
} }
).json<any>(); ).json<any>();
const result: { const result: Record<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; string,
} = { Record<string, IDataProviderHistoricalResponse>
> = {
[symbol]: {} [symbol]: {}
}; };
@ -119,8 +121,8 @@ export class FinancialModelingPrepService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'), requestTimeout = this.configurationService.get('REQUEST_TIMEOUT'),
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<Record<string, IDataProviderResponse>> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: Record<string, IDataProviderResponse> = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

46
apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts

@ -1,18 +1,3 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
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';
@ -21,6 +6,22 @@ import { DataSource, SymbolProfile } from '@prisma/client';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { GoogleSpreadsheet } from 'google-spreadsheet'; import { GoogleSpreadsheet } from 'google-spreadsheet';
import { LookupItem } from '../../../app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '../../configuration/configuration.service';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '../../interfaces/interfaces';
import { PrismaService } from '../../prisma/prisma.service';
import { SymbolProfileService } from '../../symbol-profile/symbol-profile.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '../interfaces/data-provider.interface';
@Injectable() @Injectable()
export class GoogleSheetsService implements DataProviderInterface { export class GoogleSheetsService implements DataProviderInterface {
public constructor( public constructor(
@ -60,9 +61,9 @@ export class GoogleSheetsService implements DataProviderInterface {
from, from,
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; Record<string, Record<string, IDataProviderHistoricalResponse>>
}> { > {
try { try {
const sheet = await this.getSheet({ const sheet = await this.getSheet({
symbol, symbol,
@ -71,9 +72,8 @@ export class GoogleSheetsService implements DataProviderInterface {
const rows = await sheet.getRows(); const rows = await sheet.getRows();
const historicalData: { const historicalData: Record<string, IDataProviderHistoricalResponse> =
[date: string]: IDataProviderHistoricalResponse; {};
} = {};
rows rows
.filter((_row, index) => { .filter((_row, index) => {
@ -105,8 +105,8 @@ export class GoogleSheetsService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<Record<string, IDataProviderResponse>> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: Record<string, IDataProviderResponse> = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

30
apps/api/src/services/data-provider/interfaces/data-provider.interface.ts

@ -1,13 +1,14 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { DataProviderInfo } from '@ghostfolio/common/interfaces';
import { Granularity } from '@ghostfolio/common/types'; import { Granularity } from '@ghostfolio/common/types';
import { DataSource, SymbolProfile } from '@prisma/client'; import { DataSource, SymbolProfile } from '@prisma/client';
import { LookupItem } from '../../../app/symbol/interfaces/lookup-item.interface';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '../../interfaces/interfaces';
export interface DataProviderInterface { export interface DataProviderInterface {
canHandle(symbol: string): boolean; canHandle(symbol: string): boolean;
@ -19,9 +20,14 @@ export interface DataProviderInterface {
getDataProviderInfo(): DataProviderInfo; getDataProviderInfo(): DataProviderInfo;
getDividends({ from, granularity, symbol, to }: GetDividendsParams): Promise<{ getDividends({
[date: string]: IDataProviderHistoricalResponse; from,
}>; granularity,
symbol,
to
}: GetDividendsParams): Promise<
Record<string, IDataProviderHistoricalResponse>
>;
getHistorical({ getHistorical({
from, from,
@ -29,9 +35,9 @@ export interface DataProviderInterface {
requestTimeout, requestTimeout,
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; Record<string, Record<string, IDataProviderHistoricalResponse>>
}>; // TODO: Return only one symbol >; // TODO: Return only one symbol
getMaxNumberOfSymbolsPerRequest?(): number; getMaxNumberOfSymbolsPerRequest?(): number;
@ -40,7 +46,7 @@ export interface DataProviderInterface {
getQuotes({ getQuotes({
requestTimeout, requestTimeout,
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }>; }: GetQuotesParams): Promise<Record<string, IDataProviderResponse>>;
getTestSymbol(): string; getTestSymbol(): string;

48
apps/api/src/services/data-provider/manual/manual.service.ts

@ -1,18 +1,3 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import { import {
DATE_FORMAT, DATE_FORMAT,
extractNumberFromString, extractNumberFromString,
@ -31,6 +16,22 @@ import { addDays, format, isBefore } from 'date-fns';
import got, { Headers } from 'got'; import got, { Headers } from 'got';
import jsonpath from 'jsonpath'; import jsonpath from 'jsonpath';
import { LookupItem } from '../../../app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '../../configuration/configuration.service';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '../../interfaces/interfaces';
import { PrismaService } from '../../prisma/prisma.service';
import { SymbolProfileService } from '../../symbol-profile/symbol-profile.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '../interfaces/data-provider.interface';
@Injectable() @Injectable()
export class ManualService implements DataProviderInterface { export class ManualService implements DataProviderInterface {
public constructor( public constructor(
@ -79,9 +80,9 @@ export class ManualService implements DataProviderInterface {
from, from,
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; Record<string, Record<string, IDataProviderHistoricalResponse>>
}> { > {
try { try {
const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles( const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles(
[{ symbol, dataSource: this.getName() }] [{ symbol, dataSource: this.getName() }]
@ -90,9 +91,10 @@ export class ManualService implements DataProviderInterface {
symbolProfile?.scraperConfiguration ?? {}; symbolProfile?.scraperConfiguration ?? {};
if (defaultMarketPrice) { if (defaultMarketPrice) {
const historical: { const historical: Record<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; string,
} = { Record<string, IDataProviderHistoricalResponse>
> = {
[symbol]: {} [symbol]: {}
}; };
let date = from; let date = from;
@ -135,8 +137,8 @@ export class ManualService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<Record<string, IDataProviderResponse>> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: Record<string, IDataProviderResponse> = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

35
apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts

@ -1,16 +1,3 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { ghostfolioFearAndGreedIndexSymbol } from '@ghostfolio/common/config'; import { ghostfolioFearAndGreedIndexSymbol } from '@ghostfolio/common/config';
import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper'; import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { DataProviderInfo } from '@ghostfolio/common/interfaces';
@ -20,6 +7,20 @@ import { DataSource, SymbolProfile } from '@prisma/client';
import { format } from 'date-fns'; import { format } from 'date-fns';
import got from 'got'; import got from 'got';
import { LookupItem } from '../../../app/symbol/interfaces/lookup-item.interface';
import { ConfigurationService } from '../../configuration/configuration.service';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '../../interfaces/interfaces';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '../interfaces/data-provider.interface';
@Injectable() @Injectable()
export class RapidApiService implements DataProviderInterface { export class RapidApiService implements DataProviderInterface {
public constructor( public constructor(
@ -57,9 +58,9 @@ export class RapidApiService implements DataProviderInterface {
from, from,
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; Record<string, Record<string, IDataProviderHistoricalResponse>>
}> { > {
try { try {
if (symbol === ghostfolioFearAndGreedIndexSymbol) { if (symbol === ghostfolioFearAndGreedIndexSymbol) {
const fgi = await this.getFearAndGreedIndex(); const fgi = await this.getFearAndGreedIndex();
@ -90,7 +91,7 @@ export class RapidApiService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<Record<string, IDataProviderResponse>> {
if (symbols.length <= 0) { if (symbols.length <= 0) {
return {}; return {};
} }

50
apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts

@ -1,17 +1,3 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
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 {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; 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';
@ -27,6 +13,21 @@ import {
} from 'yahoo-finance2/dist/esm/src/modules/historical'; } from 'yahoo-finance2/dist/esm/src/modules/historical';
import { Quote } from 'yahoo-finance2/dist/esm/src/modules/quote'; import { Quote } from 'yahoo-finance2/dist/esm/src/modules/quote';
import { LookupItem } from '../../../app/symbol/interfaces/lookup-item.interface';
import { CryptocurrencyService } from '../../cryptocurrency/cryptocurrency.service';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '../../interfaces/interfaces';
import { YahooFinanceDataEnhancerService } from '../data-enhancer/yahoo-finance/yahoo-finance.service';
import {
DataProviderInterface,
GetDividendsParams,
GetHistoricalParams,
GetQuotesParams,
GetSearchParams
} from '../interfaces/data-provider.interface';
@Injectable() @Injectable()
export class YahooFinanceService implements DataProviderInterface { export class YahooFinanceService implements DataProviderInterface {
public constructor( public constructor(
@ -78,9 +79,7 @@ export class YahooFinanceService implements DataProviderInterface {
} }
) )
); );
const response: { const response: Record<string, IDataProviderHistoricalResponse> = {};
[date: string]: IDataProviderHistoricalResponse;
} = {};
for (const historicalItem of historicalResult) { for (const historicalItem of historicalResult) {
response[format(historicalItem.date, DATE_FORMAT)] = { response[format(historicalItem.date, DATE_FORMAT)] = {
@ -106,9 +105,9 @@ export class YahooFinanceService implements DataProviderInterface {
from, from,
symbol, symbol,
to to
}: GetHistoricalParams): Promise<{ }: GetHistoricalParams): Promise<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; Record<string, Record<string, IDataProviderHistoricalResponse>>
}> { > {
if (isSameDay(from, to)) { if (isSameDay(from, to)) {
to = addDays(to, 1); to = addDays(to, 1);
} }
@ -127,9 +126,10 @@ export class YahooFinanceService implements DataProviderInterface {
) )
); );
const response: { const response: Record<
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; string,
} = {}; Record<string, IDataProviderHistoricalResponse>
> = {};
response[symbol] = {}; response[symbol] = {};
@ -160,8 +160,8 @@ export class YahooFinanceService implements DataProviderInterface {
public async getQuotes({ public async getQuotes({
symbols symbols
}: GetQuotesParams): Promise<{ [symbol: string]: IDataProviderResponse }> { }: GetQuotesParams): Promise<Record<string, IDataProviderResponse>> {
const response: { [symbol: string]: IDataProviderResponse } = {}; const response: Record<string, IDataProviderResponse> = {};
if (symbols.length <= 0) { if (symbols.length <= 0) {
return response; return response;

29
apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts

@ -1,9 +1,3 @@
import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
DERIVED_CURRENCIES, DERIVED_CURRENCIES,
@ -26,11 +20,18 @@ import {
import { isNumber, uniq } from 'lodash'; import { isNumber, uniq } from 'lodash';
import ms from 'ms'; import ms from 'ms';
import { LogPerformance } from '../../interceptors/performance-logging/performance-logging.interceptor';
import { DataProviderService } from '../data-provider/data-provider.service';
import { IDataGatheringItem } from '../interfaces/interfaces';
import { MarketDataService } from '../market-data/market-data.service';
import { PrismaService } from '../prisma/prisma.service';
import { PropertyService } from '../property/property.service';
@Injectable() @Injectable()
export class ExchangeRateDataService { export class ExchangeRateDataService {
private currencies: string[] = []; private currencies: string[] = [];
private currencyPairs: IDataGatheringItem[] = []; private currencyPairs: IDataGatheringItem[] = [];
private exchangeRates: { [currencyPair: string]: number } = {}; private exchangeRates: Record<string, number> = {};
public constructor( public constructor(
private readonly dataProviderService: DataProviderService, private readonly dataProviderService: DataProviderService,
@ -63,9 +64,7 @@ export class ExchangeRateDataService {
return {}; return {};
} }
const exchangeRatesByCurrency: { const exchangeRatesByCurrency: Record<string, Record<string, number>> = {};
[currency: string]: { [dateString: string]: number };
} = {};
for (const currency of currencies) { for (const currency of currencies) {
exchangeRatesByCurrency[`${currency}${targetCurrency}`] = exchangeRatesByCurrency[`${currency}${targetCurrency}`] =
@ -351,7 +350,7 @@ export class ExchangeRateDataService {
startDate: Date; startDate: Date;
}) { }) {
const dates = eachDayOfInterval({ end: endDate, start: startDate }); const dates = eachDayOfInterval({ end: endDate, start: startDate });
const factors: { [dateString: string]: number } = {}; const factors: Record<string, number> = {};
if (currencyFrom === currencyTo) { if (currencyFrom === currencyTo) {
for (const date of dates) { for (const date of dates) {
@ -379,12 +378,8 @@ export class ExchangeRateDataService {
} else { } else {
// Calculate indirectly via base currency // Calculate indirectly via base currency
const marketPriceBaseCurrencyFromCurrency: { const marketPriceBaseCurrencyFromCurrency: Record<string, number> = {};
[dateString: string]: number; const marketPriceBaseCurrencyToCurrency: Record<string, number> = {};
} = {};
const marketPriceBaseCurrencyToCurrency: {
[dateString: string]: number;
} = {};
try { try {
if (currencyFrom === DEFAULT_CURRENCY) { if (currencyFrom === DEFAULT_CURRENCY) {

2
apps/api/src/services/i18n/i18n.service.ts

@ -7,7 +7,7 @@ import { join } from 'path';
export class I18nService { export class I18nService {
private localesPath = join(__dirname, 'assets', 'locales'); private localesPath = join(__dirname, 'assets', 'locales');
private translations: { [locale: string]: cheerio.CheerioAPI } = {}; private translations: Record<string, cheerio.CheerioAPI> = {};
public constructor() { public constructor() {
this.loadFiles(); this.loadFiles();

4
apps/api/src/services/property/property.service.ts

@ -17,9 +17,7 @@ export class PropertyService {
} }
public async get() { public async get() {
const response: { const response: Record<string, boolean | object | string | string[]> = {
[key: string]: boolean | object | string | string[];
} = {
[PROPERTY_CURRENCIES]: [] [PROPERTY_CURRENCIES]: []
}; };

2
apps/api/src/services/queues/data-gathering/data-gathering.service.ts

@ -395,7 +395,7 @@ export class DataGatheringService {
} }
private async getSymbolsMax(): Promise<IDataGatheringItem[]> { private async getSymbolsMax(): Promise<IDataGatheringItem[]> {
const benchmarkAssetProfileIdMap: { [key: string]: boolean } = {}; const benchmarkAssetProfileIdMap: Record<string, boolean> = {};
( (
((await this.propertyService.getByKey( ((await this.propertyService.getByKey(
PROPERTY_BENCHMARKS PROPERTY_BENCHMARKS

6
apps/api/src/services/symbol-profile/symbol-profile.service.ts

@ -299,10 +299,6 @@ export class SymbolProfileService {
} }
private getSymbolMapping(symbolProfile: SymbolProfile) { private getSymbolMapping(symbolProfile: SymbolProfile) {
return ( return (symbolProfile['symbolMapping'] as Record<string, string>) ?? {};
(symbolProfile['symbolMapping'] as {
[key: string]: string;
}) ?? {}
);
} }
} }

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

@ -1,4 +1,3 @@
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { import {
DATE_FORMAT, DATE_FORMAT,
getDateFormatString, getDateFormatString,
@ -32,6 +31,7 @@ import { first, last } from 'lodash';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
import { UserService } from '../../services/user/user.service';
import { MarketDataDetailDialogParams } from './market-data-detail-dialog/interfaces/interfaces'; import { MarketDataDetailDialogParams } from './market-data-detail-dialog/interfaces/interfaces';
import { MarketDataDetailDialog } from './market-data-detail-dialog/market-data-detail-dialog.component'; import { MarketDataDetailDialog } from './market-data-detail-dialog/market-data-detail-dialog.component';
@ -55,11 +55,10 @@ export class AdminMarketDataDetailComponent implements OnChanges {
public defaultDateFormat: string; public defaultDateFormat: string;
public deviceType: string; public deviceType: string;
public historicalDataItems: LineChartItem[]; public historicalDataItems: LineChartItem[];
public marketDataByMonth: { public marketDataByMonth: Record<
[yearMonth: string]: { string,
[day: string]: Pick<MarketData, 'date' | 'marketPrice'> & { day: number }; Record<string, Pick<MarketData, 'date' | 'marketPrice'> & { day: number }>
}; > = {};
} = {};
public user: User; public user: User;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();

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

@ -70,16 +70,12 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
}); });
public assetProfileSubClass: string; public assetProfileSubClass: string;
public benchmarks: Partial<SymbolProfile>[]; public benchmarks: Partial<SymbolProfile>[];
public countries: { public countries: Record<string, { name: string; value: number }>;
[code: string]: { name: string; value: number };
};
public currencies: string[] = []; public currencies: string[] = [];
public ghostfolioScraperApiSymbolPrefix = ghostfolioScraperApiSymbolPrefix; public ghostfolioScraperApiSymbolPrefix = ghostfolioScraperApiSymbolPrefix;
public isBenchmark = false; public isBenchmark = false;
public marketDataDetails: MarketData[] = []; public marketDataDetails: MarketData[] = [];
public sectors: { public sectors: Record<string, { name: string; value: number }>;
[name: string]: { name: string; value: number };
};
private static readonly HISTORICAL_DATA_TEMPLATE = `date;marketPrice\n${format( private static readonly HISTORICAL_DATA_TEMPLATE = `date;marketPrice\n${format(
new Date(), new Date(),

8
apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts

@ -94,9 +94,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
public assetSubClass: string; public assetSubClass: string;
public averagePrice: number; public averagePrice: number;
public benchmarkDataItems: LineChartItem[]; public benchmarkDataItems: LineChartItem[];
public countries: { public countries: Record<string, { name: string; value: number }>;
[code: string]: { name: string; value: number };
};
public dataProviderInfo: DataProviderInfo; public dataProviderInfo: DataProviderInfo;
public dataSource: MatTableDataSource<Activity>; public dataSource: MatTableDataSource<Activity>;
public dividendInBaseCurrency: number; public dividendInBaseCurrency: number;
@ -120,9 +118,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit {
public quantity: number; public quantity: number;
public quantityPrecision = 2; public quantityPrecision = 2;
public reportDataGlitchMail: string; public reportDataGlitchMail: string;
public sectors: { public sectors: Record<string, { name: string; value: number }>;
[name: string]: { name: string; value: number };
};
public separatorKeysCodes: number[] = [COMMA, ENTER]; public separatorKeysCodes: number[] = [COMMA, ENTER];
public sortColumn = 'date'; public sortColumn = 'date';
public sortDirection: SortDirection = 'desc'; public sortDirection: SortDirection = 'desc';

2
apps/client/src/app/components/world-map-chart/world-map-chart.component.ts

@ -17,7 +17,7 @@ import svgMap from 'svgmap';
styleUrls: ['./world-map-chart.component.scss'] styleUrls: ['./world-map-chart.component.scss']
}) })
export class WorldMapChartComponent implements OnChanges, OnDestroy { export class WorldMapChartComponent implements OnChanges, OnDestroy {
@Input() countries: { [code: string]: { name?: string; value: number } }; @Input() countries: Record<string, { name?: string; value: number }>;
@Input() format: string; @Input() format: string;
@Input() isInPercent = false; @Input() isInPercent = false;
@Input() locale = getLocale(); @Input() locale = getLocale();

4
apps/client/src/app/pages/landing/landing-page.component.ts

@ -14,9 +14,7 @@ import { Subject } from 'rxjs';
templateUrl: './landing-page.html' templateUrl: './landing-page.html'
}) })
export class LandingPageComponent implements OnDestroy, OnInit { export class LandingPageComponent implements OnDestroy, OnInit {
public countriesOfSubscribersMap: { public countriesOfSubscribersMap: Record<string, { value: number }> = {};
[code: string]: { value: number };
} = {};
public currentYear = format(new Date(), 'yyyy'); public currentYear = format(new Date(), 'yyyy');
public deviceType: string; public deviceType: string;
public hasPermissionForDemo: boolean; public hasPermissionForDemo: boolean;

63
apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts

@ -1,8 +1,3 @@
import { AccountDetailDialog } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.component';
import { AccountDetailDialogParams } from '@ghostfolio/client/components/account-detail-dialog/interfaces/interfaces';
import { DataService } from '@ghostfolio/client/services/data.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { MAX_TOP_HOLDINGS, UNKNOWN_KEY } from '@ghostfolio/common/config'; import { MAX_TOP_HOLDINGS, UNKNOWN_KEY } from '@ghostfolio/common/config';
import { prettifySymbol } from '@ghostfolio/common/helper'; import { prettifySymbol } from '@ghostfolio/common/helper';
import { import {
@ -25,24 +20,27 @@ import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { AccountDetailDialog } from '../../../components/account-detail-dialog/account-detail-dialog.component';
import { AccountDetailDialogParams } from '../../../components/account-detail-dialog/interfaces/interfaces';
import { DataService } from '../../../services/data.service';
import { ImpersonationStorageService } from '../../../services/impersonation-storage.service';
import { UserService } from '../../../services/user/user.service';
@Component({ @Component({
selector: 'gf-allocations-page', selector: 'gf-allocations-page',
styleUrls: ['./allocations-page.scss'], styleUrls: ['./allocations-page.scss'],
templateUrl: './allocations-page.html' templateUrl: './allocations-page.html'
}) })
export class AllocationsPageComponent implements OnDestroy, OnInit { export class AllocationsPageComponent implements OnDestroy, OnInit {
public accounts: { public accounts: Record<
[id: string]: Pick<Account, 'name'> & { string,
Pick<Account, 'name'> & {
id: string; id: string;
value: number; value: number;
}; }
}; >;
public continents: { public continents: Record<string, { name: string; value: number }>;
[code: string]: { name: string; value: number }; public countries: Record<string, { name: string; value: number }>;
};
public countries: {
[code: string]: { name: string; value: number };
};
public deviceType: string; public deviceType: string;
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
public isLoading = false; public isLoading = false;
@ -56,15 +54,17 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
value: number; value: number;
}; };
}; };
public platforms: { public platforms: Record<
[id: string]: Pick<Platform, 'name'> & { string,
Pick<Platform, 'name'> & {
id: string; id: string;
value: number; value: number;
}; }
}; >;
public portfolioDetails: PortfolioDetails; public portfolioDetails: PortfolioDetails;
public positions: { public positions: Record<
[symbol: string]: Pick< string,
Pick<
PortfolioPosition, PortfolioPosition,
| 'assetClass' | 'assetClass'
| 'assetClassLabel' | 'assetClassLabel'
@ -73,23 +73,20 @@ export class AllocationsPageComponent implements OnDestroy, OnInit {
| 'currency' | 'currency'
| 'exchange' | 'exchange'
| 'name' | 'name'
> & { etfProvider: string; value: number }; > & { etfProvider: string; value: number }
}; >;
public sectors: { public sectors: Record<string, { name: string; value: number }>;
[name: string]: { name: string; value: number }; public symbols: Record<
}; string,
public symbols: { {
[name: string]: {
dataSource?: DataSource; dataSource?: DataSource;
name: string; name: string;
symbol: string; symbol: string;
value: number; value: number;
}; }
}; >;
public topHoldings: Holding[]; public topHoldings: Holding[];
public topHoldingsMap: { public topHoldingsMap: Record<string, { name: string; value: number }>;
[name: string]: { name: string; value: number };
};
public totalValueInEtf = 0; public totalValueInEtf = 0;
public UNKNOWN_KEY = UNKNOWN_KEY; public UNKNOWN_KEY = UNKNOWN_KEY;
public user: User; public user: User;

31
apps/client/src/app/pages/public/public-page.component.ts

@ -1,4 +1,3 @@
import { DataService } from '@ghostfolio/client/services/data.service';
import { UNKNOWN_KEY } from '@ghostfolio/common/config'; import { UNKNOWN_KEY } from '@ghostfolio/common/config';
import { prettifySymbol } from '@ghostfolio/common/helper'; import { prettifySymbol } from '@ghostfolio/common/helper';
import { import {
@ -16,6 +15,8 @@ import { DeviceDetectorService } from 'ngx-device-detector';
import { EMPTY, Subject } from 'rxjs'; import { EMPTY, Subject } from 'rxjs';
import { catchError, takeUntil } from 'rxjs/operators'; import { catchError, takeUntil } from 'rxjs/operators';
import { DataService } from '../../services/data.service';
@Component({ @Component({
host: { class: 'page' }, host: { class: 'page' },
selector: 'gf-public-page', selector: 'gf-public-page',
@ -23,29 +24,25 @@ import { catchError, takeUntil } from 'rxjs/operators';
templateUrl: './public-page.html' templateUrl: './public-page.html'
}) })
export class PublicPageComponent implements OnInit { export class PublicPageComponent implements OnInit {
public continents: { public continents: Record<string, { name: string; value: number }>;
[code: string]: { name: string; value: number }; public countries: Record<string, { name: string; value: number }>;
};
public countries: {
[code: string]: { name: string; value: number };
};
public deviceType: string; public deviceType: string;
public holdings: PublicPortfolioResponse['holdings'][string][]; public holdings: PublicPortfolioResponse['holdings'][string][];
public markets: { public markets: {
[key in Market]: { id: Market; valueInPercentage: number }; [key in Market]: { id: Market; valueInPercentage: number };
}; };
public positions: { public positions: Record<
[symbol: string]: Pick<PortfolioPosition, 'currency' | 'name'> & { string,
Pick<PortfolioPosition, 'currency' | 'name'> & {
value: number; value: number;
}; }
}; >;
public publicPortfolioDetails: PublicPortfolioResponse; public publicPortfolioDetails: PublicPortfolioResponse;
public sectors: { public sectors: Record<string, { name: string; value: number }>;
[name: string]: { name: string; value: number }; public symbols: Record<
}; string,
public symbols: { { name: string; symbol: string; value: number }
[name: string]: { name: string; symbol: string; value: number }; >;
};
public UNKNOWN_KEY = UNKNOWN_KEY; public UNKNOWN_KEY = UNKNOWN_KEY;
private accessId: string; private accessId: string;

8
libs/common/src/lib/class-transformer.ts

@ -3,11 +3,9 @@ import { Big } from 'big.js';
export function transformToMapOfBig({ export function transformToMapOfBig({
value value
}: { }: {
value: { [key: string]: string }; value: Record<string, string>;
}): { }): Record<string, Big> {
[key: string]: Big; const mapOfBig: Record<string, Big> = {};
} {
const mapOfBig: { [key: string]: Big } = {};
for (const key in value) { for (const key in value) {
mapOfBig[key] = new Big(value[key]); mapOfBig[key] = new Big(value[key]);

2
libs/common/src/lib/interfaces/admin-data.interface.ts

@ -6,7 +6,7 @@ export interface AdminData {
label2: string; label2: string;
value: number; value: number;
} & AssetProfileIdentifier)[]; } & AssetProfileIdentifier)[];
settings: { [key: string]: boolean | object | string | string[] }; settings: Record<string, boolean | object | string | string[]>;
transactionCount: number; transactionCount: number;
userCount: number; userCount: number;
version: string; version: string;

2
libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts

@ -27,7 +27,7 @@ export interface EnhancedSymbolProfile {
scraperConfiguration?: ScraperConfiguration; scraperConfiguration?: ScraperConfiguration;
sectors: Sector[]; sectors: Sector[];
symbol: string; symbol: string;
symbolMapping?: { [key: string]: string }; symbolMapping?: Record<string, string>;
updatedAt: Date; updatedAt: Date;
url?: string; url?: string;
} }

20
libs/common/src/lib/interfaces/portfolio-details.interface.ts

@ -5,16 +5,17 @@ import {
import { Market, MarketAdvanced } from '@ghostfolio/common/types'; import { Market, MarketAdvanced } from '@ghostfolio/common/types';
export interface PortfolioDetails { export interface PortfolioDetails {
accounts: { accounts: Record<
[id: string]: { string,
{
balance: number; balance: number;
currency: string; currency: string;
name: string; name: string;
valueInBaseCurrency: number; valueInBaseCurrency: number;
valueInPercentage?: number; valueInPercentage?: number;
}; }
}; >;
holdings: { [symbol: string]: PortfolioPosition }; holdings: Record<string, PortfolioPosition>;
markets?: { markets?: {
[key in Market]: { [key in Market]: {
id: Market; id: Market;
@ -29,14 +30,15 @@ export interface PortfolioDetails {
valueInPercentage: number; valueInPercentage: number;
}; };
}; };
platforms: { platforms: Record<
[id: string]: { string,
{
balance: number; balance: number;
currency: string; currency: string;
name: string; name: string;
valueInBaseCurrency: number; valueInBaseCurrency: number;
valueInPercentage?: number; valueInPercentage?: number;
}; }
}; >;
summary?: PortfolioSummary; summary?: PortfolioSummary;
} }

2
libs/common/src/lib/interfaces/portfolio-item.interface.ts

@ -4,6 +4,6 @@ export interface PortfolioItem {
date: string; date: string;
grossPerformancePercent: number; grossPerformancePercent: number;
investment: number; investment: number;
positions: { [symbol: string]: Position }; positions: Record<string, Position>;
value: number; value: number;
} }

2
libs/common/src/lib/interfaces/portfolio-report.interface.ts

@ -1,5 +1,5 @@
import { PortfolioReportRule } from './portfolio-report-rule.interface'; import { PortfolioReportRule } from './portfolio-report-rule.interface';
export interface PortfolioReport { export interface PortfolioReport {
rules: { [group: string]: PortfolioReportRule[] }; rules: Record<string, PortfolioReportRule[]>;
} }

9
libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts

@ -4,8 +4,9 @@ import { Market } from '../../types';
export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 {
alias?: string; alias?: string;
hasDetails: boolean; hasDetails: boolean;
holdings: { holdings: Record<
[symbol: string]: Pick< string,
Pick<
PortfolioPosition, PortfolioPosition,
| 'allocationInPercentage' | 'allocationInPercentage'
| 'assetClass' | 'assetClass'
@ -21,8 +22,8 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 {
| 'url' | 'url'
| 'valueInBaseCurrency' | 'valueInBaseCurrency'
| 'valueInPercentage' | 'valueInPercentage'
>; >
}; >;
markets: { markets: {
[key in Market]: Pick< [key in Market]: Pick<
PortfolioDetails['markets'][key], PortfolioDetails['markets'][key],

2
libs/common/src/lib/interfaces/scraper-configuration.interface.ts

@ -1,6 +1,6 @@
export interface ScraperConfiguration { export interface ScraperConfiguration {
defaultMarketPrice?: number; defaultMarketPrice?: number;
headers?: { [key: string]: string }; headers?: Record<string, string>;
locale?: string; locale?: string;
mode?: 'instant' | 'lazy'; mode?: 'instant' | 'lazy';
selector: string; selector: string;

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

@ -3,12 +3,8 @@ import { DateRange } from '@ghostfolio/common/types';
import { Big } from 'big.js'; import { Big } from 'big.js';
export interface SymbolMetrics { export interface SymbolMetrics {
currentValues: { currentValues: Record<string, Big>;
[date: string]: Big; currentValuesWithCurrencyEffect: Record<string, Big>;
};
currentValuesWithCurrencyEffect: {
[date: string]: Big;
};
feesWithCurrencyEffect: Big; feesWithCurrencyEffect: Big;
grossPerformance: Big; grossPerformance: Big;
grossPerformancePercentage: Big; grossPerformancePercentage: Big;
@ -17,30 +13,18 @@ export interface SymbolMetrics {
hasErrors: boolean; hasErrors: boolean;
initialValue: Big; initialValue: Big;
initialValueWithCurrencyEffect: Big; initialValueWithCurrencyEffect: Big;
investmentValuesAccumulated: { investmentValuesAccumulated: Record<string, Big>;
[date: string]: Big; investmentValuesAccumulatedWithCurrencyEffect: Record<string, Big>;
}; investmentValuesWithCurrencyEffect: Record<string, Big>;
investmentValuesAccumulatedWithCurrencyEffect: {
[date: string]: Big;
};
investmentValuesWithCurrencyEffect: {
[date: string]: Big;
};
netPerformance: Big; netPerformance: Big;
netPerformancePercentage: Big; netPerformancePercentage: Big;
netPerformancePercentageWithCurrencyEffectMap: { [key: DateRange]: Big }; netPerformancePercentageWithCurrencyEffectMap: Record<DateRange, Big>;
netPerformanceValues: { netPerformanceValues: Record<string, Big>;
[date: string]: Big; netPerformanceValuesWithCurrencyEffect: Record<string, Big>;
}; netPerformanceWithCurrencyEffectMap: Record<DateRange, Big>;
netPerformanceValuesWithCurrencyEffect: { [date: string]: Big };
netPerformanceWithCurrencyEffectMap: { [key: DateRange]: Big };
timeWeightedInvestment: Big; timeWeightedInvestment: Big;
timeWeightedInvestmentValues: { timeWeightedInvestmentValues: Record<string, Big>;
[date: string]: Big; timeWeightedInvestmentValuesWithCurrencyEffect: Record<string, Big>;
};
timeWeightedInvestmentValuesWithCurrencyEffect: {
[date: string]: Big;
};
timeWeightedInvestmentWithCurrencyEffect: Big; timeWeightedInvestmentWithCurrencyEffect: Big;
totalAccountBalanceInBaseCurrency: Big; totalAccountBalanceInBaseCurrency: Big;
totalDividend: Big; totalDividend: Big;

4
libs/common/src/lib/models/timeline-position.ts

@ -70,10 +70,10 @@ export class TimelinePosition {
netPerformancePercentage: Big; netPerformancePercentage: Big;
@Transform(transformToMapOfBig, { toClassOnly: true }) @Transform(transformToMapOfBig, { toClassOnly: true })
netPerformancePercentageWithCurrencyEffectMap: { [key: DateRange]: Big }; netPerformancePercentageWithCurrencyEffectMap: Record<DateRange, Big>;
@Transform(transformToMapOfBig, { toClassOnly: true }) @Transform(transformToMapOfBig, { toClassOnly: true })
netPerformanceWithCurrencyEffectMap: { [key: DateRange]: Big }; netPerformanceWithCurrencyEffectMap: Record<DateRange, Big>;
@Transform(transformToBig, { toClassOnly: true }) @Transform(transformToBig, { toClassOnly: true })
@Type(() => Big) @Type(() => Big)

2
libs/ui/src/lib/assistant/assistant.component.ts

@ -292,7 +292,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
} }
} }
public hasFilter(aFormValue: { [key: string]: string }) { public hasFilter(aFormValue: Record<string, string>) {
return Object.values(aFormValue).some((value) => { return Object.values(aFormValue).some((value) => {
return !!value; return !!value;
}); });

24
libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts

@ -66,13 +66,14 @@ export class GfPortfolioProportionChartComponent
@Input() locale = getLocale(); @Input() locale = getLocale();
@Input() maxItems?: number; @Input() maxItems?: number;
@Input() showLabels = false; @Input() showLabels = false;
@Input() positions: { @Input() positions: Record<
[symbol: string]: Pick<PortfolioPosition, 'type'> & { string,
Pick<PortfolioPosition, 'type'> & {
dataSource?: DataSource; dataSource?: DataSource;
name: string; name: string;
value: number; value: number;
}; }
} = {}; > = {};
@Output() proportionChartClicked = new EventEmitter<AssetProfileIdentifier>(); @Output() proportionChartClicked = new EventEmitter<AssetProfileIdentifier>();
@ -83,9 +84,7 @@ export class GfPortfolioProportionChartComponent
private readonly OTHER_KEY = 'OTHER'; private readonly OTHER_KEY = 'OTHER';
private colorMap: { private colorMap: Record<string, string> = {};
[symbol: string]: string;
} = {};
public constructor() { public constructor() {
Chart.register(ArcElement, DoughnutController, LinearScale, Tooltip); Chart.register(ArcElement, DoughnutController, LinearScale, Tooltip);
@ -109,14 +108,15 @@ export class GfPortfolioProportionChartComponent
private initialize() { private initialize() {
this.isLoading = true; this.isLoading = true;
const chartData: { const chartData: Record<
[symbol: string]: { string,
{
color?: string; color?: string;
name: string; name: string;
subCategory?: { [symbol: string]: { value: Big } }; subCategory?: Record<string, { value: Big }>;
value: Big; value: Big;
}; }
} = {}; > = {};
this.colorMap = { this.colorMap = {
[this.OTHER_KEY]: `rgba(${getTextColor(this.colorScheme)}, 0.24)`, [this.OTHER_KEY]: `rgba(${getTextColor(this.colorScheme)}, 0.24)`,
[UNKNOWN_KEY]: `rgba(${getTextColor(this.colorScheme)}, 0.12)` [UNKNOWN_KEY]: `rgba(${getTextColor(this.colorScheme)}, 0.12)`

Loading…
Cancel
Save