omniverse1 commited on
Commit
cd2ec3f
·
verified ·
1 Parent(s): f6259db

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -50
app.py CHANGED
@@ -9,13 +9,16 @@ from utils import (
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, history_period, prediction_days=30):
19
  try:
20
  if not symbol.strip():
21
  raise ValueError("Please enter a valid stock symbol.")
@@ -24,37 +27,59 @@ def analyze_stock(symbol, history_period, prediction_days=30):
24
  symbol = symbol.upper() + ".JK"
25
 
26
  stock = yf.Ticker(symbol)
27
- data = stock.history(period=history_period, interval="1d")
 
28
 
29
  if data.empty:
30
- raise ValueError(f"No price data available for {history_period}.")
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 menggunakan median (Q0.5)
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
 
57
- # Ambil harga terakhir untuk menghitung TP/SL yang tetap valid
58
  try:
59
  stock = yf.Ticker(symbol)
60
  data = stock.history(period="1d", interval="1d")
@@ -62,49 +87,44 @@ def analyze_stock(symbol, history_period, prediction_days=30):
62
  except:
63
  last_price = 0
64
 
65
- # Jika harga terakhir berhasil diambil, hitung TP/SL default
66
- tp1_default = last_price * 1.005 # 0.5% dari last price
67
- tp2_default = last_price * 1.01 # 1% dari last price
68
- sl_default = last_price * 0.95
69
 
70
  empty_predictions = {
71
- "high_30d": 0,
72
- "low_30d": 0,
73
- "change_pct": 0,
74
- "summary": f"Prediction unavailable. Model error: {e}", # Tampilkan error yang ditangkap
75
- "q01": [],
76
- "q09": [],
77
- "tp1": tp1_default,
78
- "tp2": tp2_default,
79
- "sl": sl_default,
80
  }
81
- return {}, {}, {}, empty_fig, empty_fig, empty_fig, empty_predictions
 
82
 
83
 
84
- def update_analysis(symbol, history_period, prediction_days):
85
  (
86
  fundamental_info,
87
  indicators,
88
  signals,
 
89
  fig_price,
90
  fig_technical,
91
  fig_prediction,
92
  predictions,
93
- ) = analyze_stock(symbol, history_period, prediction_days)
94
 
95
  if not fundamental_info:
96
- # Jika fundamental gagal diambil (misalnya simbol salah), berikan pesan error umum
97
  error_msg = f"Unable to fetch stock data for {symbol.upper()}. Please check the symbol."
98
- # Gunakan prediksi yang dikembalikan (meski kosong) untuk mendapatkan TP/SL default
99
  tp_sl_info = f"<b>TP1:</b> Rp{predictions.get('tp1', 0):,.2f}<br><b>TP2:</b> Rp{predictions.get('tp2', 0):,.2f}<br><b>Stop Loss:</b> Rp{predictions.get('sl', 0):,.2f}<br><br><b>Model Insight:</b><br>Data fetching failed. Cannot proceed with analysis."
100
 
101
  return (
102
- f"""<div style="color: red; padding: 10px; border: 1px solid red;">{error_msg}</div><br>{tp_sl_info}""",
103
  gr.Plot.update(value=None),
104
  gr.Plot.update(value=None),
105
  gr.Plot.update(value=None),
106
  )
107
 
 
108
  fundamentals = f"""
109
  <h4>COMPANY FUNDAMENTALS</h4>
110
  <b>Name:</b> {fundamental_info.get('name', 'N/A')} ({symbol.upper()})<br>
@@ -115,6 +135,7 @@ def update_analysis(symbol, history_period, prediction_days):
115
  <b>Volume:</b> {fundamental_info.get('volume', 0):,}<br>
116
  """
117
 
 
118
  details_list = "".join(
119
  [f"<li>{line.strip()}</li>" for line in signals.get("details", "").split("\n") if line.strip()]
120
  )
@@ -132,32 +153,47 @@ def update_analysis(symbol, history_period, prediction_days):
132
  </ul>
133
  """
134
 
135
- # Hitung Min/Max dari seluruh rentang kepercayaan (Q0.1 dan Q0.9)
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  band_min = float(min(predictions.get('q01', [0]))) if predictions.get('q01') and predictions.get('q01').size > 0 else 0
137
  band_max = float(max(predictions.get('q09', [0]))) if predictions.get('q09') and predictions.get('q09').size > 0 else 0
138
 
139
  prediction = f"""
140
- <h4>30-DAY AI FORECAST (CHRONOS-2)</h4>
141
  <b>Predicted Median High:</b> Rp{predictions.get('high_30d', 0):,.2f}<br>
142
  <b>Predicted Median Low:</b> Rp{predictions.get('low_30d', 0):,.2f}<br>
143
  <b>Expected Change:</b> {predictions.get('change_pct', 0):.2f}%<br>
144
  ---
145
  <h4>RISK ANALYSIS (90% CONFIDENCE)</h4>
146
- <b>Min. Possible Price:</b> Rp{band_min:,.2f}<br>
147
- <b>Max. Possible Price:</b> Rp{band_max:,.2f}<br>
148
  ---
149
- <b>TP1:</b> Rp{predictions.get('tp1', 0):,.2f}<br>
150
- <b>TP2:</b> Rp{predictions.get('tp2', 0):,.2f}<br>
151
- <b>Stop Loss:</b> Rp{predictions.get('sl', 0):,.2f}<br><br>
152
  <b>Model Insight:</b><br>{predictions.get('summary', 'No analysis available')}
153
  """
154
 
 
155
  return (
156
  f"""
157
- <div style="display: flex; flex-direction: row; gap: 16px;">
158
- <div style="flex: 1; min-width: 30%; border: 1px solid #ccc; padding: 10px; border-radius: 5px;">{fundamentals}</div>
159
- <div style="flex: 1; min-width: 30%; border: 1px solid #ccc; padding: 10px; border-radius: 5px;">{trading_signal}</div>
160
- <div style="flex: 1; min-width: 30%; border: 1px solid #ccc; padding: 10px; border-radius: 5px;">{prediction}</div>
 
161
  </div>
162
  """,
163
  fig_price,
@@ -166,12 +202,13 @@ def update_analysis(symbol, history_period, prediction_days):
166
  )
167
 
168
 
 
169
  with gr.Blocks(
170
  title="REXPRO FINANCIAL AI DASHBOARD"
171
  ) as app:
172
  gr.Markdown("# REXPRO FINANCIAL AI DASHBOARD")
173
  gr.Markdown(
174
- "Comprehensive stock analytics powered by **AI forecasting and technical analysis.**"
175
  )
176
 
177
  with gr.Row():
@@ -181,13 +218,6 @@ with gr.Blocks(
181
  placeholder="Example: BBCA, TLKM, ADRO, BMRI",
182
  interactive=True,
183
  )
184
- # INPUT BARU: Kontrol Periode Data Historis
185
- history_period_input = gr.Radio(
186
- label="HISTORICAL DATA PERIOD",
187
- choices=["6mo", "1y", "3y", "5y"],
188
- value="3y",
189
- interactive=True,
190
- )
191
  prediction_days = gr.Slider(
192
  label="FORECAST PERIOD (DAYS)",
193
  minimum=5,
@@ -196,7 +226,7 @@ with gr.Blocks(
196
  value=30,
197
  interactive=True,
198
  )
199
- analyze_button = gr.Button("RUN ANALYSIS")
200
 
201
  gr.Markdown("---")
202
  report_section = gr.HTML()
@@ -207,11 +237,11 @@ with gr.Blocks(
207
  price_chart = gr.Plot(label="PRICE & MOVING AVERAGES")
208
  technical_chart = gr.Plot(label="TECHNICAL INDICATORS OVERVIEW")
209
  gr.Markdown("---")
210
- prediction_chart = gr.Plot(label="AI FORECAST PROJECTION")
211
 
212
  analyze_button.click(
213
  fn=update_analysis,
214
- inputs=[symbol, history_period_input, prediction_days],
215
  outputs=[report_section, price_chart, technical_chart, prediction_chart],
216
  )
217
 
 
9
  create_price_chart,
10
  create_technical_chart,
11
  create_prediction_chart,
12
+ # Import fungsi baru
13
+ calculate_advanced_risk_metrics
14
  )
15
  import warnings
16
+ import numpy as np # Tambahkan import numpy
17
 
18
  warnings.filterwarnings("ignore")
19
 
20
 
21
+ def analyze_stock(symbol, prediction_days=30):
22
  try:
23
  if not symbol.strip():
24
  raise ValueError("Please enter a valid stock symbol.")
 
27
  symbol = symbol.upper() + ".JK"
28
 
29
  stock = yf.Ticker(symbol)
30
+ # Menggunakan periode 1 tahun untuk metrik risiko yang lebih relevan
31
+ data = stock.history(period="1y", interval="1d")
32
 
33
  if data.empty:
34
+ raise ValueError("No price data available for this stock.")
35
 
36
+ # Menghitung Indikator Teknikal (Ini juga akan mengisi kolom RSI/MACD ke 'data')
37
  indicators = calculate_technical_indicators(data)
38
  signals = generate_trading_signals(data, indicators)
39
  fundamental_info = get_fundamental_data(stock)
40
+
41
+ # Menghitung Metrik Risiko Tingkat Lanjut
42
+ risk_metrics = calculate_advanced_risk_metrics(data.copy())
43
+
44
+ # Prediksi Chronos-2 dengan Covariates
45
  predictions = predict_prices(data, prediction_days=prediction_days)
46
 
47
  fig_price = create_price_chart(data, indicators)
48
  fig_technical = create_technical_chart(data, indicators)
49
  fig_prediction = create_prediction_chart(data, predictions)
50
 
51
+ # kalkulasi TP1, TP2, SL yang diperbarui berdasarkan quantiles/range prediksi
52
  last_price = data['Close'].iloc[-1]
53
+
54
+ # Target/SL yang lebih konservatif berdasarkan range prediksi Q0.1-Q0.9
55
+ q05 = predictions.get('values', [last_price])
56
+ q01 = predictions.get('q01', [last_price * 0.95])
57
+ q09 = predictions.get('q09', [last_price * 1.05])
58
+
59
+ # TP1 (Target Konservatif): Midpoint antara harga terakhir dan median tertinggi
60
+ tp1 = (last_price + np.max(q05)) / 2
61
+ # TP2 (Target Agresif): Quantile 90% tertinggi
62
+ tp2 = np.max(q09)
63
+ # SL (Stop Loss): Quantile 10% terendah
64
+ sl = np.min(q01)
65
+
66
+ # Pastikan TP1 < TP2 dan SL lebih rendah dari TP
67
+ if tp1 > tp2:
68
+ tp1, tp2 = tp2, tp1
69
+ if sl > last_price:
70
+ sl = last_price * 0.95 # Fallback
71
 
72
  predictions["tp1"] = tp1
73
  predictions["tp2"] = tp2
74
  predictions["sl"] = sl
75
 
76
+ # Menambahkan risk_metrics ke return
77
+ return fundamental_info, indicators, signals, risk_metrics, fig_price, fig_technical, fig_prediction, predictions
78
 
79
  except Exception as e:
80
  print(f"Error analyzing {symbol}: {e}")
81
  empty_fig = gr.Plot.update(value=None)
82
 
 
83
  try:
84
  stock = yf.Ticker(symbol)
85
  data = stock.history(period="1d", interval="1d")
 
87
  except:
88
  last_price = 0
89
 
90
+ default_tp1 = last_price * 1.01
91
+ default_tp2 = last_price * 1.02
92
+ default_sl = last_price * 0.95
 
93
 
94
  empty_predictions = {
95
+ "high_30d": 0, "low_30d": 0, "change_pct": 0,
96
+ "summary": f"Prediction unavailable. Model error: {e}",
97
+ "q01": [], "q09": [],
98
+ "tp1": default_tp1, "tp2": default_tp2, "sl": default_sl,
 
 
 
 
 
99
  }
100
+ # Mengembalikan dictionary kosong untuk risk_metrics saat error
101
+ return {}, {}, {}, {}, empty_fig, empty_fig, empty_fig, empty_predictions
102
 
103
 
104
+ def update_analysis(symbol, prediction_days):
105
  (
106
  fundamental_info,
107
  indicators,
108
  signals,
109
+ risk_metrics, # Tambahan metrik risiko
110
  fig_price,
111
  fig_technical,
112
  fig_prediction,
113
  predictions,
114
+ ) = analyze_stock(symbol, prediction_days)
115
 
116
  if not fundamental_info:
 
117
  error_msg = f"Unable to fetch stock data for {symbol.upper()}. Please check the symbol."
 
118
  tp_sl_info = f"<b>TP1:</b> Rp{predictions.get('tp1', 0):,.2f}<br><b>TP2:</b> Rp{predictions.get('tp2', 0):,.2f}<br><b>Stop Loss:</b> Rp{predictions.get('sl', 0):,.2f}<br><br><b>Model Insight:</b><br>Data fetching failed. Cannot proceed with analysis."
119
 
120
  return (
121
+ f"""<div style="color: red; padding: 10px; border: 1px solid red; border-radius: 5px;">{error_msg}</div><br>{tp_sl_info}""",
122
  gr.Plot.update(value=None),
123
  gr.Plot.update(value=None),
124
  gr.Plot.update(value=None),
125
  )
126
 
127
+ # --- FUNDAMENTALS ---
128
  fundamentals = f"""
129
  <h4>COMPANY FUNDAMENTALS</h4>
130
  <b>Name:</b> {fundamental_info.get('name', 'N/A')} ({symbol.upper()})<br>
 
135
  <b>Volume:</b> {fundamental_info.get('volume', 0):,}<br>
136
  """
137
 
138
+ # --- TECHNICAL SIGNAL ---
139
  details_list = "".join(
140
  [f"<li>{line.strip()}</li>" for line in signals.get("details", "").split("\n") if line.strip()]
141
  )
 
153
  </ul>
154
  """
155
 
156
+ # --- RISK METRICS ---
157
+ risk_details = ""
158
+ if "error" in risk_metrics:
159
+ risk_details = f"<b style='color: red;'>{risk_metrics['error']}</b>"
160
+ else:
161
+ for key, value in risk_metrics.items():
162
+ risk_details += f"<b>{key.replace('_', ' ')}:</b> {value}<br>"
163
+
164
+ risk_report = f"""
165
+ <h4>ADVANCED RISK METRICS (1Y HISTORICAL)</h4>
166
+ {risk_details}
167
+ """
168
+
169
+ # --- AI FORECAST ---
170
  band_min = float(min(predictions.get('q01', [0]))) if predictions.get('q01') and predictions.get('q01').size > 0 else 0
171
  band_max = float(max(predictions.get('q09', [0]))) if predictions.get('q09') and predictions.get('q09').size > 0 else 0
172
 
173
  prediction = f"""
174
+ <h4>{prediction_days}-DAY AI FORECAST (CHRONOS-2 + COVARIATES)</h4>
175
  <b>Predicted Median High:</b> Rp{predictions.get('high_30d', 0):,.2f}<br>
176
  <b>Predicted Median Low:</b> Rp{predictions.get('low_30d', 0):,.2f}<br>
177
  <b>Expected Change:</b> {predictions.get('change_pct', 0):.2f}%<br>
178
  ---
179
  <h4>RISK ANALYSIS (90% CONFIDENCE)</h4>
180
+ <b>Min. Possible Price (Q0.1):</b> Rp{band_min:,.2f}<br>
181
+ <b>Max. Possible Price (Q0.9):</b> Rp{band_max:,.2f}<br>
182
  ---
183
+ <b>TP1 (Conservative Target):</b> Rp{predictions.get('tp1', 0):,.2f}<br>
184
+ <b>TP2 (Aggressive Target):</b> Rp{predictions.get('tp2', 0):,.2f}<br>
185
+ <b>Stop Loss (Q0.1 based):</b> Rp{predictions.get('sl', 0):,.2f}<br><br>
186
  <b>Model Insight:</b><br>{predictions.get('summary', 'No analysis available')}
187
  """
188
 
189
+ # Menggunakan tata letak 4 kolom/panel
190
  return (
191
  f"""
192
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;">
193
+ <div style="border: 1px solid #ccc; padding: 10px; border-radius: 5px; height: 100%;">{fundamentals}</div>
194
+ <div style="border: 1px solid #ccc; padding: 10px; border-radius: 5px; height: 100%;">{trading_signal}</div>
195
+ <div style="border: 1px solid #ccc; padding: 10px; border-radius: 5px; height: 100%;">{risk_report}</div>
196
+ <div style="border: 1px solid #ccc; padding: 10px; border-radius: 5px; height: 100%;">{prediction}</div>
197
  </div>
198
  """,
199
  fig_price,
 
202
  )
203
 
204
 
205
+ # --- Gradio Interface ---
206
  with gr.Blocks(
207
  title="REXPRO FINANCIAL AI DASHBOARD"
208
  ) as app:
209
  gr.Markdown("# REXPRO FINANCIAL AI DASHBOARD")
210
  gr.Markdown(
211
+ "Comprehensive stock analytics powered by **AI forecasting, advanced risk metrics, and future technical analysis.**"
212
  )
213
 
214
  with gr.Row():
 
218
  placeholder="Example: BBCA, TLKM, ADRO, BMRI",
219
  interactive=True,
220
  )
 
 
 
 
 
 
 
221
  prediction_days = gr.Slider(
222
  label="FORECAST PERIOD (DAYS)",
223
  minimum=5,
 
226
  value=30,
227
  interactive=True,
228
  )
229
+ analyze_button = gr.Button("RUN ADVANCED ANALYSIS")
230
 
231
  gr.Markdown("---")
232
  report_section = gr.HTML()
 
237
  price_chart = gr.Plot(label="PRICE & MOVING AVERAGES")
238
  technical_chart = gr.Plot(label="TECHNICAL INDICATORS OVERVIEW")
239
  gr.Markdown("---")
240
+ prediction_chart = gr.Plot(label="AI FORECAST & FUTURE TECHNICAL PROJECTION")
241
 
242
  analyze_button.click(
243
  fn=update_analysis,
244
+ inputs=[symbol, prediction_days],
245
  outputs=[report_section, price_chart, technical_chart, prediction_chart],
246
  )
247