Spaces:
Sleeping
Sleeping
Update tools, add __init__, update app.py
Browse files- .DS_Store +0 -0
- .gradio/certificate.pem +31 -0
- app.py +127 -133
- tools/__init__.py +0 -0
- tools/analytics_tool.py +76 -36
- tools/historical_data_tool.py +51 -23
- tools/market_data.py +45 -13
- tools/sentiment_tool.py +26 -24
.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 |
-
#
|
| 18 |
-
|
| 19 |
-
|
| 20 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
| 21 |
SERPER_API_KEY = os.getenv("SERPER_API_KEY")
|
| 22 |
|
| 23 |
-
|
| 24 |
-
os.environ["
|
| 25 |
-
|
| 26 |
-
os.environ["OPENAI_MODEL_NAME"] = "gpt-5-mini"
|
| 27 |
|
| 28 |
-
#
|
|
|
|
|
|
|
| 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="
|
| 40 |
-
backstory=
|
| 41 |
-
"
|
|
|
|
|
|
|
| 42 |
verbose=False,
|
| 43 |
allow_delegations=True,
|
| 44 |
-
tools
|
|
|
|
| 45 |
)
|
| 46 |
|
| 47 |
sentiment_agent = Agent(
|
| 48 |
role="Crypto Sentiment Analyst",
|
| 49 |
-
goal="Analyze public
|
| 50 |
-
backstory=
|
| 51 |
-
verbose
|
| 52 |
allow_delegations=True,
|
| 53 |
-
tools=[sentiment_tool]
|
|
|
|
| 54 |
)
|
| 55 |
|
| 56 |
historical_agent = Agent(
|
| 57 |
role="Crypto Historical Analyst",
|
| 58 |
-
goal="Analyze
|
| 59 |
-
backstory=
|
| 60 |
-
|
| 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=
|
| 71 |
-
"
|
| 72 |
-
|
| 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 |
-
|
|
|
|
| 77 |
allow_delegations=False,
|
| 78 |
-
tools=[analytics_tool]
|
|
|
|
| 79 |
)
|
| 80 |
|
| 81 |
strategy_agent = Agent(
|
| 82 |
role="Crypto Strategy Analyst",
|
| 83 |
goal=(
|
| 84 |
-
"
|
| 85 |
-
"
|
| 86 |
-
"based on risk, opportunity, and sentiment metrics."
|
| 87 |
),
|
| 88 |
-
backstory=
|
| 89 |
-
|
| 90 |
-
|
| 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 |
-
"
|
| 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 |
-
|
| 105 |
-
|
| 106 |
-
"and technical insight into clear, actionable narratives."
|
| 107 |
-
),
|
| 108 |
-
verbose=True,
|
| 109 |
)
|
| 110 |
|
| 111 |
-
#
|
|
|
|
|
|
|
| 112 |
|
| 113 |
market_data_task = Task(
|
| 114 |
-
description=(
|
| 115 |
-
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
)
|
| 118 |
|
| 119 |
sentiment_task = Task(
|
| 120 |
-
description=
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
|
|
|
| 125 |
)
|
| 126 |
|
| 127 |
historical_data_task = Task(
|
| 128 |
-
description=
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
agent=historical_agent,
|
| 131 |
)
|
| 132 |
|
| 133 |
analytics_task = Task(
|
| 134 |
description=(
|
| 135 |
-
"
|
| 136 |
-
"
|
| 137 |
-
"
|
| 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
|
| 154 |
-
"
|
| 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 |
-
"
|
| 169 |
-
"
|
| 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 |
-
#
|
|
|
|
|
|
|
|
|
|
| 187 |
crypto_analysis_crew = Crew(
|
| 188 |
-
agents=[
|
| 189 |
-
|
| 190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
verbose=True
|
| 192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
|
| 194 |
-
# define the main gradio handler
|
| 195 |
def generate_report(crypto_name, currency, days):
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
}
|
| 201 |
|
| 202 |
-
result = crypto_analysis_crew.kickoff(inputs=
|
| 203 |
-
|
| 204 |
-
#
|
| 205 |
if isinstance(result, dict) and "final_output" in result:
|
| 206 |
return result["final_output"]
|
|
|
|
| 207 |
return str(result)
|
| 208 |
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 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
|
| 222 |
|
| 223 |
with gr.Row():
|
| 224 |
crypto = gr.Textbox(
|
| 225 |
-
label="
|
| 226 |
-
placeholder="Type
|
| 227 |
value="bitcoin"
|
| 228 |
)
|
| 229 |
currency = gr.Dropdown(
|
| 230 |
["usd", "eur", "gbp"],
|
| 231 |
-
label="
|
| 232 |
value="usd"
|
| 233 |
)
|
| 234 |
days = gr.Slider(
|
| 235 |
-
30, 730, value=365, step=15,
|
| 236 |
-
label="
|
| 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 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
)
|
| 252 |
|
| 253 |
if __name__ == "__main__":
|
| 254 |
-
app.launch(server_name="0.0.0.0", server_port=7860
|
|
|
|
| 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
|
| 2 |
-
import
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 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 |
-
#
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
aligned = (
|
| 32 |
-
(trend == "upward" and "bullish"
|
| 33 |
-
(trend == "downward" and "bearish"
|
| 34 |
)
|
| 35 |
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
score = round(max(-1, min(1, score)), 2)
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
return {
|
| 44 |
-
"price":
|
| 45 |
"pct_change": pct_change,
|
| 46 |
-
"
|
| 47 |
"trend": trend,
|
| 48 |
"sentiment": sentiment,
|
| 49 |
"alignment": "aligned" if aligned else "divergent",
|
| 50 |
"composite_score": score,
|
| 51 |
-
"summary":
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
"from
|
|
|
|
| 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
|
| 32 |
history = [
|
| 33 |
-
{
|
|
|
|
|
|
|
|
|
|
| 34 |
for p in prices
|
| 35 |
]
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
return {
|
| 49 |
"symbol": symbol,
|
| 50 |
"currency": currency,
|
| 51 |
"days": days,
|
| 52 |
-
"start_price": round(
|
| 53 |
-
"end_price": round(
|
| 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 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
try:
|
| 12 |
response = requests.get(url, timeout=10)
|
| 13 |
response.raise_for_status()
|
| 14 |
data = response.json()
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
if price is None:
|
| 17 |
return {
|
| 18 |
"symbol": symbol,
|
| 19 |
"currency": currency,
|
| 20 |
-
"error": f"
|
| 21 |
}
|
|
|
|
| 22 |
return {
|
| 23 |
"symbol": symbol,
|
| 24 |
"currency": currency,
|
| 25 |
"latest_price": price,
|
| 26 |
-
"
|
| 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
|
| 4 |
from openai import OpenAI
|
| 5 |
-
from pathlib import Path
|
| 6 |
-
from dotenv import load_dotenv
|
| 7 |
|
| 8 |
-
|
| 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 |
-
|
| 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 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
#Reddit sentiment
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)}"
|
|
|