Refactoring
This commit is contained in:
parent
8571709014
commit
cbdb68e2f8
@ -7,7 +7,7 @@ import { TimelineSpecification } from '@ghostfolio/api/app/core/interfaces/timel
|
||||
import { TransactionPoint } from '@ghostfolio/api/app/core/interfaces/transaction-point.interface';
|
||||
import { PortfolioCalculator } from '@ghostfolio/api/app/core/portfolio-calculator';
|
||||
import { OrderType } from '@ghostfolio/api/models/order-type';
|
||||
import { resetHours } from '@ghostfolio/common/helper';
|
||||
import { DATE_FORMAT, resetHours } from '@ghostfolio/common/helper';
|
||||
import { Currency } from '@prisma/client';
|
||||
import Big from 'big.js';
|
||||
import {
|
||||
@ -25,7 +25,7 @@ function mockGetValue(symbol: string, date: Date) {
|
||||
if (isSameDay(today, date)) {
|
||||
return { marketPrice: 213.32 };
|
||||
} else {
|
||||
const startDate = parse('2019-02-01', 'yyyy-MM-dd', new Date());
|
||||
const startDate = parse('2019-02-01', DATE_FORMAT, new Date());
|
||||
const daysInBetween = differenceInCalendarDays(date, startDate);
|
||||
|
||||
const marketPrice = new Big('144.38').plus(
|
||||
@ -36,10 +36,10 @@ function mockGetValue(symbol: string, date: Date) {
|
||||
} else if (symbol === 'AMZN') {
|
||||
return { marketPrice: 2021.99 };
|
||||
} else if (symbol === 'TSLA') {
|
||||
if (isSameDay(parse('2021-07-26', 'yyyy-MM-dd', new Date()), date)) {
|
||||
if (isSameDay(parse('2021-07-26', DATE_FORMAT, new Date()), date)) {
|
||||
return { marketPrice: 657.62 };
|
||||
}
|
||||
if (isSameDay(parse('2021-01-02', 'yyyy-MM-dd', new Date()), date)) {
|
||||
if (isSameDay(parse('2021-01-02', DATE_FORMAT, new Date()), date)) {
|
||||
return { marketPrice: 666.66 };
|
||||
}
|
||||
return { marketPrice: 0 };
|
||||
@ -607,7 +607,7 @@ describe('PortfolioCalculator', () => {
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementation(() => new Date(Date.UTC(2021, 6, 26)).getTime()); // 2021-07-26
|
||||
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||
parse('2020-01-21', 'yyyy-MM-dd', new Date())
|
||||
parse('2020-01-21', DATE_FORMAT, new Date())
|
||||
);
|
||||
spy.mockRestore();
|
||||
|
||||
@ -642,7 +642,7 @@ describe('PortfolioCalculator', () => {
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementation(() => new Date(Date.UTC(2021, 6, 26)).getTime()); // 2021-07-26
|
||||
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||
parse('2021-01-01', 'yyyy-MM-dd', new Date())
|
||||
parse('2021-01-01', DATE_FORMAT, new Date())
|
||||
);
|
||||
spy.mockRestore();
|
||||
|
||||
@ -677,7 +677,7 @@ describe('PortfolioCalculator', () => {
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementation(() => new Date(Date.UTC(2021, 6, 26)).getTime()); // 2021-07-26
|
||||
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||
parse('2021-01-02', 'yyyy-MM-dd', new Date())
|
||||
parse('2021-01-02', DATE_FORMAT, new Date())
|
||||
);
|
||||
spy.mockRestore();
|
||||
|
||||
@ -712,7 +712,7 @@ describe('PortfolioCalculator', () => {
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementation(() => new Date(Date.UTC(2020, 9, 24)).getTime()); // 2020-10-24
|
||||
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||
parse('2019-01-01', 'yyyy-MM-dd', new Date())
|
||||
parse('2019-01-01', DATE_FORMAT, new Date())
|
||||
);
|
||||
spy.mockRestore();
|
||||
|
||||
@ -788,7 +788,7 @@ describe('PortfolioCalculator', () => {
|
||||
// gross performance percentage: 1.100526008 * 1.034838024 = 1.138866159 => 13.89 %
|
||||
|
||||
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||
parse('2020-01-01', 'yyyy-MM-dd', new Date())
|
||||
parse('2020-01-01', DATE_FORMAT, new Date())
|
||||
);
|
||||
|
||||
spy.mockRestore();
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { baseCurrency, benchmarks } from '@ghostfolio/common/config';
|
||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||
import { isApiTokenAuthorized } from '@ghostfolio/common/permissions';
|
||||
import { RequestWithUser } from '@ghostfolio/common/types';
|
||||
import {
|
||||
@ -82,7 +83,7 @@ export class ExperimentalController {
|
||||
let date = new Date();
|
||||
|
||||
if (dateString) {
|
||||
date = parse(dateString, 'yyyy-MM-dd', new Date());
|
||||
date = parse(dateString, DATE_FORMAT, new Date());
|
||||
}
|
||||
|
||||
return this.experimentalService.getValue(orders, date, baseCurrency);
|
||||
|
@ -14,7 +14,7 @@ import { ImpersonationService } from '@ghostfolio/api/services/impersonation.ser
|
||||
import { IOrder } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||
import { Type } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||
import { RulesService } from '@ghostfolio/api/services/rules.service';
|
||||
import { parseDate } from '@ghostfolio/common/helper';
|
||||
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
PortfolioItem,
|
||||
PortfolioOverview,
|
||||
@ -57,6 +57,7 @@ import {
|
||||
export class PortfolioService {
|
||||
public constructor(
|
||||
private readonly accountService: AccountService,
|
||||
private readonly currentRateService: CurrentRateService,
|
||||
private readonly dataProviderService: DataProviderService,
|
||||
private readonly exchangeRateDataService: ExchangeRateDataService,
|
||||
private readonly impersonationService: ImpersonationService,
|
||||
@ -64,8 +65,7 @@ export class PortfolioService {
|
||||
private readonly redisCacheService: RedisCacheService,
|
||||
@Inject(REQUEST) private readonly request: RequestWithUser,
|
||||
private readonly rulesService: RulesService,
|
||||
private readonly userService: UserService,
|
||||
private readonly currentRateService: CurrentRateService
|
||||
private readonly userService: UserService
|
||||
) {}
|
||||
|
||||
public async createPortfolio(aUserId: string): Promise<Portfolio> {
|
||||
@ -163,32 +163,31 @@ export class PortfolioService {
|
||||
if (transactionPoints.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const dateFormat = 'yyyy-MM-dd';
|
||||
let portfolioStart = parse(
|
||||
transactionPoints[0].date,
|
||||
dateFormat,
|
||||
DATE_FORMAT,
|
||||
new Date()
|
||||
);
|
||||
portfolioStart = this.getStartDate(aDateRange, portfolioStart);
|
||||
|
||||
const timelineSpecification: TimelineSpecification[] = [
|
||||
{
|
||||
start: format(portfolioStart, dateFormat),
|
||||
start: format(portfolioStart, DATE_FORMAT),
|
||||
accuracy: 'day'
|
||||
}
|
||||
];
|
||||
|
||||
const timeline = await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
format(new Date(), dateFormat)
|
||||
format(new Date(), DATE_FORMAT)
|
||||
);
|
||||
|
||||
return timeline
|
||||
.filter((timelineItem) => timelineItem !== null)
|
||||
.map((timelineItem) => ({
|
||||
date: timelineItem.date,
|
||||
value: timelineItem.grossPerformance.toNumber(),
|
||||
marketPrice: timelineItem.value
|
||||
marketPrice: timelineItem.value,
|
||||
value: timelineItem.grossPerformance.toNumber()
|
||||
}));
|
||||
}
|
||||
|
||||
@ -270,7 +269,7 @@ export class PortfolioService {
|
||||
for (const [date, { marketPrice }] of Object.entries(
|
||||
historicalData[aSymbol]
|
||||
)) {
|
||||
const currentDate = parse(date, 'yyyy-MM-dd', new Date());
|
||||
const currentDate = parse(date, DATE_FORMAT, new Date());
|
||||
if (
|
||||
isSameDay(currentDate, parseISO(orders[0]?.getDate())) ||
|
||||
isAfter(currentDate, parseISO(orders[0]?.getDate()))
|
||||
@ -533,7 +532,7 @@ export class PortfolioService {
|
||||
|
||||
const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({
|
||||
currency: order.currency,
|
||||
date: format(order.date, 'yyyy-MM-dd'),
|
||||
date: format(order.date, DATE_FORMAT),
|
||||
name: order.SymbolProfile?.name,
|
||||
quantity: new Big(order.quantity),
|
||||
symbol: order.symbol,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { AccountService } from '@ghostfolio/api/app/account/account.service';
|
||||
import { UNKNOWN_KEY, baseCurrency } from '@ghostfolio/common/config';
|
||||
import { getUtc, getYesterday } from '@ghostfolio/common/helper';
|
||||
import { DATE_FORMAT, getUtc, getYesterday } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AccountType,
|
||||
Currency,
|
||||
@ -30,8 +30,8 @@ jest.mock('../app/account/account.service', () => {
|
||||
jest.mock('../services/data-provider.service', () => {
|
||||
return {
|
||||
DataProviderService: jest.fn().mockImplementation(() => {
|
||||
const today = format(new Date(), 'yyyy-MM-dd');
|
||||
const yesterday = format(getYesterday(), 'yyyy-MM-dd');
|
||||
const today = format(new Date(), DATE_FORMAT);
|
||||
const yesterday = format(getYesterday(), DATE_FORMAT);
|
||||
|
||||
return {
|
||||
get: () => {
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { AccountService } from '@ghostfolio/api/app/account/account.service';
|
||||
import { CashDetails } from '@ghostfolio/api/app/account/interfaces/cash-details.interface';
|
||||
import { UNKNOWN_KEY, ghostfolioCashSymbol } from '@ghostfolio/common/config';
|
||||
import { getToday, getYesterday, resetHours } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
DATE_FORMAT,
|
||||
getToday,
|
||||
getYesterday,
|
||||
resetHours
|
||||
} from '@ghostfolio/common/helper';
|
||||
import {
|
||||
PortfolioItem,
|
||||
PortfolioPerformance,
|
||||
@ -731,7 +736,7 @@ export class Portfolio implements PortfolioInterface {
|
||||
investment: 0,
|
||||
investmentInOriginalCurrency: 0,
|
||||
marketPrice:
|
||||
historicalData[symbol]?.[format(currentDate, 'yyyy-MM-dd')]
|
||||
historicalData[symbol]?.[format(currentDate, DATE_FORMAT)]
|
||||
?.marketPrice || 0,
|
||||
quantity: 0,
|
||||
transactionCount: 0
|
||||
@ -774,7 +779,7 @@ export class Portfolio implements PortfolioInterface {
|
||||
investment: 0,
|
||||
investmentInOriginalCurrency: 0,
|
||||
marketPrice:
|
||||
historicalData[symbol]?.[format(yesterday, 'yyyy-MM-dd')]
|
||||
historicalData[symbol]?.[format(yesterday, DATE_FORMAT)]
|
||||
?.marketPrice || 0,
|
||||
name: '',
|
||||
quantity: 0,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { benchmarks, currencyPairs } from '@ghostfolio/common/config';
|
||||
import {
|
||||
DATE_FORMAT,
|
||||
getUtc,
|
||||
isGhostfolioScraperApiSymbol,
|
||||
isRakutenRapidApiSymbol,
|
||||
@ -193,11 +194,11 @@ export class DataGatheringService {
|
||||
)
|
||||
) {
|
||||
if (
|
||||
historicalData[symbol]?.[format(currentDate, 'yyyy-MM-dd')]
|
||||
historicalData[symbol]?.[format(currentDate, DATE_FORMAT)]
|
||||
?.marketPrice
|
||||
) {
|
||||
lastMarketPrice =
|
||||
historicalData[symbol]?.[format(currentDate, 'yyyy-MM-dd')]
|
||||
historicalData[symbol]?.[format(currentDate, DATE_FORMAT)]
|
||||
?.marketPrice;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
DATE_FORMAT,
|
||||
isGhostfolioScraperApiSymbol,
|
||||
isRakutenRapidApiSymbol
|
||||
} from '@ghostfolio/common/helper';
|
||||
@ -100,9 +101,9 @@ export class DataProviderService {
|
||||
|
||||
const rangeQuery =
|
||||
from && to
|
||||
? `AND date >= '${format(from, 'yyyy-MM-dd')}' AND date <= '${format(
|
||||
? `AND date >= '${format(from, DATE_FORMAT)}' AND date <= '${format(
|
||||
to,
|
||||
'yyyy-MM-dd'
|
||||
DATE_FORMAT
|
||||
)}'`
|
||||
: '';
|
||||
|
||||
@ -120,7 +121,7 @@ export class DataProviderService {
|
||||
|
||||
r[symbol] = {
|
||||
...(r[symbol] || {}),
|
||||
[format(new Date(date), 'yyyy-MM-dd')]: { marketPrice }
|
||||
[format(new Date(date), DATE_FORMAT)]: { marketPrice }
|
||||
};
|
||||
|
||||
return r;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||
import { Granularity } from '@ghostfolio/common/types';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource } from '@prisma/client';
|
||||
@ -66,8 +67,8 @@ export class AlphaVantageService implements DataProviderInterface {
|
||||
historicalData['Time Series (Digital Currency Daily)']
|
||||
).sort()) {
|
||||
if (
|
||||
isAfter(from, parse(key, 'yyyy-MM-dd', new Date())) &&
|
||||
isBefore(to, parse(key, 'yyyy-MM-dd', new Date()))
|
||||
isAfter(from, parse(key, DATE_FORMAT, new Date())) &&
|
||||
isBefore(to, parse(key, DATE_FORMAT, new Date()))
|
||||
) {
|
||||
response[symbol][key] = {
|
||||
marketPrice: parseFloat(timeSeries['4a. close (USD)'])
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
DATE_FORMAT,
|
||||
getYesterday,
|
||||
isGhostfolioScraperApiSymbol
|
||||
} from '@ghostfolio/common/helper';
|
||||
@ -95,7 +96,7 @@ export class GhostfolioScraperApiService implements DataProviderInterface {
|
||||
|
||||
return {
|
||||
[symbol]: {
|
||||
[format(getYesterday(), 'yyyy-MM-dd')]: {
|
||||
[format(getYesterday(), DATE_FORMAT)]: {
|
||||
marketPrice: value
|
||||
}
|
||||
}
|
||||
@ -109,14 +110,13 @@ export class GhostfolioScraperApiService implements DataProviderInterface {
|
||||
|
||||
public async getScraperConfigurations(): Promise<ScraperConfig[]> {
|
||||
try {
|
||||
const {
|
||||
value: scraperConfigString
|
||||
} = await this.prisma.property.findFirst({
|
||||
select: {
|
||||
value: true
|
||||
},
|
||||
where: { key: 'SCRAPER_CONFIG' }
|
||||
});
|
||||
const { value: scraperConfigString } =
|
||||
await this.prisma.property.findFirst({
|
||||
select: {
|
||||
value: true
|
||||
},
|
||||
where: { key: 'SCRAPER_CONFIG' }
|
||||
});
|
||||
|
||||
return JSON.parse(scraperConfigString);
|
||||
} catch {}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
DATE_FORMAT,
|
||||
getToday,
|
||||
getYesterday,
|
||||
isRakutenRapidApiSymbol
|
||||
@ -117,7 +118,7 @@ export class RakutenRapidApiService implements DataProviderInterface {
|
||||
|
||||
return {
|
||||
'GF.FEAR_AND_GREED_INDEX': {
|
||||
[format(getYesterday(), 'yyyy-MM-dd')]: {
|
||||
[format(getYesterday(), DATE_FORMAT)]: {
|
||||
marketPrice: fgi.previousClose.value
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
|
||||
import { isCrypto, isCurrency, parseCurrency } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
DATE_FORMAT,
|
||||
isCrypto,
|
||||
isCurrency,
|
||||
parseCurrency
|
||||
} from '@ghostfolio/common/helper';
|
||||
import { Granularity } from '@ghostfolio/common/types';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource } from '@prisma/client';
|
||||
@ -103,8 +108,8 @@ export class YahooFinanceService implements DataProviderInterface {
|
||||
[symbol: string]: IYahooFinanceHistoricalResponse[];
|
||||
} = await yahooFinance.historical({
|
||||
symbols: yahooSymbols,
|
||||
from: format(from, 'yyyy-MM-dd'),
|
||||
to: format(to, 'yyyy-MM-dd')
|
||||
from: format(from, DATE_FORMAT),
|
||||
to: format(to, DATE_FORMAT)
|
||||
});
|
||||
|
||||
const response: {
|
||||
@ -117,7 +122,7 @@ export class YahooFinanceService implements DataProviderInterface {
|
||||
response[symbol] = {};
|
||||
|
||||
timeSeries.forEach((timeSerie) => {
|
||||
response[symbol][format(timeSerie.date, 'yyyy-MM-dd')] = {
|
||||
response[symbol][format(timeSerie.date, DATE_FORMAT)] = {
|
||||
marketPrice: timeSerie.close,
|
||||
performance: timeSerie.open - timeSerie.close
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getYesterday } from '@ghostfolio/common/helper';
|
||||
import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Currency } from '@prisma/client';
|
||||
import { format } from 'date-fns';
|
||||
@ -51,7 +51,7 @@ export class ExchangeRateDataService {
|
||||
|
||||
this.pairs.forEach((pair) => {
|
||||
const [currency1, currency2] = pair.match(/.{1,3}/g);
|
||||
const date = format(getYesterday(), 'yyyy-MM-dd');
|
||||
const date = format(getYesterday(), DATE_FORMAT);
|
||||
|
||||
this.currencies[pair] = resultExtended[pair]?.[date]?.marketPrice;
|
||||
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||
import { isToday, parse } from 'date-fns';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
@ -66,7 +67,7 @@ export class PerformanceChartDialog {
|
||||
value: benchmarkItem.value * coefficient
|
||||
});
|
||||
} else if (
|
||||
isToday(parse(historicalDataItem.date, 'yyyy-MM-dd', new Date()))
|
||||
isToday(parse(historicalDataItem.date, DATE_FORMAT, new Date()))
|
||||
) {
|
||||
this.benchmarkDataItems.push({
|
||||
date: historicalDataItem.date,
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||
import { format, isSameMonth, isToday, parseISO } from 'date-fns';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
@ -115,13 +116,13 @@ export class PositionDetailDialog implements OnDestroy {
|
||||
} else {
|
||||
// Add market price
|
||||
this.historicalDataItems.push({
|
||||
date: format(new Date(), 'yyyy-MM-dd'),
|
||||
date: format(new Date(), DATE_FORMAT),
|
||||
value: this.marketPrice
|
||||
});
|
||||
|
||||
// Add benchmark
|
||||
this.benchmarkDataItems.push({
|
||||
date: format(new Date(), 'yyyy-MM-dd'),
|
||||
date: format(new Date(), DATE_FORMAT),
|
||||
value: averagePrice
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user