cicboy commited on
Commit
a0c219b
·
1 Parent(s): 9edc788

Update tools, add __init__, update app.py

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
app.py CHANGED
@@ -1,254 +1,248 @@
1
  # import libraries, APis and LLMs
2
  from crewai import Agent, Task, Crew
3
  import os
4
- from dotenv import load_dotenv
5
- from pathlib import Path
6
  from tools.market_data import MarketDataTool
7
  from tools.sentiment_tool import SentimentTool
8
  from tools.historical_data_tool import HistoricalDataTool
9
  from tools.analytics_tool import AnalyticsTool
10
- import json
11
  import gradio as gr
12
  import warnings
13
- import time
14
 
15
  warnings.filterwarnings("ignore")
16
 
17
- # Load environment variables (from local .env or Hugging Face secrets)
18
- load_dotenv()
19
-
20
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
21
  SERPER_API_KEY = os.getenv("SERPER_API_KEY")
22
 
23
- # Optional: set CHROMA_OPENAI_API_KEY for embedding compatibility
24
- os.environ["CHROMA_OPENAI_API_KEY"] = OPENAI_API_KEY
25
-
26
- os.environ["OPENAI_MODEL_NAME"] = "gpt-5-mini"
27
 
28
- #Tools
 
 
29
  market_data_tool = MarketDataTool()
30
  sentiment_tool = SentimentTool()
31
- historical_data_tool= HistoricalDataTool()
32
  analytics_tool = AnalyticsTool()
33
 
 
 
 
34
 
35
-
36
- # Define the agents
37
  market_agent = Agent(
38
  role="Crypto Market Analyst",
39
- goal="Fetches live market prices using the CoinGecko API and summarize trends.",
40
- backstory="A data-driven analyst who monitors cryptocurrency movements"
41
- "and provides concise and accurate market insights",
 
 
42
  verbose=False,
43
  allow_delegations=True,
44
- tools = [market_data_tool]
 
45
  )
46
 
47
  sentiment_agent = Agent(
48
  role="Crypto Sentiment Analyst",
49
- goal="Analyze public and media sentiment about cryptocurrencies to gauge market mood.",
50
- backstory= "You are an expert at analyzing news headlines and social media chatter to assess whether sentiment is bullish or bearish.",
51
- verbose = False,
52
  allow_delegations=True,
53
- tools=[sentiment_tool]
 
54
  )
55
 
56
  historical_agent = Agent(
57
  role="Crypto Historical Analyst",
58
- goal="Analyze past cryptocurrency trends using historical data.",
59
- backstory=(
60
- "An experienced quantitative analyst who studies long-term "
61
- "price behavior, volatility, and trend strength using CoinGecko data."
62
- ),
63
- verbose=True,
64
  allow_delegations=True,
65
  tools=[historical_data_tool],
 
66
  )
67
 
68
  analytics_agent = Agent(
69
  role="Cryptocurrency Analytics",
70
- goal="Integrate metrics from the market agent, historical agent, and sentiment agent"
71
- "and anlyze them to devise a coherent market view.",
72
- backstory=(
73
- "You are an expert quantitative strategist who synthesizes trend, sentiment and volatility data"
74
- "to produce both structured metrics and qualitative market insights."
75
  ),
76
- verbose=True,
 
77
  allow_delegations=False,
78
- tools=[analytics_tool]
 
79
  )
80
 
81
  strategy_agent = Agent(
82
  role="Crypto Strategy Analyst",
83
  goal=(
84
- "Transform analytical metrics and sentiment data into an actionable trading plan. "
85
- "Determine directional bias, entry/exit strategies, and risk management guidance "
86
- "based on risk, opportunity, and sentiment metrics."
87
  ),
88
- backstory=(
89
- "A senior crypto strategist with a background in quantitative finance and "
90
- "macro analysis. They integrate market structure, sentiment, and volatility "
91
- "to form adaptive yet risk-aware trading stances."
92
- ),
93
- verbose=True,
94
  )
95
 
96
  reporting_agent = Agent(
97
  role="Crypto Reporting Analyst",
98
  goal=(
99
- "Compile a concise, professional, and readable summary report "
100
- "from all agents' outputs. The report should combine numerical analytics, "
101
- "market context, sentiment, and trading strategy into one clear narrative."
102
  ),
103
- backstory=(
104
- "You are a senior market strategist and financial writer who creates daily reports "
105
- "for traders and institutional clients. You blend analytical data, sentiment, "
106
- "and technical insight into clear, actionable narratives."
107
- ),
108
- verbose=True,
109
  )
110
 
111
- # Define the tasks
 
 
112
 
113
  market_data_task = Task(
114
- description=("Retrieve the live market price for {cryptocurrency_selection} in {currency_selection} and summarize briefly"),
115
- expected_output=("Summary of the {cryptocurrency_selection} price in {currency_selection}"),
116
- agent=market_agent
 
 
 
 
117
  )
118
 
119
  sentiment_task = Task(
120
- description="Fetch sentiment for {cryptocurrency_selection} from Google News and Reddit, "
121
- "then using expert intuition classify it as bullish, bearish or neutral.",
122
- expected_output="Short summary with a sentiment classification.",
123
- async_execution=True,
124
- agent=sentiment_agent
 
125
  )
126
 
127
  historical_data_task = Task(
128
- description="Fetch and summarize {cryptocurrency_selection}'s {currency_selection} price trend over the past {days_selection} days.",
129
- expected_output="A short summary including start price, end price, percent change, volatility, and trend direction.",
 
 
 
 
 
 
130
  agent=historical_agent,
131
  )
132
 
133
  analytics_task = Task(
134
  description=(
135
- "Combine the results of the Market, Historical and Sentiment agents for {cryptocurrency_selection}."
136
- "First, use the 'analytics_tool' tool to compute structured metrics."
137
- "Then, interpret those metrics into an analytical narrative summarizing the overall market condition, risks and opportunites for that cryptocurrency"
138
- ),
139
- expected_output=(
140
- "A JSON object containing structured indicators from the tool"
141
- "and an LLM-generated interpretation explaining what they mean."
142
  ),
 
143
  agent=analytics_agent,
144
  inputs={
145
  "market_data": "{output_of_market_data_task}",
146
  "historical_data": "{output_of_historical_data_task}",
147
- "sentiment_data": "{output_of_sentiment_data_task}"
148
  }
149
  )
150
 
151
  strategy_task = Task(
152
  description=(
153
- "Based on the structured analytics output for {cryptocurrency_selection}, "
154
- "derive a clear trading stance. Evaluate the metrics such as sentiment_confidence, "
155
- "risk_score_out_of_10, opportunity_score_out_of_10, volatility, and RSI. "
156
- "Formulate an actionable trading bias (e.g., long, neutral, short) with reasoning. "
157
- "Provide position size guidance, entry/exit levels, and a brief risk management plan."
158
- ),
159
- expected_output=(
160
- "JSON object with fields: bias, position_size, entry_strategy, exit_strategy, "
161
- "risk_management, and reasoning."
162
  ),
 
163
  agent=strategy_agent,
164
  )
165
 
166
  reporting_task = Task(
167
  description=(
168
- "Generate a cohesive summary report for {cryptocurrency_selection}. "
169
- "Combine data and insights from the Market, Historical, Sentiment, Analytics, "
170
- "and Strategy agents. The report should read like a professional crypto analysis "
171
- "briefing with structured Markdown sections."
172
- ),
173
- expected_output=(
174
- "A Markdown-formatted report with sections:\n"
175
- "1️⃣ Market Overview\n"
176
- "2️⃣ Historical Performance\n"
177
- "3️⃣ Sentiment Analysis\n"
178
- "4️⃣ Analytical Summary\n"
179
- "5️⃣ Strategy Outlook\n"
180
- "6️⃣ Final Takeaways\n"
181
- "Each section should contain concise, data-backed explanations and actionable insights."
182
  ),
 
183
  agent=reporting_agent,
184
  )
185
 
186
- # Create a crew
 
 
 
187
  crypto_analysis_crew = Crew(
188
- agents=[market_agent, historical_agent, sentiment_agent, analytics_agent, strategy_agent, reporting_agent],
189
- tasks=[market_data_task, historical_data_task, sentiment_task, analytics_task, strategy_task, reporting_task],
190
- process = "sequential",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  verbose=True
192
- )
 
 
 
 
193
 
194
- # define the main gradio handler
195
  def generate_report(crypto_name, currency, days):
196
- crypto_trading_inputs={
197
- "cryptocurrency_selection": crypto_name.lower(),
198
- "currency_selection": currency.lower(),
199
- "days_selection": int(days),
200
  }
201
 
202
- result = crypto_analysis_crew.kickoff(inputs=crypto_trading_inputs)
203
-
204
- # Ensure Markdown rendering
205
  if isinstance(result, dict) and "final_output" in result:
206
  return result["final_output"]
 
207
  return str(result)
208
 
209
- def analyze_crypto(crypto, currency, days):
210
- yield "⏳ Starting analysis...", None
211
- time.sleep(1)
212
- yield "📊 Fetching live & historical market data...", None
213
- time.sleep(1)
214
- yield "🧠 Running multi-agent reasoning (market, sentiment, history)...", None
215
- report = generate_report(crypto, currency, days)
216
- yield "✅ Analysis Complete!", report
217
 
218
- #Gradio interface
219
  with gr.Blocks(theme=gr.themes.Monochrome()) as app:
220
  gr.Markdown("# 🪙 Crypto Intelligence Dashboard")
221
- gr.Markdown("Run a full multi-agent analysis with adjustable lookback period.")
222
 
223
  with gr.Row():
224
  crypto = gr.Textbox(
225
- label="Enter Cryptocurrency Name (e.g bitcoin, ethereum, cardano)",
226
- placeholder="Type any crytocurrency name...",
227
  value="bitcoin"
228
  )
229
  currency = gr.Dropdown(
230
  ["usd", "eur", "gbp"],
231
- label="Select Currency",
232
  value="usd"
233
  )
234
  days = gr.Slider(
235
- 30, 730, value=365, step=15,
236
- label="Days to analyze (Historical Range)"
237
  )
238
 
239
  run_button = gr.Button("🚀 Run Full Analysis", variant="primary")
240
-
241
- # Add a simple status box to show "Complete!" text
242
- status_box = gr.Markdown("💤 Idle")
243
-
244
  report_output = gr.Markdown(label="📊 Intelligence Report")
245
 
246
  run_button.click(
247
- analyze_crypto,
248
- inputs=[crypto, currency, days],
249
- outputs=[status_box, report_output],
250
- show_progress=True
251
  )
252
 
253
  if __name__ == "__main__":
254
- app.launch(server_name="0.0.0.0", server_port=7860, share=True)
 
1
  # import libraries, APis and LLMs
2
  from crewai import Agent, Task, Crew
3
  import os
 
 
4
  from tools.market_data import MarketDataTool
5
  from tools.sentiment_tool import SentimentTool
6
  from tools.historical_data_tool import HistoricalDataTool
7
  from tools.analytics_tool import AnalyticsTool
 
8
  import gradio as gr
9
  import warnings
 
10
 
11
  warnings.filterwarnings("ignore")
12
 
13
+ # ----------------------
14
+ # ENVIRONMENT VARIABLES
15
+ # ----------------------
16
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
17
  SERPER_API_KEY = os.getenv("SERPER_API_KEY")
18
 
19
+ OPENAI_MODEL_NAME = "gpt-5-mini"
20
+ os.environ["OPENAI_MODEL_NAME"] = OPENAI_MODEL_NAME
 
 
21
 
22
+ # ----------------------
23
+ # TOOLS (structured JSON)
24
+ # ----------------------
25
  market_data_tool = MarketDataTool()
26
  sentiment_tool = SentimentTool()
27
+ historical_data_tool = HistoricalDataTool()
28
  analytics_tool = AnalyticsTool()
29
 
30
+ # ----------------------
31
+ # AGENTS
32
+ # ----------------------
33
 
 
 
34
  market_agent = Agent(
35
  role="Crypto Market Analyst",
36
+ goal="Fetch live market price using CoinGecko and produce structured market JSON.",
37
+ backstory=(
38
+ "A disciplined market analyst who retrieves clean market data "
39
+ "including price and market liquidity."
40
+ ),
41
  verbose=False,
42
  allow_delegations=True,
43
+ tools=[market_data_tool],
44
+ llm=OPENAI_MODEL_NAME
45
  )
46
 
47
  sentiment_agent = Agent(
48
  role="Crypto Sentiment Analyst",
49
+ goal="Analyze public sentiment on news & Reddit using Serper + OpenAI. Output structured sentiment JSON.",
50
+ backstory="An expert in NLP-based crypto sentiment interpretation.",
51
+ verbose=False,
52
  allow_delegations=True,
53
+ tools=[sentiment_tool],
54
+ llm=OPENAI_MODEL_NAME
55
  )
56
 
57
  historical_agent = Agent(
58
  role="Crypto Historical Analyst",
59
+ goal="Analyze long-term price trends, volatility, and movement patterns using clean structured JSON.",
60
+ backstory="A quantitative analyst specializing in historical trends.",
61
+ verbose=False,
 
 
 
62
  allow_delegations=True,
63
  tools=[historical_data_tool],
64
+ llm=OPENAI_MODEL_NAME
65
  )
66
 
67
  analytics_agent = Agent(
68
  role="Cryptocurrency Analytics",
69
+ goal=(
70
+ "Combine structured outputs from market, historical, and sentiment tools "
71
+ "into final analytics including trend, volatility, alignment, and composite scoring."
 
 
72
  ),
73
+ backstory="A senior quant strategist synthesizing multiple signals into coherent analytics.",
74
+ verbose=False,
75
  allow_delegations=False,
76
+ tools=[analytics_tool],
77
+ llm=OPENAI_MODEL_NAME
78
  )
79
 
80
  strategy_agent = Agent(
81
  role="Crypto Strategy Analyst",
82
  goal=(
83
+ "Convert analytics into an actionable trading stance. Integrate sentiment, volatility, "
84
+ "risk, opportunity, and alignment to produce a structured trading strategy."
 
85
  ),
86
+ backstory="A crypto strategist experienced in forming actionable plans.",
87
+ verbose=False,
88
+ llm=OPENAI_MODEL_NAME
 
 
 
89
  )
90
 
91
  reporting_agent = Agent(
92
  role="Crypto Reporting Analyst",
93
  goal=(
94
+ "Generate a professionally formatted Markdown report synthesizing the full analysis."
 
 
95
  ),
96
+ backstory="A financial writer producing institutional-grade research.",
97
+ verbose=False,
98
+ llm=OPENAI_MODEL_NAME
 
 
 
99
  )
100
 
101
+ # ----------------------
102
+ # TASKS
103
+ # ----------------------
104
 
105
  market_data_task = Task(
106
+ description=(
107
+ "Use the market_data_tool to fetch the live market price for "
108
+ "{cryptocurrency_selection} in {currency_selection}. "
109
+ "Return structured JSON output only."
110
+ ),
111
+ expected_output="Structured JSON of current market metrics.",
112
+ agent=market_agent,
113
  )
114
 
115
  sentiment_task = Task(
116
+ description=(
117
+ "Fetch sentiment for {cryptocurrency_selection} using sentiment_tool. "
118
+ "Return structured JSON with sentiment, reasoning, and the raw headlines used."
119
+ ),
120
+ expected_output="Structured sentiment JSON (sentiment, reasoning, headlines).",
121
+ agent=sentiment_agent,
122
  )
123
 
124
  historical_data_task = Task(
125
+ description=(
126
+ "Use the historical_data_tool to retrieve structured historical price data "
127
+ "for {cryptocurrency_selection} over {days_selection} days."
128
+ ),
129
+ expected_output=(
130
+ "Structured historical JSON with start_price, end_price, pct_change, "
131
+ "volatility_pct, and trend."
132
+ ),
133
  agent=historical_agent,
134
  )
135
 
136
  analytics_task = Task(
137
  description=(
138
+ "Use analytics_tool to combine market_data, historical_data, and sentiment_data "
139
+ "into final structured analytics including trend, volatility, sentiment, alignment, "
140
+ "and composite score."
 
 
 
 
141
  ),
142
+ expected_output="Structured analytics JSON.",
143
  agent=analytics_agent,
144
  inputs={
145
  "market_data": "{output_of_market_data_task}",
146
  "historical_data": "{output_of_historical_data_task}",
147
+ "sentiment_data": "{output_of_sentiment_data_task}",
148
  }
149
  )
150
 
151
  strategy_task = Task(
152
  description=(
153
+ "Based on the structured analytics, produce an actionable trading stance "
154
+ "including: bias, strategy, risk guidance, and rationale."
 
 
 
 
 
 
 
155
  ),
156
+ expected_output="Structured strategy JSON.",
157
  agent=strategy_agent,
158
  )
159
 
160
  reporting_task = Task(
161
  description=(
162
+ "Create a well-formatted Markdown report combining all structured outputs: "
163
+ "Market, Historical, Sentiment, Analytics, and Strategy."
 
 
 
 
 
 
 
 
 
 
 
 
164
  ),
165
+ expected_output="Markdown-formatted final report.",
166
  agent=reporting_agent,
167
  )
168
 
169
+ # ----------------------
170
+ # CREW EXECUTION PIPELINE
171
+ # ----------------------
172
+
173
  crypto_analysis_crew = Crew(
174
+ agents=[
175
+ market_agent,
176
+ historical_agent,
177
+ sentiment_agent,
178
+ analytics_agent,
179
+ strategy_agent,
180
+ reporting_agent
181
+ ],
182
+ tasks=[
183
+ market_data_task,
184
+ historical_data_task,
185
+ sentiment_task,
186
+ analytics_task,
187
+ strategy_task,
188
+ reporting_task
189
+ ],
190
+ process="sequential",
191
  verbose=True
192
+ )
193
+
194
+ # ----------------------
195
+ # GRADIO UI HANDLER
196
+ # ----------------------
197
 
 
198
  def generate_report(crypto_name, currency, days):
199
+ crypto_inputs = {
200
+ "cryptocurrency_selection": crypto_name.lower(),
201
+ "currency_selection": currency.lower(),
202
+ "days_selection": int(days),
203
  }
204
 
205
+ result = crypto_analysis_crew.kickoff(inputs=crypto_inputs)
206
+
207
+ # Crew returns a dict with final_output
208
  if isinstance(result, dict) and "final_output" in result:
209
  return result["final_output"]
210
+
211
  return str(result)
212
 
213
+ # ----------------------
214
+ # GRADIO APP
215
+ # ----------------------
 
 
 
 
 
216
 
 
217
  with gr.Blocks(theme=gr.themes.Monochrome()) as app:
218
  gr.Markdown("# 🪙 Crypto Intelligence Dashboard")
219
+ gr.Markdown("Run a full multi-agent crypto analysis using structured JSON tools.")
220
 
221
  with gr.Row():
222
  crypto = gr.Textbox(
223
+ label="Cryptocurrency (e.g. bitcoin, ethereum)",
224
+ placeholder="Type cryptocurrency name...",
225
  value="bitcoin"
226
  )
227
  currency = gr.Dropdown(
228
  ["usd", "eur", "gbp"],
229
+ label="Currency",
230
  value="usd"
231
  )
232
  days = gr.Slider(
233
+ minimum=30, maximum=730, value=365, step=15,
234
+ label="Historical Lookback (days)"
235
  )
236
 
237
  run_button = gr.Button("🚀 Run Full Analysis", variant="primary")
 
 
 
 
238
  report_output = gr.Markdown(label="📊 Intelligence Report")
239
 
240
  run_button.click(
241
+ generate_report,
242
+ inputs=[crypto, currency, days],
243
+ outputs=report_output,
244
+ show_progress=True
245
  )
246
 
247
  if __name__ == "__main__":
248
+ app.launch(server_name="0.0.0.0", server_port=7860)
tools/__init__.py ADDED
File without changes
tools/analytics_tool.py CHANGED
@@ -1,54 +1,94 @@
1
- from crewai_tools import RagTool
2
- import json
3
-
4
- class AnalyticsTool(RagTool):
5
- name: str = "analytics_tool"
6
- description: str = (
7
- "Processes and aggregates outputs from market, historical and sentiment agents"
8
- "to generate structured indicators and performance metrics, giving a holistic view of the cryptocurrency's condition."
9
- )
10
 
11
- def _run(self, market_data: dict, historical_data: dict, sentiment_data: dict):
12
- # combine results from the other agents into structured numeric metrics
13
 
 
 
 
 
 
 
 
 
 
 
 
14
  try:
15
- #Normalize strings -> dicts
16
- if isinstance(market_data, str):
17
- market_data = json.loads(market_data)
18
- if isinstance(historical_data, str):
19
- historical_data = json.loads(historical_data)
20
- if isinstance(sentiment_data, str):
21
- sentiment_data = json.loads(sentiment_data)
22
-
23
- #Extract info safely
24
- current_price = market_data.get("price") or market_data.get("latest_price")
25
- pct_change = historical_data.get("pct_change", 0)
26
- volatility = historical_data.get("volatility_pct", 0)
27
- trend = historical_data.get("trend", "unknown")
28
- sentiment = sentiment_data.get("final_sentiment_classification", "neutral")
29
-
30
- #Compute basic consistency logic
 
 
 
 
 
 
 
 
 
31
  aligned = (
32
- (trend == "upward" and "bullish" in sentiment.lower()) or
33
- (trend == "downward" and "bearish" in sentiment.lower())
34
  )
35
 
36
- score = (
37
- (pct_change/10) + (0.2 if aligned else -0.2)
38
- + (0.1 if "bullish" in sentiment.lower() else -0.1 if "bearish" in sentiment.lower() else 0)
39
- )
 
 
 
 
40
 
 
 
 
 
 
 
 
 
 
 
41
  score = round(max(-1, min(1, score)), 2)
42
 
 
 
 
 
43
  return {
44
- "price": current_price,
45
  "pct_change": pct_change,
46
- "volatility": volatility,
47
  "trend": trend,
48
  "sentiment": sentiment,
49
  "alignment": "aligned" if aligned else "divergent",
50
  "composite_score": score,
51
- "summary": f"Trend={trend}, Sentiment={sentiment}, Alignment={'aligned' if aligned else 'divergent'}, Score = {score}"
 
 
 
52
  }
 
53
  except Exception as e:
54
  return {"error": f"AnalyticsTool failed: {str(e)}"}
 
1
+ from crewai.tools import BaseTool
2
+ from pydantic import BaseModel, Field
3
+
4
+
5
+ # ---------- Input Schema ----------
6
+ class AnalyticsInput(BaseModel):
7
+ market_data: dict = Field(..., description="Structured JSON from MarketDataTool")
8
+ historical_data: dict = Field(..., description="Structured JSON from HistoricalDataTool")
9
+ sentiment_data: dict = Field(..., description="Structured JSON from SentimentTool")
10
 
 
 
11
 
12
+ # ---------- Tool ----------
13
+ class AnalyticsTool(BaseTool):
14
+ name = "analytics_tool"
15
+ description = (
16
+ "Aggregates structured market, historical, and sentiment data to produce "
17
+ "quantitative indicators including pct_change, volatility, trend, sentiment, "
18
+ "alignment consistency, and a composite confidence score."
19
+ )
20
+ args_schema = AnalyticsInput
21
+
22
+ def _run(self, market_data: dict, historical_data: dict, sentiment_data: dict) -> dict:
23
  try:
24
+ # ============================================================
25
+ # 1) Extract fields safely from structured tool outputs
26
+ # ============================================================
27
+
28
+ price = market_data.get("latest_price")
29
+ pct_change = historical_data.get("pct_change")
30
+ volatility = historical_data.get("volatility_pct")
31
+ trend = historical_data.get("trend")
32
+ sentiment = sentiment_data.get("sentiment")
33
+
34
+ # Validate required fields
35
+ if price is None or pct_change is None or trend is None or sentiment is None:
36
+ return {
37
+ "error": (
38
+ "Missing required fields in analytics input. "
39
+ "Ensure all tools returned structured JSON."
40
+ )
41
+ }
42
+
43
+ sentiment = sentiment.lower()
44
+
45
+ # ============================================================
46
+ # 2) Alignment logic
47
+ # ============================================================
48
+
49
  aligned = (
50
+ (trend == "upward" and sentiment == "bullish") or
51
+ (trend == "downward" and sentiment == "bearish")
52
  )
53
 
54
+ # ============================================================
55
+ # 3) Composite score (bounded to [-1, 1])
56
+ # ============================================================
57
+
58
+ score = 0
59
+
60
+ # pct_change contribution
61
+ score += pct_change / 10 # scaled
62
 
63
+ # alignment contribution
64
+ score += 0.2 if aligned else -0.2
65
+
66
+ # sentiment contribution
67
+ if sentiment == "bullish":
68
+ score += 0.1
69
+ elif sentiment == "bearish":
70
+ score -= 0.1
71
+
72
+ # Bound between -1 and 1
73
  score = round(max(-1, min(1, score)), 2)
74
 
75
+ # ============================================================
76
+ # 4) Final structured output
77
+ # ============================================================
78
+
79
  return {
80
+ "price": price,
81
  "pct_change": pct_change,
82
+ "volatility_pct": volatility,
83
  "trend": trend,
84
  "sentiment": sentiment,
85
  "alignment": "aligned" if aligned else "divergent",
86
  "composite_score": score,
87
+ "summary": (
88
+ f"Trend={trend}, Sentiment={sentiment}, Alignment="
89
+ f"{'aligned' if aligned else 'divergent'}, Score={score}"
90
+ ),
91
  }
92
+
93
  except Exception as e:
94
  return {"error": f"AnalyticsTool failed: {str(e)}"}
tools/historical_data_tool.py CHANGED
@@ -1,21 +1,27 @@
1
- # tools/historical_data_tool.py
2
- from crewai_tools import RagTool
3
  import requests
4
- from datetime import datetime
5
  import statistics
 
 
 
 
 
 
 
 
 
 
 
6
 
7
- class HistoricalDataTool(RagTool):
8
- name: str = "get_historical_data"
9
- description: str = (
10
- "Fetches and analyzes historical cryptocurrency market data "
11
- "from the CoinGecko API for trend insights."
 
12
  )
 
13
 
14
  def _run(self, symbol: str = "bitcoin", currency: str = "usd", days: int = 30) -> dict:
15
- """
16
- Fetch historical data for the specified cryptocurrency over a given number of days.
17
- Computes basic trend and volatility statistics.
18
- """
19
  url = f"https://api.coingecko.com/api/v3/coins/{symbol}/market_chart"
20
  params = {"vs_currency": currency, "days": days}
21
 
@@ -26,35 +32,57 @@ class HistoricalDataTool(RagTool):
26
 
27
  prices = data.get("prices", [])
28
  if not prices:
29
- return {"error": f"No data found for {symbol}."}
30
 
31
- # Extract date and price
32
  history = [
33
- {"date": datetime.utcfromtimestamp(p[0] / 1000).strftime("%Y-%m-%d"), "price": p[1]}
 
 
 
34
  for p in prices
35
  ]
36
 
37
- # Calculate simple stats
38
- first, last = history[0]["price"], history[-1]["price"]
39
- pct_change = ((last - first) / first) * 100
 
 
 
 
 
 
 
40
  daily_returns = [
41
  (history[i + 1]["price"] - history[i]["price"]) / history[i]["price"]
42
  for i in range(len(history) - 1)
43
  ]
44
- volatility = statistics.stdev(daily_returns) * 100 if len(daily_returns) > 1 else 0
45
 
46
- trend = "upward" if pct_change > 0 else "downward" if pct_change < 0 else "sideways"
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  return {
49
  "symbol": symbol,
50
  "currency": currency,
51
  "days": days,
52
- "start_price": round(first, 2),
53
- "end_price": round(last, 2),
54
  "pct_change": round(pct_change, 2),
55
  "volatility_pct": round(volatility, 2),
56
  "trend": trend,
 
57
  }
58
 
59
  except Exception as e:
60
- return {"error": str(e)}
 
 
 
1
  import requests
 
2
  import statistics
3
+ from datetime import datetime
4
+ from crewai.tools import BaseTool
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ # ---------- Input Schema ----------
9
+ class HistoricalInput(BaseModel):
10
+ symbol: str = Field(..., description="Cryptocurrency ID used by CoinGecko, e.g. 'bitcoin'")
11
+ currency: str = Field(default="usd", description="Fiat currency, e.g., 'usd'")
12
+ days: int = Field(..., description="Number of past days to retrieve (e.g., 30, 90, 365)")
13
+
14
 
15
+ # ---------- Tool ----------
16
+ class HistoricalDataTool(BaseTool):
17
+ name = "get_historical_data"
18
+ description = (
19
+ "Fetches historical cryptocurrency market data from CoinGecko and computes "
20
+ "trend, percent change, and volatility. Returns structured JSON."
21
  )
22
+ args_schema = HistoricalInput
23
 
24
  def _run(self, symbol: str = "bitcoin", currency: str = "usd", days: int = 30) -> dict:
 
 
 
 
25
  url = f"https://api.coingecko.com/api/v3/coins/{symbol}/market_chart"
26
  params = {"vs_currency": currency, "days": days}
27
 
 
32
 
33
  prices = data.get("prices", [])
34
  if not prices:
35
+ return {"error": f"No historical data found for '{symbol}'."}
36
 
37
+ # -------- Extract date + price history --------
38
  history = [
39
+ {
40
+ "date": datetime.utcfromtimestamp(p[0] / 1000).strftime("%Y-%m-%d"),
41
+ "price": float(p[1])
42
+ }
43
  for p in prices
44
  ]
45
 
46
+ if len(history) < 2:
47
+ return {"error": "Insufficient historical data for volatility or trend analysis."}
48
+
49
+ start_price = history[0]["price"]
50
+ end_price = history[-1]["price"]
51
+
52
+ # -------- Percent Change --------
53
+ pct_change = ((end_price - start_price) / start_price) * 100
54
+
55
+ # -------- Daily Returns + Volatility --------
56
  daily_returns = [
57
  (history[i + 1]["price"] - history[i]["price"]) / history[i]["price"]
58
  for i in range(len(history) - 1)
59
  ]
 
60
 
61
+ volatility = (
62
+ statistics.stdev(daily_returns) * 100
63
+ if len(daily_returns) > 1
64
+ else 0
65
+ )
66
+
67
+ # -------- Trend Classification --------
68
+ if pct_change > 1.5:
69
+ trend = "upward"
70
+ elif pct_change < -1.5:
71
+ trend = "downward"
72
+ else:
73
+ trend = "sideways"
74
 
75
  return {
76
  "symbol": symbol,
77
  "currency": currency,
78
  "days": days,
79
+ "start_price": round(start_price, 2),
80
+ "end_price": round(end_price, 2),
81
  "pct_change": round(pct_change, 2),
82
  "volatility_pct": round(volatility, 2),
83
  "trend": trend,
84
+ "price_history": history, # optional but useful for advanced analytics
85
  }
86
 
87
  except Exception as e:
88
+ return {"error": f"HistoricalDataTool failed: {str(e)}"}
tools/market_data.py CHANGED
@@ -1,31 +1,63 @@
1
- import os
2
- from crewai_tools import RagTool
3
  import requests
 
 
4
 
5
- class MarketDataTool(RagTool):
6
- name: str ="get_market_data"
7
- description: str ="Fetches the current market price of a cryptocurrency in a chosen fiat currency using the CoinGecko API."
8
 
9
- def _run(self, symbol="bitcoin", currency="usd"):
10
- url = f"https://api.coingecko.com/api/v3/simple/price?ids={symbol}&vs_currencies={currency}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  try:
12
  response = requests.get(url, timeout=10)
13
  response.raise_for_status()
14
  data = response.json()
15
- price = data.get(symbol, {}).get(currency)
 
 
 
 
16
  if price is None:
17
  return {
18
  "symbol": symbol,
19
  "currency": currency,
20
- "error": f"Could not find data for {symbol.upper()} in {currency.upper()}."
21
  }
 
22
  return {
23
  "symbol": symbol,
24
  "currency": currency,
25
  "latest_price": price,
26
- "formatted": f"{symbol.capitalize()} price: {price} {currency.upper()}"
27
  }
28
- except Exception as e:
29
- return {"error": str(e)}
30
-
31
 
 
 
 
 
 
1
  import requests
2
+ from crewai.tools import BaseTool
3
+ from pydantic import BaseModel, Field
4
 
 
 
 
5
 
6
+ # ---------- Input Schema ----------
7
+ class MarketDataInput(BaseModel):
8
+ symbol: str = Field(..., description="Cryptocurrency ID in CoinGecko format, e.g., 'bitcoin'")
9
+ currency: str = Field(default="usd", description="Fiat currency, e.g., 'usd', 'eur', 'gbp'")
10
+
11
+
12
+ # ---------- Tool ----------
13
+ class MarketDataTool(BaseTool):
14
+ name = "get_market_data"
15
+ description = (
16
+ "Fetches structured market data for a cryptocurrency including price and 24h volume "
17
+ "using the CoinGecko API."
18
+ )
19
+ args_schema = MarketDataInput
20
+
21
+ def _run(self, symbol: str = "bitcoin", currency: str = "usd") -> dict:
22
+ """
23
+ Returns structured JSON:
24
+ {
25
+ "symbol": "bitcoin",
26
+ "currency": "usd",
27
+ "latest_price": 90482,
28
+ "volume_24h": 38500000000
29
+ }
30
+ """
31
+
32
+ url = (
33
+ f"https://api.coingecko.com/api/v3/simple/price"
34
+ f"?ids={symbol}"
35
+ f"&vs_currencies={currency}"
36
+ f"&include_24hr_vol=true"
37
+ )
38
+
39
  try:
40
  response = requests.get(url, timeout=10)
41
  response.raise_for_status()
42
  data = response.json()
43
+
44
+ asset = data.get(symbol, {})
45
+ price = asset.get(currency)
46
+ volume = asset.get(f"{currency}_24h_vol")
47
+
48
  if price is None:
49
  return {
50
  "symbol": symbol,
51
  "currency": currency,
52
+ "error": f"MarketDataTool: No price found for {symbol.upper()} in {currency.upper()}."
53
  }
54
+
55
  return {
56
  "symbol": symbol,
57
  "currency": currency,
58
  "latest_price": price,
59
+ "volume_24h": volume,
60
  }
 
 
 
61
 
62
+ except Exception as e:
63
+ return {"error": f"MarketDataTool failed: {str(e)}"}
tools/sentiment_tool.py CHANGED
@@ -1,21 +1,14 @@
1
  import os
2
  import requests
3
- from crewai_tools import RagTool
4
  from openai import OpenAI
5
- from pathlib import Path
6
- from dotenv import load_dotenv
7
 
8
- env_path = Path("/Users/clydecossey/Documents/Python/Crypto_Analysis_Agent/Open_AI.env")
9
- load_dotenv(dotenv_path=env_path)
10
  OPENAI_API_KEY = os.getenv(key = "OPENAI_API_KEY")
11
 
12
  client = OpenAI(api_key=OPENAI_API_KEY)
13
 
14
- serper_env_path = Path("Users/clydecossey/Documents/Python/Crypto_Analysis_Agent/Serper.env")
15
- load_dotenv(dotenv_path=serper_env_path)
16
- SERPER_API_KEY = os.getenv(key="SERPER_API_KEY")
17
-
18
- class SentimentTool(RagTool):
19
  name: str = "get_crypto_sentiment"
20
  description: str = (
21
  "Fetches recent cryptocurrency news and Reddit discussions,"
@@ -25,19 +18,29 @@ class SentimentTool(RagTool):
25
  def _run(self, query: str = "bitcoin") -> str:
26
  try:
27
  #Google news sentiment
28
- news_url = "https://google.serper.dev/news"
29
- headers = {"X-API-KEY": SERPER_API_KEY, "Content-Type": "application/json"}
30
- payload = {"q": f"{query} crypto", "num": 5}
31
- news_response = requests.post(news_url, headers=headers, json=payload, timout=10)
32
- news = news_response.json().get("news", [])
33
- headlines = [n["title"] for n in news[:5]]
34
-
 
 
 
 
 
35
  #Reddit sentiment
36
- reddit_url = f"https://www.reddit.com/search.json?q={query}&sort=new&limit=5"
37
- reddit_headers = {"User-Agent": "CryptoAgent/1.0"}
38
- reddit_response = requests.get(reddit_url,headers=reddit_headers, timeout=10)
39
- reddit_posts = reddit_response.json().get("data", {}).get("children", [])
40
- reddit_titles = [p["data"]["title"] for p in reddit_posts[:5]]
 
 
 
 
 
41
 
42
  combined_text = "News: " + " | ".join(headlines) + "\nReddit: " + " | ".join(reddit_titles)
43
 
@@ -61,5 +64,4 @@ You are a crypto sentiment analyst. Based on the following headlines and Reddit
61
  return sentiment_result
62
 
63
  except Exception as e:
64
- return f"Error fetching sentiment data: {str(e)}"
65
-
 
1
  import os
2
  import requests
3
+ from crewai.tools import BaseTool
4
  from openai import OpenAI
 
 
5
 
6
+ SERPER_API_KEY = os.getenv(key="SERPER_API_KEY")
 
7
  OPENAI_API_KEY = os.getenv(key = "OPENAI_API_KEY")
8
 
9
  client = OpenAI(api_key=OPENAI_API_KEY)
10
 
11
+ class SentimentTool(BaseTool):
 
 
 
 
12
  name: str = "get_crypto_sentiment"
13
  description: str = (
14
  "Fetches recent cryptocurrency news and Reddit discussions,"
 
18
  def _run(self, query: str = "bitcoin") -> str:
19
  try:
20
  #Google news sentiment
21
+ headlines = []
22
+ try:
23
+ news_url = "https://google.serper.dev/news"
24
+ headers = {"X-API-KEY": SERPER_API_KEY, "Content-Type": "application/json"}
25
+ payload = {"q": f"{query} crypto", "num": 5}
26
+ news_response = requests.post(news_url, headers=headers, json=payload, timeout=10)
27
+ news_response.raise_for_status()
28
+ news = news_response.json().get("news", [])
29
+ headlines = [n["title"] for n in news[:5]]
30
+ except:
31
+ pass # silent fail
32
+
33
  #Reddit sentiment
34
+ reddit_titles = []
35
+ try:
36
+ reddit_url = f"https://www.reddit.com/search.json?q={query}&sort=new&limit=5"
37
+ reddit_headers = {"User-Agent": "CryptoAgent/1.0"}
38
+ reddit_response = requests.get(reddit_url,headers=reddit_headers, timeout=10)
39
+ reddit_response.raise_for_status()
40
+ reddit_posts = reddit_response.json().get("data", {}).get("children", [])
41
+ reddit_titles = [p["data"]["title"] for p in reddit_posts[:5]]
42
+ except:
43
+ pass #silent fail
44
 
45
  combined_text = "News: " + " | ".join(headlines) + "\nReddit: " + " | ".join(reddit_titles)
46
 
 
64
  return sentiment_result
65
 
66
  except Exception as e:
67
+ return f"Error fetching sentiment data: {str(e)}"