omniverse1 commited on
Commit
db6d471
·
verified ·
1 Parent(s): 2e5e038

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +185 -127
app.py CHANGED
@@ -1,172 +1,230 @@
1
  import gradio as gr
 
2
  import yfinance as yf
3
  from utils import (
4
  calculate_technical_indicators,
5
  generate_trading_signals,
6
  get_fundamental_data,
 
7
  create_price_chart,
8
  create_technical_chart,
9
  create_prediction_chart,
10
- predict_prices,
11
  )
12
- import numpy as np
 
 
13
 
14
 
15
- def analyze_stock(symbol, mode, pred_days):
16
  try:
 
 
 
 
 
 
17
  stock = yf.Ticker(symbol)
18
- data = stock.history(period="1y")
19
 
20
  if data.empty:
21
- msg = f"No data found for {symbol}. Check the symbol or try adding .JK (e.g., ADRO.JK)"
22
- return (
23
- {"name": "N/A", "current_price": 0, "market_cap": 0, "pe_ratio": 0, "dividend_yield": 0, "volume": 0},
24
- {"overall": "N/A", "strength": 0, "support": 0, "resistance": 0, "stop_loss": 0, "details": msg},
25
- None, None, None, None
26
- )
27
 
28
  indicators = calculate_technical_indicators(data)
29
  signals = generate_trading_signals(data, indicators)
30
- fundamentals = get_fundamental_data(stock)
 
31
 
32
  fig_price = create_price_chart(data, indicators)
33
  fig_technical = create_technical_chart(data, indicators)
 
 
 
 
 
 
 
34
 
35
- if mode == "AI Prediction":
36
- prediction = predict_prices(data, prediction_days=pred_days)
37
- fig_prediction = create_prediction_chart(data, prediction)
38
- return fundamentals, signals, fig_price, fig_technical, fig_prediction, prediction
39
- else:
40
- return fundamentals, signals, fig_price, fig_technical, None, None
41
 
42
  except Exception as e:
43
- msg = f"Error analyzing {symbol}: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  return (
45
- {"name": "N/A", "current_price": 0, "market_cap": 0, "pe_ratio": 0, "dividend_yield": 0, "volume": 0},
46
- {"overall": "Error", "strength": 0, "support": 0, "resistance": 0, "stop_loss": 0, "details": msg},
47
- None, None, None, None
 
 
 
48
  )
49
 
50
-
51
- def format_fundamental_output(f):
52
- return f"""
53
- <div class="card">
54
- <h3>COMPANY FUNDAMENTALS</h3>
55
- <p><b>Name:</b> {f['name']}</p>
56
- <p><b>Current Price:</b> Rp{f['current_price']:,.2f}</p>
57
- <p><b>Market Cap:</b> {f['market_cap']:,}</p>
58
- <p><b>P/E Ratio:</b> {f['pe_ratio']:.2f}</p>
59
- <p><b>Dividend Yield:</b> {f['dividend_yield']:.2f}%</p>
60
- <p><b>Volume:</b> {f['volume']:,}</p>
61
- </div>
62
  """
63
 
 
 
 
64
 
65
- def format_signal_output(s):
66
- details = s.get("details", "")
67
- detail_list = details.split("\n") if details else []
68
- formatted_details = "<ul>" + "".join(f"<li>{d}</li>" for d in detail_list) + "</ul>"
69
- return f"""
70
- <div class="card">
71
- <h3>TECHNICAL SIGNAL SUMMARY</h3>
72
- <p><b>Overall Trend:</b> {s.get('overall','N/A')}</p>
73
- <p><b>Signal Strength:</b> {s.get('strength',0):.2f}%</p>
74
- <p><b>Support:</b> Rp{s.get('support',0):,.2f}</p>
75
- <p><b>Resistance:</b> Rp{s.get('resistance',0):,.2f}</p>
76
- <p><b>Stop Loss:</b> Rp{s.get('stop_loss',0):,.2f}</p>
77
- <h4>Detailed Signals:</h4>
78
- {formatted_details}
79
- </div>
80
  """
81
 
 
 
 
 
 
 
 
 
 
 
82
 
83
- def format_ai_output(p):
84
- if p is None or not isinstance(p, dict) or "values" not in p or len(p["values"]) == 0:
85
- return """
86
- <div class="card">
87
- <h3>30-DAY AI FORECAST (CHRONOS-BOLT)</h3>
88
- <p>No AI prediction data available.</p>
89
  </div>
90
- """
91
- tp1 = p["mean_30d"] * 0.97
92
- tp2 = p["mean_30d"] * 1.02
93
- sl = p["low_30d"] * 0.95
94
- return f"""
95
- <div class="card">
96
- <h3>30-DAY AI FORECAST (CHRONOS-BOLT)</h3>
97
- <p><b>Predicted High:</b> Rp{p['high_30d']:,.2f}</p>
98
- <p><b>Predicted Low:</b> Rp{p['low_30d']:,.2f}</p>
99
- <p><b>Expected Change:</b> {p['change_pct']:.2f}%</p>
100
- <p><b>TP1:</b> Rp{tp1:,.2f}</p>
101
- <p><b>TP2:</b> Rp{tp2:,.2f}</p>
102
- <p><b>Stop Loss:</b> Rp{sl:,.2f}</p>
103
- <h4>Model Insight:</h4>
104
- <p style="font-size:13px;line-height:1.4;">{p['summary']}</p>
105
- </div>
106
- """
107
 
108
 
109
- with gr.Blocks(css="""
110
- body { font-family: 'Inter', sans-serif; background-color: #f9fafc; color: #222; }
111
- .gradio-container { max-width: 1300px; margin: auto; }
112
- h1 { text-align:center; color:#003366; margin-bottom:20px; }
113
- h3 { color:#003366; margin-bottom:8px; }
114
- .card {
115
- background: #ffffff;
116
- border-radius: 10px;
117
- box-shadow: 0 2px 4px rgba(0,0,0,0.08);
118
- padding: 18px;
119
- margin: 5px;
120
- flex: 1;
121
- min-width: 0;
122
- }
123
- .row-flex {
124
- display: flex;
125
- flex-wrap: wrap;
126
- justify-content: space-between;
127
- gap: 10px;
128
- }
129
- ul { margin: 6px 0 0 20px; padding: 0; }
130
- li { margin-bottom: 4px; font-size: 14px; }
131
- """) as demo:
132
-
133
- gr.HTML("<h1>STOCK ANALYSIS DASHBOARD</h1>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  with gr.Row():
136
- stock_input = gr.Textbox(label="Enter Stock Symbol (e.g. BBCA.JK, ADRO.JK)", placeholder="Type stock symbol...")
137
- mode_input = gr.Radio(["Technical Analysis", "AI Prediction"], label="Select Analysis Mode", value="Technical Analysis")
138
- pred_days_input = gr.Slider(7, 60, value=30, step=1, label="Prediction Days (AI only)")
139
- analyze_button = gr.Button("Analyze", variant="primary")
140
-
141
- gr.HTML("<hr style='margin:20px 0;'>")
142
-
143
- with gr.Row(elem_classes="row-flex"):
144
- fundamentals_output = gr.HTML()
145
- signal_output = gr.HTML()
146
- ai_output = gr.HTML()
147
-
148
- gr.HTML("<hr style='margin:20px 0;'>")
 
 
149
 
150
- with gr.Row():
151
- chart_price = gr.Plot(label="Price Chart")
152
- chart_technical = gr.Plot(label="Technical Chart")
153
- chart_prediction = gr.Plot(label="AI Prediction Chart")
154
 
155
- def run_analysis(symbol, mode, pred_days):
156
- fundamentals, signals, fig_price, fig_technical, fig_prediction, prediction = analyze_stock(symbol, mode, pred_days)
157
- return (
158
- format_fundamental_output(fundamentals),
159
- format_signal_output(signals),
160
- format_ai_output(prediction) if mode == "AI Prediction" else "",
161
- fig_price,
162
- fig_technical,
163
- fig_prediction if mode == "AI Prediction" else None,
164
- )
165
 
166
  analyze_button.click(
167
- fn=run_analysis,
168
- inputs=[stock_input, mode_input, pred_days_input],
169
- outputs=[fundamentals_output, signal_output, ai_output, chart_price, chart_technical, chart_prediction]
170
  )
171
 
172
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
1
  import gradio as gr
2
+ import pandas as pd
3
  import yfinance as yf
4
  from utils import (
5
  calculate_technical_indicators,
6
  generate_trading_signals,
7
  get_fundamental_data,
8
+ predict_prices,
9
  create_price_chart,
10
  create_technical_chart,
11
  create_prediction_chart,
 
12
  )
13
+ import warnings
14
+
15
+ warnings.filterwarnings("ignore")
16
 
17
 
18
+ def analyze_stock(symbol, prediction_days=30):
19
  try:
20
+ if not symbol.strip():
21
+ raise ValueError("Please enter a valid stock symbol.")
22
+
23
+ if not symbol.endswith(".JK"):
24
+ symbol = symbol.upper() + ".JK"
25
+
26
  stock = yf.Ticker(symbol)
27
+ data = stock.history(period="6mo", interval="1d")
28
 
29
  if data.empty:
30
+ raise ValueError("No price data available for this stock.")
 
 
 
 
 
31
 
32
  indicators = calculate_technical_indicators(data)
33
  signals = generate_trading_signals(data, indicators)
34
+ fundamental_info = get_fundamental_data(stock)
35
+ predictions = predict_prices(data, prediction_days=prediction_days)
36
 
37
  fig_price = create_price_chart(data, indicators)
38
  fig_technical = create_technical_chart(data, indicators)
39
+ fig_prediction = create_prediction_chart(data, predictions)
40
+
41
+ # kalkulasi TP1, TP2, SL
42
+ last_price = data['Close'].iloc[-1]
43
+ tp1 = last_price * (1 + (predictions.get("change_pct", 0) / 200))
44
+ tp2 = last_price * (1 + (predictions.get("change_pct", 0) / 100))
45
+ sl = last_price * 0.95
46
 
47
+ predictions["tp1"] = tp1
48
+ predictions["tp2"] = tp2
49
+ predictions["sl"] = sl
50
+
51
+ return fundamental_info, indicators, signals, fig_price, fig_technical, fig_prediction, predictions
 
52
 
53
  except Exception as e:
54
+ print(f"Error analyzing {symbol}: {e}")
55
+ empty_fig = gr.Plot.update(value=None)
56
+ empty_predictions = {
57
+ "high_30d": 0,
58
+ "low_30d": 0,
59
+ "change_pct": 0,
60
+ "summary": "Prediction unavailable.",
61
+ }
62
+ return {}, {}, {}, empty_fig, empty_fig, empty_fig, empty_predictions
63
+
64
+
65
+ def update_analysis(symbol, prediction_days):
66
+ (
67
+ fundamental_info,
68
+ indicators,
69
+ signals,
70
+ fig_price,
71
+ fig_technical,
72
+ fig_prediction,
73
+ predictions,
74
+ ) = analyze_stock(symbol, prediction_days)
75
+
76
+ if not fundamental_info:
77
  return (
78
+ "Unable to fetch stock data.",
79
+ "No technical signals available.",
80
+ "No prediction data available.",
81
+ gr.Plot.update(value=None),
82
+ gr.Plot.update(value=None),
83
+ gr.Plot.update(value=None),
84
  )
85
 
86
+ fundamentals = f"""
87
+ <h4>COMPANY FUNDAMENTALS</h4>
88
+ <b>Name:</b> {fundamental_info.get('name', 'N/A')} ({symbol.upper()})<br>
89
+ <b>Current Price:</b> Rp{fundamental_info.get('current_price', 0):,.2f}<br>
90
+ <b>Market Cap:</b> {fundamental_info.get('market_cap', 0):,}<br>
91
+ <b>P/E Ratio:</b> {fundamental_info.get('pe_ratio', 0):.2f}<br>
92
+ <b>Dividend Yield:</b> {fundamental_info.get('dividend_yield', 0):.2f}%<br>
93
+ <b>Volume:</b> {fundamental_info.get('volume', 0):,}<br>
 
 
 
 
94
  """
95
 
96
+ details_list = "".join(
97
+ [f"<li>{line.strip()}</li>" for line in signals.get("details", "").split("\n") if line.strip()]
98
+ )
99
 
100
+ trading_signal = f"""
101
+ <h4>TECHNICAL SIGNAL SUMMARY</h4>
102
+ <b>Overall Trend:</b> {signals.get('overall', 'N/A')}<br>
103
+ <b>Signal Strength:</b> {signals.get('strength', 0):.2f}%<br>
104
+ <b>Support:</b> Rp{signals.get('support', 0):,.2f}<br>
105
+ <b>Resistance:</b> Rp{signals.get('resistance', 0):,.2f}<br>
106
+ <b>Stop Loss:</b> Rp{signals.get('stop_loss', 0):,.2f}<br><br>
107
+ <b>Detailed Signals:</b>
108
+ <ul style="margin-top: 8px; padding-left: 20px; line-height: 1.6;">
109
+ {details_list}
110
+ </ul>
 
 
 
 
111
  """
112
 
113
+ prediction = f"""
114
+ <h4>30-DAY AI FORECAST (CHRONOS-BOLT)</h4>
115
+ <b>Predicted High:</b> Rp{predictions.get('high_30d', 0):,.2f}<br>
116
+ <b>Predicted Low:</b> Rp{predictions.get('low_30d', 0):,.2f}<br>
117
+ <b>Expected Change:</b> {predictions.get('change_pct', 0):.2f}%<br><br>
118
+ <b>TP1:</b> Rp{predictions.get('tp1', 0):,.2f}<br>
119
+ <b>TP2:</b> Rp{predictions.get('tp2', 0):,.2f}<br>
120
+ <b>Stop Loss:</b> Rp{predictions.get('sl', 0):,.2f}<br><br>
121
+ <b>Model Insight:</b><br>{predictions.get('summary', 'No analysis available')}
122
+ """
123
 
124
+ return (
125
+ f"""
126
+ <div class='triple-panel'>
127
+ <div class='panel-box'>{fundamentals}</div>
128
+ <div class='panel-box'>{trading_signal}</div>
129
+ <div class='panel-box'>{prediction}</div>
130
  </div>
131
+ """,
132
+ fig_price,
133
+ fig_technical,
134
+ fig_prediction,
135
+ )
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
 
138
+ with gr.Blocks(
139
+ title="REXPRO FINANCIAL AI DASHBOARD",
140
+ theme=gr.themes.Soft(primary_hue="blue", secondary_hue="gray"),
141
+ css="""
142
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
143
+ body {
144
+ background-color: #f9fafb;
145
+ color: #1e293b;
146
+ font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
147
+ }
148
+ h1 {
149
+ color: #1e40af;
150
+ text-align: center;
151
+ font-weight: 700;
152
+ letter-spacing: 0.5px;
153
+ }
154
+ h2, h3, h4 {
155
+ color: #1e3a8a;
156
+ font-weight: 600;
157
+ margin-bottom: 6px;
158
+ }
159
+ .gr-button {
160
+ background-color: #2563eb !important;
161
+ color: white !important;
162
+ font-weight: 600;
163
+ border-radius: 8px;
164
+ padding: 10px 18px;
165
+ }
166
+ .panel-box {
167
+ background-color: #ffffff;
168
+ border-radius: 10px;
169
+ border: 1px solid #e2e8f0;
170
+ padding: 16px;
171
+ flex: 1;
172
+ min-width: 30%;
173
+ }
174
+ .triple-panel {
175
+ display: flex;
176
+ flex-direction: row;
177
+ justify-content: space-between;
178
+ gap: 16px;
179
+ width: 100%;
180
+ }
181
+ ul { margin: 0; padding: 0 0 0 18px; }
182
+ li { margin-bottom: 4px; }
183
+ .gr-plot {
184
+ background-color: #ffffff;
185
+ border: 1px solid #e2e8f0;
186
+ border-radius: 10px;
187
+ }
188
+ """,
189
+ ) as app:
190
+ gr.Markdown("# REXPRO FINANCIAL AI DASHBOARD")
191
+ gr.Markdown(
192
+ "Comprehensive stock analytics powered by **AI forecasting and technical analysis.**"
193
+ )
194
 
195
  with gr.Row():
196
+ symbol = gr.Textbox(
197
+ label="STOCK SYMBOL (IDX)",
198
+ value="BBCA",
199
+ placeholder="Example: BBCA, TLKM, ADRO, BMRI",
200
+ interactive=True,
201
+ )
202
+ prediction_days = gr.Slider(
203
+ label="FORECAST PERIOD (DAYS)",
204
+ minimum=5,
205
+ maximum=60,
206
+ step=5,
207
+ value=30,
208
+ interactive=True,
209
+ )
210
+ analyze_button = gr.Button("RUN ANALYSIS")
211
 
212
+ gr.Markdown("---")
213
+ report_section = gr.HTML()
214
+ gr.Markdown("---")
 
215
 
216
+ with gr.Tab("MARKET CHARTS"):
217
+ with gr.Row():
218
+ price_chart = gr.Plot(label="PRICE & MOVING AVERAGES")
219
+ technical_chart = gr.Plot(label="TECHNICAL INDICATORS OVERVIEW")
220
+ gr.Markdown("---")
221
+ prediction_chart = gr.Plot(label="AI FORECAST PROJECTION")
 
 
 
 
222
 
223
  analyze_button.click(
224
+ fn=update_analysis,
225
+ inputs=[symbol, prediction_days],
226
+ outputs=[report_section, price_chart, technical_chart, prediction_chart],
227
  )
228
 
229
+ if __name__ == "__main__":
230
+ app.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=True)