quantalogic 0.35.0__py3-none-any.whl → 0.50.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. quantalogic/__init__.py +0 -4
  2. quantalogic/agent.py +603 -363
  3. quantalogic/agent_config.py +233 -46
  4. quantalogic/agent_factory.py +34 -22
  5. quantalogic/coding_agent.py +16 -14
  6. quantalogic/config.py +2 -1
  7. quantalogic/console_print_events.py +4 -8
  8. quantalogic/console_print_token.py +2 -2
  9. quantalogic/docs_cli.py +15 -10
  10. quantalogic/event_emitter.py +258 -83
  11. quantalogic/flow/__init__.py +23 -0
  12. quantalogic/flow/flow.py +595 -0
  13. quantalogic/flow/flow_extractor.py +672 -0
  14. quantalogic/flow/flow_generator.py +89 -0
  15. quantalogic/flow/flow_manager.py +407 -0
  16. quantalogic/flow/flow_manager_schema.py +169 -0
  17. quantalogic/flow/flow_yaml.md +419 -0
  18. quantalogic/generative_model.py +109 -77
  19. quantalogic/get_model_info.py +5 -5
  20. quantalogic/interactive_text_editor.py +100 -73
  21. quantalogic/main.py +17 -21
  22. quantalogic/model_info_list.py +3 -3
  23. quantalogic/model_info_litellm.py +14 -14
  24. quantalogic/prompts.py +2 -1
  25. quantalogic/{llm.py → quantlitellm.py} +29 -39
  26. quantalogic/search_agent.py +4 -4
  27. quantalogic/server/models.py +4 -1
  28. quantalogic/task_file_reader.py +5 -5
  29. quantalogic/task_runner.py +20 -20
  30. quantalogic/tool_manager.py +10 -21
  31. quantalogic/tools/__init__.py +98 -68
  32. quantalogic/tools/composio/composio.py +416 -0
  33. quantalogic/tools/{generate_database_report_tool.py → database/generate_database_report_tool.py} +4 -9
  34. quantalogic/tools/database/sql_query_tool_advanced.py +261 -0
  35. quantalogic/tools/document_tools/markdown_to_docx_tool.py +620 -0
  36. quantalogic/tools/document_tools/markdown_to_epub_tool.py +438 -0
  37. quantalogic/tools/document_tools/markdown_to_html_tool.py +362 -0
  38. quantalogic/tools/document_tools/markdown_to_ipynb_tool.py +319 -0
  39. quantalogic/tools/document_tools/markdown_to_latex_tool.py +420 -0
  40. quantalogic/tools/document_tools/markdown_to_pdf_tool.py +623 -0
  41. quantalogic/tools/document_tools/markdown_to_pptx_tool.py +319 -0
  42. quantalogic/tools/duckduckgo_search_tool.py +2 -4
  43. quantalogic/tools/finance/alpha_vantage_tool.py +440 -0
  44. quantalogic/tools/finance/ccxt_tool.py +373 -0
  45. quantalogic/tools/finance/finance_llm_tool.py +387 -0
  46. quantalogic/tools/finance/google_finance.py +192 -0
  47. quantalogic/tools/finance/market_intelligence_tool.py +520 -0
  48. quantalogic/tools/finance/technical_analysis_tool.py +491 -0
  49. quantalogic/tools/finance/tradingview_tool.py +336 -0
  50. quantalogic/tools/finance/yahoo_finance.py +236 -0
  51. quantalogic/tools/git/bitbucket_clone_repo_tool.py +181 -0
  52. quantalogic/tools/git/bitbucket_operations_tool.py +326 -0
  53. quantalogic/tools/git/clone_repo_tool.py +189 -0
  54. quantalogic/tools/git/git_operations_tool.py +532 -0
  55. quantalogic/tools/google_packages/google_news_tool.py +480 -0
  56. quantalogic/tools/grep_app_tool.py +123 -186
  57. quantalogic/tools/{dalle_e.py → image_generation/dalle_e.py} +37 -27
  58. quantalogic/tools/jinja_tool.py +6 -10
  59. quantalogic/tools/language_handlers/__init__.py +22 -9
  60. quantalogic/tools/list_directory_tool.py +131 -42
  61. quantalogic/tools/llm_tool.py +45 -15
  62. quantalogic/tools/llm_vision_tool.py +59 -7
  63. quantalogic/tools/markitdown_tool.py +17 -5
  64. quantalogic/tools/nasa_packages/models.py +47 -0
  65. quantalogic/tools/nasa_packages/nasa_apod_tool.py +232 -0
  66. quantalogic/tools/nasa_packages/nasa_neows_tool.py +147 -0
  67. quantalogic/tools/nasa_packages/services.py +82 -0
  68. quantalogic/tools/presentation_tools/presentation_llm_tool.py +396 -0
  69. quantalogic/tools/product_hunt/product_hunt_tool.py +258 -0
  70. quantalogic/tools/product_hunt/services.py +63 -0
  71. quantalogic/tools/rag_tool/__init__.py +48 -0
  72. quantalogic/tools/rag_tool/document_metadata.py +15 -0
  73. quantalogic/tools/rag_tool/query_response.py +20 -0
  74. quantalogic/tools/rag_tool/rag_tool.py +566 -0
  75. quantalogic/tools/rag_tool/rag_tool_beta.py +264 -0
  76. quantalogic/tools/read_html_tool.py +24 -38
  77. quantalogic/tools/replace_in_file_tool.py +10 -10
  78. quantalogic/tools/safe_python_interpreter_tool.py +10 -24
  79. quantalogic/tools/search_definition_names.py +2 -2
  80. quantalogic/tools/sequence_tool.py +14 -23
  81. quantalogic/tools/sql_query_tool.py +17 -19
  82. quantalogic/tools/tool.py +39 -15
  83. quantalogic/tools/unified_diff_tool.py +1 -1
  84. quantalogic/tools/utilities/csv_processor_tool.py +234 -0
  85. quantalogic/tools/utilities/download_file_tool.py +179 -0
  86. quantalogic/tools/utilities/mermaid_validator_tool.py +661 -0
  87. quantalogic/tools/utils/__init__.py +1 -4
  88. quantalogic/tools/utils/create_sample_database.py +24 -38
  89. quantalogic/tools/utils/generate_database_report.py +74 -82
  90. quantalogic/tools/wikipedia_search_tool.py +17 -21
  91. quantalogic/utils/ask_user_validation.py +1 -1
  92. quantalogic/utils/async_utils.py +35 -0
  93. quantalogic/utils/check_version.py +3 -5
  94. quantalogic/utils/get_all_models.py +2 -1
  95. quantalogic/utils/git_ls.py +21 -7
  96. quantalogic/utils/lm_studio_model_info.py +9 -7
  97. quantalogic/utils/python_interpreter.py +113 -43
  98. quantalogic/utils/xml_utility.py +178 -0
  99. quantalogic/version_check.py +1 -1
  100. quantalogic/welcome_message.py +7 -7
  101. quantalogic/xml_parser.py +0 -1
  102. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/METADATA +40 -1
  103. quantalogic-0.50.0.dist-info/RECORD +148 -0
  104. quantalogic-0.35.0.dist-info/RECORD +0 -102
  105. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/LICENSE +0 -0
  106. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/WHEEL +0 -0
  107. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,520 @@
1
+ """Advanced Market Intelligence Tool for comprehensive financial information retrieval."""
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from datetime import datetime
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ import ccxt
9
+ import numpy as np
10
+ import pandas as pd
11
+ import requests
12
+ import tweepy
13
+ import yfinance as yf
14
+ from alpha_vantage.fundamentaldata import FundamentalData
15
+ from alpha_vantage.timeseries import TimeSeries
16
+ from loguru import logger
17
+ from newsapi import NewsApiClient
18
+ from serpapi import GoogleSearch
19
+
20
+ from quantalogic.agent import Agent
21
+ from quantalogic.tools.tool import Tool, ToolArgument
22
+
23
+
24
+ @dataclass
25
+ class MarketNews:
26
+ """Container for market news data."""
27
+ title: str
28
+ source: str
29
+ url: str
30
+ published_at: datetime
31
+ sentiment: float
32
+ relevance: float
33
+ summary: str
34
+ keywords: List[str]
35
+
36
+ @dataclass
37
+ class SocialMetrics:
38
+ """Container for social media metrics."""
39
+ sentiment_score: float
40
+ engagement_rate: float
41
+ mention_count: int
42
+ trending_topics: List[str]
43
+ influential_posts: List[Dict]
44
+ source: str
45
+ timestamp: datetime
46
+
47
+ @dataclass
48
+ class FundamentalMetrics:
49
+ """Container for fundamental analysis metrics."""
50
+ market_cap: float
51
+ pe_ratio: Optional[float]
52
+ pb_ratio: Optional[float]
53
+ debt_to_equity: Optional[float]
54
+ current_ratio: Optional[float]
55
+ quick_ratio: Optional[float]
56
+ roa: Optional[float]
57
+ roe: Optional[float]
58
+ profit_margin: Optional[float]
59
+ revenue_growth: Optional[float]
60
+ institutional_ownership: Optional[float]
61
+ insider_ownership: Optional[float]
62
+
63
+ @dataclass
64
+ class MarketIntelligence:
65
+ """Container for comprehensive market intelligence."""
66
+ symbol: str
67
+ asset_type: str
68
+ timestamp: datetime
69
+ current_price: float
70
+ market_news: List[MarketNews]
71
+ social_metrics: Dict[str, SocialMetrics]
72
+ fundamental_metrics: Optional[FundamentalMetrics]
73
+ analyst_ratings: Dict[str, Any]
74
+ market_events: List[Dict]
75
+ regulatory_updates: List[Dict]
76
+ competitor_analysis: Dict[str, Any]
77
+ risk_metrics: Dict[str, float]
78
+
79
+ class MarketIntelligenceTool(Tool):
80
+ """Advanced market intelligence tool for comprehensive financial information retrieval."""
81
+
82
+ name: str = "market_intelligence_tool"
83
+ description: str = (
84
+ "Advanced tool for retrieving comprehensive market intelligence including "
85
+ "news, social metrics, fundamental data, and market events for various "
86
+ "financial instruments."
87
+ )
88
+
89
+ arguments: list = [
90
+ ToolArgument(
91
+ name="symbols",
92
+ arg_type="list",
93
+ description="List of trading symbols to analyze",
94
+ required=True,
95
+ example="['BTC/USDT', 'AAPL', 'SPY']"
96
+ ),
97
+ ToolArgument(
98
+ name="asset_types",
99
+ arg_type="list",
100
+ description="List of asset types (crypto/stock/index)",
101
+ required=True,
102
+ example="['crypto', 'stock', 'index']"
103
+ ),
104
+ ToolArgument(
105
+ name="data_types",
106
+ arg_type="list",
107
+ description="Types of data to retrieve",
108
+ required=False,
109
+ default="['all']"
110
+ ),
111
+ ToolArgument(
112
+ name="time_range",
113
+ arg_type="string",
114
+ description="Time range for historical data",
115
+ required=False,
116
+ default="7d"
117
+ )
118
+ ]
119
+
120
+ def __init__(self, **kwargs):
121
+ """Initialize the Market Intelligence tool with API configurations."""
122
+ super().__init__(**kwargs)
123
+
124
+ # Initialize API clients
125
+ self._init_api_clients()
126
+
127
+ # Initialize cache
128
+ self.cache = {}
129
+
130
+ def _init_api_clients(self):
131
+ """Initialize various API clients."""
132
+ try:
133
+ # News API
134
+ self.news_api = NewsApiClient(api_key=os.getenv('NEWS_API_KEY'))
135
+
136
+ # Alpha Vantage
137
+ self.alpha_vantage_fd = FundamentalData(key=os.getenv('ALPHA_VANTAGE_KEY'))
138
+ self.alpha_vantage_ts = TimeSeries(key=os.getenv('ALPHA_VANTAGE_KEY'))
139
+
140
+ # Twitter API
141
+ auth = tweepy.OAuthHandler(
142
+ os.getenv('TWITTER_API_KEY'),
143
+ os.getenv('TWITTER_API_SECRET')
144
+ )
145
+ auth.set_access_token(
146
+ os.getenv('TWITTER_ACCESS_TOKEN'),
147
+ os.getenv('TWITTER_ACCESS_TOKEN_SECRET')
148
+ )
149
+ self.twitter_api = tweepy.API(auth)
150
+
151
+ # SerpAPI
152
+ self.serp_api = GoogleSearch({
153
+ "api_key": os.getenv('SERPAPI_KEY')
154
+ })
155
+
156
+ # CCXT
157
+ self.exchange = ccxt.binance()
158
+
159
+ except Exception as e:
160
+ logger.error(f"Error initializing API clients: {e}")
161
+ raise
162
+
163
+ async def _fetch_market_news(self, symbol: str, asset_type: str) -> List[MarketNews]:
164
+ """Fetch and analyze market news from multiple sources."""
165
+ news_items = []
166
+
167
+ try:
168
+ # News API
169
+ news_query = f"{symbol} stock" if asset_type == "stock" else f"{symbol} crypto"
170
+ news_response = self.news_api.get_everything(
171
+ q=news_query,
172
+ language='en',
173
+ sort_by='relevancy',
174
+ page_size=20
175
+ )
176
+
177
+ # Google News via SerpAPI
178
+ serp_params = {
179
+ "q": f"{symbol} news",
180
+ "tbm": "nws",
181
+ "num": 20
182
+ }
183
+ serp_response = self.serp_api.get_dict()
184
+
185
+ # Combine and process news
186
+ for article in news_response['articles'] + serp_response.get('news_results', []):
187
+ # Analyze sentiment
188
+ sentiment = self._analyze_sentiment(article['title'] + " " + article.get('description', ''))
189
+
190
+ # Calculate relevance
191
+ relevance = self._calculate_relevance(article, symbol)
192
+
193
+ news_items.append(MarketNews(
194
+ title=article['title'],
195
+ source=article['source']['name'],
196
+ url=article['url'],
197
+ published_at=datetime.strptime(article['publishedAt'][:19], '%Y-%m-%dT%H:%M:%S'),
198
+ sentiment=sentiment,
199
+ relevance=relevance,
200
+ summary=article.get('description', ''),
201
+ keywords=self._extract_keywords(article)
202
+ ))
203
+
204
+ return sorted(news_items, key=lambda x: (x.relevance, x.published_at), reverse=True)
205
+
206
+ except Exception as e:
207
+ logger.error(f"Error fetching market news: {e}")
208
+ return []
209
+
210
+ async def _fetch_social_metrics(self, symbol: str, asset_type: str) -> Dict[str, SocialMetrics]:
211
+ """Fetch and analyze social media metrics."""
212
+ social_data = {}
213
+
214
+ try:
215
+ # Twitter Analysis
216
+ twitter_metrics = await self._analyze_twitter(symbol)
217
+ social_data['twitter'] = twitter_metrics
218
+
219
+ # Reddit Analysis
220
+ reddit_metrics = await self._analyze_reddit(symbol)
221
+ social_data['reddit'] = reddit_metrics
222
+
223
+ # StockTwits Analysis (if stock)
224
+ if asset_type == 'stock':
225
+ stocktwits_metrics = await self._analyze_stocktwits(symbol)
226
+ social_data['stocktwits'] = stocktwits_metrics
227
+
228
+ return social_data
229
+
230
+ except Exception as e:
231
+ logger.error(f"Error fetching social metrics: {e}")
232
+ return {}
233
+
234
+ async def _fetch_fundamental_data(self, symbol: str, asset_type: str) -> Optional[FundamentalMetrics]:
235
+ """Fetch fundamental data for stocks."""
236
+ if asset_type != 'stock':
237
+ return None
238
+
239
+ try:
240
+ # Alpha Vantage Fundamental Data
241
+ overview = self.alpha_vantage_fd.get_company_overview(symbol)[0]
242
+
243
+ # Yahoo Finance Additional Data
244
+ yf_data = yf.Ticker(symbol)
245
+ info = yf_data.info
246
+
247
+ return FundamentalMetrics(
248
+ market_cap=float(overview.get('MarketCapitalization', 0)),
249
+ pe_ratio=float(overview.get('PERatio', 0)) or None,
250
+ pb_ratio=float(overview.get('PriceToBookRatio', 0)) or None,
251
+ debt_to_equity=float(overview.get('DebtToEquityRatio', 0)) or None,
252
+ current_ratio=float(overview.get('CurrentRatio', 0)) or None,
253
+ quick_ratio=float(info.get('quickRatio', 0)) or None,
254
+ roa=float(overview.get('ReturnOnAssetsTTM', 0)) or None,
255
+ roe=float(overview.get('ReturnOnEquityTTM', 0)) or None,
256
+ profit_margin=float(overview.get('ProfitMargin', 0)) or None,
257
+ revenue_growth=float(info.get('revenueGrowth', 0)) or None,
258
+ institutional_ownership=float(info.get('institutionPercentHeld', 0)) or None,
259
+ insider_ownership=float(info.get('insiderPercentHeld', 0)) or None
260
+ )
261
+
262
+ except Exception as e:
263
+ logger.error(f"Error fetching fundamental data: {e}")
264
+ return None
265
+
266
+ async def _fetch_analyst_ratings(self, symbol: str, asset_type: str) -> Dict[str, Any]:
267
+ """Fetch and analyze analyst ratings and price targets."""
268
+ ratings = {}
269
+
270
+ try:
271
+ if asset_type == 'stock':
272
+ # Yahoo Finance Analyst Ratings
273
+ yf_data = yf.Ticker(symbol)
274
+ recommendations = yf_data.recommendations
275
+
276
+ if recommendations is not None:
277
+ ratings['recommendations'] = recommendations.to_dict('records')
278
+
279
+ # Seeking Alpha via SerpAPI
280
+ serp_params = {
281
+ "q": f"{symbol} stock analysis site:seekingalpha.com",
282
+ "num": 5
283
+ }
284
+ serp_response = self.serp_api.get_dict()
285
+
286
+ if 'organic_results' in serp_response:
287
+ ratings['analysis_articles'] = serp_response['organic_results']
288
+
289
+ elif asset_type == 'crypto':
290
+ # CoinGecko API for crypto
291
+ url = f"https://api.coingecko.com/api/v3/coins/{symbol.lower()}/market_data"
292
+ response = requests.get(url)
293
+ if response.status_code == 200:
294
+ ratings['market_data'] = response.json()
295
+
296
+ return ratings
297
+
298
+ except Exception as e:
299
+ logger.error(f"Error fetching analyst ratings: {e}")
300
+ return {}
301
+
302
+ async def _fetch_market_events(self, symbol: str, asset_type: str) -> List[Dict]:
303
+ """Fetch significant market events and calendar items."""
304
+ events = []
305
+
306
+ try:
307
+ if asset_type == 'stock':
308
+ # Earnings Calendar
309
+ yf_data = yf.Ticker(symbol)
310
+ calendar = yf_data.calendar
311
+ if calendar is not None:
312
+ events.extend([
313
+ {'type': 'earnings', 'data': calendar}
314
+ ])
315
+
316
+ # SEC Filings
317
+ filings = yf_data.get_sec_filings()
318
+ if filings is not None:
319
+ events.extend([
320
+ {'type': 'sec_filing', 'data': filing}
321
+ for filing in filings.to_dict('records')
322
+ ])
323
+
324
+ elif asset_type == 'crypto':
325
+ # CoinGecko Events
326
+ url = f"https://api.coingecko.com/api/v3/coins/{symbol.lower()}/events"
327
+ response = requests.get(url)
328
+ if response.status_code == 200:
329
+ events.extend([
330
+ {'type': 'crypto_event', 'data': event}
331
+ for event in response.json()['data']
332
+ ])
333
+
334
+ return events
335
+
336
+ except Exception as e:
337
+ logger.error(f"Error fetching market events: {e}")
338
+ return []
339
+
340
+ async def _fetch_regulatory_updates(self, symbol: str, asset_type: str) -> List[Dict]:
341
+ """Fetch regulatory updates and compliance information."""
342
+ updates = []
343
+
344
+ try:
345
+ # SEC API for stocks
346
+ if asset_type == 'stock':
347
+ sec_url = f"https://data.sec.gov/submissions/CIK{symbol}.json"
348
+ response = requests.get(sec_url)
349
+ if response.status_code == 200:
350
+ updates.extend([
351
+ {'type': 'sec_update', 'data': filing}
352
+ for filing in response.json()['filings']['recent']
353
+ ])
354
+
355
+ # Regulatory news search
356
+ serp_params = {
357
+ "q": f"{symbol} regulation compliance news",
358
+ "tbm": "nws",
359
+ "num": 5
360
+ }
361
+ serp_response = self.serp_api.get_dict()
362
+
363
+ if 'news_results' in serp_response:
364
+ updates.extend([
365
+ {'type': 'regulatory_news', 'data': news}
366
+ for news in serp_response['news_results']
367
+ ])
368
+
369
+ return updates
370
+
371
+ except Exception as e:
372
+ logger.error(f"Error fetching regulatory updates: {e}")
373
+ return []
374
+
375
+ async def _analyze_competitors(self, symbol: str, asset_type: str) -> Dict[str, Any]:
376
+ """Analyze competitor performance and relative metrics."""
377
+ analysis = {}
378
+
379
+ try:
380
+ if asset_type == 'stock':
381
+ # Get sector peers
382
+ yf_data = yf.Ticker(symbol)
383
+ info = yf_data.info
384
+
385
+ if 'sector' in info:
386
+ # Find companies in same sector
387
+ sector_etf = self._get_sector_etf(info['sector'])
388
+ if sector_etf:
389
+ etf_data = yf.Ticker(sector_etf)
390
+ holdings = etf_data.get_holdings()
391
+
392
+ if holdings is not None:
393
+ analysis['sector_peers'] = holdings.head(10).to_dict('records')
394
+
395
+ # Relative performance
396
+ if 'sector_peers' in analysis:
397
+ peer_performance = {}
398
+ for peer in analysis['sector_peers']:
399
+ peer_data = yf.Ticker(peer['symbol'])
400
+ peer_performance[peer['symbol']] = {
401
+ 'return_1y': peer_data.info.get('regularMarketPrice', 0) /
402
+ peer_data.info.get('regularMarketPreviousClose', 1) - 1,
403
+ 'market_cap': peer_data.info.get('marketCap', 0),
404
+ 'pe_ratio': peer_data.info.get('forwardPE', 0)
405
+ }
406
+ analysis['peer_performance'] = peer_performance
407
+
408
+ elif asset_type == 'crypto':
409
+ # Get top coins by market cap
410
+ url = "https://api.coingecko.com/api/v3/coins/markets"
411
+ params = {
412
+ 'vs_currency': 'usd',
413
+ 'order': 'market_cap_desc',
414
+ 'per_page': 10,
415
+ 'page': 1
416
+ }
417
+ response = requests.get(url, params=params)
418
+ if response.status_code == 200:
419
+ analysis['market_leaders'] = response.json()
420
+
421
+ return analysis
422
+
423
+ except Exception as e:
424
+ logger.error(f"Error analyzing competitors: {e}")
425
+ return {}
426
+
427
+ async def _calculate_risk_metrics(self, symbol: str, asset_type: str) -> Dict[str, float]:
428
+ """Calculate comprehensive risk metrics."""
429
+ risk_metrics = {}
430
+
431
+ try:
432
+ # Get historical data
433
+ if asset_type == 'stock':
434
+ data = yf.download(symbol, period='1y')
435
+ else:
436
+ data = self.exchange.fetch_ohlcv(symbol, '1d', limit=365)
437
+ data = pd.DataFrame(data, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
438
+
439
+ # Calculate risk metrics
440
+ returns = data['close'].pct_change().dropna()
441
+
442
+ risk_metrics.update({
443
+ 'volatility': returns.std() * np.sqrt(252),
444
+ 'var_95': returns.quantile(0.05),
445
+ 'max_drawdown': (data['close'] / data['close'].expanding(min_periods=1).max() - 1).min(),
446
+ 'sharpe_ratio': (returns.mean() * 252) / (returns.std() * np.sqrt(252)),
447
+ 'sortino_ratio': (returns.mean() * 252) / (returns[returns < 0].std() * np.sqrt(252))
448
+ })
449
+
450
+ return risk_metrics
451
+
452
+ except Exception as e:
453
+ logger.error(f"Error calculating risk metrics: {e}")
454
+ return {}
455
+
456
+ async def execute(self, agent: Agent, **kwargs) -> Dict[str, MarketIntelligence]:
457
+ """Execute comprehensive market intelligence gathering."""
458
+ try:
459
+ symbols = kwargs['symbols']
460
+ asset_types = kwargs['asset_types']
461
+ data_types = kwargs.get('data_types', ['all'])
462
+
463
+ results = {}
464
+
465
+ for symbol, asset_type in zip(symbols, asset_types):
466
+ # Get current price
467
+ current_price = await self._get_current_price(symbol, asset_type)
468
+
469
+ # Gather intelligence components
470
+ market_news = await self._fetch_market_news(symbol, asset_type)
471
+ social_metrics = await self._fetch_social_metrics(symbol, asset_type)
472
+ fundamental_metrics = await self._fetch_fundamental_data(symbol, asset_type)
473
+ analyst_ratings = await self._fetch_analyst_ratings(symbol, asset_type)
474
+ market_events = await self._fetch_market_events(symbol, asset_type)
475
+ regulatory_updates = await self._fetch_regulatory_updates(symbol, asset_type)
476
+ competitor_analysis = await self._analyze_competitors(symbol, asset_type)
477
+ risk_metrics = await self._calculate_risk_metrics(symbol, asset_type)
478
+
479
+ # Combine all intelligence
480
+ results[symbol] = MarketIntelligence(
481
+ symbol=symbol,
482
+ asset_type=asset_type,
483
+ timestamp=datetime.now(),
484
+ current_price=current_price,
485
+ market_news=market_news,
486
+ social_metrics=social_metrics,
487
+ fundamental_metrics=fundamental_metrics,
488
+ analyst_ratings=analyst_ratings,
489
+ market_events=market_events,
490
+ regulatory_updates=regulatory_updates,
491
+ competitor_analysis=competitor_analysis,
492
+ risk_metrics=risk_metrics
493
+ )
494
+
495
+ return results
496
+
497
+ except Exception as e:
498
+ logger.error(f"Error executing market intelligence gathering: {e}")
499
+ raise
500
+
501
+ def validate_arguments(self, **kwargs) -> bool:
502
+ """Validate the provided arguments."""
503
+ try:
504
+ required_args = [arg.name for arg in self.arguments if arg.required]
505
+ for arg in required_args:
506
+ if arg not in kwargs:
507
+ raise ValueError(f"Missing required argument: {arg}")
508
+
509
+ if len(kwargs['symbols']) != len(kwargs['asset_types']):
510
+ raise ValueError("Number of symbols must match number of asset types")
511
+
512
+ valid_asset_types = ['crypto', 'stock', 'index']
513
+ invalid_types = set(kwargs['asset_types']) - set(valid_asset_types)
514
+ if invalid_types:
515
+ raise ValueError(f"Invalid asset types: {invalid_types}")
516
+
517
+ return True
518
+ except Exception as e:
519
+ logger.error(f"Argument validation error: {e}")
520
+ return False