This commit is contained in:
career091101 2025-08-11 20:52:36 +09:00 committed by GitHub
commit f9be21d68d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 1626 additions and 158 deletions

22
.github/workflows/claude.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: Claude Code
permissions:
contents: write
pull-requests: write
issues: write
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
run-claude:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v0
with:
trigger_phrase: "@claude"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}

37
.github/workflows/pre-commit.yml vendored Normal file
View File

@ -0,0 +1,37 @@
name: pre-commit
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: pre-commit-${{ github.ref }}
cancel-in-progress: true
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- uses: actions/setup-go@v5
with:
go-version: '1.22'
# Gitleaks requires Go
- name: Install gitleaks
run: |
go install github.com/gitleaks/gitleaks/v8@latest
echo "$HOME/go/bin" >> $GITHUB_PATH
- uses: pre-commit/action@v3.0.1
with:
extra_args: --all-files

20
.gitleaks.toml Normal file
View File

@ -0,0 +1,20 @@
# Gitleaks configuration for TradingAgents
# https://github.com/gitleaks/gitleaks
[extend]
# Use default rules
useDefault = true
[allowlist]
# Add project-specific allowlist patterns here
# Example:
# paths = [
# "test/fixtures",
# "docs/examples"
# ]
# Allow specific commits (if needed)
# commits = ["commit-hash"]
# Allow specific files
# files = ["file-with-false-positive.txt"]

20
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,20 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.8
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
files: "\\.(js|jsx|ts|tsx|mts|cts|json|css|scss|md|ya?ml)$"
exclude: "uv.lock"
- repo: https://github.com/gitleaks/gitleaks
rev: v8.28.0
hooks:
- id: gitleaks
args: ["protect", "--staged", "--redact", "--verbose"]

11
.prettierignore Normal file
View File

@ -0,0 +1,11 @@
node_modules
dist
build
coverage
.venv
*.pyc
__pycache__
.pytest_cache
.ruff_cache
uv.lock
*.egg-info

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
}

100
.tasks/QUICKSTART.md Normal file
View File

@ -0,0 +1,100 @@
# 🚀 Scopecraft Quick Start Guide
Welcome to Scopecraft ! This guide will help you get started with the new workflow-based task system.
## 📋 Workflow States
Tasks move through three workflow states:
1. **📥 Backlog**: Future work not yet started
2. **🔄 Current**: Active work in progress
3. **✅ Archive**: Completed work
## 🎯 Basic Commands
### Creating Tasks
```bash
# Create a new task (goes to backlog by default)
sc task create --title "Add user authentication" --type feature --area auth
# Create and start immediately (goes to current)
sc task create --title "Fix login bug" --type bug --area auth --current
# Create from a template
sc task create --template feature --title "New Feature" --area ui
```
### Working with Tasks
```bash
# Start working on a task (move to current)
sc task start implement-oauth-0127-AB
# Complete a task (move to archive)
sc task complete implement-oauth-0127-AB
# View a task
sc task get implement-oauth-0127-AB
# Update task metadata
sc task update implement-oauth-0127-AB --status "🔴 Blocked"
# Update a specific section
sc task update-section implement-oauth-0127-AB instruction "New instructions here"
```
### Listing Tasks
```bash
# List current tasks (default)
sc task list
# List backlog tasks
sc task list --workflow backlog
# List all tasks including archived
sc task list --all
# Filter by type, status, or area
sc task list --type bug --status "🔵 In Progress"
```
## 📁 Task Structure
Each task is a `.task.md` file with:
1. **Frontmatter**: Type, status, area, and custom fields
2. **Sections**:
- **Instruction**: What needs to be done
- **Tasks**: Checklist of subtasks
- **Deliverable**: Work outputs
- **Log**: Execution history
## 🔧 Complex Tasks
For large tasks, create a folder with subtasks:
```bash
# Create a complex task
sc task create --title "Dashboard Redesign" --complex
# Add subtasks (numbered for sequencing)
sc task add-subtask dashboard-redesign "01-user-research"
sc task add-subtask dashboard-redesign "02-implement-ui"
sc task add-subtask dashboard-redesign "03-test-implementation"
```
## 💡 Tips
1. **Keep current small**: Only tasks actively being worked on
2. **Review backlog regularly**: Prioritize and prune
3. **Archive monthly**: Tasks are auto-organized by YYYY-MM
4. **Use templates**: Ensure consistency across similar tasks
5. **Update logs**: Document progress in the Log section
## 🔗 Task References
Reference other tasks using: `@task:implement-oauth-0127-AB`
Reference specific sections: `@task:implement-oauth-0127-AB#deliverable`
---
For more information, visit the [Scopecraft documentation](https://github.com/timmeeuwissen/scopecraft).

View File

@ -0,0 +1,16 @@
# Auth機能
---
type: feature
status: todo
area: general
---
## Instruction
## Tasks
## Deliverable
## Log

858
CLAUDE.md Normal file
View File

@ -0,0 +1,858 @@
# CLAUDE.md
このファイルはClaude Code (claude.ai/code) がTradingAgentsリポジトリで作業する際のガイダンスとベストプラクティスを提供します。
## 1. プロジェクトの目的と非目標
### 目的
- **マルチエージェント金融分析**: 実世界の投資会社の組織構造を模倣したLLMベースの取引分析システム
- **包括的市場分析**: テクニカル分析、ファンダメンタルズ分析、センチメント分析、ニュース分析の統合
- **協調的意思決定**: 複数のエージェントによるディベートと段階的な意思決定プロセス
- **学習機能**: 過去の取引から学習し、将来の判断を改善
### 非目標
- **実際の取引実行**: 本システムは分析と推奨のみを提供し、実際の取引は行わない
- **投資助言**: 金融投資アドバイスではなく、研究目的のフレームワーク
- **リアルタイム取引**: 高頻度取引やリアルタイムアービトラージは対象外
- **規制対応**: 金融規制への準拠は使用者の責任
## 2. コーディング規約
### 命名規則
```python
# クラス名: PascalCase
class TradingAgentsGraph:
class FinancialSituationMemory:
# 関数名: snake_case
def create_market_analyst():
def get_finnhub_news():
# 定数: UPPER_SNAKE_CASE
DEFAULT_CONFIG = {...}
MAX_DEBATE_ROUNDS = 3
# プライベート属性: 先頭にアンダースコア
self._config = config
def _create_tool_nodes():
```
### 型付け
```python
# 必須: 関数シグネチャに型ヒント使用
from typing import Dict, Any, List, Optional, Annotated
def propagate(self, company_name: str, trade_date: str) -> Tuple[Dict, str]:
pass
# Annotated使用例 (tradingagents/agents/utils/agent_utils.py)
@tool
def get_finnhub_news(
ticker: Annotated[str, "Search query of a company, e.g. 'AAPL, TSM, etc."],
start_date: Annotated[str, "Start date in yyyy-mm-dd format"],
) -> str:
```
### 制御フロー
```python
# 条件分岐の明確化 (tradingagents/graph/conditional_logic.py)
def should_continue_debate(self, state: AgentState) -> str:
if state["investment_debate_state"]["count"] >= 2 * self.max_debate_rounds:
return "Research Manager"
if state["investment_debate_state"]["current_response"].startswith("Bull"):
return "Bear Researcher"
return "Bull Researcher"
```
### コメント・ドキュメント
```python
# クラス・関数にはdocstring必須
def create_market_analyst(llm, toolkit):
"""
市場アナリストノードを作成
Args:
llm: 使用するLLMインスタンス
toolkit: データアクセス用ツールキット
Returns:
market_analyst_node: 設定済みのノード関数
"""
```
## 3. LLM/プロンプト設計
### モデル選択戦略
```python
# 深い思考モデル: 複雑な判断・ディベート調整
# Research Manager, Risk Managerで使用
self.deep_thinking_llm = ChatOpenAI(model=config["deep_think_llm"]) # o1-preview, gpt-4o
# 速い思考モデル: 迅速な分析・データ処理
# 各アナリスト、リサーチャーで使用
self.quick_thinking_llm = ChatOpenAI(model=config["quick_think_llm"]) # gpt-4o-mini
```
### プロンプトテンプレート設計
```python
# 構造化プロンプト例 (tradingagents/agents/analysts/market_analyst.py)
system_message = """You are a trading assistant tasked with analyzing financial markets.
Your role is to select the **most relevant indicators** for a given market condition...
Moving Averages:
- close_50_sma: 50 SMA: A medium-term trend indicator...
- close_200_sma: 200 SMA: A long-term trend benchmark...
[具体的な指標と使用方法を列挙]
Make sure to append a Markdown table at the end of the report..."""
# MessagesPlaceholderで動的コンテンツ挿入
prompt = ChatPromptTemplate.from_messages([
("system", system_message),
MessagesPlaceholder(variable_name="messages"),
])
```
### 出力検証
```python
# 最終判断の明確なマーカー
"FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL**"
# ツール呼び出しチェック
if len(result.tool_calls) == 0:
report = result.content
```
## 4. シークレット/設定管理
### 環境変数管理
```bash
# 必須環境変数 (.env.example作成推奨)
export FINNHUB_API_KEY=$YOUR_FINNHUB_API_KEY
export OPENAI_API_KEY=$YOUR_OPENAI_API_KEY
export GOOGLE_API_KEY=$YOUR_GOOGLE_API_KEY # Google使用時
export ANTHROPIC_API_KEY=$YOUR_ANTHROPIC_API_KEY # Anthropic使用時
```
### 設定ファイル構造
```python
# tradingagents/default_config.py
DEFAULT_CONFIG = {
"project_dir": os.path.abspath(...), # 動的パス解決
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", "./results"), # 環境変数オーバーライド可能
"llm_provider": "openai",
"deep_think_llm": "o4-mini",
"quick_think_llm": "gpt-4o-mini",
"max_debate_rounds": 1,
"online_tools": True, # オンライン/オフライン切替
}
```
### APIキー漏洩防止
```python
# ログ出力時の秘匿化
def log_api_call(endpoint: str, params: Dict):
# APIキーを含まないパラメータのみログ出力
safe_params = {k: v for k, v in params.items() if "key" not in k.lower()}
logger.info(f"API Call: {endpoint}, params: {safe_params}")
```
## 5. データフローとキャッシュ
### 外部API利用方針
```python
# オンライン/オフライン切替 (tradingagents/agents/analysts/market_analyst.py)
if toolkit.config["online_tools"]:
tools = [
toolkit.get_YFin_data_online,
toolkit.get_stockstats_indicators_report_online,
]
else:
tools = [
toolkit.get_YFin_data, # キャッシュデータ使用
toolkit.get_stockstats_indicators_report,
]
```
### レート制限対策
```python
# API呼び出し間隔制御
import time
from functools import wraps
def rate_limit(calls_per_second=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
time.sleep(1 / calls_per_second)
return func(*args, **kwargs)
return wrapper
return decorator
```
### キャッシュ戦略
```python
# データキャッシュディレクトリ構造
# tradingagents/dataflows/data_cache/
# ├── [ticker]/
# │ ├── news_data/
# │ ├── price_data/
# │ └── fundamentals/
# キャッシュファイル命名
cache_file = f"{ticker}_{date}_{data_type}.json"
```
## 6. エージェント/グラフ設計
### 状態管理パターン
```python
# 階層的状態設計 (tradingagents/agents/utils/agent_states.py)
class AgentState(MessagesState):
# 基本情報
company_of_interest: str
trade_date: str
# 分析レポート
market_report: str
sentiment_report: str
news_report: str
fundamentals_report: str
# ディベート状態
investment_debate_state: InvestDebateState
risk_debate_state: RiskDebateState
# 最終決定
final_trade_decision: str
```
### メモリシステム
```python
# ChromaDBベクトルデータベース使用 (tradingagents/agents/utils/memory.py)
class FinancialSituationMemory:
def add_situations(self, situations_and_advice):
# 埋め込みベクトル生成
embeddings.append(self.get_embedding(situation))
# ChromaDBに保存
self.situation_collection.add(...)
def get_memories(self, current_situation, n_matches=1):
# 類似検索
query_embedding = self.get_embedding(current_situation)
results = self.situation_collection.query(...)
```
### 条件分岐ロジック
```python
# 明示的な条件分岐 (tradingagents/graph/conditional_logic.py)
def should_continue_market(self, state: AgentState):
messages = state["messages"]
last_message = messages[-1]
if last_message.tool_calls:
return "tools_market" # ツール実行へ
return "Msg Clear Market" # 次のノードへ
```
### デバッグ方針
```python
# デバッグモード実装 (tradingagents/graph/trading_graph.py)
if self.debug:
trace = []
for chunk in self.graph.stream(init_agent_state, **args):
chunk["messages"][-1].pretty_print() # リアルタイム出力
trace.append(chunk)
```
## 7. エラーハンドリングとロギング
### 構造化エラーハンドリング
```python
# API呼び出しエラー処理
try:
result = get_data_in_range(ticker, before, curr_date, "news_data", DATA_DIR)
except Exception as e:
logger.error(f"Failed to fetch news for {ticker}: {str(e)}")
return "" # 空文字列で継続(フェイルセーフ)
```
### ログレベル戦略
```python
import logging
# 環境別ログレベル
log_level = logging.DEBUG if debug else logging.INFO
logging.basicConfig(
level=log_level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
```
### 例外分類
```python
class TradingAgentsException(Exception):
"""基底例外クラス"""
pass
class DataFetchError(TradingAgentsException):
"""データ取得エラー"""
pass
class LLMInvocationError(TradingAgentsException):
"""LLM呼び出しエラー"""
pass
```
## 8. パフォーマンス最適化
### 並列化戦略
```python
# 複数アナリストの並列実行 (tradingagents/graph/setup.py)
# LangGraphが自動的に独立したードを並列実行
workflow.add_edge(START, "Market Analyst") # これらは
workflow.add_edge(START, "Social Analyst") # 並列に
workflow.add_edge(START, "News Analyst") # 実行される
workflow.add_edge(START, "Fundamentals Analyst")
```
### トークン管理
```python
# リフレクション時のトークン制限 (tradingagents/graph/reflection.py)
"Extract key insights from the summary into a concise sentence of no more than 1000 tokens."
```
### コスト最適化
```python
# メッセージ削除でコンテキスト削減 (tradingagents/agents/utils/agent_utils.py)
def create_msg_delete():
def delete_messages(state):
messages = state["messages"]
removal_operations = [RemoveMessage(id=m.id) for m in messages]
placeholder = HumanMessage(content="Continue") # 最小限のプレースホルダー
return {"messages": removal_operations + [placeholder]}
```
## 9. テスト戦略
### テスト構造(推奨)
```
tests/
├── unit/
│ ├── test_agents/
│ ├── test_dataflows/
│ └── test_graph/
├── integration/
│ ├── test_full_pipeline.py
│ └── test_api_integration.py
└── fixtures/
└── sample_data.json
```
### モックデータ使用
```python
# オフラインモードでテスト
test_config = DEFAULT_CONFIG.copy()
test_config["online_tools"] = False # キャッシュデータ使用
# モックLLM応答
@patch('langchain_openai.ChatOpenAI.invoke')
def test_market_analyst(mock_invoke):
mock_invoke.return_value = AIMessage(content="FINAL TRANSACTION PROPOSAL: **HOLD**")
```
## 10. CLI UXガイドライン
### 入力検証
```python
# questionary による対話的入力 (cli/utils.py)
def get_ticker() -> str:
ticker = questionary.text(
"Enter the ticker symbol to analyze:",
validate=lambda x: len(x.strip()) > 0 or "Please enter a valid ticker symbol.",
style=questionary.Style([("text", "fg:green")])
).ask()
```
### リッチ表示
```python
# Rich Layout構造 (cli/main.py)
layout = Layout()
layout.split_column(
Layout(name="header", size=3),
Layout(name="body"),
Layout(name="footer", size=3)
)
```
### フェイルセーフ
```python
# 終了処理
if not ticker:
console.print("\n[red]No ticker symbol provided. Exiting...[/red]")
exit(1)
```
## 11. セキュリティ/コンプライアンス
### APIキー保護
```python
# 環境変数から取得、ハードコード禁止
api_key = os.getenv("FINNHUB_API_KEY")
if not api_key:
raise ValueError("FINNHUB_API_KEY not set")
```
### 依存関係管理
```bash
# 定期的な依存関係更新
pip install --upgrade -r requirements.txt
pip audit # 脆弱性チェック
```
### データプライバシー
```python
# PII個人識別情報の除外
def sanitize_output(text: str) -> str:
# メールアドレス、電話番号等を除去
import re
text = re.sub(r'\S+@\S+', '[EMAIL]', text)
text = re.sub(r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b', '[PHONE]', text)
return text
```
## 12. 依存/ビルド/実行方法
### 必須ツール
```bash
# Python 3.10以上必須
python --version # >= 3.10
# 仮想環境作成
conda create -n tradingagents python=3.13
conda activate tradingagents
```
### インストール手順
```bash
# 依存関係インストール
pip install -r requirements.txt
# 開発用インストール
pip install -e .
```
### 実行コマンド
```bash
# CLI実行
python -m cli.main
# プログラム実行
python main.py
# カスタム設定での実行
FINNHUB_API_KEY=xxx OPENAI_API_KEY=yyy python main.py
```
### よくある落とし穴
```python
# 1. ハードコードされたパス
# NG: "/Users/yluo/Documents/Code/ScAI/FR1-data"
# OK: os.getenv("DATA_DIR", "./data")
# 2. APIキー不足
# 必ずFINNHUB_API_KEYとLLMプロバイダーのAPIキーを設定
# 3. オンライン/オフライン混在
# config["online_tools"]を統一的に設定
```
## 13. コントリビュート規約
### ブランチ戦略
```bash
# 機能開発
git checkout -b feature/agent-improvement
# バグ修正
git checkout -b fix/api-error-handling
# ドキュメント
git checkout -b docs/update-readme
```
### コミットメッセージ
```bash
# 形式: <type>: <description>
feat: Add portfolio optimization agent
fix: Handle FinnHub API timeout
docs: Update installation guide
refactor: Simplify debate logic
test: Add unit tests for market analyst
```
### PR要件
- [ ] コードがPEP 8準拠
- [ ] 型ヒント追加
- [ ] docstring記載
- [ ] テスト追加/更新
- [ ] CLAUDE.md更新必要に応じて
## 14. 変更の安全運用
### 実験フラグ
```python
# 新機能の段階的ロールアウト
DEFAULT_CONFIG = {
"experimental_features": {
"advanced_memory": False, # デフォルトOFF
"parallel_analysis": True, # 段階的有効化
}
}
if config.get("experimental_features", {}).get("advanced_memory"):
# 新機能のコード
```
### ロールバック戦略
```python
# 設定による動作切替
if config.get("fallback_mode"):
# 安定版の処理
return legacy_analysis()
else:
# 新バージョンの処理
return advanced_analysis()
```
### 監視ポイント
```python
# 重要メトリクスのログ出力
logger.info(f"Analysis completed: ticker={ticker}, duration={duration}s, tokens={token_count}")
logger.info(f"Debate rounds: {state['investment_debate_state']['count']}")
logger.info(f"Final decision: {state['final_trade_decision']}")
```
## 15. LangGraphベストプラクティス (2024-2025)
### コアフレームワーク原則
LangGraphは制御可能なエージェントを構築するために設計されており、複雑なタスクを確実に処理し、エージェントが軌道から外れることを防ぐモデレーションと品質ループを簡単に追加できます。
#### グラフベース設計
```python
# すべての相互作用を循環グラフとしてモデル化
from langgraph.graph import StateGraph, START, END
workflow = StateGraph(AgentState)
# 複数のループとif文を持つ高度なワークフロー実装
```
#### 低レベルの柔軟性
```python
# 完全にカスタマイズ可能なエージェント作成
# 単一、マルチエージェント、階層的な制御フロー
# すべて一つのフレームワークで実現
```
### 状態管理パターン
#### 共有状態vs プライベート状態
```python
# 共有状態パターン
class SharedAgentState(TypedDict):
messages: List[BaseMessage]
shared_context: str
# プライベート状態パターン(サブグラフ用)
class SearchAgentState(TypedDict):
queries: List[str]
documents: List[Document]
# 親グラフとは独立した状態スキーマ
```
#### 階層的メモリ管理
```python
# 三層構造のメモリシステム
class MemoryHierarchy:
short_term: ConversationalMemory # 短期会話メモリ
long_term: HistoricalStorage # 長期履歴ストレージ
external: RAGDataSource # 外部データソースRAG
```
### チェックポイントと永続化
#### チェックポインター実装
```python
from langgraph.checkpoint import MemorySaver, PostgresSaver
# インメモリチェックポインター(開発用)
checkpointer = MemorySaver()
# PostgreSQLチェックポインター本番用
checkpointer = PostgresSaver(connection_string="postgresql://...")
# グラフコンパイル時に指定
graph = workflow.compile(checkpointer=checkpointer)
```
#### スレッド管理
```python
# スレッドIDを使用した状態の永続化
config = {"configurable": {"thread_id": "user_123_session_456"}}
result = graph.invoke(input_data, config=config)
# 後から同じスレッドを再開
resumed_result = graph.invoke(new_input, config=config)
```
### Human-in-the-Loop パターン (2024最新)
#### interrupt関数の使用
```python
from langgraph.prebuilt import interrupt
def review_decision_node(state):
decision = analyze_data(state)
# 人間のレビューが必要な場合は中断
if decision.requires_human_review:
human_input = interrupt(
"Please review this decision: " + decision.summary
)
decision = update_decision(decision, human_input)
return {"decision": decision}
```
#### レビューと承認パターン
```python
# ツール呼び出しのレビュー
def review_tool_calls(state):
tool_calls = state["pending_tool_calls"]
# 危険な操作は人間の承認を要求
if any(is_dangerous(call) for call in tool_calls):
approval = interrupt(f"Approve these operations? {tool_calls}")
if not approval:
return {"tool_calls": [], "status": "rejected"}
return {"tool_calls": tool_calls, "status": "approved"}
```
#### 状態編集パターン
```python
# グラフ状態の人間による編集
def allow_state_editing(state):
# 現在の状態を表示
display_state = format_for_human(state)
# 人間による編集を許可
edited_state = interrupt(
f"Current state: {display_state}\nEdit if needed:"
)
# 編集された状態で続行
return merge_states(state, edited_state)
```
### マルチエージェントオーケストレーション
#### スーパーバイザーアーキテクチャ
```python
# ハンドオフツールを使用したスーパーバイザー実装
def create_supervisor_graph():
workflow = StateGraph(SupervisorState)
# スーパーバイザーノード
workflow.add_node("supervisor", supervisor_agent)
# ワーカーエージェント
workflow.add_node("analyst", analyst_agent)
workflow.add_node("researcher", researcher_agent)
# 動的ルーティング
workflow.add_conditional_edges(
"supervisor",
route_to_worker,
["analyst", "researcher", END]
)
return workflow.compile()
```
#### コラボレーションパターン
```python
# 共有スクラッチパッドでの協調
class CollaborativeState(TypedDict):
shared_messages: List[Message] # 全エージェントが見える
individual_contexts: Dict[str, Any] # エージェント固有
```
#### 階層的チーム構造
```python
# サブグラフとしてのチーム実装
def create_hierarchical_teams():
# 各チームは独立したLangGraphオブジェクト
research_team = create_research_team_graph()
analysis_team = create_analysis_team_graph()
# メインワークフロー
main_workflow = StateGraph(MainState)
main_workflow.add_node("research", research_team)
main_workflow.add_node("analysis", analysis_team)
return main_workflow.compile()
```
### パフォーマンス最適化
#### ストリーミングサポート
```python
# トークンごとのストリーミング
async for chunk in graph.astream(input_data, config):
if chunk.get("messages"):
print(chunk["messages"][-1].content)
# 中間ステップのストリーミング
if chunk.get("intermediate_steps"):
display_reasoning(chunk["intermediate_steps"])
```
#### インテリジェントキャッシング
```python
# LangGraph Platformの自動キャッシング
config = {
"caching": {
"enable": True,
"ttl": 3600, # 1時間
"max_size": "1GB"
}
}
```
#### 並列実行
```python
# 独立したノードの自動並列化
workflow.add_edge(START, ["agent1", "agent2", "agent3"])
# LangGraphが自動的に並列実行
```
### エラーハンドリングと再試行
#### 自動再試行
```python
from langgraph.prebuilt import RetryPolicy
retry_policy = RetryPolicy(
max_attempts=3,
backoff_factor=2,
exceptions=(APIError, TimeoutError)
)
graph = workflow.compile(retry_policy=retry_policy)
```
#### フォールトトレランス
```python
# チェックポイントによる障害復旧
try:
result = graph.invoke(input_data, config)
except Exception as e:
# チェックポイントから自動復旧
last_checkpoint = checkpointer.get_latest(thread_id)
result = graph.invoke(None, config, from_checkpoint=last_checkpoint)
```
### 本番環境デプロイメント
#### LangGraph Platform設定
```python
# 水平スケーリング設定
deployment_config = {
"scaling": {
"min_replicas": 2,
"max_replicas": 10,
"target_cpu_utilization": 70
},
"task_queue": {
"max_concurrent": 100,
"timeout": 300
}
}
```
#### 監視とロギング
```python
# 構造化ログの実装
import structlog
logger = structlog.get_logger()
def monitored_node(state):
logger.info("node_execution_start",
node_name="analyst",
thread_id=state.get("thread_id"))
result = process(state)
logger.info("node_execution_complete",
node_name="analyst",
duration=elapsed_time,
token_count=count_tokens(result))
return result
```
### 2025年の推奨事項
#### 長コンテキストRAG
```python
# 25,000トークン以上の処理
config = {
"context_window": 32000,
"chunking_strategy": "semantic",
"overlap": 200
}
```
#### Agentic RAGパターン
```python
# モジュラーでインテリジェントなエージェントワークフロー
class AgenticRAG:
def __init__(self):
self.retrieval_agent = RetrievalAgent()
self.reasoning_agent = ReasoningAgent()
self.generation_agent = GenerationAgent()
def process(self, query):
# 各エージェントが専門的な役割を担当
docs = self.retrieval_agent.retrieve(query)
reasoning = self.reasoning_agent.analyze(docs)
response = self.generation_agent.generate(reasoning)
return response
```
## クイックリファレンス
### 主要ファイル
- `tradingagents/graph/trading_graph.py` - メインオーケストレーター
- `tradingagents/agents/` - 各エージェント実装
- `tradingagents/dataflows/interface.py` - データAPI統合
- `cli/main.py` - CLIエントリーポイント
### 重要な設定
```python
config["max_debate_rounds"] = 3 # ディベート回数
config["online_tools"] = True # リアルタイムデータ使用
config["llm_provider"] = "openai" # LLMプロバイダー
```
### デバッグTips
```python
# デバッグモード有効化
ta = TradingAgentsGraph(debug=True, config=config)
# 状態ログ確認
self._log_state(trade_date, final_state) # logs/に保存
```

283
TECHNICAL_SPECS.md Normal file
View File

@ -0,0 +1,283 @@
# TradingAgents 技術仕様書
## プロジェクト概要
TradingAgentsは、実世界の投資会社の組織構造を模倣したマルチエージェントLLMベースの金融取引フレームワークです。専門化されたAIエージェントが協調して市場分析と取引判断を行います。
## システムアーキテクチャ
### 1. コア構成
```
TradingAgents/
├── tradingagents/ # コアパッケージ
│ ├── agents/ # マルチエージェントシステム
│ ├── dataflows/ # データ処理・API統合
│ ├── graph/ # グラフベースオーケストレーション
│ └── default_config.py # デフォルト設定
├── cli/ # コマンドラインインターフェース
└── main.py # エントリーポイント
```
### 2. エージェントシステム詳細
#### 2.1 エージェント階層
**Phase I: アナリストチーム**
- **Market Analyst**: テクニカル分析SMA、EMA、MACD、RSI、ボリンジャーバンド等
- **Social Media Analyst**: Reddit、ソーシャルメディアのセンチメント分析
- **News Analyst**: グローバルニュース、マクロ経済指標の分析
- **Fundamentals Analyst**: 財務諸表、インサイダー取引、企業業績分析
**Phase II: リサーチチーム**
- **Bull Researcher**: 楽観的視点での投資機会評価
- **Bear Researcher**: 悲観的視点でのリスク評価
- **Research Manager**: ディベートの調整と最終判断
**Phase III: トレーディングチーム**
- **Trader**: 全分析を統合し、取引戦略を策定
**Phase IV: リスク管理チーム**
- **Aggressive Debator**: 積極的リスク姿勢
- **Conservative Debator**: 保守的リスク姿勢
- **Neutral Debator**: 中立的リスク評価
- **Risk Manager**: リスク評価の統合と最終判断
**Phase V: ポートフォリオ管理**
- **Portfolio Manager**: 最終取引承認/拒否
#### 2.2 エージェント間通信
```python
# 状態管理クラス
class AgentState(MessagesState):
company_of_interest: str
trade_date: str
market_report: str
sentiment_report: str
news_report: str
fundamentals_report: str
investment_plan: str
trader_investment_plan: str
final_trade_decision: str
class InvestDebateState:
bull_history: str
bear_history: str
judge_decision: str
count: int
class RiskDebateState:
risky_history: str
safe_history: str
neutral_history: str
judge_decision: str
count: int
```
### 3. データフロー・API統合
#### 3.1 外部データソース
**金融データプロバイダー**
- **Yahoo Finance** (yfinance): 株価、出来高、財務データ
- **FinnHub**: ニュース、インサイダー取引、センチメント
- **StockStats**: テクニカル指標計算
**ソーシャル・ニュースデータ**
- **Reddit API** (praw): r/wallstreetbets等のセンチメント
- **Google News**: 最新ニュース記事
- **OpenAI API**: リアルタイムニュース要約
#### 3.2 データキャッシング
```python
# オンライン/オフラインモード切替
config["online_tools"] = True # リアルタイムデータ
config["online_tools"] = False # キャッシュデータ使用
```
### 4. LLM統合システム
#### 4.1 対応LLMプロバイダー
```python
# プロバイダー設定
config["llm_provider"] = "openai" # OpenAI GPT
config["llm_provider"] = "anthropic" # Claude
config["llm_provider"] = "google" # Gemini
config["llm_provider"] = "ollama" # ローカルLLM
config["llm_provider"] = "openrouter" # OpenRouter
```
#### 4.2 デュアルLLM戦略
```python
# 深い思考モデル(複雑な分析・判断)
config["deep_think_llm"] = "o1-preview" # または gpt-4o
# 速い思考モデル(迅速な応答)
config["quick_think_llm"] = "gpt-4o-mini"
```
### 5. メモリ・学習システム
#### 5.1 FinancialSituationMemory
```python
class FinancialSituationMemory:
def __init__(self, name, config):
# ChromaDBベクトルデータベース使用
# OpenAI/Nomic埋め込みモデル
def add_situations(self, situations_and_advice):
# 過去の取引状況と結果を保存
def get_memories(self, current_situation, n_matches=1):
# 類似状況から学習を取得
```
#### 5.2 メモリ種別
- **bull_memory**: 楽観的予測の履歴
- **bear_memory**: 悲観的予測の履歴
- **trader_memory**: 取引決定の履歴
- **invest_judge_memory**: 投資判断の履歴
- **risk_manager_memory**: リスク評価の履歴
### 6. グラフベース実行フロー
#### 6.1 LangGraphフレームワーク
```python
# グラフ構築
workflow = StateGraph(AgentState)
# ノード追加
workflow.add_node("Market Analyst", market_analyst_node)
workflow.add_node("tools_market", tool_node)
# 条件付きエッジ
workflow.add_conditional_edges(
"Market Analyst",
should_continue_market,
["tools_market", "Msg Clear Market"]
)
```
#### 6.2 実行フロー
1. **並列分析フェーズ**: 全アナリストが同時にデータ収集
2. **リサーチディベート**: Bull vs Bear、最大N回のディベート
3. **取引決定**: トレーダーが統合レポート作成
4. **リスク評価**: 3つの視点からリスク議論
5. **最終承認**: ポートフォリオマネージャーが決定
### 7. CLI技術仕様
#### 7.1 リアルタイムUI
**Richライブラリによる4パネル表示**
```python
Layout(
Header: ウェルカムメッセージ
Progress: エージェント状態テーブル
Messages: メッセージログ100件バッファ
Analysis: 現在のレポート表示
Footer: 統計情報
)
```
#### 7.2 ユーザーインタラクション
1. **ティッカー選択**: 銘柄コード入力
2. **日付選択**: 分析日YYYY-MM-DD
3. **アナリスト選択**: チェックボックス
4. **研究深度**: Shallow(1) / Medium(3) / Deep(5)
5. **LLMプロバイダー**: 選択メニュー
6. **モデル選択**: 深い/速い思考モデル
### 8. 依存関係
#### 8.1 主要ライブラリ
**LLMフレームワーク**
- langchain-openai >=0.3.23
- langchain-anthropic >=0.3.15
- langchain-google-genai >=2.1.5
- langgraph >=0.4.8
**金融データ**
- yfinance >=0.2.63
- finnhub-python >=2.4.23
- stockstats >=0.6.5
**UI/CLI**
- typer (CLIフレームワーク)
- rich >=14.0.0 (ターミナルUI)
- questionary >=2.1.0 (対話型プロンプト)
**データ処理**
- pandas >=2.3.0
- chromadb >=1.0.12
- redis >=6.2.0
### 9. 設定システム
#### 9.1 デフォルト設定
```python
DEFAULT_CONFIG = {
"llm_provider": "openai",
"deep_think_llm": "o4-mini",
"quick_think_llm": "gpt-4o-mini",
"max_debate_rounds": 1,
"max_risk_discuss_rounds": 1,
"max_recur_limit": 100,
"online_tools": True,
"backend_url": "https://api.openai.com/v1"
}
```
#### 9.2 カスタマイズ例
```python
config = DEFAULT_CONFIG.copy()
config["llm_provider"] = "google"
config["deep_think_llm"] = "gemini-2.0-flash"
config["max_debate_rounds"] = 3
config["online_tools"] = False # オフラインモード
```
### 10. 実行要件
#### 10.1 システム要件
- **Python**: >=3.10
- **メモリ**: 推奨8GB以上
- **ストレージ**: データキャッシュ用1GB以上
#### 10.2 API要件
```bash
export FINNHUB_API_KEY=$YOUR_FINNHUB_API_KEY
export OPENAI_API_KEY=$YOUR_OPENAI_API_KEY
```
### 11. セキュリティ考慮事項
- APIキーは環境変数で管理
- キャッシュデータはローカル保存
- ChromaDBによる埋め込みベクトルのローカル管理
- SSL/TLS通信の使用
### 12. パフォーマンス最適化
- **並列処理**: 複数アナリストの同時実行
- **キャッシング**: 頻繁にアクセスするデータの保存
- **メモリ管理**: dequeによる効率的なバッファリング
- **UI更新**: 4FPSの最適化されたリフレッシュレート
## まとめ
TradingAgentsは、高度に構造化されたマルチエージェントシステムで、実世界の投資会社の意思決定プロセスを忠実に再現しています。LangGraphによるオーケストレーション、複数のLLMプロバイダー対応、包括的な金融データ統合により、柔軟かつ強力な金融分析プラットフォームを実現しています。

View File

@ -1,29 +1,24 @@
from typing import Optional
import datetime
import typer
from pathlib import Path
from functools import wraps
from rich.console import Console
from rich.panel import Panel
from rich.spinner import Spinner
from rich.live import Live
from rich.columns import Columns
from rich.markdown import Markdown
from rich.layout import Layout
from rich.text import Text
from rich.live import Live
from rich.table import Table
from collections import deque
import time
from rich.tree import Tree
from functools import wraps
from pathlib import Path
import typer
from rich import box
from rich.align import Align
from rich.rule import Rule
from rich.columns import Columns
from rich.console import Console
from rich.layout import Layout
from rich.live import Live
from rich.markdown import Markdown
from rich.panel import Panel
from rich.spinner import Spinner
from rich.table import Table
from rich.text import Text
from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.default_config import DEFAULT_CONFIG
from cli.models import AnalystType
from cli.utils import *
from tradingagents.default_config import DEFAULT_CONFIG
from tradingagents.graph.trading_graph import TradingAgentsGraph
console = Console()
@ -394,7 +389,7 @@ def update_display(layout, spinner_text=None):
def get_user_selections():
"""Get all user selections before starting the analysis display."""
# Display ASCII art welcome message
with open("./cli/static/welcome.txt", "r") as f:
with open("./cli/static/welcome.txt") as f:
welcome_ascii = f.read()
# Create welcome box content

View File

@ -1,6 +1,4 @@
from enum import Enum
from typing import List, Optional, Dict
from pydantic import BaseModel
class AnalystType(str, Enum):

View File

@ -1,5 +1,5 @@
import questionary
from typing import List, Optional, Tuple, Dict
from cli.models import AnalystType
@ -64,7 +64,7 @@ def get_analysis_date() -> str:
return date.strip()
def select_analysts() -> List[AnalystType]:
def select_analysts() -> list[AnalystType]:
"""Select analysts using an interactive checkbox."""
choices = questionary.checkbox(
"Select Your [Analysts Team]:",

View File

@ -1,5 +1,5 @@
from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.default_config import DEFAULT_CONFIG
from tradingagents.graph.trading_graph import TradingAgentsGraph
# Create a custom config
config = DEFAULT_CONFIG.copy()

View File

@ -32,3 +32,21 @@ dependencies = [
"typing-extensions>=4.14.0",
"yfinance>=0.2.63",
]
[project.optional-dependencies]
dev = [
"pre-commit>=3.5.0",
"ruff>=0.6.9",
]
[tool.ruff]
line-length = 100
target-version = "py310"
respect-gitignore = true
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"

17
renovate.json Normal file
View File

@ -0,0 +1,17 @@
{
"extends": ["config:recommended", ":enablePreCommit", ":semanticCommits", ":timezone(Asia/Tokyo)"],
"labels": ["dependencies"],
"schedule": ["after 09:00 on Monday"],
"pre-commit": {
"enabled": true
},
"python": {
"rangeStrategy": "pin"
},
"packageRules": [
{
"matchManagers": ["pip_requirements", "pip_setup", "pyproject"],
"groupName": "Python dependencies"
}
]
}

14
scripts/notify-macos.sh Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
msg="${1:-Done}"
title="${2:-Claude Code}"
if command -v terminal-notifier >/dev/null 2>&1; then
terminal-notifier -title "$title" -message "$msg" -group "claude-code" -sound default
elif command -v osascript >/dev/null 2>&1; then
# Fallback to osascript if terminal-notifier is not installed
osascript -e "display notification \"$msg\" with title \"$title\""
else
echo "⚠️ No notification system available. Message: $msg"
fi

View File

@ -2,7 +2,7 @@
Setup script for the TradingAgents package.
"""
from setuptools import setup, find_packages
from setuptools import find_packages, setup
setup(
name="tradingagents",

View File

@ -1,23 +1,18 @@
from .utils.agent_utils import Toolkit, create_msg_delete
from .utils.agent_states import AgentState, InvestDebateState, RiskDebateState
from .utils.memory import FinancialSituationMemory
from .analysts.fundamentals_analyst import create_fundamentals_analyst
from .analysts.market_analyst import create_market_analyst
from .analysts.news_analyst import create_news_analyst
from .analysts.social_media_analyst import create_social_media_analyst
from .managers.research_manager import create_research_manager
from .managers.risk_manager import create_risk_manager
from .researchers.bear_researcher import create_bear_researcher
from .researchers.bull_researcher import create_bull_researcher
from .risk_mgmt.aggresive_debator import create_risky_debator
from .risk_mgmt.conservative_debator import create_safe_debator
from .risk_mgmt.neutral_debator import create_neutral_debator
from .managers.research_manager import create_research_manager
from .managers.risk_manager import create_risk_manager
from .trader.trader import create_trader
from .utils.agent_states import AgentState, InvestDebateState, RiskDebateState
from .utils.agent_utils import Toolkit, create_msg_delete
from .utils.memory import FinancialSituationMemory
__all__ = [
"FinancialSituationMemory",

View File

@ -1,6 +1,5 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
import json
def create_fundamentals_analyst(llm, toolkit):

View File

@ -1,6 +1,5 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
import json
def create_market_analyst(llm, toolkit):

View File

@ -1,6 +1,5 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
import json
def create_news_analyst(llm, toolkit):

View File

@ -1,6 +1,5 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
import json
def create_social_media_analyst(llm, toolkit):

View File

@ -1,5 +1,3 @@
import time
import json
def create_research_manager(llm, memory):

View File

@ -1,5 +1,3 @@
import time
import json
def create_risk_manager(llm, memory):

View File

@ -1,6 +1,3 @@
from langchain_core.messages import AIMessage
import time
import json
def create_bear_researcher(llm, memory):

View File

@ -1,6 +1,3 @@
from langchain_core.messages import AIMessage
import time
import json
def create_bull_researcher(llm, memory):

View File

@ -1,5 +1,3 @@
import time
import json
def create_risky_debator(llm):

View File

@ -1,6 +1,3 @@
from langchain_core.messages import AIMessage
import time
import json
def create_safe_debator(llm):

View File

@ -1,5 +1,3 @@
import time
import json
def create_neutral_debator(llm):

View File

@ -1,6 +1,4 @@
import functools
import time
import json
def create_trader(llm, memory):

View File

@ -1,10 +1,9 @@
from typing import Annotated, Sequence
from datetime import date, timedelta, datetime
from typing_extensions import TypedDict, Optional
from langchain_openai import ChatOpenAI
from typing import Annotated
from langgraph.graph import MessagesState
from typing_extensions import TypedDict
from tradingagents.agents import *
from langgraph.prebuilt import ToolNode
from langgraph.graph import END, StateGraph, START, MessagesState
# Researcher team state

View File

@ -1,18 +1,11 @@
from langchain_core.messages import BaseMessage, HumanMessage, ToolMessage, AIMessage
from typing import List
from datetime import datetime
from typing import Annotated
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import RemoveMessage
from langchain_core.messages import HumanMessage, RemoveMessage
from langchain_core.tools import tool
from datetime import date, timedelta, datetime
import functools
import pandas as pd
import os
from dateutil.relativedelta import relativedelta
from langchain_openai import ChatOpenAI
import tradingagents.dataflows.interface as interface
from tradingagents.default_config import DEFAULT_CONFIG
from langchain_core.messages import HumanMessage
def create_msg_delete():

View File

@ -1,18 +1,13 @@
from .finnhub_utils import get_data_in_range
from .googlenews_utils import getNewsData
from .yfin_utils import YFinanceUtils
from .reddit_utils import fetch_top_from_category
from .stockstats_utils import StockstatsUtils
from .yfin_utils import YFinanceUtils
from .interface import (
# News and sentiment functions
get_finnhub_news,
get_finnhub_company_insider_sentiment,
get_finnhub_company_insider_transactions,
# News and sentiment functions
get_finnhub_news,
get_google_news,
get_reddit_global_news,
get_reddit_company_news,
get_reddit_global_news,
# Financial statements functions
get_simfin_balance_sheet,
get_simfin_cashflow,
@ -20,10 +15,13 @@ from .interface import (
# Technical analysis functions
get_stock_stats_indicators_window,
get_stockstats_indicator,
get_YFin_data,
# Market data functions
get_YFin_data_window,
get_YFin_data,
)
from .reddit_utils import fetch_top_from_category
from .stockstats_utils import StockstatsUtils
from .yfin_utils import YFinanceUtils
__all__ = [
# News and sentiment functions

View File

@ -1,9 +1,9 @@
import tradingagents.default_config as default_config
from typing import Dict, Optional
# Use default config but allow it to be overridden
_config: Optional[Dict] = None
DATA_DIR: Optional[str] = None
_config: dict | None = None
DATA_DIR: str | None = None
def initialize_config():
@ -14,7 +14,7 @@ def initialize_config():
DATA_DIR = _config["data_dir"]
def set_config(config: Dict):
def set_config(config: dict):
"""Update the configuration with custom values."""
global _config, DATA_DIR
if _config is None:
@ -23,7 +23,7 @@ def set_config(config: Dict):
DATA_DIR = _config["data_dir"]
def get_config() -> Dict:
def get_config() -> dict:
"""Get the current configuration."""
if _config is None:
initialize_config()

View File

@ -25,7 +25,7 @@ def get_data_in_range(ticker, start_date, end_date, data_type, data_dir, period=
data_dir, "finnhub_data", data_type, f"{ticker}_data_formatted.json"
)
data = open(data_path, "r")
data = open(data_path)
data = json.load(data)
# filter keys (date, str in format YYYY-MM-DD) by the date range (str, str in format YYYY-MM-DD)

View File

@ -1,15 +1,14 @@
import json
import random
import time
from datetime import datetime
import requests
from bs4 import BeautifulSoup
from datetime import datetime
import time
import random
from tenacity import (
retry,
retry_if_result,
stop_after_attempt,
wait_exponential,
retry_if_exception_type,
retry_if_result,
)

View File

@ -1,19 +1,19 @@
from typing import Annotated, Dict
from .reddit_utils import fetch_top_from_category
from .yfin_utils import *
from .stockstats_utils import *
from .googlenews_utils import *
from .finnhub_utils import get_data_in_range
from dateutil.relativedelta import relativedelta
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
import json
import os
from datetime import datetime
from typing import Annotated
import pandas as pd
from tqdm import tqdm
import yfinance as yf
from dateutil.relativedelta import relativedelta
from openai import OpenAI
from .config import get_config, set_config, DATA_DIR
from tqdm import tqdm
from .config import DATA_DIR, get_config
from .finnhub_utils import get_data_in_range
from .googlenews_utils import *
from .reddit_utils import fetch_top_from_category
from .stockstats_utils import *
from .yfin_utils import *
def get_finnhub_news(

View File

@ -1,11 +1,8 @@
import requests
import time
import json
from datetime import datetime, timedelta
from contextlib import contextmanager
from typing import Annotated
import os
import re
from datetime import datetime
from typing import Annotated
ticker_to_company = {
"AAPL": "Apple",

View File

@ -1,8 +1,10 @@
import os
from typing import Annotated
import pandas as pd
import yfinance as yf
from stockstats import wrap
from typing import Annotated
import os
from .config import get_config

View File

@ -1,9 +1,8 @@
import os
import json
import pandas as pd
from datetime import date, timedelta, datetime
from datetime import date, datetime, timedelta
from typing import Annotated
import pandas as pd
SavePathType = Annotated[str, "File path to save data. If None, data is not saved."]
def save_output(data: pd.DataFrame, tag: str, save_path: SavePathType = None) -> None:

View File

@ -1,12 +1,14 @@
# gets data/stats
import yfinance as yf
from typing import Annotated, Callable, Any, Optional
from pandas import DataFrame
import pandas as pd
from collections.abc import Callable
from functools import wraps
from typing import Annotated, Any
from .utils import save_output, SavePathType, decorate_all_methods
import pandas as pd
import yfinance as yf
from pandas import DataFrame
from .utils import SavePathType, decorate_all_methods
def init_ticker(func: Callable) -> Callable:
@ -52,7 +54,7 @@ class YFinanceUtils:
def get_company_info(
symbol: Annotated[str, "ticker symbol"],
save_path: Optional[str] = None,
save_path: str | None = None,
) -> DataFrame:
"""Fetches and returns company information as a DataFrame."""
ticker = symbol
@ -72,7 +74,7 @@ class YFinanceUtils:
def get_stock_dividends(
symbol: Annotated[str, "ticker symbol"],
save_path: Optional[str] = None,
save_path: str | None = None,
) -> DataFrame:
"""Fetches and returns the latest dividends data as a DataFrame."""
ticker = symbol

View File

@ -1,11 +1,11 @@
# TradingAgents/graph/__init__.py
from .trading_graph import TradingAgentsGraph
from .conditional_logic import ConditionalLogic
from .setup import GraphSetup
from .propagation import Propagator
from .reflection import Reflector
from .setup import GraphSetup
from .signal_processing import SignalProcessor
from .trading_graph import TradingAgentsGraph
__all__ = [
"TradingAgentsGraph",

View File

@ -1,8 +1,8 @@
# TradingAgents/graph/propagation.py
from typing import Dict, Any
from typing import Any
from tradingagents.agents.utils.agent_states import (
AgentState,
InvestDebateState,
RiskDebateState,
)
@ -17,7 +17,7 @@ class Propagator:
def create_initial_state(
self, company_name: str, trade_date: str
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""Create the initial state for the agent graph."""
return {
"messages": [("human", company_name)],
@ -41,7 +41,7 @@ class Propagator:
"news_report": "",
}
def get_graph_args(self) -> Dict[str, Any]:
def get_graph_args(self) -> dict[str, Any]:
"""Get arguments for the graph invocation."""
return {
"stream_mode": "values",

View File

@ -1,6 +1,7 @@
# TradingAgents/graph/reflection.py
from typing import Dict, Any
from typing import Any
from langchain_openai import ChatOpenAI
@ -46,7 +47,7 @@ Your goal is to deliver detailed insights into investment decisions and highligh
Adhere strictly to these instructions, and ensure your output is detailed, accurate, and actionable. You will also be given objective descriptions of the market from a price movements, technical indicator, news, and sentiment perspective to provide more context for your analysis.
"""
def _extract_current_situation(self, current_state: Dict[str, Any]) -> str:
def _extract_current_situation(self, current_state: dict[str, Any]) -> str:
"""Extract the current market situation from the state."""
curr_market_report = current_state["market_report"]
curr_sentiment_report = current_state["sentiment_report"]

View File

@ -1,8 +1,7 @@
# TradingAgents/graph/setup.py
from typing import Dict, Any
from langchain_openai import ChatOpenAI
from langgraph.graph import END, StateGraph, START
from langgraph.graph import END, START, StateGraph
from langgraph.prebuilt import ToolNode
from tradingagents.agents import *
@ -20,7 +19,7 @@ class GraphSetup:
quick_thinking_llm: ChatOpenAI,
deep_thinking_llm: ChatOpenAI,
toolkit: Toolkit,
tool_nodes: Dict[str, ToolNode],
tool_nodes: dict[str, ToolNode],
bull_memory,
bear_memory,
trader_memory,

View File

@ -1,31 +1,24 @@
# TradingAgents/graph/trading_graph.py
import json
import os
from pathlib import Path
import json
from datetime import date
from typing import Dict, Any, Tuple, List, Optional
from typing import Any
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import ToolNode
from tradingagents.agents import *
from tradingagents.default_config import DEFAULT_CONFIG
from tradingagents.agents.utils.memory import FinancialSituationMemory
from tradingagents.agents.utils.agent_states import (
AgentState,
InvestDebateState,
RiskDebateState,
)
from tradingagents.dataflows.interface import set_config
from tradingagents.default_config import DEFAULT_CONFIG
from .conditional_logic import ConditionalLogic
from .setup import GraphSetup
from .propagation import Propagator
from .reflection import Reflector
from .setup import GraphSetup
from .signal_processing import SignalProcessor
@ -36,7 +29,7 @@ class TradingAgentsGraph:
self,
selected_analysts=["market", "social", "news", "fundamentals"],
debug=False,
config: Dict[str, Any] = None,
config: dict[str, Any] = None,
):
"""Initialize the trading agents graph and components.
@ -109,7 +102,7 @@ class TradingAgentsGraph:
# Set up the graph
self.graph = self.graph_setup.setup_graph(selected_analysts)
def _create_tool_nodes(self) -> Dict[str, ToolNode]:
def _create_tool_nodes(self) -> dict[str, ToolNode]:
"""Create tool nodes for different data sources."""
return {
"market": ToolNode(

102
uv.lock
View File

@ -461,6 +461,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" },
]
[[package]]
name = "cfgv"
version = "3.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" },
]
[[package]]
name = "chainlit"
version = "2.5.5"
@ -782,6 +791,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" },
]
[[package]]
name = "distlib"
version = "0.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
]
[[package]]
name = "distro"
version = "1.9.0"
@ -1385,6 +1403,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" },
]
[[package]]
name = "identify"
version = "2.6.13"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/ca/ffbabe3635bb839aa36b3a893c91a9b0d368cb4d8073e03a12896970af82/identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32", size = 99243, upload-time = "2025-08-09T19:35:00.6Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e7/ce/461b60a3ee109518c055953729bf9ed089a04db895d47e95444071dcdef2/identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b", size = 99153, upload-time = "2025-08-09T19:34:59.1Z" },
]
[[package]]
name = "idna"
version = "3.10"
@ -1831,7 +1858,7 @@ name = "langgraph-checkpoint"
version = "2.0.26"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "langchain-core", marker = "python_full_version < '4.0'" },
{ name = "langchain-core", marker = "python_full_version < '4'" },
{ name = "ormsgpack" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c5/61/e2518ac9216a4e9f4efda3ac61595e3c9e9ac00833141c9688e8d56bd7eb/langgraph_checkpoint-2.0.26.tar.gz", hash = "sha256:2b800195532d5efb079db9754f037281225ae175f7a395523f4bf41223cbc9d6", size = 37874, upload-time = "2025-05-15T17:31:22.466Z" }
@ -2376,6 +2403,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" },
]
[[package]]
name = "nodeenv"
version = "1.9.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
]
[[package]]
name = "numpy"
version = "2.2.6"
@ -3564,6 +3600,22 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/96/5c/8af904314e42d5401afcfaff69940dc448e974f80f7aa39b241a4fbf0cf1/prawcore-2.4.0-py3-none-any.whl", hash = "sha256:29af5da58d85704b439ad3c820873ad541f4535e00bb98c66f0fbcc8c603065a", size = 17203, upload-time = "2023-10-01T23:30:47.651Z" },
]
[[package]]
name = "pre-commit"
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cfgv" },
{ name = "identify" },
{ name = "nodeenv" },
{ name = "pyyaml" },
{ name = "virtualenv" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" },
]
[[package]]
name = "prompt-toolkit"
version = "3.0.51"
@ -4278,6 +4330,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" },
]
[[package]]
name = "ruff"
version = "0.12.8"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4b/da/5bd7565be729e86e1442dad2c9a364ceeff82227c2dece7c29697a9795eb/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033", size = 5242373, upload-time = "2025-08-07T19:05:47.268Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c9/1e/c843bfa8ad1114fab3eb2b78235dda76acd66384c663a4e0415ecc13aa1e/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513", size = 11675315, upload-time = "2025-08-07T19:05:06.15Z" },
{ url = "https://files.pythonhosted.org/packages/24/ee/af6e5c2a8ca3a81676d5480a1025494fd104b8896266502bb4de2a0e8388/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc", size = 12456653, upload-time = "2025-08-07T19:05:09.759Z" },
{ url = "https://files.pythonhosted.org/packages/99/9d/e91f84dfe3866fa648c10512904991ecc326fd0b66578b324ee6ecb8f725/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec", size = 11659690, upload-time = "2025-08-07T19:05:12.551Z" },
{ url = "https://files.pythonhosted.org/packages/fe/ac/a363d25ec53040408ebdd4efcee929d48547665858ede0505d1d8041b2e5/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53", size = 11896923, upload-time = "2025-08-07T19:05:14.821Z" },
{ url = "https://files.pythonhosted.org/packages/58/9f/ea356cd87c395f6ade9bb81365bd909ff60860975ca1bc39f0e59de3da37/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f", size = 11477612, upload-time = "2025-08-07T19:05:16.712Z" },
{ url = "https://files.pythonhosted.org/packages/1a/46/92e8fa3c9dcfd49175225c09053916cb97bb7204f9f899c2f2baca69e450/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f", size = 13182745, upload-time = "2025-08-07T19:05:18.709Z" },
{ url = "https://files.pythonhosted.org/packages/5e/c4/f2176a310f26e6160deaf661ef60db6c3bb62b7a35e57ae28f27a09a7d63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609", size = 14206885, upload-time = "2025-08-07T19:05:21.025Z" },
{ url = "https://files.pythonhosted.org/packages/87/9d/98e162f3eeeb6689acbedbae5050b4b3220754554526c50c292b611d3a63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a", size = 13639381, upload-time = "2025-08-07T19:05:23.423Z" },
{ url = "https://files.pythonhosted.org/packages/81/4e/1b7478b072fcde5161b48f64774d6edd59d6d198e4ba8918d9f4702b8043/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb", size = 12613271, upload-time = "2025-08-07T19:05:25.507Z" },
{ url = "https://files.pythonhosted.org/packages/e8/67/0c3c9179a3ad19791ef1b8f7138aa27d4578c78700551c60d9260b2c660d/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818", size = 12847783, upload-time = "2025-08-07T19:05:28.14Z" },
{ url = "https://files.pythonhosted.org/packages/4e/2a/0b6ac3dd045acf8aa229b12c9c17bb35508191b71a14904baf99573a21bd/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea", size = 11702672, upload-time = "2025-08-07T19:05:30.413Z" },
{ url = "https://files.pythonhosted.org/packages/9d/ee/f9fdc9f341b0430110de8b39a6ee5fa68c5706dc7c0aa940817947d6937e/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3", size = 11440626, upload-time = "2025-08-07T19:05:32.492Z" },
{ url = "https://files.pythonhosted.org/packages/89/fb/b3aa2d482d05f44e4d197d1de5e3863feb13067b22c571b9561085c999dc/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161", size = 12462162, upload-time = "2025-08-07T19:05:34.449Z" },
{ url = "https://files.pythonhosted.org/packages/18/9f/5c5d93e1d00d854d5013c96e1a92c33b703a0332707a7cdbd0a4880a84fb/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46", size = 12913212, upload-time = "2025-08-07T19:05:36.541Z" },
{ url = "https://files.pythonhosted.org/packages/71/13/ab9120add1c0e4604c71bfc2e4ef7d63bebece0cfe617013da289539cef8/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3", size = 11694382, upload-time = "2025-08-07T19:05:38.468Z" },
{ url = "https://files.pythonhosted.org/packages/f6/dc/a2873b7c5001c62f46266685863bee2888caf469d1edac84bf3242074be2/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e", size = 12740482, upload-time = "2025-08-07T19:05:40.391Z" },
{ url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" },
]
[[package]]
name = "setuptools"
version = "80.9.0"
@ -4721,6 +4798,12 @@ dependencies = [
{ name = "yfinance" },
]
[package.optional-dependencies]
dev = [
{ name = "pre-commit" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "akshare", specifier = ">=1.16.98" },
@ -4738,11 +4821,13 @@ requires-dist = [
{ name = "pandas", specifier = ">=2.3.0" },
{ name = "parsel", specifier = ">=1.10.0" },
{ name = "praw", specifier = ">=7.8.1" },
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.5.0" },
{ name = "pytz", specifier = ">=2025.2" },
{ name = "questionary", specifier = ">=2.1.0" },
{ name = "redis", specifier = ">=6.2.0" },
{ name = "requests", specifier = ">=2.32.4" },
{ name = "rich", specifier = ">=14.0.0" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.6.9" },
{ name = "setuptools", specifier = ">=80.9.0" },
{ name = "stockstats", specifier = ">=0.6.5" },
{ name = "tqdm", specifier = ">=4.67.1" },
@ -4750,6 +4835,7 @@ requires-dist = [
{ name = "typing-extensions", specifier = ">=4.14.0" },
{ name = "yfinance", specifier = ">=0.2.63" },
]
provides-extras = ["dev"]
[[package]]
name = "tushare"
@ -4920,6 +5006,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018, upload-time = "2024-10-14T23:38:10.888Z" },
]
[[package]]
name = "virtualenv"
version = "20.33.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "distlib" },
{ name = "filelock" },
{ name = "platformdirs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8b/60/4f20960df6c7b363a18a55ab034c8f2bcd5d9770d1f94f9370ec104c1855/virtualenv-20.33.1.tar.gz", hash = "sha256:1b44478d9e261b3fb8baa5e74a0ca3bc0e05f21aa36167bf9cbf850e542765b8", size = 6082160, upload-time = "2025-08-05T16:10:55.605Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ca/ff/ded57ac5ff40a09e6e198550bab075d780941e0b0f83cbeabd087c59383a/virtualenv-20.33.1-py3-none-any.whl", hash = "sha256:07c19bc66c11acab6a5958b815cbcee30891cd1c2ccf53785a28651a0d8d8a67", size = 6060362, upload-time = "2025-08-05T16:10:52.81Z" },
]
[[package]]
name = "w3lib"
version = "2.3.1"