feat: optimize strategy generator (cache OHLCV, min_sharpe 1.5, predix generate-strategies CLI)

This commit is contained in:
TPTBusiness
2026-05-03 21:58:07 +02:00
parent cc265d6045
commit 982b902b1a
3 changed files with 91 additions and 1 deletions
+84
View File
@@ -1467,6 +1467,90 @@ def build_strategies_ai(
console.print(traceback.format_exc())
@app.command()
def generate_strategies(
count: int = typer.Option(10, "--count", "-n", help="Number of strategies to generate"),
workers: int = typer.Option(2, "--workers", "-w", help="Parallel workers"),
style: str = typer.Option("swing", "--style", "-s", help="Trading style: daytrading or swing"),
optuna: bool = typer.Option(True, "--optuna/--no-optuna", help="Enable Optuna optimization"),
optuna_trials: int = typer.Option(30, "--optuna-trials", help="Number of Optuna trials per strategy"),
top_factors: int = typer.Option(20, "--top-factors", help="Number of top factors to consider"),
min_sharpe: float = typer.Option(1.5, "--min-sharpe", help="Minimum Sharpe for acceptance"),
max_drawdown: float = typer.Option(-0.30, "--max-dd", help="Maximum drawdown allowed"),
min_win_rate: float = typer.Option(0.40, "--min-winrate", help="Minimum win rate for acceptance"),
):
"""
Generate trading strategies from top factors using LLM + Optuna optimization.
Loads top evaluated factors, uses LLM to generate strategy code,
evaluates with real EUR/USD OHLCV backtest (2.26M 1min bars),
and optimizes hyperparameters with Optuna (3-stage: 10→15→5 trials).
Uses the verified backtest engine (Sharpe on strategy returns,
MaxDD on equity curve, WinRate on trade P&L) with runtime verification.
Examples:
$ predix generate-strategies # 10 strategies, Optuna, swing
$ predix generate-strategies -n 20 -w 4 # 20 strategies, 4 workers
$ predix generate-strategies --min-sharpe 3.0 # Stricter acceptance
$ predix generate-strategies -s daytrading # Day trading style
$ predix generate-strategies --no-optuna # Skip optimization
"""
from rich.console import Console as RichConsole
from rich.table import Table as RichTable
console.print(f"\n[bold cyan]{'='*60}[/bold cyan]")
console.print("[bold cyan] Predix Strategy Generator[/bold cyan]")
console.print(f"[bold cyan]{'='*60}[/bold cyan]")
console.print(f" Strategies: [cyan]{count}[/cyan] Workers: [cyan]{workers}[/cyan] Style: [cyan]{style}[/cyan]")
console.print(f" Optuna: {'[green]Yes[/green]' if optuna else '[yellow]No[/yellow]'} (trials={optuna_trials}) Factors: [cyan]{top_factors}[/cyan]")
console.print(f" Accept: Sharpe≥[green]{min_sharpe}[/green] DD≥[green]{max_drawdown}[/green] WR≥[green]{min_win_rate}[/green]")
console.print(f"[bold cyan]{'='*60}[/bold cyan]\n")
try:
from rdagent.scenarios.qlib.local.strategy_orchestrator import StrategyOrchestrator
orchestrator = StrategyOrchestrator(
top_factors=top_factors,
trading_style=style,
min_sharpe=min_sharpe,
max_drawdown=max_drawdown,
min_win_rate=min_win_rate,
use_optuna=optuna,
optuna_trials=optuna_trials,
continuous_optimization=optuna,
)
results = orchestrator.generate_strategies(count=count, workers=workers)
accepted = [r for r in results if r.get("status") == "success"]
rejected = len(results) - len(accepted)
console.print(f"\n[bold green]✓ {len(accepted)} accepted[/bold green] [yellow]{rejected} rejected[/yellow]")
if accepted:
accepted.sort(key=lambda r: r.get("sharpe_ratio", 0), reverse=True)
table = RichTable(title="Top Generated Strategies", show_header=True, header_style="bold cyan")
table.add_column("#", width=4)
table.add_column("Strategy", width=30)
table.add_column("Sharpe", width=8, justify="right")
table.add_column("MaxDD", width=8, justify="right")
table.add_column("WinRate", width=8, justify="right")
table.add_column("Trades", width=7, justify="right")
for i, r in enumerate(accepted[:10], 1):
table.add_row(
str(i), r.get("strategy_name", "?")[:28],
f"{r.get('sharpe_ratio', 0):.2f}", f"{r.get('max_drawdown', 0):.1%}",
f"{r.get('win_rate', 0):.1%}", str(r.get('num_trades', '?')),
)
console.print(table)
except ImportError as e:
console.print(f"[yellow]Strategy generator not available: {e}[/yellow]")
except Exception as e:
console.print(f"[bold red]❌ {e}[/bold red]")
@app.command()
def health():
"""Check system health and configuration status.
+6
View File
@@ -617,6 +617,9 @@ def generate_strategies_cli(
top_factors: int = typer.Option(20, "--top-factors", help="Number of top factors to consider"),
continuous: bool = typer.Option(True, "--continuous/--single-pass", help="Optimize ALL strategies including rejected ones"),
max_iterations: int = typer.Option(1, "--max-iterations", "-i", help="Number of generation-optimization cycles (1 = single pass, >1 = continuous)"),
min_sharpe: float = typer.Option(1.5, "--min-sharpe", help="Minimum Sharpe ratio for acceptance"),
max_drawdown: float = typer.Option(-0.30, "--max-dd", help="Maximum drawdown allowed"),
min_win_rate: float = typer.Option(0.40, "--min-winrate", help="Minimum win rate for acceptance"),
):
"""
Generate trading strategies from evaluated factors.
@@ -698,6 +701,9 @@ def generate_strategies_cli(
orchestrator = StrategyOrchestrator(
top_factors=top_factors,
trading_style=style,
min_sharpe=min_sharpe,
max_drawdown=max_drawdown,
min_win_rate=min_win_rate,
use_optuna=optuna,
optuna_trials=optuna_trials,
continuous_optimization=continuous,
+1 -1
View File
@@ -341,7 +341,7 @@ class QuantRDLoop(RDLoop):
orchestrator = StrategyOrchestrator(
top_factors=20,
trading_style="swing",
min_sharpe=0.5,
min_sharpe=1.5,
max_drawdown=-0.20,
min_win_rate=0.40,
use_optuna=True,