Skip to content

Commit 96094c8

Browse files
Merge pull request #1 from AlejandroDiazD/develop
First stable version v1.0.0
2 parents d04a1e9 + 70bbc14 commit 96094c8

File tree

18 files changed

+519
-1
lines changed

18 files changed

+519
-1
lines changed

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*.pyo
5+
*.pyd
6+
*.so
7+
8+
# Virtual environments
9+
venv/
10+
.env/
11+
.venv/
12+
venv-fintech/
13+
14+
# Jupyter / Streamlit cache
15+
.ipynb_checkpoints
16+
.streamlit/
17+
.cache/
18+
**/__pycache__/

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
This project adheres to [Semantic Versioning](https://semver.org/).
5+
6+
---
7+
8+
## [Unreleased]
9+
10+
### Added
11+
- (placeholder for upcoming features)
12+
13+
---
14+
15+
## [v1.0.0] - 2025-11-02
16+
17+
### Added
18+
- Initial Streamlit dashboard structure
19+
- Modular architecture (`modules/data`, `modules/analysis`, `modules/visualization`, `modules/ui`)
20+
- Real-time data via Yahoo Finance (`yfinance`)
21+
- Linear regression forecasting
22+
- RSI and SMA (20/50) indicators
23+
- Asset selector covering indices, commodities, crypto, bonds, and ETFs
24+
- Plotly visualizations (candlestick, RSI, forecast)
25+
- User Guide tab
26+
- README, LICENSE (MIT), and .gitignore
27+
28+
### Fixed
29+
- None (initial release)
30+
31+
### Changed
32+
- None (initial release)
33+
34+
### Removed
35+
- None (initial release)

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 <Tu Nombre>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# 💹 FinMarket Analyzer
2+
3+
> ⚠️ **Disclaimer:** This project is still under active development and continuous evolution.
4+
5+
---
6+
7+
An interactive **Python & Streamlit dashboard** for exploring and analyzing global financial markets.
8+
It provides real-time insights, technical indicators, and linear trend forecasting across equities, commodities, crypto, fixed income, and thematic ETFs.
9+
10+
---
11+
12+
## 🚀 Features
13+
14+
- 📈 **Interactive dashboards** built with Streamlit and Plotly
15+
- 💰 **Multi-asset support** — equities, commodities, crypto, fixed income, thematic ETFs
16+
- 📊 **Technical indicators** — Simple Moving Averages (SMA), RSI
17+
- 🔮 **Forecasting** — basic linear regression trend prediction
18+
- 🧱 **Modular architecture** — easy to extend with new models or analytics
19+
- 💾 **Cached data** for performance via Streamlit’s cache system
20+
21+
---
22+
23+
## 🧠 Tech Stack
24+
25+
- **Python 3.12+**
26+
- [Streamlit](https://streamlit.io)
27+
- [Plotly](https://plotly.com/python/)
28+
- [yfinance](https://github.com/ranaroussi/yfinance)
29+
- [pandas](https://pandas.pydata.org/)
30+
- [scikit-learn](https://scikit-learn.org/)
31+
32+
---
33+
34+
## 🏗️ Installation
35+
36+
Clone the repository and install the dependencies:
37+
38+
```bash
39+
git clone https://github.com/<your-username>/finmarket-analyzer.git
40+
cd finmarket-analyzer
41+
pip install -r requirements.txt
42+
```
43+
44+
## ▶️ Run the App
45+
46+
```bash
47+
streamlit run finmarket_app/app.py
48+
```
49+
50+
## Author
51+
52+
**AlejandroDiazD**
53+
[GitHub](https://github.com/AlejandroDiazD)
54+
55+
## License
56+
57+
This project is licensed under the **MIT License** — see the [LICENSE](LICENSE) file for details.
58+
59+
You are free to use, modify, and distribute this software, provided that the original license and copyright notice are included.

finmarket_app/app.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import streamlit as st
2+
import pandas as pd
3+
4+
from modules.data.data_loader import load_data
5+
from modules.analysis.indicators import add_technical_indicators
6+
from modules.analysis.metrics import get_quick_metrics
7+
from modules.analysis.forecast import make_linear_forecast
8+
from modules.visualization.plots import plot_candlestick, plot_rsi, plot_forecast
9+
from modules.ui.user_guide import render_user_guide
10+
from modules.ui.utils_ui import format_asset_title
11+
12+
13+
st.set_page_config(page_title="Market Insights Dashboard", layout="wide")
14+
st.title("📊 Market Insights Dashboard")
15+
16+
# --- Sidebar ---
17+
st.sidebar.header("Settings")
18+
# Dropdown for asset selection
19+
asset_options = {
20+
# ---- Global Indices ----
21+
"S&P 500": "^GSPC",
22+
"NASDAQ 100": "^NDX",
23+
"MSCI World": "URTH",
24+
"MSCI Emerging Markets": "EEM",
25+
26+
# ---- Commodities ----
27+
"Gold": "GC=F",
28+
29+
# ---- Cryptocurrencies ----
30+
"Bitcoin": "BTC-USD",
31+
"Ethereum": "ETH-USD",
32+
33+
# ---- Fixed Income ----
34+
"EU Gov Bonds 1-3Y": "EUNA.AS", # working ETF replacement
35+
36+
# ---- Thematic ETFs ----
37+
"VanEck Uranium & Nuclear ETF": "NLR",
38+
"iShares Global Clean Energy": "ICLN",
39+
}
40+
41+
asset_name = st.sidebar.selectbox("Select asset", list(asset_options.keys()))
42+
ticker = asset_options[asset_name]
43+
44+
period = st.sidebar.selectbox("Period", ["1y", "5y", "10y", "max"])
45+
interval = st.sidebar.selectbox("Interval", ["1d", "1wk", "1mo"])
46+
47+
st.sidebar.header("Forecast Settings")
48+
forecast_horizon = st.sidebar.slider("Horizon (steps)", 7, 90, 14)
49+
forecast_window = st.sidebar.slider("Training window (points)", 30, 365, 120)
50+
51+
# --- Tabs ---
52+
tab_dashboard, tab_forecast, tab_guide = st.tabs(["📈 Dashboard", "🔮 Forecast", "📘 User Guide"])
53+
54+
with tab_dashboard:
55+
data = load_data(ticker, period, interval)
56+
if data.empty:
57+
st.error("⚠️ No market data available for this ticker.")
58+
st.stop()
59+
60+
data = add_technical_indicators(data)
61+
display_title = format_asset_title(asset_name, ticker)
62+
st.plotly_chart(plot_candlestick(data, display_title), width='stretch')
63+
st.subheader("📉 Relative Strength Index (RSI)")
64+
st.plotly_chart(plot_rsi(data), width='stretch')
65+
66+
st.subheader("📈 Quick Metrics")
67+
col1, col2, col3 = st.columns(3)
68+
price, change_week, rsi = get_quick_metrics(data)
69+
col1.metric("Current Price", f"{price:.2f}")
70+
col2.metric("1-Week Change", f"{change_week:.2f}%")
71+
col3.metric("Current RSI", f"{rsi:.2f}")
72+
73+
with tab_forecast:
74+
st.subheader("🔮 Simple Linear Forecast")
75+
data = load_data(ticker, period, interval)
76+
if data.empty:
77+
st.warning("No data available to build a forecast.")
78+
st.stop()
79+
80+
fc = make_linear_forecast(data, interval, forecast_window, forecast_horizon)
81+
if fc.empty:
82+
st.warning("Not enough data to produce a forecast.")
83+
else:
84+
st.plotly_chart(plot_forecast(data, fc, display_title), width='stretch')
85+
86+
st.info("This forecast uses a simple linear regression trend based on recent data.")
87+
88+
with tab_guide:
89+
render_user_guide()
File renamed without changes.

utils/data_loader.py renamed to finmarket_app/modules/analysis/__init__.py

File renamed without changes.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from __future__ import annotations
2+
import numpy as np
3+
import pandas as pd
4+
from sklearn.linear_model import LinearRegression
5+
6+
def _infer_horizon(interval: str, default: int = 14) -> int:
7+
return default
8+
9+
def _ensure_series(close: pd.Series | pd.DataFrame) -> pd.Series:
10+
if isinstance(close, pd.DataFrame):
11+
close = close.squeeze()
12+
return close
13+
14+
def make_linear_forecast(
15+
data: pd.DataFrame,
16+
interval: str = "1d",
17+
window: int | None = None,
18+
horizon: int | None = None
19+
) -> pd.DataFrame:
20+
"""
21+
Build a simple univariate linear-regression forecast on the Close price.
22+
23+
- Uses the last `window` points to fit a linear trend.
24+
- Projects the next `horizon` steps.
25+
- Adds naive confidence bands from residual std (not statistically rigorous).
26+
27+
Returns
28+
-------
29+
pd.DataFrame
30+
Forecast DataFrame with index = forecast dates and columns:
31+
['yhat', 'yhat_lower', 'yhat_upper']
32+
"""
33+
if data is None or data.empty:
34+
return pd.DataFrame()
35+
36+
close = _ensure_series(data["Close"]).dropna()
37+
if close.empty or len(close) < 10:
38+
return pd.DataFrame()
39+
40+
n = len(close)
41+
if window is None:
42+
window = max(30, int(n * 0.5)) # use half the series or at least 30
43+
window = min(window, n)
44+
45+
if horizon is None:
46+
horizon = _infer_horizon(interval, default=14)
47+
48+
# --- Prepare training data ---
49+
y = close.iloc[-window:].values # shape (window,)
50+
X = np.arange(window).reshape(-1, 1)
51+
52+
# --- Fit linear model ---
53+
model = LinearRegression()
54+
model.fit(X, y)
55+
56+
# --- Compute in-sample residuals ---
57+
y_hat_in = model.predict(X)
58+
resid = y - y_hat_in
59+
sigma = np.std(resid) if len(resid) > 1 else 0.0
60+
61+
# --- Predict future ---
62+
X_fut = np.arange(window, window + horizon).reshape(-1, 1)
63+
y_hat = model.predict(X_fut)
64+
65+
# --- Confidence bands (approx. ±1.96σ) ---
66+
y_lower = y_hat - 1.96 * sigma
67+
y_upper = y_hat + 1.96 * sigma
68+
69+
# --- Build future index ---
70+
last_ts = close.index[-1]
71+
if close.index.inferred_type in ("datetime64", "datetime64tz"):
72+
freq_map = {"1d": "D", "1wk": "W", "1mo": "MS"}
73+
freq = freq_map.get(interval, "D")
74+
future_index = pd.date_range(last_ts, periods=horizon + 1, freq=freq)[1:]
75+
else:
76+
future_index = pd.RangeIndex(n, n + horizon)
77+
78+
# --- Build output DataFrame ---
79+
out = pd.DataFrame(
80+
{"yhat": y_hat, "yhat_lower": y_lower, "yhat_upper": y_upper},
81+
index=future_index
82+
)
83+
84+
# ✅ Clean and sort output (robustness)
85+
out = out.sort_index()
86+
out = out.apply(pd.to_numeric, errors="coerce")
87+
out = out.dropna(subset=["yhat"])
88+
89+
return out
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import ta
2+
import pandas as pd
3+
4+
def add_technical_indicators(data: pd.DataFrame) -> pd.DataFrame:
5+
"""
6+
Adds simple moving averages (SMA 20, SMA 50) and RSI.
7+
Handles MultiIndex columns from yfinance.
8+
"""
9+
if isinstance(data.columns, pd.MultiIndex):
10+
data.columns = [col[0] for col in data.columns]
11+
if isinstance(data["Close"], pd.DataFrame):
12+
data["Close"] = data["Close"].squeeze()
13+
14+
data["SMA_20"] = ta.trend.sma_indicator(data["Close"], window=20)
15+
data["SMA_50"] = ta.trend.sma_indicator(data["Close"], window=50)
16+
data["RSI"] = ta.momentum.rsi(data["Close"], window=14)
17+
return data
18+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import pandas as pd
2+
3+
def get_quick_metrics(data: pd.DataFrame):
4+
"""
5+
Returns current price, weekly change, and RSI.
6+
"""
7+
current_price = data['Close'].iloc[-1]
8+
change_week = (data['Close'].iloc[-1] / data['Close'].iloc[-5] - 1) * 100
9+
current_rsi = data['RSI'].iloc[-1]
10+
return current_price, change_week, current_rsi
11+

0 commit comments

Comments
 (0)