Skip to content

Commit 9414d6f

Browse files
Merge pull request #22 from merendamattia/develop
Update portfolio justification requirements and improve UI feedback
2 parents 1f80720 + ac2b16e commit 9414d6f

File tree

8 files changed

+107
-28
lines changed

8 files changed

+107
-28
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ RAG_EMBEDDING_MODEL=all-roberta-large-v1
3232
MONTECARLO_SIMULATION_SCENARIOS=1000
3333
MONTECARLO_SIMULATION_YEARS=20
3434
MONTECARLO_MIN_ASSET_VOLATILITY=0.1
35+
MONTECARLO_DEFAULT_INITIAL_INVESTMENT=1000

Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ FROM python:3.11-slim
55
RUN apt-get update && apt-get install -y --no-install-recommends \
66
build-essential \
77
curl \
8+
unzip \
89
&& rm -rf /var/lib/apt/lists/*
910

1011
# Copy requirements and install
1112
COPY requirements.txt .
12-
RUN pip install --no-cache-dir -r requirements.txt
13+
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
1314

1415
# Set working directory
1516
WORKDIR /app
@@ -21,6 +22,9 @@ COPY prompts/ prompts/
2122
COPY dataset/ dataset/
2223
COPY .env.example .env
2324

25+
# Unzip dataset
26+
RUN cd dataset && unzip ETFs.zip && rm ETFs.zip
27+
2428
# Create non-root user for security
2529
RUN useradd -m -u 1000 streamlit && chown -R streamlit:streamlit /app
2630
USER streamlit

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
44
[![Latest Release](https://img.shields.io/github/v/release/merendamattia/personal-financial-ai-agent?label=release)](https://github.com/merendamattia/personal-financial-ai-agent/releases)
5+
[![Docker Hub](https://img.shields.io/badge/Docker%20Hub-personal--financial--ai--agent-blue?logo=docker)](https://hub.docker.com/repository/docker/merendamattia/personal-financial-ai-agent)
56
[![Python 3.11+](https://img.shields.io/badge/Python-3.11%2B-blue)](https://www.python.org/)
67

78
An intelligent personal financial advisor powered by AI. Get expert financial guidance in your preferred language with support for multiple LLM providers, including local offline inference with Ollama.
@@ -112,7 +113,7 @@ Choose your preferred AI provider based on your needs:
112113
1. Create account and get API key from [OpenAI Platform](https://platform.openai.com/api-keys)
113114
2. Set `OPENAI_API_KEY` in `.env`
114115
- **Configuration:**
115-
- `OPENAI_MODEL` in `.env` (default: `gpt-4o-mini`)
116+
- `OPENAI_MODEL` in `.env` (default: `gpt-4.1-mini`)
116117

117118
**Provider Selection:**
118119
The app detects available providers from your `.env` configuration. Select your preferred provider when starting the app or switch anytime using the sidebar button.

app.py

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
os.getenv("MONTECARLO_SIMULATION_SCENARIOS", 1000)
3030
)
3131
MONTECARLO_SIMULATION_YEARS = int(os.getenv("MONTECARLO_SIMULATION_YEARS", 20))
32+
MONTECARLO_DEFAULT_INITIAL_INVESTMENT = int(
33+
os.getenv("MONTECARLO_DEFAULT_INITIAL_INVESTMENT", 1000)
34+
)
3235

3336
# Configure logging
3437
logging.basicConfig(
@@ -492,10 +495,7 @@ def _show_provider_selection():
492495
"""
493496
logger.debug("No provider selected, showing provider selection modal")
494497

495-
st.title("💰 Financial AI Agent")
496-
st.markdown("Your personal financial advisor powered by AI")
497-
498-
st.divider()
498+
_show_header()
499499
st.subheader("🔧 Select Your LLM Provider")
500500
st.markdown(
501501
"Choose which AI service to use for this conversation. You can change this anytime."
@@ -554,6 +554,25 @@ def _show_provider_selection():
554554
)
555555

556556

557+
def _show_header():
558+
"""
559+
Display the application header.
560+
"""
561+
st.title("💰 Financial AI Agent")
562+
st.markdown(
563+
"An intelligent personal financial advisor powered by AI. Get expert financial guidance in your preferred language with support for multiple LLM providers, including local offline inference with Ollama."
564+
)
565+
566+
st.markdown(
567+
"""
568+
[![GitHub](https://img.shields.io/badge/GitHub-merendamattia%2Fpersonal--financial--ai--agent-black?logo=github)](https://github.com/merendamattia/personal-financial-ai-agent)
569+
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
570+
[![Latest Release](https://img.shields.io/github/v/release/merendamattia/personal-financial-ai-agent?label=release)](https://github.com/merendamattia/personal-financial-ai-agent/releases)
571+
"""
572+
)
573+
st.divider()
574+
575+
557576
def _show_loading_screen():
558577
"""
559578
Display loading screen while initializing agent.
@@ -563,10 +582,7 @@ def _show_loading_screen():
563582
"""
564583
logger.debug("Agent not yet initialized, showing loading screen")
565584

566-
st.title("💰 Financial AI Agent")
567-
st.markdown("Your personal financial advisor powered by AI")
568-
569-
st.divider()
585+
_show_header()
570586

571587
# Loading animation
572588
loading_col1, loading_col2, loading_col3 = st.columns([1, 2, 1])
@@ -732,8 +748,7 @@ def _display_financial_profile_summary():
732748
"""
733749
if st.session_state.financial_profile:
734750
logger.debug("Displaying extracted financial profile")
735-
st.divider()
736-
st.subheader("📊 Financial Profile Summary")
751+
st.subheader("Financial Profile Summary")
737752

738753
# Convert profile to dictionary for display
739754
profile_dict = st.session_state.financial_profile.model_dump()
@@ -835,8 +850,8 @@ def _display_expected_returns(portfolio, financial_advisor_agent):
835850
return
836851

837852
# Display disclaimer about past performance
838-
st.info(
839-
"⚠️ **Important:** I rendimenti passati non sono indicatori affidabili dei rendimenti futuri. "
853+
st.warning(
854+
"⚠️ **Importante:** I rendimenti passati non sono indicatori affidabili dei rendimenti futuri. "
840855
"Queste proiezioni si basano su medie storiche e possono variare significativamente."
841856
)
842857

@@ -1007,6 +1022,8 @@ def _display_wealth_simulation(
10071022
try:
10081023
if asset_symbol.upper() == "BITCOIN":
10091024
asset_symbol = "BTC-EUR"
1025+
elif asset_symbol.upper() == "GOLD":
1026+
asset_symbol = "SGLD"
10101027

10111028
result_data = financial_advisor_agent.analyze_asset(
10121029
asset_symbol, years=10
@@ -1124,6 +1141,14 @@ def _display_wealth_simulation(
11241141
monthly_contribution,
11251142
)
11261143

1144+
# Check if initial_investment is 0 and replace with symbolic value
1145+
if initial_investment == 0:
1146+
logger.warning(
1147+
"PAC SECTION - Initial investment is 0, using symbolic value of €%s",
1148+
MONTECARLO_DEFAULT_INITIAL_INVESTMENT,
1149+
)
1150+
initial_investment = MONTECARLO_DEFAULT_INITIAL_INVESTMENT
1151+
11271152
time_steps = MONTECARLO_SIMULATION_YEARS * 12 # Monthly steps
11281153

11291154
# Run Monte Carlo simulation
@@ -1590,8 +1615,7 @@ def main():
15901615
st.rerun()
15911616

15921617
# Header
1593-
st.title("💰 Financial AI Agent")
1594-
st.markdown("Your personal financial advisor powered by AI")
1618+
_show_header()
15951619

15961620
# Sidebar
15971621
_setup_sidebar(chatbot_agent, financial_advisor_agent)
@@ -1629,14 +1653,14 @@ def main():
16291653

16301654
# Show message if conversation is completed
16311655
if st.session_state.conversation_completed:
1632-
logger.debug("Conversation is completed, showing completion message")
1633-
st.info(
1634-
"**Assessment completed!** All your financial questions have been answered and the summary has been provided. "
1635-
"Click 'Clear Conversation' to start a new assessment or 'Change Provider' to start over."
1636-
)
1656+
if not st.session_state.generated_portfolio:
1657+
st.toast(
1658+
"Analyzing profile and generating portfolio...",
1659+
icon="🔄",
1660+
duration="long",
1661+
)
16371662

1638-
# Display financial profile
1639-
_display_financial_profile_summary()
1663+
logger.debug("Conversation is completed")
16401664

16411665
# Generate and display portfolio
16421666
if st.session_state.financial_profile:
@@ -1660,7 +1684,6 @@ def main():
16601684
if portfolio:
16611685
st.session_state.generated_portfolio = portfolio
16621686
logger.info("Portfolio auto-generated successfully")
1663-
st.rerun()
16641687
else:
16651688
logger.warning("Failed to generate portfolio")
16661689
st.error(
@@ -1676,7 +1699,6 @@ def main():
16761699
)
16771700
if portfolio:
16781701
st.session_state.generated_portfolio = portfolio
1679-
st.rerun()
16801702
else:
16811703
logger.debug("Portfolio already generated, displaying...")
16821704

@@ -1689,6 +1711,34 @@ def main():
16891711
st.session_state.generated_portfolio, financial_advisor_agent
16901712
)
16911713

1714+
# Show completion message after portfolio
1715+
st.divider()
1716+
st.toast(
1717+
"Assessment Completed Successfully! Your personalized portfolio analysis is ready.",
1718+
icon="🎉",
1719+
duration="long",
1720+
)
1721+
1722+
# Celebration animation and message
1723+
st.balloons()
1724+
st.success(
1725+
"🎉 **Assessment Completed Successfully!** 🎉\n\n"
1726+
"All your financial questions have been answered and your personalized portfolio analysis is ready! "
1727+
"Your financial profile and PAC metrics have been extracted and analyzed."
1728+
)
1729+
1730+
# Financial Profile in an expanded section
1731+
with st.expander(
1732+
"📊 View Your Financial Profile & Summary", expanded=False
1733+
):
1734+
_display_financial_profile_summary()
1735+
st.info(
1736+
"💡 **Next Steps:**\n"
1737+
"- Review your portfolio allocation above\n"
1738+
"- Consider consulting with a financial advisor for personalized advice\n"
1739+
"- Click 'Clear Conversation' to start a new assessment or 'Change Provider' to start over"
1740+
)
1741+
16921742
else:
16931743
logger.debug("No financial profile available to display")
16941744
else:

prompts/portfolio_extraction.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ Generate a well-balanced, customized investment portfolio that:
2121
1. Maximum 3-5 assets to maintain simplicity and manageability
2222
2. All allocations must sum to 100%
2323
3. Minimum 10% in bonds (unless explicitly overridden by risk profile)
24-
4. Conservative allocation for specific profiles: if client is over 45 years old, has significant debts, or has dependents/family members to support, consider allocating at least 50% to bonds for capital preservation and stability
24+
4. Conservative allocation for specific profiles:
25+
- If client is over 45 years old, has significant debts, or has dependents/family members to support, consider allocating at least 50% to bonds for capital preservation and stability.
26+
- If client is under 45 years old, consider allocating at least 30% in stocks, while still maintaining a minimum bond allocation of 10% for portfolio stability as required above.
2527
5. Conservative portfolios: Include Gold (precious metals hedge) as 5-10% allocation (write only GOLD, do not use any other assets name)
2628
6. Aggressive portfolios: Consider Bitcoin (5-15%) for growth diversification (write only BITCOIN, do not use any other assets name)
2729
7. Each asset must have explicit justification based on the client's profile
@@ -30,7 +32,7 @@ Generate a well-balanced, customized investment portfolio that:
3032
10. Geographic reasoning: Include in justification why specific geographic regions were chosen based on client profile
3133

3234
## Critical Requirements:
33-
- Each asset must have a specific justification based on the client's profile
35+
- Each asset must have a specific justification based on the client's profile, INCLUDE also the complete name (not only the ticker) of the asset at the start of justification
3436
- Conservative investors: Include protective assets like bonds
3537
- Moderate investors: Balance growth and stability
3638
- Aggressive investors: Focus on growth assets with minimal bonds

requirements.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,25 @@ altair==5.5.0
77
annotated-types==0.7.0
88
antlr4-python3-runtime==4.9.3
99
anyio==4.9.0
10+
appnope==0.1.4
1011
asttokens==3.0.0
1112
attrs==25.4.0
1213
beautifulsoup4==4.14.2
1314
blinker==1.9.0
1415
Brotli==1.1.0
1516
cachetools==5.5.2
1617
certifi==2025.4.26
18+
cffi==2.0.0
1719
cfgv==3.4.0
1820
charset-normalizer==3.4.2
1921
click==8.2.1
2022
cloudpickle==3.1.1
2123
colorama==0.4.6
24+
coloredlogs==15.0.1
2225
colorlog==6.10.1
2326
comm==0.2.2
2427
contourpy==1.3.2
28+
curl_cffi==0.13.0
2529
cycler==0.12.1
2630
databricks-sdk==0.57.0
2731
datapizza-ai==0.0.2
@@ -44,10 +48,13 @@ et_xmlfile==2.0.0
4448
executing==2.2.0
4549
Faker==37.11.0
4650
fastapi==0.115.12
51+
fastembed==0.7.3
4752
filelock==3.18.0
4853
filetype==1.2.0
4954
Flask==3.1.1
55+
flatbuffers==25.9.23
5056
fonttools==4.58.2
57+
frozendict==2.4.6
5158
frozenlist==1.8.0
5259
fsspec==2025.5.1
5360
gitdb==4.0.12
@@ -74,6 +81,7 @@ httpcore==1.0.9
7481
httplib2==0.31.0
7582
httpx==0.28.1
7683
huggingface-hub==0.35.3
84+
humanfriendly==10.0
7785
hyperframe==6.1.0
7886
identify==2.6.15
7987
idna==3.10
@@ -98,6 +106,7 @@ kagglehub==0.3.12
98106
kiwisolver==1.4.8
99107
latex2mathml==3.78.1
100108
lazy_loader==0.4
109+
loguru==0.7.3
101110
Mako==1.3.10
102111
Markdown==3.8
103112
markdown-it-py==4.0.0
@@ -106,17 +115,21 @@ MarkupSafe==3.0.2
106115
matplotlib==3.9.2
107116
matplotlib-inline==0.1.7
108117
mdurl==0.1.2
118+
mmh3==5.2.0
109119
mpire==2.10.2
110120
mpmath==1.3.0
111121
multidict==6.7.0
112122
multiprocess==0.70.18
123+
multitasking==0.0.12
113124
narwhals==2.9.0
114125
nest-asyncio==1.6.0
115126
networkx==3.5
116127
ninja==1.13.0
117128
nodeenv==1.9.1
118129
numpy==2.2.6
119130
omegaconf==2.3.0
131+
onnxruntime==1.23.2
132+
openai==2.6.0
120133
opencv-python==4.12.0.88
121134
opencv-python-headless==4.12.0.88
122135
openpyxl==3.1.5
@@ -128,6 +141,7 @@ pandas==2.2.3
128141
pandas_flavor==0.7.0
129142
parso==0.8.4
130143
patsy==1.0.1
144+
peewee==3.18.2
131145
pexpect==4.9.0
132146
pillow==11.3.0
133147
pingouin==0.5.5
@@ -145,10 +159,12 @@ protobuf==5.29.5
145159
psutil==7.0.0
146160
ptyprocess==0.7.0
147161
pure_eval==0.2.3
162+
py_rust_stemmers==0.1.5
148163
pyarrow==19.0.1
149164
pyasn1==0.6.1
150165
pyasn1_modules==0.4.2
151166
pyclipper==1.3.0.post6
167+
pycparser==2.23
152168
pydantic==2.11.5
153169
pydantic-extra-types==2.10.6
154170
pydantic-settings==2.11.0
@@ -157,6 +173,7 @@ pydeck==0.9.1
157173
Pygments==2.19.1
158174
pylatexenc==2.10
159175
pyparsing==3.2.3
176+
pypdf==6.1.3
160177
pypdfium2==4.30.0
161178
pytest==8.4.2
162179
python-bidi==0.6.6
@@ -170,6 +187,7 @@ rapidocr==3.4.2
170187
readmeai==0.6.3
171188
referencing==0.37.0
172189
regex==2025.9.18
190+
reportlab==4.4.4
173191
requests==2.32.4
174192
rich==14.2.0
175193
rpds-py==0.27.1
@@ -181,6 +199,7 @@ scikit-learn==1.6.1
181199
scipy==1.13.1
182200
seaborn==0.13.2
183201
semchunk==2.2.2
202+
sentence-transformers==5.1.2
184203
shapely==2.1.2
185204
shellingham==1.5.4
186205
six==1.17.0
@@ -208,6 +227,7 @@ tornado==6.5.1
208227
tqdm==4.67.1
209228
traitlets==5.14.3
210229
transformers==4.57.1
230+
typer==0.19.2
211231
typing-inspection==0.4.1
212232
typing_extensions==4.14.0
213233
tzdata==2025.2

src/tools/analyze_financial_asset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def _search_and_resolve_symbol(symbol: str) -> SymbolResolution:
157157
"""
158158
logger.info("Searching for symbol: %s", symbol)
159159

160-
SYMBOLS_WITHOUT_SUFFIX = {"BTC-EUR", "GOLD"}
160+
SYMBOLS_WITHOUT_SUFFIX = {"BTC-EUR"}
161161

162162
try:
163163
if symbol.upper() in SYMBOLS_WITHOUT_SUFFIX:

0 commit comments

Comments
 (0)