|
|
|
|
|
import gradio as gr |
|
import pandas as pd |
|
import yfinance as yf |
|
from datetime import datetime |
|
import plotly.graph_objects as go |
|
import numpy as np |
|
|
|
|
|
|
|
def calculate_sma(df, window): |
|
return df['Close'].rolling(window=window).mean() |
|
|
|
def calculate_ema(df, window): |
|
return df['Close'].ewm(span=window, adjust=False).mean() |
|
|
|
|
|
def calculate_macd(df): |
|
short_ema = df['Close'].ewm(span=12, adjust=False).mean() |
|
long_ema = df['Close'].ewm(span=26, adjust=False).mean() |
|
macd = short_ema - long_ema |
|
signal = macd.ewm(span=9, adjust=False).mean() |
|
return macd, signal |
|
|
|
|
|
def calculate_rsi(df): |
|
delta = df['Close'].diff() |
|
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean() |
|
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean() |
|
rs = gain / loss |
|
rsi = 100 - (100 / (1 + rs)) |
|
return rsi |
|
|
|
def calculate_bollinger_bands(df): |
|
middle_bb = df['Close'].rolling(window=20).mean() |
|
upper_bb = middle_bb + 2 * df['Close'].rolling(window=20).std() |
|
lower_bb = middle_bb - 2 * df['Close'].rolling(window=20).std() |
|
return middle_bb, upper_bb, lower_bb |
|
|
|
def calculate_stochastic_oscillator(df): |
|
lowest_low = df['Low'].rolling(window=14).min() |
|
highest_high = df['High'].rolling(window=14).max() |
|
slowk = ((df['Close'] - lowest_low) / (highest_high - lowest_low)) * 100 |
|
slowd = slowk.rolling(window=3).mean() |
|
return slowk, slowd |
|
|
|
|
|
|
|
def calculate_cmf(df, window=20): |
|
mfv = ((df['Close'] - df['Low']) - (df['High'] - df['Close'])) / (df['High'] - df['Low']) * df['Volume'] |
|
cmf = mfv.rolling(window=window).sum() / df['Volume'].rolling(window=window).sum() |
|
return cmf |
|
|
|
def calculate_cci(df, window=20): |
|
"""Calculate Commodity Channel Index (CCI).""" |
|
typical_price = (df['High'] + df['Low'] + df['Close']) / 3 |
|
sma = typical_price.rolling(window=window).mean() |
|
mean_deviation = (typical_price - sma).abs().rolling(window=window).mean() |
|
cci = (typical_price - sma) / (0.015 * mean_deviation) |
|
return cci |
|
|
|
|
|
|
|
def generate_trading_signals(df): |
|
|
|
df['SMA_30'] = calculate_sma(df, 30) |
|
df['SMA_100'] = calculate_sma(df, 100) |
|
df['EMA_12'] = calculate_ema(df, 12) |
|
df['EMA_26'] = calculate_ema(df, 26) |
|
df['RSI'] = calculate_rsi(df) |
|
df['MiddleBB'], df['UpperBB'], df['LowerBB'] = calculate_bollinger_bands(df) |
|
df['SlowK'], df['SlowD'] = calculate_stochastic_oscillator(df) |
|
df['CMF'] = calculate_cmf(df) |
|
df['CCI'] = calculate_cci(df) |
|
|
|
|
|
|
|
|
|
df['SMA_Signal'] = np.where(df['SMA_30'] > df['SMA_100'], 1, 0) |
|
|
|
macd, signal = calculate_macd(df) |
|
df['MACD_Signal'] = np.select([(macd > signal) & (macd.shift(1) <= signal.shift(1)), |
|
(macd < signal) & (macd.shift(1) >= signal.shift(1))],[1, -1], default=0) |
|
|
|
|
|
|
|
df['RSI_Signal'] = np.where(df['RSI'] < 20, 1, 0) |
|
df['RSI_Signal'] = np.where(df['RSI'] > 90, -1, df['RSI_Signal']) |
|
|
|
df['BB_Signal'] = np.where(df['Close'] < df['LowerBB'], 0, 0) |
|
df['BB_Signal'] = np.where(df['Close'] > df['UpperBB'], -1, df['BB_Signal']) |
|
|
|
df['Stochastic_Signal'] = np.where((df['SlowK'] < 10) & (df['SlowD'] < 15), 1, 0) |
|
df['Stochastic_Signal'] = np.where((df['SlowK'] > 90) & (df['SlowD'] > 85), -1, df['Stochastic_Signal']) |
|
|
|
|
|
df['CMF_Signal'] = np.where(df['CMF'] > 0.3, -1, np.where(df['CMF'] < -0.3, 1, 0)) |
|
|
|
|
|
df['CCI_Signal'] = np.where(df['CCI'] < -180, 1, 0) |
|
df['CCI_Signal'] = np.where(df['CCI'] > 150, -1, df['CCI_Signal']) |
|
|
|
|
|
|
|
|
|
df['Combined_Signal'] = df[['RSI_Signal', 'BB_Signal', |
|
'Stochastic_Signal', 'CMF_Signal', |
|
'CCI_Signal']].sum(axis=1) |
|
|
|
return df |
|
|
|
|
|
|
|
def plot_combined_signals(df, ticker): |
|
|
|
fig = go.Figure() |
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
x=df.index, y=df['Close'], |
|
mode='lines', |
|
name='Closing Price', |
|
line=dict(color='lightcoral', width=2) |
|
)) |
|
|
|
|
|
buy_signals = df[df['Combined_Signal'] >= 3] |
|
fig.add_trace(go.Scatter( |
|
x=buy_signals.index, y=buy_signals['Close'], |
|
mode='markers', |
|
marker=dict(symbol='triangle-up', size=10, color='lightgreen'), |
|
name='Buy Signal' |
|
)) |
|
|
|
|
|
sell_signals = df[df['Combined_Signal'] <= -3] |
|
fig.add_trace(go.Scatter( |
|
x=sell_signals.index, y=sell_signals['Close'], |
|
mode='markers', |
|
marker=dict(symbol='triangle-down', size=10, color='lightsalmon'), |
|
name='Sell Signal' |
|
)) |
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
x=df.index, y=df['Combined_Signal'], |
|
mode='lines', |
|
name='Combined Signal', |
|
line=dict(color='deepskyblue', width=2), |
|
yaxis='y2' |
|
)) |
|
|
|
|
|
fig.update_layout( |
|
title=f'{ticker}: Stock Price and Combined Trading Signal (Last 120 Days)', |
|
xaxis=dict(title='Date'), |
|
yaxis=dict(title='Price', side='left'), |
|
yaxis2=dict(title='Combined Signal', overlaying='y', side='right', showgrid=False), |
|
plot_bgcolor='black', |
|
paper_bgcolor='black', |
|
font=dict(color='white') |
|
) |
|
|
|
return fig |
|
|
|
|
|
|
|
|
|
|
|
def plot_individual_signals(df, ticker): |
|
|
|
fig = go.Figure() |
|
fig.add_trace(go.Scatter( |
|
x=df.index, y=df['Close'], |
|
mode='lines', |
|
name='Closing Price', |
|
line=dict(color='lightcoral', width=2) |
|
)) |
|
|
|
|
|
signal_names = ['RSI_Signal', 'BB_Signal', |
|
'Stochastic_Signal', 'CMF_Signal', |
|
'CCI_Signal'] |
|
|
|
for signal in signal_names: |
|
buy_signals = df[df[signal] == 1] |
|
sell_signals = df[df[signal] == -1] |
|
|
|
fig.add_trace(go.Scatter( |
|
x=buy_signals.index, y=buy_signals['Close'], |
|
mode='markers', |
|
marker=dict(symbol='triangle-up', size=10, color='lightgreen'), |
|
name=f'{signal} Buy Signal' |
|
)) |
|
|
|
fig.add_trace(go.Scatter( |
|
x=sell_signals.index, y=sell_signals['Close'], |
|
mode='markers', |
|
marker=dict(symbol='triangle-down', size=10, color='lightsalmon'), |
|
name=f'{signal} Sell Signal' |
|
)) |
|
|
|
fig.update_layout( |
|
title=f'{ticker}: Individual Trading Signals', |
|
xaxis=dict(title='Date'), |
|
yaxis=dict(title='Price', side='left'), |
|
plot_bgcolor='black', |
|
paper_bgcolor='black', |
|
font=dict(color='white') |
|
) |
|
|
|
return fig |
|
|
|
|
|
def display_signals(df): |
|
|
|
signals_df = df[['Close', 'SMA_Signal', 'MACD_Signal', 'RSI_Signal', |
|
'BB_Signal', 'Stochastic_Signal', |
|
'CMF_Signal', 'CCI_Signal']].copy() |
|
|
|
|
|
signals_df.index.name = 'Date' |
|
|
|
|
|
for column in signals_df.columns: |
|
signals_df[column] = signals_df[column].replace( |
|
{1: 'Buy', -1: 'Sell', 0: 'Hold'} |
|
) |
|
|
|
return signals_df |
|
|
|
def stock_analysis(ticker, start_date, end_date): |
|
|
|
df = yf.download(ticker, start=start_date, end=end_date) |
|
|
|
|
|
if isinstance(df.columns, pd.MultiIndex): |
|
df.columns = df.columns.droplevel(level=1) |
|
|
|
|
|
df.columns = ['Close', 'High', 'Low', 'Open', 'Volume'] |
|
|
|
|
|
df = generate_trading_signals(df) |
|
|
|
|
|
df_last_60 = df.tail(120) |
|
|
|
|
|
fig_signals = plot_combined_signals(df_last_60, ticker) |
|
|
|
|
|
fig_individual_signals = plot_individual_signals(df_last_60, ticker) |
|
|
|
|
|
signals_df = df_last_60[['Close', 'SMA_Signal', 'MACD_Signal', 'RSI_Signal', 'BB_Signal', |
|
'Stochastic_Signal','CMF_Signal', |
|
'CCI_Signal']] |
|
|
|
return fig_signals, fig_individual_signals |
|
|
|
|
|
|
|
|
|
|
|
with gr.Blocks() as demo: |
|
gr.Markdown("## Stock Market Analysis App") |
|
|
|
ticker_input = gr.Textbox(label="Enter Stock Ticker (e.g., AAPL, NVDA)", value="NVDA") |
|
start_date_input = gr.Textbox(label="Start Date (YYYY-MM-DD)", value="2022-01-01") |
|
end_date_input = gr.Textbox(label="End Date (YYYY-MM-DD)", value="2026-01-01") |
|
|
|
|
|
button = gr.Button("Analyze Stock") |
|
|
|
|
|
combined_signals_output = gr.Plot(label="Combined Trading Signals") |
|
individual_signals_output = gr.Plot(label="Individual Trading Signals") |
|
|
|
|
|
|
|
button.click(stock_analysis, inputs=[ticker_input, start_date_input, end_date_input], |
|
outputs=[combined_signals_output, individual_signals_output]) |
|
|
|
|
|
demo.launch() |