omniverse1 commited on
Commit
a469221
·
verified ·
1 Parent(s): 9bf8edc

Update Gradio app with multiple files

Browse files
Files changed (6) hide show
  1. app.py +35 -93
  2. data_processor.py +24 -38
  3. plotter.py +38 -40
  4. requirements.txt +16 -10
  5. sentiment_analyzer.py +3 -11
  6. trading_logic.py +1 -3
app.py CHANGED
@@ -35,11 +35,7 @@ def create_chart_analysis(ticker, interval):
35
  # Buat chart menggunakan MPLFINANCE (dikembalikan sebagai HTML)
36
  chart_html = create_mplfinance_chart(
37
  df,
38
- ticker=f'{
39
- ticker
40
- } ({
41
- interval
42
- })',
43
  predictions=predictions
44
  )
45
 
@@ -56,35 +52,20 @@ interval
56
  # Create metrics display
57
  metrics = {
58
  "Ticker": ticker,
59
- "Current Price": f"${current_price:.2f
60
- }",
61
  "Signal": signal.upper(),
62
- "Confidence": f"{
63
- confidence:.1%
64
- }",
65
- "Take Profit": f"${
66
- tp:.2f
67
- }" if tp else "N/A",
68
- "Stop Loss": f"${
69
- sl:.2f
70
- }" if sl else "N/A",
71
- "RSI": f"{
72
- df['RSI'].iloc[-1]:.1f
73
- }",
74
- "MACD": f"{
75
- df['MACD'].iloc[-1]:.4f
76
- }",
77
- "Volume": f"{
78
- df['Volume'].iloc[-1]:,.0f
79
- }"
80
  }
81
 
82
  return chart_html, metrics
83
 
84
  except Exception as e:
85
- return f"Error creating chart: {
86
- e
87
- }", None
88
 
89
  def analyze_sentiment(ticker):
90
  """Analyze gold/crypto market sentiment"""
@@ -96,33 +77,19 @@ def analyze_sentiment(ticker):
96
  fig = go.Figure(go.Indicator(
97
  mode="gauge+number+delta",
98
  value=sentiment_score,
99
- domain={
100
- 'x': [0, 1], 'y': [0, 1]
101
- },
102
- title={
103
- 'text': f"{ticker
104
- } Market Sentiment (Simulated)"},
105
- delta={
106
- 'reference': 0
107
- },
108
  gauge={
109
- 'axis': {'range': [-1, 1]
110
- },
111
- 'bar': {
112
- 'color': "#FFD700"
113
- },
114
  'steps': [
115
- {
116
- 'range': [-1, -0.5], 'color': "rgba(255,0,0,0.5)"
117
- },
118
- {
119
- 'range': [gauge_range[1] * 0.7, gauge_range[1]], 'color': "rgba(0,255,0,0.5)"
120
- },
121
  {'range': [0.5, 1], 'color': "rgba(0,255,0,0.5)"}
122
  ],
123
  'threshold': {
124
- 'line': {'color': "black", 'width': 4
125
- },
126
  'thickness': 0.75,
127
  'value': 0
128
  }
@@ -165,19 +132,12 @@ def get_fundamentals(ticker):
165
  fig = go.Figure(go.Indicator(
166
  mode="gauge+number",
167
  value=gauge_value,
168
- title={
169
- 'text': gauge_title
170
- },
171
  gauge={
172
- 'axis': {'range': gauge_range
173
- },
174
- 'bar': {
175
- 'color': "#FFD700"
176
- },
177
  'steps': [
178
- {
179
- 'range': [gauge_range[0], gauge_range[1] * 0.3], 'color': "rgba(255,0,0,0.5)"
180
- },
181
  {'range': [gauge_range[1] * 0.3, gauge_range[1] * 0.7], 'color': "rgba(100,100,100,0.3)"},
182
  {'range': [gauge_range[1] * 0.7, gauge_range[1]], 'color': "rgba(0,255,0,0.5)"}
183
  ]
@@ -202,40 +162,22 @@ with gr.Blocks(
202
  theme=gr.themes.Default(primary_hue="yellow", secondary_hue="yellow"),
203
  title="Ultimate Market Analysis & Prediction",
204
  css="""
205
- .gradio-container {
206
- background-color: #f0f4f9; color: black
207
- }
208
- .gr-button-primary {
209
- background-color: #FFD700 !important; color: #000000 !important
210
- }
211
- .gr-button-secondary {
212
- border-color: #FFD700 !important; color: #000000 !important
213
- }
214
- .gr-tab button {
215
- color: black !important
216
- }
217
- .gr-tab button.selected {
218
- background-color: #FFD700 !important; color: #000000 !important
219
- }
220
- .gr-highlighted {
221
- background-color: #CCCCCC !important
222
- }
223
- .anycoder-link {
224
- color: #FFD700 !important; text-decoration: none; font-weight: bold
225
- }
226
  .mpl-chart-container {
227
  border: 1px solid #CCCCCC;
228
- border-radius: 5px;
229
- overflow: hidden;
230
- background: white;
231
- width: 100%;
232
- }
233
- .chart-title {
234
- color: black !important;
235
- }
236
- .metric-label {
237
- color: black !important;
238
- }
239
  """
240
  ) as demo:
241
 
 
35
  # Buat chart menggunakan MPLFINANCE (dikembalikan sebagai HTML)
36
  chart_html = create_mplfinance_chart(
37
  df,
38
+ ticker=f'{ticker} ({interval})',
 
 
 
 
39
  predictions=predictions
40
  )
41
 
 
52
  # Create metrics display
53
  metrics = {
54
  "Ticker": ticker,
55
+ "Current Price": f"${current_price:.2f}",
 
56
  "Signal": signal.upper(),
57
+ "Confidence": f"{confidence:.1%}",
58
+ "Take Profit": f"${tp:.2f}" if tp else "N/A",
59
+ "Stop Loss": f"${sl:.2f}" if sl else "N/A",
60
+ "RSI": f"{df['RSI'].iloc[-1]:.1f}",
61
+ "MACD": f"{df['MACD'].iloc[-1]:.4f}",
62
+ "Volume": f"{df['Volume'].iloc[-1]:,.0f}"
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
 
65
  return chart_html, metrics
66
 
67
  except Exception as e:
68
+ return f"Error creating chart: {str(e)}", None
 
 
69
 
70
  def analyze_sentiment(ticker):
71
  """Analyze gold/crypto market sentiment"""
 
77
  fig = go.Figure(go.Indicator(
78
  mode="gauge+number+delta",
79
  value=sentiment_score,
80
+ domain={'x': [0, 1], 'y': [0, 1]},
81
+ title={'text': f"{ticker} Market Sentiment (Simulated)"},
82
+ delta={'reference': 0},
 
 
 
 
 
 
83
  gauge={
84
+ 'axis': {'range': [-1, 1]},
85
+ 'bar': {'color': "#FFD700"},
 
 
 
86
  'steps': [
87
+ {'range': [-1, -0.5], 'color': "rgba(255,0,0,0.5)"},
88
+ {'range': [-0.5, 0.5], 'color': "rgba(100,100,100,0.3)"},
 
 
 
 
89
  {'range': [0.5, 1], 'color': "rgba(0,255,0,0.5)"}
90
  ],
91
  'threshold': {
92
+ 'line': {'color': "black", 'width': 4},
 
93
  'thickness': 0.75,
94
  'value': 0
95
  }
 
132
  fig = go.Figure(go.Indicator(
133
  mode="gauge+number",
134
  value=gauge_value,
135
+ title={'text': gauge_title},
 
 
136
  gauge={
137
+ 'axis': {'range': gauge_range},
138
+ 'bar': {'color': "#FFD700"},
 
 
 
139
  'steps': [
140
+ {'range': [gauge_range[0], gauge_range[1] * 0.3], 'color': "rgba(255,0,0,0.5)"},
 
 
141
  {'range': [gauge_range[1] * 0.3, gauge_range[1] * 0.7], 'color': "rgba(100,100,100,0.3)"},
142
  {'range': [gauge_range[1] * 0.7, gauge_range[1]], 'color': "rgba(0,255,0,0.5)"}
143
  ]
 
162
  theme=gr.themes.Default(primary_hue="yellow", secondary_hue="yellow"),
163
  title="Ultimate Market Analysis & Prediction",
164
  css="""
165
+ .gradio-container {background-color: #f0f4f9; color: black}
166
+ .gr-button-primary {background-color: #FFD700 !important; color: #000000 !important}
167
+ .gr-button-secondary {border-color: #FFD700 !important; color: #000000 !important}
168
+ .gr-tab button {color: black !important}
169
+ .gr-tab button.selected {background-color: #FFD700 !important; color: #000000 !important}
170
+ .gr-highlighted {background-color: #CCCCCC !important}
171
+ .anycoder-link {color: #FFD700 !important; text-decoration: none; font-weight: bold}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  .mpl-chart-container {
173
  border: 1px solid #CCCCCC;
174
+ border-radius: 5px;
175
+ overflow: hidden;
176
+ background: white;
177
+ width: 100%;
178
+ }
179
+ .chart-title {color: black !important;}
180
+ .metric-label {color: black !important;}
 
 
 
 
181
  """
182
  ) as demo:
183
 
data_processor.py CHANGED
@@ -4,15 +4,12 @@ import numpy as np
4
  from datetime import datetime, timedelta
5
 
6
  class DataProcessor:
7
- # Hapus self.ticker di __init__
8
  def __init__(self):
9
  self.fundamentals_cache = {}
10
 
11
- # Tambahkan 'ticker' sebagai argumen pertama
12
  def get_market_data(self, ticker="GC=F", interval="1d"):
13
  """Fetch market data from Yahoo Finance for a given ticker"""
14
  try:
15
- # Map internal intervals to yfinance format
16
  interval_map = {
17
  "5m": "5m",
18
  "15m": "15m",
@@ -27,9 +24,8 @@ class DataProcessor:
27
 
28
  yf_interval = interval_map.get(interval, "1d")
29
 
30
- # Determine appropriate period based on interval
31
  if interval in ["5m", "15m", "30m", "1h", "4h"]:
32
- period = "60d" # Intraday data limited to 60 days
33
  elif interval in ["1d"]:
34
  period = "1y"
35
  elif interval in ["1wk"]:
@@ -37,43 +33,42 @@ class DataProcessor:
37
  else:
38
  period = "max"
39
 
40
- # Use the passed ticker
41
  ticker_obj = yf.Ticker(ticker)
42
  df = ticker_obj.history(interval=yf_interval, period=period)
43
 
44
  if df.empty:
45
  raise ValueError(f"No data retrieved from Yahoo Finance for {ticker}")
46
 
47
- # Ensure proper column names
48
  df.columns = [col.capitalize() for col in df.columns]
49
 
50
- # mplfinance requires a datetime index (already provided by yfinance)
51
-
52
  return df
53
 
54
  except Exception as e:
55
  print(f"Error fetching data for {ticker}: {e}")
56
  return pd.DataFrame()
57
 
58
- # calculate_indicators tetap sama, tidak perlu perubahan
59
  def calculate_indicators(self, df):
60
  """Calculate technical indicators"""
61
  if df.empty:
62
  return df
63
 
64
- # Simple Moving Averages
 
65
  df['SMA_20'] = df['Close'].rolling(window=20).mean()
66
- df['SMA_50'] = df['Close'].rolling(window=50).mean()
67
 
68
  # Exponential Moving Averages
69
  df['EMA_12'] = df['Close'].ewm(span=12, adjust=False).mean()
70
  df['EMA_26'] = df['Close'].ewm(span=26, adjust=False).mean()
71
 
72
- # MACD
73
  df['MACD'] = df['EMA_12'] - df['EMA_26']
74
  df['MACD_signal'] = df['MACD'].ewm(span=9, adjust=False).mean()
75
  df['MACD_histogram'] = df['MACD'] - df['MACD_signal']
76
 
 
 
 
 
77
  # RSI
78
  delta = df['Close'].diff()
79
  gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
@@ -99,63 +94,54 @@ class DataProcessor:
99
  df['Volume_SMA'] = df['Volume'].rolling(window=20).mean()
100
  df['Volume_ratio'] = df['Volume'] / df['Volume_SMA']
101
 
 
 
 
 
 
 
 
 
 
102
  return df
103
 
104
- # get_fundamental_data tetap sama (mock)
105
  def get_fundamental_data(self, ticker="GC=F"):
106
  """Get fundamental gold market data (now generalized/mocked)"""
107
  try:
108
- # Note: We keep this mocked as per previous discussion, but now accept ticker
109
-
110
  if ticker == "BTC-USD":
111
  fundamentals = {
112
  "Crypto Volatility Index": round(np.random.uniform(50, 150), 1),
113
  "Dominance Index": f"{np.random.uniform(40, 60):.2f}%",
114
  "Fear & Greed Index": np.random.choice(["Extreme Fear", "Fear", "Neutral", "Greed", "Extreme Greed"]),
115
  "Hash Rate Trend": np.random.choice(["Increasing", "Stable", "Decreasing"]),
116
- "Institutional Flow (Net)": f"{
117
- np.random.uniform(-100, 100):,.0f
118
- }M USD",
119
  "Market Sentiment": np.random.choice(["Bullish", "Neutral", "Bearish"]),
120
  }
121
- else: # Default Gold (GC=F)
122
  fundamentals = {
123
  "Gold Strength Index": round(np.random.uniform(30, 80), 1),
124
  "Dollar Index (DXY)": round(np.random.uniform(90, 110), 1),
125
  "Real Interest Rate": f"{np.random.uniform(-2, 5):.2f}%",
126
- "Gold Volatility": f"{
127
- np.random.uniform(10, 40):.1f
128
- }%",
129
- "Commercial Hedgers (Net)": f"{
130
- np.random.uniform(-50000, 50000):,.0f
131
- }",
132
- "Managed Money (Net)": f"{
133
- np.random.uniform(-100000, 100000):,.0f
134
- }",
135
  "Market Sentiment": np.random.choice(["Bullish", "Neutral", "Bearish"]),
136
  }
137
 
138
  return fundamentals
139
 
140
  except Exception as e:
141
- print(f"Error fetching fundamentals: {
142
- e
143
- }")
144
- return {
145
- "Error": str(e)
146
- }
147
 
148
- # prepare_for_chronos tetap sama, tidak perlu perubahan
149
  def prepare_for_chronos(self, df, lookback=100):
150
  """Prepare data for Chronos model"""
151
  if df.empty or len(df) < lookback:
152
  return None
153
 
154
- # Use close prices and normalize
155
  prices = df['Close'].iloc[-lookback:].values
156
  prices = prices.astype(np.float32)
157
 
158
- # Normalize to help model performance
159
  mean = np.mean(prices)
160
  std = np.std(prices)
161
  normalized = (prices - mean) / (std + 1e-8)
 
4
  from datetime import datetime, timedelta
5
 
6
  class DataProcessor:
 
7
  def __init__(self):
8
  self.fundamentals_cache = {}
9
 
 
10
  def get_market_data(self, ticker="GC=F", interval="1d"):
11
  """Fetch market data from Yahoo Finance for a given ticker"""
12
  try:
 
13
  interval_map = {
14
  "5m": "5m",
15
  "15m": "15m",
 
24
 
25
  yf_interval = interval_map.get(interval, "1d")
26
 
 
27
  if interval in ["5m", "15m", "30m", "1h", "4h"]:
28
+ period = "60d"
29
  elif interval in ["1d"]:
30
  period = "1y"
31
  elif interval in ["1wk"]:
 
33
  else:
34
  period = "max"
35
 
 
36
  ticker_obj = yf.Ticker(ticker)
37
  df = ticker_obj.history(interval=yf_interval, period=period)
38
 
39
  if df.empty:
40
  raise ValueError(f"No data retrieved from Yahoo Finance for {ticker}")
41
 
 
42
  df.columns = [col.capitalize() for col in df.columns]
43
 
 
 
44
  return df
45
 
46
  except Exception as e:
47
  print(f"Error fetching data for {ticker}: {e}")
48
  return pd.DataFrame()
49
 
 
50
  def calculate_indicators(self, df):
51
  """Calculate technical indicators"""
52
  if df.empty:
53
  return df
54
 
55
+ # Simple Moving Averages (5, 20 as requested)
56
+ df['SMA_5'] = df['Close'].rolling(window=5).mean()
57
  df['SMA_20'] = df['Close'].rolling(window=20).mean()
 
58
 
59
  # Exponential Moving Averages
60
  df['EMA_12'] = df['Close'].ewm(span=12, adjust=False).mean()
61
  df['EMA_26'] = df['Close'].ewm(span=26, adjust=False).mean()
62
 
63
+ # MACD (12, 26, 9)
64
  df['MACD'] = df['EMA_12'] - df['EMA_26']
65
  df['MACD_signal'] = df['MACD'].ewm(span=9, adjust=False).mean()
66
  df['MACD_histogram'] = df['MACD'] - df['MACD_signal']
67
 
68
+ # Split histogram into positive and negative for plotting
69
+ df['MACD_bar_positive'] = df['MACD_histogram'].where(df['MACD_histogram'] > 0, 0)
70
+ df['MACD_bar_negative'] = df['MACD_histogram'].where(df['MACD_histogram'] < 0, 0)
71
+
72
  # RSI
73
  delta = df['Close'].diff()
74
  gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
 
94
  df['Volume_SMA'] = df['Volume'].rolling(window=20).mean()
95
  df['Volume_ratio'] = df['Volume'] / df['Volume_SMA']
96
 
97
+ # Stochastic Oscillator (14, 3)
98
+ low_14 = df['Low'].rolling(window=14).min()
99
+ high_14 = df['High'].rolling(window=14).max()
100
+ df['%K'] = 100 * (df['Close'] - low_14) / (high_14 - low_14)
101
+ df['%D'] = df['%K'].rolling(window=3).mean()
102
+ df['%SD'] = df['%D'].rolling(window=3).mean()
103
+ df['UL'] = 70 # Upper limit
104
+ df['DL'] = 30 # Lower limit
105
+
106
  return df
107
 
 
108
  def get_fundamental_data(self, ticker="GC=F"):
109
  """Get fundamental gold market data (now generalized/mocked)"""
110
  try:
 
 
111
  if ticker == "BTC-USD":
112
  fundamentals = {
113
  "Crypto Volatility Index": round(np.random.uniform(50, 150), 1),
114
  "Dominance Index": f"{np.random.uniform(40, 60):.2f}%",
115
  "Fear & Greed Index": np.random.choice(["Extreme Fear", "Fear", "Neutral", "Greed", "Extreme Greed"]),
116
  "Hash Rate Trend": np.random.choice(["Increasing", "Stable", "Decreasing"]),
117
+ "Institutional Flow (Net)": f"{np.random.uniform(-100, 100):,.0f}M USD",
 
 
118
  "Market Sentiment": np.random.choice(["Bullish", "Neutral", "Bearish"]),
119
  }
120
+ else:
121
  fundamentals = {
122
  "Gold Strength Index": round(np.random.uniform(30, 80), 1),
123
  "Dollar Index (DXY)": round(np.random.uniform(90, 110), 1),
124
  "Real Interest Rate": f"{np.random.uniform(-2, 5):.2f}%",
125
+ "Gold Volatility": f"{np.random.uniform(10, 40):.1f}%",
126
+ "Commercial Hedgers (Net)": f"{np.random.uniform(-50000, 50000):,.0f}",
127
+ "Managed Money (Net)": f"{np.random.uniform(-100000, 100000):,.0f}",
 
 
 
 
 
 
128
  "Market Sentiment": np.random.choice(["Bullish", "Neutral", "Bearish"]),
129
  }
130
 
131
  return fundamentals
132
 
133
  except Exception as e:
134
+ print(f"Error fetching fundamentals: {e}")
135
+ return {"Error": str(e)}
 
 
 
 
136
 
 
137
  def prepare_for_chronos(self, df, lookback=100):
138
  """Prepare data for Chronos model"""
139
  if df.empty or len(df) < lookback:
140
  return None
141
 
 
142
  prices = df['Close'].iloc[-lookback:].values
143
  prices = prices.astype(np.float32)
144
 
 
145
  mean = np.mean(prices)
146
  std = np.std(prices)
147
  normalized = (prices - mean) / (std + 1e-8)
plotter.py CHANGED
@@ -7,75 +7,73 @@ import matplotlib.pyplot as plt
7
  def create_mplfinance_chart(df, ticker, predictions=None):
8
  """
9
  Creates a custom mplfinance candlestick chart and returns it as a base64 encoded image.
10
- Style: white, black, with accent #f0f4f9.
11
  """
12
  if df.empty:
13
  return None
14
 
15
- # --- 1. Define Custom Style ---
16
  mc = mpf.make_marketcolors(
17
- up='green', down='red', # Candlestick body colors (yfinance default)
18
- edge='inherit',
19
  wick='black',
20
- volume='#f0f4f9', # Accent color for volume
 
21
  inherit=True
22
  )
23
 
24
  s = mpf.make_mpf_style(
25
- base_mpf_style='nightclouds', # Start with a dark base
26
  marketcolors=mc,
27
- # Customize background and grid to achieve white/black contrast
28
- facecolor='#f0f4f9', # Background accent color (very light gray/off-white)
29
  edgecolor='black',
30
- gridcolor='gray',
31
- gridstyle=':',
32
  figcolor='white',
33
  rc={'axes.labelcolor': 'black',
34
  'xtick.color': 'black',
35
  'ytick.color': 'black',
36
- 'figure.titlesize': 14,
37
- 'axes.titlesize': 16}
 
38
  )
39
 
40
- # --- 2. Define Technical Indicators (Apanels) ---
41
  apds = []
42
- # MACD Subplot
 
 
 
 
 
 
 
 
 
 
43
  apds.append(
44
- mpf.make_addplot(df['MACD'], color='black', panel=2, title='MACD'),
45
  )
 
46
  apds.append(
47
- mpf.make_addplot(df['MACD_signal'], color='#FFA500', panel=2),
48
  )
49
 
50
- # RSI Subplot
51
  apds.append(
52
- mpf.make_addplot(df['RSI'], color='red', panel=3, title='RSI', y_on_right=True),
53
  )
54
 
55
- # Overlays on Main Chart (MAs and BB)
56
- apds.extend([
57
- mpf.make_addplot(df['SMA_20'], color='#FFD700', linestyle='-', width=1.5),
58
- mpf.make_addplot(df['SMA_50'], color='#FFA500', linestyle='-', width=1.5),
59
- mpf.make_addplot(df['BB_upper'], color='grey', linestyle='--', width=0.5),
60
- mpf.make_addplot(df['BB_lower'], color='grey', linestyle='--', width=0.5),
61
- ])
62
-
63
- # --- 3. Prediction Plotting (Trace as Overlay) ---
64
  if predictions is not None and predictions.any():
65
-
66
- # Calculate future index (assuming daily frequency for simplicity)
67
  last_date = df.index[-1]
68
  future_index = pd.date_range(start=last_date, periods=len(predictions) + 1, freq=df.index.freq or 'D')[1:]
69
-
70
- # Create a series to align with the main DataFrame index for plotting
71
  future_series = pd.Series(predictions, index=future_index)
72
 
73
- # Create an 'scatter' addplot for the prediction line
74
  apds.append(
75
- mpf.make_addplot(future_series, color='blue', linestyle='-.', width=2, marker='o', markersize=5)
76
  )
77
 
78
- # --- 4. Plotting ---
79
  fig, axes = mpf.plot(
80
  df,
81
  type='candle',
@@ -83,18 +81,18 @@ def create_mplfinance_chart(df, ticker, predictions=None):
83
  title=f'{ticker} Price Chart and Analysis',
84
  ylabel='Price (USD)',
85
  volume=True,
86
- volume_panel=1,
87
  addplot=apds,
88
- figratio=(16,9),
 
89
  figscale=1.5,
 
90
  returnfig=True
91
  )
92
 
93
- # --- 5. Convert to Base64 (for Gradio) ---
94
  buf = BytesIO()
95
- fig.savefig(buf, format='png', bbox_inches='tight')
96
- plt.close(fig) # Close the figure to free memory
97
  image_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
98
 
99
- # Gradio expects an HTML tag for raw HTML image display
100
  return f'<img src="data:image/png;base64,{image_base64}" style="width: 100%; height: auto;">'
 
7
  def create_mplfinance_chart(df, ticker, predictions=None):
8
  """
9
  Creates a custom mplfinance candlestick chart and returns it as a base64 encoded image.
10
+ Implements the exact layout: Candlestick + Volume + MACD + Stochastic.
11
  """
12
  if df.empty:
13
  return None
14
 
15
+ # Define style - Yahoo style as requested
16
  mc = mpf.make_marketcolors(
17
+ up='#00ff00', down='#ff0000', # Green/Red candles
 
18
  wick='black',
19
+ edge='black',
20
+ volume='#00bfff',
21
  inherit=True
22
  )
23
 
24
  s = mpf.make_mpf_style(
25
+ base_mpf_style='yahoo',
26
  marketcolors=mc,
27
+ facecolor='white',
 
28
  edgecolor='black',
29
+ gridcolor='lightgray',
30
+ gridstyle='-',
31
  figcolor='white',
32
  rc={'axes.labelcolor': 'black',
33
  'xtick.color': 'black',
34
  'ytick.color': 'black',
35
+ 'figure.titlesize': 16,
36
+ 'axes.titlesize': 14,
37
+ 'axes.titleweight': 'bold'}
38
  )
39
 
40
+ # Define panels: [Candlestick, Volume, MACD, Stochastic]
41
  apds = []
42
+
43
+ # MACD Panel (Panel 2 - index 1 for addplot)
44
+ # MACD Line
45
+ apds.append(
46
+ mpf.make_addplot(df['MACD'], color='#606060', panel=2, ylabel='MACD', secondary_y=False)
47
+ )
48
+ # Signal Line
49
+ apds.append(
50
+ mpf.make_addplot(df['MACD_signal'], color='#1f77b4', panel=2, secondary_y=False)
51
+ )
52
+ # Positive Histogram Bars
53
  apds.append(
54
+ mpf.make_addplot(df['MACD_bar_positive'], type='bar', color='#4dc790', panel=2, width=0.8)
55
  )
56
+ # Negative Histogram Bars
57
  apds.append(
58
+ mpf.make_addplot(df['MACD_bar_negative'], type='bar', color='#fd6b6c', panel=2, width=0.8)
59
  )
60
 
61
+ # Stochastic Panel (Panel 3 - index 2 for addplot)
62
  apds.append(
63
+ mpf.make_addplot(df[['%D', '%SD', 'UL', 'DL']], panel=3, ylabel='Stoch (14,3)', ylim=[0, 100])
64
  )
65
 
66
+ # Prediction overlay on main chart
 
 
 
 
 
 
 
 
67
  if predictions is not None and predictions.any():
 
 
68
  last_date = df.index[-1]
69
  future_index = pd.date_range(start=last_date, periods=len(predictions) + 1, freq=df.index.freq or 'D')[1:]
 
 
70
  future_series = pd.Series(predictions, index=future_index)
71
 
 
72
  apds.append(
73
+ mpf.make_addplot(future_series, color='blue', linestyle='-.', width=2, marker='o', markersize=4)
74
  )
75
 
76
+ # Plotting
77
  fig, axes = mpf.plot(
78
  df,
79
  type='candle',
 
81
  title=f'{ticker} Price Chart and Analysis',
82
  ylabel='Price (USD)',
83
  volume=True,
 
84
  addplot=apds,
85
+ mav=(5, 20), # Moving averages as requested
86
+ figratio=(16, 9),
87
  figscale=1.5,
88
+ panel_ratios=(3, 1, 3, 3), # Ratio as requested
89
  returnfig=True
90
  )
91
 
92
+ # Convert to Base64
93
  buf = BytesIO()
94
+ fig.savefig(buf, format='png', bbox_inches='tight', dpi=100)
95
+ plt.close(fig)
96
  image_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
97
 
 
98
  return f'<img src="data:image/png;base64,{image_base64}" style="width: 100%; height: auto;">'
requirements.txt CHANGED
@@ -1,13 +1,19 @@
1
- gradio
2
- yfinance
3
- torch
4
- transformers
5
  pandas
6
- numpy
7
  plotly
8
- scipy
9
- scikit-learn
10
- safetensors
11
- huggingface-hub
12
- chronos-forecasting
13
  mplfinance
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  pandas
 
2
  plotly
3
+ numpy
4
+ gradio
 
 
 
5
  mplfinance
6
+ openpyxl
7
+ requests
8
+ Pillow
9
+ matplotlib
10
+ scikit-learn
11
+ nltk
12
+ torch
13
+ git+https://github.com/huggingface/transformers
14
+ sentencepiece
15
+ accelerate
16
+ tokenizers
17
+ yfinance
18
+ scipy
19
+ joblib
sentiment_analyzer.py CHANGED
@@ -50,25 +50,17 @@ class SentimentAnalyzer:
50
 
51
  # Tampilan News (menggunakan background terang #E0E0E0 agar terlihat di tema putih)
52
  news_html = "<div style='max-height: 200px; overflow-y: auto; color: black;'>"
53
- news_html += f"<h4 style='color: {
54
- title_color
55
- };'>Latest {
56
- ticker
57
- } News (Simulated)</h4>"
58
 
59
  for news in selected_news:
60
  sentiment_label = "🟢" if "positive" in news or "rising" in news or "support" in news or "bullish" in news or "accumulation" in news else \
61
  "🔴" if "sell-off" in news or "weighs" in news or "outflows" in news or "Profit-taking" in news or "fear" in news else \
62
  "🟡"
63
- news_html += f"<p style='margin: 10px 0; padding: 10px; background: #E0E0E0; border-radius: 5px; color: black;'>{
64
- sentiment_label
65
- } {news}</p>"
66
 
67
  news_html += "</div>"
68
 
69
  return sentiment, news_html
70
 
71
  except Exception as e:
72
- return 0, f"<p>Error analyzing sentiment: {
73
- str(e)
74
- }</p>"
 
50
 
51
  # Tampilan News (menggunakan background terang #E0E0E0 agar terlihat di tema putih)
52
  news_html = "<div style='max-height: 200px; overflow-y: auto; color: black;'>"
53
+ news_html += f"<h4 style='color: {title_color};'>Latest {ticker} News (Simulated)</h4>"
 
 
 
 
54
 
55
  for news in selected_news:
56
  sentiment_label = "🟢" if "positive" in news or "rising" in news or "support" in news or "bullish" in news or "accumulation" in news else \
57
  "🔴" if "sell-off" in news or "weighs" in news or "outflows" in news or "Profit-taking" in news or "fear" in news else \
58
  "🟡"
59
+ news_html += f"<p style='margin: 10px 0; padding: 10px; background: #E0E0E0; border-radius: 5px; color: black;'>{sentiment_label} {news}</p>"
 
 
60
 
61
  news_html += "</div>"
62
 
63
  return sentiment, news_html
64
 
65
  except Exception as e:
66
+ return 0, f"<p>Error analyzing sentiment: {str(e)}</p>"
 
 
trading_logic.py CHANGED
@@ -80,9 +80,7 @@ class TradingLogic:
80
  return signal, confidence
81
 
82
  except Exception as e:
83
- print(f"Signal generation error: {
84
- e
85
- }")
86
  return "hold", 0.0
87
 
88
  def calculate_tp_sl(self, current_price, atr, signal):
 
80
  return signal, confidence
81
 
82
  except Exception as e:
83
+ print(f"Signal generation error: {e}")
 
 
84
  return "hold", 0.0
85
 
86
  def calculate_tp_sl(self, current_price, atr, signal):