mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-27 15:37:44 +00:00
feat: Add CLI model selection (local vs OpenRouter)
- Add --model/-m flag to select LLM backend * local: llama.cpp on localhost (default) * openrouter: Cloud models via OpenRouter API - Create predix.py as new CLI entry point with model selection - Add OPENROUTER_API_KEY and OPENROUTER_MODEL to .env - Add health and status commands to CLI - Update rdagent/app/cli.py with model selection logic Usage: predix quant # Local (default) predix quant -m openrouter # OpenRouter cloud predix quant -m local -d # Local + dashboard Tests: 93 passed
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Predix CLI - Wrapper for rdagent with LLM model selection.
|
||||
|
||||
Usage:
|
||||
predix quant # Local llama.cpp (default)
|
||||
predix quant --model local # Explicit local
|
||||
predix quant --model openrouter # OpenRouter cloud model
|
||||
predix quant -d # With web dashboard
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv(Path(__file__).parent / ".env")
|
||||
|
||||
import typer
|
||||
from rich.console import Console
|
||||
|
||||
app = typer.Typer(help="Predix - AI Quantitative Trading Agent")
|
||||
console = Console()
|
||||
|
||||
|
||||
@app.command()
|
||||
def quant(
|
||||
model: str = typer.Option(
|
||||
"local",
|
||||
"--model", "-m",
|
||||
help="LLM backend: 'local' (llama.cpp) or 'openrouter' (cloud)",
|
||||
),
|
||||
dashboard: bool = typer.Option(
|
||||
False,
|
||||
"--dashboard/-d",
|
||||
help="Start web dashboard",
|
||||
),
|
||||
cli_dashboard: bool = typer.Option(
|
||||
False,
|
||||
"--cli-dashboard/-c",
|
||||
help="Start CLI dashboard",
|
||||
),
|
||||
step_n: int = typer.Option(None, help="Number of steps to run"),
|
||||
loop_n: int = typer.Option(None, help="Number of loops to run"),
|
||||
):
|
||||
"""
|
||||
Start EURUSD quantitative trading loop.
|
||||
|
||||
Examples:
|
||||
predix quant # Local llama.cpp
|
||||
predix quant -m openrouter # OpenRouter cloud model
|
||||
predix quant -d # With web dashboard
|
||||
predix quant -m openrouter -d # Both
|
||||
"""
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
# ---- LLM Model Selection ----
|
||||
if model == "openrouter":
|
||||
api_key = os.getenv("OPENROUTER_API_KEY", "")
|
||||
if not api_key:
|
||||
console.print("\n[bold red]❌ OPENROUTER_API_KEY not set in .env[/bold red]")
|
||||
console.print("[yellow]Add your API key to .env:[/yellow]")
|
||||
console.print(' OPENROUTER_API_KEY=sk-or-your-key-here')
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = api_key
|
||||
os.environ["OPENAI_API_BASE"] = "https://openrouter.ai/api/v1"
|
||||
os.environ["CHAT_MODEL"] = os.getenv("OPENROUTER_MODEL", "openrouter/google/gemini-2.0-flash:free")
|
||||
|
||||
console.print(f"\n[bold blue]🌐 Using OpenRouter:[/bold blue] [cyan]{os.environ['CHAT_MODEL']}[/cyan]")
|
||||
elif model == "local":
|
||||
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY", "local")
|
||||
os.environ["OPENAI_API_BASE"] = os.getenv("OPENAI_API_BASE", "http://localhost:8081/v1")
|
||||
os.environ["CHAT_MODEL"] = os.getenv("CHAT_MODEL", "openai/qwen3.5-35b")
|
||||
|
||||
console.print(f"\n[bold green]🏠 Using local LLM:[/bold green] [cyan]{os.environ['CHAT_MODEL']}[/cyan]")
|
||||
console.print(f" [dim]Base: {os.environ['OPENAI_API_BASE']}[/dim]")
|
||||
else:
|
||||
console.print(f"\n[yellow]⚠️ Unknown model: '{model}'. Using .env settings.[/yellow]")
|
||||
|
||||
# ---- Dashboards ----
|
||||
if dashboard:
|
||||
def start_web_dashboard():
|
||||
console.print(f"\n[bold green]🚀 Web Dashboard: http://localhost:5000[/bold green]")
|
||||
subprocess.run(
|
||||
["python", "web/dashboard_api.py"],
|
||||
cwd=str(Path(__file__).parent),
|
||||
env={**os.environ, "FLASK_ENV": "development"},
|
||||
)
|
||||
|
||||
threading.Thread(target=start_web_dashboard, daemon=True).start()
|
||||
time.sleep(2)
|
||||
|
||||
if cli_dashboard:
|
||||
def start_cli_dash():
|
||||
from rdagent.log.ui.predix_dashboard import run_dashboard
|
||||
run_dashboard(log_path="fin_quant.log", refresh_interval=3)
|
||||
|
||||
threading.Thread(target=start_cli_dash, daemon=True).start()
|
||||
time.sleep(1)
|
||||
|
||||
# ---- Start fin_quant ----
|
||||
from rdagent.app.qlib_rd_loop.quant import main as fin_quant
|
||||
|
||||
console.print(f"\n[bold cyan]📊 Starting EURUSD Trading Loop...[/bold cyan]\n")
|
||||
|
||||
fin_quant(
|
||||
step_n=step_n,
|
||||
loop_n=loop_n,
|
||||
)
|
||||
|
||||
|
||||
@app.command()
|
||||
def health():
|
||||
"""Check system health and configuration."""
|
||||
from rdagent.app.utils.health_check import health_check
|
||||
health_check()
|
||||
|
||||
|
||||
@app.command()
|
||||
def status():
|
||||
"""Show current trading loop status."""
|
||||
import sqlite3
|
||||
|
||||
# Process check
|
||||
result = subprocess.run(
|
||||
["pgrep", "-f", "fin_quant"],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
if result.returncode == 0:
|
||||
console.print("[bold green]✅ Trading Loop: RUNNING[/bold green]")
|
||||
else:
|
||||
console.print("[bold yellow]⏸️ Trading Loop: STOPPED[/bold yellow]")
|
||||
|
||||
# DB stats
|
||||
db_path = Path(__file__).parent / "results" / "db" / "backtest_results.db"
|
||||
if db_path.exists():
|
||||
conn = sqlite3.connect(str(db_path))
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT COUNT(*) FROM backtest_runs")
|
||||
runs = c.fetchone()[0]
|
||||
c.execute("SELECT COUNT(*) FROM factors")
|
||||
factors = c.fetchone()[0]
|
||||
conn.close()
|
||||
|
||||
console.print(f"\n📊 Results:")
|
||||
console.print(f" Backtest runs: {runs}")
|
||||
console.print(f" Factors: {factors}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
+54
-11
@@ -114,24 +114,67 @@ def fin_quant_cli(
|
||||
with_dashboard: bool = typer.Option(False, "--with-dashboard/-d", help="Start web dashboard automatically"),
|
||||
with_cli_dashboard: bool = typer.Option(False, "--cli-dashboard/-c", help="Show beautiful CLI dashboard"),
|
||||
dashboard_port: int = typer.Option(5000, "--dashboard-port", help="Dashboard port"),
|
||||
model: str = typer.Option(
|
||||
"local",
|
||||
"--model",
|
||||
"-m",
|
||||
help="LLM backend to use: 'local' (llama.cpp), 'openrouter' (cloud models), or custom env var prefix",
|
||||
),
|
||||
):
|
||||
"""
|
||||
Start EURUSD quantitative trading loop.
|
||||
|
||||
|
||||
Options:
|
||||
--with-dashboard/-d: Start web dashboard at http://localhost:5000
|
||||
--cli-dashboard/-c: Show beautiful terminal UI with live stats
|
||||
|
||||
--model/-m: LLM backend ('local' | 'openrouter')
|
||||
|
||||
Examples:
|
||||
rdagent fin_quant
|
||||
rdagent fin_quant -d # Web dashboard
|
||||
rdagent fin_quant -c # CLI dashboard
|
||||
rdagent fin_quant -d -c # Both dashboards
|
||||
rdagent fin_quant # Local llama.cpp (default)
|
||||
rdagent fin_quant -m local # Explicit local
|
||||
rdagent fin_quant -m openrouter # Use OpenRouter model
|
||||
rdagent fin_quant -d # Web dashboard
|
||||
rdagent fin_quant -d -c # Both dashboards
|
||||
|
||||
OpenRouter Setup:
|
||||
1. Set OPENROUTER_API_KEY in .env
|
||||
2. Set OPENROUTER_MODEL (default: openrouter/google/gemini-2.0-flash:free)
|
||||
3. Run: rdagent fin_quant -m openrouter
|
||||
"""
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
|
||||
# ---- LLM Model Selection ----
|
||||
if model == "openrouter":
|
||||
api_key = os.getenv("OPENROUTER_API_KEY", "")
|
||||
if not api_key:
|
||||
console.print("\n[bold red]❌ OPENROUTER_API_KEY not set in .env[/bold red]")
|
||||
console.print("[yellow]Add your API key to .env and retry:[/yellow]")
|
||||
console.print(' OPENROUTER_API_KEY=sk-or-your-key-here')
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = api_key
|
||||
os.environ["OPENAI_API_BASE"] = "https://openrouter.ai/api/v1"
|
||||
os.environ["CHAT_MODEL"] = os.getenv("OPENROUTER_MODEL", "openrouter/google/gemini-2.0-flash:free")
|
||||
|
||||
console.print(f"\n[bold blue]🌐 Using OpenRouter model:[/bold blue] [cyan]{os.environ['CHAT_MODEL']}[/cyan]")
|
||||
elif model == "local":
|
||||
# Ensure local defaults are set
|
||||
if not os.getenv("OPENAI_API_BASE"):
|
||||
os.environ["OPENAI_API_BASE"] = "http://localhost:8081/v1"
|
||||
if not os.getenv("CHAT_MODEL"):
|
||||
os.environ["CHAT_MODEL"] = "openai/qwen3.5-35b"
|
||||
|
||||
console.print(f"\n[bold green]🏠 Using local LLM:[/bold green] [cyan]{os.environ['CHAT_MODEL']}[/cyan]")
|
||||
console.print(f" [dim]Base URL: {os.environ['OPENAI_API_BASE']}[/dim]")
|
||||
else:
|
||||
console.print(f"\n[yellow]⚠️ Unknown model backend: '{model}'. Using current .env settings.[/yellow]")
|
||||
|
||||
# Start Web Dashboard wenn gewünscht
|
||||
if with_dashboard:
|
||||
def start_web_dashboard():
|
||||
@@ -143,21 +186,21 @@ def fin_quant_cli(
|
||||
cwd=str(Path(__file__).parent.parent.parent),
|
||||
env={**os.environ, "FLASK_ENV": "development"}
|
||||
)
|
||||
|
||||
|
||||
dashboard_thread = threading.Thread(target=start_web_dashboard, daemon=True)
|
||||
dashboard_thread.start()
|
||||
time.sleep(2)
|
||||
|
||||
|
||||
# Start CLI Dashboard wenn gewünscht
|
||||
if with_cli_dashboard:
|
||||
def start_cli_dash():
|
||||
from rdagent.log.ui.predix_dashboard import run_dashboard
|
||||
run_dashboard(log_path="fin_quant.log", refresh_interval=3)
|
||||
|
||||
|
||||
cli_thread = threading.Thread(target=start_cli_dash, daemon=True)
|
||||
cli_thread.start()
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
# Fin Quant starten
|
||||
fin_quant(path=path, step_n=step_n, loop_n=loop_n, all_duration=all_duration, checkout=checkout)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user