Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
import datetime as dt
|
5 |
+
import yfinance as yf
|
6 |
+
import ta
|
7 |
+
|
8 |
+
# List of all futures stocks
|
9 |
+
futures_stocks = [
|
10 |
+
"ACC.NS", "ADANIENT.NS", "ADANIPORTS.NS", "AMBUJACEM.NS", "APOLLOHOSP.NS",
|
11 |
+
"ASHOKLEY.NS", "ASIANPAINT.NS", "AXISBANK.NS", "BAJAJ-AUTO.NS", "BAJFINANCE.NS",
|
12 |
+
"BAJAJFINSV.NS", "BALKRISIND.NS", "BANDHANBNK.NS", "BANKBARODA.NS", "BANKINDIA.NS",
|
13 |
+
"BERGEPAINT.NS", "BHARATFORG.NS", "BPCL.NS", "BHARTIARTL.NS", "BIOCON.NS",
|
14 |
+
"BOSCHLTD.NS", "BRITANNIA.NS", "CANBK.NS", "CHOLAFIN.NS", "CIPLA.NS",
|
15 |
+
"COALINDIA.NS", "CONCOR.NS", "CUMMINSIND.NS", "DABUR.NS", "DELTACORP.NS",
|
16 |
+
"DIVISLAB.NS", "DLF.NS", "DRREDDY.NS", "EICHERMOT.NS", "FEDERALBNK.NS",
|
17 |
+
"GAIL.NS", "GLENMARK.NS", "GMRINFRA.NS", "GODREJCP.NS", "GODREJPROP.NS",
|
18 |
+
"GRANULES.NS", "GRASIM.NS", "HAVELLS.NS", "HCLTECH.NS",
|
19 |
+
"HDFCAMC.NS", "HDFCBANK.NS", "HDFCLIFE.NS", "HEROMOTOCO.NS", "HINDALCO.NS",
|
20 |
+
"HINDCOPPER.NS", "HINDPETRO.NS", "HINDUNILVR.NS", "IBULHSGFIN.NS", "ICICIBANK.NS",
|
21 |
+
"ICICIGI.NS", "ICICIPRULI.NS", "IDEA.NS", "IDFCFIRSTB.NS", "INDHOTEL.NS",
|
22 |
+
"INDIACEM.NS", "INDIAMART.NS", "INDIGO.NS", "INDUSINDBK.NS", "INFY.NS",
|
23 |
+
"IRCTC.NS", "ITC.NS", "JINDALSTEL.NS", "JSWSTEEL.NS", "JUBLFOOD.NS",
|
24 |
+
"KOTAKBANK.NS", "L&TFH.NS", "LALPATHLAB.NS", "LAURUSLABS.NS", "LICHSGFIN.NS",
|
25 |
+
"LT.NS", "LUPIN.NS", "M&M.NS", "M&MFIN.NS", "MANAPPURAM.NS",
|
26 |
+
"MARICO.NS", "MARUTI.NS", "MCDOWELL-N.NS", "MCX.NS", "METROPOLIS.NS",
|
27 |
+
"MFSL.NS", "MGL.NS", "MINDTREE.NS", "MOTHERSUMI.NS", "MPHASIS.NS",
|
28 |
+
"MRF.NS", "MUTHOOTFIN.NS", "NAM-INDIA.NS", "NATIONALUM.NS", "NAUKRI.NS",
|
29 |
+
"NAVINFLUOR.NS", "NBCC.NS", "NESTLEIND.NS", "NMDC.NS", "NTPC.NS",
|
30 |
+
"ONGC.NS", "PAGEIND.NS", "PEL.NS", "PETRONET.NS", "PFC.NS",
|
31 |
+
"PFIZER.NS", "PIDILITIND.NS", "PIIND.NS", "PNB.NS", "POLYCAB.NS",
|
32 |
+
"POWERGRID.NS", "PVR.NS", "RAMCOCEM.NS", "RBLBANK.NS", "RECLTD.NS",
|
33 |
+
"RELIANCE.NS", "SAIL.NS", "SBICARD.NS", "SBILIFE.NS", "SBIN.NS",
|
34 |
+
"SHREECEM.NS", "SIEMENS.NS", "SRF.NS", "SRTRANSFIN.NS", "SUNPHARMA.NS",
|
35 |
+
"SUNTV.NS", "TATACHEM.NS", "TATACOMM.NS", "TATACONSUM.NS", "TATAMOTORS.NS",
|
36 |
+
"TATAPOWER.NS", "TATASTEEL.NS", "TCS.NS", "TECHM.NS", "TITAN.NS",
|
37 |
+
"TORNTPHARM.NS", "TORNTPOWER.NS", "TRENT.NS", "TVSMOTOR.NS", "UBL.NS",
|
38 |
+
"ULTRACEMCO.NS", "UPL.NS", "VBL.NS", "VEDL.NS", "VOLTAS.NS",
|
39 |
+
"WHIRLPOOL.NS", "WIPRO.NS", "ZEEL.NS"
|
40 |
+
]
|
41 |
+
|
42 |
+
|
43 |
+
@st.cache_data
|
44 |
+
def fetch_and_calculate_indicators(ticker, start_date, end_date, rsi_length, stoch_length, k_period, d_period):
|
45 |
+
try:
|
46 |
+
df = yf.download(ticker, start=start_date, end=end_date)
|
47 |
+
|
48 |
+
if df.empty:
|
49 |
+
return None
|
50 |
+
|
51 |
+
# Calculate RSI
|
52 |
+
df['RSI'] = ta.momentum.RSIIndicator(df['Close'], window=rsi_length).rsi()
|
53 |
+
|
54 |
+
# Calculate Stochastic RSI
|
55 |
+
stoch_rsi = ta.momentum.StochRSIIndicator(
|
56 |
+
df['Close'],
|
57 |
+
window=stoch_length,
|
58 |
+
smooth1=k_period,
|
59 |
+
smooth2=d_period
|
60 |
+
)
|
61 |
+
df['StochRSI_K'] = stoch_rsi.stochrsi_k()
|
62 |
+
df['StochRSI_D'] = stoch_rsi.stochrsi_d()
|
63 |
+
|
64 |
+
# Calculate OBV
|
65 |
+
df['OBV'] = ta.volume.OnBalanceVolumeIndicator(df['Close'], df['Volume']).on_balance_volume()
|
66 |
+
|
67 |
+
if df['RSI'].isna().all() or df['StochRSI_K'].isna().all() or df['OBV'].isna().all():
|
68 |
+
return None
|
69 |
+
|
70 |
+
return df
|
71 |
+
except Exception as e:
|
72 |
+
st.error(f"Error occurred for {ticker}: {str(e)}")
|
73 |
+
return None
|
74 |
+
|
75 |
+
def filter_stocks_by_indicators(stocks, start_date, end_date, rsi_threshold, stoch_rsi_threshold, obv_threshold, indicator_choice, rsi_length, stoch_length, k_period, d_period):
|
76 |
+
filtered_stocks = []
|
77 |
+
progress_bar = st.progress(0)
|
78 |
+
for i, ticker in enumerate(stocks):
|
79 |
+
df = fetch_and_calculate_indicators(ticker, start_date, end_date, rsi_length, stoch_length, k_period, d_period)
|
80 |
+
if df is not None and not df['RSI'].empty and not df['StochRSI_K'].empty and not df['OBV'].empty:
|
81 |
+
rsi_value = df['RSI'].iloc[-1]
|
82 |
+
stoch_rsi_value = df['StochRSI_K'].iloc[-1]
|
83 |
+
obv_value = df['OBV'].iloc[-1]
|
84 |
+
obv_change = (df['OBV'].iloc[-1] - df['OBV'].iloc[-2]) / df['OBV'].iloc[-2] * 100 # Percentage change
|
85 |
+
|
86 |
+
if not pd.isna(rsi_value) and not pd.isna(stoch_rsi_value) and not pd.isna(obv_value):
|
87 |
+
if (indicator_choice == 'RSI' and rsi_value < rsi_threshold) or \
|
88 |
+
(indicator_choice == 'StochRSI' and stoch_rsi_value < stoch_rsi_threshold) or \
|
89 |
+
(indicator_choice == 'OBV' and obv_change > obv_threshold) or \
|
90 |
+
(indicator_choice == 'All' and rsi_value < rsi_threshold and stoch_rsi_value < stoch_rsi_threshold and obv_change > obv_threshold):
|
91 |
+
filtered_stocks.append(ticker)
|
92 |
+
st.info(f"{ticker} - RSI: {rsi_value:.2f}, StochRSI: {stoch_rsi_value:.2f}, OBV Change: {obv_change:.2f}%")
|
93 |
+
else:
|
94 |
+
st.info(f"{ticker} - RSI: {rsi_value:.2f}, StochRSI: {stoch_rsi_value:.2f}, OBV Change: {obv_change:.2f}% (doesn't meet criteria)")
|
95 |
+
else:
|
96 |
+
st.info(f"{ticker} has NaN value for RSI, StochRSI, or OBV.")
|
97 |
+
else:
|
98 |
+
st.info(f"No valid data available for {ticker}.")
|
99 |
+
progress_bar.progress((i + 1) / len(stocks))
|
100 |
+
return filtered_stocks
|
101 |
+
|
102 |
+
def main():
|
103 |
+
st.title("Indian Stock Market Scanner: RSI, Stochastic RSI, and OBV")
|
104 |
+
|
105 |
+
start_date = st.date_input("Start Date", dt.date(2020, 1, 1))
|
106 |
+
end_date = st.date_input("End Date", dt.date.today())
|
107 |
+
|
108 |
+
indicator_choice = st.radio("Choose Indicator", ('RSI', 'StochRSI', 'OBV', 'All'))
|
109 |
+
|
110 |
+
col1, col2, col3 = st.columns(3)
|
111 |
+
|
112 |
+
with col1:
|
113 |
+
rsi_threshold = st.slider("RSI Threshold", 0, 100, 30)
|
114 |
+
rsi_length = st.number_input("RSI Length", value=14, min_value=1, max_value=100)
|
115 |
+
|
116 |
+
with col2:
|
117 |
+
stoch_rsi_threshold = st.slider("Stochastic RSI Threshold", 0.0, 1.0, 0.2)
|
118 |
+
stoch_length = st.number_input("Stochastic Length", value=14, min_value=1, max_value=100)
|
119 |
+
|
120 |
+
with col3:
|
121 |
+
obv_threshold = st.slider("OBV Change Threshold (%)", -10.0, 10.0, 2.0)
|
122 |
+
k_period = st.number_input("K Period", value=3, min_value=1, max_value=20)
|
123 |
+
d_period = st.number_input("D Period", value=3, min_value=1, max_value=20)
|
124 |
+
|
125 |
+
output_filename = st.text_input("Enter output filename (with .csv extension):", "filtered_stocks.csv")
|
126 |
+
|
127 |
+
if st.button("Start Scanning", key="start_scanning_button"):
|
128 |
+
st.write(f"Scanning for stocks based on selected criteria...")
|
129 |
+
filtered_stocks = filter_stocks_by_indicators(
|
130 |
+
futures_stocks, start_date, end_date, rsi_threshold, stoch_rsi_threshold, obv_threshold,
|
131 |
+
indicator_choice, rsi_length, stoch_length, k_period, d_period
|
132 |
+
)
|
133 |
+
|
134 |
+
if filtered_stocks:
|
135 |
+
st.success(f"Found {len(filtered_stocks)} stocks meeting the criteria:")
|
136 |
+
st.write(filtered_stocks)
|
137 |
+
df = pd.DataFrame(filtered_stocks, columns=["Ticker"])
|
138 |
+
st.download_button(
|
139 |
+
label="Download CSV",
|
140 |
+
data=df.to_csv(index=False).encode('utf-8'),
|
141 |
+
file_name=output_filename,
|
142 |
+
mime='text/csv',
|
143 |
+
)
|
144 |
+
st.success(f"Click the button above to download the filtered stocks.")
|
145 |
+
else:
|
146 |
+
st.warning(f"No stocks found meeting the selected criteria.")
|
147 |
+
|
148 |
+
if __name__ == "__main__":
|
149 |
+
main()
|