Riy777 commited on
Commit
1af938b
ยท
1 Parent(s): dc2c23a

Update LLM.py

Browse files
Files changed (1) hide show
  1. LLM.py +338 -146
LLM.py CHANGED
@@ -6,7 +6,7 @@ from backoff import on_exception, expo
6
  from openai import OpenAI, RateLimitError, APITimeoutError
7
  import numpy as np
8
  from sentiment_news import NewsFetcher
9
- from helpers import parse_json_from_response, validate_required_fields, format_technical_indicators, format_strategy_scores
10
 
11
  NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
12
  PRIMARY_MODEL = "nvidia/llama-3.1-nemotron-ultra-253b-v1"
@@ -16,52 +16,68 @@ class PatternAnalysisEngine:
16
  self.llm = llm_service
17
 
18
  def _format_chart_data_for_llm(self, ohlcv_data):
19
- if not ohlcv_data or len(ohlcv_data) < 20:
 
20
  return "Insufficient chart data for pattern analysis"
 
21
  try:
22
- candles_to_analyze = ohlcv_data[-50:] if len(ohlcv_data) > 50 else ohlcv_data
23
- chart_description = ["CANDLE DATA FOR PATTERN ANALYSIS:", f"Total candles available: {len(ohlcv_data)}", f"Candles used for analysis: {len(candles_to_analyze)}", ""]
24
-
25
- if len(candles_to_analyze) >= 10:
26
- recent_candles = candles_to_analyze[-10:]
27
- chart_description.append("Recent 10 Candles (Latest First):")
28
- for i, candle in enumerate(reversed(recent_candles)):
29
- candle_idx = len(candles_to_analyze) - i
30
- desc = f"Candle {candle_idx}: O:{candle[1]:.6f} H:{candle[2]:.6f} L:{candle[3]:.6f} C:{candle[4]:.6f} V:{candle[5]:.0f}"
31
- chart_description.append(f" {desc}")
32
-
33
- if len(candles_to_analyze) >= 2:
34
- first_close = candles_to_analyze[0][4]
35
- last_close = candles_to_analyze[-1][4]
36
- price_change = ((last_close - first_close) / first_close) * 100
37
- trend = "BULLISH" if price_change > 2 else "BEARISH" if price_change < -2 else "SIDEWAYS"
38
- highs = [c[2] for c in candles_to_analyze]
39
- lows = [c[3] for c in candles_to_analyze]
40
- high_max = max(highs)
41
- low_min = min(lows)
42
- volatility = ((high_max - low_min) / low_min) * 100
43
- chart_description.extend(["", "MARKET STRUCTURE ANALYSIS:", f"Trend Direction: {trend}", f"Price Change: {price_change:+.2f}%", f"Volatility Range: {volatility:.2f}%", f"Highest Price: {high_max:.6f}", f"Lowest Price: {low_min:.6f}"])
44
 
45
- if len(candles_to_analyze) >= 5:
46
- volumes = [c[5] for c in candles_to_analyze]
47
- avg_volume = sum(volumes) / len(volumes)
48
- current_volume = candles_to_analyze[-1][5]
49
- volume_ratio = current_volume / avg_volume if avg_volume > 0 else 1
50
- volume_signal = "HIGH" if volume_ratio > 2 else "NORMAL" if volume_ratio > 0.5 else "LOW"
51
- chart_description.extend(["", "VOLUME ANALYSIS:", f"Current Volume: {current_volume:,.0f}", f"Volume Ratio: {volume_ratio:.2f}x average", f"Volume Signal: {volume_signal}"])
52
-
53
- return "\n".join(chart_description)
54
  except Exception as e:
55
  return f"Error formatting chart data: {str(e)}"
56
 
57
  async def analyze_chart_patterns(self, symbol, ohlcv_data):
58
  try:
59
- if not ohlcv_data or len(ohlcv_data) < 20:
60
- return {"pattern_detected": "insufficient_data", "pattern_confidence": 0.1, "pattern_analysis": "Insufficient candle data for pattern analysis"}
61
 
62
  chart_text = self._format_chart_data_for_llm(ohlcv_data)
63
- prompt = f"Analyze the following candle data for {symbol} and identify patterns.\n\nCANDLE DATA FOR ANALYSIS:\n{chart_text}\n\nOUTPUT FORMAT (JSON):\n{{\"pattern_detected\": \"pattern_name\",\"pattern_confidence\": 0.85,\"pattern_strength\": \"strong/medium/weak\",\"predicted_direction\": \"up/down/sideways\",\"predicted_movement_percent\": 5.50,\"timeframe_expectation\": \"15-25 minutes\",\"entry_suggestion\": 0.1234,\"target_suggestion\": 0.1357,\"stop_suggestion\": 0.1189,\"key_support\": 0.1200,\"key_resistance\": 0.1300,\"pattern_analysis\": \"Detailed explanation\"}}"
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  response = await self.llm._call_llm(prompt)
66
  return self._parse_pattern_response(response)
67
  except Exception as e:
@@ -107,21 +123,28 @@ class LLMService:
107
  symbol = data_payload.get('symbol', 'unknown')
108
  target_strategy = data_payload.get('target_strategy', 'GENERIC')
109
 
 
110
  news_text = await self.news_fetcher.get_news_for_symbol(symbol)
111
  pattern_analysis = await self._get_pattern_analysis(data_payload)
112
- prompt = self._create_enhanced_trading_prompt(data_payload, news_text, pattern_analysis)
 
 
 
113
 
114
  # โœ… ุญูุธ ุงู„ู€ Prompt ููŠ R2 ู‚ุจู„ ุฅุฑุณุงู„ู‡ ู„ู„ู†ู…ูˆุฐุฌ
115
  if self.r2_service:
116
  analysis_data = {
 
117
  'current_price': data_payload.get('current_price'),
118
  'final_score': data_payload.get('final_score'),
119
  'enhanced_final_score': data_payload.get('enhanced_final_score'),
120
  'target_strategy': target_strategy,
121
- 'pattern_analysis': pattern_analysis
 
 
122
  }
123
  await self.r2_service.save_llm_prompts_async(
124
- symbol, 'trading_decision', prompt, analysis_data
125
  )
126
 
127
  async with self.semaphore:
@@ -131,26 +154,28 @@ class LLMService:
131
  if decision_dict:
132
  decision_dict['model_source'] = self.model_name
133
  decision_dict['pattern_analysis'] = pattern_analysis
 
134
  return decision_dict
135
  else:
136
- # โŒ ู„ุง ู†ุณุชุฎุฏู… ุฃูŠ ู…ุญุงูƒุงุฉ - ู†ุฑุฌุน None ููŠ ุญุงู„ุฉ ุงู„ูุดู„
137
  print(f"โŒ ูุดู„ ุชุญู„ูŠู„ ุงู„ู†ู…ูˆุฐุฌ ุงู„ุถุฎู… ู„ู€ {symbol} - ู„ุง ุชูˆุฌุฏ ู‚ุฑุงุฑุงุช ุจุฏูŠู„ุฉ")
138
  return None
139
 
140
  except Exception as e:
141
  print(f"โŒ ุฎุทุฃ ููŠ ู‚ุฑุงุฑ ุงู„ุชุฏุงูˆู„ ู„ู€ {data_payload.get('symbol', 'unknown')}: {e}")
142
- # โŒ ู„ุง ู†ุณุชุฎุฏู… ุฃูŠ ู…ุญุงูƒุงุฉ
143
  return None
144
 
145
  def _parse_llm_response_enhanced(self, response_text: str, fallback_strategy: str, symbol: str) -> dict:
146
  try:
147
  json_str = parse_json_from_response(response_text)
148
  if not json_str:
 
149
  return None
150
 
151
  decision_data = json.loads(json_str)
152
  required_fields = ['action', 'reasoning', 'risk_assessment', 'trade_type', 'stop_loss', 'take_profit', 'expected_target_minutes', 'confidence_level']
153
  if not validate_required_fields(decision_data, required_fields):
 
154
  return None
155
 
156
  strategy_value = decision_data.get('strategy')
@@ -159,28 +184,23 @@ class LLMService:
159
 
160
  return decision_data
161
  except Exception as e:
162
- print(f"Error parsing LLM response for {symbol}: {e}")
163
  return None
164
 
165
  async def _get_pattern_analysis(self, data_payload):
166
  try:
167
  symbol = data_payload['symbol']
168
- if 'raw_ohlcv' in data_payload and '1h' in data_payload['raw_ohlcv']:
169
- ohlcv_data = data_payload['raw_ohlcv']['1h']
170
- if ohlcv_data and len(ohlcv_data) >= 20:
171
- return await self.pattern_engine.analyze_chart_patterns(symbol, ohlcv_data)
172
 
173
- if 'advanced_indicators' in data_payload and '1h' in data_payload['advanced_indicators']:
174
- ohlcv_data = data_payload['advanced_indicators']['1h']
175
- if ohlcv_data and len(ohlcv_data) >= 20:
176
- return await self.pattern_engine.analyze_chart_patterns(symbol, ohlcv_data)
177
 
178
  return None
179
  except Exception as e:
180
- print(f"Pattern analysis failed for {data_payload.get('symbol')}: {e}")
181
  return None
182
 
183
- def _create_enhanced_trading_prompt(self, payload: dict, news_text: str, pattern_analysis: dict) -> str:
184
  symbol = payload.get('symbol', 'N/A')
185
  current_price = payload.get('current_price', 'N/A')
186
  reasons = payload.get('reasons_for_candidacy', [])
@@ -191,88 +211,242 @@ class LLMService:
191
  target_strategy = payload.get('target_strategy', 'GENERIC')
192
  final_score = payload.get('final_score', 'N/A')
193
  enhanced_final_score = payload.get('enhanced_final_score', 'N/A')
194
- whale_data = payload.get('whale_data', {})
195
 
196
- final_score_display = f"{final_score:.2f}" if isinstance(final_score, (int, float)) else str(final_score)
197
- enhanced_score_display = f"{enhanced_final_score:.2f}" if isinstance(enhanced_final_score, (int, float)) else str(enhanced_final_score)
198
 
 
199
  indicators_summary = format_technical_indicators(advanced_indicators)
200
  strategies_summary = format_strategy_scores(strategy_scores, recommended_strategy)
201
  pattern_summary = self._format_pattern_analysis(pattern_analysis)
202
- whale_analysis_section = self._format_whale_analysis(sentiment_data.get('general_whale_activity', {}), whale_data, symbol)
 
 
203
 
204
  prompt = f"""
205
- TRADING ANALYSIS FOR {symbol}
206
-
207
- STRATEGY: {target_strategy}
208
- Current Price: {current_price}
209
- System Score: {final_score_display}
210
- Enhanced Score: {enhanced_score_display}
211
-
212
- CHART PATTERN ANALYSIS:
213
- {pattern_summary}
214
-
215
- TECHNICAL INDICATORS:
216
- {indicators_summary}
217
-
218
- STRATEGY ANALYSIS:
219
- {strategies_summary}
220
-
221
- MARKET CONTEXT:
222
- - BTC Trend: {sentiment_data.get('btc_sentiment', 'N/A')}
223
- - Fear & Greed: {sentiment_data.get('fear_and_greed_index', 'N/A')}
224
-
225
- WHALE ANALYSIS:
226
- {whale_analysis_section}
227
-
228
- NEWS:
229
- {news_text}
230
-
231
- OUTPUT (JSON):
232
- {{
233
- "action": "BUY/SELL/HOLD",
234
- "reasoning": "Detailed explanation",
235
- "risk_assessment": "Risk analysis",
236
- "trade_type": "LONG/SHORT",
237
- "stop_loss": 0.0000,
238
- "take_profit": 0.0000,
239
- "expected_target_minutes": 15,
240
- "confidence_level": 0.85,
241
- "strategy": "{target_strategy}",
242
- "pattern_influence": "Pattern influence description"
243
- }}
244
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  return prompt
246
 
247
  def _format_pattern_analysis(self, pattern_analysis):
248
  if not pattern_analysis:
249
- return "No clear patterns detected"
 
250
  confidence = pattern_analysis.get('pattern_confidence', 0)
251
  pattern_name = pattern_analysis.get('pattern_detected', 'unknown')
252
- analysis_lines = [f"Pattern: {pattern_name}", f"Confidence: {confidence:.1%}", f"Predicted Move: {pattern_analysis.get('predicted_direction', 'N/A')}", f"Analysis: {pattern_analysis.get('pattern_analysis', 'No detailed analysis')}"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  return "\n".join(analysis_lines)
254
 
255
- def _format_whale_analysis(self, general_whale_activity, symbol_whale_data, symbol):
256
- from sentiment_news import SentimentAnalyzer
257
- temp_analyzer = SentimentAnalyzer(None)
258
- return temp_analyzer.format_whale_analysis(general_whale_activity, symbol_whale_data, symbol)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
  async def re_analyze_trade_async(self, trade_data: dict, processed_data: dict):
261
  try:
262
  symbol = trade_data['symbol']
263
  original_strategy = trade_data.get('strategy', 'GENERIC')
264
 
 
265
  news_text = await self.news_fetcher.get_news_for_symbol(symbol)
266
  pattern_analysis = await self._get_pattern_analysis(processed_data)
267
- prompt = self._create_re_analysis_prompt(trade_data, processed_data, news_text, pattern_analysis)
268
 
269
- # โœ… ุญูุธ ุงู„ู€ Prompt ููŠ R2 ู‚ุจู„ ุฅุฑุณุงู„ู‡ ู„ู„ู†ู…ูˆุฐุฌ
 
 
270
  if self.r2_service:
271
  analysis_data = {
 
272
  'entry_price': trade_data.get('entry_price'),
273
  'current_price': processed_data.get('current_price'),
274
  'original_strategy': original_strategy,
275
- 'pattern_analysis': pattern_analysis
 
276
  }
277
  await self.r2_service.save_llm_prompts_async(
278
  symbol, 'trade_reanalysis', prompt, analysis_data
@@ -284,15 +458,15 @@ class LLMService:
284
  re_analysis_dict = self._parse_re_analysis_response(response, original_strategy, symbol)
285
  if re_analysis_dict:
286
  re_analysis_dict['model_source'] = self.model_name
 
287
  return re_analysis_dict
288
  else:
289
- # โŒ ู„ุง ู†ุณุชุฎุฏู… ุฃูŠ ู…ุญุงูƒุงุฉ - ู†ุฑุฌุน None ููŠ ุญุงู„ุฉ ุงู„ูุดู„
290
- print(f"โŒ ูุดู„ ุฅุนุงุฏุฉ ุชุญู„ูŠู„ ุงู„ู†ู…ูˆุฐุฌ ุงู„ุถุฎู… ู„ู€ {symbol} - ู„ุง ุชูˆุฌุฏ ู‚ุฑุงุฑุงุช ุจุฏูŠู„ุฉ")
291
  return None
292
 
293
  except Exception as e:
294
  print(f"โŒ ุฎุทุฃ ููŠ ุฅุนุงุฏุฉ ุชุญู„ูŠู„ LLM: {e}")
295
- # โŒ ู„ุง ู†ุณุชุฎุฏู… ุฃูŠ ู…ุญุงูƒุงุฉ
296
  return None
297
 
298
  def _parse_re_analysis_response(self, response_text: str, fallback_strategy: str, symbol: str) -> dict:
@@ -311,7 +485,7 @@ class LLMService:
311
  print(f"Error parsing re-analysis response for {symbol}: {e}")
312
  return None
313
 
314
- def _create_re_analysis_prompt(self, trade_data: dict, processed_data: dict, news_text: str, pattern_analysis: dict) -> str:
315
  symbol = trade_data.get('symbol', 'N/A')
316
  entry_price = trade_data.get('entry_price', 'N/A')
317
  current_price = processed_data.get('current_price', 'N/A')
@@ -325,41 +499,56 @@ class LLMService:
325
 
326
  indicators_summary = format_technical_indicators(processed_data.get('advanced_indicators', {}))
327
  pattern_summary = self._format_pattern_analysis(pattern_analysis)
328
- whale_analysis_section = self._format_whale_analysis(processed_data.get('sentiment_data', {}).get('general_whale_activity', {}), processed_data.get('whale_data', {}), symbol)
 
329
 
330
  prompt = f"""
331
- TRADE RE-ANALYSIS FOR {symbol}
332
-
333
- TRADE CONTEXT:
334
- - Strategy: {strategy}
335
- - Entry Price: {entry_price}
336
- - Current Price: {current_price}
337
- - Performance: {price_change_display}
338
-
339
- UPDATED PATTERN ANALYSIS:
340
- {pattern_summary}
341
-
342
- UPDATED TECHNICALS:
343
- {indicators_summary}
344
-
345
- UPDATED WHALE DATA:
346
- {whale_analysis_section}
347
-
348
- LATEST NEWS:
349
- {news_text}
350
-
351
- OUTPUT (JSON):
352
- {{
353
- "action": "HOLD/CLOSE_TRADE/UPDATE_TRADE",
354
- "reasoning": "Justification",
355
- "new_stop_loss": 0.0000,
356
- "new_take_profit": 0.0000,
357
- "new_expected_minutes": 15,
358
- "confidence_level": 0.85,
359
- "strategy": "{strategy}",
360
- "pattern_influence_reanalysis": "Pattern influence description"
361
- }}
362
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  return prompt
364
 
365
  @_rate_limit_nvidia_api
@@ -369,12 +558,15 @@ class LLMService:
369
  model=self.model_name,
370
  messages=[{"role": "user", "content": prompt}],
371
  temperature=self.temperature,
372
- seed=42
 
373
  )
374
  return response.choices[0].message.content
375
  except (RateLimitError, APITimeoutError) as e:
376
- print(f"LLM API Error: {e}. Retrying...")
377
  raise
378
  except Exception as e:
379
- print(f"Unexpected LLM API error: {e}")
380
- raise
 
 
 
6
  from openai import OpenAI, RateLimitError, APITimeoutError
7
  import numpy as np
8
  from sentiment_news import NewsFetcher
9
+ from helpers import parse_json_from_response, validate_required_fields, format_technical_indicators, format_strategy_scores, format_candle_data_for_pattern_analysis, format_whale_analysis_for_llm
10
 
11
  NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
12
  PRIMARY_MODEL = "nvidia/llama-3.1-nemotron-ultra-253b-v1"
 
16
  self.llm = llm_service
17
 
18
  def _format_chart_data_for_llm(self, ohlcv_data):
19
+ """ุชู†ุณูŠู‚ ุดุงู…ู„ ู„ุจูŠุงู†ุงุช ุงู„ุดู…ูˆุน ู„ุชุญู„ูŠู„ ุงู„ุฃู†ู…ุงุท"""
20
+ if not ohlcv_data:
21
  return "Insufficient chart data for pattern analysis"
22
+
23
  try:
24
+ # ุงุณุชุฎุฏุงู… ุฌู…ูŠุน ุงู„ุฃุทุฑ ุงู„ุฒู…ู†ูŠุฉ ุงู„ู…ุชุงุญุฉ
25
+ all_timeframes = []
26
+ for timeframe, candles in ohlcv_data.items():
27
+ if candles and len(candles) >= 20:
28
+ candle_summary = format_candle_data_for_pattern_analysis({timeframe: candles}, timeframe)
29
+ all_timeframes.append(f"=== {timeframe.upper()} TIMEFRAME ===\n{candle_summary}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ return "\n\n".join(all_timeframes) if all_timeframes else "No sufficient timeframe data available"
 
 
 
 
 
 
 
 
32
  except Exception as e:
33
  return f"Error formatting chart data: {str(e)}"
34
 
35
  async def analyze_chart_patterns(self, symbol, ohlcv_data):
36
  try:
37
+ if not ohlcv_data:
38
+ return {"pattern_detected": "insufficient_data", "pattern_confidence": 0.1, "pattern_analysis": "No candle data available"}
39
 
40
  chart_text = self._format_chart_data_for_llm(ohlcv_data)
 
41
 
42
+ prompt = f"""
43
+ ANALYZE CHART PATTERNS FOR {symbol}
44
+
45
+ CANDLE DATA FOR TECHNICAL ANALYSIS:
46
+ {chart_text}
47
+
48
+ PATTERN ANALYSIS INSTRUCTIONS:
49
+ 1. Analyze ALL available timeframes (1w, 1d, 4h, 1h, 15m, 5m)
50
+ 2. Identify clear chart patterns (Double Top/Bottom, Head & Shoulders, Triangles, Flags, etc.)
51
+ 3. Assess trend direction and strength
52
+ 4. Identify key support and resistance levels
53
+ 5. Evaluate volume patterns
54
+ 6. Look for convergence/divergence across timeframes
55
+ 7. Consider candlestick patterns and formations
56
+
57
+ CRITICAL: You MUST analyze at least 3 different timeframes to confirm patterns.
58
+
59
+ OUTPUT FORMAT (JSON):
60
+ {{
61
+ "pattern_detected": "pattern_name",
62
+ "pattern_confidence": 0.85,
63
+ "pattern_strength": "strong/medium/weak",
64
+ "predicted_direction": "up/down/sideways",
65
+ "predicted_movement_percent": 5.50,
66
+ "timeframe_expectation": "15-25 minutes",
67
+ "key_support_levels": [0.1200, 0.1180, 0.1150],
68
+ "key_resistance_levels": [0.1300, 0.1320, 0.1350],
69
+ "pattern_analysis": "Detailed explanation covering multiple timeframes",
70
+ "timeframe_confirmations": {{
71
+ "1h": "pattern_details",
72
+ "4h": "pattern_details",
73
+ "1d": "pattern_details"
74
+ }},
75
+ "risk_assessment": "low/medium/high",
76
+ "recommended_entry": 0.1234,
77
+ "recommended_targets": [0.1357, 0.1400],
78
+ "recommended_stop_loss": 0.1189
79
+ }}
80
+ """
81
  response = await self.llm._call_llm(prompt)
82
  return self._parse_pattern_response(response)
83
  except Exception as e:
 
123
  symbol = data_payload.get('symbol', 'unknown')
124
  target_strategy = data_payload.get('target_strategy', 'GENERIC')
125
 
126
+ # ุฌู„ุจ ุฌู…ูŠุน ุงู„ุจูŠุงู†ุงุช ุงู„ู…ุทู„ูˆุจุฉ
127
  news_text = await self.news_fetcher.get_news_for_symbol(symbol)
128
  pattern_analysis = await self._get_pattern_analysis(data_payload)
129
+ whale_data = data_payload.get('whale_data', {})
130
+
131
+ # ุฅู†ุดุงุก ุงู„ู€ prompt ุงู„ุดุงู…ู„
132
+ prompt = self._create_comprehensive_trading_prompt(data_payload, news_text, pattern_analysis, whale_data)
133
 
134
  # โœ… ุญูุธ ุงู„ู€ Prompt ููŠ R2 ู‚ุจู„ ุฅุฑุณุงู„ู‡ ู„ู„ู†ู…ูˆุฐุฌ
135
  if self.r2_service:
136
  analysis_data = {
137
+ 'symbol': symbol,
138
  'current_price': data_payload.get('current_price'),
139
  'final_score': data_payload.get('final_score'),
140
  'enhanced_final_score': data_payload.get('enhanced_final_score'),
141
  'target_strategy': target_strategy,
142
+ 'pattern_analysis': pattern_analysis,
143
+ 'whale_data_available': whale_data.get('data_available', False),
144
+ 'timestamp': datetime.now().isoformat()
145
  }
146
  await self.r2_service.save_llm_prompts_async(
147
+ symbol, 'comprehensive_trading_decision', prompt, analysis_data
148
  )
149
 
150
  async with self.semaphore:
 
154
  if decision_dict:
155
  decision_dict['model_source'] = self.model_name
156
  decision_dict['pattern_analysis'] = pattern_analysis
157
+ decision_dict['whale_data_integrated'] = whale_data.get('data_available', False)
158
  return decision_dict
159
  else:
 
160
  print(f"โŒ ูุดู„ ุชุญู„ูŠู„ ุงู„ู†ู…ูˆุฐุฌ ุงู„ุถุฎู… ู„ู€ {symbol} - ู„ุง ุชูˆุฌุฏ ู‚ุฑุงุฑุงุช ุจุฏูŠู„ุฉ")
161
  return None
162
 
163
  except Exception as e:
164
  print(f"โŒ ุฎุทุฃ ููŠ ู‚ุฑุงุฑ ุงู„ุชุฏุงูˆู„ ู„ู€ {data_payload.get('symbol', 'unknown')}: {e}")
165
+ traceback.print_exc()
166
  return None
167
 
168
  def _parse_llm_response_enhanced(self, response_text: str, fallback_strategy: str, symbol: str) -> dict:
169
  try:
170
  json_str = parse_json_from_response(response_text)
171
  if not json_str:
172
+ print(f"โŒ ูุดู„ ุงุณุชุฎุฑุงุฌ JSON ู…ู† ุงุณุชุฌุงุจุฉ ุงู„ู†ู…ูˆุฐุฌ ู„ู€ {symbol}")
173
  return None
174
 
175
  decision_data = json.loads(json_str)
176
  required_fields = ['action', 'reasoning', 'risk_assessment', 'trade_type', 'stop_loss', 'take_profit', 'expected_target_minutes', 'confidence_level']
177
  if not validate_required_fields(decision_data, required_fields):
178
+ print(f"โŒ ุญู‚ูˆู„ ู…ุทู„ูˆุจุฉ ู…ูู‚ูˆุฏุฉ ููŠ ุงุณุชุฌุงุจุฉ ุงู„ู†ู…ูˆุฐุฌ ู„ู€ {symbol}")
179
  return None
180
 
181
  strategy_value = decision_data.get('strategy')
 
184
 
185
  return decision_data
186
  except Exception as e:
187
+ print(f"โŒ ุฎุทุฃ ููŠ ุชุญู„ูŠู„ ุงุณุชุฌุงุจุฉ ุงู„ู†ู…ูˆุฐุฌ ู„ู€ {symbol}: {e}")
188
  return None
189
 
190
  async def _get_pattern_analysis(self, data_payload):
191
  try:
192
  symbol = data_payload['symbol']
193
+ ohlcv_data = data_payload.get('ohlcv') or data_payload.get('raw_ohlcv')
 
 
 
194
 
195
+ if ohlcv_data:
196
+ return await self.pattern_engine.analyze_chart_patterns(symbol, ohlcv_data)
 
 
197
 
198
  return None
199
  except Exception as e:
200
+ print(f"โŒ ูุดู„ ุชุญู„ูŠู„ ุงู„ุฃู†ู…ุงุท ู„ู€ {data_payload.get('symbol')}: {e}")
201
  return None
202
 
203
+ def _create_comprehensive_trading_prompt(self, payload: dict, news_text: str, pattern_analysis: dict, whale_data: dict) -> str:
204
  symbol = payload.get('symbol', 'N/A')
205
  current_price = payload.get('current_price', 'N/A')
206
  reasons = payload.get('reasons_for_candidacy', [])
 
211
  target_strategy = payload.get('target_strategy', 'GENERIC')
212
  final_score = payload.get('final_score', 'N/A')
213
  enhanced_final_score = payload.get('enhanced_final_score', 'N/A')
214
+ ohlcv_data = payload.get('ohlcv') or payload.get('raw_ohlcv', {})
215
 
216
+ final_score_display = f"{final_score:.3f}" if isinstance(final_score, (int, float)) else str(final_score)
217
+ enhanced_score_display = f"{enhanced_final_score:.3f}" if isinstance(enhanced_final_score, (int, float)) else str(enhanced_final_score)
218
 
219
+ # ุชู†ุณูŠู‚ ุฌู…ูŠุน ุงู„ุจูŠุงู†ุงุช ุจุดูƒู„ ุดุงู…ู„
220
  indicators_summary = format_technical_indicators(advanced_indicators)
221
  strategies_summary = format_strategy_scores(strategy_scores, recommended_strategy)
222
  pattern_summary = self._format_pattern_analysis(pattern_analysis)
223
+ whale_analysis_section = format_whale_analysis_for_llm(whale_data)
224
+ candle_data_section = self._format_candle_data_comprehensive(ohlcv_data)
225
+ market_context_section = self._format_market_context(sentiment_data)
226
 
227
  prompt = f"""
228
+ COMPREHENSIVE TRADING ANALYSIS FOR {symbol}
229
+
230
+ ๐ŸŽฏ STRATEGY CONTEXT:
231
+ - Target Strategy: {target_strategy}
232
+ - Recommended Strategy: {recommended_strategy}
233
+ - Current Price: ${current_price}
234
+ - System Score: {final_score_display}
235
+ - Enhanced Score: {enhanced_score_display}
236
+
237
+ ๐Ÿ“Š TECHNICAL INDICATORS (ALL TIMEFRAMES):
238
+ {indicators_summary}
239
+
240
+ ๐Ÿ“ˆ CANDLE DATA & PATTERN ANALYSIS:
241
+ {candle_data_section}
242
+
243
+ ๐Ÿ” PATTERN ANALYSIS RESULTS:
244
+ {pattern_summary}
245
+
246
+ ๐ŸŽฏ STRATEGY ANALYSIS:
247
+ {strategies_summary}
248
+
249
+ ๐Ÿ‹ WHALE ACTIVITY ANALYSIS:
250
+ {whale_analysis_section}
251
+
252
+ ๐ŸŒ MARKET CONTEXT:
253
+ {market_context_section}
254
+
255
+ ๐Ÿ“ฐ LATEST NEWS:
256
+ {news_text if news_text else "No significant news found"}
257
+
258
+ ๐Ÿ“‹ REASONS FOR CANDIDACY:
259
+ {chr(10).join([f"โ€ข {reason}" for reason in reasons]) if reasons else "No specific reasons provided"}
260
+
261
+ ๐ŸŽฏ TRADING DECISION INSTRUCTIONS:
262
+
263
+ 1. ANALYZE ALL PROVIDED DATA: technical indicators, whale activity, patterns, market context
264
+ 2. CONSIDER STRATEGY ALIGNMENT: {target_strategy}
265
+ 3. EVALUATE RISK-REWARD RATIO based on support/resistance levels
266
+ 4. INTEGRATE WHALE ACTIVITY signals into your decision
267
+ 5. CONSIDER PATTERN STRENGTH and timeframe confirmations
268
+ 6. ASSESS MARKET SENTIMENT impact
269
+
270
+ CRITICAL: You MUST provide specific price levels and time expectations.
271
+
272
+ OUTPUT FORMAT (JSON):
273
+ {{
274
+ "action": "BUY/SELL/HOLD",
275
+ "reasoning": "Detailed explanation integrating ALL data sources (technical, whale, patterns, news)",
276
+ "risk_assessment": "low/medium/high",
277
+ "trade_type": "LONG/SHORT",
278
+ "stop_loss": 0.000000,
279
+ "take_profit": 0.000000,
280
+ "expected_target_minutes": 15,
281
+ "confidence_level": 0.85,
282
+ "strategy": "{target_strategy}",
283
+ "whale_influence": "How whale data influenced the decision",
284
+ "pattern_influence": "How chart patterns influenced the decision",
285
+ "key_support_level": 0.000000,
286
+ "key_resistance_level": 0.000000,
287
+ "risk_reward_ratio": 2.5
288
+ }}
289
+ """
290
  return prompt
291
 
292
  def _format_pattern_analysis(self, pattern_analysis):
293
  if not pattern_analysis:
294
+ return "No clear patterns detected across analyzed timeframes"
295
+
296
  confidence = pattern_analysis.get('pattern_confidence', 0)
297
  pattern_name = pattern_analysis.get('pattern_detected', 'unknown')
298
+ predicted_direction = pattern_analysis.get('predicted_direction', 'N/A')
299
+ movement_percent = pattern_analysis.get('predicted_movement_percent', 'N/A')
300
+
301
+ analysis_lines = [
302
+ f"๐ŸŽฏ Pattern: {pattern_name}",
303
+ f"๐Ÿ“Š Confidence: {confidence:.1%}",
304
+ f"๐Ÿ“ˆ Predicted Direction: {predicted_direction}",
305
+ f"๐Ÿ’ฐ Expected Movement: {movement_percent}%",
306
+ f"๐Ÿ“ Analysis: {pattern_analysis.get('pattern_analysis', 'No detailed analysis')}"
307
+ ]
308
+
309
+ # ุฅุถุงูุฉ ู…ุณุชูˆูŠุงุช ุงู„ุฏุนู… ูˆุงู„ู…ู‚ุงูˆู…ุฉ ุฅุฐุง ูƒุงู†ุช ู…ุชูˆูุฑุฉ
310
+ support_levels = pattern_analysis.get('key_support_levels', [])
311
+ resistance_levels = pattern_analysis.get('key_resistance_levels', [])
312
+
313
+ if support_levels:
314
+ analysis_lines.append(f"๐Ÿ›Ÿ Support Levels: {', '.join([f'{level:.6f}' for level in support_levels[:3]])}")
315
+ if resistance_levels:
316
+ analysis_lines.append(f"๐Ÿšง Resistance Levels: {', '.join([f'{level:.6f}' for level in resistance_levels[:3]])}")
317
+
318
  return "\n".join(analysis_lines)
319
 
320
+ def _format_candle_data_comprehensive(self, ohlcv_data):
321
+ """ุชู†ุณูŠู‚ ุดุงู…ู„ ู„ุจูŠุงู†ุงุช ุงู„ุดู…ูˆุน"""
322
+ if not ohlcv_data:
323
+ return "No candle data available for analysis"
324
+
325
+ try:
326
+ timeframes_available = []
327
+ for timeframe, candles in ohlcv_data.items():
328
+ if candles and len(candles) >= 20:
329
+ timeframes_available.append(f"{timeframe.upper()} ({len(candles)} candles)")
330
+
331
+ if not timeframes_available:
332
+ return "Insufficient candle data across all timeframes"
333
+
334
+ summary = f"๐Ÿ“Š Available Timeframes: {', '.join(timeframes_available)}\n\n"
335
+
336
+ # ุชุญู„ูŠู„ ู„ูƒู„ ุฅุทุงุฑ ุฒู…ู†ูŠ ุฑุฆูŠุณูŠ
337
+ for timeframe in ['1d', '4h', '1h', '15m']:
338
+ if timeframe in ohlcv_data and ohlcv_data[timeframe]:
339
+ candles = ohlcv_data[timeframe]
340
+ if len(candles) >= 20:
341
+ timeframe_analysis = self._analyze_timeframe_candles(candles, timeframe)
342
+ summary += f"โฐ {timeframe.upper()} ANALYSIS:\n{timeframe_analysis}\n\n"
343
+
344
+ return summary
345
+ except Exception as e:
346
+ return f"Error formatting candle data: {str(e)}"
347
+
348
+ def _analyze_timeframe_candles(self, candles, timeframe):
349
+ """ุชุญู„ูŠู„ ุงู„ุดู…ูˆุน ู„ุฅุทุงุฑ ุฒู…ู†ูŠ ู…ุญุฏุฏ"""
350
+ try:
351
+ if len(candles) < 20:
352
+ return "Insufficient data"
353
+
354
+ recent_candles = candles[-20:] # ุขุฎุฑ 20 ุดู…ุนุฉ
355
+
356
+ # ุญุณุงุจ ุงู„ู…ุชุบูŠุฑุงุช ุงู„ุฃุณุงุณูŠุฉ
357
+ closes = [c[4] for c in recent_candles]
358
+ opens = [c[1] for c in recent_candles]
359
+ highs = [c[2] for c in recent_candles]
360
+ lows = [c[3] for c in recent_candles]
361
+ volumes = [c[5] for c in recent_candles]
362
+
363
+ current_price = closes[-1]
364
+ first_price = closes[0]
365
+ price_change = ((current_price - first_price) / first_price) * 100
366
+
367
+ # ุชุญู„ูŠู„ ุงู„ุงุชุฌุงู‡
368
+ if price_change > 2:
369
+ trend = "๐ŸŸข UPTREND"
370
+ elif price_change < -2:
371
+ trend = "๐Ÿ”ด DOWNTREND"
372
+ else:
373
+ trend = "โšช SIDEWAYS"
374
+
375
+ # ุชุญู„ูŠู„ ุงู„ุชู‚ู„ุจ
376
+ high_max = max(highs)
377
+ low_min = min(lows)
378
+ volatility = ((high_max - low_min) / low_min) * 100
379
+
380
+ # ุชุญู„ูŠู„ ุงู„ุญุฌู…
381
+ avg_volume = sum(volumes) / len(volumes)
382
+ current_volume = volumes[-1]
383
+ volume_ratio = current_volume / avg_volume if avg_volume > 0 else 1
384
+
385
+ # ุชุญู„ูŠู„ ุงู„ุดู…ูˆุน
386
+ green_candles = sum(1 for i in range(len(closes)) if closes[i] > opens[i])
387
+ red_candles = len(closes) - green_candles
388
+ candle_ratio = green_candles / len(closes)
389
+
390
+ analysis = [
391
+ f"๐Ÿ“ˆ Trend: {trend} ({price_change:+.2f}%)",
392
+ f"๐ŸŒŠ Volatility: {volatility:.2f}%",
393
+ f"๐Ÿ“ฆ Volume: {volume_ratio:.2f}x average",
394
+ f"๐Ÿ•ฏ๏ธ Candles: {green_candles}๐ŸŸข/{red_candles}๐Ÿ”ด ({candle_ratio:.1%} green)",
395
+ f"๐Ÿ’ฐ Range: {low_min:.6f} - {high_max:.6f}",
396
+ f"๐ŸŽฏ Current: {current_price:.6f}"
397
+ ]
398
+
399
+ return "\n".join(analysis)
400
+ except Exception as e:
401
+ return f"Analysis error: {str(e)}"
402
+
403
+ def _format_market_context(self, sentiment_data):
404
+ """ุชู†ุณูŠู‚ ุณูŠุงู‚ ุงู„ุณูˆู‚"""
405
+ if not sentiment_data:
406
+ return "No market context data available"
407
+
408
+ btc_sentiment = sentiment_data.get('btc_sentiment', 'N/A')
409
+ fear_greed = sentiment_data.get('fear_and_greed_index', 'N/A')
410
+ market_trend = sentiment_data.get('market_trend', 'N/A')
411
+
412
+ lines = [
413
+ "๐ŸŒ MARKET CONTEXT:",
414
+ f"โ€ข Bitcoin Sentiment: {btc_sentiment}",
415
+ f"โ€ข Fear & Greed Index: {fear_greed}",
416
+ f"โ€ข Market Trend: {market_trend}"
417
+ ]
418
+
419
+ general_whale = sentiment_data.get('general_whale_activity', {})
420
+ if general_whale:
421
+ whale_sentiment = general_whale.get('sentiment', 'N/A')
422
+ critical_alert = general_whale.get('critical_alert', False)
423
+ lines.append(f"โ€ข General Whale Sentiment: {whale_sentiment}")
424
+ if critical_alert:
425
+ lines.append("โ€ข โš ๏ธ CRITICAL WHALE ALERT")
426
+
427
+ return "\n".join(lines)
428
 
429
  async def re_analyze_trade_async(self, trade_data: dict, processed_data: dict):
430
  try:
431
  symbol = trade_data['symbol']
432
  original_strategy = trade_data.get('strategy', 'GENERIC')
433
 
434
+ # ุฌู„ุจ ุฌู…ูŠุน ุงู„ุจูŠุงู†ุงุช ุงู„ู…ุญุฏุซุฉ
435
  news_text = await self.news_fetcher.get_news_for_symbol(symbol)
436
  pattern_analysis = await self._get_pattern_analysis(processed_data)
437
+ whale_data = processed_data.get('whale_data', {})
438
 
439
+ prompt = self._create_re_analysis_prompt(trade_data, processed_data, news_text, pattern_analysis, whale_data)
440
+
441
+ # โœ… ุญูุธ ุงู„ู€ Prompt ููŠ R2
442
  if self.r2_service:
443
  analysis_data = {
444
+ 'symbol': symbol,
445
  'entry_price': trade_data.get('entry_price'),
446
  'current_price': processed_data.get('current_price'),
447
  'original_strategy': original_strategy,
448
+ 'pattern_analysis': pattern_analysis,
449
+ 'whale_data_available': whale_data.get('data_available', False)
450
  }
451
  await self.r2_service.save_llm_prompts_async(
452
  symbol, 'trade_reanalysis', prompt, analysis_data
 
458
  re_analysis_dict = self._parse_re_analysis_response(response, original_strategy, symbol)
459
  if re_analysis_dict:
460
  re_analysis_dict['model_source'] = self.model_name
461
+ re_analysis_dict['whale_data_integrated'] = whale_data.get('data_available', False)
462
  return re_analysis_dict
463
  else:
464
+ print(f"โŒ ูุดู„ ุฅุนุงุฏุฉ ุชุญู„ูŠู„ ุงู„ู†ู…ูˆุฐุฌ ุงู„ุถุฎู… ู„ู€ {symbol}")
 
465
  return None
466
 
467
  except Exception as e:
468
  print(f"โŒ ุฎุทุฃ ููŠ ุฅุนุงุฏุฉ ุชุญู„ูŠู„ LLM: {e}")
469
+ traceback.print_exc()
470
  return None
471
 
472
  def _parse_re_analysis_response(self, response_text: str, fallback_strategy: str, symbol: str) -> dict:
 
485
  print(f"Error parsing re-analysis response for {symbol}: {e}")
486
  return None
487
 
488
+ def _create_re_analysis_prompt(self, trade_data: dict, processed_data: dict, news_text: str, pattern_analysis: dict, whale_data: dict) -> str:
489
  symbol = trade_data.get('symbol', 'N/A')
490
  entry_price = trade_data.get('entry_price', 'N/A')
491
  current_price = processed_data.get('current_price', 'N/A')
 
499
 
500
  indicators_summary = format_technical_indicators(processed_data.get('advanced_indicators', {}))
501
  pattern_summary = self._format_pattern_analysis(pattern_analysis)
502
+ whale_analysis_section = format_whale_analysis_for_llm(whale_data)
503
+ market_context_section = self._format_market_context(processed_data.get('sentiment_data', {}))
504
 
505
  prompt = f"""
506
+ TRADE RE-ANALYSIS FOR {symbol}
507
+
508
+ ๐Ÿ“Š TRADE CONTEXT:
509
+ - Strategy: {strategy}
510
+ - Entry Price: {entry_price}
511
+ - Current Price: {current_price}
512
+ - Performance: {price_change_display}
513
+ - Trade Age: {trade_data.get('hold_duration_minutes', 'N/A')} minutes
514
+
515
+ ๐Ÿ”„ UPDATED TECHNICAL ANALYSIS:
516
+ {indicators_summary}
517
+
518
+ ๐Ÿ“ˆ UPDATED PATTERN ANALYSIS:
519
+ {pattern_summary}
520
+
521
+ ๐Ÿ‹ UPDATED WHALE ACTIVITY:
522
+ {whale_analysis_section}
523
+
524
+ ๐ŸŒ UPDATED MARKET CONTEXT:
525
+ {market_context_section}
526
+
527
+ ๐Ÿ“ฐ LATEST NEWS:
528
+ {news_text if news_text else "No significant news found"}
529
+
530
+ ๐ŸŽฏ RE-ANALYSIS INSTRUCTIONS:
531
+
532
+ 1. Evaluate if the original thesis still holds
533
+ 2. Consider new whale activity and patterns
534
+ 3. Assess current risk-reward ratio
535
+ 4. Decide whether to hold, close, or adjust the trade
536
+ 5. Provide specific updated levels if adjusting
537
+
538
+ OUTPUT FORMAT (JSON):
539
+ {{
540
+ "action": "HOLD/CLOSE_TRADE/UPDATE_TRADE",
541
+ "reasoning": "Comprehensive justification based on updated analysis",
542
+ "new_stop_loss": 0.000000,
543
+ "new_take_profit": 0.000000,
544
+ "new_expected_minutes": 15,
545
+ "confidence_level": 0.85,
546
+ "strategy": "{strategy}",
547
+ "whale_influence_reanalysis": "How updated whale data influenced decision",
548
+ "pattern_influence_reanalysis": "How updated patterns influenced decision",
549
+ "risk_adjustment": "low/medium/high"
550
+ }}
551
+ """
552
  return prompt
553
 
554
  @_rate_limit_nvidia_api
 
558
  model=self.model_name,
559
  messages=[{"role": "user", "content": prompt}],
560
  temperature=self.temperature,
561
+ seed=42,
562
+ max_tokens=4000
563
  )
564
  return response.choices[0].message.content
565
  except (RateLimitError, APITimeoutError) as e:
566
+ print(f"โŒ LLM API Error: {e}. Retrying...")
567
  raise
568
  except Exception as e:
569
+ print(f"โŒ Unexpected LLM API error: {e}")
570
+ raise
571
+
572
+ print("โœ… LLM Service loaded - Comprehensive Analysis with Whale Data & Pattern Integration")