114 lines
4.0 KiB
Python
114 lines
4.0 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():
|
|
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(
|
|
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():
|
|
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,
|
|
),
|
|
}
|
|
config = SubscriptionDCAConfig(n_chunks=n_chunks, spread_fraction=0.8)
|
|
sys_config = MycoSystemConfig(n_reserve_assets=3)
|
|
subscribers = [("subscriber_1", "custom", 0.0)]
|
|
|
|
with st.spinner("Running subscription DCA..."):
|
|
sim = simulate_subscription_dca(
|
|
tiers=tiers, config=config, system_config=sys_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)
|