Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions benchmarks/perp-daily-volume.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ ledger_columns:

stacked_share: true

# Gauges move once per UTC day: sub-day chart ranges would show one point
# at "now", so the chart opens on 30 days and the ledger's base window is
# the last closed day rather than "24h".
chart:
min_range: 7d
default_range: 30d
window_label: last closed day

faq:
- q: "How is this different from bench 041, perp DEX volume share?"
a: "Bench 041 polls each venue's live rolling 24 hour figure every five minutes and ranks the time average. This bench counts closed UTC days, which is what DeFiLlama, weekly reports and flip claims use, and keeps a year of daily history per venue. The two agree on quiet days and disagree whenever a big hour sits on one side of a midnight boundary."
Expand Down
8 changes: 6 additions & 2 deletions src/components/ledger-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,12 @@ export function LedgerTable({
}
return colValue(r, col);
};
// A daily-cadence bench relabels the base window ("last closed day"
// instead of "24h") via the spec's chart.window_label.
const baseWindowLabel = benchmark.chart?.windowLabel ?? "24h";
const colLabel = (col: LedgerColumn): string => {
if (!hasWindows) return col.label;
const w = windowKey !== "24h" && col.windows?.[windowKey] ? windowKey : "24h";
const w = windowKey !== "24h" && col.windows?.[windowKey] ? windowKey : baseWindowLabel;
return `${col.label} (${w})`;
};

Expand Down Expand Up @@ -361,6 +364,7 @@ export function LedgerTable({
<button
key={w}
type="button"
title={w === "24h" && baseWindowLabel !== "24h" ? baseWindowLabel : undefined}
onClick={() => setWindowKey(w)}
className={[
"rounded px-2.5 py-1 text-[11px] font-sans tabular uppercase tracking-[0.1em] font-medium transition-colors",
Expand All @@ -369,7 +373,7 @@ export function LedgerTable({
: "text-ink-muted hover:text-ink hover:bg-paper-soft",
].join(" ")}
>
{w}
{w === "24h" && baseWindowLabel !== "24h" ? baseWindowLabel : w}
</button>
))}
</div>
Expand Down
20 changes: 15 additions & 5 deletions src/components/time-series-chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,14 @@
longRangeDisabledTitle,
onAvailableRangesChange,
}: Props) {
const [rangeLocal, setRangeLocal] = useState<Range>("24h");
// Daily-cadence benches (spec `chart` block) hide sub-day ranges,
// which would show one point at "now", and open on a longer window.
const minRange: Range = benchmark.chart?.minRange ?? "1h";
const rangeOrder: Range[] = ["1h", "6h", "24h", "7d", "30d", "90d", "180d", "1y", "all"];
const rangeAllowed = (r: Range) => rangeOrder.indexOf(r) >= rangeOrder.indexOf(minRange);
const [rangeLocal, setRangeLocal] = useState<Range>(
benchmark.chart?.defaultRange ?? (rangeAllowed("24h") ? "24h" : minRange),
);
const range = rangeProp ?? rangeLocal;
const setRange = (next: Range) => {
if (onRangeChange) onRangeChange(next);
Expand Down Expand Up @@ -413,12 +420,14 @@
useEffect(() => {
if (panelActive) return;
/* eslint-disable react-hooks/set-state-in-effect */
if (range === "7d" && lazySeries7d !== null && !seriesHasData(lazySeries7d)) setRange("24h");
if (range === "30d" && lazySeries30d !== null && !seriesHasData(lazySeries30d)) setRange("24h");
const base: Range = rangeAllowed("24h") ? "24h" : minRange;
if (range === "7d" && lazySeries7d !== null && !seriesHasData(lazySeries7d) && base !== "7d") setRange(base);
if (range === "30d" && lazySeries30d !== null && !seriesHasData(lazySeries30d) && base !== "30d")
setRange(has7d && rangeAllowed("7d") ? "7d" : base);
if (range === "90d" && lazySeries90d !== null && Object.keys(lazySeries90d).length === 0)
setRange(has30d ? "30d" : "24h");
setRange(has30d ? "30d" : base);
if (range === "1y" && lazySeries1y !== null && Object.keys(lazySeries1y).length === 0)
setRange(has90d ? "90d" : "24h");
setRange(has90d ? "90d" : base);
/* eslint-enable react-hooks/set-state-in-effect */
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [range, panelActive, lazySeries7d, lazySeries30d, lazySeries90d, lazySeries1y]);
Expand Down Expand Up @@ -543,7 +552,7 @@
return higherIsBetter ? bv - av : av - bv;
});
return built;
}, [benchmark, range, region, colors, excluded, seriesOverride, seriesOverride7d, seriesOverride30d, higherIsBetterOverride, lazySeries7d, lazySeries30d, lazySeries90d, lazySeries1y, isLongRange, longRangeSeries, panelLazy90d, panelLazy1y]);

Check warning on line 555 in src/components/time-series-chart/index.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useMemo has missing dependencies: 'panelLazy30d' and 'panelLazy7d'. Either include them or remove the dependency array

// Top-N selector — sized off the post-filter line count via the
// shared `useTopN` hook so the option set agrees across every
Expand Down Expand Up @@ -621,6 +630,7 @@
<div className="mb-4 flex flex-wrap items-center justify-between gap-3" data-chart-export-omit="true">
<div className="flex flex-wrap items-center gap-1">
{RANGES.map((r) => {
if (!rangeAllowed(r)) return null;
// Hide entirely once we have confirmed there's no data for this range.
if (r === "7d" && lazySeries7d !== null && !has7d) return null;
if (r === "30d" && lazySeries30d !== null && !has30d) return null;
Expand Down
11 changes: 11 additions & 0 deletions src/lib/materialize/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ import {
const SPECS_DIR =
process.env.OCB_SPECS_DIR ?? path.join(process.cwd(), "benchmarks");

/** Camel-cased copy of the spec's optional `chart` block. */
export function chartFromSpec(spec: { chart?: { min_range?: "24h" | "7d" | "30d"; default_range?: "24h" | "7d" | "30d" | "90d" | "1y"; window_label?: string } }): Benchmark["chart"] {
if (!spec.chart) return undefined;
return {
minRange: spec.chart.min_range,
defaultRange: spec.chart.default_range,
windowLabel: spec.chart.window_label,
};
}

/** Side-effect hooks injected by the caller (site: KV snapshot write;
* worker: store publish). Keeps this module free of persistence deps. */
export type LoadHooks = {
Expand Down Expand Up @@ -138,6 +148,7 @@ export function buildEditorial(
metric: spec.metric,
panelMainLabel: spec.panel_main_label,
stackedShare: spec.stacked_share,
chart: chartFromSpec(spec),
unit: spec.unit,
higherIsBetter: spec.higher_is_better,
abstract: spec.abstract,
Expand Down
16 changes: 16 additions & 0 deletions src/lib/spec-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,22 @@ export const SpecSchema = z
* where the relative contribution of each venue over time is the
* primary signal. */
stacked_share: z.boolean().optional(),
/**
* Chart cadence for benches whose gauges move once a day (closed
* UTC-day series such as perp-daily-volume). Sub-day ranges (1h, 6h,
* 24h) would render a single point at "now", so `min_range` hides
* them and `default_range` opens the chart on a window that holds
* data. `window_label` replaces the "24h" suffix on the ledger's
* headline columns ("last closed day").
*/
chart: z
.object({
min_range: z.enum(["24h", "7d", "30d"]).optional(),
default_range: z.enum(["24h", "7d", "30d", "90d", "1y"]).optional(),
window_label: z.string().min(1).max(24).optional(),
})
.strict()
.optional(),
metric_panels: z
.array(
z.object({
Expand Down
2 changes: 2 additions & 0 deletions src/lib/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type { Spec } from "@/lib/spec-schema";
import { canonicalChainSlug } from "@/lib/chain-aliases";
import { renderBenchmarkText } from "@/lib/bench-template";
import {
chartFromSpec,
draftPlaceholderForSpec,
filterSig,
loadSpecsUncached,
Expand Down Expand Up @@ -137,6 +138,7 @@ export function overlayEditorial(stored: Benchmark, spec: Spec): Benchmark {
// worker running a divergent branch (keyed-rpc-robinhood: main is
// Singapore-only while dev/worker carries the region dims).
dimensions: spec.dimensions,
chart: chartFromSpec(spec) ?? stored.chart,
// Same source-of-truth rule as dimensions: the live YAML decides
// the aggregate pin, never the snapshot.
aggregateFilters: spec.aggregate_filters,
Expand Down
8 changes: 8 additions & 0 deletions src/types/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,14 @@ export type Benchmark = {
* `metric_panels` in the YAML. */
metricPanels?: MetricPanel[];
panelMainLabel?: string;
/** Chart cadence hints from the spec's `chart` block: hide sub-day
* ranges, open on a longer window, relabel the "24h" ledger suffix.
* Set for benches whose gauges move once per UTC day. */
chart?: {
minRange?: "24h" | "7d" | "30d";
defaultRange?: "24h" | "7d" | "30d" | "90d" | "1y";
windowLabel?: string;
};
/** When true the bench page renders a stacked bar chart (absolute + %
* share over time) below the main chart. Set via `stacked_share: true`
* in the bench YAML. Meaningful only for volume-share benches. */
Expand Down
Loading