Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 50 additions & 47 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,47 +1,50 @@
# 🌙 Moon Dev's Compliance Agent Requirements 🌙
# Created with ❤️ by Moon Dev's AI Assistant

# Core dependencies
numpy>=1.24.0
opencv-python>=4.8.0
whisper>=1.1.10
openai>=1.51.0
anthropic>=0.5.0
groq>=0.4.0
# google-generativeai>=0.3.0 # Temporarily disabled due to protobuf conflict
# To re-enable Gemini, uncomment above and ensure compatible versions:
# google-generativeai==0.8.3
# protobuf==4.25.1
# proto-plus==1.25.0
termcolor>=2.3.0
python-dotenv>=1.0.0
pathlib>=1.0.1

# For transcription
openai-whisper>=20231117
ffmpeg-python>=0.2.0

# For image processing
pillow>=10.0.0

# For API access
requests>=2.31.0

# For data handling
pandas>=2.0.0

# For progress bars and arXiv paper downloader
# Updated by Moon Dev's AI Assistant on arXiv downloader project
tqdm>=4.66.0

backtesting>=0.3.3
ta-lib>=0.4.0
PyPDF2>=3.0.0
youtube-transcript-api>=0.6.2
pandas-ta>=0.3.14b0

# MetaTrader 5 Integration
MetaTrader5>=5.0.45

# Production Utilities
psutil>=5.9.0 # System monitoring
annotated-types==0.7.0
argcomplete==3.1.4
blinker==1.7.0
certifi==2025.10.5
charset-normalizer==3.4.4
click==8.3.0
click-default-group==1.2.4
colorama==0.4.6
conan==2.22.1
cryptography==41.0.7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Upgrade vulnerable cryptography pin

cryptography==41.0.7 is subject to multiple high-severity advisories (e.g., CVE-2023-50782, CVE-2024-26130). Please move to a patched release (>=42.0.0) before shipping to avoid known remote-attack and DoS vectors.(osv.dev)

🧰 Tools
🪛 OSV Scanner (2.2.4)

[HIGH] 10-10: cryptography 41.0.7: undefined

(PYSEC-2024-225)


[HIGH] 10-10: cryptography 41.0.7: Python Cryptography package vulnerable to Bleichenbacher timing oracle attack

(GHSA-3ww4-gg4f-jr7f)


[HIGH] 10-10: cryptography 41.0.7: cryptography NULL pointer dereference with pkcs12.serialize_key_and_certificates when called with a non-matching certificate and private key and an hmac_hash override

(GHSA-6vqw-3v5j-54x4)


[HIGH] 10-10: cryptography 41.0.7: Null pointer dereference in PKCS12 parsing

(GHSA-9v9h-cgj8-h64p)


[HIGH] 10-10: cryptography 41.0.7: pyca/cryptography has a vulnerable OpenSSL included in cryptography wheels

(GHSA-h4gh-qq45-vh27)

🤖 Prompt for AI Agents
In requirements.txt around line 10, the pinned cryptography==41.0.7 is
vulnerable; update the requirement to a patched release (e.g.,
cryptography>=42.0.0) to remediate the CVEs, then regenerate any lockfiles or
constraints (pip-compile/Pipfile.lock/poetry.lock) and run tests/CI to ensure no
breakages from the upgrade.

dbus-python==1.3.2
distro==1.8.0
fasteners==0.20
httplib2==0.20.4
idna==3.11
Jinja2==3.1.6
launchpadlib==1.11.0
lazr.restfulclient==0.14.6
lazr.uri==1.0.6
markdown-it-py==4.0.0
MarkupSafe==3.0.3
mdurl==0.1.2
mem-layer @ git+https://github.com/icojerrel/mem-layer.git@d5573c9f7600b485c79051065bf66b8f76d6acb4
networkx==3.5
oauthlib==3.2.2
packaging==24.0
patch-ng==1.18.1
pluggy==1.6.0
pydantic==2.12.4
pydantic_core==2.41.5
Pygments==2.19.2
PyGObject==3.48.2
PyJWT==2.7.0
pyparsing==3.1.1
python-apt==2.7.7+ubuntu5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove distro-only python-apt==2.7.7+ubuntu5 requirement

This version identifier is Ubuntu-specific; there is no matching wheel on PyPI, so pip install -r requirements.txt will fail anywhere outside that distro (and even on Ubuntu when using virtualenvs). Drop this pin or gate it behind platform-specific installation instructions to keep installs working cross-platform.(pypi.org)

🤖 Prompt for AI Agents
In requirements.txt at line 35, the pinned entry python-apt==2.7.7+ubuntu5 is
Ubuntu-specific and will break pip installs on other platforms; remove this
distro-only pin from requirements.txt and either (a) omit python-apt entirely
from the global requirements so cross-platform installs succeed and document
platform-specific installation steps (install via the OS package manager or a
separate linux-only requirements file), or (b) move it into a platform-gated
installation path (separate requirements-linux.txt or documented
sys_platform-specific instructions) so only Ubuntu/Debian environments install
python-apt.

python-dateutil==2.9.0.post0
PyYAML==6.0.1
requests==2.32.5
rich==14.2.0
six==1.16.0
sqlite-fts4==1.0.3
sqlite-utils==3.38
tabulate==0.9.0
toml==0.10.2
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.5.0
wadllib==1.3.6
xmltodict==0.13.0
yq==3.1.0
63 changes: 60 additions & 3 deletions src/agents/risk_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@
from src.agents.base_agent import BaseAgent
import traceback

# Memory integration
try:
from src.memory import AgentMemory, MemoryScope
MEMORY_AVAILABLE = True
except ImportError:
MEMORY_AVAILABLE = False
print("⚠️ Memory module not available - running without persistent memory")

# Load environment variables
load_dotenv()

Expand Down Expand Up @@ -113,6 +121,15 @@ def __init__(self):
print(f"🏦 Initial Portfolio Balance: ${self.start_balance:.2f}")

self.current_value = self.start_balance

# Initialize memory
if MEMORY_AVAILABLE:
self.memory = AgentMemory(agent_name="risk_agent")
if self.memory.enabled:
cprint("🧠 Memory layer initialized", "cyan")
else:
self.memory = None

cprint("🛡️ Risk Agent initialized!", "white", "on_blue")

def get_portfolio_value(self):
Expand Down Expand Up @@ -453,6 +470,19 @@ def check_risk_limits(self):
def handle_limit_breach(self, breach_type, current_value):
"""Handle breached risk limits with AI consultation if enabled"""
try:
# Store breach event in memory
if self.memory and self.memory.enabled:
self.memory.broadcast(
f"RISK LIMIT BREACH: {breach_type} - Current value: {current_value}",
scope=MemoryScope.ALERTS,
priority="critical",
metadata={
"breach_type": breach_type,
"current_value": current_value,
"timestamp": datetime.now().isoformat()
}
)

# If AI confirmation is disabled, close positions immediately
if not USE_AI_CONFIRMATION:
print(f"\n🚨 {breach_type} limit breached! Closing all positions immediately...")
Expand Down Expand Up @@ -537,7 +567,21 @@ def handle_limit_breach(self, breach_type, current_value):

# Parse decision
decision = response_text.split('\n')[0].strip()


# Store AI decision in memory
if self.memory and self.memory.enabled:
self.memory.store(
f"AI Risk Decision: {decision} - {response_text[:200]}",
scope=MemoryScope.RISK,
priority="high",
metadata={
"breach_type": breach_type,
"decision": decision,
"reasoning": response_text,
"model": "DeepSeek" if self.deepseek_client else "Claude"
}
)

if decision == "CLOSE_ALL":
print("🚨 AI recommends closing all positions!")
self.close_all_positions()
Expand Down Expand Up @@ -571,11 +615,24 @@ def run(self):
# Get current PnL
current_pnl = self.get_current_pnl()
current_balance = self.get_portfolio_value()

print(f"\n💰 Current PnL: ${current_pnl:.2f}")
print(f"💼 Current Balance: ${current_balance:.2f}")
print(f"📉 Minimum Balance Limit: ${MINIMUM_BALANCE_USD:.2f}")


# Store portfolio status in memory
if self.memory and self.memory.enabled:
self.memory.store(
f"Portfolio status: PnL ${current_pnl:.2f}, Balance ${current_balance:.2f}",
scope=MemoryScope.RISK,
priority="medium",
metadata={
"pnl": current_pnl,
"balance": current_balance,
"start_balance": self.start_balance
}
)

# Check minimum balance limit
if current_balance < MINIMUM_BALANCE_USD:
print(f"⚠️ ALERT: Current balance ${current_balance:.2f} is below minimum ${MINIMUM_BALANCE_USD:.2f}")
Expand Down
84 changes: 75 additions & 9 deletions src/agents/sentiment_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,17 @@ def patched_client(*args, **kwargs):

httpx.Client = patched_client

# imports
# imports
from twikit import Client, TooManyRequests, BadRequest

# Memory integration
try:
from src.memory import AgentMemory, MemoryScope
MEMORY_AVAILABLE = True
except ImportError:
MEMORY_AVAILABLE = False
print("⚠️ Memory module not available - running without persistent memory")

class SentimentAgent:
def __init__(self):
"""Initialize the Sentiment Agent"""
Expand All @@ -102,15 +110,23 @@ def __init__(self):
self.model = None
self.audio_dir = Path("src/audio")
self.audio_dir.mkdir(parents=True, exist_ok=True)

# Initialize sentiment history file
if not os.path.exists(SENTIMENT_HISTORY_FILE):
pd.DataFrame(columns=['timestamp', 'sentiment_score', 'num_tweets']).to_csv(SENTIMENT_HISTORY_FILE, index=False)

# Load the sentiment model at initialization
cprint("🤖 Loading sentiment model...", "cyan")
self.init_sentiment_model()


# Initialize memory
if MEMORY_AVAILABLE:
self.memory = AgentMemory(agent_name="sentiment_agent")
if self.memory.enabled:
cprint("🧠 Memory layer initialized", "cyan")
else:
self.memory = None

cprint("🌙 Moon Dev's Sentiment Agent initialized!", "green")

def init_sentiment_model(self):
Expand Down Expand Up @@ -197,7 +213,7 @@ def save_sentiment_score(self, sentiment_score, num_tweets):
'sentiment_score': sentiment_score,
'num_tweets': num_tweets
}])

# Load existing data
if os.path.exists(SENTIMENT_HISTORY_FILE):
history_df = pd.read_csv(SENTIMENT_HISTORY_FILE)
Expand All @@ -212,9 +228,33 @@ def save_sentiment_score(self, sentiment_score, num_tweets):
history_df = pd.concat([history_df, new_data], ignore_index=True)
else:
history_df = new_data

history_df.to_csv(SENTIMENT_HISTORY_FILE, index=False)


# Store in memory layer
if self.memory and self.memory.enabled:
# Convert score to readable label
if sentiment_score > 0.3:
label = "very positive"
elif sentiment_score > 0:
label = "slightly positive"
elif sentiment_score > -0.3:
label = "slightly negative"
else:
label = "very negative"

self.memory.store(
f"Sentiment: {label} (score: {sentiment_score:.2f}) from {num_tweets} tweets",
scope=MemoryScope.SENTIMENT,
priority="medium",
metadata={
"sentiment_score": sentiment_score,
"num_tweets": num_tweets,
"score_percent": (sentiment_score + 1) * 50,
"label": label
}
)
Comment on lines +247 to +256
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Ensure metadata uses JSON-serializable primitives

np.float64 and other numpy scalars in the new metadata blocks will raise TypeError: Object of type float64 is not JSON serializable when AgentMemory.store() hands the payload to the mem-layer. That means every call to persist sentiment data or broadcasts will fail once memory is enabled. Cast the values to native Python types before storing so serialization succeeds.

-                self.memory.store(
+                self.memory.store(
                     f"Sentiment: {label} (score: {sentiment_score:.2f}) from {num_tweets} tweets",
                     scope=MemoryScope.SENTIMENT,
                     priority="medium",
                     metadata={
-                        "sentiment_score": sentiment_score,
-                        "num_tweets": num_tweets,
-                        "score_percent": (sentiment_score + 1) * 50,
+                        "sentiment_score": float(sentiment_score),
+                        "num_tweets": int(num_tweets),
+                        "score_percent": float((sentiment_score + 1) * 50),
                         "label": label
                     }
                 )
@@
-                self.memory.broadcast(
+                self.memory.broadcast(
                     f"EXTREME sentiment detected: {sentiment} ({score_percent:.1f}/100)",
                     scope=MemoryScope.ALERTS,
                     priority="high",
                     metadata={
-                        "sentiment_score": sentiment_score,
+                        "sentiment_score": float(sentiment_score),
                         "label": sentiment,
-                        "num_tweets": len(texts)
+                        "num_tweets": len(texts)
                     }
                 )
@@
-                self.memory.broadcast(
+                self.memory.broadcast(
                     f"Large sentiment shift {direction}: {abs(percent_change):.1f} points in {int(time_diff)} min",
                     scope=MemoryScope.ALERTS,
                     priority="high",
                     metadata={
-                        "percent_change": percent_change,
-                        "time_minutes": int(time_diff),
-                        "current_score": sentiment_score
+                        "percent_change": float(percent_change),
+                        "time_minutes": int(time_diff),
+                        "current_score": float(sentiment_score)
                     }
                 )

Also applies to: 349-372

🤖 Prompt for AI Agents
In src/agents/sentiment_agent.py around lines 247 to 256 (and similarly at
349-372), the metadata dictionary can contain numpy scalar types (e.g.,
np.float64) which are not JSON-serializable; convert all values to native Python
primitives before storing — e.g., cast sentiment_score to float(), num_tweets to
int(), compute score_percent as float((float(sentiment_score) + 1) * 50) and
ensure label is a str — then pass that cleaned metadata into
AgentMemory.store()/broadcast so serialization succeeds.


except Exception as e:
cprint(f"❌ Error saving sentiment history: {str(e)}", "red")

Expand Down Expand Up @@ -300,11 +340,37 @@ def analyze_and_announce_sentiment(self, tweets):
message += f" - a small {abs(percent_change):.1f}% change"

message += "."

# Announce with voice if sentiment is significant or if there's a big change
is_important = abs(sentiment_score) > SENTIMENT_ANNOUNCE_THRESHOLD or (percent_change is not None and abs(percent_change) > 5)
self._announce(message, is_important)


# Broadcast extreme sentiment or significant changes to all agents
if self.memory and self.memory.enabled:
if abs(sentiment_score) > 0.5: # Very extreme sentiment
self.memory.broadcast(
f"EXTREME sentiment detected: {sentiment} ({score_percent:.1f}/100)",
scope=MemoryScope.ALERTS,
priority="high",
metadata={
"sentiment_score": sentiment_score,
"label": sentiment,
"num_tweets": len(texts)
}
)
elif percent_change is not None and abs(percent_change) > 10: # Big sentiment shift
direction = "up" if percent_change > 0 else "down"
self.memory.broadcast(
f"Large sentiment shift {direction}: {abs(percent_change):.1f} points in {int(time_diff)} min",
scope=MemoryScope.ALERTS,
priority="high",
metadata={
"percent_change": percent_change,
"time_minutes": int(time_diff),
"current_score": sentiment_score
}
)

# If not announcing vocally, print the raw score for debugging
if not is_important:
cprint(f"📊 Raw sentiment score: {sentiment_score:.2f} (on scale of -1 to 1)", "cyan")
Expand Down
Loading