# ========================================================= # SIPXAR — PAPER TRADING (SESSION STATE ONLY) # ========================================================= import math import numpy as np import pandas as pd import yfinance as yf import streamlit as st from datetime import datetime # ========================================================= # CONFIG # ========================================================= MONTHLY_SIP = 10000 NIFTY = "NIFTYBEES.NS" GOLD = "GOLDBEES.NS" SMA_MONTHS = 36 BASE_EQUITY = 0.60 TILT_MULT = 1.5 MAX_TILT = 0.25 MIN_EQUITY = 0.20 MAX_EQUITY = 0.90 # ========================================================= # DATA # ========================================================= @st.cache_data(ttl=300) def fetch_price(ticker): df = yf.download( ticker, period="5d", interval="1d", auto_adjust=True, progress=False ) return float(df["Close"].iloc[-1].item()) @st.cache_data(ttl=3600) def fetch_sma(ticker): df = yf.download( ticker, period=f"{SMA_MONTHS+2}mo", interval="1mo", auto_adjust=True, progress=False ).dropna() return float(df["Close"].tail(SMA_MONTHS).mean()) # ========================================================= # STRATEGY # ========================================================= def compute_weights(n_price, g_price, n_sma, g_sma): dev_n = (n_price / n_sma) - 1 dev_g = (g_price / g_sma) - 1 rel = dev_n - dev_g tilt = np.clip(-rel * TILT_MULT, -MAX_TILT, MAX_TILT) eq_w = BASE_EQUITY * (1 + tilt) eq_w = min(max(eq_w, MIN_EQUITY), MAX_EQUITY) return eq_w, 1 - eq_w # ========================================================= # SESSION STATE INIT # ========================================================= if "nifty_units" not in st.session_state: st.session_state.nifty_units = 0 st.session_state.gold_units = 0 st.session_state.invested = 0 st.session_state.ledger = [] # ========================================================= # STREAMLIT UI # ========================================================= st.set_page_config(page_title="SIPXAR Paper Trading", layout="centered") st.title("📊 SIPXAR — Paper Trading (Session Only)") # Fetch market data n_price = fetch_price(NIFTY) g_price = fetch_price(GOLD) n_sma = fetch_sma(NIFTY) g_sma = fetch_sma(GOLD) eq_w, g_w = compute_weights(n_price, g_price, n_sma, g_sma) # Market snapshot st.subheader("Market Snapshot") st.write(f"**NIFTY:** ₹{n_price:.2f}") st.write(f"**GOLD:** ₹{g_price:.2f}") # Allocation st.subheader("Allocation Weights") st.progress(eq_w) st.write(f"Equity: **{eq_w:.2%}** | Gold: **{g_w:.2%}**") # ========================================================= # SIP ACTION # ========================================================= if st.button("Run Paper SIP (Once)"): n_amt = MONTHLY_SIP * eq_w g_amt = MONTHLY_SIP * g_w n_qty = math.floor(n_amt / n_price) g_qty = math.floor(g_amt / g_price) if n_qty == 0 and g_qty == 0: st.warning( "SIP amount too small to buy ETF units at current prices." ) else: st.session_state.nifty_units += n_qty st.session_state.gold_units += g_qty st.session_state.invested += MONTHLY_SIP port_value = ( st.session_state.nifty_units * n_price + st.session_state.gold_units * g_price ) st.session_state.ledger.append({ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "nifty_units": st.session_state.nifty_units, "gold_units": st.session_state.gold_units, "invested": st.session_state.invested, "portfolio_value": port_value, "pnl": port_value - st.session_state.invested }) st.success("Paper SIP executed") # ========================================================= # PORTFOLIO VIEW # ========================================================= if st.session_state.invested > 0: port_value = ( st.session_state.nifty_units * n_price + st.session_state.gold_units * g_price ) pnl = port_value - st.session_state.invested st.subheader("Portfolio Summary") st.metric("Total Invested", f"₹{st.session_state.invested:,.0f}") st.metric("Portfolio Value", f"₹{port_value:,.0f}") st.metric("PnL", f"₹{pnl:,.0f}") df = pd.DataFrame(st.session_state.ledger) st.subheader("Equity Curve") st.line_chart(df[["portfolio_value", "invested"]]) st.subheader("Ledger") st.dataframe(df, use_container_width=True) else: st.info("No SIPs yet. Click the button to start.")