FinSense is my first hackathon build: a small autonomous “financial copilot” that tries to turn market noise into one clear, simple action. It chats, runs a deterministic portfolio simulation, blends in market sentiment, and fires risk alerts. Then it gives a summary (≤180 words), one Action line, and a disclaimer so it’s clear this is just a demo. Why I built it? Most tools give static allocations and ignore context like sentiment swings or volatility spikes. I wanted something that stays inside risk bands, explains why it makes a suggestion, and doesn’t fall apart during a live demo.
What’s different (for me): Agent + guardrails: I mix LLM reasoning with deterministic sims and a fixed output schema. Risk-aware tweaks: I cap sentiment-based tilts at ±5% and always renormalise to 100%. Demo-stable: Seeded Monte Carlo and a simple 24-hour sentiment cache keep results predictable. AWS-first: Bedrock + Lambda/SageMaker + EventBridge + S3, so it’s modular and cheap to run.
How I put it together: Analytics core (Python): Seeded sims for allocation + KPIs (exp. return, vol, max drawdown). Sentiment module: Daily label (bearish/neutral/bullish) with confidence; cached for 24h. Risk rules: Volatility spike, sentiment flip, exposure mismatch → alert with severity + evidence + suggested action. Orchestrator: Validates inputs, calls the tools, and prompts the LLM to produce the short explanation + one action + disclaimer. UI (demo): Minimal form (risk, horizon, optional age, optional sentiment override) plus results (advice, pie, KPIs, alerts, “Advanced” raw JSON).
What I learned: Pairing an LLM with seeded Monte Carlo gives me “smart but stable.” Small, explainable rules beat fancy heuristics when time is tight. Short prompts + strict schemas keep outputs tidy and consistent.
Challenges: Keeping the advice consistent (solved with a fixed format + fallback). Avoiding over-reaction to headlines (band-guarded ±5% tilts). Time and cost (token caps, timeouts, daily cache).
A bit of math (copy-paste LaTeX)
Equity price dynamics (GBM, discrete form) St+Δt=Stexp[(μ−21σ2)Δt+σΔtZt],Zt∼N(0,1).
Portfolio path and maximum drawdown Let Pt be the portfolio value at step t. The maximum drawdown (MDD) over the path is MDD=0≤t≤Tmax(max0≤u≤tPumax0≤u≤tPu−Pt).
Monte Carlo estimates (seeded for determinism) Generate N scenarios {Pt^(i)}^(T) t=0 with a fixed random seed. Define the 1-year return in scenario i as R^(i) 1y. then E[R1y] = N1i=1∑NR1y(i), σ[R1y]=N−11i=1∑N(R1y(i)−E[R1y])2
Portfolio composition With weights We, Wb, Wc for equities, bonds, and cash (so We + Wb + Wc = 1) , the scenario return mixes as R1y(i)=weReq(i)+wbRbond(i)+wcRcash(i).
Sentiment-aware tilt (band-guarded) Given a sentiment labels∈{bearish, neutral,bullish} with confidence c∈[0,1], I adjust equity weight by at most 5% scaled by c, staying inside the chosen risk band: Δwe=⎩⎨⎧−0.05c,0,+0.05c,s=bearish,s=neutral,s=bullish. After applying ΔWe, I offset bonds/cash as needed and renormalise so We+Wb+Wc =1. I then recompute the KPIs on the tilted weights.
Built With
- .env
- ai
- analytics/orchestrator)
- bedrock
- black
- charts)-modeling-&-data-numpy
- cloudwatch
- config
- dev
- dev/test/deploy)
- dotenv)
- eventbridge
- for
- format/lint)
- iam
- kpi
- lambda
- languages-&-runtime-python-3.x-ui-streamlit-(chat-ui
- llm
- logs/metrics)
- makefile
- ops
- orchestration
- pandas-monte-carlo-simulation-(seeded)-json-schema-/-pydantic-(i/o-validation)-charts-plotly-(allocation-pie
- pytest
- quality
- roles)
- ruff
- s3
- sagemaker
- scripts
- tests)
- ulid/uuid
- unit/contract
Log in or sign up for Devpost to join the conversation.