|
@ -1,6 +1,7 @@ |
|
|
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; |
|
|
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; |
|
|
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; |
|
|
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; |
|
|
import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-order.interface'; |
|
|
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 { TransactionPointSymbol } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point-symbol.interface'; |
|
|
import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface'; |
|
|
import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface'; |
|
|
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; |
|
|
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; |
|
@ -32,6 +33,7 @@ import { Logger } from '@nestjs/common'; |
|
|
import { Big } from 'big.js'; |
|
|
import { Big } from 'big.js'; |
|
|
import { plainToClass } from 'class-transformer'; |
|
|
import { plainToClass } from 'class-transformer'; |
|
|
import { |
|
|
import { |
|
|
|
|
|
addMilliseconds, |
|
|
differenceInDays, |
|
|
differenceInDays, |
|
|
eachDayOfInterval, |
|
|
eachDayOfInterval, |
|
|
endOfDay, |
|
|
endOfDay, |
|
@ -863,6 +865,29 @@ export abstract class PortfolioCalculator { |
|
|
return chartDateMap; |
|
|
return chartDateMap; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private async computeAndCacheSnapshot() { |
|
|
|
|
|
const snapshot = await this.computeSnapshot(); |
|
|
|
|
|
|
|
|
|
|
|
const expiration = addMilliseconds( |
|
|
|
|
|
new Date(), |
|
|
|
|
|
this.configurationService.get('CACHE_QUOTES_TTL') |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
this.redisCacheService.set( |
|
|
|
|
|
this.redisCacheService.getPortfolioSnapshotKey({ |
|
|
|
|
|
filters: this.filters, |
|
|
|
|
|
userId: this.userId |
|
|
|
|
|
}), |
|
|
|
|
|
JSON.stringify(<PortfolioSnapshotValue>(<unknown>{ |
|
|
|
|
|
expiration: expiration.getTime(), |
|
|
|
|
|
portfolioSnapshot: snapshot |
|
|
|
|
|
})), |
|
|
|
|
|
0 |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
return snapshot; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@LogPerformance |
|
|
@LogPerformance |
|
|
private computeTransactionPoints() { |
|
|
private computeTransactionPoints() { |
|
|
this.transactionPoints = []; |
|
|
this.transactionPoints = []; |
|
@ -1006,19 +1031,33 @@ export abstract class PortfolioCalculator { |
|
|
private async initialize() { |
|
|
private async initialize() { |
|
|
const startTimeTotal = performance.now(); |
|
|
const startTimeTotal = performance.now(); |
|
|
|
|
|
|
|
|
const cachedSnapshot = await this.redisCacheService.get( |
|
|
let cachedPortfolioSnapshot: PortfolioSnapshot; |
|
|
|
|
|
let isCachedPortfolioSnapshotExpired = false; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
const cachedPortfolioSnapshotValue = await this.redisCacheService.get( |
|
|
this.redisCacheService.getPortfolioSnapshotKey({ |
|
|
this.redisCacheService.getPortfolioSnapshotKey({ |
|
|
filters: this.filters, |
|
|
filters: this.filters, |
|
|
userId: this.userId |
|
|
userId: this.userId |
|
|
}) |
|
|
}) |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
if (cachedSnapshot) { |
|
|
const { expiration, portfolioSnapshot }: PortfolioSnapshotValue = |
|
|
this.snapshot = plainToClass( |
|
|
JSON.parse(cachedPortfolioSnapshotValue); |
|
|
|
|
|
|
|
|
|
|
|
cachedPortfolioSnapshot = plainToClass( |
|
|
PortfolioSnapshot, |
|
|
PortfolioSnapshot, |
|
|
JSON.parse(cachedSnapshot) |
|
|
portfolioSnapshot |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if (isAfter(new Date(), new Date(expiration))) { |
|
|
|
|
|
isCachedPortfolioSnapshotExpired = true; |
|
|
|
|
|
} |
|
|
|
|
|
} catch {} |
|
|
|
|
|
|
|
|
|
|
|
if (cachedPortfolioSnapshot) { |
|
|
|
|
|
this.snapshot = cachedPortfolioSnapshot; |
|
|
|
|
|
|
|
|
Logger.debug( |
|
|
Logger.debug( |
|
|
`Fetched portfolio snapshot from cache in ${( |
|
|
`Fetched portfolio snapshot from cache in ${( |
|
|
(performance.now() - startTimeTotal) / |
|
|
(performance.now() - startTimeTotal) / |
|
@ -1026,17 +1065,14 @@ export abstract class PortfolioCalculator { |
|
|
).toFixed(3)} seconds`,
|
|
|
).toFixed(3)} seconds`,
|
|
|
'PortfolioCalculator' |
|
|
'PortfolioCalculator' |
|
|
); |
|
|
); |
|
|
} else { |
|
|
|
|
|
this.snapshot = await this.computeSnapshot(); |
|
|
|
|
|
|
|
|
|
|
|
this.redisCacheService.set( |
|
|
if (isCachedPortfolioSnapshotExpired) { |
|
|
this.redisCacheService.getPortfolioSnapshotKey({ |
|
|
// Compute in the background
|
|
|
filters: this.filters, |
|
|
this.computeAndCacheSnapshot(); |
|
|
userId: this.userId |
|
|
} |
|
|
}), |
|
|
} else { |
|
|
JSON.stringify(this.snapshot), |
|
|
// Wait for computation
|
|
|
this.configurationService.get('CACHE_QUOTES_TTL') |
|
|
this.snapshot = await this.computeAndCacheSnapshot(); |
|
|
); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|