feat(portfolio): add end-to-end auto and check-portfolio commands
This commit is contained in:
parent
d7a932bc83
commit
1d589c0621
91
cli/main.py
91
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]")
|
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."""
|
"""Full pipeline: scan -> filter -> per-ticker deep dive."""
|
||||||
import asyncio
|
import asyncio
|
||||||
from tradingagents.pipeline.macro_bridge import (
|
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"))
|
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)
|
macro_path = Path(macro_output)
|
||||||
if not macro_path.exists():
|
if not macro_path.exists():
|
||||||
console.print(f"[red]File not found: {macro_path}[/red]")
|
console.print(f"[red]File not found: {macro_path}[/red]")
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
|
|
||||||
min_conviction = typer.prompt("Minimum conviction (high/medium/low)", default="medium")
|
if min_conviction_opt is None:
|
||||||
tickers_input = typer.prompt("Specific tickers (comma-separated, or blank for all)", default="")
|
min_conviction = typer.prompt("Minimum conviction (high/medium/low)", default="medium")
|
||||||
ticker_filter = [t.strip() for t in tickers_input.split(",") if t.strip()] or None
|
else:
|
||||||
analysis_date = typer.prompt("Analysis date", default=datetime.datetime.now().strftime("%Y-%m-%d"))
|
min_conviction = min_conviction_opt
|
||||||
dry_run = typer.confirm("Dry run (no API calls)?", default=False)
|
|
||||||
|
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
|
# Parse macro output
|
||||||
macro_context, all_candidates = parse_macro_output(macro_path)
|
macro_context, all_candidates = parse_macro_output(macro_path)
|
||||||
|
|
@ -1490,5 +1515,57 @@ def portfolio():
|
||||||
run_portfolio(portfolio_id, date, macro_path)
|
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__":
|
if __name__ == "__main__":
|
||||||
app()
|
app()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue