Set monthly granulary for fx rates
This commit is contained in:
parent
8e158e1786
commit
669cf59269
@ -329,24 +329,18 @@ class PricesRepository {
|
|||||||
throw Error(`Cannot get single historical price from the database`);
|
throw Error(`Cannot get single historical price from the database`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pricesUsedForExchangeRates; // If we don't have a fx API key, we need to use the latest prices to compute the exchange rates
|
const [latestPrices] = await DB.query(`
|
||||||
if (!config.FIAT_PRICE.API_KEY) {
|
SELECT ${ApiPriceFields}
|
||||||
const [latestPrices] = await DB.query(`
|
FROM prices
|
||||||
SELECT ${ApiPriceFields}
|
ORDER BY time DESC
|
||||||
FROM prices
|
LIMIT 1
|
||||||
ORDER BY time DESC
|
`);
|
||||||
LIMIT 1
|
if (!Array.isArray(latestPrices)) {
|
||||||
`);
|
throw Error(`Cannot get single historical price from the database`);
|
||||||
if (!Array.isArray(latestPrices)) {
|
}
|
||||||
throw Error(`Cannot get single historical price from the database`);
|
|
||||||
}
|
|
||||||
pricesUsedForExchangeRates = latestPrices[0] as ApiPrice;
|
|
||||||
} else {
|
|
||||||
pricesUsedForExchangeRates = rates[0] as ApiPrice;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute fiat exchange rates
|
// Compute fiat exchange rates
|
||||||
let latestPrice = pricesUsedForExchangeRates;
|
let latestPrice = latestPrices[0] as ApiPrice;
|
||||||
if (!latestPrice || latestPrice.USD === -1) {
|
if (!latestPrice || latestPrice.USD === -1) {
|
||||||
latestPrice = priceUpdater.getEmptyPricesObj();
|
latestPrice = priceUpdater.getEmptyPricesObj();
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ const emptyRates = {
|
|||||||
SGD: -1,
|
SGD: -1,
|
||||||
THB: -1,
|
THB: -1,
|
||||||
TRY: -1,
|
TRY: -1,
|
||||||
|
USD: -1,
|
||||||
ZAR: -1,
|
ZAR: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ export interface PriceHistory {
|
|||||||
|
|
||||||
export interface ConversionFeed {
|
export interface ConversionFeed {
|
||||||
$getQuota(): Promise<any>;
|
$getQuota(): Promise<any>;
|
||||||
$fetchLatestConversionRates(): Promise<any>;
|
$fetchLatestConversionRates(): Promise<ConversionRates>;
|
||||||
$fetchConversionRates(date: string): Promise<any>;
|
$fetchConversionRates(date: string): Promise<ConversionRates>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConversionRates {
|
export interface ConversionRates {
|
||||||
@ -46,6 +46,7 @@ class PriceUpdater {
|
|||||||
public historyInserted = false;
|
public historyInserted = false;
|
||||||
private additionalCurrenciesHistoryInserted = false;
|
private additionalCurrenciesHistoryInserted = false;
|
||||||
private additionalCurrenciesHistoryRunning = false;
|
private additionalCurrenciesHistoryRunning = false;
|
||||||
|
private lastFailedHistoricalRun = 0;
|
||||||
private timeBetweenUpdatesMs = 360_0000 / config.MEMPOOL.PRICE_UPDATES_PER_HOUR;
|
private timeBetweenUpdatesMs = 360_0000 / config.MEMPOOL.PRICE_UPDATES_PER_HOUR;
|
||||||
private cyclePosition = -1;
|
private cyclePosition = -1;
|
||||||
private firstRun = true;
|
private firstRun = true;
|
||||||
@ -146,6 +147,11 @@ class PriceUpdater {
|
|||||||
this.additionalCurrenciesHistoryInserted = false;
|
this.additionalCurrenciesHistoryInserted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.lastFailedHistoricalRun > 0 && (Math.round(new Date().getTime() / 1000) - this.lastFailedHistoricalRun) > 60) {
|
||||||
|
// If the last attempt to insert missing prices failed, we try again after 60 seconds
|
||||||
|
this.additionalCurrenciesHistoryInserted = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (config.FIAT_PRICE.API_KEY && this.currencyConversionFeed && (Math.round(new Date().getTime() / 1000) - this.lastTimeConversionsRatesFetched) > 3600 * 24) {
|
if (config.FIAT_PRICE.API_KEY && this.currencyConversionFeed && (Math.round(new Date().getTime() / 1000) - this.lastTimeConversionsRatesFetched) > 3600 * 24) {
|
||||||
// Once a day, fetch conversion rates from api: we don't need more granularity for fiat currencies and have a limited number of requests
|
// Once a day, fetch conversion rates from api: we don't need more granularity for fiat currencies and have a limited number of requests
|
||||||
try {
|
try {
|
||||||
@ -165,6 +171,7 @@ class PriceUpdater {
|
|||||||
if (this.additionalCurrenciesHistoryInserted === false && config.DATABASE.ENABLED === true && config.FIAT_PRICE.API_KEY && !this.additionalCurrenciesHistoryRunning) {
|
if (this.additionalCurrenciesHistoryInserted === false && config.DATABASE.ENABLED === true && config.FIAT_PRICE.API_KEY && !this.additionalCurrenciesHistoryRunning) {
|
||||||
await this.$insertMissingAdditionalPrices();
|
await this.$insertMissingAdditionalPrices();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.err(`Cannot save BTC prices in db. Reason: ${e instanceof Error ? e.message : e}`, logger.tags.mining);
|
logger.err(`Cannot save BTC prices in db. Reason: ${e instanceof Error ? e.message : e}`, logger.tags.mining);
|
||||||
}
|
}
|
||||||
@ -393,46 +400,53 @@ class PriceUpdater {
|
|||||||
* We calculate the additional prices from the USD price and the conversion rates
|
* We calculate the additional prices from the USD price and the conversion rates
|
||||||
*/
|
*/
|
||||||
private async $insertMissingAdditionalPrices(): Promise<void> {
|
private async $insertMissingAdditionalPrices(): Promise<void> {
|
||||||
this.additionalCurrenciesHistoryRunning = true;
|
this.lastFailedHistoricalRun = 0;
|
||||||
const priceTimesToFill = await PricesRepository.$getPricesTimesWithMissingFields();
|
const priceTimesToFill = await PricesRepository.$getPricesTimesWithMissingFields();
|
||||||
if (priceTimesToFill.length === 0) {
|
if (priceTimesToFill.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
const remainingQuota = await this.currencyConversionFeed?.$getQuota();
|
||||||
|
if (remainingQuota['month']['remaining'] < 500) { // We need some calls left for the daily updates
|
||||||
|
logger.debug(`Not enough currency API credit to insert missing prices in ${priceTimesToFill.length} rows (${remainingQuota['month']['remaining']} calls left).`, logger.tags.mining);
|
||||||
|
this.additionalCurrenciesHistoryInserted = true; // Do not try again until next day
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.err(`Cannot fetch currency API credit, insertion of missing prices aborted. Reason: ${(e instanceof Error ? e.message : e)}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.additionalCurrenciesHistoryRunning = true;
|
||||||
logger.debug(`Fetching missing conversion rates from external API to fill ${priceTimesToFill.length} rows`, logger.tags.mining);
|
logger.debug(`Fetching missing conversion rates from external API to fill ${priceTimesToFill.length} rows`, logger.tags.mining);
|
||||||
|
|
||||||
let conversionRates: { [timestamp: number]: ConversionRates } = {};
|
let conversionRates: { [timestamp: number]: ConversionRates } = {};
|
||||||
let totalInserted = 0;
|
let totalInserted = 0;
|
||||||
|
|
||||||
let requestCounter = 0;
|
for (let i = 0; i < priceTimesToFill.length; i++) {
|
||||||
|
const priceTime = priceTimesToFill[i];
|
||||||
for (const priceTime of priceTimesToFill) {
|
|
||||||
const missingLegacyCurrencies = this.getMissingLegacyCurrencies(priceTime); // In the case a legacy currency (EUR, GBP, CAD, CHF, AUD, JPY)
|
const missingLegacyCurrencies = this.getMissingLegacyCurrencies(priceTime); // In the case a legacy currency (EUR, GBP, CAD, CHF, AUD, JPY)
|
||||||
const year = new Date(priceTime.time * 1000).getFullYear(); // is missing, we use the same process as for the new currencies
|
const year = new Date(priceTime.time * 1000).getFullYear(); // is missing, we use the same process as for the new currencies
|
||||||
const yearTimestamp = new Date(year, 0, 1).getTime() / 1000;
|
const month = new Date(priceTime.time * 1000).getMonth();
|
||||||
if (conversionRates[yearTimestamp] === undefined) {
|
const yearMonthTimestamp = new Date(year, month, 1).getTime() / 1000;
|
||||||
try {
|
if (conversionRates[yearMonthTimestamp] === undefined) {
|
||||||
if (requestCounter >= 10) {
|
conversionRates[yearMonthTimestamp] = await this.currencyConversionFeed?.$fetchConversionRates(`${year}-${month + 1 < 10 ? `0${month + 1}` : `${month + 1}`}-01`) || { USD: -1 };
|
||||||
await new Promise(resolve => setTimeout(resolve, 60_000)); // avoid getting 429'd
|
if (conversionRates[yearMonthTimestamp]['USD'] < 0) {
|
||||||
requestCounter = 0;
|
logger.err(`Cannot fetch conversion rates from the API for ${year}-${month + 1 < 10 ? `0${month + 1}` : `${month + 1}`}-01. Aborting insertion of missing prices.`, logger.tags.mining);
|
||||||
}
|
this.lastFailedHistoricalRun = Math.round(new Date().getTime() / 1000);
|
||||||
conversionRates[yearTimestamp] = await this.currencyConversionFeed?.$fetchConversionRates(`${year}-01-01`);
|
break;
|
||||||
++requestCounter;
|
|
||||||
} catch (e) {
|
|
||||||
logger.err(`Cannot fetch conversion rates from the API for year ${year}. Reason: ${(e instanceof Error ? e.message : e)}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversionRates[yearTimestamp] === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prices: ApiPrice = this.getEmptyPricesObj();
|
const prices: ApiPrice = this.getEmptyPricesObj();
|
||||||
|
|
||||||
let willInsert = false;
|
let willInsert = false;
|
||||||
for (const conversionCurrency of this.newCurrencies.concat(missingLegacyCurrencies)) {
|
for (const conversionCurrency of this.newCurrencies.concat(missingLegacyCurrencies)) {
|
||||||
if (conversionRates[yearTimestamp][conversionCurrency] > 0 && priceTime.USD * conversionRates[yearTimestamp][conversionCurrency] < MAX_PRICES[conversionCurrency]) {
|
if (conversionRates[yearMonthTimestamp][conversionCurrency] > 0 && priceTime.USD * conversionRates[yearMonthTimestamp][conversionCurrency] < MAX_PRICES[conversionCurrency]) {
|
||||||
prices[conversionCurrency] = year >= 2013 ? Math.round(priceTime.USD * conversionRates[yearTimestamp][conversionCurrency]) : Math.round(priceTime.USD * conversionRates[yearTimestamp][conversionCurrency] * 100) / 100;
|
prices[conversionCurrency] = year >= 2013 ? Math.round(priceTime.USD * conversionRates[yearMonthTimestamp][conversionCurrency]) : Math.round(priceTime.USD * conversionRates[yearMonthTimestamp][conversionCurrency] * 100) / 100;
|
||||||
willInsert = true;
|
willInsert = true;
|
||||||
|
} else {
|
||||||
|
prices[conversionCurrency] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,6 +455,7 @@ class PriceUpdater {
|
|||||||
++totalInserted;
|
++totalInserted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Inserted ${totalInserted} missing additional currency prices into the db`, logger.tags.mining);
|
logger.debug(`Inserted ${totalInserted} missing additional currency prices into the db`, logger.tags.mining);
|
||||||
this.additionalCurrenciesHistoryInserted = true;
|
this.additionalCurrenciesHistoryInserted = true;
|
||||||
this.additionalCurrenciesHistoryRunning = false;
|
this.additionalCurrenciesHistoryRunning = false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user