Spaces:
Runtime error
Runtime error
# app.py | |
# Import required libraries | |
import streamlit as st | |
import yfinance as yf | |
from datetime import date, timedelta | |
import pandas as pd | |
import numpy as np | |
import matplotlib.pyplot as plt | |
import io | |
# Optional: Install 'ta' library for technical indicators (uncomment if needed) | |
# !pip install ta | |
# Set Streamlit page configuration | |
st.set_page_config( | |
page_title='📈 Indian Stock Data Downloader and Volume Analyzer', | |
layout='wide', | |
initial_sidebar_state='expanded' | |
) | |
# Set the title of the app | |
st.title('📈 Indian Stock Data Downloader and Volume Analyzer') | |
# Sidebar for user inputs | |
st.sidebar.header('🔧 Configuration') | |
# File uploader for 'stock_list.csv' | |
uploaded_file = st.sidebar.file_uploader( | |
"📂 Upload your stock list CSV", | |
type=["csv"], | |
help="The CSV file should contain 'Symbol' and 'Company Name' columns." | |
) | |
if uploaded_file is not None: | |
try: | |
# Read the uploaded CSV file into a DataFrame | |
stock_df = pd.read_csv(uploaded_file) | |
# Ensure that the required columns are present | |
required_columns = {'Symbol', 'Company Name'} | |
if not required_columns.issubset(stock_df.columns): | |
st.error(f"The CSV file must contain the following columns: {', '.join(required_columns)}") | |
else: | |
# Create a dictionary mapping company names to stock symbols | |
stock_dict = pd.Series(stock_df['Symbol'].values, index=stock_df['Company Name']).to_dict() | |
# Multiselect widget for stock selection | |
selected_stocks = st.sidebar.multiselect( | |
'✅ Select Stocks:', | |
options=list(stock_dict.keys()), | |
default=list(stock_dict.keys())[:5] # Select first 5 stocks by default | |
) | |
# Date input widgets for date range selection | |
default_start_date = date.today() - timedelta(days=365) # Past year | |
start_date = st.sidebar.date_input('📅 Start Date', default_start_date) | |
end_date = st.sidebar.date_input('📅 End Date', date.today()) | |
# Ensure that start_date is before end_date | |
if start_date > end_date: | |
st.sidebar.error("❌ Start Date must be before End Date.") | |
# Checkbox for selecting data options | |
st.sidebar.header('📊 Data Options') | |
data_options = st.sidebar.multiselect( | |
'Select Data to Download:', | |
['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume'], | |
default=['Open', 'High', 'Low', 'Close', 'Volume'] | |
) | |
# Technical Indicators selection | |
st.sidebar.header('🔍 Technical Indicators') | |
indicators = st.sidebar.multiselect( | |
'Select Technical Indicators to Calculate:', | |
['OBV (Amount)', 'RSI', 'MACD'] | |
) | |
# Download and Analyze Data button | |
if st.sidebar.button('📥 Download and Analyze Data'): | |
if selected_stocks: | |
for company_name in selected_stocks: | |
ticker = stock_dict[company_name] | |
# Append '.NS' or '.BO' for NSE or BSE stocks if not already present | |
if not (ticker.endswith('.NS') or ticker.endswith('.BO')): | |
# Default to NSE; modify as needed | |
ticker += '.NS' | |
# Fetch data from Yahoo Finance | |
try: | |
with st.spinner(f"⏳ Fetching data for {company_name} ({ticker})..."): | |
stock_data = yf.download(ticker, start=start_date, end=end_date) | |
# Check if data is returned | |
if stock_data.empty: | |
st.warning(f"No data found for **{company_name}** ({ticker}) in the selected date range.") | |
continue | |
# Filter data based on selected options | |
available_options = [option for option in data_options if option in stock_data.columns] | |
if not available_options: | |
st.warning(f"No selected data options available for **{company_name}** ({ticker}).") | |
continue | |
stock_data = stock_data[available_options] | |
st.subheader(f"📊 Data for {company_name} ({ticker}):") | |
st.dataframe(stock_data) | |
# Reset index to get 'Date' as a column | |
stock_data = stock_data.reset_index() | |
# Calculate Amount (Close * Volume) if 'Close' and 'Volume' are selected | |
if 'Close' in stock_data.columns and 'Volume' in stock_data.columns: | |
stock_data['Amount'] = stock_data['Close'] * stock_data['Volume'] | |
else: | |
stock_data['Amount'] = np.nan | |
# OBV in terms of Amount | |
if 'OBV (Amount)' in indicators and 'Amount' in stock_data.columns: | |
# Calculate direction | |
stock_data['Daily_Return'] = stock_data['Close'].pct_change() | |
stock_data['Direction'] = stock_data['Daily_Return'].apply( | |
lambda x: 1 if x > 0 else (-1 if x < 0 else 0)) | |
stock_data['OBV_Amount'] = (stock_data['Amount'] * stock_data['Direction']).cumsum() | |
# Plot OBV in Amount | |
fig_obv_amount, ax = plt.subplots(figsize=(12, 4)) | |
ax.plot(stock_data['Date'], stock_data['OBV_Amount'], label='OBV (Amount)', color='orange') | |
ax.set_xlabel('Date') | |
ax.set_ylabel('On-Balance Volume (Amount)') | |
ax.set_title(f"{company_name} OBV (Amount)") | |
ax.legend() | |
st.pyplot(fig_obv_amount) | |
# RSI | |
if 'RSI' in indicators and 'Close' in stock_data.columns: | |
delta = stock_data['Close'].diff() | |
up = delta.clip(lower=0) | |
down = -1 * delta.clip(upper=0) | |
roll_up = up.rolling(window=14).mean() | |
roll_down = down.rolling(window=14).mean() | |
RS = roll_up / roll_down | |
stock_data['RSI'] = 100.0 - (100.0 / (1.0 + RS)) | |
# Plot RSI | |
fig_rsi, ax = plt.subplots(figsize=(12, 4)) | |
ax.plot(stock_data['Date'], stock_data['RSI'], label='RSI', color='green') | |
ax.set_xlabel('Date') | |
ax.set_ylabel('RSI') | |
ax.set_title(f"{company_name} RSI") | |
ax.axhline(70, color='red', linestyle='--') | |
ax.axhline(30, color='blue', linestyle='--') | |
ax.legend() | |
st.pyplot(fig_rsi) | |
# MACD | |
if 'MACD' in indicators and 'Close' in stock_data.columns: | |
exp1 = stock_data['Close'].ewm(span=12, adjust=False).mean() | |
exp2 = stock_data['Close'].ewm(span=26, adjust=False).mean() | |
stock_data['MACD'] = exp1 - exp2 | |
stock_data['MACD_Signal'] = stock_data['MACD'].ewm(span=9, adjust=False).mean() | |
stock_data['MACD_Hist'] = stock_data['MACD'] - stock_data['MACD_Signal'] | |
# Plot MACD | |
fig_macd, ax = plt.subplots(figsize=(12, 4)) | |
ax.plot(stock_data['Date'], stock_data['MACD'], label='MACD', color='purple') | |
ax.plot(stock_data['Date'], stock_data['MACD_Signal'], label='Signal', color='red') | |
ax.bar(stock_data['Date'], stock_data['MACD_Hist'], label='Histogram', color='grey') | |
ax.set_xlabel('Date') | |
ax.set_ylabel('MACD') | |
ax.set_title(f"{company_name} MACD") | |
ax.legend() | |
st.pyplot(fig_macd) | |
# Calculate moving averages for Volume if 'Volume' is selected | |
if 'Volume' in stock_data.columns: | |
stock_data['Volume_MA_5'] = stock_data['Volume'].rolling(window=5).mean() | |
stock_data['Volume_MA_20'] = stock_data['Volume'].rolling(window=20).mean() | |
# Plotting price and volume | |
fig, ax1 = plt.subplots(figsize=(12, 6)) | |
# Plot the closing price | |
ax1.plot(stock_data['Date'], stock_data['Close'], color='blue', label='Close Price') | |
ax1.set_xlabel('Date') | |
ax1.set_ylabel('Close Price', color='blue') | |
ax1.tick_params(axis='y', labelcolor='blue') | |
# Create a second y-axis for volume | |
ax2 = ax1.twinx() | |
ax2.bar(stock_data['Date'], stock_data['Volume'], color='gray', alpha=0.3, label='Volume') | |
# Plot moving averages of volume | |
ax2.plot(stock_data['Date'], stock_data['Volume_MA_5'], color='red', label='5-Day MA') | |
ax2.plot(stock_data['Date'], stock_data['Volume_MA_20'], color='green', label='20-Day MA') | |
ax2.set_ylabel('Volume', color='gray') | |
ax2.tick_params(axis='y', labelcolor='gray') | |
# Add legends and title | |
fig.legend(loc='upper left', bbox_to_anchor=(0.15, 0.85)) | |
plt.title(f"{company_name} Price and Volume Chart with Moving Averages") | |
fig.tight_layout() | |
st.pyplot(fig) | |
# Volume analysis | |
stock_data['Volume_Pct_Change'] = stock_data['Volume'].pct_change() * 100 | |
average_volume = stock_data['Volume'].mean() | |
current_volume = stock_data['Volume'].iloc[-1] | |
volume_trend = 'increasing' if current_volume > average_volume else 'decreasing' | |
st.subheader(f"📊 Volume Analysis for {company_name}") | |
st.write(f"- **Average Volume:** {average_volume:,.0f}") | |
st.write(f"- **Current Volume:** {current_volume:,.0f}") | |
st.write(f"- **Volume is currently {volume_trend}.**") | |
# Convert data to CSV | |
csv = stock_data.to_csv(index=False).encode('utf-8') | |
# Download button for CSV | |
st.download_button( | |
label=f"📥 Download {company_name} Data as CSV", | |
data=csv, | |
file_name=f"{ticker}_{start_date}_{end_date}.csv", | |
mime='text/csv' | |
) | |
else: | |
st.warning("⚠️ Please select at least one stock.") | |
except Exception as e: | |
st.error(f"❌ Error processing the uploaded file: {e}") | |
else: | |
st.info("🔍 Please upload your `stock_list.csv` file to get started.") | |
st.markdown(""" | |
**Sample `stock_list.csv` Format:** | |
```csv | |
Symbol,Company Name | |
TCS,Tata Consultancy Services | |
INFY,Infosys Limited | |
RELIANCE,Reliance Industries | |
HDFCBANK,HDFC Bank | |
ICICIBANK,ICICI Bank | |
``` | |
You can create your own CSV file following the above format. Ensure that the CSV has headers `Symbol` and `Company Name`. | |
**Sample CSV Download:** | |
[Download Sample `stock_list.csv`](https://huggingface.co/spaces/riteshcp/Indian_Futures_OBV_Downloader/resolve/main/sample_stock_list.csv) | |
""") | |