Spaces:
Sleeping
Sleeping
Upload 11 files
Browse files- app.py +58 -0
- auth.py +49 -0
- data_fetcher.py +21 -0
- generated-icon.png +0 -0
- integrations.py +43 -0
- main.py +57 -0
- pyproject.toml +17 -0
- requirements.txt +17 -0
- styles.py +45 -0
- uv.lock +0 -0
- visualization.py +45 -0
app.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Renaming main.py to app.py for Hugging Face compatibility
|
| 2 |
+
import streamlit as st
|
| 3 |
+
import plotly.graph_objects as go
|
| 4 |
+
import yfinance as yf
|
| 5 |
+
import pandas as pd
|
| 6 |
+
from datetime import datetime, timedelta
|
| 7 |
+
from data_fetcher import get_stock_data, get_company_info
|
| 8 |
+
from visualization import create_stock_chart, create_metrics_table
|
| 9 |
+
import styles
|
| 10 |
+
|
| 11 |
+
def main():
|
| 12 |
+
# Apply custom styles
|
| 13 |
+
styles.apply_custom_styles()
|
| 14 |
+
|
| 15 |
+
st.title("📈 Stock Analysis Terminal")
|
| 16 |
+
|
| 17 |
+
# Stock Symbol Input
|
| 18 |
+
symbol = st.text_input("Enter Stock Symbol (e.g., AAPL)", "").upper()
|
| 19 |
+
|
| 20 |
+
if symbol:
|
| 21 |
+
try:
|
| 22 |
+
# Fetch stock data
|
| 23 |
+
stock_data = get_stock_data(symbol)
|
| 24 |
+
company_info = get_company_info(symbol)
|
| 25 |
+
|
| 26 |
+
# Display company info
|
| 27 |
+
col1, col2 = st.columns(2)
|
| 28 |
+
with col1:
|
| 29 |
+
st.subheader(company_info.get('longName', symbol))
|
| 30 |
+
st.write(f"Sector: {company_info.get('sector', 'N/A')}")
|
| 31 |
+
with col2:
|
| 32 |
+
st.metric(
|
| 33 |
+
"Current Price",
|
| 34 |
+
f"${stock_data['Close'].iloc[-1]:.2f}",
|
| 35 |
+
f"{((stock_data['Close'].iloc[-1] / stock_data['Close'].iloc[-2]) - 1) * 100:.2f}%"
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
# Stock Chart
|
| 39 |
+
st.plotly_chart(create_stock_chart(stock_data, symbol), use_container_width=True)
|
| 40 |
+
|
| 41 |
+
# Financial Metrics Table
|
| 42 |
+
metrics_df = create_metrics_table(company_info)
|
| 43 |
+
st.dataframe(metrics_df, use_container_width=True)
|
| 44 |
+
|
| 45 |
+
# Download button for CSV
|
| 46 |
+
csv = stock_data.to_csv(index=True)
|
| 47 |
+
st.download_button(
|
| 48 |
+
label="Download Data as CSV",
|
| 49 |
+
data=csv,
|
| 50 |
+
file_name=f"{symbol}_stock_data.csv",
|
| 51 |
+
mime="text/csv"
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
except Exception as e:
|
| 55 |
+
st.error(f"Error retrieving data for {symbol}: {str(e)}")
|
| 56 |
+
|
| 57 |
+
if __name__ == "__main__":
|
| 58 |
+
main()
|
auth.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import firebase_admin
|
| 3 |
+
from firebase_admin import credentials, auth
|
| 4 |
+
from firebase_admin import initialize_app
|
| 5 |
+
|
| 6 |
+
def initialize_firebase():
|
| 7 |
+
if not firebase_admin._apps:
|
| 8 |
+
cred = credentials.Certificate(st.secrets["firebase"])
|
| 9 |
+
initialize_app(cred)
|
| 10 |
+
|
| 11 |
+
def check_authentication():
|
| 12 |
+
initialize_firebase()
|
| 13 |
+
|
| 14 |
+
if 'user_authenticated' not in st.session_state:
|
| 15 |
+
st.session_state.user_authenticated = False
|
| 16 |
+
|
| 17 |
+
if not st.session_state.user_authenticated:
|
| 18 |
+
col1, col2 = st.columns(2)
|
| 19 |
+
|
| 20 |
+
with col1:
|
| 21 |
+
email = st.text_input("Email")
|
| 22 |
+
with col2:
|
| 23 |
+
password = st.text_input("Password", type="password")
|
| 24 |
+
|
| 25 |
+
col1, col2 = st.columns(2)
|
| 26 |
+
|
| 27 |
+
with col1:
|
| 28 |
+
if st.button("Login"):
|
| 29 |
+
try:
|
| 30 |
+
user = auth.get_user_by_email(email)
|
| 31 |
+
st.session_state.user_authenticated = True
|
| 32 |
+
st.experimental_rerun()
|
| 33 |
+
except:
|
| 34 |
+
st.error("Invalid credentials")
|
| 35 |
+
|
| 36 |
+
with col2:
|
| 37 |
+
if st.button("Sign Up"):
|
| 38 |
+
try:
|
| 39 |
+
user = auth.create_user(
|
| 40 |
+
email=email,
|
| 41 |
+
password=password
|
| 42 |
+
)
|
| 43 |
+
st.success("Account created successfully!")
|
| 44 |
+
except:
|
| 45 |
+
st.error("Could not create account")
|
| 46 |
+
|
| 47 |
+
return False
|
| 48 |
+
|
| 49 |
+
return True
|
data_fetcher.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import yfinance as yf
|
| 3 |
+
import pandas as pd
|
| 4 |
+
from datetime import datetime, timedelta
|
| 5 |
+
|
| 6 |
+
@st.cache_data(ttl=3600)
|
| 7 |
+
def get_stock_data(symbol):
|
| 8 |
+
"""Fetch stock data from Yahoo Finance"""
|
| 9 |
+
end_date = datetime.now()
|
| 10 |
+
start_date = end_date - timedelta(days=365)
|
| 11 |
+
|
| 12 |
+
stock = yf.Ticker(symbol)
|
| 13 |
+
df = stock.history(start=start_date, end=end_date)
|
| 14 |
+
|
| 15 |
+
return df
|
| 16 |
+
|
| 17 |
+
@st.cache_data(ttl=3600)
|
| 18 |
+
def get_company_info(symbol):
|
| 19 |
+
"""Fetch company information"""
|
| 20 |
+
stock = yf.Ticker(symbol)
|
| 21 |
+
return stock.info
|
generated-icon.png
ADDED
|
|
integrations.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from google.oauth2.credentials import Credentials
|
| 3 |
+
from google_auth_oauthlib.flow import InstalledAppFlow
|
| 4 |
+
from googleapiclient.discovery import build
|
| 5 |
+
from datetime import datetime, timedelta
|
| 6 |
+
|
| 7 |
+
def add_to_google_calendar(symbol, company_name):
|
| 8 |
+
"""Add analysis review event to Google Calendar"""
|
| 9 |
+
creds = Credentials.from_authorized_user_info(st.secrets["google"])
|
| 10 |
+
service = build('calendar', 'v3', credentials=creds)
|
| 11 |
+
|
| 12 |
+
event = {
|
| 13 |
+
'summary': f'Stock Analysis Review - {symbol}',
|
| 14 |
+
'description': f'Review financial analysis for {company_name}',
|
| 15 |
+
'start': {
|
| 16 |
+
'dateTime': (datetime.now() + timedelta(days=1)).isoformat(),
|
| 17 |
+
'timeZone': 'UTC',
|
| 18 |
+
},
|
| 19 |
+
'end': {
|
| 20 |
+
'dateTime': (datetime.now() + timedelta(days=1, hours=1)).isoformat(),
|
| 21 |
+
'timeZone': 'UTC',
|
| 22 |
+
},
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
service.events().insert(calendarId='primary', body=event).execute()
|
| 26 |
+
|
| 27 |
+
def update_google_sheet(stock_data, symbol):
|
| 28 |
+
"""Export stock data to Google Sheets"""
|
| 29 |
+
creds = Credentials.from_authorized_user_info(st.secrets["google"])
|
| 30 |
+
service = build('sheets', 'v4', credentials=creds)
|
| 31 |
+
|
| 32 |
+
spreadsheet_id = st.secrets["google_sheet_id"]
|
| 33 |
+
range_name = f'{symbol}!A1'
|
| 34 |
+
|
| 35 |
+
values = [stock_data.columns.tolist()] + stock_data.reset_index().values.tolist()
|
| 36 |
+
body = {'values': values}
|
| 37 |
+
|
| 38 |
+
service.spreadsheets().values().update(
|
| 39 |
+
spreadsheetId=spreadsheet_id,
|
| 40 |
+
range=range_name,
|
| 41 |
+
valueInputOption='RAW',
|
| 42 |
+
body=body
|
| 43 |
+
).execute()
|
main.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import plotly.graph_objects as go
|
| 3 |
+
import yfinance as yf
|
| 4 |
+
import pandas as pd
|
| 5 |
+
from datetime import datetime, timedelta
|
| 6 |
+
from data_fetcher import get_stock_data, get_company_info
|
| 7 |
+
from visualization import create_stock_chart, create_metrics_table
|
| 8 |
+
import styles
|
| 9 |
+
|
| 10 |
+
def main():
|
| 11 |
+
# Apply custom styles
|
| 12 |
+
styles.apply_custom_styles()
|
| 13 |
+
|
| 14 |
+
st.title("📈 Stock Analysis Terminal")
|
| 15 |
+
|
| 16 |
+
# Stock Symbol Input
|
| 17 |
+
symbol = st.text_input("Enter Stock Symbol (e.g., AAPL)", "").upper()
|
| 18 |
+
|
| 19 |
+
if symbol:
|
| 20 |
+
try:
|
| 21 |
+
# Fetch stock data
|
| 22 |
+
stock_data = get_stock_data(symbol)
|
| 23 |
+
company_info = get_company_info(symbol)
|
| 24 |
+
|
| 25 |
+
# Display company info
|
| 26 |
+
col1, col2 = st.columns(2)
|
| 27 |
+
with col1:
|
| 28 |
+
st.subheader(company_info.get('longName', symbol))
|
| 29 |
+
st.write(f"Sector: {company_info.get('sector', 'N/A')}")
|
| 30 |
+
with col2:
|
| 31 |
+
st.metric(
|
| 32 |
+
"Current Price",
|
| 33 |
+
f"${stock_data['Close'].iloc[-1]:.2f}",
|
| 34 |
+
f"{((stock_data['Close'].iloc[-1] / stock_data['Close'].iloc[-2]) - 1) * 100:.2f}%"
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
# Stock Chart
|
| 38 |
+
st.plotly_chart(create_stock_chart(stock_data, symbol), use_container_width=True)
|
| 39 |
+
|
| 40 |
+
# Financial Metrics Table
|
| 41 |
+
metrics_df = create_metrics_table(company_info)
|
| 42 |
+
st.dataframe(metrics_df, use_container_width=True)
|
| 43 |
+
|
| 44 |
+
# Download button for CSV
|
| 45 |
+
csv = stock_data.to_csv(index=True)
|
| 46 |
+
st.download_button(
|
| 47 |
+
label="Download Data as CSV",
|
| 48 |
+
data=csv,
|
| 49 |
+
file_name=f"{symbol}_stock_data.csv",
|
| 50 |
+
mime="text/csv"
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
except Exception as e:
|
| 54 |
+
st.error(f"Error retrieving data for {symbol}: {str(e)}")
|
| 55 |
+
|
| 56 |
+
if __name__ == "__main__":
|
| 57 |
+
main()
|
pyproject.toml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "repl-nix-workspace"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "Add your description here"
|
| 5 |
+
requires-python = ">=3.11"
|
| 6 |
+
dependencies = [
|
| 7 |
+
"firebase-admin>=6.6.0",
|
| 8 |
+
"google-api-python-client>=2.159.0",
|
| 9 |
+
"google-auth>=2.37.0",
|
| 10 |
+
"google-auth-oauthlib>=1.2.1",
|
| 11 |
+
"huggingface-hub>=0.27.1",
|
| 12 |
+
"pandas>=2.2.3",
|
| 13 |
+
"plotly>=5.24.1",
|
| 14 |
+
"streamlit>=1.41.1",
|
| 15 |
+
"telegram>=0.0.1",
|
| 16 |
+
"yfinance>=0.2.52",
|
| 17 |
+
]
|
requirements.txt
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit
|
| 2 |
+
yfinance
|
| 3 |
+
pandas
|
| 4 |
+
plotly
|
| 5 |
+
firebase-admin
|
| 6 |
+
google-api-python-client
|
| 7 |
+
google-auth-oauthlib
|
| 8 |
+
huggingface-hub
|
| 9 |
+
firebase-admin
|
| 10 |
+
google-api-python-client
|
| 11 |
+
google-auth
|
| 12 |
+
google-auth-oauthlib
|
| 13 |
+
huggingface-hub
|
| 14 |
+
pandas
|
| 15 |
+
plotly
|
| 16 |
+
streamlit
|
| 17 |
+
yfinance
|
styles.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
|
| 3 |
+
def apply_custom_styles():
|
| 4 |
+
"""Apply custom CSS styles to the application"""
|
| 5 |
+
st.markdown("""
|
| 6 |
+
<style>
|
| 7 |
+
.stApp {
|
| 8 |
+
max-width: 1200px;
|
| 9 |
+
margin: 0 auto;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
.stTextInput > div > div > input {
|
| 13 |
+
background-color: #1E1E1E;
|
| 14 |
+
color: #FFFFFF;
|
| 15 |
+
border: 1px solid #4A4A4A;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
.stDataFrame {
|
| 19 |
+
background-color: #1E1E1E;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
.stButton > button {
|
| 23 |
+
background-color: #FF4B4B;
|
| 24 |
+
color: white;
|
| 25 |
+
border: none;
|
| 26 |
+
border-radius: 4px;
|
| 27 |
+
padding: 0.5rem 1rem;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.stButton > button:hover {
|
| 31 |
+
background-color: #FF6B6B;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
h1, h2, h3 {
|
| 35 |
+
color: #FFFFFF;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
.metric-card {
|
| 39 |
+
background-color: #262730;
|
| 40 |
+
padding: 1rem;
|
| 41 |
+
border-radius: 8px;
|
| 42 |
+
margin: 0.5rem 0;
|
| 43 |
+
}
|
| 44 |
+
</style>
|
| 45 |
+
""", unsafe_allow_html=True)
|
uv.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
visualization.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import plotly.graph_objects as go
|
| 2 |
+
import pandas as pd
|
| 3 |
+
|
| 4 |
+
def create_stock_chart(stock_data, symbol):
|
| 5 |
+
"""Create an interactive stock price chart"""
|
| 6 |
+
fig = go.Figure()
|
| 7 |
+
|
| 8 |
+
fig.add_trace(
|
| 9 |
+
go.Candlestick(
|
| 10 |
+
x=stock_data.index,
|
| 11 |
+
open=stock_data['Open'],
|
| 12 |
+
high=stock_data['High'],
|
| 13 |
+
low=stock_data['Low'],
|
| 14 |
+
close=stock_data['Close'],
|
| 15 |
+
name=symbol
|
| 16 |
+
)
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
fig.update_layout(
|
| 20 |
+
title=f"{symbol} Stock Price",
|
| 21 |
+
yaxis_title="Price (USD)",
|
| 22 |
+
template="plotly_dark",
|
| 23 |
+
xaxis_rangeslider_visible=False
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
return fig
|
| 27 |
+
|
| 28 |
+
def create_metrics_table(company_info):
|
| 29 |
+
"""Create a table of key financial metrics"""
|
| 30 |
+
metrics = {
|
| 31 |
+
'Metric': [
|
| 32 |
+
'Market Cap', 'P/E Ratio', 'Forward P/E',
|
| 33 |
+
'PEG Ratio', 'Dividend Yield', 'Beta'
|
| 34 |
+
],
|
| 35 |
+
'Value': [
|
| 36 |
+
company_info.get('marketCap', 'N/A'),
|
| 37 |
+
company_info.get('trailingPE', 'N/A'),
|
| 38 |
+
company_info.get('forwardPE', 'N/A'),
|
| 39 |
+
company_info.get('pegRatio', 'N/A'),
|
| 40 |
+
company_info.get('dividendYield', 'N/A'),
|
| 41 |
+
company_info.get('beta', 'N/A')
|
| 42 |
+
]
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
return pd.DataFrame(metrics)
|