From 1d589c0621e194e8ba72f69f182f208ee2391051 Mon Sep 17 00:00:00 2001 From: Ahmet Guzererler Date: Sat, 21 Mar 2026 03:18:20 +0100 Subject: [PATCH] feat(portfolio): add end-to-end auto and check-portfolio commands --- cli/main.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 7 deletions(-) diff --git a/cli/main.py b/cli/main.py index b763bb41..f6c57782 100644 --- a/cli/main.py +++ b/cli/main.py @@ -1301,7 +1301,13 @@ def run_scan(date: Optional[str] = None): console.print(f"\n[green]Results saved to {save_dir}[/green]") -def run_pipeline(): +def run_pipeline( + macro_path_str: Optional[str] = None, + min_conviction_opt: Optional[str] = None, + ticker_filter_list: Optional[list[str]] = None, + analysis_date_opt: Optional[str] = None, + dry_run_opt: Optional[bool] = None, +): """Full pipeline: scan -> filter -> per-ticker deep dive.""" import asyncio from tradingagents.pipeline.macro_bridge import ( @@ -1313,17 +1319,36 @@ def run_pipeline(): console.print(Panel("[bold green]Macro → TradingAgents Pipeline[/bold green]", border_style="green")) - macro_output = typer.prompt("Path to macro scan JSON") + if macro_path_str is None: + macro_output = typer.prompt("Path to macro scan JSON") + else: + macro_output = macro_path_str + macro_path = Path(macro_output) if not macro_path.exists(): console.print(f"[red]File not found: {macro_path}[/red]") raise typer.Exit(1) - min_conviction = typer.prompt("Minimum conviction (high/medium/low)", default="medium") - tickers_input = typer.prompt("Specific tickers (comma-separated, or blank for all)", default="") - ticker_filter = [t.strip() for t in tickers_input.split(",") if t.strip()] or None - analysis_date = typer.prompt("Analysis date", default=datetime.datetime.now().strftime("%Y-%m-%d")) - dry_run = typer.confirm("Dry run (no API calls)?", default=False) + if min_conviction_opt is None: + min_conviction = typer.prompt("Minimum conviction (high/medium/low)", default="medium") + else: + min_conviction = min_conviction_opt + + if ticker_filter_list is None: + tickers_input = typer.prompt("Specific tickers (comma-separated, or blank for all)", default="") + ticker_filter = [t.strip() for t in tickers_input.split(",") if t.strip()] or None + else: + ticker_filter = ticker_filter_list + + if analysis_date_opt is None: + analysis_date = typer.prompt("Analysis date", default=datetime.datetime.now().strftime("%Y-%m-%d")) + else: + analysis_date = analysis_date_opt + + if dry_run_opt is None: + dry_run = typer.confirm("Dry run (no API calls)?", default=False) + else: + dry_run = dry_run_opt # Parse macro output macro_context, all_candidates = parse_macro_output(macro_path) @@ -1490,5 +1515,57 @@ def portfolio(): run_portfolio(portfolio_id, date, macro_path) +@app.command(name="check-portfolio") +def check_portfolio( + portfolio_id: str = typer.Option("main_portfolio", "--portfolio-id", "-p", help="Portfolio ID"), + date: Optional[str] = typer.Option(None, "--date", "-d", help="Analysis date in YYYY-MM-DD format (default: today)"), +): + """Run Portfolio Manager to review current holdings only (no new candidates).""" + import json + import tempfile + + console.print(Panel("[bold green]Portfolio Manager: Holdings Review[/bold green]", border_style="green")) + if date is None: + date = datetime.datetime.now().strftime("%Y-%m-%d") + + # Create a dummy scan_summary with no candidates + dummy_scan = {"stocks_to_investigate": []} + with tempfile.NamedTemporaryFile("w+", delete=False, suffix=".json") as f: + json.dump(dummy_scan, f) + dummy_path = Path(f.name) + + try: + run_portfolio(portfolio_id, date, dummy_path) + finally: + dummy_path.unlink(missing_ok=True) + + +@app.command() +def auto( + portfolio_id: str = typer.Option("main_portfolio", "--portfolio-id", "-p", help="Portfolio ID"), + date: Optional[str] = typer.Option(None, "--date", "-d", help="Analysis date in YYYY-MM-DD format (default: today)"), +): + """Run end-to-end: scan -> pipeline -> portfolio manager.""" + console.print(Panel("[bold green]TradingAgents Auto Mode[/bold green]", border_style="green")) + if date is None: + date = datetime.datetime.now().strftime("%Y-%m-%d") + + console.print("\n[bold magenta]--- Step 1: Market Scan ---[/bold magenta]") + run_scan(date=date) + + console.print("\n[bold magenta]--- Step 2: Per-Ticker Pipeline ---[/bold magenta]") + macro_path = get_daily_dir(date) / "summary" / "scan_summary.json" + run_pipeline( + macro_path_str=str(macro_path), + min_conviction_opt="medium", + ticker_filter_list=None, + analysis_date_opt=date, + dry_run_opt=False + ) + + console.print("\n[bold magenta]--- Step 3: Portfolio Manager ---[/bold magenta]") + run_portfolio(portfolio_id, date, macro_path) + + if __name__ == "__main__": app()