204 lines
6.4 KiB
Python
204 lines
6.4 KiB
Python
"""Plotly figure factories for the MYCO dashboard.
|
|
|
|
All functions return go.Figure instances — no Streamlit dependency.
|
|
"""
|
|
|
|
import plotly.graph_objects as go
|
|
from plotly.subplots import make_subplots
|
|
|
|
|
|
def fig_simulation_overview(result) -> go.Figure:
|
|
"""2x2 subplot: supply, reserve, backing ratio, minting breakdown."""
|
|
fig = make_subplots(
|
|
rows=2, cols=2,
|
|
subplot_titles=("Supply", "Reserve Value", "Backing Ratio", "Minting"),
|
|
)
|
|
|
|
t = result.times.tolist()
|
|
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=result.supply.tolist(), name="Supply", line=dict(color="#2196F3")),
|
|
row=1, col=1,
|
|
)
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=result.reserve_value.tolist(), name="Reserve", line=dict(color="#4CAF50")),
|
|
row=1, col=2,
|
|
)
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=result.backing_ratio.tolist(), name="Backing Ratio", line=dict(color="#FF9800")),
|
|
row=2, col=1,
|
|
)
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=result.financial_minted.tolist(), name="Financial", line=dict(color="#9C27B0")),
|
|
row=2, col=2,
|
|
)
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=result.commitment_minted.tolist(), name="Commitment", line=dict(color="#E91E63")),
|
|
row=2, col=2,
|
|
)
|
|
|
|
fig.update_layout(height=600, showlegend=True, template="plotly_white")
|
|
return fig
|
|
|
|
|
|
def fig_dca_comparison(results: dict) -> go.Figure:
|
|
"""Chunk prices + cumulative tokens for DCA strategies."""
|
|
fig = make_subplots(
|
|
rows=1, cols=2,
|
|
subplot_titles=("Price per Chunk", "Cumulative Tokens"),
|
|
)
|
|
|
|
colors = {"fixed": "#2196F3", "twap_aware": "#FF9800"}
|
|
|
|
for name, dca_result in results.items():
|
|
history = dca_result.order.history
|
|
chunks_x = list(range(len(history)))
|
|
prices = [h["price"] for h in history]
|
|
tokens = [h["tokens"] for h in history]
|
|
|
|
import numpy as np
|
|
cumulative = np.cumsum(tokens).tolist()
|
|
|
|
fig.add_trace(
|
|
go.Scatter(x=chunks_x, y=prices, name=f"{name} price",
|
|
mode="lines+markers", line=dict(color=colors[name])),
|
|
row=1, col=1,
|
|
)
|
|
fig.add_trace(
|
|
go.Scatter(x=chunks_x, y=cumulative, name=f"{name} cumulative",
|
|
mode="lines+markers", line=dict(color=colors[name])),
|
|
row=1, col=2,
|
|
)
|
|
|
|
# Lump sum reference
|
|
for name, dca_result in results.items():
|
|
fig.add_hline(
|
|
y=dca_result.lump_sum_tokens, row=1, col=2,
|
|
line_dash="dash", line_color="gray",
|
|
annotation_text="lump sum" if name == "fixed" else None,
|
|
)
|
|
break
|
|
|
|
fig.update_layout(height=400, template="plotly_white")
|
|
return fig
|
|
|
|
|
|
def fig_signal_routing(routing: dict) -> go.Figure:
|
|
"""3-panel: price+TWAP, deviation+volatility, adaptive params."""
|
|
fig = make_subplots(
|
|
rows=3, cols=1, shared_xaxes=True,
|
|
subplot_titles=("Price Trajectory", "Signals", "Adaptive Parameters"),
|
|
)
|
|
|
|
t = routing["times"]
|
|
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=routing.get("prices", t), name="Spot Price",
|
|
line=dict(color="#2196F3")),
|
|
row=1, col=1,
|
|
)
|
|
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=routing["twap_deviation"], name="TWAP Deviation",
|
|
line=dict(color="#FF5722")),
|
|
row=2, col=1,
|
|
)
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=routing["volatility"], name="Volatility",
|
|
line=dict(color="#9C27B0")),
|
|
row=2, col=1,
|
|
)
|
|
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=routing["flow_threshold"], name="Flow Threshold",
|
|
line=dict(color="#4CAF50")),
|
|
row=3, col=1,
|
|
)
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=routing["surge_fee_rate"], name="Surge Fee",
|
|
line=dict(color="#FF9800")),
|
|
row=3, col=1,
|
|
)
|
|
|
|
fig.update_layout(height=700, template="plotly_white")
|
|
return fig
|
|
|
|
|
|
def fig_bank_run_sweep(results: dict) -> go.Figure:
|
|
"""Overlaid reserve curves for different redemption fractions."""
|
|
fig = go.Figure()
|
|
|
|
colors = ["#2196F3", "#FF9800", "#E91E63", "#4CAF50", "#9C27B0"]
|
|
for i, (frac, result) in enumerate(results.items()):
|
|
fig.add_trace(go.Scatter(
|
|
x=result.times.tolist(),
|
|
y=result.reserve_value.tolist(),
|
|
name=f"{frac:.0%} redemption",
|
|
line=dict(color=colors[i % len(colors)]),
|
|
))
|
|
|
|
fig.update_layout(
|
|
title="Bank Run Stress Test — Reserve Curves",
|
|
xaxis_title="Time",
|
|
yaxis_title="Reserve Value",
|
|
height=500,
|
|
template="plotly_white",
|
|
)
|
|
return fig
|
|
|
|
|
|
def fig_crdt_divergence(sim_result: dict) -> go.Figure:
|
|
"""Peer divergence timeline + merge event scatter."""
|
|
fig = make_subplots(
|
|
rows=2, cols=1, shared_xaxes=True,
|
|
subplot_titles=("Peer Divergence", "Network Events"),
|
|
)
|
|
|
|
t = sim_result["times"]
|
|
|
|
# Divergence line
|
|
fig.add_trace(
|
|
go.Scatter(x=t, y=sim_result["divergence"], name="Divergence",
|
|
line=dict(color="#2196F3", width=2)),
|
|
row=1, col=1,
|
|
)
|
|
fig.add_hline(y=1, line_dash="dash", line_color="green", row=1, col=1)
|
|
|
|
# Shade partitions
|
|
for i, active in enumerate(sim_result["partition_active"]):
|
|
if active:
|
|
fig.add_vrect(
|
|
x0=t[i] - 0.5, x1=t[i] + 0.5,
|
|
fillcolor="red", opacity=0.1, line_width=0,
|
|
row=1, col=1,
|
|
)
|
|
|
|
# Event scatter
|
|
from src.crdt.bridge.events import EventType
|
|
events = sim_result.get("events", [])
|
|
peer_ids = list(sim_result.get("peer_signatures", {}).keys())
|
|
peer_y = {pid: i for i, pid in enumerate(peer_ids)}
|
|
|
|
for etype, color, marker, label in [
|
|
(EventType.MERGE, "blue", "circle", "Merge"),
|
|
(EventType.PARTITION, "red", "x", "Partition"),
|
|
(EventType.RECONNECT, "green", "triangle-up", "Reconnect"),
|
|
]:
|
|
filtered = [e for e in events if e.event_type == etype]
|
|
if filtered:
|
|
fig.add_trace(go.Scatter(
|
|
x=[e.time for e in filtered],
|
|
y=[peer_y.get(e.source_peer, 0) for e in filtered],
|
|
mode="markers",
|
|
marker=dict(color=color, symbol=marker, size=8),
|
|
name=label,
|
|
), row=2, col=1)
|
|
|
|
fig.update_yaxes(
|
|
tickvals=list(range(len(peer_ids))),
|
|
ticktext=peer_ids,
|
|
row=2, col=1,
|
|
)
|
|
fig.update_layout(height=600, template="plotly_white")
|
|
return fig
|