Add daily historical price - show USD in block fee reward charts

This commit is contained in:
nymkappa 2022-07-11 23:16:48 +02:00
parent 40634a0eb8
commit a97675c538
No known key found for this signature in database
GPG Key ID: E155910B16E8BD04
16 changed files with 74 additions and 117 deletions

View File

@ -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'));

View File

@ -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;
} }
} }
} }

View File

@ -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;

View File

@ -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();

View File

@ -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
*/ */

View File

@ -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[]) {

View File

@ -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 [];
} }
} }

View File

@ -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[]) {

View File

@ -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[]) {

View File

@ -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[]) {

View File

@ -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) {

View File

@ -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`);
} }
} }
} }

View File

@ -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>

View File

@ -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,
} }
}, },
], ],

View File

@ -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>

View File

@ -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,
} }
}, },
], ],