Merge branch 'master' into nymkappa/mega-branch

This commit is contained in:
nymkappa 2023-11-16 16:10:55 +09:00
commit 64a51803c4
No known key found for this signature in database
GPG key ID: E155910B16E8BD04
4 changed files with 61 additions and 41 deletions

View file

@ -7,6 +7,8 @@ import { formatNumber } from '@angular/common';
import { StateService } from '../../services/state.service';
import { Subscription } from 'rxjs';
const OUTLIERS_MEDIAN_MULTIPLIER = 4;
@Component({
selector: 'app-incoming-transactions-graph',
templateUrl: './incoming-transactions-graph.component.html',
@ -29,6 +31,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
@Input() left: number | string = '0';
@Input() template: ('widget' | 'advanced') = 'widget';
@Input() windowPreferenceOverride: string;
@Input() outlierCappingEnabled: boolean = false;
isLoading = true;
mempoolStatsChartOption: EChartsOption = {};
@ -40,6 +43,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
MA: number[][] = [];
weightMode: boolean = false;
rateUnitSub: Subscription;
medianVbytesPerSecond: number | undefined;
constructor(
@Inject(LOCALE_ID) private locale: string,
@ -65,16 +69,35 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference');
const windowSize = Math.max(10, Math.floor(this.data.series[0].length / 8));
this.MA = this.calculateMA(this.data.series[0], windowSize);
if (this.outlierCappingEnabled === true) {
this.computeMedianVbytesPerSecond(this.data.series[0]);
}
this.mountChart();
}
rendered() {
if (!this.data) {
return;
return;
}
this.isLoading = false;
}
/**
* Calculate the median value of the vbytes per second chart to hide outliers
*/
computeMedianVbytesPerSecond(data: number[][]): void {
const vBytes: number[] = [];
for (const value of data) {
vBytes.push(value[1]);
}
const sorted = vBytes.slice().sort((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
this.medianVbytesPerSecond = sorted[middle];
if (sorted.length % 2 === 0) {
this.medianVbytesPerSecond = (sorted[middle - 1] + sorted[middle]) / 2;
}
}
/// calculate the moving average of the provided data based on windowSize
calculateMA(data: number[][], windowSize: number = 100): number[][] {
//update const variables that are not changed
@ -232,6 +255,13 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
}
],
yAxis: {
max: (value) => {
if (!this.outlierCappingEnabled || value.max < this.medianVbytesPerSecond * OUTLIERS_MEDIAN_MULTIPLIER) {
return undefined;
} else {
return Math.round(this.medianVbytesPerSecond * OUTLIERS_MEDIAN_MULTIPLIER);
}
},
type: 'value',
axisLabel: {
fontSize: 11,

View file

@ -109,18 +109,26 @@
<div>
<div class="card mb-3">
<div class="card-header">
<div class="d-flex d-md-block align-items-baseline">
<span i18n="statistics.transaction-vbytes-per-second">Transaction vBytes per second (vB/s)</span>
<button class="btn p-0 pl-2" style="margin: 0 0 4px 0px" (click)="onSaveChart('incoming')">
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
</button>
</div>
<div class="vbytes-title">
<div>
<span i18n="statistics.transaction-vbytes-per-second">Transaction vBytes per second (vB/s)</span>
<button class="btn p-0 pl-2" style="margin: 0 0 4px 0px" (click)="onSaveChart('incoming')">
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true"></fa-icon>
</button>
</div>
<div class="form-check">
<input style="margin-top: 9px" class="form-check-input" type="checkbox" [checked]="outlierCappingEnabled" id="hide-outliers" (change)="onOutlierToggleChange($event)">
<label class="form-check-label" for="hide-outliers">
<small i18n="statistics.cap-outliers">Cap outliers</small>
</label>
</div>
</div>
</div>
<div class="card-body">
<div class="incoming-transactions-graph">
<app-incoming-transactions-graph #incominggraph [height]="500" [left]="65" [template]="'advanced'"
[data]="mempoolTransactionsWeightPerSecondData"></app-incoming-transactions-graph>
[data]="mempoolTransactionsWeightPerSecondData" [outlierCappingEnabled]="outlierCappingEnabled"></app-incoming-transactions-graph>
</div>
</div>
</div>

View file

@ -222,4 +222,13 @@
border-top-right-radius: 0;
}
}
}
.vbytes-title {
display: flex;
align-items: baseline;
justify-content: space-between;
@media (max-width: 767px) {
display: block;
}
}

View file

@ -35,7 +35,7 @@ export class StatisticsComponent implements OnInit {
showCount = false;
maxFeeIndex: number;
dropDownOpen = false;
outlierCappingEnabled = false;
mempoolStats: OptimizedMempoolStats[] = [];
mempoolVsizeFeesData: any;
@ -67,6 +67,7 @@ export class StatisticsComponent implements OnInit {
this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.mempool:See mempool size (in MvB) and transactions per second (in vB/s) visualized over time.`);
this.stateService.networkChanged$.subscribe((network) => this.network = network);
this.graphWindowPreference = this.storageService.getValue('graphWindowPreference') ? this.storageService.getValue('graphWindowPreference').trim() : '2h';
this.outlierCappingEnabled = this.storageService.getValue('cap-outliers') === 'true';
this.radioGroupForm = this.formBuilder.group({
dateSpan: this.graphWindowPreference
@ -156,8 +157,6 @@ export class StatisticsComponent implements OnInit {
}
this.maxFeeIndex = maxTier;
this.capExtremeVbytesValues();
this.mempoolTransactionsWeightPerSecondData = {
labels: labels,
series: [mempoolStats.map((stats) => [stats.added * 1000, stats.vbytes_per_second])],
@ -211,36 +210,10 @@ export class StatisticsComponent implements OnInit {
}
});
}
/**
* All value higher that "median * capRatio" are capped
*/
capExtremeVbytesValues() {
if (this.stateService.network.length !== 0) {
return; // Only cap on Bitcoin mainnet
}
let capRatio = 10;
if (['1m', '3m', '6m', '1y', '2y', '3y', '4y'].includes(this.graphWindowPreference)) {
capRatio = 4;
}
// Find median value
const vBytes: number[] = [];
for (const stat of this.mempoolStats) {
vBytes.push(stat.vbytes_per_second);
}
const sorted = vBytes.slice().sort((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
let median = sorted[middle];
if (sorted.length % 2 === 0) {
median = (sorted[middle - 1] + sorted[middle]) / 2;
}
// Cap
for (const stat of this.mempoolStats) {
stat.vbytes_per_second = Math.min(median * capRatio, stat.vbytes_per_second);
}
onOutlierToggleChange(e): void {
this.outlierCappingEnabled = e.target.checked;
this.storageService.setValue('cap-outliers', e.target.checked);
}
onSaveChart(name) {