| 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 = """ |
| <style> |
| html, body { |
| background: #f7fafc !important; |
| } |
| body .gradio-container { |
| box-sizing: border-box; |
| width: 100% !important; |
| max-width: 1400px !important; |
| margin: 0 auto !important; |
| padding: 20px clamp(16px, 2.5vw, 32px) 32px !important; |
| background: #f7fafc !important; |
| } |
| body .gradio-container > .main.fillable.app { |
| padding: 0 !important; |
| } |
| @media (max-width: 640px) { |
| html, body { |
| overflow-x: hidden !important; |
| } |
| body .gradio-container { |
| max-width: none !important; |
| padding: 8px 12px 20px !important; |
| } |
| } |
| </style> |
| """ |
|
|
| 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""" |
| <div class="hero-copy"> |
| <h1 style="color:#fff"> |
| <span class="desktop-title">Polymarket OrderFilled Analytics</span> |
| <span class="mobile-title">Polymarket Analytics</span> |
| </h1> |
| <p style="color:#d9f4f0">{copy["hero_subtitle"]}</p> |
| </div> |
| """ |
|
|
|
|
| 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}}<br>{prefix}%{{y:,.2f}}<extra></extra>" |
| if prefix |
| else "%{x}<br>%{y:,.0f}<extra></extra>" |
| ), |
| ) |
| ) |
| 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}}<br>{prefix}%{{x:,.2f}}<extra></extra>" |
| if prefix |
| else "%{y}<br>%{x:,.0f}<extra></extra>" |
| ), |
| ) |
| ) |
| 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( |
| ( |
| '<div class="kpi">' |
| f'<div class="kpi-label">{label}</div>' |
| f'<div class="kpi-value">{value}</div>' |
| "</div>" |
| ) |
| for label, value in zip(labels, values, strict=True) |
| ) |
| return f'<div class="kpi-grid">{cards}</div>' |
|
|
|
|
| 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) |
|
|