Dmitry Beresnev commited on
Commit
f7323a3
Β·
1 Parent(s): 50e9fe0

feat: restructure to multi-page app with modular architecture

Browse files

- Multi-page navigation (Stocks, Crypto, Forex, Screener, Dashboard)
- Reorganize into components/, pages/, utils/ structure
- Add caching layer for performance optimization
- HuggingFace Spaces ready with updated README
- All existing features preserved in Stock Analysis page

.env.example CHANGED
@@ -1 +1,10 @@
1
- OPENBB_TOKEN=openbb_workspace_token_here
 
 
 
 
 
 
 
 
 
 
1
+ # Financial Platform Environment Variables
2
+
3
+ # DeepSeek API Key (for AI-powered insights)
4
+ DEEPSEEK_API_KEY=your-deepseek-api-key-here
5
+
6
+ # News Service URL (for news aggregation with sentiment analysis)
7
+ NEWS_SERVICE_URL=http://localhost:5000
8
+
9
+ # Alpha Vantage API Key (optional, for forex data)
10
+ ALPHA_VANTAGE_KEY=your-alpha-vantage-key-here
.gitignore CHANGED
@@ -19,4 +19,6 @@ ml_models/
19
  exp_results/
20
  # Ignore png and jpg files
21
  *.png
22
- *.jpg
 
 
 
19
  exp_results/
20
  # Ignore png and jpg files
21
  *.png
22
+ *.jpg
23
+ # Ignore .ruff
24
+ .ruff_cache
README.md CHANGED
@@ -1,12 +1,177 @@
1
  ---
2
  title: FinancialPlatform
3
- emoji: πŸ‘€
4
- colorFrom: gray
5
  colorTo: green
6
- sdk: docker
 
 
7
  pinned: false
8
  license: apache-2.0
9
- short_description: OpneBB-based Financial Platform. Bloomberg-like terminal
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: FinancialPlatform
3
+ emoji: πŸ“ˆ
4
+ colorFrom: blue
5
  colorTo: green
6
+ sdk: streamlit
7
+ sdk_version: 1.30.0
8
+ app_file: app/app.py
9
  pinned: false
10
  license: apache-2.0
11
+ short_description: Multi-asset financial analysis platform with OpenBB, TradingView integration, and AI insights
12
  ---
13
 
14
+ # Financial Analysis Platform
15
+
16
+ A comprehensive multi-asset financial analysis platform built with Streamlit, providing real-time data, technical indicators, and AI-powered insights.
17
+
18
+ ## Features
19
+
20
+ ### πŸ“ˆ Stock Analysis
21
+ - Real-time stock price data from OpenBB
22
+ - Technical indicators (SMA, EMA, RSI)
23
+ - Company profile and financial statements
24
+ - Revenue and net income trends
25
+ - TradingView chart integration
26
+ - Profitability metrics analysis
27
+
28
+ ### β‚Ώ Cryptocurrency (Coming Soon)
29
+ - Real-time cryptocurrency prices
30
+ - Market cap and 24h volume
31
+ - Technical indicators for crypto assets
32
+ - TradingView crypto charts
33
+
34
+ ### πŸ’± Forex Trading (Coming Soon)
35
+ - Foreign exchange rate analysis
36
+ - Major, minor, and exotic pairs
37
+ - Pip calculator
38
+ - Economic calendar integration
39
+
40
+ ### πŸ” Market Screener (Coming Soon)
41
+ - Multi-criteria filtering
42
+ - Technical pattern recognition
43
+ - Sort by volume, price change, RSI
44
+ - Export results to CSV
45
+
46
+ ### πŸ€– News & AI Dashboard (Coming Soon)
47
+ - Real-time news aggregation
48
+ - AI-powered sentiment analysis
49
+ - Trading recommendations
50
+ - Market trend detection
51
+
52
+ ## Installation
53
+
54
+ 1. Clone the repository:
55
+ ```bash
56
+ git clone <repository-url>
57
+ cd FinancialPlatform
58
+ ```
59
+
60
+ 2. Install dependencies:
61
+ ```bash
62
+ pip install -r requirements.txt
63
+ ```
64
+
65
+ 3. Create a `.env` file based on `.env.example`:
66
+ ```bash
67
+ cp .env.example .env
68
+ ```
69
+
70
+ 4. Configure your API keys in `.env`:
71
+ ```
72
+ DEEPSEEK_API_KEY=your-key-here
73
+ NEWS_SERVICE_URL=http://localhost:5000
74
+ ALPHA_VANTAGE_KEY=your-key-here
75
+ ```
76
+
77
+ ## Usage
78
+
79
+ Run the application:
80
+ ```bash
81
+ streamlit run app/app.py
82
+ ```
83
+
84
+ The application will open in your default web browser at `http://localhost:8501`.
85
+
86
+ ## Project Structure
87
+
88
+ ```
89
+ FinancialPlatform/
90
+ β”œβ”€β”€ app/
91
+ β”‚ β”œβ”€β”€ app.py # Main landing page
92
+ β”‚ β”œβ”€β”€ pages/
93
+ β”‚ β”‚ β”œβ”€β”€ 01_Stocks.py # Stock analysis page
94
+ β”‚ β”‚ β”œβ”€β”€ 02_Crypto.py # Cryptocurrency analysis
95
+ β”‚ β”‚ β”œβ”€β”€ 03_Forex.py # Forex analysis
96
+ β”‚ β”‚ β”œβ”€β”€ 04_Screener.py # Market screener
97
+ β”‚ β”‚ └── 05_Dashboard.py # News & AI dashboard
98
+ β”‚ β”œβ”€β”€ components/
99
+ β”‚ β”‚ β”œβ”€β”€ chart.py # Chart creation utilities
100
+ β”‚ β”‚ β”œβ”€β”€ data_sources.py # Data fetching functions
101
+ β”‚ β”‚ β”œβ”€β”€ ui.py # UI component functions
102
+ β”‚ β”‚ └── styles.py # Dark theme CSS
103
+ β”‚ └── utils/
104
+ β”‚ β”œβ”€β”€ config.py # Configuration management
105
+ β”‚ └── formatters.py # Data formatting utilities
106
+ β”œβ”€β”€ requirements.txt
107
+ β”œβ”€β”€ .env.example
108
+ └── README.md
109
+ ```
110
+
111
+ ## Technology Stack
112
+
113
+ - **Frontend**: Streamlit
114
+ - **Data Sources**: OpenBB SDK, yfinance
115
+ - **Charting**: Plotly, TradingView widgets
116
+ - **AI**: DeepSeek API (planned)
117
+ - **Styling**: Custom CSS with dark theme
118
+
119
+ ## Features in Development
120
+
121
+ - [ ] Cryptocurrency data integration (Binance API)
122
+ - [ ] Forex data integration (Alpha Vantage)
123
+ - [ ] Market screener with advanced filters
124
+ - [ ] News aggregation service
125
+ - [ ] AI-powered trading insights
126
+ - [ ] Sentiment analysis
127
+ - [ ] Additional technical indicators (MACD, Bollinger Bands, ATR)
128
+
129
+ ## Configuration
130
+
131
+ ### Environment Variables
132
+
133
+ - `DEEPSEEK_API_KEY`: API key for AI-powered insights
134
+ - `NEWS_SERVICE_URL`: URL for news aggregation service
135
+ - `ALPHA_VANTAGE_KEY`: API key for forex data (optional)
136
+
137
+ ### Cache Settings
138
+
139
+ Data caching is configured in `utils/config.py`:
140
+ - Price data: 1 hour TTL
141
+ - Fundamental data: 24 hours TTL
142
+ - News data: 15 minutes TTL
143
+
144
+ ## Deployment
145
+
146
+ ### HuggingFace Spaces
147
+
148
+ This application is optimized for deployment on HuggingFace Spaces:
149
+
150
+ 1. Create a new Space on HuggingFace
151
+ 2. Set the Space type to "Streamlit"
152
+ 3. Add your environment variables in the Space settings:
153
+ - `DEEPSEEK_API_KEY`
154
+ - `NEWS_SERVICE_URL`
155
+ - `ALPHA_VANTAGE_KEY`
156
+ 4. Push your code to the Space repository
157
+
158
+ ### Local Development
159
+
160
+ For local development with hot-reload:
161
+ ```bash
162
+ streamlit run app/app.py --server.runOnSave=true
163
+ ```
164
+
165
+ ## Contributing
166
+
167
+ Contributions are welcome! Please feel free to submit a Pull Request.
168
+
169
+ ## License
170
+
171
+ Apache 2.0 License
172
+
173
+ ## Acknowledgments
174
+
175
+ - OpenBB for financial data API
176
+ - TradingView for chart widgets
177
+ - Streamlit for the amazing web framework
app/app.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Financial Analysis Dashboard - Main Application Landing Page."""
2
+
3
+ import streamlit as st
4
+ from components.styles import DARK_THEME_CSS
5
+
6
+ # ---- Configuration ----
7
+ st.set_page_config(
8
+ page_title="Financial Dashboard",
9
+ page_icon="πŸ“ˆ",
10
+ layout="wide",
11
+ initial_sidebar_state="expanded",
12
+ menu_items={
13
+ "About": "A professional financial analysis platform with multi-asset support"
14
+ }
15
+ )
16
+
17
+ # ---- Apply Dark Theme ----
18
+ st.markdown(DARK_THEME_CSS, unsafe_allow_html=True)
19
+
20
+ # ---- Header ----
21
+ st.markdown("# πŸ“ˆ Financial Analysis Platform")
22
+ st.markdown("### Professional multi-asset analysis with technical indicators, AI insights, and real-time data")
23
+
24
+ st.markdown("---")
25
+
26
+ # ---- Feature Overview ----
27
+ col1, col2, col3 = st.columns(3)
28
+
29
+ with col1:
30
+ st.markdown("""
31
+ <div style="padding: 1.5rem; background: linear-gradient(135deg, #1f2937 0%, #111827 100%); border-radius: 10px; border: 1px solid #30363d;">
32
+ <h3>πŸ“ˆ Stock Analysis</h3>
33
+ <p>Comprehensive stock analysis with technical indicators, financial metrics, and TradingView charts.</p>
34
+ <ul>
35
+ <li>Real-time price data</li>
36
+ <li>Technical indicators (SMA, EMA, RSI)</li>
37
+ <li>Financial statements</li>
38
+ <li>Company profiles</li>
39
+ </ul>
40
+ </div>
41
+ """, unsafe_allow_html=True)
42
+
43
+ with col2:
44
+ st.markdown("""
45
+ <div style="padding: 1.5rem; background: linear-gradient(135deg, #1f2937 0%, #111827 100%); border-radius: 10px; border: 1px solid #30363d;">
46
+ <h3>β‚Ώ Cryptocurrency</h3>
47
+ <p>Track and analyze major cryptocurrencies with real-time market data.</p>
48
+ <ul>
49
+ <li>BTC, ETH, and major altcoins</li>
50
+ <li>24h volume & market cap</li>
51
+ <li>Price charts & indicators</li>
52
+ <li>Market sentiment</li>
53
+ </ul>
54
+ </div>
55
+ """, unsafe_allow_html=True)
56
+
57
+ with col3:
58
+ st.markdown("""
59
+ <div style="padding: 1.5rem; background: linear-gradient(135deg, #1f2937 0%, #111827 100%); border-radius: 10px; border: 1px solid #30363d;">
60
+ <h3>πŸ’± Forex Trading</h3>
61
+ <p>Foreign exchange analysis for major, minor, and exotic currency pairs.</p>
62
+ <ul>
63
+ <li>Major pairs (EUR/USD, GBP/USD)</li>
64
+ <li>Real-time exchange rates</li>
65
+ <li>Technical analysis</li>
66
+ <li>Pip calculator</li>
67
+ </ul>
68
+ </div>
69
+ """, unsafe_allow_html=True)
70
+
71
+ st.markdown("<br>", unsafe_allow_html=True)
72
+
73
+ col4, col5 = st.columns(2)
74
+
75
+ with col4:
76
+ st.markdown("""
77
+ <div style="padding: 1.5rem; background: linear-gradient(135deg, #1f2937 0%, #111827 100%); border-radius: 10px; border: 1px solid #30363d;">
78
+ <h3>πŸ” Market Screener</h3>
79
+ <p>Advanced screening tools to find investment opportunities across markets.</p>
80
+ <ul>
81
+ <li>Multi-criteria filtering</li>
82
+ <li>Technical pattern recognition</li>
83
+ <li>Sort by volume, price change, RSI</li>
84
+ <li>Export results to CSV</li>
85
+ </ul>
86
+ </div>
87
+ """, unsafe_allow_html=True)
88
+
89
+ with col5:
90
+ st.markdown("""
91
+ <div style="padding: 1.5rem; background: linear-gradient(135deg, #1f2937 0%, #111827 100%); border-radius: 10px; border: 1px solid #30363d;">
92
+ <h3>πŸ€– News & AI Dashboard</h3>
93
+ <p>AI-powered market insights with sentiment analysis and trading recommendations.</p>
94
+ <ul>
95
+ <li>Real-time news aggregation</li>
96
+ <li>Sentiment analysis</li>
97
+ <li>AI trading insights</li>
98
+ <li>Market trend detection</li>
99
+ </ul>
100
+ </div>
101
+ """, unsafe_allow_html=True)
102
+
103
+ st.markdown("---")
104
+
105
+ # ---- Quick Start ----
106
+ st.markdown("## πŸš€ Quick Start")
107
+ st.markdown("Use the sidebar to navigate to different sections:")
108
+
109
+ quick_col1, quick_col2, quick_col3 = st.columns(3)
110
+
111
+ with quick_col1:
112
+ if st.button("πŸ“ˆ Stock Analysis", use_container_width=True):
113
+ st.switch_page("pages/01_Stocks.py")
114
+
115
+ with quick_col2:
116
+ if st.button("β‚Ώ Cryptocurrency", use_container_width=True):
117
+ st.info("Coming soon!")
118
+
119
+ with quick_col3:
120
+ if st.button("πŸ’± Forex Trading", use_container_width=True):
121
+ st.info("Coming soon!")
122
+
123
+ st.markdown("<br>", unsafe_allow_html=True)
124
+
125
+ quick_col4, quick_col5 = st.columns(2)
126
+
127
+ with quick_col4:
128
+ if st.button("πŸ” Market Screener", use_container_width=True):
129
+ st.info("Coming soon!")
130
+
131
+ with quick_col5:
132
+ if st.button("πŸ€– News & AI Dashboard", use_container_width=True):
133
+ st.info("Coming soon!")
134
+
135
+ st.markdown("---")
136
+
137
+ # ---- Sidebar ----
138
+ with st.sidebar:
139
+ st.markdown("## πŸ“‹ Navigation")
140
+ st.info("Select a page from the sidebar to get started.")
141
+
142
+ st.markdown("---")
143
+ st.markdown("## ℹ️ About")
144
+ st.markdown("""
145
+ This platform provides comprehensive financial analysis across multiple asset classes:
146
+
147
+ - **Stocks**: Technical & fundamental analysis
148
+ - **Crypto**: Real-time cryptocurrency tracking
149
+ - **Forex**: Currency pair analysis
150
+ - **Screener**: Find investment opportunities
151
+ - **Dashboard**: AI-powered insights
152
+ """)
153
+
154
+ st.markdown("---")
155
+ st.markdown("### πŸ”§ Features")
156
+ st.markdown("""
157
+ - βœ… Real-time data
158
+ - βœ… Technical indicators
159
+ - βœ… TradingView integration
160
+ - βœ… Dark theme UI
161
+ - βœ… AI-powered insights
162
+ - βœ… News sentiment analysis
163
+ """)
app/components/chart.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Chart creation utilities for the financial dashboard."""
2
+
3
+ import plotly.graph_objects as go
4
+ import pandas as pd
5
+
6
+
7
+ def get_dark_theme_layout():
8
+ """Get common dark theme layout settings for all charts."""
9
+ return dict(
10
+ plot_bgcolor="#0d1117",
11
+ paper_bgcolor="#0e1117",
12
+ font=dict(color="#e6edf3", size=12, family="Arial, sans-serif"),
13
+ xaxis=dict(
14
+ gridcolor="#30363d",
15
+ showgrid=True,
16
+ zeroline=False,
17
+ color="#8b949e"
18
+ ),
19
+ yaxis=dict(
20
+ gridcolor="#30363d",
21
+ showgrid=True,
22
+ zeroline=False,
23
+ color="#8b949e"
24
+ ),
25
+ legend=dict(
26
+ bgcolor="rgba(13, 17, 23, 0.8)",
27
+ bordercolor="#30363d",
28
+ borderwidth=1,
29
+ font=dict(color="#e6edf3")
30
+ ),
31
+ hoverlabel=dict(
32
+ bgcolor="#0d1117",
33
+ bordercolor="#30363d",
34
+ font=dict(color="#e6edf3")
35
+ )
36
+ )
37
+
38
+
39
+ def create_price_chart(df: pd.DataFrame, symbol: str, period: int) -> go.Figure:
40
+ """Create price chart with SMA and EMA indicators."""
41
+ fig = go.Figure()
42
+
43
+ fig.add_trace(go.Scatter(
44
+ x=df.index, y=df["close"],
45
+ name="Close Price",
46
+ line=dict(color="#0066ff", width=2.5)
47
+ ))
48
+ fig.add_trace(go.Scatter(
49
+ x=df.index, y=df["SMA"],
50
+ name=f"SMA {period}",
51
+ line=dict(color="#00d084", width=2, dash="dash")
52
+ ))
53
+ fig.add_trace(go.Scatter(
54
+ x=df.index, y=df["EMA"],
55
+ name=f"EMA {period}",
56
+ line=dict(color="#ffa500", width=2, dash="dot")
57
+ ))
58
+
59
+ layout = get_dark_theme_layout()
60
+ fig.update_layout(
61
+ title=f"{symbol} - Price with Moving Averages",
62
+ xaxis_title="Date",
63
+ yaxis_title="Price ($)",
64
+ hovermode="x unified",
65
+ template="plotly_dark",
66
+ height=500,
67
+ margin=dict(l=0, r=0, t=40, b=0),
68
+ **layout
69
+ )
70
+
71
+ return fig
72
+
73
+
74
+ def create_rsi_chart(df: pd.DataFrame, symbol: str) -> go.Figure:
75
+ """Create RSI (Relative Strength Index) chart."""
76
+ fig = go.Figure()
77
+
78
+ fig.add_trace(go.Scatter(
79
+ x=df.index, y=df["RSI"],
80
+ name="RSI",
81
+ line=dict(color="#ff3838", width=2.5),
82
+ fill="tozeroy",
83
+ fillcolor="rgba(255, 56, 56, 0.15)"
84
+ ))
85
+
86
+ fig.add_hline(y=70, line_dash="dash", line_color="rgba(255, 165, 0, 0.6)",
87
+ annotation_text="Overbought (70)")
88
+ fig.add_hline(y=30, line_dash="dash", line_color="rgba(0, 208, 132, 0.6)",
89
+ annotation_text="Oversold (30)")
90
+ fig.add_hline(y=50, line_dash="dot", line_color="rgba(139, 148, 158, 0.3)")
91
+
92
+ layout = get_dark_theme_layout()
93
+ layout["yaxis"]["range"] = [0, 100]
94
+
95
+ fig.update_layout(
96
+ title=f"{symbol} - Relative Strength Index (RSI)",
97
+ xaxis_title="Date",
98
+ yaxis_title="RSI",
99
+ hovermode="x unified",
100
+ template="plotly_dark",
101
+ height=500,
102
+ margin=dict(l=0, r=0, t=40, b=0),
103
+ **layout
104
+ )
105
+
106
+ return fig
107
+
108
+
109
+ def create_financial_chart(income_data: pd.DataFrame) -> go.Figure:
110
+ """Create financial revenue and net income chart."""
111
+ fig = go.Figure()
112
+
113
+ fig.add_trace(go.Bar(
114
+ x=income_data['period_ending'],
115
+ y=income_data['total_revenue'],
116
+ name="Total Revenue",
117
+ marker=dict(color='#0066ff', opacity=0.9),
118
+ yaxis='y1'
119
+ ))
120
+
121
+ fig.add_trace(go.Bar(
122
+ x=income_data['period_ending'],
123
+ y=income_data['net_income'],
124
+ name="Net Income",
125
+ marker=dict(color='#00d084', opacity=0.9),
126
+ yaxis='y1'
127
+ ))
128
+
129
+ layout = get_dark_theme_layout()
130
+ fig.update_layout(
131
+ title="Revenue & Net Income (Annual)",
132
+ xaxis_title="Period",
133
+ yaxis_title="Amount ($)",
134
+ hovermode="x unified",
135
+ template="plotly_dark",
136
+ height=400,
137
+ barmode='group',
138
+ margin=dict(l=0, r=0, t=40, b=0),
139
+ **layout
140
+ )
141
+
142
+ return fig
app/components/data_sources.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Data fetching and processing utilities for the financial dashboard."""
2
+
3
+ import pandas as pd
4
+ from openbb import sdk
5
+ import streamlit as st
6
+
7
+
8
+ @st.cache_data(ttl=3600)
9
+ def load_stock_data(symbol: str) -> pd.DataFrame:
10
+ """Load historical stock price data with caching."""
11
+ df = sdk.equity.price.historical(symbol=symbol).to_dataframe()
12
+ return df
13
+
14
+
15
+ @st.cache_data(ttl=86400)
16
+ def load_company_profile(symbol: str):
17
+ """Load company profile information with caching."""
18
+ profile_response = sdk.equity.profile(symbol=symbol)
19
+ profile_info = profile_response.results[0] if hasattr(profile_response, 'results') and profile_response.results else None
20
+ return profile_info
21
+
22
+
23
+ @st.cache_data(ttl=86400)
24
+ def load_income_statement(symbol: str) -> pd.DataFrame:
25
+ """Load company income statement data with caching."""
26
+ income_stmt = sdk.equity.fundamental.income(symbol=symbol).to_dataframe()
27
+ return income_stmt
28
+
29
+
30
+ def calculate_technical_indicators(df: pd.DataFrame, period: int) -> pd.DataFrame:
31
+ """Calculate SMA, EMA, and RSI indicators."""
32
+ df = df.copy()
33
+ df["SMA"] = df["close"].rolling(period).mean()
34
+ df["EMA"] = df["close"].ewm(span=period, adjust=False).mean()
35
+
36
+ # Calculate RSI
37
+ delta = df["close"].diff()
38
+ gain = delta.clip(lower=0)
39
+ loss = -1 * delta.clip(upper=0)
40
+ avg_gain = gain.rolling(period).mean()
41
+ avg_loss = loss.rolling(period).mean()
42
+ rs = avg_gain / avg_loss
43
+ df["RSI"] = 100 - (100 / (1 + rs))
44
+
45
+ return df
46
+
47
+
48
+ def get_price_metrics(df: pd.DataFrame) -> dict:
49
+ """Calculate key price metrics."""
50
+ current_price = df["close"].iloc[-1]
51
+ prev_close = df["close"].iloc[-2] if len(df) > 1 else df["close"].iloc[0]
52
+ price_change = current_price - prev_close
53
+ price_change_pct = (price_change / prev_close) * 100 if prev_close != 0 else 0
54
+
55
+ return {
56
+ "current_price": current_price,
57
+ "price_change": price_change,
58
+ "price_change_pct": price_change_pct,
59
+ "high_52w": df['high'].max(),
60
+ "low_52w": df['low'].min(),
61
+ }
62
+
63
+
64
+ def get_profitability_metrics(income_data: pd.Series) -> dict:
65
+ """Calculate profitability metrics from income statement."""
66
+ total_rev = income_data.get('total_revenue', 0)
67
+ gross_prof = income_data.get('gross_profit', 0)
68
+ net_inc = income_data.get('net_income', 0)
69
+ operating_inc = income_data.get('operating_income', 0)
70
+
71
+ metrics = {}
72
+
73
+ if total_rev and total_rev > 0:
74
+ metrics["gross_margin"] = (gross_prof / total_rev) * 100 if pd.notna(gross_prof) else 0
75
+ metrics["net_margin"] = (net_inc / total_rev) * 100 if pd.notna(net_inc) else 0
76
+ if operating_inc:
77
+ metrics["operating_margin"] = (operating_inc / total_rev) * 100
78
+ else:
79
+ metrics = {"gross_margin": 0, "net_margin": 0}
80
+
81
+ return metrics
app/components/styles.py ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Dark theme CSS styles for the financial dashboard."""
2
+
3
+ DARK_THEME_CSS = """
4
+ <style>
5
+ :root {
6
+ --primary-color: #0066ff;
7
+ --secondary-color: #1f77e2;
8
+ --success-color: #00d084;
9
+ --danger-color: #ff3838;
10
+ --warning-color: #ffa500;
11
+ --bg-dark: #0e1117;
12
+ --bg-darker: #010409;
13
+ --text-primary: #e6edf3;
14
+ --text-secondary: #8b949e;
15
+ --border-color: #30363d;
16
+ }
17
+
18
+ /* Main background */
19
+ html, body {
20
+ background-color: var(--bg-darker) !important;
21
+ color: var(--text-primary) !important;
22
+ margin: 0 !important;
23
+ padding: 0 !important;
24
+ }
25
+
26
+ /* Streamlit containers */
27
+ .main, [data-testid="stAppViewContainer"] {
28
+ background-color: var(--bg-dark) !important;
29
+ }
30
+
31
+ /* Hide header and footer */
32
+ [data-testid="stHeader"] {
33
+ background-color: var(--bg-dark) !important;
34
+ }
35
+
36
+ [data-testid="stToolbar"] {
37
+ background-color: var(--bg-dark) !important;
38
+ }
39
+
40
+ .stApp {
41
+ background-color: var(--bg-dark) !important;
42
+ }
43
+
44
+ [data-testid="stDecoration"] {
45
+ background-color: var(--bg-dark) !important;
46
+ }
47
+
48
+ [data-testid="stSidebar"] {
49
+ background-color: #0d1117 !important;
50
+ border-right: 1px solid var(--border-color);
51
+ }
52
+
53
+ /* Text colors */
54
+ p, span, div, h1, h2, h3, h4, h5, h6, label, li, a {
55
+ color: var(--text-primary) !important;
56
+ }
57
+
58
+ /* Headings */
59
+ h1, h2, h3 {
60
+ color: var(--text-primary) !important;
61
+ font-weight: 700 !important;
62
+ }
63
+
64
+ /* Links */
65
+ a {
66
+ color: var(--primary-color) !important;
67
+ text-decoration: none !important;
68
+ }
69
+
70
+ a:hover {
71
+ color: var(--secondary-color) !important;
72
+ text-decoration: underline !important;
73
+ }
74
+
75
+ /* Labels and text inputs */
76
+ label {
77
+ color: var(--text-primary) !important;
78
+ font-weight: 500 !important;
79
+ }
80
+
81
+ /* Paragraph text */
82
+ p {
83
+ color: var(--text-primary) !important;
84
+ line-height: 1.6 !important;
85
+ }
86
+
87
+ /* Metric card styling */
88
+ [data-testid="metric-container"] {
89
+ background: linear-gradient(135deg, #1f2937 0%, #111827 100%) !important;
90
+ border: 1px solid var(--border-color) !important;
91
+ border-radius: 10px !important;
92
+ padding: 1.5rem !important;
93
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3) !important;
94
+ }
95
+
96
+ .metric-card {
97
+ background: linear-gradient(135deg, #1f2937 0%, #111827 100%);
98
+ padding: 1.5rem;
99
+ border-radius: 10px;
100
+ border: 1px solid var(--border-color);
101
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
102
+ }
103
+
104
+ .metric-value {
105
+ font-size: 2.5rem;
106
+ font-weight: 700;
107
+ color: var(--primary-color);
108
+ margin: 0.5rem 0;
109
+ }
110
+
111
+ .metric-label {
112
+ font-size: 0.875rem;
113
+ color: var(--text-secondary);
114
+ text-transform: uppercase;
115
+ letter-spacing: 0.05em;
116
+ }
117
+
118
+ .section-title {
119
+ color: var(--text-primary);
120
+ border-bottom: 2px solid var(--primary-color);
121
+ padding-bottom: 1rem;
122
+ margin-top: 2rem;
123
+ margin-bottom: 1.5rem;
124
+ }
125
+
126
+ /* Button styling */
127
+ .stButton > button {
128
+ background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%) !important;
129
+ color: #ffffff !important;
130
+ border: none !important;
131
+ border-radius: 8px !important;
132
+ padding: 0.75rem 2rem !important;
133
+ font-weight: 700 !important;
134
+ transition: all 0.3s ease !important;
135
+ box-shadow: 0 4px 6px rgba(0, 102, 255, 0.2) !important;
136
+ }
137
+
138
+ .stButton > button:hover {
139
+ box-shadow: 0 8px 16px rgba(0, 102, 255, 0.4) !important;
140
+ transform: translateY(-2px) !important;
141
+ }
142
+
143
+ .stButton > button:active {
144
+ transform: translateY(0) !important;
145
+ }
146
+
147
+ /* Input fields */
148
+ [data-testid="stTextInput"] input,
149
+ [data-testid="stSlider"] input {
150
+ background-color: #161b22 !important;
151
+ border: 1px solid var(--border-color) !important;
152
+ color: var(--text-primary) !important;
153
+ border-radius: 6px !important;
154
+ }
155
+
156
+ [data-testid="stTextInput"] input::placeholder {
157
+ color: var(--text-secondary) !important;
158
+ }
159
+
160
+ /* Slider */
161
+ [data-testid="stSlider"] {
162
+ color: var(--primary-color) !important;
163
+ }
164
+
165
+ /* Tabs */
166
+ [data-testid="stTabs"] [role="tablist"] {
167
+ background-color: transparent !important;
168
+ border-bottom: 2px solid var(--border-color) !important;
169
+ }
170
+
171
+ [data-testid="stTabs"] [role="tab"] {
172
+ color: var(--text-secondary) !important;
173
+ background-color: transparent !important;
174
+ border: none !important;
175
+ padding: 1rem 1.5rem !important;
176
+ }
177
+
178
+ [data-testid="stTabs"] [role="tab"][aria-selected="true"] {
179
+ color: var(--primary-color) !important;
180
+ border-bottom: 3px solid var(--primary-color) !important;
181
+ }
182
+
183
+ /* Dataframe */
184
+ [data-testid="dataframe"] {
185
+ background-color: #0d1117 !important;
186
+ }
187
+
188
+ .dataframe {
189
+ background-color: #0d1117 !important;
190
+ color: var(--text-primary) !important;
191
+ }
192
+
193
+ /* Info/Error boxes */
194
+ [data-testid="stInfo"],
195
+ [data-testid="stSuccess"],
196
+ [data-testid="stWarning"],
197
+ [data-testid="stError"] {
198
+ background-color: rgba(0, 102, 255, 0.1) !important;
199
+ border-left: 4px solid var(--primary-color) !important;
200
+ border-radius: 6px !important;
201
+ }
202
+
203
+ [data-testid="stError"] {
204
+ background-color: rgba(255, 56, 56, 0.1) !important;
205
+ border-left-color: var(--danger-color) !important;
206
+ }
207
+
208
+ /* Markdown */
209
+ [data-testid="stMarkdown"] {
210
+ color: var(--text-primary) !important;
211
+ }
212
+
213
+ /* Expander */
214
+ [data-testid="stExpander"] {
215
+ background-color: #161b22 !important;
216
+ border: 1px solid var(--border-color) !important;
217
+ border-radius: 6px !important;
218
+ }
219
+
220
+ /* Metric text styling */
221
+ [data-testid="metric-container"] p {
222
+ color: var(--text-primary) !important;
223
+ }
224
+
225
+ [data-testid="metric-container"] [data-testid="stMetricValue"] {
226
+ color: var(--primary-color) !important;
227
+ font-weight: 700 !important;
228
+ }
229
+
230
+ /* Slider label color */
231
+ [data-testid="stSlider"] label {
232
+ color: var(--text-primary) !important;
233
+ }
234
+
235
+ /* Text input label */
236
+ [data-testid="stTextInput"] label {
237
+ color: var(--text-primary) !important;
238
+ }
239
+
240
+ /* Write and markdown text */
241
+ [data-testid="stMarkdownContainer"] p {
242
+ color: var(--text-primary) !important;
243
+ }
244
+
245
+ [data-testid="stMarkdownContainer"] strong {
246
+ color: var(--primary-color) !important;
247
+ font-weight: 600 !important;
248
+ }
249
+
250
+ /* Spinner text */
251
+ [data-testid="stSpinner"] {
252
+ color: var(--primary-color) !important;
253
+ }
254
+
255
+ /* Column separators */
256
+ hr {
257
+ border-color: var(--border-color) !important;
258
+ }
259
+
260
+ /* Scrollbar */
261
+ ::-webkit-scrollbar {
262
+ width: 8px;
263
+ height: 8px;
264
+ }
265
+
266
+ ::-webkit-scrollbar-track {
267
+ background: #0d1117;
268
+ }
269
+
270
+ ::-webkit-scrollbar-thumb {
271
+ background: var(--border-color);
272
+ border-radius: 4px;
273
+ }
274
+
275
+ ::-webkit-scrollbar-thumb:hover {
276
+ background: var(--primary-color);
277
+ }
278
+
279
+ /* Selection highlighting */
280
+ ::selection {
281
+ background-color: var(--primary-color);
282
+ color: #fff;
283
+ }
284
+
285
+ /* Fix all white backgrounds */
286
+ .stApp > header {
287
+ background-color: var(--bg-dark) !important;
288
+ }
289
+
290
+ .stApp > header::before {
291
+ background: none !important;
292
+ }
293
+
294
+ .stApp > header::after {
295
+ background: none !important;
296
+ }
297
+
298
+ /* Streamlit elements background */
299
+ [data-testid="stVerticalBlock"] {
300
+ background-color: transparent !important;
301
+ }
302
+
303
+ [data-testid="stVerticalBlockBorderWrapper"] {
304
+ background-color: transparent !important;
305
+ }
306
+
307
+ /* Remove white decorative elements */
308
+ .st-emotion-cache-1gvbgyg {
309
+ background-color: var(--bg-dark) !important;
310
+ }
311
+
312
+ .st-emotion-cache-1jicfl2 {
313
+ background-color: var(--bg-dark) !important;
314
+ }
315
+
316
+ /* Ensure all root divs are dark */
317
+ div[class*="st-"] {
318
+ background-color: transparent !important;
319
+ }
320
+
321
+ /* Modal and overlay backgrounds */
322
+ .stModal {
323
+ background-color: var(--bg-dark) !important;
324
+ }
325
+
326
+ /* Alert boxes background */
327
+ .stAlert {
328
+ background-color: rgba(0, 102, 255, 0.1) !important;
329
+ }
330
+ </style>
331
+ """
app/components/ui.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """UI component functions for the financial dashboard."""
2
+
3
+ import streamlit as st
4
+ import pandas as pd
5
+ import sys
6
+ import os
7
+
8
+ # Add parent directory to path for imports
9
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
10
+
11
+ from utils.formatters import format_financial_value
12
+ from components.data_sources import get_profitability_metrics
13
+
14
+
15
+ def display_price_metrics(metrics: dict):
16
+ """Display key price metrics in columns."""
17
+ st.markdown('<div class="section-title">πŸ“Š Price Metrics</div>', unsafe_allow_html=True)
18
+
19
+ col1, col2, col3, col4 = st.columns(4)
20
+
21
+ with col1:
22
+ st.metric("Current Price", f"${metrics['current_price']:.2f}",
23
+ f"{metrics['price_change']:+.2f}", delta_color="normal")
24
+
25
+ with col2:
26
+ st.metric("Day Change %", f"{metrics['price_change_pct']:+.2f}%",
27
+ None, delta_color="normal")
28
+
29
+ with col3:
30
+ st.metric("52W High", f"${metrics['high_52w']:.2f}")
31
+
32
+ with col4:
33
+ st.metric("52W Low", f"${metrics['low_52w']:.2f}")
34
+
35
+
36
+ def display_company_info(profile_info):
37
+ """Display company information."""
38
+ st.markdown('<div class="section-title">πŸ“‹ Company Information</div>', unsafe_allow_html=True)
39
+
40
+ if profile_info:
41
+ info_col1, info_col2 = st.columns(2)
42
+ with info_col1:
43
+ st.write(f"**Company Name:** {getattr(profile_info, 'name', 'N/A')}")
44
+ st.write(f"**Sector:** {getattr(profile_info, 'sector', 'N/A')}")
45
+ st.write(f"**Industry:** {getattr(profile_info, 'industry', 'N/A')}")
46
+
47
+ with info_col2:
48
+ st.write(f"**Country:** {getattr(profile_info, 'country', 'N/A')}")
49
+ st.write(f"**Exchange:** {getattr(profile_info, 'exchange', 'N/A')}")
50
+ st.write(f"**Website:** {getattr(profile_info, 'website', 'N/A')}")
51
+
52
+
53
+ def display_financial_metrics(income_stmt: pd.DataFrame):
54
+ """Display financial metrics from income statement."""
55
+ st.markdown('<div class="section-title">πŸ’° Financial Metrics</div>', unsafe_allow_html=True)
56
+
57
+ latest_income = income_stmt.iloc[0] if len(income_stmt) > 0 else None
58
+
59
+ if latest_income is not None:
60
+ # First row of metrics
61
+ fin_col1, fin_col2, fin_col3, fin_col4 = st.columns(4)
62
+
63
+ with fin_col1:
64
+ revenue = latest_income.get('total_revenue', 0)
65
+ if pd.notna(revenue) and revenue > 0:
66
+ st.metric("Total Revenue", format_financial_value(revenue))
67
+ else:
68
+ st.metric("Total Revenue", "N/A")
69
+
70
+ with fin_col2:
71
+ net_income = latest_income.get('net_income', 0)
72
+ if pd.notna(net_income) and net_income > 0:
73
+ st.metric("Net Income", format_financial_value(net_income))
74
+ else:
75
+ st.metric("Net Income", "N/A")
76
+
77
+ with fin_col3:
78
+ gross_profit = latest_income.get('gross_profit', 0)
79
+ if pd.notna(gross_profit) and gross_profit > 0:
80
+ st.metric("Gross Profit", format_financial_value(gross_profit))
81
+ else:
82
+ st.metric("Gross Profit", "N/A")
83
+
84
+ with fin_col4:
85
+ operating_income = latest_income.get('operating_income', 0)
86
+ if pd.notna(operating_income) and operating_income > 0:
87
+ st.metric("Operating Income", format_financial_value(operating_income))
88
+ else:
89
+ st.metric("Operating Income", "N/A")
90
+
91
+ # Second row of metrics
92
+ fin_col5, fin_col6, fin_col7, fin_col8 = st.columns(4)
93
+
94
+ with fin_col5:
95
+ eps = latest_income.get('diluted_earnings_per_share', 0)
96
+ if pd.notna(eps):
97
+ st.metric("EPS (Diluted)", f"${eps:.2f}")
98
+ else:
99
+ st.metric("EPS (Diluted)", "N/A")
100
+
101
+ with fin_col6:
102
+ ebitda = latest_income.get('ebitda', 0)
103
+ if pd.notna(ebitda) and ebitda > 0:
104
+ st.metric("EBITDA", format_financial_value(ebitda))
105
+ else:
106
+ st.metric("EBITDA", "N/A")
107
+
108
+ with fin_col7:
109
+ cogs = latest_income.get('cost_of_revenue', 0)
110
+ if pd.notna(cogs) and cogs > 0:
111
+ st.metric("Cost of Revenue", format_financial_value(cogs))
112
+ else:
113
+ st.metric("Cost of Revenue", "N/A")
114
+
115
+ with fin_col8:
116
+ rd_expense = latest_income.get('research_and_development_expense', 0)
117
+ if pd.notna(rd_expense) and rd_expense > 0:
118
+ st.metric("R&D Expense", format_financial_value(rd_expense))
119
+ else:
120
+ st.metric("R&D Expense", "N/A")
121
+
122
+
123
+ def display_income_statement(income_stmt: pd.DataFrame):
124
+ """Display formatted income statement table."""
125
+ st.markdown("### Income Statement")
126
+
127
+ if not income_stmt.empty:
128
+ display_columns = [
129
+ 'period_ending',
130
+ 'total_revenue',
131
+ 'cost_of_revenue',
132
+ 'gross_profit',
133
+ 'operating_income',
134
+ 'net_income',
135
+ 'diluted_earnings_per_share',
136
+ 'ebitda'
137
+ ]
138
+
139
+ available_cols = [col for col in display_columns if col in income_stmt.columns]
140
+ financial_display = income_stmt[available_cols].copy()
141
+
142
+ for col in financial_display.columns:
143
+ if col != 'period_ending':
144
+ financial_display[col] = financial_display[col].apply(
145
+ lambda x: format_financial_value(x)
146
+ )
147
+
148
+ st.dataframe(financial_display, use_container_width=True, hide_index=True)
149
+
150
+
151
+ def display_profitability_metrics(income_stmt: pd.DataFrame):
152
+ """Display profitability metrics."""
153
+ st.markdown("### Profitability Metrics")
154
+
155
+ prof_col1, prof_col2 = st.columns(2)
156
+ latest_data = income_stmt.iloc[0]
157
+ metrics = get_profitability_metrics(latest_data)
158
+
159
+ with prof_col1:
160
+ if "gross_margin" in metrics:
161
+ st.metric("Gross Margin", f"{metrics['gross_margin']:.2f}%")
162
+ if "net_margin" in metrics:
163
+ st.metric("Net Profit Margin", f"{metrics['net_margin']:.2f}%")
164
+
165
+ with prof_col2:
166
+ if "operating_margin" in metrics:
167
+ st.metric("Operating Margin", f"{metrics['operating_margin']:.2f}%")
168
+
169
+ if len(income_stmt) > 1:
170
+ prev_revenue = income_stmt.iloc[1].get('total_revenue', 0)
171
+ total_rev = latest_data.get('total_revenue', 0)
172
+ if prev_revenue and prev_revenue > 0:
173
+ revenue_growth = ((total_rev - prev_revenue) / prev_revenue) * 100
174
+ st.metric("Revenue Growth (YoY)", f"{revenue_growth:+.2f}%")
app/pages/01_Stocks.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Stock Analysis Page - Comprehensive stock analysis with technical indicators."""
2
+
3
+ import streamlit as st
4
+ import sys
5
+ import os
6
+
7
+ # Add parent directory to path for imports
8
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9
+
10
+ from components.styles import DARK_THEME_CSS
11
+ from components.data_sources import (
12
+ load_stock_data,
13
+ load_company_profile,
14
+ load_income_statement,
15
+ calculate_technical_indicators,
16
+ get_price_metrics,
17
+ )
18
+ from components.chart import (
19
+ create_price_chart,
20
+ create_rsi_chart,
21
+ create_financial_chart,
22
+ )
23
+ from components.ui import (
24
+ display_price_metrics,
25
+ display_company_info,
26
+ display_financial_metrics,
27
+ display_income_statement,
28
+ display_profitability_metrics,
29
+ )
30
+
31
+
32
+ # ---- Page Configuration ----
33
+ st.set_page_config(
34
+ page_title="Stocks - Financial Dashboard",
35
+ page_icon="πŸ“ˆ",
36
+ layout="wide",
37
+ initial_sidebar_state="expanded",
38
+ )
39
+
40
+ # ---- Apply Dark Theme ----
41
+ st.markdown(DARK_THEME_CSS, unsafe_allow_html=True)
42
+
43
+ # ---- Header ----
44
+ st.markdown("# πŸ“ˆ Stock Analysis")
45
+ st.markdown("Real-time technical analysis with comprehensive financial metrics")
46
+
47
+ # ---- Sidebar Configuration ----
48
+ with st.sidebar:
49
+ st.markdown("## βš™οΈ Settings")
50
+ symbol = st.text_input("Stock Ticker", "AAPL", help="Enter a valid stock ticker symbol").upper()
51
+ period = st.slider("Indicator Period", 5, 50, 20, help="Period for SMA, EMA, and RSI calculations")
52
+
53
+ st.markdown("---")
54
+ st.markdown("### About")
55
+ st.info("Analyze stocks with technical indicators, financials, and TradingView charts.")
56
+
57
+
58
+ def main():
59
+ """Main stock analysis logic."""
60
+ if st.button("πŸ“Š Load Stock Data", key="load_btn", use_container_width=True):
61
+ try:
62
+ # Load data
63
+ with st.spinner("Loading data..."):
64
+ df = load_stock_data(symbol)
65
+ profile_info = load_company_profile(symbol)
66
+ income_stmt = load_income_statement(symbol)
67
+
68
+ # Calculate technical indicators
69
+ df = calculate_technical_indicators(df, period)
70
+
71
+ # Display price metrics
72
+ metrics = get_price_metrics(df)
73
+ display_price_metrics(metrics)
74
+
75
+ # Display company information
76
+ display_company_info(profile_info)
77
+
78
+ # Display financial metrics
79
+ if not income_stmt.empty:
80
+ display_financial_metrics(income_stmt)
81
+
82
+ # Financial history chart
83
+ st.markdown('<div class="section-title">πŸ“Š Revenue & Net Income Trend</div>', unsafe_allow_html=True)
84
+ income_chart_data = income_stmt[['period_ending', 'total_revenue', 'net_income']].dropna()
85
+
86
+ if len(income_chart_data) > 0:
87
+ fig_financial = create_financial_chart(income_chart_data)
88
+ st.plotly_chart(fig_financial, use_container_width=True)
89
+
90
+ # ---- Tabs ----
91
+ tab1, tab2, tab3, tab4 = st.tabs([
92
+ "πŸ“ˆ Price & Moving Averages",
93
+ "πŸ“Š RSI Indicator",
94
+ "πŸ“‰ TradingView",
95
+ "πŸ“‹ Financials"
96
+ ])
97
+
98
+ # Tab 1: Price & Moving Averages
99
+ with tab1:
100
+ fig_price = create_price_chart(df, symbol, period)
101
+ st.plotly_chart(fig_price, use_container_width=True)
102
+
103
+ # Tab 2: RSI Indicator
104
+ with tab2:
105
+ fig_rsi = create_rsi_chart(df, symbol)
106
+ st.plotly_chart(fig_rsi, use_container_width=True)
107
+
108
+ # Tab 3: TradingView
109
+ with tab3:
110
+ tradingview_html = f"""
111
+ <div class="tradingview-widget-container">
112
+ <div id="tradingview_{symbol}"></div>
113
+ <script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
114
+ <script type="text/javascript">
115
+ new TradingView.widget({{
116
+ "width": "100%",
117
+ "height": 600,
118
+ "symbol": "{symbol}",
119
+ "interval": "D",
120
+ "timezone": "Etc/UTC",
121
+ "theme": "dark",
122
+ "style": "1",
123
+ "locale": "en",
124
+ "enable_publishing": false,
125
+ "allow_symbol_change": true,
126
+ "container_id": "tradingview_{symbol}"
127
+ }});
128
+ </script>
129
+ </div>
130
+ """
131
+ st.components.v1.html(tradingview_html, height=650)
132
+
133
+ # Tab 4: Detailed Financials
134
+ with tab4:
135
+ if not income_stmt.empty:
136
+ display_income_statement(income_stmt)
137
+ display_profitability_metrics(income_stmt)
138
+
139
+ except Exception as e:
140
+ st.error(f"Error loading data for {symbol}: {str(e)}")
141
+ st.info("Please check the ticker symbol and try again.")
142
+
143
+
144
+ if __name__ == "__main__":
145
+ main()
app/pages/02_Crypto.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Cryptocurrency Analysis Page - Track and analyze cryptocurrencies."""
2
+
3
+ import streamlit as st
4
+ import sys
5
+ import os
6
+
7
+ # Add parent directory to path for imports
8
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9
+
10
+ from components.styles import DARK_THEME_CSS
11
+
12
+
13
+ # ---- Page Configuration ----
14
+ st.set_page_config(
15
+ page_title="Crypto - Financial Dashboard",
16
+ page_icon="β‚Ώ",
17
+ layout="wide",
18
+ initial_sidebar_state="expanded",
19
+ )
20
+
21
+ # ---- Apply Dark Theme ----
22
+ st.markdown(DARK_THEME_CSS, unsafe_allow_html=True)
23
+
24
+ # ---- Header ----
25
+ st.markdown("# β‚Ώ Cryptocurrency Analysis")
26
+ st.markdown("Track and analyze major cryptocurrencies with real-time market data")
27
+
28
+ st.markdown("---")
29
+
30
+ # ---- Sidebar Configuration ----
31
+ with st.sidebar:
32
+ st.markdown("## βš™οΈ Settings")
33
+ crypto_symbol = st.selectbox(
34
+ "Cryptocurrency",
35
+ ["BTC/USD", "ETH/USD", "BNB/USD", "ADA/USD", "SOL/USD"],
36
+ help="Select a cryptocurrency pair"
37
+ )
38
+ period = st.slider("Indicator Period", 5, 50, 20, help="Period for technical indicators")
39
+
40
+ st.markdown("---")
41
+ st.markdown("### About")
42
+ st.info("Analyze cryptocurrencies with technical indicators and real-time market data.")
43
+
44
+
45
+ # ---- Main Content ----
46
+ st.info("🚧 This page is under development. Cryptocurrency analysis features coming soon!")
47
+
48
+ st.markdown("""
49
+ ### Planned Features:
50
+
51
+ - **Real-time Price Data**: Live cryptocurrency prices from Binance
52
+ - **Market Metrics**: 24h volume, market cap, price changes
53
+ - **Technical Indicators**: SMA, EMA, RSI, MACD for crypto assets
54
+ - **TradingView Charts**: Interactive crypto charts
55
+ - **Market Sentiment**: Community sentiment analysis
56
+ - **Top Movers**: Biggest gainers and losers in 24h
57
+
58
+ Stay tuned for updates!
59
+ """)
60
+
61
+ # Placeholder metrics
62
+ col1, col2, col3, col4 = st.columns(4)
63
+
64
+ with col1:
65
+ st.metric("Current Price", "N/A", "N/A")
66
+
67
+ with col2:
68
+ st.metric("24h Change", "N/A", "N/A")
69
+
70
+ with col3:
71
+ st.metric("24h Volume", "N/A")
72
+
73
+ with col4:
74
+ st.metric("Market Cap", "N/A")
app/pages/03_Forex.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Forex Trading Analysis Page - Analyze foreign exchange pairs."""
2
+
3
+ import streamlit as st
4
+ import sys
5
+ import os
6
+
7
+ # Add parent directory to path for imports
8
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9
+
10
+ from components.styles import DARK_THEME_CSS
11
+
12
+
13
+ # ---- Page Configuration ----
14
+ st.set_page_config(
15
+ page_title="Forex - Financial Dashboard",
16
+ page_icon="πŸ’±",
17
+ layout="wide",
18
+ initial_sidebar_state="expanded",
19
+ )
20
+
21
+ # ---- Apply Dark Theme ----
22
+ st.markdown(DARK_THEME_CSS, unsafe_allow_html=True)
23
+
24
+ # ---- Header ----
25
+ st.markdown("# πŸ’± Forex Trading Analysis")
26
+ st.markdown("Foreign exchange analysis for major, minor, and exotic currency pairs")
27
+
28
+ st.markdown("---")
29
+
30
+ # ---- Sidebar Configuration ----
31
+ with st.sidebar:
32
+ st.markdown("## βš™οΈ Settings")
33
+ forex_pair = st.selectbox(
34
+ "Currency Pair",
35
+ ["EUR/USD", "GBP/USD", "USD/JPY", "USD/CHF", "AUD/USD", "USD/CAD"],
36
+ help="Select a forex pair"
37
+ )
38
+ period = st.slider("Indicator Period", 5, 50, 20, help="Period for technical indicators")
39
+
40
+ st.markdown("---")
41
+ st.markdown("### About")
42
+ st.info("Analyze forex pairs with technical indicators and real-time exchange rates.")
43
+
44
+
45
+ # ---- Main Content ----
46
+ st.info("🚧 This page is under development. Forex analysis features coming soon!")
47
+
48
+ st.markdown("""
49
+ ### Planned Features:
50
+
51
+ - **Real-time Exchange Rates**: Live forex rates from multiple sources
52
+ - **Major, Minor & Exotic Pairs**: Comprehensive coverage
53
+ - **Technical Analysis**: Full suite of technical indicators
54
+ - **Pip Calculator**: Calculate pip values for position sizing
55
+ - **Economic Calendar**: Important economic events
56
+ - **TradingView Charts**: Interactive forex charts
57
+
58
+ Stay tuned for updates!
59
+ """)
60
+
61
+ # Placeholder metrics
62
+ col1, col2, col3, col4 = st.columns(4)
63
+
64
+ with col1:
65
+ st.metric("Current Rate", "N/A", "N/A")
66
+
67
+ with col2:
68
+ st.metric("24h Change", "N/A", "N/A")
69
+
70
+ with col3:
71
+ st.metric("Bid Price", "N/A")
72
+
73
+ with col4:
74
+ st.metric("Ask Price", "N/A")
app/pages/04_Screener.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Market Screener Page - Find investment opportunities across markets."""
2
+
3
+ import streamlit as st
4
+ import sys
5
+ import os
6
+
7
+ # Add parent directory to path for imports
8
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9
+
10
+ from components.styles import DARK_THEME_CSS
11
+
12
+
13
+ # ---- Page Configuration ----
14
+ st.set_page_config(
15
+ page_title="Screener - Financial Dashboard",
16
+ page_icon="πŸ”",
17
+ layout="wide",
18
+ initial_sidebar_state="expanded",
19
+ )
20
+
21
+ # ---- Apply Dark Theme ----
22
+ st.markdown(DARK_THEME_CSS, unsafe_allow_html=True)
23
+
24
+ # ---- Header ----
25
+ st.markdown("# πŸ” Market Screener")
26
+ st.markdown("Advanced screening tools to find investment opportunities across markets")
27
+
28
+ st.markdown("---")
29
+
30
+ # ---- Sidebar Configuration ----
31
+ with st.sidebar:
32
+ st.markdown("## βš™οΈ Screening Filters")
33
+
34
+ asset_type = st.selectbox(
35
+ "Asset Type",
36
+ ["Stocks", "Crypto", "Forex"],
37
+ help="Select asset type to screen"
38
+ )
39
+
40
+ st.markdown("### Price Filters")
41
+ min_price = st.number_input("Min Price ($)", value=0.0, step=1.0)
42
+ max_price = st.number_input("Max Price ($)", value=1000.0, step=10.0)
43
+
44
+ st.markdown("### Technical Filters")
45
+ rsi_min = st.slider("Min RSI", 0, 100, 30)
46
+ rsi_max = st.slider("Max RSI", 0, 100, 70)
47
+
48
+ volume_min = st.number_input("Min Volume", value=1000000, step=100000)
49
+
50
+ st.markdown("---")
51
+ if st.button("πŸ” Run Screener", use_container_width=True):
52
+ st.info("Screening in progress...")
53
+
54
+
55
+ # ---- Main Content ----
56
+ st.info("🚧 This page is under development. Market screener features coming soon!")
57
+
58
+ st.markdown("""
59
+ ### Planned Features:
60
+
61
+ - **Multi-Asset Screening**: Stocks, crypto, and forex
62
+ - **Technical Filters**: RSI, MACD, moving averages, volume
63
+ - **Fundamental Filters**: P/E ratio, market cap, revenue growth
64
+ - **Pattern Recognition**: Chart patterns and technical setups
65
+ - **Custom Criteria**: Build your own screening rules
66
+ - **Export Results**: Download screening results as CSV
67
+ - **Saved Screens**: Save your favorite screening criteria
68
+
69
+ Stay tuned for updates!
70
+ """)
71
+
72
+ # Placeholder table
73
+ st.markdown("### Screening Results")
74
+ st.info("No screening results yet. Configure filters and run the screener.")
app/pages/05_Dashboard.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """News & AI Dashboard Page - AI-powered market insights and news aggregation."""
2
+
3
+ import streamlit as st
4
+ import sys
5
+ import os
6
+
7
+ # Add parent directory to path for imports
8
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9
+
10
+ from components.styles import DARK_THEME_CSS
11
+
12
+
13
+ # ---- Page Configuration ----
14
+ st.set_page_config(
15
+ page_title="Dashboard - Financial Dashboard",
16
+ page_icon="πŸ€–",
17
+ layout="wide",
18
+ initial_sidebar_state="expanded",
19
+ )
20
+
21
+ # ---- Apply Dark Theme ----
22
+ st.markdown(DARK_THEME_CSS, unsafe_allow_html=True)
23
+
24
+ # ---- Header ----
25
+ st.markdown("# πŸ€– News & AI Dashboard")
26
+ st.markdown("AI-powered market insights with sentiment analysis and trading recommendations")
27
+
28
+ st.markdown("---")
29
+
30
+ # ---- Sidebar Configuration ----
31
+ with st.sidebar:
32
+ st.markdown("## βš™οΈ Settings")
33
+
34
+ news_source = st.multiselect(
35
+ "News Sources",
36
+ ["All", "Reuters", "Bloomberg", "CNBC", "Yahoo Finance"],
37
+ default=["All"],
38
+ help="Filter news by source"
39
+ )
40
+
41
+ sentiment_filter = st.select_slider(
42
+ "Sentiment Filter",
43
+ options=["Very Negative", "Negative", "Neutral", "Positive", "Very Positive"],
44
+ value="Neutral"
45
+ )
46
+
47
+ st.markdown("---")
48
+ st.markdown("### AI Analysis")
49
+ ai_enabled = st.checkbox("Enable AI Insights", value=False)
50
+
51
+ if ai_enabled:
52
+ st.info("⚠️ AI insights require API key configuration.")
53
+
54
+
55
+ # ---- Main Content ----
56
+ st.info("🚧 This page is under development. News and AI features coming soon!")
57
+
58
+ st.markdown("""
59
+ ### Planned Features:
60
+
61
+ #### πŸ“° News Aggregation
62
+ - **Real-time News Feed**: Latest financial news from multiple sources
63
+ - **Sentiment Analysis**: AI-powered sentiment scoring for each article
64
+ - **Ticker-based Filtering**: See news for specific stocks
65
+ - **Source Filtering**: Choose your preferred news sources
66
+
67
+ #### πŸ€– AI-Powered Insights
68
+ - **Market Analysis**: AI analysis of market conditions
69
+ - **Price Predictions**: ML-based price trend predictions
70
+ - **Support/Resistance**: Automated technical level detection
71
+ - **Trading Signals**: Buy/sell/hold recommendations
72
+ - **Risk Assessment**: Position risk analysis
73
+
74
+ #### πŸ“Š Market Overview
75
+ - **Trending Tickers**: Most active and trending securities
76
+ - **Sector Performance**: Real-time sector rotation analysis
77
+ - **Market Breadth**: Advance/decline metrics
78
+
79
+ Stay tuned for updates!
80
+ """)
81
+
82
+ # Placeholder sections
83
+ col1, col2 = st.columns([2, 1])
84
+
85
+ with col1:
86
+ st.markdown("### πŸ“° Latest News")
87
+ st.info("No news articles available yet. Configure your API keys to enable news aggregation.")
88
+
89
+ with col2:
90
+ st.markdown("### πŸ€– AI Chat")
91
+ st.info("AI chat interface coming soon! You'll be able to ask questions about market conditions, get trading ideas, and receive personalized insights.")
92
+
93
+ st.markdown("---")
94
+
95
+ st.markdown("### πŸ“ˆ Trending Tickers")
96
+ st.info("Trending ticker data will be displayed here.")
app/utils/config.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Configuration management for the financial dashboard."""
2
+
3
+ import os
4
+ from dotenv import load_dotenv
5
+
6
+ # Load environment variables
7
+ load_dotenv()
8
+
9
+
10
+ class Config:
11
+ """Application configuration."""
12
+
13
+ # API Keys
14
+ DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "")
15
+ NEWS_SERVICE_URL = os.getenv("NEWS_SERVICE_URL", "")
16
+ ALPHA_VANTAGE_KEY = os.getenv("ALPHA_VANTAGE_KEY", "")
17
+
18
+ # Cache settings
19
+ PRICE_DATA_TTL = 3600 # 1 hour
20
+ FUNDAMENTAL_DATA_TTL = 86400 # 24 hours
21
+ NEWS_DATA_TTL = 900 # 15 minutes
22
+
23
+ # App settings
24
+ DEFAULT_STOCK_SYMBOL = "AAPL"
25
+ DEFAULT_CRYPTO_SYMBOL = "BTC/USD"
26
+ DEFAULT_FOREX_SYMBOL = "EUR/USD"
27
+ DEFAULT_INDICATOR_PERIOD = 20
28
+
29
+ # Data source settings
30
+ MAX_RETRY_ATTEMPTS = 3
31
+ REQUEST_TIMEOUT = 30
32
+
33
+
34
+ config = Config()
app/utils/formatters.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Data formatting utilities for the financial dashboard."""
2
+
3
+ import pandas as pd
4
+
5
+
6
+ def format_financial_value(value) -> str:
7
+ """Format financial values with appropriate units."""
8
+ if pd.isna(value):
9
+ return "N/A"
10
+ if abs(value) >= 1e9:
11
+ return f"${value/1e9:.2f}B"
12
+ elif abs(value) >= 1e6:
13
+ return f"${value/1e6:.2f}M"
14
+ else:
15
+ return f"${value:.2f}"
16
+
17
+
18
+ def format_percentage(value: float, decimals: int = 2) -> str:
19
+ """Format percentage values."""
20
+ if pd.isna(value):
21
+ return "N/A"
22
+ return f"{value:.{decimals}f}%"
23
+
24
+
25
+ def format_currency(value: float, decimals: int = 2) -> str:
26
+ """Format currency values."""
27
+ if pd.isna(value):
28
+ return "N/A"
29
+ return f"${value:,.{decimals}f}"
requirements.txt CHANGED
@@ -1,6 +1,6 @@
1
- openbb
2
- streamlit
3
- plotly
4
- pandas
5
- python-dotenv
6
- ta-lib
 
1
+ streamlit>=1.30.0
2
+ pandas>=2.0.0
3
+ plotly>=5.18.0
4
+ openbb>=4.0.0
5
+ python-dotenv>=1.0.0
6
+ requests>=2.31.0