mirror of
https://github.com/mempool/mempool.git
synced 2024-11-20 10:21:52 +01:00
Add daily historical price - show USD in block fee reward charts
This commit is contained in:
parent
40634a0eb8
commit
a97675c538
@ -279,7 +279,8 @@ class DatabaseMigration {
|
|||||||
await this.$executeQuery('ALTER TABLE `nodes` ADD longitude double NULL DEFAULT NULL');
|
await this.$executeQuery('ALTER TABLE `nodes` ADD longitude double NULL DEFAULT NULL');
|
||||||
await this.$executeQuery('ALTER TABLE `nodes` ADD latitude double NULL DEFAULT NULL');
|
await this.$executeQuery('ALTER TABLE `nodes` ADD latitude double NULL DEFAULT NULL');
|
||||||
}
|
}
|
||||||
if (databaseSchemaVersion < 25 && isBitcoin == true) { // Link blocks to prices
|
|
||||||
|
if (databaseSchemaVersion < 30 && isBitcoin == true) { // Link blocks to prices
|
||||||
await this.$executeQuery('ALTER TABLE `prices` ADD `id` int NULL AUTO_INCREMENT UNIQUE');
|
await this.$executeQuery('ALTER TABLE `prices` ADD `id` int NULL AUTO_INCREMENT UNIQUE');
|
||||||
await this.$executeQuery('DROP TABLE IF EXISTS `blocks_prices`');
|
await this.$executeQuery('DROP TABLE IF EXISTS `blocks_prices`');
|
||||||
await this.$executeQuery(this.getCreateBlocksPricesTableQuery(), await this.$checkIfTableExists('blocks_prices'));
|
await this.$executeQuery(this.getCreateBlocksPricesTableQuery(), await this.$checkIfTableExists('blocks_prices'));
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IndexedDifficultyAdjustment, PoolInfo, PoolStats, RewardStats } from '../../mempool.interfaces';
|
import { BlockPrice, PoolInfo, PoolStats, RewardStats } from '../../mempool.interfaces';
|
||||||
import BlocksRepository from '../../repositories/BlocksRepository';
|
import BlocksRepository from '../../repositories/BlocksRepository';
|
||||||
import PoolsRepository from '../../repositories/PoolsRepository';
|
import PoolsRepository from '../../repositories/PoolsRepository';
|
||||||
import HashratesRepository from '../../repositories/HashratesRepository';
|
import HashratesRepository from '../../repositories/HashratesRepository';
|
||||||
@ -7,11 +7,10 @@ import logger from '../../logger';
|
|||||||
import { Common } from '../common';
|
import { Common } from '../common';
|
||||||
import loadingIndicators from '../loading-indicators';
|
import loadingIndicators from '../loading-indicators';
|
||||||
import { escape } from 'mysql2';
|
import { escape } from 'mysql2';
|
||||||
import indexer from '../../indexer';
|
|
||||||
import DifficultyAdjustmentsRepository from '../../repositories/DifficultyAdjustmentsRepository';
|
import DifficultyAdjustmentsRepository from '../../repositories/DifficultyAdjustmentsRepository';
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import BlocksAuditsRepository from '../../repositories/BlocksAuditsRepository';
|
import BlocksAuditsRepository from '../../repositories/BlocksAuditsRepository';
|
||||||
import PricesRepository from '../repositories/PricesRepository';
|
import PricesRepository from '../../repositories/PricesRepository';
|
||||||
|
|
||||||
class Mining {
|
class Mining {
|
||||||
blocksPriceIndexingRunning = false;
|
blocksPriceIndexingRunning = false;
|
||||||
@ -34,7 +33,7 @@ class Mining {
|
|||||||
*/
|
*/
|
||||||
public async $getHistoricalBlockFees(interval: string | null = null): Promise<any> {
|
public async $getHistoricalBlockFees(interval: string | null = null): Promise<any> {
|
||||||
return await BlocksRepository.$getHistoricalBlockFees(
|
return await BlocksRepository.$getHistoricalBlockFees(
|
||||||
this.getTimeRangeForAmounts(interval),
|
this.getTimeRange(interval, 5),
|
||||||
Common.getSqlInterval(interval)
|
Common.getSqlInterval(interval)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -44,7 +43,7 @@ class Mining {
|
|||||||
*/
|
*/
|
||||||
public async $getHistoricalBlockRewards(interval: string | null = null): Promise<any> {
|
public async $getHistoricalBlockRewards(interval: string | null = null): Promise<any> {
|
||||||
return await BlocksRepository.$getHistoricalBlockRewards(
|
return await BlocksRepository.$getHistoricalBlockRewards(
|
||||||
this.getTimeRangeForAmounts(interval),
|
this.getTimeRange(interval),
|
||||||
Common.getSqlInterval(interval)
|
Common.getSqlInterval(interval)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -529,33 +528,18 @@ class Mining {
|
|||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTimeRangeForAmounts(interval: string | null): number {
|
private getTimeRange(interval: string | null, scale = 1): number {
|
||||||
switch (interval) {
|
switch (interval) {
|
||||||
case '3y': return 1296000;
|
case '3y': return 43200 * scale; // 12h
|
||||||
case '2y': return 864000;
|
case '2y': return 28800 * scale; // 8h
|
||||||
case '1y': return 432000;
|
case '1y': return 28800 * scale; // 8h
|
||||||
case '6m': return 216000;
|
case '6m': return 10800 * scale; // 3h
|
||||||
case '3m': return 108000;
|
case '3m': return 7200 * scale; // 2h
|
||||||
case '1m': return 36000;
|
case '1m': return 1800 * scale; // 30min
|
||||||
case '1w': return 8400;
|
case '1w': return 300 * scale; // 5min
|
||||||
case '3d': return 3600;
|
case '3d': return 1 * scale;
|
||||||
case '24h': return 1200;
|
case '24h': return 1 * scale;
|
||||||
default: return 3888000;
|
default: return 86400 * scale;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getTimeRange(interval: string | null): number {
|
|
||||||
switch (interval) {
|
|
||||||
case '3y': return 43200; // 12h
|
|
||||||
case '2y': return 28800; // 8h
|
|
||||||
case '1y': return 28800; // 8h
|
|
||||||
case '6m': return 10800; // 3h
|
|
||||||
case '3m': return 7200; // 2h
|
|
||||||
case '1m': return 1800; // 30min
|
|
||||||
case '1w': return 300; // 5min
|
|
||||||
case '3d': return 1;
|
|
||||||
case '24h': return 1;
|
|
||||||
default: return 86400;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import icons from './api/liquid/icons';
|
|||||||
import { Common } from './api/common';
|
import { Common } from './api/common';
|
||||||
import poolsUpdater from './tasks/pools-updater';
|
import poolsUpdater from './tasks/pools-updater';
|
||||||
import indexer from './indexer';
|
import indexer from './indexer';
|
||||||
import priceUpdater from './tasks/price-updater';
|
|
||||||
import nodesRoutes from './api/explorer/nodes.routes';
|
import nodesRoutes from './api/explorer/nodes.routes';
|
||||||
import channelsRoutes from './api/explorer/channels.routes';
|
import channelsRoutes from './api/explorer/channels.routes';
|
||||||
import generalLightningRoutes from './api/explorer/general.routes';
|
import generalLightningRoutes from './api/explorer/general.routes';
|
||||||
@ -35,8 +34,6 @@ import miningRoutes from "./api/mining/mining-routes";
|
|||||||
import bisqRoutes from "./api/bisq/bisq.routes";
|
import bisqRoutes from "./api/bisq/bisq.routes";
|
||||||
import liquidRoutes from "./api/liquid/liquid.routes";
|
import liquidRoutes from "./api/liquid/liquid.routes";
|
||||||
import bitcoinRoutes from "./api/bitcoin/bitcoin.routes";
|
import bitcoinRoutes from "./api/bitcoin/bitcoin.routes";
|
||||||
import BlocksAuditsRepository from './repositories/BlocksAuditsRepository';
|
|
||||||
import mining from "./api/mining";
|
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
@ -168,7 +165,6 @@ class Server {
|
|||||||
await blocks.$updateBlocks();
|
await blocks.$updateBlocks();
|
||||||
await memPool.$updateMempool();
|
await memPool.$updateMempool();
|
||||||
indexer.$run();
|
indexer.$run();
|
||||||
priceUpdater.$run().then(mining.$indexBlockPrices.bind(this));
|
|
||||||
|
|
||||||
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
||||||
this.currentBackendRetryInterval = 5;
|
this.currentBackendRetryInterval = 5;
|
||||||
|
@ -5,6 +5,7 @@ import mining from './api/mining/mining';
|
|||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
import HashratesRepository from './repositories/HashratesRepository';
|
import HashratesRepository from './repositories/HashratesRepository';
|
||||||
import bitcoinClient from './api/bitcoin/bitcoin-client';
|
import bitcoinClient from './api/bitcoin/bitcoin-client';
|
||||||
|
import priceUpdater from './tasks/price-updater';
|
||||||
|
|
||||||
class Indexer {
|
class Indexer {
|
||||||
runIndexer = true;
|
runIndexer = true;
|
||||||
@ -38,6 +39,8 @@ class Indexer {
|
|||||||
logger.debug(`Running mining indexer`);
|
logger.debug(`Running mining indexer`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await priceUpdater.$run();
|
||||||
|
|
||||||
const chainValid = await blocks.$generateBlockDatabase();
|
const chainValid = await blocks.$generateBlockDatabase();
|
||||||
if (chainValid === false) {
|
if (chainValid === false) {
|
||||||
// Chain of block hash was invalid, so we need to reindex. Stop here and continue at the next iteration
|
// Chain of block hash was invalid, so we need to reindex. Stop here and continue at the next iteration
|
||||||
@ -47,6 +50,7 @@ class Indexer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await mining.$indexBlockPrices();
|
||||||
await mining.$indexDifficultyAdjustments();
|
await mining.$indexDifficultyAdjustments();
|
||||||
await this.$resetHashratesIndexingState(); // TODO - Remove this as it's not efficient
|
await this.$resetHashratesIndexingState(); // TODO - Remove this as it's not efficient
|
||||||
await mining.$generateNetworkHashrateHistory();
|
await mining.$generateNetworkHashrateHistory();
|
||||||
|
@ -274,7 +274,7 @@ class BlocksRepository {
|
|||||||
merkle_root,
|
merkle_root,
|
||||||
previous_block_hash as previousblockhash,
|
previous_block_hash as previousblockhash,
|
||||||
avg_fee,
|
avg_fee,
|
||||||
avg_fee_rate,
|
avg_fee_rate
|
||||||
FROM blocks
|
FROM blocks
|
||||||
WHERE pool_id = ?`;
|
WHERE pool_id = ?`;
|
||||||
params.push(pool.id);
|
params.push(pool.id);
|
||||||
@ -288,6 +288,7 @@ class BlocksRepository {
|
|||||||
LIMIT 10`;
|
LIMIT 10`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log(query, params);
|
||||||
const [rows] = await DB.query(query, params);
|
const [rows] = await DB.query(query, params);
|
||||||
|
|
||||||
const blocks: BlockExtended[] = [];
|
const blocks: BlockExtended[] = [];
|
||||||
@ -360,12 +361,12 @@ class BlocksRepository {
|
|||||||
SELECT *, blocks.height, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, hash as id,
|
SELECT *, blocks.height, UNIX_TIMESTAMP(blocks.blockTimestamp) as blockTimestamp, hash as id,
|
||||||
pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug,
|
pools.id as pool_id, pools.name as pool_name, pools.link as pool_link, pools.slug as pool_slug,
|
||||||
pools.addresses as pool_addresses, pools.regexes as pool_regexes,
|
pools.addresses as pool_addresses, pools.regexes as pool_regexes,
|
||||||
previous_block_hash as previousblockhash,
|
previous_block_hash as previousblockhash
|
||||||
FROM blocks
|
FROM blocks
|
||||||
JOIN pools ON blocks.pool_id = pools.id
|
JOIN pools ON blocks.pool_id = pools.id
|
||||||
WHERE hash = '${hash}';
|
WHERE hash = '?';
|
||||||
`;
|
`;
|
||||||
const [rows]: any[] = await DB.query(query);
|
const [rows]: any[] = await DB.query(query, [hash]);
|
||||||
|
|
||||||
if (rows.length <= 0) {
|
if (rows.length <= 0) {
|
||||||
return null;
|
return null;
|
||||||
@ -488,8 +489,11 @@ class BlocksRepository {
|
|||||||
let query = `SELECT
|
let query = `SELECT
|
||||||
CAST(AVG(blocks.height) as INT) as avgHeight,
|
CAST(AVG(blocks.height) as INT) as avgHeight,
|
||||||
CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
|
CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
|
||||||
CAST(AVG(fees) as INT) as avgFees
|
CAST(AVG(fees) as INT) as avgFees,
|
||||||
|
prices.USD
|
||||||
FROM blocks
|
FROM blocks
|
||||||
|
JOIN blocks_prices on blocks_prices.height = blocks.height
|
||||||
|
JOIN prices on prices.id = blocks_prices.price_id
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (interval !== null) {
|
if (interval !== null) {
|
||||||
@ -514,8 +518,11 @@ class BlocksRepository {
|
|||||||
let query = `SELECT
|
let query = `SELECT
|
||||||
CAST(AVG(blocks.height) as INT) as avgHeight,
|
CAST(AVG(blocks.height) as INT) as avgHeight,
|
||||||
CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
|
CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp,
|
||||||
CAST(AVG(reward) as INT) as avgRewards
|
CAST(AVG(reward) as INT) as avgRewards,
|
||||||
|
prices.USD
|
||||||
FROM blocks
|
FROM blocks
|
||||||
|
JOIN blocks_prices on blocks_prices.height = blocks.height
|
||||||
|
JOIN prices on prices.id = blocks_prices.price_id
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (interval !== null) {
|
if (interval !== null) {
|
||||||
@ -663,22 +670,6 @@ class BlocksRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Save block price
|
|
||||||
*/
|
|
||||||
public async $saveBlockPrice(blockPrice: BlockPrice): Promise<void> {
|
|
||||||
try {
|
|
||||||
await DB.query(`INSERT INTO blocks_prices(height, price_id) VALUE (?, ?)`, [blockPrice.height, blockPrice.priceId]);
|
|
||||||
} catch (e: any) {
|
|
||||||
if (e.errno === 1062) { // ER_DUP_ENTRY - This scenario is possible upon node backend restart
|
|
||||||
logger.debug(`Cannot save block price for block ${blockPrice.height} because it has already been indexed, ignoring`);
|
|
||||||
} else {
|
|
||||||
logger.err(`Cannot save block price into db. Reason: ` + (e instanceof Error ? e.message : e));
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save block price by batch
|
* Save block price by batch
|
||||||
*/
|
*/
|
||||||
|
@ -16,7 +16,7 @@ class BitfinexApi implements PriceFeed {
|
|||||||
return response ? parseInt(response['last_price'], 10) : -1;
|
return response ? parseInt(response['last_price'], 10) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $fetchRecentHourlyPrice(currencies: string[]): Promise<PriceHistory> {
|
public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise<PriceHistory> {
|
||||||
const priceHistory: PriceHistory = {};
|
const priceHistory: PriceHistory = {};
|
||||||
|
|
||||||
for (const currency of currencies) {
|
for (const currency of currencies) {
|
||||||
@ -24,7 +24,7 @@ class BitfinexApi implements PriceFeed {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await query(this.urlHist.replace('{GRANULARITY}', '1h').replace('{CURRENCY}', currency));
|
const response = await query(this.urlHist.replace('{GRANULARITY}', type === 'hour' ? '1h' : '1D').replace('{CURRENCY}', currency));
|
||||||
const pricesRaw = response ? response : [];
|
const pricesRaw = response ? response : [];
|
||||||
|
|
||||||
for (const price of pricesRaw as any[]) {
|
for (const price of pricesRaw as any[]) {
|
||||||
|
@ -16,7 +16,7 @@ class BitflyerApi implements PriceFeed {
|
|||||||
return response ? parseInt(response['ltp'], 10) : -1;
|
return response ? parseInt(response['ltp'], 10) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $fetchRecentHourlyPrice(currencies: string[]): Promise<PriceHistory> {
|
public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise<PriceHistory> {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ class CoinbaseApi implements PriceFeed {
|
|||||||
return response ? parseInt(response['data']['amount'], 10) : -1;
|
return response ? parseInt(response['data']['amount'], 10) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $fetchRecentHourlyPrice(currencies: string[]): Promise<PriceHistory> {
|
public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise<PriceHistory> {
|
||||||
const priceHistory: PriceHistory = {};
|
const priceHistory: PriceHistory = {};
|
||||||
|
|
||||||
for (const currency of currencies) {
|
for (const currency of currencies) {
|
||||||
@ -24,7 +24,7 @@ class CoinbaseApi implements PriceFeed {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await query(this.urlHist.replace('{GRANULARITY}', '3600').replace('{CURRENCY}', currency));
|
const response = await query(this.urlHist.replace('{GRANULARITY}', type === 'hour' ? '3600' : '86400').replace('{CURRENCY}', currency));
|
||||||
const pricesRaw = response ? response : [];
|
const pricesRaw = response ? response : [];
|
||||||
|
|
||||||
for (const price of pricesRaw as any[]) {
|
for (const price of pricesRaw as any[]) {
|
||||||
|
@ -16,7 +16,7 @@ class FtxApi implements PriceFeed {
|
|||||||
return response ? parseInt(response['result']['last'], 10) : -1;
|
return response ? parseInt(response['result']['last'], 10) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $fetchRecentHourlyPrice(currencies: string[]): Promise<PriceHistory> {
|
public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise<PriceHistory> {
|
||||||
const priceHistory: PriceHistory = {};
|
const priceHistory: PriceHistory = {};
|
||||||
|
|
||||||
for (const currency of currencies) {
|
for (const currency of currencies) {
|
||||||
@ -24,7 +24,7 @@ class FtxApi implements PriceFeed {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await query(this.urlHist.replace('{GRANULARITY}', '3600').replace('{CURRENCY}', currency));
|
const response = await query(this.urlHist.replace('{GRANULARITY}', type === 'hour' ? '3600' : '86400').replace('{CURRENCY}', currency));
|
||||||
const pricesRaw = response ? response['result'] : [];
|
const pricesRaw = response ? response['result'] : [];
|
||||||
|
|
||||||
for (const price of pricesRaw as any[]) {
|
for (const price of pricesRaw as any[]) {
|
||||||
|
@ -16,7 +16,7 @@ class GeminiApi implements PriceFeed {
|
|||||||
return response ? parseInt(response['last'], 10) : -1;
|
return response ? parseInt(response['last'], 10) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $fetchRecentHourlyPrice(currencies: string[]): Promise<PriceHistory> {
|
public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise<PriceHistory> {
|
||||||
const priceHistory: PriceHistory = {};
|
const priceHistory: PriceHistory = {};
|
||||||
|
|
||||||
for (const currency of currencies) {
|
for (const currency of currencies) {
|
||||||
@ -24,7 +24,7 @@ class GeminiApi implements PriceFeed {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await query(this.urlHist.replace('{GRANULARITY}', '1hr').replace('{CURRENCY}', currency));
|
const response = await query(this.urlHist.replace('{GRANULARITY}', type === 'hour' ? '1hr' : '1day').replace('{CURRENCY}', currency));
|
||||||
const pricesRaw = response ? response : [];
|
const pricesRaw = response ? response : [];
|
||||||
|
|
||||||
for (const price of pricesRaw as any[]) {
|
for (const price of pricesRaw as any[]) {
|
||||||
|
@ -26,7 +26,7 @@ class KrakenApi implements PriceFeed {
|
|||||||
return response ? parseInt(response['result'][this.getTicker(currency)]['c'][0], 10) : -1;
|
return response ? parseInt(response['result'][this.getTicker(currency)]['c'][0], 10) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $fetchRecentHourlyPrice(currencies: string[]): Promise<PriceHistory> {
|
public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise<PriceHistory> {
|
||||||
const priceHistory: PriceHistory = {};
|
const priceHistory: PriceHistory = {};
|
||||||
|
|
||||||
for (const currency of currencies) {
|
for (const currency of currencies) {
|
||||||
|
@ -16,7 +16,7 @@ export interface PriceFeed {
|
|||||||
currencies: string[];
|
currencies: string[];
|
||||||
|
|
||||||
$fetchPrice(currency): Promise<number>;
|
$fetchPrice(currency): Promise<number>;
|
||||||
$fetchRecentHourlyPrice(currencies: string[]): Promise<PriceHistory>;
|
$fetchRecentPrice(currencies: string[], type: string): Promise<PriceHistory>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PriceHistory {
|
export interface PriceHistory {
|
||||||
@ -185,7 +185,8 @@ class PriceUpdater {
|
|||||||
await new KrakenApi().$insertHistoricalPrice();
|
await new KrakenApi().$insertHistoricalPrice();
|
||||||
|
|
||||||
// Insert missing recent hourly prices
|
// Insert missing recent hourly prices
|
||||||
await this.$insertMissingRecentPrices();
|
await this.$insertMissingRecentPrices('day');
|
||||||
|
await this.$insertMissingRecentPrices('hour');
|
||||||
|
|
||||||
this.historyInserted = true;
|
this.historyInserted = true;
|
||||||
this.lastHistoricalRun = new Date().getTime();
|
this.lastHistoricalRun = new Date().getTime();
|
||||||
@ -195,17 +196,17 @@ class PriceUpdater {
|
|||||||
* Find missing hourly prices and insert them in the database
|
* Find missing hourly prices and insert them in the database
|
||||||
* It has a limited backward range and it depends on which API are available
|
* It has a limited backward range and it depends on which API are available
|
||||||
*/
|
*/
|
||||||
private async $insertMissingRecentPrices(): Promise<void> {
|
private async $insertMissingRecentPrices(type: 'hour' | 'day'): Promise<void> {
|
||||||
const existingPriceTimes = await PricesRepository.$getPricesTimes();
|
const existingPriceTimes = await PricesRepository.$getPricesTimes();
|
||||||
|
|
||||||
logger.info(`Fetching hourly price history from exchanges and saving missing ones into the database, this may take a while`);
|
logger.info(`Fetching ${type === 'day' ? 'dai' : 'hour'}ly price history from exchanges and saving missing ones into the database, this may take a while`);
|
||||||
|
|
||||||
const historicalPrices: PriceHistory[] = [];
|
const historicalPrices: PriceHistory[] = [];
|
||||||
|
|
||||||
// Fetch all historical hourly prices
|
// Fetch all historical hourly prices
|
||||||
for (const feed of this.feeds) {
|
for (const feed of this.feeds) {
|
||||||
try {
|
try {
|
||||||
historicalPrices.push(await feed.$fetchRecentHourlyPrice(this.currencies));
|
historicalPrices.push(await feed.$fetchRecentPrice(this.currencies, type));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err(`Cannot fetch hourly historical price from ${feed.name}. Ignoring this feed. Reason: ${e instanceof Error ? e.message : e}`);
|
logger.err(`Cannot fetch hourly historical price from ${feed.name}. Ignoring this feed. Reason: ${e instanceof Error ? e.message : e}`);
|
||||||
}
|
}
|
||||||
@ -252,9 +253,9 @@ class PriceUpdater {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (totalInserted > 0) {
|
if (totalInserted > 0) {
|
||||||
logger.notice(`Inserted ${totalInserted} hourly historical prices into the db`);
|
logger.notice(`Inserted ${totalInserted} ${type === 'day' ? 'dai' : 'hour'}ly historical prices into the db`);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(`Inserted ${totalInserted} hourly historical prices into the db`);
|
logger.debug(`Inserted ${totalInserted} ${type === 'day' ? 'dai' : 'hour'}ly historical prices into the db`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,6 @@
|
|||||||
</button>
|
</button>
|
||||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats">
|
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats">
|
||||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 144">
|
|
||||||
<input ngbButton type="radio" [value]="'24h'" fragment="24h" [routerLink]="['/graphs/mining/block-fees' | relativeUrl]"> 24h
|
|
||||||
</label>
|
|
||||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 432">
|
|
||||||
<input ngbButton type="radio" [value]="'3d'" fragment="3d" [routerLink]="['/graphs/mining/block-fees' | relativeUrl]"> 3D
|
|
||||||
</label>
|
|
||||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 1008">
|
|
||||||
<input ngbButton type="radio" [value]="'1w'" fragment="1w" [routerLink]="['/graphs/mining/block-fees' | relativeUrl]"> 1W
|
|
||||||
</label>
|
|
||||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 4320">
|
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 4320">
|
||||||
<input ngbButton type="radio" [value]="'1m'" fragment="1m" [routerLink]="['/graphs/mining/block-fees' | relativeUrl]"> 1M
|
<input ngbButton type="radio" [value]="'1m'" fragment="1m" [routerLink]="['/graphs/mining/block-fees' | relativeUrl]"> 1M
|
||||||
</label>
|
</label>
|
||||||
|
@ -60,14 +60,14 @@ export class BlockFeesGraphComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.seoService.setTitle($localize`:@@6c453b11fd7bd159ae30bc381f367bc736d86909:Block Fees`);
|
this.seoService.setTitle($localize`:@@6c453b11fd7bd159ae30bc381f367bc736d86909:Block Fees`);
|
||||||
this.miningWindowPreference = this.miningService.getDefaultTimespan('24h');
|
this.miningWindowPreference = this.miningService.getDefaultTimespan('1m');
|
||||||
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
||||||
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
||||||
|
|
||||||
this.route
|
this.route
|
||||||
.fragment
|
.fragment
|
||||||
.subscribe((fragment) => {
|
.subscribe((fragment) => {
|
||||||
if (['24h', '3d', '1w', '1m', '3m', '6m', '1y', '2y', '3y', 'all'].indexOf(fragment) > -1) {
|
if (['1m', '3m', '6m', '1y', '2y', '3y', 'all'].indexOf(fragment) > -1) {
|
||||||
this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false });
|
this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -84,7 +84,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
|||||||
tap((response) => {
|
tap((response) => {
|
||||||
this.prepareChartOptions({
|
this.prepareChartOptions({
|
||||||
blockFees: response.body.map(val => [val.timestamp * 1000, val.avgFees / 100000000, val.avgHeight]),
|
blockFees: response.body.map(val => [val.timestamp * 1000, val.avgFees / 100000000, val.avgHeight]),
|
||||||
blockFeesUSD: response.body.filter(val => val.usd > 0).map(val => [val.timestamp * 1000, val.avgFees / 100000000 * val.usd, val.avgHeight]),
|
blockFeesUSD: response.body.filter(val => val.USD > 0).map(val => [val.timestamp * 1000, val.avgFees / 100000000 * val.USD, val.avgHeight]),
|
||||||
});
|
});
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}),
|
}),
|
||||||
@ -102,14 +102,14 @@ export class BlockFeesGraphComponent implements OnInit {
|
|||||||
prepareChartOptions(data) {
|
prepareChartOptions(data) {
|
||||||
this.chartOptions = {
|
this.chartOptions = {
|
||||||
color: [
|
color: [
|
||||||
new graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{ offset: 0, color: '#00ACC1' },
|
|
||||||
{ offset: 1, color: '#0D47A1' },
|
|
||||||
]),
|
|
||||||
new graphic.LinearGradient(0, 0, 0, 1, [
|
new graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
{ offset: 0, color: '#FDD835' },
|
{ offset: 0, color: '#FDD835' },
|
||||||
{ offset: 1, color: '#FB8C00' },
|
{ offset: 1, color: '#FB8C00' },
|
||||||
]),
|
]),
|
||||||
|
new graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{ offset: 0, color: '#C0CA33' },
|
||||||
|
{ offset: 1, color: '#1B5E20' },
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
animation: false,
|
animation: false,
|
||||||
grid: {
|
grid: {
|
||||||
@ -188,9 +188,6 @@ export class BlockFeesGraphComponent implements OnInit {
|
|||||||
return `${val} BTC`;
|
return `${val} BTC`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
max: (value) => {
|
|
||||||
return Math.floor(value.max * 2 * 10) / 10;
|
|
||||||
},
|
|
||||||
splitLine: {
|
splitLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
type: 'dotted',
|
type: 'dotted',
|
||||||
@ -223,9 +220,10 @@ export class BlockFeesGraphComponent implements OnInit {
|
|||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: 0.25,
|
smooth: 0.25,
|
||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
areaStyle: {
|
lineStyle: {
|
||||||
opacity: 0.25,
|
width: 1,
|
||||||
},
|
opacity: 1,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
legendHoverLink: false,
|
legendHoverLink: false,
|
||||||
@ -238,7 +236,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
|||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: 2,
|
width: 2,
|
||||||
opacity: 0.75,
|
opacity: 1,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -9,15 +9,6 @@
|
|||||||
</button>
|
</button>
|
||||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats">
|
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats">
|
||||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 144">
|
|
||||||
<input ngbButton type="radio" [value]="'24h'" fragment="24h" [routerLink]="['/graphs/mining/block-rewards' | relativeUrl]"> 24h
|
|
||||||
</label>
|
|
||||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 432">
|
|
||||||
<input ngbButton type="radio" [value]="'3d'" fragment="3d" [routerLink]="['/graphs/mining/block-rewards' | relativeUrl]"> 3D
|
|
||||||
</label>
|
|
||||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 1008">
|
|
||||||
<input ngbButton type="radio" [value]="'1w'" fragment="1w" [routerLink]="['/graphs/mining/block-rewards' | relativeUrl]"> 1W
|
|
||||||
</label>
|
|
||||||
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 4320">
|
<label ngbButtonLabel class="btn-primary btn-sm" *ngIf="stats.blockCount >= 4320">
|
||||||
<input ngbButton type="radio" [value]="'1m'" fragment="1m" [routerLink]="['/graphs/mining/block-rewards' | relativeUrl]"> 1M
|
<input ngbButton type="radio" [value]="'1m'" fragment="1m" [routerLink]="['/graphs/mining/block-rewards' | relativeUrl]"> 1M
|
||||||
</label>
|
</label>
|
||||||
|
@ -58,14 +58,14 @@ export class BlockRewardsGraphComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.seoService.setTitle($localize`:@@8ba8fe810458280a83df7fdf4c614dfc1a826445:Block Rewards`);
|
this.seoService.setTitle($localize`:@@8ba8fe810458280a83df7fdf4c614dfc1a826445:Block Rewards`);
|
||||||
this.miningWindowPreference = this.miningService.getDefaultTimespan('24h');
|
this.miningWindowPreference = this.miningService.getDefaultTimespan('3m');
|
||||||
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference });
|
||||||
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference);
|
||||||
|
|
||||||
this.route
|
this.route
|
||||||
.fragment
|
.fragment
|
||||||
.subscribe((fragment) => {
|
.subscribe((fragment) => {
|
||||||
if (['24h', '3d', '1w', '1m', '3m', '6m', '1y', '2y', '3y', 'all'].indexOf(fragment) > -1) {
|
if (['3m', '6m', '1y', '2y', '3y', 'all'].indexOf(fragment) > -1) {
|
||||||
this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false });
|
this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -82,7 +82,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
|||||||
tap((response) => {
|
tap((response) => {
|
||||||
this.prepareChartOptions({
|
this.prepareChartOptions({
|
||||||
blockRewards: response.body.map(val => [val.timestamp * 1000, val.avgRewards / 100000000, val.avgHeight]),
|
blockRewards: response.body.map(val => [val.timestamp * 1000, val.avgRewards / 100000000, val.avgHeight]),
|
||||||
blockRewardsUSD: response.body.filter(val => val.usd > 0).map(val => [val.timestamp * 1000, val.avgRewards / 100000000 * val.usd, val.avgHeight]),
|
blockRewardsUSD: response.body.filter(val => val.USD > 0).map(val => [val.timestamp * 1000, val.avgRewards / 100000000 * val.USD, val.avgHeight]),
|
||||||
});
|
});
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}),
|
}),
|
||||||
@ -103,14 +103,14 @@ export class BlockRewardsGraphComponent implements OnInit {
|
|||||||
this.chartOptions = {
|
this.chartOptions = {
|
||||||
animation: false,
|
animation: false,
|
||||||
color: [
|
color: [
|
||||||
new graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{ offset: 0, color: '#00ACC1' },
|
|
||||||
{ offset: 1, color: '#0D47A1' },
|
|
||||||
]),
|
|
||||||
new graphic.LinearGradient(0, 0, 0, 1, [
|
new graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
{ offset: 0, color: '#FDD835' },
|
{ offset: 0, color: '#FDD835' },
|
||||||
{ offset: 1, color: '#FB8C00' },
|
{ offset: 1, color: '#FB8C00' },
|
||||||
]),
|
]),
|
||||||
|
new graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{ offset: 0, color: '#C0CA33' },
|
||||||
|
{ offset: 1, color: '#1B5E20' },
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
grid: {
|
grid: {
|
||||||
top: 20,
|
top: 20,
|
||||||
@ -232,9 +232,6 @@ export class BlockRewardsGraphComponent implements OnInit {
|
|||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: 0.25,
|
smooth: 0.25,
|
||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
areaStyle: {
|
|
||||||
opacity: 0.25,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
legendHoverLink: false,
|
legendHoverLink: false,
|
||||||
@ -248,6 +245,9 @@ export class BlockRewardsGraphComponent implements OnInit {
|
|||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: 2,
|
width: 2,
|
||||||
opacity: 0.75,
|
opacity: 0.75,
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.05,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
Loading…
Reference in New Issue
Block a user