myco-bonding-curve/dashboard/tabs/dca_explorer.py

119 lines
4.2 KiB
Python

"""DCA Explorer tab — strategy comparison + subscription DCA."""
import streamlit as st
from src.composed.simulator import scenario_dca_comparison
from src.composed.subscription_dca import (
SubscriptionDCAConfig,
simulate_subscription_dca,
)
from src.composed.myco_surface import MycoSystemConfig
from src.commitments.subscription import SubscriptionTier
from dashboard.charts import fig_dca_comparison
import plotly.graph_objects as go
from plotly.subplots import make_subplots
def render():
st.header("DCA Explorer")
tab_compare, tab_subscription = st.tabs(["Strategy Comparison", "Subscription DCA"])
with tab_compare:
_render_comparison()
with tab_subscription:
_render_subscription()
def _render_comparison():
config = st.session_state.get("myco_config")
n_assets = config.n_reserve_assets if config else 3
col1, col2, col3 = st.columns(3)
with col1:
total = st.number_input("Total amount ($)", 1000, 100_000, 10_000, step=1000, key="dca_total")
with col2:
chunks = st.slider("Chunks", 5, 50, 20, key="dca_chunks")
with col3:
interval = st.number_input("Interval", 0.5, 10.0, 1.0, step=0.5, key="dca_interval")
if st.button("Compare Strategies", key="dca_compare_run"):
with st.spinner("Running DCA comparison..."):
results = scenario_dca_comparison(
n_assets=n_assets,
total_amount=float(total),
n_chunks=chunks,
interval=float(interval),
)
st.session_state["dca_results"] = results
if "dca_results" in st.session_state:
results = st.session_state["dca_results"]
cols = st.columns(len(results))
for i, (name, r) in enumerate(results.items()):
with cols[i]:
st.subheader(name)
st.metric("Tokens", f"{r.order.total_tokens_received:.2f}")
st.metric("Avg Price", f"{r.order.avg_price:.6f}")
st.metric("DCA Advantage", f"{r.dca_advantage:+.4f}")
st.plotly_chart(fig_dca_comparison(results), use_container_width=True)
def _render_subscription():
config = st.session_state.get("myco_config", MycoSystemConfig())
col1, col2, col3 = st.columns(3)
with col1:
tier_payment = st.number_input("Payment/period ($)", 50, 5000, 100, step=50, key="sub_payment")
with col2:
n_chunks = st.slider("DCA chunks", 2, 20, 5, key="sub_chunks")
with col3:
duration = st.slider("Duration (days)", 90, 730, 365, key="sub_duration")
if st.button("Simulate Subscription DCA", key="sub_run"):
tiers = {
"custom": SubscriptionTier(
name="custom",
payment_per_period=float(tier_payment),
period_length=30.0,
loyalty_multiplier_max=2.0,
loyalty_halflife=90.0,
),
}
dca_config = SubscriptionDCAConfig(n_chunks=n_chunks, spread_fraction=0.8)
subscribers = [("subscriber_1", "custom", 0.0)]
with st.spinner("Running subscription DCA..."):
sim = simulate_subscription_dca(
tiers=tiers, config=dca_config, system_config=config,
subscribers=subscribers, duration=float(duration), dt=1.0,
)
st.session_state["sub_dca_result"] = sim
if "sub_dca_result" in st.session_state:
sim = st.session_state["sub_dca_result"]
t = sim["times"].tolist()
fig = make_subplots(
rows=1, cols=2,
subplot_titles=("Token Accumulation", "Total Supply"),
)
fig.add_trace(go.Scatter(
x=t, y=sim["total_curve_tokens"].tolist(),
name="Curve Tokens", line=dict(color="#2196F3"),
), row=1, col=1)
fig.add_trace(go.Scatter(
x=t, y=sim["total_loyalty_bonus"].tolist(),
name="Loyalty Bonus", line=dict(color="#FF9800", dash="dash"),
), row=1, col=1)
fig.add_trace(go.Scatter(
x=t, y=sim["total_supply"].tolist(),
name="Supply", line=dict(color="#4CAF50"),
), row=1, col=2)
fig.update_layout(height=400, template="plotly_white")
st.plotly_chart(fig, use_container_width=True)