Production-style multi-agent stock research and selection system built with crewAI.
The project researches a user-selected sector, identifies trending companies, performs financial analysis, and chooses the best candidate for investment with a final decision report and optional push notification.
- What This Project Does
- High-Level Architecture
- Execution Flow (Command to Final Decision)
- Crew Terminology Explained
- Memory Architecture Explained
- Repository Structure
- Prerequisites
- Quick Start
- Environment Variables
- Run the Project
- Outputs
- How Hierarchical and Sequential Behavior Works Here
- Configuration Deep Dive
- Tooling and Integrations
- Customization Guide
- Troubleshooting
- Security and Operational Notes
- Roadmap Ideas
- License
Given a sector (for example: Technology, Healthcare, Finance), this crew executes a three-stage investment workflow:
- Finds 2-3 trending companies in the selected sector from recent news.
- Produces detailed research for each selected company.
- Picks one best company for investment, explains the decision, and optionally sends a push notification.
This project is a crewAI architecture with a manager-led hierarchy.
- Entry point:
src/stock_picker/main.py - Crew orchestration and memory wiring:
src/stock_picker/crew.py - Agent definitions (roles/goals/LLMs):
src/stock_picker/config/agents.yaml - Task graph and outputs:
src/stock_picker/config/tasks.yaml - Custom push tool:
src/stock_picker/tools/push_tool.py - Generated artifacts:
output/ - Memory storage:
memory/
- Orchestration model: manager-driven, hierarchical process.
- Task dependencies: context-based task chaining.
- Output contracts: structured Pydantic outputs for intermediate stages.
- Persistence: long-term and retrieval-backed short/entity memory.
When you run the crew:
main.pyasks for asectorinput in the terminal.main.pycreatesinputs = { "sector": ..., "current_date": ... }.StockPicker().crew().kickoff(inputs=inputs)starts execution.- The manager agent coordinates agents under
Process.hierarchical. - Task 1 (
find_trending_companies) runs and writesoutput/trending_companies.json. - Task 2 (
research_trending_companies) consumes Task 1 context and writesoutput/research_report.json. - Task 3 (
pick_best_company) consumes Task 2 context, sends push notification, and writesoutput/decision.md. - Final result is printed to terminal.
crewAI can feel abstract at first. Here is the terminology mapped directly to this repository.
A crew is the full multi-agent system that executes your workflow.
In this project, the crew is created in crew.py and includes:
- Agents
- Tasks
- Process strategy (
hierarchical) - Manager agent
- Memory systems
An agent is an AI worker with a role, goal, backstory, and optional tools.
Defined in agents.yaml and instantiated in crew.py:
trending_company_finderfinancial_researcherstock_pickermanager(orchestration only)
A task is a unit of work assigned to one agent.
Defined in tasks.yaml with:
- Description
- Expected output
- Assigned agent
- Context dependencies
- Output file
Process determines orchestration style.
Process.hierarchicalmeans a manager agent can plan/delegate dynamically.- This differs from pure linear execution where tasks are always fired in fixed order without manager intervention.
Context is prior task output passed to later tasks.
In this project:
- Research task consumes company-finder output.
- Decision task consumes research output.
This creates a directed task graph with dependency-aware execution.
Tools are external capabilities an agent can invoke.
Used here:
SerperDevToolfor web/news search.PushNotificationToolfor Pushover notification delivery.
This project uses three active memory systems in the crew, and one implied conceptual layer.
- Class:
LongTermMemory - Storage: SQLite via
LTMSQLiteStorage - Path:
./memory/long_term_memory_storage.db - Purpose: keep durable learnings or prior run knowledge across sessions.
Think of this as durable historical memory.
- Class:
ShortTermMemory - Storage:
RAGStoragewith OpenAI embeddings (text-embedding-3-small) - Path:
./memory/ - Purpose: retrieval-backed temporary context used while reasoning and composing outputs.
Think of this as active working memory.
- Class:
EntityMemory - Storage:
RAGStoragewith OpenAI embeddings - Path:
./memory/ - Purpose: track facts tied to named entities (for example companies/tickers).
Think of this as per-entity knowledge state.
Contextual memory is not a separate class instantiated here, but functionally emerges from:
- Task context chaining in
tasks.yaml - Short-term retrieval context
- Entity-specific recall
In practice, contextual memory is the crew's ability to answer based on "what has happened so far in this run" and "what is relevant now".
- Reduces repeated selection of the same companies over time.
- Preserves sector-level and company-level learnings.
- Improves coherence between discovery, research, and decision tasks.
.
|-- pyproject.toml
|-- README.md
|-- .env.example
|-- src/
| `-- stock_picker/
| |-- main.py
| |-- crew.py
| |-- config/
| | |-- agents.yaml
| | `-- tasks.yaml
| `-- tools/
| `-- push_tool.py
|-- output/
`-- memory/
- Python 3.10 to 3.12
- uv
- OpenAI API key
- Serper API key (for web/news search)
- Optional: Pushover credentials for push notifications
pip install uvuv syncAlternative:
crewai installCopy .env.example to .env, then fill in real values.
crewai runUse the provided .env.example as a template.
Required for core workflow:
OPENAI_API_KEY: used for LLM and embeddings.SERPER_API_KEY: used bySerperDevToolfor internet/news search.
Optional for push alerts:
PUSHOVER_USER: Pushover user key.PUSHOVER_TOKEN: Pushover app token.
From repository root:
crewai runYou will be prompted:
Enter the sector you want to research (e.g. Technology, Healthcare, Finance):
The crew then executes all tasks and prints final decision output in terminal.
After a successful run, the following files are generated in output/:
trending_companies.json: discovered candidates.research_report.json: detailed per-company research.decision.md: final pick and rationale.
This project uses both ideas together:
- Hierarchical orchestration: enabled by
process=Process.hierarchicalandmanager_agent. - Sequential dependencies: enforced by explicit
contextlinks in tasks.
In effect:
- The manager can control and delegate how work gets done.
- The dependency graph still ensures that downstream tasks receive required upstream outputs.
This yields controlled flexibility: dynamic orchestration with deterministic data flow.
Each agent includes:
role: what the agent is.goal: mission criteria.backstory: behavior shaping context.llm: model identifier.
Current model choices:
- Worker agents:
openai/gpt-4o-mini - Manager:
openai/gpt-4o
Each task includes:
descriptionexpected_outputagentcontext(optional dependencies)output_file
Two stages use Pydantic output schemas:
- Trending companies list
- Detailed research list
This enforces stronger shape consistency for intermediate outputs.
Used by:
- Trending finder
- Financial researcher
Purpose:
- Current web/news retrieval for evidence-backed analysis.
Used by:
- Final stock picker
Implementation details:
- Endpoint:
https://api.pushover.net/1/messages.json - Method: POST
- Payload: user, token, message
Edit src/stock_picker/config/agents.yaml and add corresponding @agent methods in src/stock_picker/crew.py.
Edit src/stock_picker/config/tasks.yaml and add corresponding @task methods in src/stock_picker/crew.py.
In crew.py, switch:
Process.hierarchicaltoProcess.sequentialif you want strict linear execution without manager delegation.
Update memory storage paths in crew.py if you want environment-specific persistence locations.
- Add valuation metrics (P/E, growth, debt, FCF).
- Add risk scoring rubric.
- Add guardrails for diversification and concentration limits.
- Verify
.envexists and includes required keys. - Ensure shell session loads environment variables.
- Confirm
SERPER_API_KEYis valid. - Retry with a broader sector keyword.
- Check
PUSHOVER_USERandPUSHOVER_TOKEN. - Confirm Pushover app/device settings.
- Confirm
memory/is writable. - Check for
memory/long_term_memory_storage.dbcreation after run.
- Do not commit real
.envsecrets. - Keep
.envin.gitignore(already configured). - Consider rotating API keys periodically.
- Validate generated investment output before real-world decisions.
- Add backtesting and benchmark comparison.
- Add portfolio construction instead of single-pick output.
- Add compliance/risk policy constraints.
- Add scheduled runs and notification digests.
- Add unit and integration tests for tools and task contracts.
This project is licensed under the MIT License. See the LICENSE file for details.