from __future__ import annotations
from typing import Any
import gradio as gr
import pandas as pd
import plotly.graph_objects as go
from analytics import (
AnalyticsStore,
Snapshot,
aggregate_period_metrics,
available_categories,
available_market_types,
available_months,
filter_metrics,
)
STORE = AnalyticsStore()
COLORS = {
"ink": "#102A43",
"muted": "#627D98",
"cyan": "#00A6A6",
"blue": "#2563EB",
"amber": "#F59E0B",
"grid": "#E6EDF5",
}
CSS = """
.hero {
align-items: center !important;
gap: 16px !important;
border-radius: 18px;
padding: 20px 24px;
margin-bottom: 10px;
color: white;
background:
radial-gradient(circle at 90% 15%, rgba(45, 212, 191, .33), transparent 28%),
linear-gradient(125deg, #102a43 0%, #163f66 52%, #0f766e 100%);
box-shadow: 0 12px 32px rgba(16, 42, 67, .16);
}
.hero, .hero h1, .hero p {
color: #ffffff !important;
-webkit-text-fill-color: #ffffff !important;
}
.hero-copy { padding: 0 !important; }
.hero-copy h1 {
margin: 0;
font-size: clamp(1.55rem, 3vw, 2rem);
letter-spacing: -.03em;
line-height: 1.08;
}
.hero-copy p {
margin: 6px 0 0;
color: #d9f4f0 !important;
font-size: .92rem;
}
.mobile-title { display: none; }
.hero-refresh {
align-self: center !important;
flex: 0 0 auto !important;
min-width: 112px !important;
min-height: 40px !important;
border: 1px solid rgba(255, 255, 255, .8) !important;
background: #102a43 !important;
color: #ffffff !important;
-webkit-text-fill-color: #ffffff !important;
font-weight: 700 !important;
box-shadow: 0 8px 20px rgba(7, 29, 43, .22) !important;
}
.hero-refresh * {
color: #ffffff !important;
-webkit-text-fill-color: #ffffff !important;
opacity: 1 !important;
}
.hero-refresh:hover {
border-color: #ffffff !important;
background: #0f766e !important;
color: #ffffff !important;
-webkit-text-fill-color: #ffffff !important;
}
.kpi-grid {
display: grid;
grid-template-columns: repeat(5, minmax(150px, 1fr));
align-items: stretch;
gap: 10px;
margin: 2px 0 10px;
}
.kpi {
box-sizing: border-box;
height: 100%;
min-height: 92px;
display: flex;
flex-direction: column;
justify-content: space-between;
background: white;
border: 1px solid #e6edf5;
border-radius: 14px;
padding: 13px 15px;
box-shadow: 0 6px 18px rgba(16, 42, 67, .05);
}
.kpi-label {
color: #627d98;
font-size: .74rem;
line-height: 1.2;
text-transform: uppercase;
letter-spacing: .06em;
}
.kpi-value {
color: #102a43;
font-size: 1.35rem;
font-weight: 750;
line-height: 1.15;
margin-top: 6px;
}
.control-row {
display: grid !important;
grid-template-columns: repeat(6, minmax(0, 1fr));
align-items: end !important;
gap: 10px !important;
margin-bottom: 2px;
}
.control-row > * {
width: 100% !important;
min-width: 0 !important;
}
.control-row > .form {
display: contents !important;
}
.filter-control {
min-width: 0 !important;
padding: 7px 9px !important;
}
.filter-control .container > span {
display: block !important;
height: auto !important;
margin: 0 0 3px !important;
padding: 0 !important;
overflow: hidden;
color: #627d98 !important;
background: transparent !important;
font-size: .72rem !important;
font-weight: 600 !important;
line-height: 1.2 !important;
text-overflow: ellipsis;
white-space: nowrap;
}
.kpi-host .html-container {
padding: 0 !important;
}
.section-title {
color: #102a43;
margin: 10px 0 0 !important;
padding: 0 !important;
min-height: 0 !important;
}
.section-title h3 {
margin: 0 !important;
font-size: 1rem !important;
line-height: 1.3 !important;
}
.chart-row { gap: 10px !important; }
.chart {
overflow: hidden !important;
border-radius: 14px !important;
}
.chart .modebar-container,
.chart .modebar {
display: none !important;
}
.chart .js-plotly-plot {
touch-action: pan-y !important;
}
.status-line {
color: #627d98 !important;
font-size: .88rem;
-webkit-text-fill-color: #627d98 !important;
}
.status-line :is(p, strong, em, span),
.metric-note :is(p, strong, em, span) {
color: #627d98 !important;
-webkit-text-fill-color: #627d98 !important;
}
.metric-note {
color: #627d98 !important;
-webkit-text-fill-color: #627d98 !important;
}
.status-line code,
.metric-note code {
color: #fff !important;
background: #102a43 !important;
-webkit-text-fill-color: #fff !important;
}
@media (max-width: 900px) {
.control-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
html, body { overflow-x: hidden !important; }
.gradio-container .contain > .column {
gap: 8px !important;
}
.hero {
display: grid !important;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center !important;
gap: 8px !important;
padding: 14px 14px !important;
margin-bottom: 6px;
border-radius: 14px;
box-shadow: 0 8px 22px rgba(16, 42, 67, .14);
}
.hero > * {
width: auto !important;
min-width: 0 !important;
}
.desktop-title { display: none; }
.mobile-title { display: inline; }
.hero-copy h1 {
font-size: 1.35rem;
white-space: nowrap;
}
.hero-copy p {
margin-top: 4px;
font-size: .76rem;
line-height: 1.3;
}
.hero-refresh {
width: auto !important;
min-width: 76px !important;
min-height: 40px !important;
padding: 0 10px !important;
font-size: .82rem !important;
box-shadow: none !important;
}
.control-row {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 6px 8px !important;
margin: 0 !important;
}
.control-row label {
font-size: .72rem !important;
margin-bottom: 2px !important;
}
.control-row input {
font-size: .86rem !important;
}
.kpi-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 7px;
margin: 6px 0 8px;
}
.kpi {
min-height: 72px;
border-radius: 11px;
padding: 9px 11px;
box-shadow: 0 4px 12px rgba(16, 42, 67, .04);
}
.kpi-label {
font-size: .65rem;
line-height: 1.15;
letter-spacing: .04em;
}
.kpi-value {
margin-top: 3px;
font-size: 1.05rem;
}
.kpi:last-child {
grid-column: 1 / -1;
}
.section-title {
margin-top: 8px !important;
}
.section-title h3 {
font-size: .92rem !important;
}
.chart-row {
display: grid !important;
grid-template-columns: minmax(0, 1fr);
gap: 8px !important;
}
.chart-row > .form {
display: contents !important;
}
.chart-row > * {
width: 100% !important;
min-width: 0 !important;
}
.chart {
min-width: 0 !important;
border-radius: 11px !important;
}
.chart .js-plotly-plot,
.chart .plot-container,
.chart .svg-container {
height: 300px !important;
min-height: 300px !important;
}
.status-line {
font-size: .72rem;
}
}
"""
HEAD = """
"""
THEME = gr.themes.Soft(
primary_hue="teal",
secondary_hue="blue",
neutral_hue="slate",
)
ENGLISH = "English"
CHINESE = "中文"
LANGUAGES = (ENGLISH, CHINESE)
TEXT = {
ENGLISH: {
"hero_subtitle": (
"V1 + V2 exchange activity on Polygon · "
"yearly, quarterly, monthly, and daily analytics"
),
"version": "Protocol version",
"market": "Market type",
"category": "Category",
"year": "Daily detail year",
"calendar_month": "Daily detail month",
"refresh": "↻ Refresh",
"yearly_section": "### Yearly data",
"quarterly_section": "### Quarterly data",
"daily_section": "### Daily detail",
"monthly_section": "### Monthly data",
"monthly_table_section": "### Monthly detail",
"category_section": "### Category comparison",
"metric_note": (
"**Metric note.** Nominal collateral volume is derived from the "
"collateral leg of each `OrderFilled` event and is not the same "
"as Polymarket's official reported volume. All dates are UTC."
),
"yearly_volume_title": "Yearly nominal collateral volume",
"yearly_fills_title": "Yearly OrderFilled events",
"quarterly_volume_title": "Quarterly nominal collateral volume",
"quarterly_fills_title": "Quarterly OrderFilled events",
"volume_title": "Monthly nominal collateral volume",
"fills_title": "Monthly OrderFilled events",
"daily_volume_title": "Daily nominal volume",
"daily_fills_title": "Daily fill events",
"category_volume_title": "Nominal volume by category",
"category_fills_title": "Fill events by category",
"empty": "No data for this selection",
"kpis": (
"Fill events",
"Unique transactions",
"Nominal volume",
"Fees",
"Daily detail",
),
"columns": (
"Month",
"Fill events",
"Unique transactions",
"Nominal volume",
"Fees",
"Complete",
),
"generated": "Source generated",
"refreshed": "Dashboard refreshed",
},
CHINESE: {
"hero_subtitle": (
"Polygon 上的 V1 + V2 成交活动 · 年度、季度、月度与每日分析"
),
"version": "协议版本",
"market": "市场类型",
"category": "分类",
"year": "按日查看年份",
"calendar_month": "按日查看月份",
"refresh": "↻ 刷新",
"yearly_section": "### 年度数据",
"quarterly_section": "### 季度数据",
"daily_section": "### 每日明细",
"monthly_section": "### 月度数据",
"monthly_table_section": "### 月度明细",
"category_section": "### 分类对比",
"metric_note": (
"**指标说明:** 名义抵押金额来自每个 `OrderFilled` 事件的抵押"
"资产腿,不等同于 Polymarket 官方公布的 Volume。所有日期均为 UTC。"
),
"yearly_volume_title": "每年名义抵押金额",
"yearly_fills_title": "每年成交事件",
"quarterly_volume_title": "每季度名义抵押金额",
"quarterly_fills_title": "每季度成交事件",
"volume_title": "每月名义抵押金额",
"fills_title": "每月成交事件",
"daily_volume_title": "每日名义金额",
"daily_fills_title": "每日成交事件",
"category_volume_title": "各分类名义金额",
"category_fills_title": "各分类成交事件",
"empty": "当前筛选条件没有数据",
"kpis": (
"成交事件",
"唯一交易",
"名义金额",
"手续费",
"按日月份",
),
"columns": (
"月份",
"成交事件",
"唯一交易",
"名义金额",
"手续费",
"是否完整",
),
"generated": "数据生成时间",
"refreshed": "页面刷新时间",
},
}
VERSION_LABELS = {
ENGLISH: {"all": "All", "v1": "V1", "v2": "V2"},
CHINESE: {"all": "全部", "v1": "V1", "v2": "V2"},
}
MONTH_LABELS = {
ENGLISH: (
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
),
CHINESE: tuple(f"{month}月" for month in range(1, 13)),
}
MARKET_LABELS = {
ENGLISH: {
"all": "All",
"standard": "Standard",
"neg_risk": "Neg Risk",
},
CHINESE: {
"all": "全部",
"standard": "标准市场",
"neg_risk": "负风险市场",
},
}
def normalized_language(language: str | None) -> str:
return CHINESE if language == CHINESE else ENGLISH
def browser_language(value: str | None) -> str:
return CHINESE if str(value).lower().startswith("zh") else ENGLISH
def hero_html(language: str) -> str:
copy = TEXT[normalized_language(language)]
return f"""
Polymarket OrderFilled Analytics
Polymarket Analytics
{copy["hero_subtitle"]}
"""
def version_choices(language: str) -> list[tuple[str, str]]:
labels = VERSION_LABELS[normalized_language(language)]
return [(labels[value], value) for value in ("all", "v1", "v2")]
def market_choices(
values: list[str],
language: str,
) -> list[tuple[str, str]]:
labels = MARKET_LABELS[normalized_language(language)]
return [(labels[value], value) for value in values]
def category_choices(
snapshot: Snapshot,
values: list[str],
language: str,
) -> list[tuple[str, str]]:
language_key = "zh" if normalized_language(language) == CHINESE else "en"
labels = {
str(item["key"]): str(item["labels"].get(language_key, item["key"]))
for item in snapshot.categories
}
return [(labels.get(value, value), value) for value in values]
def calendar_month_choices(
values: list[str],
language: str,
) -> list[tuple[str, str]]:
labels = MONTH_LABELS[normalized_language(language)]
return [(labels[int(value) - 1], value) for value in values]
def display_quarters(frame: pd.DataFrame) -> pd.DataFrame:
if frame.empty:
return frame
displayed = frame.copy()
displayed["period"] = displayed["period"].str.replace(
r"^(\d{4})-Q([1-4])$",
r"\1 Q\2",
regex=True,
)
return displayed
def resolve_year_month(
available: list[str],
selected_year: str | None,
selected_calendar_month: str | None,
) -> tuple[list[str], str, list[str], str, str]:
years = sorted({value[:4] for value in available})
year = selected_year if selected_year in years else (
years[-1] if years else ""
)
calendar_months = [
value[5:7] for value in available if value.startswith(f"{year}-")
]
calendar_month = (
selected_calendar_month
if selected_calendar_month in calendar_months
else (calendar_months[-1] if calendar_months else "")
)
combined = (
f"{year}-{calendar_month}" if year and calendar_month else ""
)
return years, year, calendar_months, calendar_month, combined
def compact_number(value: float) -> str:
absolute = abs(value)
for divisor, suffix in (
(1_000_000_000_000, "T"),
(1_000_000_000, "B"),
(1_000_000, "M"),
(1_000, "K"),
):
if absolute >= divisor:
return f"{value / divisor:,.2f}{suffix}"
return f"{value:,.0f}"
def compact_usd(value: float) -> str:
return f"${compact_number(value)}"
def empty_figure(title: str, message: str) -> go.Figure:
figure = go.Figure()
figure.add_annotation(
text=message,
x=0.5,
y=0.5,
xref="paper",
yref="paper",
showarrow=False,
font={"color": COLORS["muted"], "size": 15},
)
return style_figure(figure, title)
def style_figure(figure: go.Figure, title: str) -> go.Figure:
figure.update_layout(
title={"text": title, "x": 0.02, "font": {"size": 18}},
paper_bgcolor="white",
plot_bgcolor="white",
font={"family": "Inter, ui-sans-serif, system-ui", "color": COLORS["ink"]},
margin={"l": 55, "r": 24, "t": 62, "b": 48},
hoverlabel={"bgcolor": COLORS["ink"], "font_color": "white"},
hovermode="closest",
dragmode=False,
showlegend=False,
)
figure.update_xaxes(
showgrid=False,
linecolor=COLORS["grid"],
tickfont={"color": COLORS["muted"]},
fixedrange=True,
)
figure.update_yaxes(
gridcolor=COLORS["grid"],
zeroline=False,
tickfont={"color": COLORS["muted"]},
fixedrange=True,
)
return figure
def bar_figure(
frame: pd.DataFrame,
x: str,
y: str,
title: str,
color: str,
empty_message: str,
prefix: str = "",
) -> go.Figure:
if frame.empty:
return empty_figure(title, empty_message)
figure = go.Figure(
go.Bar(
x=frame[x],
y=frame[y],
marker={
"color": color,
"line": {"width": 0},
},
hovertemplate=(
f"%{{x}}
{prefix}%{{y:,.2f}}"
if prefix
else "%{x}
%{y:,.0f}"
),
)
)
return style_figure(figure, title)
def category_comparison_figure(
frame: pd.DataFrame,
metric: str,
title: str,
color: str,
empty_message: str,
prefix: str = "",
) -> go.Figure:
if frame.empty:
return empty_figure(title, empty_message)
ordered = frame.sort_values(metric, ascending=True)
figure = go.Figure(
go.Bar(
x=ordered[metric],
y=ordered["category_label"],
orientation="h",
marker={"color": color, "line": {"width": 0}},
hovertemplate=(
f"%{{y}}
{prefix}%{{x:,.2f}}"
if prefix
else "%{y}
%{x:,.0f}"
),
)
)
styled = style_figure(figure, title)
styled.update_layout(margin={"l": 120, "r": 24, "t": 62, "b": 48})
return styled
def category_comparison_frame(
snapshot: Snapshot,
version: str,
market_type: str,
month: str,
language: str,
) -> pd.DataFrame:
frame = snapshot.monthly[
(snapshot.monthly["version"] == version)
& (snapshot.monthly["market_type"] == market_type)
& (snapshot.monthly["month"] == month)
& (snapshot.monthly["category"] != "all")
].copy()
language_key = "zh" if normalized_language(language) == CHINESE else "en"
labels = {
str(item["key"]): str(item["labels"].get(language_key, item["key"]))
for item in snapshot.categories
if bool(item["enabled"]) and not bool(item["aggregate_only"])
}
frame = frame[frame["category"].isin(labels)].copy()
frame["category_label"] = frame["category"].map(labels)
return frame
def kpi_html(
frame: pd.DataFrame,
month: str,
language: str,
) -> str:
if frame.empty:
values = ("0", "0", "$0", "$0", "N/A")
else:
values = (
compact_number(float(frame["fill_event_count"].sum())),
compact_number(float(frame["unique_transaction_count"].sum())),
compact_usd(float(frame["nominal_collateral_volume"].sum())),
compact_usd(float(frame["fee_amount"].sum())),
month,
)
labels = TEXT[normalized_language(language)]["kpis"]
cards = "".join(
(
''
f'
{label}
'
f'
{value}
'
"
"
)
for label, value in zip(labels, values, strict=True)
)
return f'{cards}
'
def status_text(snapshot: Snapshot, language: str) -> str:
copy = TEXT[normalized_language(language)]
generated = snapshot.generated_at.strftime("%Y-%m-%d %H:%M UTC")
loaded = snapshot.loaded_at.strftime("%Y-%m-%d %H:%M:%S UTC")
category_suffix = (
f" · Categories: `{snapshot.category_config_sha256[:12]}`"
if snapshot.category_config_sha256
else ""
)
return (
f"{copy['generated']}: **{generated}** · "
f"{copy['refreshed']}: **{loaded}** · "
f"Bucket: `{STORE.bucket_id}`{category_suffix}"
)
def table_frame(
frame: pd.DataFrame,
language: str,
) -> pd.DataFrame:
columns = TEXT[normalized_language(language)]["columns"]
if frame.empty:
return pd.DataFrame(columns=columns)
table = frame[
[
"month",
"fill_event_count",
"unique_transaction_count",
"nominal_collateral_volume",
"fee_amount",
"is_complete",
]
].copy()
table.columns = columns
volume_column = columns[3]
fee_column = columns[4]
month_column = columns[0]
table[volume_column] = table[volume_column].map(
lambda value: f"${value:,.2f}"
)
table[fee_column] = table[fee_column].map(
lambda value: f"${value:,.2f}"
)
return table.sort_values(month_column, ascending=False)
def render_dashboard(
version: str,
market_type: str,
category: str,
selected_year: str | None,
selected_calendar_month: str | None,
language: str,
refresh: bool = False,
) -> tuple[Any, ...]:
copy = TEXT[normalized_language(language)]
snapshot = STORE.refresh() if refresh else STORE.get()
categories = available_categories(
snapshot.monthly,
snapshot.categories,
version,
market_type,
)
category = category if category in categories else categories[0]
monthly = filter_metrics(
snapshot.monthly,
version,
market_type,
category,
)
yearly = aggregate_period_metrics(monthly, "year")
quarterly = display_quarters(
aggregate_period_metrics(monthly, "quarter")
)
months = available_months(
snapshot.monthly,
version,
market_type,
category,
)
(
years,
year,
calendar_months,
calendar_month,
month,
) = resolve_year_month(
months,
selected_year,
selected_calendar_month,
)
daily = filter_metrics(snapshot.daily, version, market_type, category)
daily = daily[daily["month"] == month].copy()
comparison = category_comparison_frame(
snapshot,
version,
market_type,
month,
language,
)
volume_title = copy["volume_title"]
fills_title = copy["fills_title"]
daily_volume_title = f"{copy['daily_volume_title']} · {month or 'N/A'}"
daily_fills_title = f"{copy['daily_fills_title']} · {month or 'N/A'}"
return (
kpi_html(monthly, month or "N/A", language),
bar_figure(
yearly,
"period",
"nominal_collateral_volume",
copy["yearly_volume_title"],
COLORS["cyan"],
copy["empty"],
"$",
),
bar_figure(
yearly,
"period",
"fill_event_count",
copy["yearly_fills_title"],
COLORS["blue"],
copy["empty"],
),
bar_figure(
quarterly,
"period",
"nominal_collateral_volume",
copy["quarterly_volume_title"],
COLORS["cyan"],
copy["empty"],
"$",
),
bar_figure(
quarterly,
"period",
"fill_event_count",
copy["quarterly_fills_title"],
COLORS["blue"],
copy["empty"],
),
bar_figure(
monthly,
"month",
"nominal_collateral_volume",
volume_title,
COLORS["cyan"],
copy["empty"],
"$",
),
bar_figure(
monthly,
"month",
"fill_event_count",
fills_title,
COLORS["blue"],
copy["empty"],
),
gr.update(choices=years, value=year),
gr.update(
choices=calendar_month_choices(calendar_months, language),
value=calendar_month,
),
bar_figure(
daily,
"date",
"nominal_collateral_volume",
daily_volume_title,
COLORS["amber"],
copy["empty"],
"$",
),
bar_figure(
daily,
"date",
"fill_event_count",
daily_fills_title,
COLORS["blue"],
copy["empty"],
),
table_frame(monthly, language),
status_text(snapshot, language),
category_comparison_figure(
comparison,
"nominal_collateral_volume",
f"{copy['category_volume_title']} · {month or 'N/A'}",
COLORS["cyan"],
copy["empty"],
"$",
),
category_comparison_figure(
comparison,
"fill_event_count",
f"{copy['category_fills_title']} · {month or 'N/A'}",
COLORS["blue"],
copy["empty"],
),
)
def update_market_types(
version: str,
language: str,
) -> Any:
snapshot = STORE.get()
choices = available_market_types(snapshot.monthly, version)
value = "all" if "all" in choices else choices[0]
return gr.update(
choices=market_choices(choices, language),
value=value,
label=TEXT[normalized_language(language)]["market"],
)
def update_categories(
version: str,
market_type: str,
language: str,
) -> Any:
snapshot = STORE.get()
values = available_categories(
snapshot.monthly,
snapshot.categories,
version,
market_type,
)
value = "all" if "all" in values else values[0]
return gr.update(
choices=category_choices(snapshot, values, language),
value=value,
label=TEXT[normalized_language(language)]["category"],
)
def localized_ui(
language: str,
version: str,
market_type: str,
category: str,
selected_year: str | None,
selected_calendar_month: str | None,
) -> tuple[Any, ...]:
language = normalized_language(language)
copy = TEXT[language]
snapshot = STORE.get()
markets = available_market_types(snapshot.monthly, version)
market_value = market_type if market_type in markets else markets[0]
categories = available_categories(
snapshot.monthly,
snapshot.categories,
version,
market_value,
)
category_value = category if category in categories else categories[0]
months = available_months(
snapshot.monthly,
version,
market_value,
category_value,
)
(
years,
year,
calendar_months,
calendar_month,
_,
) = resolve_year_month(
months,
selected_year,
selected_calendar_month,
)
return (
hero_html(language),
gr.update(
choices=version_choices(language),
value=version,
label=copy["version"],
),
gr.update(
choices=market_choices(markets, language),
value=market_value,
label=copy["market"],
),
gr.update(
choices=category_choices(snapshot, categories, language),
value=category_value,
label=copy["category"],
),
gr.update(
choices=years,
value=year,
label=copy["year"],
),
gr.update(
choices=calendar_month_choices(calendar_months, language),
value=calendar_month,
label=copy["calendar_month"],
),
gr.update(value=copy["refresh"]),
copy["yearly_section"],
copy["quarterly_section"],
copy["daily_section"],
copy["monthly_section"],
copy["monthly_table_section"],
copy["category_section"],
copy["metric_note"],
)
snapshot = STORE.get()
initial_version = "all"
initial_market = "all"
initial_categories = available_categories(
snapshot.monthly,
snapshot.categories,
initial_version,
initial_market,
)
initial_category = "all" if "all" in initial_categories else initial_categories[0]
initial_months = available_months(
snapshot.monthly,
initial_version,
initial_market,
initial_category,
)
initial_month = initial_months[-1]
initial_year, initial_calendar_month = initial_month.split("-")
initial = render_dashboard(
initial_version,
initial_market,
initial_category,
initial_year,
initial_calendar_month,
ENGLISH,
)
with gr.Blocks(
title="Polymarket OrderFilled Analytics",
) as demo:
browser_locale = gr.Textbox(value="en", visible=False)
with gr.Row(elem_classes=["hero"], equal_height=True):
hero = gr.HTML(hero_html(ENGLISH), scale=8, min_width=260)
refresh_button = gr.Button(
TEXT[ENGLISH]["refresh"],
variant="secondary",
elem_classes=["hero-refresh"],
scale=1,
min_width=150,
)
with gr.Row(elem_classes=["control-row"]):
version = gr.Dropdown(
choices=version_choices(ENGLISH),
value=initial_version,
label=TEXT[ENGLISH]["version"],
allow_custom_value=False,
filterable=False,
elem_classes=["filter-control"],
)
market_type = gr.Dropdown(
choices=market_choices(
available_market_types(snapshot.monthly, initial_version),
ENGLISH,
),
value=initial_market,
label=TEXT[ENGLISH]["market"],
allow_custom_value=False,
filterable=False,
elem_classes=["filter-control"],
)
category = gr.Dropdown(
choices=category_choices(
snapshot,
initial_categories,
ENGLISH,
),
value=initial_category,
label=TEXT[ENGLISH]["category"],
allow_custom_value=False,
filterable=False,
elem_classes=["filter-control"],
)
year = gr.Dropdown(
choices=sorted({value[:4] for value in initial_months}),
value=initial_year,
label=TEXT[ENGLISH]["year"],
allow_custom_value=False,
filterable=False,
elem_classes=["filter-control"],
)
calendar_month = gr.Dropdown(
choices=calendar_month_choices(
[
value[5:7]
for value in initial_months
if value.startswith(f"{initial_year}-")
],
ENGLISH,
),
value=initial_calendar_month,
label=TEXT[ENGLISH]["calendar_month"],
allow_custom_value=False,
filterable=False,
elem_classes=["filter-control"],
)
language = gr.Dropdown(
choices=list(LANGUAGES),
value=ENGLISH,
label="Language / 语言",
allow_custom_value=False,
filterable=False,
elem_classes=["filter-control"],
)
kpis = gr.HTML(initial[0], elem_classes=["kpi-host"])
category_heading = gr.Markdown(
TEXT[ENGLISH]["category_section"],
elem_classes=["section-title"],
)
with gr.Row(equal_height=True, elem_classes=["chart-row"]):
category_volume = gr.Plot(
initial[13],
show_label=False,
elem_classes=["chart"],
)
category_fills = gr.Plot(
initial[14],
show_label=False,
elem_classes=["chart"],
)
yearly_heading = gr.Markdown(
TEXT[ENGLISH]["yearly_section"],
elem_classes=["section-title"],
)
with gr.Row(equal_height=True, elem_classes=["chart-row"]):
yearly_volume = gr.Plot(
initial[1],
show_label=False,
elem_classes=["chart"],
)
yearly_fills = gr.Plot(
initial[2],
show_label=False,
elem_classes=["chart"],
)
quarterly_heading = gr.Markdown(
TEXT[ENGLISH]["quarterly_section"],
elem_classes=["section-title"],
)
with gr.Row(equal_height=True, elem_classes=["chart-row"]):
quarterly_volume = gr.Plot(
initial[3],
show_label=False,
elem_classes=["chart"],
)
quarterly_fills = gr.Plot(
initial[4],
show_label=False,
elem_classes=["chart"],
)
monthly_heading = gr.Markdown(
TEXT[ENGLISH]["monthly_section"],
elem_classes=["section-title"],
)
with gr.Row(equal_height=True, elem_classes=["chart-row"]):
monthly_volume = gr.Plot(
initial[5],
show_label=False,
elem_classes=["chart"],
)
monthly_fills = gr.Plot(
initial[6],
show_label=False,
elem_classes=["chart"],
)
daily_heading = gr.Markdown(
TEXT[ENGLISH]["daily_section"],
elem_classes=["section-title"],
)
with gr.Row(equal_height=True, elem_classes=["chart-row"]):
daily_volume = gr.Plot(
initial[9],
show_label=False,
elem_classes=["chart"],
)
daily_fills = gr.Plot(
initial[10],
show_label=False,
elem_classes=["chart"],
)
monthly_table_heading = gr.Markdown(
TEXT[ENGLISH]["monthly_table_section"],
elem_classes=["section-title"],
)
table = gr.Dataframe(
value=initial[11],
interactive=False,
wrap=True,
max_height=460,
)
status = gr.Markdown(initial[12], elem_classes=["status-line"])
outputs = [
kpis,
yearly_volume,
yearly_fills,
quarterly_volume,
quarterly_fills,
monthly_volume,
monthly_fills,
year,
calendar_month,
daily_volume,
daily_fills,
table,
status,
category_volume,
category_fills,
]
localized_outputs = [
hero,
version,
market_type,
category,
year,
calendar_month,
refresh_button,
yearly_heading,
quarterly_heading,
daily_heading,
monthly_heading,
monthly_table_heading,
category_heading,
]
version.change(
update_market_types,
inputs=[version, language],
outputs=market_type,
).then(
update_categories,
inputs=[version, market_type, language],
outputs=category,
).then(
render_dashboard,
inputs=[
version,
market_type,
category,
year,
calendar_month,
language,
],
outputs=outputs,
)
market_type.change(
update_categories,
inputs=[version, market_type, language],
outputs=category,
).then(
render_dashboard,
inputs=[
version,
market_type,
category,
year,
calendar_month,
language,
],
outputs=outputs,
)
category.change(
render_dashboard,
inputs=[
version,
market_type,
category,
year,
calendar_month,
language,
],
outputs=outputs,
)
year.change(
render_dashboard,
inputs=[
version,
market_type,
category,
year,
calendar_month,
language,
],
outputs=outputs,
)
calendar_month.change(
render_dashboard,
inputs=[
version,
market_type,
category,
year,
calendar_month,
language,
],
outputs=outputs,
)
refresh_button.click(
lambda version, market, category, year, month, language: (
render_dashboard(
version,
market,
category,
year,
month,
language,
refresh=True,
)
),
inputs=[
version,
market_type,
category,
year,
calendar_month,
language,
],
outputs=outputs,
)
metric_note = gr.Markdown(
TEXT[ENGLISH]["metric_note"],
elem_classes=["metric-note"],
)
localized_outputs.append(metric_note)
language.change(
localized_ui,
inputs=[
language,
version,
market_type,
category,
year,
calendar_month,
],
outputs=localized_outputs,
).then(
render_dashboard,
inputs=[
version,
market_type,
category,
year,
calendar_month,
language,
],
outputs=outputs,
)
demo.load(
browser_language,
inputs=browser_locale,
outputs=language,
js="() => navigator.language || 'en'",
).then(
localized_ui,
inputs=[
language,
version,
market_type,
category,
year,
calendar_month,
],
outputs=localized_outputs,
).then(
render_dashboard,
inputs=[
version,
market_type,
category,
year,
calendar_month,
language,
],
outputs=outputs,
)
if __name__ == "__main__":
demo.launch(theme=THEME, css=CSS, head=HEAD, ssr_mode=False)