Compare commits

..

8 Commits

Author SHA1 Message Date
shokollm
8693946cb8 fix: add bcrypt version constraint for passlib compatibility
bcrypt 5.0.0 is incompatible with passlib 1.7.x - passlib tries to
access bcrypt.__about__.__version__ which was removed in bcrypt 5.x.

Constrain bcrypt to >=4.0,<5.0 to maintain compatibility.
2026-04-10 00:55:18 +00:00
a2f549c056 Merge pull request 'fix: correct import paths in ai_agent module' (#39) from fix/ai-agent-imports into main 2026-04-09 17:32:21 +02:00
shokollm
ad6e57655d fix: correct import paths in ai_agent module
- Fix relative import path in crew.py (from ..core to ...core)
- Update __init__.py exports to match actual class names
- Remove incorrect CrewAgent and LLMConnector exports
2026-04-09 15:27:09 +00:00
ac5e9d8b81 Merge pull request 'fix: add error logging to simulate engine to prevent silent failures' (#38) from fix/issue-30 into main 2026-04-09 12:19:36 +02:00
shokollm
81f3342365 fix: add error logging to simulate engine to prevent silent failures
Errors during price fetching are now logged and stored in an errors list,
allowing users to see error count/warnings in simulation results.

Acceptance Criteria:
- [x] Errors are logged (not silently swallowed)
- [x] User can see error count/warnings in simulation results
- [x] Simulation completes even if some price fetches fail (graceful degradation)
2026-04-09 10:16:22 +00:00
6adad0701d Merge pull request 'fix: consolidate AveCloudClient to single implementation' (#37) from fix/issue-29 into main 2026-04-09 12:11:59 +02:00
shokollm
405b35c3ba fix: consolidate AveCloudClient to single implementation in services/ave/client.py 2026-04-09 10:06:16 +00:00
dd25d38e7e Merge pull request 'feat: implement stop-loss and take-profit risk management' (#36) from fix/issue-28 into main 2026-04-09 11:39:50 +02:00
7 changed files with 32 additions and 77 deletions

View File

@@ -1,4 +1,4 @@
from .crew import CrewAgent
from .llm_connector import LLMConnector
from .crew import TradingCrew, get_trading_crew
from .llm_connector import MiniMaxLLM, MiniMaxConnector
__all__ = ["CrewAgent", "LLMConnector"]
__all__ = ["TradingCrew", "get_trading_crew", "MiniMaxLLM", "MiniMaxConnector"]

View File

@@ -1,7 +1,7 @@
from typing import List, Optional, Dict, Any
from crewai import Agent, Task, Crew
from .llm_connector import MiniMaxConnector, MiniMaxLLM
from ..core.config import get_settings
from ...core.config import get_settings
class StrategyValidator:

View File

@@ -90,6 +90,22 @@ class AveCloudClient:
return data.get("data", [])
raise Exception(f"Failed to fetch klines: {data}")
async def get_token_price(self, token_id: str) -> Optional[Dict[str, Any]]:
url = f"{self.DATA_API_URL}/v2/tokens/price"
async with httpx.AsyncClient() as client:
response = await client.post(
url,
headers=self._data_headers(),
json={"token_ids": [token_id]},
timeout=30.0,
)
response.raise_for_status()
data = response.json()
if data.get("status") == 200:
prices = data.get("data", {})
return prices.get(token_id)
return None
async def get_trending_tokens(
self, chain: Optional[str] = None, limit: int = 20
) -> List[Dict[str, Any]]:

View File

@@ -1,70 +0,0 @@
import httpx
from typing import List, Dict, Any, Optional
from datetime import datetime
class AveCloudClient:
BASE_URL = "https://prod.ave-api.com"
def __init__(self, api_key: str, plan: str = "free"):
self.api_key = api_key
self.plan = plan
def _headers(self) -> Dict[str, str]:
return {"X-API-KEY": self.api_key}
async def get_klines(
self,
token_id: str,
interval: str = "1h",
limit: int = 100,
start_time: Optional[int] = None,
end_time: Optional[int] = None,
) -> List[Dict[str, Any]]:
url = f"{self.BASE_URL}/v2/klines/token/{token_id}"
params = {"interval": interval, "limit": limit}
if start_time:
params["start_time"] = start_time
if end_time:
params["end_time"] = end_time
async with httpx.AsyncClient() as client:
response = await client.get(
url, headers=self._headers(), params=params, timeout=30.0
)
response.raise_for_status()
data = response.json()
if data.get("status") == 200:
return data.get("data", [])
raise Exception(f"Failed to fetch klines: {data}")
async def get_token_price(self, token_id: str) -> Optional[Dict[str, Any]]:
url = f"{self.BASE_URL}/v2/tokens/price"
async with httpx.AsyncClient() as client:
response = await client.post(
url,
headers=self._headers(),
json={"token_ids": [token_id]},
timeout=30.0,
)
response.raise_for_status()
data = response.json()
if data.get("status") == 200:
prices = data.get("data", {})
return prices.get(token_id)
return None
async def get_batch_prices(self, token_ids: List[str]) -> Dict[str, Dict[str, Any]]:
url = f"{self.BASE_URL}/v2/tokens/price"
async with httpx.AsyncClient() as client:
response = await client.post(
url,
headers=self._headers(),
json={"token_ids": token_ids},
timeout=30.0,
)
response.raise_for_status()
data = response.json()
if data.get("status") == 200:
return data.get("data", {})
return {}

View File

@@ -2,7 +2,7 @@ import uuid
import asyncio
from datetime import datetime
from typing import Dict, Any, List, Optional
from .ave_client import AveCloudClient
from ..ave.client import AveCloudClient
class BacktestEngine:

View File

@@ -1,8 +1,11 @@
import uuid
import asyncio
import logging
from datetime import datetime
from typing import Dict, Any, List, Optional
from ..backtest.ave_client import AveCloudClient
from ..ave.client import AveCloudClient
logger = logging.getLogger(__name__)
class SimulateEngine:
@@ -38,6 +41,7 @@ class SimulateEngine:
self.entry_time: Optional[int] = None
self.current_balance: float = config.get("initial_balance", 10000.0)
self.trades: List[Dict[str, Any]] = []
self.errors: List[str] = []
async def run(self) -> Dict[str, Any]:
self.running = True
@@ -74,7 +78,9 @@ class SimulateEngine:
self.last_volume = current_volume
except Exception as e:
pass
logger.warning(f"Failed to get price for {token_id}: {e}")
self.errors.append(f"Price fetch failed for {token_id}: {str(e)}")
continue
for _ in range(self.check_interval):
if not self.running:
@@ -92,6 +98,8 @@ class SimulateEngine:
self.results = self.results or {}
self.results["total_signals"] = len(self.signals)
self.results["total_errors"] = len(self.errors)
self.results["errors"] = self.errors
self.results["signals"] = self.signals
self.results["started_at"] = self.started_at
self.results["ended_at"] = datetime.utcnow()

View File

@@ -6,6 +6,7 @@ pydantic-settings>=2.1.0
email-validator>=2.0.0
python-jose[cryptography]>=3.3.0
passlib[bcrypt]>=1.7.4
bcrypt>=4.0,<5.0 # Required for passlib compatibility
crewai>=0.1.0
anthropic>=0.18.0
httpx>=0.26.0