|
|
@ -12,8 +12,10 @@ import { |
|
|
ResponseError |
|
|
ResponseError |
|
|
} from '@ghostfolio/common/interfaces'; |
|
|
} from '@ghostfolio/common/interfaces'; |
|
|
|
|
|
|
|
|
|
|
|
import { utc } from '@date-fns/utc'; |
|
|
import { Injectable } from '@nestjs/common'; |
|
|
import { Injectable } from '@nestjs/common'; |
|
|
import { isBefore, isToday } from 'date-fns'; |
|
|
import { Type as ActivityType } from '@prisma/client'; |
|
|
|
|
|
import { compareDesc, isBefore, isSameDay } from 'date-fns'; |
|
|
import { isEmpty, uniqBy } from 'lodash-es'; |
|
|
import { isEmpty, uniqBy } from 'lodash-es'; |
|
|
|
|
|
|
|
|
import { GetValueObject } from './interfaces/get-value-object.interface'; |
|
|
import { GetValueObject } from './interfaces/get-value-object.interface'; |
|
|
@ -122,44 +124,19 @@ export class CurrentRateService { |
|
|
if (!isEmpty(quoteErrors)) { |
|
|
if (!isEmpty(quoteErrors)) { |
|
|
for (const { dataSource, symbol } of quoteErrors) { |
|
|
for (const { dataSource, symbol } of quoteErrors) { |
|
|
try { |
|
|
try { |
|
|
// If missing quote, fallback to the latest available historical market price
|
|
|
const valueOfToday = response.values.find((currentValue) => { |
|
|
let value: GetValueObject = response.values.find((currentValue) => { |
|
|
|
|
|
return ( |
|
|
return ( |
|
|
currentValue.dataSource === dataSource && |
|
|
currentValue.dataSource === dataSource && |
|
|
currentValue.symbol === symbol && |
|
|
currentValue.symbol === symbol && |
|
|
isToday(currentValue.date) |
|
|
isSameDay(currentValue.date, today, { in: utc }) |
|
|
); |
|
|
); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
if (!value) { |
|
|
if (valueOfToday?.marketPrice) { |
|
|
const latestMarketData = await this.marketDataService.getLatest({ |
|
|
continue; |
|
|
dataSource, |
|
|
|
|
|
symbol |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
let marketPrice = latestMarketData?.marketPrice; |
|
|
|
|
|
|
|
|
|
|
|
if (!marketPrice) { |
|
|
|
|
|
// Fallback to unit price of latest activity
|
|
|
|
|
|
const latestActivity = |
|
|
|
|
|
await this.activitiesService.getLatestActivity({ |
|
|
|
|
|
dataSource, |
|
|
|
|
|
symbol |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
marketPrice = latestActivity?.unitPrice ?? 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
value = { |
|
|
|
|
|
dataSource, |
|
|
|
|
|
marketPrice, |
|
|
|
|
|
symbol, |
|
|
|
|
|
date: today |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
response.values.push(value); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// If missing quote, fallback to the latest available historical market price
|
|
|
const [latestValue] = response.values |
|
|
const [latestValue] = response.values |
|
|
.filter((currentValue) => { |
|
|
.filter((currentValue) => { |
|
|
return ( |
|
|
return ( |
|
|
@ -169,18 +146,43 @@ export class CurrentRateService { |
|
|
); |
|
|
); |
|
|
}) |
|
|
}) |
|
|
.sort((a, b) => { |
|
|
.sort((a, b) => { |
|
|
if (a.date < b.date) { |
|
|
return compareDesc(a.date, b.date); |
|
|
return 1; |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (a.date > b.date) { |
|
|
let marketPrice = latestValue?.marketPrice; |
|
|
return -1; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return 0; |
|
|
if (!marketPrice) { |
|
|
|
|
|
// Fallback to latest market price outside of the date range
|
|
|
|
|
|
const latestMarketData = await this.marketDataService.getLatest({ |
|
|
|
|
|
dataSource, |
|
|
|
|
|
symbol |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
value.marketPrice = latestValue.marketPrice; |
|
|
marketPrice = latestMarketData?.marketPrice; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!marketPrice) { |
|
|
|
|
|
// Fallback to unit price of latest buy or sell activity
|
|
|
|
|
|
const latestActivity = |
|
|
|
|
|
await this.activitiesService.getLatestActivity({ |
|
|
|
|
|
dataSource, |
|
|
|
|
|
symbol, |
|
|
|
|
|
types: [ActivityType.BUY, ActivityType.SELL] |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
marketPrice = latestActivity?.unitPrice ?? 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (valueOfToday) { |
|
|
|
|
|
valueOfToday.marketPrice = marketPrice; |
|
|
|
|
|
} else { |
|
|
|
|
|
response.values.push({ |
|
|
|
|
|
dataSource, |
|
|
|
|
|
marketPrice, |
|
|
|
|
|
symbol, |
|
|
|
|
|
date: today |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
} catch {} |
|
|
} catch {} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -189,8 +191,10 @@ export class CurrentRateService { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private containsToday(dates: Date[]): boolean { |
|
|
private containsToday(dates: Date[]): boolean { |
|
|
|
|
|
const today = resetHours(new Date()); |
|
|
|
|
|
|
|
|
for (const date of dates) { |
|
|
for (const date of dates) { |
|
|
if (isToday(date)) { |
|
|
if (isSameDay(date, today, { in: utc })) { |
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|