-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Refactor: update ai hedge fund with latest apis and validated natural language to base model conversion #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
97c7696
feat: refactor AI Hedge Fund Agent structure and add remote agent sup…
vcfgv f0615a7
fix: enhance error handling in GenericAgentExecutor and improve logging
vcfgv 63a16cd
feat: implement remote agent configuration loading and connection han…
vcfgv 5f1539b
feat: add example script for using RemoteConnections with remote agents
vcfgv 400f030
feat: add agno as dependency
vcfgv ecb987f
feat: enhance remote agent demo with validated natural language to ba…
vcfgv 8e37c90
fix lint and format
vcfgv ec5a7e7
feat: remove test_client.py as it is no longer needed
vcfgv 63a7812
feat: disable default listener and push notifications in agent config…
vcfgv 09cb17e
feat: rename send_message parameter `exhaustive` as `streaming`
vcfgv 0c8bbd9
refactor: streamline error handling in AIHedgeFundAgent's stream method
vcfgv 222de7b
feat: move example scripts for core agent functionality and remote co…
vcfgv 43be7f2
feat: exclude examples from packaging
vcfgv 3ea2c39
feat: add comprehensive README for ValueCell Agent Core with examples…
vcfgv a1e78e5
feat: implement get_agent_card_path utility function and update remot…
vcfgv 34a9508
feat: add parse_host_port function to handle URL-like strings and upd…
vcfgv c827b99
refactor: update agent decorator usage to create_wrapped_agent and im…
vcfgv db90c16
doc: optimize BaseAgent stream docstring
vcfgv 1e139f6
feat: enable listener by default in start_agent method
vcfgv b062dc1
fix format
vcfgv 92b27ac
fix: update tickers description to use allowed_tickers set
vcfgv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "AIHedgeFundAgent", | ||
| "url": "http://localhost:10001/", | ||
| "enabled": true | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,67 +1,115 @@ | ||
| import asyncio | ||
| import json | ||
| import logging | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| import click | ||
| import httpx | ||
| import uvicorn | ||
| from a2a.server.apps import A2AStarletteApplication | ||
| from a2a.server.request_handlers import DefaultRequestHandler | ||
| from a2a.server.tasks import ( | ||
| BasePushNotificationSender, | ||
| InMemoryPushNotificationConfigStore, | ||
| InMemoryTaskStore, | ||
| from datetime import datetime | ||
| from typing import List | ||
|
|
||
| from agno.agent import Agent | ||
| from agno.models.openrouter import OpenRouter | ||
| from dateutil.relativedelta import relativedelta | ||
| from pydantic import BaseModel, Field, field_validator | ||
| from valuecell.core.agent.decorator import create_wrapped_agent | ||
| from valuecell.core.agent.types import BaseAgent | ||
|
|
||
| from src.main import run_hedge_fund | ||
| from src.utils.analysts import ANALYST_ORDER | ||
|
|
||
| allowed_analysts = set( | ||
| key for display_name, key in sorted(ANALYST_ORDER, key=lambda x: x[1]) | ||
| ) | ||
| from a2a.types import AgentCard | ||
| from adapter.agent_executor import VanillaAgentExecutor | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| @click.command() | ||
| @click.option('--host', 'host', default='localhost') | ||
| @click.option('--port', 'port', default=10001) | ||
| @click.option('--agent-card', 'agent_card_file') | ||
| def main(host, port, agent_card_file): | ||
| """Starts an Agent server.""" | ||
| try: | ||
| if not agent_card_file: | ||
| raise ValueError('Agent card is required') | ||
| with Path.open(agent_card_file) as file: | ||
| data = json.load(file) | ||
| agent_card = AgentCard(**data) | ||
|
|
||
| client = httpx.AsyncClient() | ||
| push_notification_config_store = InMemoryPushNotificationConfigStore() | ||
| push_notification_sender = BasePushNotificationSender( | ||
| client, config_store=push_notification_config_store | ||
| ) | ||
| allowed_tickers = {"AAPL", "GOOGL", "MSFT", "NVDA", "TSLA"} | ||
|
|
||
|
|
||
| class HedgeFundRequest(BaseModel): | ||
| tickers: List[str] = Field( | ||
| ..., | ||
| description=f"List of stock tickers to analyze. Must be from: {allowed_tickers}", | ||
| ) | ||
| selected_analysts: List[str] = Field( | ||
| default=[], | ||
| description=f"List of analysts to use for analysis. If empty, all analysts will be used. Must be from {allowed_analysts}", | ||
| ) | ||
|
|
||
| request_handler = DefaultRequestHandler( | ||
| agent_executor=VanillaAgentExecutor(), | ||
| task_store=InMemoryTaskStore(), | ||
| push_config_store=push_notification_config_store, | ||
| push_sender=push_notification_sender, | ||
| @field_validator("tickers") | ||
| @classmethod | ||
| def validate_tickers(cls, v): | ||
| invalid_tickers = set(v) - allowed_tickers | ||
| if invalid_tickers: | ||
| raise ValueError( | ||
| f"Invalid tickers: {invalid_tickers}. Allowed: {allowed_tickers}" | ||
| ) | ||
| return v | ||
|
|
||
| @field_validator("selected_analysts") | ||
| @classmethod | ||
| def validate_analysts(cls, v): | ||
| if v: # Only validate if not empty | ||
| invalid_analysts = set(v) - allowed_analysts | ||
| if invalid_analysts: | ||
| raise ValueError( | ||
| f"Invalid analysts: {invalid_analysts}. Allowed: {allowed_analysts}" | ||
| ) | ||
| return v | ||
|
|
||
|
|
||
| class AIHedgeFundAgent(BaseAgent): | ||
| def __init__(self): | ||
| super().__init__() | ||
| self.agno_agent = Agent( | ||
| model=OpenRouter(id="openai/gpt-4o-mini"), | ||
| response_model=HedgeFundRequest, | ||
| markdown=True, | ||
| ) | ||
|
|
||
| server = A2AStarletteApplication( | ||
| agent_card=agent_card, http_handler=request_handler | ||
| async def stream(self, query, session_id, task_id): | ||
| run_response = self.agno_agent.run( | ||
| f"Parse the following hedge fund analysis request and extract the parameters: {query}" | ||
| ) | ||
| hedge_fund_request = run_response.content | ||
|
|
||
| logger.info(f'Starting server on {host}:{port}') | ||
| end_date = datetime.now().strftime("%Y-%m-%d") | ||
| end_date_obj = datetime.strptime(end_date, "%Y-%m-%d") | ||
| start_date = (end_date_obj - relativedelta(months=3)).strftime("%Y-%m-%d") | ||
|
|
||
| initial_cash = 10000.00 | ||
| portfolio = { | ||
| "cash": initial_cash, | ||
| "margin_requirement": 0, | ||
| "margin_used": 0.0, | ||
| "positions": { | ||
| ticker: { | ||
| "long": 0, | ||
| "short": 0, | ||
| "long_cost_basis": 0.0, | ||
| "short_cost_basis": 0.0, | ||
| "short_margin_used": 0.0, | ||
| } | ||
| for ticker in hedge_fund_request.tickers | ||
| }, | ||
| "realized_gains": { | ||
| ticker: { | ||
| "long": 0.0, | ||
| "short": 0.0, | ||
| } | ||
| for ticker in hedge_fund_request.tickers | ||
| }, | ||
| } | ||
|
|
||
| result = run_hedge_fund( | ||
| tickers=hedge_fund_request.tickers, | ||
| start_date=start_date, | ||
| end_date=end_date, | ||
| portfolio=portfolio, | ||
| model_name="openai/gpt-4o-mini", | ||
| model_provider="OpenRouter", | ||
| selected_analysts=hedge_fund_request.selected_analysts, | ||
| ) | ||
|
|
||
| uvicorn.run(server.build(), host=host, port=port) | ||
| except FileNotFoundError: | ||
| logger.error(f"Error: File '{agent_card_file}' not found.") | ||
| sys.exit(1) | ||
| except json.JSONDecodeError: | ||
| logger.error(f"Error: File '{agent_card_file}' contains invalid JSON.") | ||
| sys.exit(1) | ||
| except Exception as e: | ||
| logger.error(f'An error occurred during server startup: {e}') | ||
| sys.exit(1) | ||
| yield { | ||
| "content": json.dumps(result), | ||
| "is_task_complete": True, | ||
| } | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| main() | ||
| if __name__ == "__main__": | ||
| agent = create_wrapped_agent(AIHedgeFundAgent) | ||
| asyncio.run(agent.serve()) | ||
28 changes: 0 additions & 28 deletions
28
python/third_party/ai-hedge-fund/adapter/agent_executor.py
This file was deleted.
Oops, something went wrong.
27 changes: 0 additions & 27 deletions
27
python/third_party/ai-hedge-fund/adapter/hedge_fund_agent.json
This file was deleted.
Oops, something went wrong.
111 changes: 0 additions & 111 deletions
111
python/third_party/ai-hedge-fund/adapter/test_client.py
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| echo "Starting AI Hedge Fund Agent..." | ||
| echo "Python Environment Overview:" | ||
| echo "uv: $(which uv)" | ||
| echo "python: $(which python)" | ||
|
|
||
| python -m adapter |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.