TradingAgents/cc_tools.py

325 lines
12 KiB
Python

#!/usr/bin/env python3
"""CLI bridge between Claude Code subagents and TradingAgents data/memory infrastructure.
Usage:
python cc_tools.py <command> [args...]
Data Commands:
get_stock_data <symbol> <start_date> <end_date>
get_indicators <symbol> <indicator> <curr_date> [look_back_days]
get_fundamentals <ticker> <curr_date>
get_balance_sheet <ticker> [freq] [curr_date]
get_cashflow <ticker> [freq] [curr_date]
get_income_statement <ticker> [freq] [curr_date]
get_news <ticker> <start_date> <end_date>
get_global_news <curr_date> [look_back_days] [limit]
get_insider_transactions <ticker>
Memory Commands:
memory_get <memory_name> <situation_file> [n_matches]
memory_add <memory_name> <situation_file> <advice_file>
memory_clear <memory_name>
Results Commands:
save_results <ticker> <trade_date> <state_json_file>
"""
import argparse
import json
import os
import sys
from pathlib import Path
def _init_config():
"""Initialize the data vendor configuration."""
from tradingagents.default_config import DEFAULT_CONFIG
from tradingagents.dataflows.config import set_config
set_config(DEFAULT_CONFIG)
# Create data cache directory
os.makedirs(
os.path.join(DEFAULT_CONFIG["project_dir"], "dataflows/data_cache"),
exist_ok=True,
)
def _route(method, *args, **kwargs):
"""Route a method call through the vendor interface."""
_init_config()
from tradingagents.dataflows.interface import route_to_vendor
return route_to_vendor(method, *args, **kwargs)
# --- Memory persistence helpers ---
MEMORY_DIR = Path("eval_results/.memory")
def _memory_path(name: str) -> Path:
return MEMORY_DIR / f"{name}.json"
def _load_memory(name: str):
"""Load a FinancialSituationMemory from disk, or create a fresh one."""
from tradingagents.agents.utils.memory import FinancialSituationMemory
mem = FinancialSituationMemory(name)
path = _memory_path(name)
if path.exists():
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
docs = data.get("documents", [])
recs = data.get("recommendations", [])
if docs and recs and len(docs) == len(recs):
mem.add_situations(list(zip(docs, recs)))
return mem
def _save_memory(name: str, mem):
"""Persist a FinancialSituationMemory to disk."""
MEMORY_DIR.mkdir(parents=True, exist_ok=True)
path = _memory_path(name)
data = {
"documents": mem.documents,
"recommendations": mem.recommendations,
}
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
# --- Command handlers ---
def cmd_get_stock_data(args):
result = _route("get_stock_data", args.symbol, args.start_date, args.end_date)
print(result)
def cmd_get_indicators(args):
look_back = int(args.look_back_days) if args.look_back_days else 30
result = _route("get_indicators", args.symbol, args.indicator, args.curr_date, look_back)
print(result)
def cmd_get_fundamentals(args):
result = _route("get_fundamentals", args.ticker, args.curr_date)
print(result)
def cmd_get_balance_sheet(args):
freq = args.freq or "quarterly"
curr_date = args.curr_date or None
result = _route("get_balance_sheet", args.ticker, freq, curr_date)
print(result)
def cmd_get_cashflow(args):
freq = args.freq or "quarterly"
curr_date = args.curr_date or None
result = _route("get_cashflow", args.ticker, freq, curr_date)
print(result)
def cmd_get_income_statement(args):
freq = args.freq or "quarterly"
curr_date = args.curr_date or None
result = _route("get_income_statement", args.ticker, freq, curr_date)
print(result)
def cmd_get_news(args):
result = _route("get_news", args.ticker, args.start_date, args.end_date)
print(result)
def cmd_get_global_news(args):
look_back = int(args.look_back_days) if args.look_back_days else 7
limit = int(args.limit) if args.limit else 5
result = _route("get_global_news", args.curr_date, look_back, limit)
print(result)
def cmd_get_insider_transactions(args):
result = _route("get_insider_transactions", args.ticker)
print(result)
def cmd_memory_get(args):
situation_text = Path(args.situation_file).read_text(encoding="utf-8")
n_matches = int(args.n_matches) if args.n_matches else 2
mem = _load_memory(args.memory_name)
results = mem.get_memories(situation_text, n_matches=n_matches)
if not results:
print("No past memories found.")
else:
for i, rec in enumerate(results, 1):
print(f"--- Memory Match {i} (score: {rec['similarity_score']:.2f}) ---")
print(rec["recommendation"])
print()
def cmd_memory_add(args):
situation_text = Path(args.situation_file).read_text(encoding="utf-8")
advice_text = Path(args.advice_file).read_text(encoding="utf-8")
mem = _load_memory(args.memory_name)
mem.add_situations([(situation_text, advice_text)])
_save_memory(args.memory_name, mem)
print(f"Memory added to '{args.memory_name}'. Total entries: {len(mem.documents)}")
def cmd_memory_clear(args):
path = _memory_path(args.memory_name)
if path.exists():
path.unlink()
print(f"Memory '{args.memory_name}' cleared.")
def cmd_save_results(args):
state_data = json.loads(Path(args.state_json_file).read_text(encoding="utf-8"))
log_entry = {
str(args.trade_date): {
"company_of_interest": state_data.get("company_of_interest", args.ticker),
"trade_date": args.trade_date,
"market_report": state_data.get("market_report", ""),
"sentiment_report": state_data.get("sentiment_report", ""),
"news_report": state_data.get("news_report", ""),
"fundamentals_report": state_data.get("fundamentals_report", ""),
"investment_debate_state": state_data.get("investment_debate_state", {}),
"trader_investment_decision": state_data.get("trader_investment_plan", ""),
"risk_debate_state": state_data.get("risk_debate_state", {}),
"investment_plan": state_data.get("investment_plan", ""),
"final_trade_decision": state_data.get("final_trade_decision", ""),
}
}
directory = Path(f"eval_results/{args.ticker}/TradingAgentsStrategy_logs/")
directory.mkdir(parents=True, exist_ok=True)
out_path = directory / f"full_states_log_{args.trade_date}.json"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(log_entry, f, indent=4, ensure_ascii=False)
print(f"Results saved to {out_path}")
# --- Argument parser ---
def build_parser():
parser = argparse.ArgumentParser(
description="TradingAgents CLI tools for Claude Code subagents",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# get_stock_data
p = subparsers.add_parser("get_stock_data", help="Get OHLCV stock price data")
p.add_argument("symbol", help="Ticker symbol (e.g., AAPL)")
p.add_argument("start_date", help="Start date (yyyy-mm-dd)")
p.add_argument("end_date", help="End date (yyyy-mm-dd)")
p.set_defaults(func=cmd_get_stock_data)
# get_indicators
p = subparsers.add_parser("get_indicators", help="Get technical indicators")
p.add_argument("symbol", help="Ticker symbol")
p.add_argument("indicator", help="Indicator name (e.g., rsi, macd)")
p.add_argument("curr_date", help="Current trading date (yyyy-mm-dd)")
p.add_argument("look_back_days", nargs="?", default=None, help="Lookback days (default: 30)")
p.set_defaults(func=cmd_get_indicators)
# get_fundamentals
p = subparsers.add_parser("get_fundamentals", help="Get company fundamentals")
p.add_argument("ticker", help="Ticker symbol")
p.add_argument("curr_date", help="Current date (yyyy-mm-dd)")
p.set_defaults(func=cmd_get_fundamentals)
# get_balance_sheet
p = subparsers.add_parser("get_balance_sheet", help="Get balance sheet data")
p.add_argument("ticker", help="Ticker symbol")
p.add_argument("freq", nargs="?", default=None, help="Frequency: annual/quarterly (default: quarterly)")
p.add_argument("curr_date", nargs="?", default=None, help="Current date (yyyy-mm-dd)")
p.set_defaults(func=cmd_get_balance_sheet)
# get_cashflow
p = subparsers.add_parser("get_cashflow", help="Get cash flow statement")
p.add_argument("ticker", help="Ticker symbol")
p.add_argument("freq", nargs="?", default=None, help="Frequency: annual/quarterly (default: quarterly)")
p.add_argument("curr_date", nargs="?", default=None, help="Current date (yyyy-mm-dd)")
p.set_defaults(func=cmd_get_cashflow)
# get_income_statement
p = subparsers.add_parser("get_income_statement", help="Get income statement")
p.add_argument("ticker", help="Ticker symbol")
p.add_argument("freq", nargs="?", default=None, help="Frequency: annual/quarterly (default: quarterly)")
p.add_argument("curr_date", nargs="?", default=None, help="Current date (yyyy-mm-dd)")
p.set_defaults(func=cmd_get_income_statement)
# get_news
p = subparsers.add_parser("get_news", help="Get company news")
p.add_argument("ticker", help="Ticker symbol")
p.add_argument("start_date", help="Start date (yyyy-mm-dd)")
p.add_argument("end_date", help="End date (yyyy-mm-dd)")
p.set_defaults(func=cmd_get_news)
# get_global_news
p = subparsers.add_parser("get_global_news", help="Get global macroeconomic news")
p.add_argument("curr_date", help="Current date (yyyy-mm-dd)")
p.add_argument("look_back_days", nargs="?", default=None, help="Lookback days (default: 7)")
p.add_argument("limit", nargs="?", default=None, help="Max articles (default: 5)")
p.set_defaults(func=cmd_get_global_news)
# get_insider_transactions
p = subparsers.add_parser("get_insider_transactions", help="Get insider transactions")
p.add_argument("ticker", help="Ticker symbol")
p.set_defaults(func=cmd_get_insider_transactions)
# memory_get
p = subparsers.add_parser("memory_get", help="Retrieve memories matching a situation")
p.add_argument("memory_name", help="Memory name (e.g., bull_memory)")
p.add_argument("situation_file", help="Path to file containing the situation text")
p.add_argument("n_matches", nargs="?", default=None, help="Number of matches (default: 2)")
p.set_defaults(func=cmd_memory_get)
# memory_add
p = subparsers.add_parser("memory_add", help="Add a situation+advice to memory")
p.add_argument("memory_name", help="Memory name (e.g., bull_memory)")
p.add_argument("situation_file", help="Path to file containing the situation text")
p.add_argument("advice_file", help="Path to file containing the advice text")
p.set_defaults(func=cmd_memory_add)
# memory_clear
p = subparsers.add_parser("memory_clear", help="Clear all entries from a memory")
p.add_argument("memory_name", help="Memory name to clear")
p.set_defaults(func=cmd_memory_clear)
# save_results
p = subparsers.add_parser("save_results", help="Save analysis results to JSON log")
p.add_argument("ticker", help="Ticker symbol")
p.add_argument("trade_date", help="Trade date (yyyy-mm-dd)")
p.add_argument("state_json_file", help="Path to JSON file containing the full state")
p.set_defaults(func=cmd_save_results)
return parser
def main():
parser = build_parser()
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
try:
args.func(args)
except Exception as e:
print(f"ERROR: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()