Sanyog Chavhan commited on
Commit
e74d865
Β·
1 Parent(s): cb9f190

Pushing only required files, removed sensitive files

Browse files
Files changed (6) hide show
  1. ai_insights.py +34 -0
  2. app.py +89 -0
  3. data_loader.py +31 -0
  4. indicators.py +111 -0
  5. portfolio_optimiser.py +67 -0
  6. requirements.txt +8 -0
ai_insights.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import openai
2
+ import streamlit as st
3
+ import os
4
+
5
+ def get_ai_insight(indicator_name, indicator_value, api_key):
6
+ """
7
+ Generates an AI-driven insight for a given indicator and its value using LLM.
8
+
9
+ Args:
10
+ indicator_name (str): Name of the technical indicator.
11
+ indicator_value (float): Value of the technical indicator.
12
+ api_key(str) : API key for accessing the LLM
13
+
14
+ Returns:
15
+ str: AI-generated insight.
16
+ """
17
+ client = openai.OpenAI(api_key = api_key)
18
+ try:
19
+ prompt = f"Explain the significance of a {indicator_name} value of {indicator_value} in stock trading in one sentence."
20
+ response = client.chat.completions.create(
21
+ model="gpt-4-turbo", # Use "gpt-3.5-turbo" for faster and cheaper results
22
+ messages=[{"role": "system", "content": "You are a stock trading expert."},
23
+ {"role": "user", "content": prompt}],
24
+ temperature=0.5 # Moderate creativity
25
+ )
26
+
27
+ return response.choices[0].message.content.strip()
28
+
29
+ except openai.AuthenticationError:
30
+ return "Invalid OpenAI API key. Please check your API key in Streamlit secrets."
31
+ except openai.RateLimitError:
32
+ return "OpenAI API rate limit exceeded. Try again later."
33
+ except Exception as e:
34
+ return f"Error generating AI insight: {e}"
app.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import plotly.express as px
4
+ from data_loader import load_data
5
+ from indicators import calculate_indicators
6
+ from ai_insights import get_ai_insight
7
+ from portfolio_optimiser import optimize_portfolio
8
+ import os
9
+
10
+ # Streamlit UI
11
+ st.title("AI-Based Stock Analysis and Portfolio Optimisation")
12
+
13
+ # User Inputs
14
+ tickers = st.text_input("Enter stock tickers (comma-separated, e.g., TSLA, AAPL, MSFT):", "TSLA, AAPL")
15
+ tickers = [ticker.strip().upper() for ticker in tickers.split(",")]
16
+
17
+ start_date = st.date_input("Start Date:", value=pd.to_datetime("2024-01-01"))
18
+ end_date = st.date_input("End Date:", value=pd.to_datetime("2025-01-01"))
19
+
20
+ ma_window = st.slider("Moving Average Window:", min_value=5, max_value=50, value=20)
21
+ rsi_window = st.slider("RSI Window:", min_value=5, max_value=30, value=14)
22
+ macd_fast = st.slider("MACD Fast Period:", min_value=5, max_value=30, value=12)
23
+ macd_slow = st.slider("MACD Slow Period:", min_value=20, max_value=50, value=26)
24
+ macd_signal = st.slider("MACD Signal Period:", min_value=5, max_value=20, value=9)
25
+
26
+ risk_tolerance = st.slider("Risk Tolerance (0-1):", min_value=0.0, max_value=1.0, value=0.5, step=0.01)
27
+
28
+
29
+ def calculate_and_return_everything(data):
30
+ # Calculate Indicators
31
+ indicator_data = calculate_indicators(data, ma_window, rsi_window, macd_fast, macd_slow, macd_signal)
32
+ if indicator_data is None:
33
+ st.stop()
34
+
35
+ # **AI Insights**
36
+ st.subheader("πŸ’‘ AI Insights")
37
+ last_row = indicator_data.iloc[-1]
38
+
39
+ api_key = st.secrets["OPENAI_API_KEY"] # Ensure API key is set
40
+
41
+ # Get AI insights for each indicator
42
+ ma_insight = get_ai_insight("Moving Average", last_row['MA'], api_key)
43
+ bb_insight = get_ai_insight("Bollinger Bands", f"Upper: {last_row['Upper_BB']}, Lower: {last_row['Lower_BB']}", api_key)
44
+ rsi_insight = get_ai_insight("RSI", last_row['RSI'], api_key)
45
+ macd_insight = get_ai_insight("MACD", last_row['MACD'], api_key)
46
+
47
+ st.write(f"Moving Average: {ma_insight}")
48
+ st.write(f"Bollinger Bands: {bb_insight}")
49
+ st.write(f"RSI: {rsi_insight}")
50
+ st.write(f"MACD: {macd_insight}")
51
+
52
+ return indicator_data
53
+
54
+
55
+
56
+ if st.button("Analyse"):
57
+ all_data = {}
58
+ for i in tickers:
59
+ # Load Data
60
+ data = load_data(i, start_date, end_date) # Passing ticker instead of tickers
61
+ if data is None:
62
+ st.warning(f"No data found for {i}. Skipping...")
63
+ continue
64
+ st.subheader(f'Ticker: {i}')
65
+ id = calculate_and_return_everything(data)
66
+ if id is not None:
67
+ all_data[i] = id
68
+
69
+ if not all_data:
70
+ st.warning("No valid data available for any of the tickers. Exiting analysis.")
71
+ st.stop()
72
+
73
+ # πŸ“Œ **Portfolio Optimisation**
74
+ st.subheader("Portfolio Optimisation")
75
+
76
+ # Extract closing prices for portfolio optimization
77
+ close_prices = pd.DataFrame({ticker: all_data[ticker]['Close'] for ticker in all_data})
78
+
79
+ print(close_prices.columns)
80
+
81
+ asset_weights = optimize_portfolio(close_prices, risk_tolerance)
82
+ if asset_weights:
83
+ st.write("### Optimised Asset Allocation:")
84
+ for ticker, weight in asset_weights.items():
85
+ st.write(f"**{ticker}:** {weight:.4f}")
86
+ else:
87
+ st.write("Could not perform portfolio optimisation.")
88
+
89
+ print("Ensure that you have your OpenAI API set as environmental variable in Hugging Face Spaces for this script to work")
data_loader.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import yfinance as yf
2
+ import pandas as pd
3
+ import streamlit as st
4
+
5
+ def load_data(ticker, start_date, end_date):
6
+ """
7
+ Loads stock data from Yahoo Finance for a single ticker and date range.
8
+
9
+ Args:
10
+ ticker (str): Stock ticker symbol.
11
+ start_date (str): Start date in YYYY-MM-DD format.
12
+ end_date (str): End date in YYYY-MM-DD format.
13
+
14
+ Returns:
15
+ pandas.DataFrame: DataFrame containing stock data for the ticker.
16
+ """
17
+ try:
18
+ data = yf.download(ticker, start=start_date, end=end_date)
19
+ # Flatten the MultiIndex columns
20
+ data.columns = data.columns.droplevel(1) # Drop the second level (ticker name)
21
+ data.columns.name = None # Remove the column name
22
+
23
+ if data is None or data.empty:
24
+ st.error(f"No data found for ticker: {ticker}")
25
+ return None
26
+
27
+ return data
28
+
29
+ except Exception as e:
30
+ st.error(f"Error loading data: {e}")
31
+ return None
indicators.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import pandas_ta as ta
3
+ import plotly.graph_objects as go
4
+ import streamlit as st
5
+ from ai_insights import get_ai_insight # Import AI insight function
6
+
7
+ def calculate_indicators(data, ma_window, rsi_window, macd_fast, macd_slow, macd_signal):
8
+ """
9
+ Calculates Moving Average (MA), Bollinger Bands, RSI, MACD, and displays them in a 2x2 grid in Streamlit.
10
+ Also includes AI-generated insights next to each chart.
11
+
12
+ Args:
13
+ data (pandas.DataFrame): Stock data.
14
+ ma_window (int): Moving Average window.
15
+ rsi_window (int): RSI window.
16
+ macd_fast (int): MACD Fast period.
17
+ macd_slow (int): MACD Slow period.
18
+ macd_signal (int): MACD Signal period.
19
+
20
+ Returns:
21
+ pandas.DataFrame: Data with calculated indicators.
22
+ """
23
+
24
+ # πŸ“Œ **Calculate Moving Average (MA)**
25
+ data['MA'] = data['Close'].rolling(window=ma_window).mean()
26
+
27
+ # πŸ“Œ **Calculate Bollinger Bands**
28
+ data['stddev'] = data['Close'].rolling(window=ma_window).std()
29
+ data['Upper_BB'] = data['MA'] + (2 * data['stddev'])
30
+ data['Lower_BB'] = data['MA'] - (2 * data['stddev'])
31
+
32
+ # πŸ“Œ **Calculate RSI**
33
+ delta = data['Close'].diff(1)
34
+ gain = (delta.where(delta > 0, 0)).rolling(window=rsi_window).mean()
35
+ loss = (-delta.where(delta < 0, 0)).rolling(window=rsi_window).mean()
36
+ rs = gain / loss
37
+ data['RSI'] = 100 - (100 / (1 + rs))
38
+
39
+ # πŸ“Œ **Calculate MACD**
40
+ data['MACD'] = data['Close'].ewm(span=macd_fast, adjust=False).mean() - data['Close'].ewm(span=macd_slow, adjust=False).mean()
41
+ data['MACD_Signal'] = data['MACD'].ewm(span=macd_signal, adjust=False).mean()
42
+ data['MACD_Hist'] = data['MACD'] - data['MACD_Signal']
43
+
44
+ # πŸ“Œ **Create Candlestick Chart with Bollinger Bands & MA**
45
+ fig_candlestick = go.Figure()
46
+ fig_candlestick.add_trace(go.Candlestick(
47
+ x=data.index,
48
+ open=data['Open'], high=data['High'],
49
+ low=data['Low'], close=data['Close'],
50
+ name="Candlestick",
51
+ increasing_line_color="green",
52
+ decreasing_line_color="red"
53
+ ))
54
+ fig_candlestick.add_trace(go.Scatter(x=data.index, y=data['MA'], mode='lines', name=f"{ma_window}-day MA", line=dict(color="blue")))
55
+ fig_candlestick.add_trace(go.Scatter(x=data.index, y=data['Upper_BB'], mode='lines', name="Upper BB", line=dict(color="purple", dash='dot')))
56
+ fig_candlestick.add_trace(go.Scatter(x=data.index, y=data['Lower_BB'], mode='lines', name="Lower BB", line=dict(color="purple", dash='dot')))
57
+ fig_candlestick.update_layout(title="πŸ“ˆ Stock Price with Bollinger Bands & MA", xaxis_rangeslider_visible=True, template="plotly_dark", height=500, width=900)
58
+
59
+ # πŸ“Œ **Create RSI Chart**
60
+ fig_rsi = go.Figure()
61
+ fig_rsi.add_trace(go.Scatter(x=data.index, y=data['RSI'], mode='lines', name="RSI", line=dict(color="orange")))
62
+ fig_rsi.add_trace(go.Scatter(x=data.index, y=[70]*len(data), mode='lines', name="Overbought (70)", line=dict(color="red", dash='dash')))
63
+ fig_rsi.add_trace(go.Scatter(x=data.index, y=[30]*len(data), mode='lines', name="Oversold (30)", line=dict(color="green", dash='dash')))
64
+ fig_rsi.update_layout(title="πŸ“Š Relative Strength Index (RSI)", xaxis_rangeslider_visible=False, template="plotly_dark", height=500, width=900)
65
+
66
+ # πŸ“Œ **Create MACD Chart**
67
+ fig_macd = go.Figure()
68
+ fig_macd.add_trace(go.Scatter(x=data.index, y=data['MACD'], mode='lines', name="MACD", line=dict(color="blue")))
69
+ fig_macd.add_trace(go.Scatter(x=data.index, y=data['MACD_Signal'], mode='lines', name="MACD Signal", line=dict(color="red")))
70
+ fig_macd.add_trace(go.Bar(x=data.index, y=data['MACD_Hist'], name="MACD Histogram", marker_color="purple"))
71
+ fig_macd.update_layout(title="πŸ“‰ MACD Indicator", xaxis_rangeslider_visible=False, template="plotly_dark", height=500, width=900)
72
+
73
+ # πŸ“Œ **Display Charts One Below Another**
74
+ st.subheader("πŸ“ˆ Stock Price with Bollinger Bands & Moving Averages")
75
+ st.plotly_chart(fig_candlestick, use_container_width=True) # πŸ“Š Stock Chart
76
+
77
+ st.subheader("πŸ“Š Relative Strength Index (RSI)")
78
+ st.plotly_chart(fig_rsi, use_container_width=True) # πŸ“Š RSI Chart
79
+
80
+ st.subheader("πŸ“‰ MACD Indicator")
81
+ st.plotly_chart(fig_macd, use_container_width=True) # πŸ“‰ MACD Chart
82
+
83
+
84
+ print(data.columns)
85
+
86
+ '''
87
+ # πŸ“Œ **Display Charts and AI Insights in a 2x2 Grid**
88
+ col1, col2 = st.columns((7, 1.5)) # 2/3 chart, 1/3 AI insight
89
+ with col1:
90
+ st.plotly_chart(fig_candlestick, use_container_width=True) # πŸ“ˆ Stock Price
91
+ with col2:
92
+ st.subheader("πŸ’‘ AI Insight for Moving Average")
93
+ st.write(ma_insight)
94
+ st.subheader("πŸ’‘ AI Insight for Bollinger Bands")
95
+ st.write(bb_insight)
96
+
97
+ col3, col4 = st.columns((7, 1.5))
98
+ with col3:
99
+ st.plotly_chart(fig_rsi, use_container_width=True) # πŸ“Š RSI
100
+ with col4:
101
+ st.subheader("πŸ’‘ AI Insight for RSI")
102
+ st.write(rsi_insight)
103
+
104
+ col5, col6 = st.columns((7, 1.5))
105
+ with col5:
106
+ st.plotly_chart(fig_macd, use_container_width=True) # πŸ“‰ MACD
107
+ with col6:
108
+ st.subheader("πŸ’‘ AI Insight for MACD")
109
+ st.write(macd_insight)
110
+ '''
111
+ return data # βœ… Return only data, all charts & AI insights are displayed in Streamlit
portfolio_optimiser.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from scipy.optimize import minimize
4
+
5
+ def optimize_portfolio(data, risk_tolerance):
6
+ """
7
+ Performs Modern Portfolio Theory (MPT) optimization with diversification constraints.
8
+
9
+ Args:
10
+ data (pandas.DataFrame): DataFrame containing historical stock prices.
11
+ risk_tolerance (float): Risk tolerance level (higher value means higher risk).
12
+
13
+ Returns:
14
+ dict: Dictionary containing optimized asset weights for a diversified portfolio.
15
+ """
16
+ try:
17
+ # βœ… Calculate daily returns & covariance matrix
18
+ returns = data.pct_change().dropna()
19
+ mean_returns = returns.mean()
20
+ cov_matrix = returns.cov()
21
+
22
+ num_assets = len(mean_returns)
23
+ risk_free_rate = 0.01 # Example: Assume a 1% risk-free rate
24
+
25
+ # πŸ“Œ **Objective Function: Maximize Sharpe Ratio**
26
+ def negative_sharpe_ratio(weights):
27
+ portfolio_return = np.sum(mean_returns * weights)
28
+ portfolio_std_dev = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
29
+ sharpe_ratio = (portfolio_return - risk_free_rate) / portfolio_std_dev
30
+ return -sharpe_ratio # Negative because we minimize in SciPy
31
+
32
+ # πŸ“Œ **Constraints:**
33
+ constraints = [
34
+ {"type": "eq", "fun": lambda x: np.sum(x) - 1}, # Sum of weights must be 1
35
+ ]
36
+
37
+ # πŸ“Œ **Set Diversification Constraints**
38
+ max_allocation = 0.50 # Max 50% allocation per asset
39
+ min_assets_allocated = 2 # At least 2 assets must have nonzero allocation
40
+
41
+ def diversification_constraint(weights):
42
+ return np.count_nonzero(weights) - min_assets_allocated # Enforce minimum assets
43
+
44
+ constraints.append({"type": "ineq", "fun": diversification_constraint})
45
+
46
+ # πŸ“Œ **Set Bounds (Per-Asset Allocation Limits)**
47
+ bounds = tuple((0.05, max_allocation) for _ in range(num_assets)) # Min 5%, Max 50%
48
+
49
+ # πŸ“Œ **Initialize Equal Weights**
50
+ initial_weights = np.array([1 / num_assets] * num_assets)
51
+
52
+ # πŸ“Œ **Optimize Portfolio Allocation**
53
+ optimized_results = minimize(
54
+ negative_sharpe_ratio,
55
+ initial_weights,
56
+ method="SLSQP",
57
+ bounds=bounds,
58
+ constraints=constraints,
59
+ )
60
+
61
+ # βœ… Extract Optimal Weights
62
+ asset_weights = dict(zip(data.columns, optimized_results.x))
63
+ return asset_weights
64
+
65
+ except Exception as e:
66
+ print(f"Error optimizing portfolio: {e}")
67
+ return None
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ yfinance
3
+ pandas
4
+ numpy
5
+ scipy
6
+ plotly
7
+ openai
8
+ pandas-ta