omniverse1 commited on
Commit
c930ac9
·
verified ·
1 Parent(s): fbcd158

Update utils.py

Browse files
Files changed (1) hide show
  1. utils.py +38 -282
utils.py CHANGED
@@ -9,7 +9,6 @@ from plotly.subplots import make_subplots
9
  import spaces
10
 
11
  def get_indonesian_stocks():
12
- """Get list of major Indonesian stocks"""
13
  return {
14
  "BBCA.JK": "Bank Central Asia",
15
  "BBRI.JK": "Bank BRI",
@@ -34,10 +33,7 @@ def get_indonesian_stocks():
34
  }
35
 
36
  def calculate_technical_indicators(data):
37
- """Calculate various technical indicators"""
38
  indicators = {}
39
-
40
- # RSI (Relative Strength Index)
41
  def calculate_rsi(prices, period=14):
42
  delta = prices.diff()
43
  gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
@@ -45,13 +41,10 @@ def calculate_technical_indicators(data):
45
  rs = gain / loss
46
  rsi = 100 - (100 / (1 + rs))
47
  return rsi
48
-
49
  indicators['rsi'] = {
50
  'current': calculate_rsi(data['Close']).iloc[-1],
51
  'values': calculate_rsi(data['Close'])
52
  }
53
-
54
- # MACD
55
  def calculate_macd(prices, fast=12, slow=26, signal=9):
56
  exp1 = prices.ewm(span=fast).mean()
57
  exp2 = prices.ewm(span=slow).mean()
@@ -59,7 +52,6 @@ def calculate_technical_indicators(data):
59
  signal_line = macd.ewm(span=signal).mean()
60
  histogram = macd - signal_line
61
  return macd, signal_line, histogram
62
-
63
  macd, signal_line, histogram = calculate_macd(data['Close'])
64
  indicators['macd'] = {
65
  'macd': macd.iloc[-1],
@@ -69,64 +61,45 @@ def calculate_technical_indicators(data):
69
  'macd_values': macd,
70
  'signal_values': signal_line
71
  }
72
-
73
- # Bollinger Bands
74
  def calculate_bollinger_bands(prices, period=20, std_dev=2):
75
  sma = prices.rolling(window=period).mean()
76
  std = prices.rolling(window=period).std()
77
  upper_band = sma + (std * std_dev)
78
  lower_band = sma - (std * std_dev)
79
  return upper_band, sma, lower_band
80
-
81
  upper, middle, lower = calculate_bollinger_bands(data['Close'])
82
  current_price = data['Close'].iloc[-1]
83
  bb_position = (current_price - lower.iloc[-1]) / (upper.iloc[-1] - lower.iloc[-1])
84
-
85
  indicators['bollinger'] = {
86
  'upper': upper.iloc[-1],
87
  'middle': middle.iloc[-1],
88
  'lower': lower.iloc[-1],
89
  'position': 'UPPER' if bb_position > 0.8 else 'LOWER' if bb_position < 0.2 else 'MIDDLE'
90
  }
91
-
92
- # Moving Averages
93
  sma_20_series = data['Close'].rolling(20).mean()
94
  sma_50_series = data['Close'].rolling(50).mean()
95
-
96
  indicators['moving_averages'] = {
97
- 'sma_20': sma_20_series.iloc[-1], # Current value
98
- 'sma_50': sma_50_series.iloc[-1], # Current value
99
  'sma_200': data['Close'].rolling(200).mean().iloc[-1],
100
  'ema_12': data['Close'].ewm(span=12).mean().iloc[-1],
101
  'ema_26': data['Close'].ewm(span=26).mean().iloc[-1],
102
-
103
- # ADDED: Full historical series for plotting
104
  'sma_20_values': sma_20_series,
105
  'sma_50_values': sma_50_series
106
  }
107
-
108
- # Volume indicators
109
  indicators['volume'] = {
110
  'current': data['Volume'].iloc[-1],
111
  'avg_20': data['Volume'].rolling(20).mean().iloc[-1],
112
  'ratio': data['Volume'].iloc[-1] / data['Volume'].rolling(20).mean().iloc[-1]
113
  }
114
-
115
  return indicators
116
 
117
  def generate_trading_signals(data, indicators):
118
- """Generate trading signals based on technical indicators"""
119
  signals = {}
120
-
121
  current_price = data['Close'].iloc[-1]
122
-
123
- # Initialize scores
124
  buy_signals = 0
125
  sell_signals = 0
126
-
127
  signal_details = []
128
-
129
- # RSI Signal
130
  rsi = indicators['rsi']['current']
131
  if rsi < 30:
132
  buy_signals += 1
@@ -136,8 +109,6 @@ def generate_trading_signals(data, indicators):
136
  signal_details.append(f"❌ RSI ({rsi:.1f}) - Overbought - SELL signal")
137
  else:
138
  signal_details.append(f"⚪ RSI ({rsi:.1f}) - Neutral")
139
-
140
- # MACD Signal
141
  macd_hist = indicators['macd']['histogram']
142
  if macd_hist > 0:
143
  buy_signals += 1
@@ -145,8 +116,6 @@ def generate_trading_signals(data, indicators):
145
  else:
146
  sell_signals += 1
147
  signal_details.append(f"❌ MACD Histogram ({macd_hist:.4f}) - Negative - SELL signal")
148
-
149
- # Bollinger Bands Signal
150
  bb_position = indicators['bollinger']['position']
151
  if bb_position == 'LOWER':
152
  buy_signals += 1
@@ -156,11 +125,8 @@ def generate_trading_signals(data, indicators):
156
  signal_details.append(f"❌ Bollinger Bands - Near upper band - SELL signal")
157
  else:
158
  signal_details.append("⚪ Bollinger Bands - Middle position")
159
-
160
- # Moving Averages Signal
161
  sma_20 = indicators['moving_averages']['sma_20']
162
  sma_50 = indicators['moving_averages']['sma_50']
163
-
164
  if current_price > sma_20 > sma_50:
165
  buy_signals += 1
166
  signal_details.append(f"✅ Price above MA(20,50) - Bullish - BUY signal")
@@ -169,8 +135,6 @@ def generate_trading_signals(data, indicators):
169
  signal_details.append(f"❌ Price below MA(20,50) - Bearish - SELL signal")
170
  else:
171
  signal_details.append("⚪ Moving Averages - Mixed signals")
172
-
173
- # Volume Signal
174
  volume_ratio = indicators['volume']['ratio']
175
  if volume_ratio > 1.5:
176
  buy_signals += 0.5
@@ -180,22 +144,16 @@ def generate_trading_signals(data, indicators):
180
  signal_details.append(f"❌ Low volume ({volume_ratio:.1f}x avg) - Weakens SELL signal")
181
  else:
182
  signal_details.append(f"⚪ Normal volume ({volume_ratio:.1f}x avg)")
183
-
184
- # Determine overall signal
185
  total_signals = buy_signals + sell_signals
186
  signal_strength = (buy_signals / max(total_signals, 1)) * 100
187
-
188
  if buy_signals > sell_signals:
189
  overall_signal = "BUY"
190
  elif sell_signals > buy_signals:
191
  overall_signal = "SELL"
192
  else:
193
  overall_signal = "HOLD"
194
-
195
- # Calculate support and resistance
196
  recent_high = data['High'].tail(20).max()
197
  recent_low = data['Low'].tail(20).min()
198
-
199
  signals = {
200
  'overall': overall_signal,
201
  'strength': signal_strength,
@@ -204,15 +162,12 @@ def generate_trading_signals(data, indicators):
204
  'resistance': recent_high,
205
  'stop_loss': recent_low * 0.95 if overall_signal == "BUY" else recent_high * 1.05
206
  }
207
-
208
  return signals
209
 
210
  def get_fundamental_data(stock):
211
- """Get fundamental data for the stock"""
212
  try:
213
  info = stock.info
214
  history = stock.history(period="1d")
215
-
216
  fundamental_info = {
217
  'name': info.get('longName', 'N/A'),
218
  'current_price': history['Close'].iloc[-1] if not history.empty else 0,
@@ -232,7 +187,6 @@ Book Value: {info.get('bookValue', 'N/A')}
232
  Price to Book: {info.get('priceToBook', 'N/A')}
233
  """.strip()
234
  }
235
-
236
  return fundamental_info
237
  except Exception as e:
238
  print(f"Error getting fundamental data: {e}")
@@ -247,7 +201,6 @@ Price to Book: {info.get('priceToBook', 'N/A')}
247
  }
248
 
249
  def format_large_number(num):
250
- """Format large numbers to readable format"""
251
  if num >= 1e12:
252
  return f"{num/1e12:.2f}T"
253
  elif num >= 1e9:
@@ -261,86 +214,50 @@ def format_large_number(num):
261
 
262
  @spaces.GPU(duration=120)
263
  def predict_prices(data, model, tokenizer, prediction_days=30):
264
- """Predict future prices using Chronos-Bolt model"""
265
  try:
266
- # Prepare data for prediction
267
  prices = data['Close'].values
268
  context_length = min(len(prices), 512)
269
-
270
- # Tokenize the input
271
  input_sequence = prices[-context_length:]
272
-
273
- # --- CRITICAL FIX: Simulate Quantization ---
274
- # 1. Normalize prices (0 to 1)
275
  price_min = np.min(input_sequence)
276
  price_max = np.max(input_sequence)
277
-
278
  if price_max == price_min:
279
- normalized_sequence = np.zeros_like(input_sequence)
280
  else:
281
- normalized_sequence = (input_sequence - price_min) / (price_max - price_min)
282
-
283
- # 2. Scale to a token space (max vocab size 4096) and convert to Long
284
- VOCAB_SIZE = 4096
285
- # Convert to Long/Int to satisfy model embedding layer
286
- token_indices = (normalized_sequence * (VOCAB_SIZE - 1)).astype(np.long)
287
-
288
- # Create prediction input
289
- # Pass tokens to the model
290
  prediction_input = torch.tensor(token_indices).unsqueeze(0).to(model.device)
291
- # --- END CRITICAL FIX ---
292
-
293
- # Generate predictions
294
  with torch.no_grad():
295
- # Use max_new_tokens for generation length.
296
- # do_sample is necessary for generating probabilistic time-series forecasts
297
- forecast = model.generate(
298
- prediction_input,
299
- max_new_tokens=prediction_days,
300
- do_sample=True
301
- )
302
-
303
- # Handle complex Chronos output: [batch_size, num_samples, prediction_length]
304
  output_tensor = forecast[0] if isinstance(forecast, (tuple, list)) else forecast
305
-
306
- # Average across the samples and convert to a simple 1D numpy array
307
- # Note: The output is still in TOKEN SPACE. We must INVERSE-SCALE it back to PRICE SPACE.
308
  predictions_tokens = output_tensor.float().mean(dim=1).squeeze().cpu().numpy()
309
-
310
- # --- CRITICAL INVERSE-SCALE FIX ---
311
- # Inverse normalize the predicted tokens back to the price range
312
- predictions = (predictions_tokens / (VOCAB_SIZE - 1)) * (price_max - price_min) + price_min
313
-
314
- # Handle case where predictions is a single scalar (convert to array for safety)
315
  if predictions.ndim == 0:
316
- predictions = np.array([predictions.item()])
317
-
318
- # Use actual prediction length from the output tensor
319
  pred_len = len(predictions)
320
-
321
- # Calculate prediction statistics
322
  last_price = prices[-1]
323
  predicted_high = np.max(predictions)
324
  predicted_low = np.min(predictions)
325
  predicted_mean = np.mean(predictions)
326
  change_pct = ((predicted_mean - last_price) / last_price) * 100
327
-
328
  return {
329
  'values': predictions,
330
- 'dates': pd.date_range(
331
- start=data.index[-1] + timedelta(days=1),
332
- periods=pred_len,
333
- freq='D'
334
- ),
335
  'high_30d': predicted_high,
336
  'low_30d': predicted_low,
337
  'mean_30d': predicted_mean,
338
  'change_pct': change_pct,
339
  'summary': f"""
340
- AI Model: Amazon Chronos-Bolt (Simulated Quantization)
341
  Prediction Period: {pred_len} days
342
  Expected Change: {change_pct:.2f}%
343
- Confidence: Medium (based on historical patterns)
344
  Note: AI predictions are for reference only and not financial advice
345
  """.strip()
346
  }
@@ -357,198 +274,37 @@ Note: AI predictions are for reference only and not financial advice
357
  }
358
 
359
  def create_price_chart(data, indicators):
360
- """Create price chart with technical indicators"""
361
- fig = make_subplots(
362
- rows=3, cols=1,
363
- shared_xaxes=True,
364
- vertical_spacing=0.05,
365
- subplot_titles=('Price & Moving Averages', 'RSI', 'MACD'),
366
- row_width=[0.2, 0.2, 0.7]
367
- )
368
-
369
- # Price and Moving Averages
370
- fig.add_trace(
371
- go.Candlestick(
372
- x=data.index,
373
- open=data['Open'],
374
- high=data['High'],
375
- low=data['Low'],
376
- close=data['Close'],
377
- name='Price'
378
- ),
379
- row=1, col=1
380
- )
381
-
382
- # Add moving averages
383
- fig.add_trace(
384
- go.Scatter(
385
- x=data.index,
386
- y=indicators['moving_averages']['sma_20_values'],
387
- name='SMA 20',
388
- line=dict(color='orange', width=1)
389
- ),
390
- row=1, col=1
391
- )
392
-
393
- fig.add_trace(
394
- go.Scatter(
395
- x=data.index,
396
- y=indicators['moving_averages']['sma_50_values'],
397
- name='SMA 50',
398
- line=dict(color='blue', width=1)
399
- ),
400
- row=1, col=1
401
- )
402
-
403
- # RSI
404
- fig.add_trace(
405
- go.Scatter(
406
- x=data.index,
407
- y=indicators['rsi']['values'],
408
- name='RSI',
409
- line=dict(color='purple')
410
- ),
411
- row=2, col=1
412
- )
413
-
414
  fig.add_hline(y=70, line_dash="dash", line_color="red", row=2, col=1)
415
  fig.add_hline(y=30, line_dash="dash", line_color="green", row=2, col=1)
416
-
417
- # MACD
418
- fig.add_trace(
419
- go.Scatter(
420
- x=data.index,
421
- y=indicators['macd']['macd_values'],
422
- name='MACD',
423
- line=dict(color='blue')
424
- ),
425
- row=3, col=1
426
- )
427
-
428
- fig.add_trace(
429
- go.Scatter(
430
- x=data.index,
431
- y=indicators['macd']['signal_values'],
432
- name='Signal',
433
- line=dict(color='red')
434
- ),
435
- row=3, col=1
436
- )
437
-
438
- fig.update_layout(
439
- title='Technical Analysis Dashboard',
440
- height=900,
441
- showlegend=True,
442
- xaxis_rangeslider_visible=False
443
- )
444
-
445
  return fig
446
 
447
  def create_technical_chart(data, indicators):
448
- """Create technical indicators dashboard"""
449
- fig = make_subplots(
450
- rows=2, cols=2,
451
- subplot_titles=('Bollinger Bands', 'Volume', 'Price vs MA', 'RSI Analysis'),
452
- specs=[[{"secondary_y": False}, {"secondary_y": False}],
453
- [{"secondary_y": False}, {"secondary_y": False}]]
454
- )
455
-
456
- # Bollinger Bands
457
- fig.add_trace(
458
- go.Scatter(x=data.index, y=data['Close'], name='Price', line=dict(color='black')),
459
- row=1, col=1
460
- )
461
-
462
- # Volume
463
- fig.add_trace(
464
- go.Bar(x=data.index, y=data['Volume'], name='Volume', marker_color='lightblue'),
465
- row=1, col=2
466
- )
467
-
468
- # Price vs Moving Averages
469
- fig.add_trace(
470
- go.Scatter(x=data.index, y=data['Close'], name='Price', line=dict(color='black')),
471
- row=2, col=1
472
- )
473
-
474
- fig.add_trace(
475
- go.Scatter(
476
- x=data.index,
477
- y=indicators['moving_averages']['sma_20_values'],
478
- name='SMA 20',
479
- line=dict(color='orange', dash='dash')
480
- ),
481
- row=2, col=1
482
- )
483
-
484
- fig.update_layout(
485
- title='Technical Indicators Overview',
486
- height=600,
487
- showlegend=False
488
- )
489
-
490
  return fig
491
 
492
  def create_prediction_chart(data, predictions):
493
- """Create prediction visualization"""
494
- # Use len() check which works for both list and numpy array
495
  if not len(predictions['values']):
496
  return go.Figure()
497
-
498
  fig = go.Figure()
499
-
500
- # Historical prices
501
- fig.add_trace(
502
- go.Scatter(
503
- x=data.index[-60:],
504
- y=data['Close'].values[-60:],
505
- name='Historical Price',
506
- line=dict(color='blue', width=2)
507
- )
508
- )
509
-
510
- # Predictions
511
- fig.add_trace(
512
- go.Scatter(
513
- x=predictions['dates'],
514
- y=predictions['values'],
515
- name='AI Prediction',
516
- line=dict(color='red', width=2, dash='dash')
517
- )
518
- )
519
-
520
- # Confidence interval (simple)
521
  pred_std = np.std(predictions['values'])
522
  upper_band = predictions['values'] + (pred_std * 1.96)
523
  lower_band = predictions['values'] - (pred_std * 1.96)
524
-
525
- fig.add_trace(
526
- go.Scatter(
527
- x=predictions['dates'],
528
- y=upper_band,
529
- name='Upper Band',
530
- line=dict(color='lightcoral', width=1),
531
- fill=None
532
- )
533
- )
534
-
535
- fig.add_trace(
536
- go.Scatter(
537
- x=predictions['dates'],
538
- y=lower_band,
539
- name='Lower Band',
540
- line=dict(color='lightcoral', width=1),
541
- fill='tonexty',
542
- fillcolor='rgba(255,182,193,0.2)'
543
- )
544
- )
545
-
546
- fig.update_layout(
547
- title=f'Price Prediction - Next {len(predictions["dates"])} Days',
548
- xaxis_title='Date',
549
- yaxis_title='Price (IDR)',
550
- hovermode='x unified',
551
- height=500
552
- )
553
-
554
- return fig
 
9
  import spaces
10
 
11
  def get_indonesian_stocks():
 
12
  return {
13
  "BBCA.JK": "Bank Central Asia",
14
  "BBRI.JK": "Bank BRI",
 
33
  }
34
 
35
  def calculate_technical_indicators(data):
 
36
  indicators = {}
 
 
37
  def calculate_rsi(prices, period=14):
38
  delta = prices.diff()
39
  gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
 
41
  rs = gain / loss
42
  rsi = 100 - (100 / (1 + rs))
43
  return rsi
 
44
  indicators['rsi'] = {
45
  'current': calculate_rsi(data['Close']).iloc[-1],
46
  'values': calculate_rsi(data['Close'])
47
  }
 
 
48
  def calculate_macd(prices, fast=12, slow=26, signal=9):
49
  exp1 = prices.ewm(span=fast).mean()
50
  exp2 = prices.ewm(span=slow).mean()
 
52
  signal_line = macd.ewm(span=signal).mean()
53
  histogram = macd - signal_line
54
  return macd, signal_line, histogram
 
55
  macd, signal_line, histogram = calculate_macd(data['Close'])
56
  indicators['macd'] = {
57
  'macd': macd.iloc[-1],
 
61
  'macd_values': macd,
62
  'signal_values': signal_line
63
  }
 
 
64
  def calculate_bollinger_bands(prices, period=20, std_dev=2):
65
  sma = prices.rolling(window=period).mean()
66
  std = prices.rolling(window=period).std()
67
  upper_band = sma + (std * std_dev)
68
  lower_band = sma - (std * std_dev)
69
  return upper_band, sma, lower_band
 
70
  upper, middle, lower = calculate_bollinger_bands(data['Close'])
71
  current_price = data['Close'].iloc[-1]
72
  bb_position = (current_price - lower.iloc[-1]) / (upper.iloc[-1] - lower.iloc[-1])
 
73
  indicators['bollinger'] = {
74
  'upper': upper.iloc[-1],
75
  'middle': middle.iloc[-1],
76
  'lower': lower.iloc[-1],
77
  'position': 'UPPER' if bb_position > 0.8 else 'LOWER' if bb_position < 0.2 else 'MIDDLE'
78
  }
 
 
79
  sma_20_series = data['Close'].rolling(20).mean()
80
  sma_50_series = data['Close'].rolling(50).mean()
 
81
  indicators['moving_averages'] = {
82
+ 'sma_20': sma_20_series.iloc[-1],
83
+ 'sma_50': sma_50_series.iloc[-1],
84
  'sma_200': data['Close'].rolling(200).mean().iloc[-1],
85
  'ema_12': data['Close'].ewm(span=12).mean().iloc[-1],
86
  'ema_26': data['Close'].ewm(span=26).mean().iloc[-1],
 
 
87
  'sma_20_values': sma_20_series,
88
  'sma_50_values': sma_50_series
89
  }
 
 
90
  indicators['volume'] = {
91
  'current': data['Volume'].iloc[-1],
92
  'avg_20': data['Volume'].rolling(20).mean().iloc[-1],
93
  'ratio': data['Volume'].iloc[-1] / data['Volume'].rolling(20).mean().iloc[-1]
94
  }
 
95
  return indicators
96
 
97
  def generate_trading_signals(data, indicators):
 
98
  signals = {}
 
99
  current_price = data['Close'].iloc[-1]
 
 
100
  buy_signals = 0
101
  sell_signals = 0
 
102
  signal_details = []
 
 
103
  rsi = indicators['rsi']['current']
104
  if rsi < 30:
105
  buy_signals += 1
 
109
  signal_details.append(f"❌ RSI ({rsi:.1f}) - Overbought - SELL signal")
110
  else:
111
  signal_details.append(f"⚪ RSI ({rsi:.1f}) - Neutral")
 
 
112
  macd_hist = indicators['macd']['histogram']
113
  if macd_hist > 0:
114
  buy_signals += 1
 
116
  else:
117
  sell_signals += 1
118
  signal_details.append(f"❌ MACD Histogram ({macd_hist:.4f}) - Negative - SELL signal")
 
 
119
  bb_position = indicators['bollinger']['position']
120
  if bb_position == 'LOWER':
121
  buy_signals += 1
 
125
  signal_details.append(f"❌ Bollinger Bands - Near upper band - SELL signal")
126
  else:
127
  signal_details.append("⚪ Bollinger Bands - Middle position")
 
 
128
  sma_20 = indicators['moving_averages']['sma_20']
129
  sma_50 = indicators['moving_averages']['sma_50']
 
130
  if current_price > sma_20 > sma_50:
131
  buy_signals += 1
132
  signal_details.append(f"✅ Price above MA(20,50) - Bullish - BUY signal")
 
135
  signal_details.append(f"❌ Price below MA(20,50) - Bearish - SELL signal")
136
  else:
137
  signal_details.append("⚪ Moving Averages - Mixed signals")
 
 
138
  volume_ratio = indicators['volume']['ratio']
139
  if volume_ratio > 1.5:
140
  buy_signals += 0.5
 
144
  signal_details.append(f"❌ Low volume ({volume_ratio:.1f}x avg) - Weakens SELL signal")
145
  else:
146
  signal_details.append(f"⚪ Normal volume ({volume_ratio:.1f}x avg)")
 
 
147
  total_signals = buy_signals + sell_signals
148
  signal_strength = (buy_signals / max(total_signals, 1)) * 100
 
149
  if buy_signals > sell_signals:
150
  overall_signal = "BUY"
151
  elif sell_signals > buy_signals:
152
  overall_signal = "SELL"
153
  else:
154
  overall_signal = "HOLD"
 
 
155
  recent_high = data['High'].tail(20).max()
156
  recent_low = data['Low'].tail(20).min()
 
157
  signals = {
158
  'overall': overall_signal,
159
  'strength': signal_strength,
 
162
  'resistance': recent_high,
163
  'stop_loss': recent_low * 0.95 if overall_signal == "BUY" else recent_high * 1.05
164
  }
 
165
  return signals
166
 
167
  def get_fundamental_data(stock):
 
168
  try:
169
  info = stock.info
170
  history = stock.history(period="1d")
 
171
  fundamental_info = {
172
  'name': info.get('longName', 'N/A'),
173
  'current_price': history['Close'].iloc[-1] if not history.empty else 0,
 
187
  Price to Book: {info.get('priceToBook', 'N/A')}
188
  """.strip()
189
  }
 
190
  return fundamental_info
191
  except Exception as e:
192
  print(f"Error getting fundamental data: {e}")
 
201
  }
202
 
203
  def format_large_number(num):
 
204
  if num >= 1e12:
205
  return f"{num/1e12:.2f}T"
206
  elif num >= 1e9:
 
214
 
215
  @spaces.GPU(duration=120)
216
  def predict_prices(data, model, tokenizer, prediction_days=30):
 
217
  try:
 
218
  prices = data['Close'].values
219
  context_length = min(len(prices), 512)
 
 
220
  input_sequence = prices[-context_length:]
 
 
 
221
  price_min = np.min(input_sequence)
222
  price_max = np.max(input_sequence)
 
223
  if price_max == price_min:
224
+ normalized_sequence = np.zeros_like(input_sequence)
225
  else:
226
+ normalized_sequence = (input_sequence - price_min) / (price_max - price_min)
227
+ VOCAB_SIZE = getattr(model.config, "vocab_size", 2)
228
+ if VOCAB_SIZE == 2:
229
+ token_indices = (normalized_sequence > 0.5).astype(np.int64)
230
+ else:
231
+ token_indices = (normalized_sequence * (VOCAB_SIZE - 1)).astype(np.int64)
 
 
 
232
  prediction_input = torch.tensor(token_indices).unsqueeze(0).to(model.device)
 
 
 
233
  with torch.no_grad():
234
+ forecast = model.generate(prediction_input, max_new_tokens=prediction_days, do_sample=True)
 
 
 
 
 
 
 
 
235
  output_tensor = forecast[0] if isinstance(forecast, (tuple, list)) else forecast
 
 
 
236
  predictions_tokens = output_tensor.float().mean(dim=1).squeeze().cpu().numpy()
237
+ if VOCAB_SIZE == 2:
238
+ predictions = predictions_tokens * (price_max - price_min) + price_min
239
+ else:
240
+ predictions = (predictions_tokens / (VOCAB_SIZE - 1)) * (price_max - price_min) + price_min
 
 
241
  if predictions.ndim == 0:
242
+ predictions = np.array([predictions.item()])
 
 
243
  pred_len = len(predictions)
 
 
244
  last_price = prices[-1]
245
  predicted_high = np.max(predictions)
246
  predicted_low = np.min(predictions)
247
  predicted_mean = np.mean(predictions)
248
  change_pct = ((predicted_mean - last_price) / last_price) * 100
 
249
  return {
250
  'values': predictions,
251
+ 'dates': pd.date_range(start=data.index[-1] + timedelta(days=1), periods=pred_len, freq='D'),
 
 
 
 
252
  'high_30d': predicted_high,
253
  'low_30d': predicted_low,
254
  'mean_30d': predicted_mean,
255
  'change_pct': change_pct,
256
  'summary': f"""
257
+ AI Model: Amazon Chronos-Bolt (Binary Quantization)
258
  Prediction Period: {pred_len} days
259
  Expected Change: {change_pct:.2f}%
260
+ Confidence: Medium
261
  Note: AI predictions are for reference only and not financial advice
262
  """.strip()
263
  }
 
274
  }
275
 
276
  def create_price_chart(data, indicators):
277
+ fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.05, subplot_titles=('Price & Moving Averages', 'RSI', 'MACD'), row_width=[0.2, 0.2, 0.7])
278
+ fig.add_trace(go.Candlestick(x=data.index, open=data['Open'], high=data['High'], low=data['Low'], close=data['Close'], name='Price'), row=1, col=1)
279
+ fig.add_trace(go.Scatter(x=data.index, y=indicators['moving_averages']['sma_20_values'], name='SMA 20', line=dict(color='orange', width=1)), row=1, col=1)
280
+ fig.add_trace(go.Scatter(x=data.index, y=indicators['moving_averages']['sma_50_values'], name='SMA 50', line=dict(color='blue', width=1)), row=1, col=1)
281
+ fig.add_trace(go.Scatter(x=data.index, y=indicators['rsi']['values'], name='RSI', line=dict(color='purple')), row=2, col=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  fig.add_hline(y=70, line_dash="dash", line_color="red", row=2, col=1)
283
  fig.add_hline(y=30, line_dash="dash", line_color="green", row=2, col=1)
284
+ fig.add_trace(go.Scatter(x=data.index, y=indicators['macd']['macd_values'], name='MACD', line=dict(color='blue')), row=3, col=1)
285
+ fig.add_trace(go.Scatter(x=data.index, y=indicators['macd']['signal_values'], name='Signal', line=dict(color='red')), row=3, col=1)
286
+ fig.update_layout(title='Technical Analysis Dashboard', height=900, showlegend=True, xaxis_rangeslider_visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  return fig
288
 
289
  def create_technical_chart(data, indicators):
290
+ fig = make_subplots(rows=2, cols=2, subplot_titles=('Bollinger Bands', 'Volume', 'Price vs MA', 'RSI Analysis'), specs=[[{"secondary_y": False}, {"secondary_y": False}], [{"secondary_y": False}, {"secondary_y": False}]])
291
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], name='Price', line=dict(color='black')), row=1, col=1)
292
+ fig.add_trace(go.Bar(x=data.index, y=data['Volume'], name='Volume', marker_color='lightblue'), row=1, col=2)
293
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], name='Price', line=dict(color='black')), row=2, col=1)
294
+ fig.add_trace(go.Scatter(x=data.index, y=indicators['moving_averages']['sma_20_values'], name='SMA 20', line=dict(color='orange', dash='dash')), row=2, col=1)
295
+ fig.update_layout(title='Technical Indicators Overview', height=600, showlegend=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  return fig
297
 
298
  def create_prediction_chart(data, predictions):
 
 
299
  if not len(predictions['values']):
300
  return go.Figure()
 
301
  fig = go.Figure()
302
+ fig.add_trace(go.Scatter(x=data.index[-60:], y=data['Close'].values[-60:], name='Historical Price', line=dict(color='blue', width=2)))
303
+ fig.add_trace(go.Scatter(x=predictions['dates'], y=predictions['values'], name='AI Prediction', line=dict(color='red', width=2, dash='dash')))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  pred_std = np.std(predictions['values'])
305
  upper_band = predictions['values'] + (pred_std * 1.96)
306
  lower_band = predictions['values'] - (pred_std * 1.96)
307
+ fig.add_trace(go.Scatter(x=predictions['dates'], y=upper_band, name='Upper Band', line=dict(color='lightcoral', width=1), fill=None))
308
+ fig.add_trace(go.Scatter(x=predictions['dates'], y=lower_band, name='Lower Band', line=dict(color='lightcoral', width=1), fill='tonexty', fillcolor='rgba(255,182,193,0.2)'))
309
+ fig.update_layout(title=f'Price Prediction - Next {len(predictions["dates"])} Days', xaxis_title='Date', yaxis_title='Price (IDR)', hovermode='x unified', height=500)
310
+ return fig