omniverse1 commited on
Commit
81179e3
ยท
verified ยท
1 Parent(s): d45c1d3

update app 1.5

Browse files
Files changed (1) hide show
  1. app.py +113 -67
app.py CHANGED
@@ -1,25 +1,29 @@
1
  import gradio as gr
2
  import pandas as pd
3
  import numpy as np
4
- import yfinance as yf
5
  from datetime import datetime, timedelta
6
  import plotly.graph_objects as go
7
  from plotly.subplots import make_subplots
8
  import warnings
 
 
 
 
9
  warnings.filterwarnings('ignore')
10
 
11
  from models import load_timesfm_model, predict_stock_prices
12
  from utils import (
13
  get_idx_stocks, fetch_stock_data, prepare_timesfm_data,
14
- calculate_metrics, create_forecast_plot, get_stock_info
 
15
  )
16
- from config import IDX_STOCKS, DEFAULT_PERIOD, DEFAULT_FORECAST_HORIZON
17
 
18
  # Load model at startup
19
  model = None
20
 
21
  def load_model():
22
- """Load the Chronos pipeline (named load_timesfm_model for compatibility)"""
23
  global model
24
  if model is None:
25
  model = load_timesfm_model()
@@ -29,24 +33,38 @@ def get_stock_choices():
29
  """Get available stock choices for dropdown"""
30
  return list(IDX_STOCKS.keys())
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  def analyze_stock(symbol, period, forecast_horizon, use_volume):
33
  """Main analysis function"""
34
  try:
35
  # Load model if not already loaded
36
- # 'model' here is now the Chronos Pipeline
37
  model = load_model()
38
 
39
- # Fetch stock data
40
  stock_data = fetch_stock_data(symbol, period)
41
  if stock_data is None or len(stock_data) < 30:
42
- return None, None, "Error: Insufficient data. Please try a different stock or longer period."
43
 
44
- # Prepare data for Chronos
45
- # timesfm_data contains RAW 'Close' prices (unscaled), scaler is None
46
  timesfm_data, scaler = prepare_timesfm_data(stock_data, use_volume)
47
 
48
  # Make predictions
49
- # forecast_prices now contains the final unscaled prices directly
50
  forecast_prices = predict_stock_prices(model, timesfm_data, forecast_horizon)
51
 
52
  # Create dates for forecast
@@ -57,54 +75,67 @@ def analyze_stock(symbol, period, forecast_horizon, use_volume):
57
  freq='D'
58
  )
59
 
60
- # Calculate metrics
61
- actual_prices = stock_data['Close'].values
62
- last_price = actual_prices[-1]
63
 
64
- # Create forecast plot
65
  fig = create_forecast_plot(stock_data, forecast_dates, forecast_prices, symbol)
66
 
67
- # Create summary table
 
 
 
 
 
68
  summary_data = {
69
- 'Metric': ['Current Price', 'Forecast High', 'Forecast Low',
70
- 'Forecast Change (%)', 'Volatility'],
71
- 'Value': [
72
  f"Rp {last_price:,.2f}",
73
  f"Rp {np.max(forecast_prices):,.2f}",
74
  f"Rp {np.min(forecast_prices):,.2f}",
75
  f"{((np.mean(forecast_prices) - last_price) / last_price * 100):.2f}%",
76
- f"{np.std(forecast_prices) / np.mean(forecast_prices) * 100:.2f}%"
 
 
77
  ]
78
  }
79
 
80
  summary_df = pd.DataFrame(summary_data)
81
 
82
- # Get additional stock info
83
  stock_info = get_stock_info(symbol)
84
  info_text = f"""
85
  **{stock_info.get('shortName', symbol)}**
86
 
87
- **Current Statistics:**
88
- - Market Cap: {stock_info.get('marketCap', 'N/A')}
89
- - Volume: {stock_data['Volume'].iloc[-1]:,.0f}
90
- - 52W High: {stock_info.get('fiftyTwoWeekHigh', 'N/A')}
91
- - 52W Low: {stock_info.get('fiftyTwoWeekLow', 'N/A')}
92
 
93
- **Forecast Summary:**
94
- - Period: {forecast_horizon} days
95
- - Expected Trend: {'Upward' if np.mean(forecast_prices) > last_price else 'Downward'}
96
- - Price Range: Rp {np.min(forecast_prices):,.2f} - Rp {np.max(forecast_prices):,.2f}
97
  """
98
 
99
- return fig, summary_df, info_text
100
 
101
  except Exception as e:
102
- return None, None, f"Error occurred: {str(e)}"
103
 
104
  def create_interface():
105
  """Create the Gradio interface"""
 
 
 
 
 
 
 
106
  with gr.Blocks(
107
- title="IDX Stock Price Predictor",
108
  theme=gr.themes.Soft(),
109
  css="""
110
  .gradio-container {
@@ -120,27 +151,35 @@ def create_interface():
120
  """
121
  # ๐Ÿ‡ฎ๐Ÿ‡ฉ IDX Stock Price Predictor
122
 
123
- **Built with [anycoder](https://huggingface.co/spaces/akhaliq/anycoder)**
124
 
125
- Predict Indonesian Stock Exchange (IDX) stock prices using Chronos-Bolt-Base model.
126
 
127
- **Instructions:**
128
- 1. Select a stock from the dropdown
129
- 2. Choose the historical data period
130
- 3. Set forecast horizon (days)
131
- 4. Click "Analyze Stock" to generate predictions
132
  """
133
  )
134
 
 
 
 
 
 
 
 
 
135
  with gr.Row():
136
  with gr.Column(scale=1):
137
- gr.Markdown("### ๐Ÿ“Š Analysis Parameters")
138
 
139
  stock_dropdown = gr.Dropdown(
140
  choices=get_stock_choices(),
141
  value="BBCA.JK",
142
- label="Select Stock",
143
- info="Choose from major IDX stocks"
144
  )
145
 
146
  period_slider = gr.Slider(
@@ -148,8 +187,8 @@ def create_interface():
148
  maximum=365,
149
  value=DEFAULT_PERIOD,
150
  step=1,
151
- label="Historical Period (days)",
152
- info="Number of days of historical data to use"
153
  )
154
 
155
  forecast_slider = gr.Slider(
@@ -157,45 +196,45 @@ def create_interface():
157
  maximum=30,
158
  value=DEFAULT_FORECAST_HORIZON,
159
  step=1,
160
- label="Forecast Horizon (days)",
161
- info="Number of days to predict ahead"
162
  )
163
 
164
  volume_checkbox = gr.Checkbox(
165
- label="Include Volume in Analysis",
166
  value=False,
167
- info="Use trading volume as additional feature"
168
  )
169
 
170
  analyze_btn = gr.Button(
171
- "๐Ÿ” Analyze Stock",
172
  variant="primary",
173
  size="lg"
174
  )
175
 
176
  with gr.Column(scale=2):
177
- gr.Markdown("### ๐Ÿ“ˆ Results")
178
 
179
- with gr.Tab("Forecast Chart"):
 
 
 
 
 
180
  plot_output = gr.Plot(
181
- label="Price Forecast",
182
  show_label=False
183
  )
184
 
185
- with gr.Tab("Summary Statistics"):
186
  summary_output = gr.DataFrame(
187
- label="Forecast Summary",
188
  show_label=False
189
  )
190
-
191
- with gr.Tab("Stock Information"):
192
- info_output = gr.Markdown(
193
- label="Stock Details",
194
- value="Select a stock and click analyze to see information."
195
- )
196
 
197
  # Examples section
198
- gr.Markdown("### ๐Ÿ’ก Quick Examples")
199
  examples = gr.Examples(
200
  examples=[
201
  ["BBCA.JK", 90, 7, False],
@@ -204,7 +243,7 @@ def create_interface():
204
  ["BMRI.JK", 180, 5, True],
205
  ],
206
  inputs=[stock_dropdown, period_slider, forecast_slider, volume_checkbox],
207
- outputs=[plot_output, summary_output, info_output],
208
  fn=analyze_stock,
209
  cache_examples=False
210
  )
@@ -213,9 +252,9 @@ def create_interface():
213
  gr.Markdown(
214
  """
215
  ---
216
- **โš ๏ธ Disclaimer:** This tool is for educational purposes only. Stock market predictions are uncertain and should not be used as financial advice. Always consult with a qualified financial advisor before making investment decisions.
217
 
218
- **Data Source:** Yahoo Finance | **Model:** Amazon Chronos-Bolt-Base
219
  """
220
  )
221
 
@@ -223,15 +262,22 @@ def create_interface():
223
  analyze_btn.click(
224
  fn=analyze_stock,
225
  inputs=[stock_dropdown, period_slider, forecast_slider, volume_checkbox],
226
- outputs=[plot_output, summary_output, info_output],
227
  show_progress=True
228
  )
229
 
230
- # Load model on startup
231
- demo.load(fn=load_model)
 
 
 
 
232
 
233
  return demo
234
 
235
  if __name__ == "__main__":
 
 
236
  demo = create_interface()
 
237
  demo.launch(share=True, server_name="0.0.0.0")
 
1
  import gradio as gr
2
  import pandas as pd
3
  import numpy as np
 
4
  from datetime import datetime, timedelta
5
  import plotly.graph_objects as go
6
  from plotly.subplots import make_subplots
7
  import warnings
8
+ import yfinance as yf
9
+ from typing import Dict, List, Tuple
10
+ import atexit
11
+
12
  warnings.filterwarnings('ignore')
13
 
14
  from models import load_timesfm_model, predict_stock_prices
15
  from utils import (
16
  get_idx_stocks, fetch_stock_data, prepare_timesfm_data,
17
+ create_forecast_plot, get_stock_info, market_status_manager,
18
+ calculate_technical_indicators
19
  )
20
+ from config import IDX_STOCKS, DEFAULT_PERIOD, DEFAULT_FORECAST_HORIZON, IDX_MARKET_CONFIG
21
 
22
  # Load model at startup
23
  model = None
24
 
25
  def load_model():
26
+ """Load the Chronos Pipeline"""
27
  global model
28
  if model is None:
29
  model = load_timesfm_model()
 
33
  """Get available stock choices for dropdown"""
34
  return list(IDX_STOCKS.keys())
35
 
36
+ def get_idx_market_status() -> str:
37
+ """Get formatted IDX market status for display"""
38
+ status = market_status_manager.get_status('IDX_STOCKS')
39
+ status_emoji = "๐ŸŸข" if status.is_open else "๐Ÿ”ด"
40
+ status_text = "BUKA" if status.is_open else "TUTUP"
41
+
42
+ info_lines = [
43
+ f"**Status Saat Ini:** {status.status_text} (WIB)",
44
+ f"**Waktu Buka/Tutup:** {IDX_MARKET_CONFIG['IDX_STOCKS']['open_time']} - {IDX_MARKET_CONFIG['IDX_STOCKS']['close_time']} WIB",
45
+ f"**Waktu Sampai Tutup:** {status.time_until_close}",
46
+ f"**Hari Trading Berikutnya:** {status.next_trading_day}",
47
+ f"**Waktu Sampai Buka:** {status.time_until_open}",
48
+ f"**Terakhir Diperbarui:** {status.last_updated}"
49
+ ]
50
+
51
+ return f"## {status_emoji} {status.market_name}: {status_text}\n\n" + "\n".join(info_lines)
52
+
53
  def analyze_stock(symbol, period, forecast_horizon, use_volume):
54
  """Main analysis function"""
55
  try:
56
  # Load model if not already loaded
 
57
  model = load_model()
58
 
59
+ # Fetch stock data. This now includes technical indicators.
60
  stock_data = fetch_stock_data(symbol, period)
61
  if stock_data is None or len(stock_data) < 30:
62
+ return None, None, None, get_idx_market_status() + f"\n\nError: Data tidak mencukupi untuk {symbol}. Coba periode yang lebih panjang."
63
 
64
+ # Prepare data for Chronos (unscaled 'Close' prices)
 
65
  timesfm_data, scaler = prepare_timesfm_data(stock_data, use_volume)
66
 
67
  # Make predictions
 
68
  forecast_prices = predict_stock_prices(model, timesfm_data, forecast_horizon)
69
 
70
  # Create dates for forecast
 
75
  freq='D'
76
  )
77
 
78
+ # Calculate last price and technical summary
79
+ last_price = stock_data['Close'].iloc[-1]
 
80
 
81
+ # Create forecast plot (using the simplified utility function)
82
  fig = create_forecast_plot(stock_data, forecast_dates, forecast_prices, symbol)
83
 
84
+ # --- Create Summary Table ---
85
+ # Get last technical indicator values
86
+ last_rsi = stock_data['RSI'].iloc[-1]
87
+ last_macd = stock_data['MACD'].iloc[-1]
88
+ last_macd_signal = stock_data['MACD_Signal'].iloc[-1]
89
+
90
  summary_data = {
91
+ 'Metrik': ['Harga Saat Ini', 'Puncak Prediksi', 'Dasar Prediksi',
92
+ 'Perubahan Prediksi (%)', 'Volatilitas Historis', 'RSI (14 Hari)', 'Sinyal MACD'],
93
+ 'Nilai': [
94
  f"Rp {last_price:,.2f}",
95
  f"Rp {np.max(forecast_prices):,.2f}",
96
  f"Rp {np.min(forecast_prices):,.2f}",
97
  f"{((np.mean(forecast_prices) - last_price) / last_price * 100):.2f}%",
98
+ f"{stock_data['Volatility'].iloc[-1] * np.sqrt(252) * 100:.2f}% (Tahunan)",
99
+ f"{last_rsi:.2f}",
100
+ f"{'Beli' if last_macd > last_macd_signal else 'Jual' if last_macd < last_macd_signal else 'Netral'}"
101
  ]
102
  }
103
 
104
  summary_df = pd.DataFrame(summary_data)
105
 
106
+ # --- Get Additional Stock Info ---
107
  stock_info = get_stock_info(symbol)
108
  info_text = f"""
109
  **{stock_info.get('shortName', symbol)}**
110
 
111
+ **Statistik Saat Ini:**
112
+ - Kapitalisasi Pasar: {stock_info.get('marketCap', 'N/A')}
113
+ - Volume Trading: {stock_data['Volume'].iloc[-1]:,.0f}
114
+ - 52W Tertinggi: Rp {stock_info.get('fiftyTwoWeekHigh', 'N/A'):,.0f}
115
+ - 52W Terendah: Rp {stock_info.get('fiftyTwoWeekLow', 'N/A'):,.0f}
116
 
117
+ **Ringkasan Prediksi:**
118
+ - Periode Prediksi: {forecast_horizon} hari
119
+ - Tren Harapan: {'Naik' if np.mean(forecast_prices) > last_price else 'Turun'}
120
+ - Rentang Harga: Rp {np.min(forecast_prices):,.2f} - Rp {np.max(forecast_prices):,.2f}
121
  """
122
 
123
+ return fig, summary_df, info_text, get_idx_market_status()
124
 
125
  except Exception as e:
126
+ return None, None, None, get_idx_market_status() + f"\n\nError occurred during analysis: {str(e)}"
127
 
128
  def create_interface():
129
  """Create the Gradio interface"""
130
+ # Register cleanup function
131
+ def cleanup_on_exit():
132
+ market_status_manager.stop()
133
+ print("Market status manager stopped successfully")
134
+
135
+ atexit.register(cleanup_on_exit)
136
+
137
  with gr.Blocks(
138
+ title="IDX Stock Price Predictor (Chronos-Bolt)",
139
  theme=gr.themes.Soft(),
140
  css="""
141
  .gradio-container {
 
151
  """
152
  # ๐Ÿ‡ฎ๐Ÿ‡ฉ IDX Stock Price Predictor
153
 
154
+ **Didukung oleh Model Chronos-Bolt (Base) Amazon**
155
 
156
+ Memprediksi harga saham Bursa Efek Indonesia (IDX) menggunakan model *Time Series Foundation Model* Chronos-Bolt.
157
 
158
+ **Instruksi:**
159
+ 1. Pilih saham IDX dari *dropdown*
160
+ 2. Tentukan periode data historis (Hari)
161
+ 3. Tentukan horison prediksi (Hari)
162
+ 4. Klik "Analisis Saham" untuk melihat prediksi
163
  """
164
  )
165
 
166
+ # --- Market Status Display ---
167
+ market_status_output = gr.Markdown(
168
+ value=get_idx_market_status(),
169
+ label="Status Pasar IDX Saat Ini"
170
+ )
171
+
172
+ gr.Markdown("---")
173
+
174
  with gr.Row():
175
  with gr.Column(scale=1):
176
+ gr.Markdown("### ๐Ÿ“Š Parameter Analisis")
177
 
178
  stock_dropdown = gr.Dropdown(
179
  choices=get_stock_choices(),
180
  value="BBCA.JK",
181
+ label="Pilih Saham",
182
+ info="Pilih dari saham-saham utama IDX"
183
  )
184
 
185
  period_slider = gr.Slider(
 
187
  maximum=365,
188
  value=DEFAULT_PERIOD,
189
  step=1,
190
+ label="Periode Historis (Hari)",
191
+ info="Jumlah hari data historis yang akan digunakan"
192
  )
193
 
194
  forecast_slider = gr.Slider(
 
196
  maximum=30,
197
  value=DEFAULT_FORECAST_HORIZON,
198
  step=1,
199
+ label="Horison Prediksi (Hari)",
200
+ info="Jumlah hari ke depan yang akan diprediksi"
201
  )
202
 
203
  volume_checkbox = gr.Checkbox(
204
+ label="Sertakan Volume dalam Analisis (Eksperimental)",
205
  value=False,
206
+ info="Gunakan volume trading sebagai fitur tambahan (mungkin tidak kompatibel dengan Chronos-Bolt)"
207
  )
208
 
209
  analyze_btn = gr.Button(
210
+ "๐Ÿ” Analisis Saham",
211
  variant="primary",
212
  size="lg"
213
  )
214
 
215
  with gr.Column(scale=2):
216
+ gr.Markdown("### ๐Ÿ“ˆ Hasil Prediksi")
217
 
218
+ info_output = gr.Markdown(
219
+ label="Detail Saham",
220
+ value="Pilih saham dan klik analisis untuk melihat informasi."
221
+ )
222
+
223
+ with gr.Tab("Grafik Prediksi"):
224
  plot_output = gr.Plot(
225
+ label="Prediksi Harga",
226
  show_label=False
227
  )
228
 
229
+ with gr.Tab("Ringkasan Statistik"):
230
  summary_output = gr.DataFrame(
231
+ label="Ringkasan Prediksi",
232
  show_label=False
233
  )
234
+
 
 
 
 
 
235
 
236
  # Examples section
237
+ gr.Markdown("### ๐Ÿ’ก Contoh Cepat")
238
  examples = gr.Examples(
239
  examples=[
240
  ["BBCA.JK", 90, 7, False],
 
243
  ["BMRI.JK", 180, 5, True],
244
  ],
245
  inputs=[stock_dropdown, period_slider, forecast_slider, volume_checkbox],
246
+ outputs=[plot_output, summary_output, info_output, market_status_output],
247
  fn=analyze_stock,
248
  cache_examples=False
249
  )
 
252
  gr.Markdown(
253
  """
254
  ---
255
+ **โš ๏ธ Disclaimer:** Alat ini hanya untuk tujuan edukasi. Prediksi pasar saham tidak pasti dan tidak boleh digunakan sebagai nasihat keuangan. Selalu berkonsultasi dengan penasihat keuangan yang berkualifikasi sebelum membuat keputusan investasi.
256
 
257
+ **Sumber Data:** Yahoo Finance | **Model:** Amazon Chronos-Bolt-Base
258
  """
259
  )
260
 
 
262
  analyze_btn.click(
263
  fn=analyze_stock,
264
  inputs=[stock_dropdown, period_slider, forecast_slider, volume_checkbox],
265
+ outputs=[plot_output, summary_output, info_output, market_status_output],
266
  show_progress=True
267
  )
268
 
269
+ # Initial load to update market status and load model
270
+ demo.load(
271
+ fn=lambda: (load_model(), get_idx_market_status()),
272
+ outputs=[gr.State(), market_status_output],
273
+ show_progress=False
274
+ )
275
 
276
  return demo
277
 
278
  if __name__ == "__main__":
279
+ import atexit
280
+
281
  demo = create_interface()
282
+ # Cleanup atexit is registered inside create_interface
283
  demo.launch(share=True, server_name="0.0.0.0")