File size: 8,767 Bytes
20eee66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
from google import genai
from google.genai import types
import json
from typing import Dict, Any, List
from src.api_clients import coingecko_client, cryptocompare_client
from src.cache_manager import cache_manager
from src.config import config
import asyncio

class ResearchAgent:
    def __init__(self):
        self.client = genai.Client(api_key=config.GEMINI_API_KEY)
        self.symbol_map = {
            "btc": "bitcoin", "eth": "ethereum", "sol": "solana",
            "ada": "cardano", "dot": "polkadot", "bnb": "binancecoin",
            "usdc": "usd-coin", "usdt": "tether", "xrp": "ripple",
            "avax": "avalanche-2", "link": "chainlink", "matic": "matic-network"
        }
    
    def _format_coin_id(self, symbol: str) -> str:
        return self.symbol_map.get(symbol.lower(), symbol.lower())
    
    async def get_market_overview(self) -> Dict[str, Any]:
        cache_key = "market_overview"
        cached = cache_manager.get(cache_key)
        if cached:
            return cached
        
        try:
            market_data = await coingecko_client.get_market_data(per_page=20)
            global_data = await coingecko_client.get_global_data()
            trending = await coingecko_client.get_trending()
            
            result = {
                "market_data": market_data,
                "global_data": global_data,
                "trending": trending
            }
            
            cache_manager.set(cache_key, result)
            return result
            
        except Exception as e:
            raise Exception(f"Failed to fetch market overview: {str(e)}")
    
    async def get_price_history(self, symbol: str) -> Dict[str, Any]:
        cache_key = f"price_history_{symbol.lower()}"
        cached = cache_manager.get(cache_key)
        if cached:
            return cached
        
        try:
            coin_id = self._format_coin_id(symbol)
            data = await coingecko_client.get_price_history(coin_id, days=30)
            
            cache_manager.set(cache_key, data)
            return data
            
        except Exception as e:
            raise Exception(f"Failed to fetch price history for {symbol}: {str(e)}")
    
    async def get_coin_analysis(self, symbol: str) -> Dict[str, Any]:
        cache_key = f"coin_analysis_{symbol.lower()}"
        cached = cache_manager.get(cache_key)
        if cached:
            return cached
        
        try:
            coin_id = self._format_coin_id(symbol)
            
            tasks = [
                coingecko_client.get_coin_data(coin_id),
                coingecko_client.get_price_history(coin_id, days=7),
                cryptocompare_client.get_social_data(symbol.upper())
            ]
            
            coin_data, price_history, social_data = await asyncio.gather(*tasks, return_exceptions=True)
            
            result = {}
            if not isinstance(coin_data, Exception):
                result["coin_data"] = coin_data
            if not isinstance(price_history, Exception):
                result["price_history"] = price_history
            if not isinstance(social_data, Exception):
                result["social_data"] = social_data
            
            cache_manager.set(cache_key, result)
            return result
            
        except Exception as e:
            raise Exception(f"Failed to analyze {symbol}: {str(e)}")
    
    def _format_market_data(self, data: Dict[str, Any]) -> str:
        if not data:
            return "No market data available"
        
        formatted = "πŸ“Š MARKET OVERVIEW\n\n"
        
        if "global_data" in data and "data" in data["global_data"]:
            global_info = data["global_data"]["data"]
            total_mcap = global_info.get("total_market_cap", {}).get("usd", 0)
            total_volume = global_info.get("total_volume", {}).get("usd", 0)
            btc_dominance = global_info.get("market_cap_percentage", {}).get("btc", 0)
            
            formatted += f"Total Market Cap: ${total_mcap:,.0f}\n"
            formatted += f"24h Volume: ${total_volume:,.0f}\n"
            formatted += f"Bitcoin Dominance: {btc_dominance:.1f}%\n\n"
        
        if "trending" in data and "coins" in data["trending"]:
            formatted += "πŸ”₯ TRENDING COINS\n"
            for i, coin in enumerate(data["trending"]["coins"][:5], 1):
                name = coin.get("item", {}).get("name", "Unknown")
                symbol = coin.get("item", {}).get("symbol", "")
                formatted += f"{i}. {name} ({symbol.upper()})\n"
            formatted += "\n"
        
        if "market_data" in data:
            formatted += "πŸ’° TOP CRYPTOCURRENCIES\n"
            for i, coin in enumerate(data["market_data"][:10], 1):
                name = coin.get("name", "Unknown")
                symbol = coin.get("symbol", "").upper()
                price = coin.get("current_price", 0)
                change = coin.get("price_change_percentage_24h", 0)
                change_symbol = "πŸ“ˆ" if change >= 0 else "πŸ“‰"
                
                formatted += f"{i:2d}. {name} ({symbol}): ${price:,.4f} {change_symbol} {change:+.2f}%\n"
        
        return formatted
    
    async def research(self, query: str) -> str:
        try:
            if not config.GEMINI_API_KEY:
                return "❌ Gemini API key not configured. Please set GEMINI_API_KEY environment variable."
            
            system_prompt = """You are an expert Web3 and cryptocurrency research analyst. 
            Provide comprehensive, accurate, and actionable insights based on real market data.
            
            Guidelines:
            - Give specific, data-driven analysis
            - Include price targets and risk assessments when relevant
            - Explain technical concepts clearly
            - Provide actionable recommendations
            - Use emojis for better readability
            - Be concise but thorough
            """
            
            market_context = ""
            try:
                if any(keyword in query.lower() for keyword in ["market", "overview", "trending", "top"]):
                    market_data = await self.get_market_overview()
                    market_context = f"\n\nCURRENT MARKET DATA:\n{self._format_market_data(market_data)}"
                
                for symbol in ["btc", "eth", "sol", "ada", "dot", "bnb", "avax", "link"]:
                    if symbol in query.lower() or symbol.upper() in query:
                        analysis_data = await self.get_coin_analysis(symbol)
                        if "coin_data" in analysis_data:
                            coin_info = analysis_data["coin_data"]
                            market_data = coin_info.get("market_data", {})
                            current_price = market_data.get("current_price", {}).get("usd", 0)
                            price_change = market_data.get("price_change_percentage_24h", 0)
                            market_cap = market_data.get("market_cap", {}).get("usd", 0)
                            volume = market_data.get("total_volume", {}).get("usd", 0)
                            
                            market_context += f"\n\n{symbol.upper()} DATA:\n"
                            market_context += f"Price: ${current_price:,.4f}\n"
                            market_context += f"24h Change: {price_change:+.2f}%\n"
                            market_context += f"Market Cap: ${market_cap:,.0f}\n"
                            market_context += f"Volume: ${volume:,.0f}\n"
                        break
                
            except Exception as e:
                market_context = f"\n\nNote: Some market data unavailable ({str(e)})"
            
            full_prompt = f"{query}{market_context}"
            
            response = self.client.models.generate_content(
                model="gemini-2.5-flash",
                contents=[
                    types.Content(
                        role="user", 
                        parts=[types.Part(text=full_prompt)]
                    )
                ],
                config=types.GenerateContentConfig(
                    system_instruction=system_prompt,
                    temperature=0.3,
                    max_output_tokens=2000
                )
            )
            
            if response.text:
                return response.text
            else:
                return "❌ No response generated. Please try rephrasing your query."
                
        except Exception as e:
            return f"❌ Research failed: {str(e)}"
    
    async def close(self):
        await coingecko_client.close()
        await cryptocompare_client.close()