TradingAgents/centillion_run.py

236 lines
6.3 KiB
Python

"""
Centillion Investment Partners - Portfolio Analysis Runner
Runs the TradingAgents framework across all portfolio + watchlist tickers
and outputs buy/sell/hold recommendations.
Usage:
# Analyze full portfolio + watchlist
python centillion_run.py
# Analyze a single ticker
python centillion_run.py --ticker CVLT
# Analyze only the watchlist
python centillion_run.py --watchlist-only
# Analyze only US names
python centillion_run.py --region us
# Custom analysis date
python centillion_run.py --date 2025-03-21
"""
import argparse
import json
import os
import sys
from datetime import datetime, timedelta
from dotenv import load_dotenv
load_dotenv()
from centillion_config import (
ALL_PORTFOLIO,
ALL_TICKERS,
ALL_WATCHLIST,
CENTILLION_CONFIG,
EU_PORTFOLIO,
EU_WATCHLIST,
US_PORTFOLIO,
US_WATCHLIST,
)
from tradingagents.graph.trading_graph import TradingAgentsGraph
def parse_args():
parser = argparse.ArgumentParser(
description="Centillion Investment Partners - Trading Agent Analysis"
)
parser.add_argument("--ticker", type=str, help="Analyze a single ticker")
parser.add_argument(
"--watchlist-only", action="store_true", help="Only analyze watchlist"
)
parser.add_argument(
"--portfolio-only", action="store_true", help="Only analyze current holdings"
)
parser.add_argument(
"--region",
choices=["us", "eu", "all"],
default="all",
help="Region filter (default: all)",
)
parser.add_argument(
"--date",
type=str,
default=None,
help="Analysis date in YYYY-MM-DD format (default: yesterday)",
)
parser.add_argument(
"--output",
type=str,
default=None,
help="Output file path for JSON results",
)
parser.add_argument(
"--debug", action="store_true", help="Enable debug output from agents"
)
return parser.parse_args()
def get_tickers(args):
"""Determine which tickers to analyze based on flags."""
if args.ticker:
return [args.ticker.upper()]
region = args.region
if args.portfolio_only:
if region == "us":
return US_PORTFOLIO
elif region == "eu":
return EU_PORTFOLIO
return ALL_PORTFOLIO
if args.watchlist_only:
if region == "us":
return US_WATCHLIST
elif region == "eu":
return EU_WATCHLIST
return ALL_WATCHLIST
# Default: everything
if region == "us":
return US_PORTFOLIO + US_WATCHLIST
elif region == "eu":
return EU_PORTFOLIO + EU_WATCHLIST
return ALL_TICKERS
def get_analysis_date(args):
"""Get the analysis date — defaults to yesterday (last trading day approximation)."""
if args.date:
return args.date
yesterday = datetime.now() - timedelta(days=1)
# Skip weekends
if yesterday.weekday() == 6: # Sunday
yesterday -= timedelta(days=2)
elif yesterday.weekday() == 5: # Saturday
yesterday -= timedelta(days=1)
return yesterday.strftime("%Y-%m-%d")
def run_analysis(tickers, date, debug=False):
"""Run the trading agents analysis on each ticker."""
ta = TradingAgentsGraph(debug=debug, config=CENTILLION_CONFIG)
results = {}
total = len(tickers)
for i, ticker in enumerate(tickers, 1):
print(f"\n{'='*60}")
print(f" [{i}/{total}] Analyzing {ticker} as of {date}")
print(f"{'='*60}\n")
try:
_, decision = ta.propagate(ticker, date)
results[ticker] = {
"decision": decision,
"date": date,
"status": "success",
"in_portfolio": ticker in ALL_PORTFOLIO,
}
# Print summary
print(f"\n >> {ticker}: {_extract_signal(decision)}")
except Exception as e:
print(f"\n >> {ticker}: ERROR - {e}")
results[ticker] = {
"decision": str(e),
"date": date,
"status": "error",
"in_portfolio": ticker in ALL_PORTFOLIO,
}
return results
def _extract_signal(decision_text):
"""Extract the BUY/SELL/HOLD signal from the decision text."""
text_upper = decision_text.upper() if isinstance(decision_text, str) else ""
for signal in ["BUY", "SELL", "HOLD"]:
if signal in text_upper:
return signal
return "UNCLEAR"
def print_summary(results):
"""Print a clean summary table of all results."""
print(f"\n\n{'='*70}")
print(" CENTILLION INVESTMENT PARTNERS — ANALYSIS SUMMARY")
print(f"{'='*70}\n")
buys, sells, holds, errors = [], [], [], []
for ticker, res in results.items():
if res["status"] == "error":
errors.append(ticker)
continue
signal = _extract_signal(res["decision"])
tag = " [HELD]" if res["in_portfolio"] else " [WATCH]"
if signal == "BUY":
buys.append(f" {ticker}{tag}")
elif signal == "SELL":
sells.append(f" {ticker}{tag}")
else:
holds.append(f" {ticker}{tag}")
if buys:
print("BUY signals:")
print("\n".join(buys))
if sells:
print("\nSELL signals:")
print("\n".join(sells))
if holds:
print("\nHOLD signals:")
print("\n".join(holds))
if errors:
print(f"\nErrors: {', '.join(errors)}")
print(f"\n{'='*70}\n")
def save_results(results, output_path):
"""Save results to JSON file."""
os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
with open(output_path, "w") as f:
json.dump(results, f, indent=2, default=str)
print(f"Results saved to {output_path}")
def main():
args = parse_args()
tickers = get_tickers(args)
date = get_analysis_date(args)
print(f"\n Centillion Investment Partners — TradingAgents Analysis")
print(f" Date: {date}")
print(f" Tickers ({len(tickers)}): {', '.join(tickers)}")
print(f" LLM: {CENTILLION_CONFIG['llm_provider']} / {CENTILLION_CONFIG['deep_think_llm']}")
print()
results = run_analysis(tickers, date, debug=args.debug)
print_summary(results)
# Save results
output_path = args.output or f"results/centillion_{date}.json"
save_results(results, output_path)
if __name__ == "__main__":
main()