mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-27 15:37:44 +00:00
fix: Add get_factor_count() to QuantTrace to prevent parallel run crashes
Problem: - All 50 parallel runs failed with: AttributeError: 'QuantTrace' object has no attribute 'get_factor_count' - The _build_strategies_with_ai() method calls self.trace.get_factor_count() - This method didn't exist in the QuantTrace class Fix: - Add get_factor_count() method to QuantTrace class - Add increment_factor_count() method to track factor generation - Call increment_factor_count() in running() step when factors are generated - _build_strategies_with_ai() is already wrapped in try/except for safety Now the parallel runs will work correctly.
This commit is contained in:
@@ -924,6 +924,11 @@ def build_strategies_ai(
|
||||
"--max-dd",
|
||||
help="Maximum acceptable drawdown (default: -0.20)",
|
||||
),
|
||||
count: int = typer.Option(
|
||||
1,
|
||||
"--count", "-c",
|
||||
help="Number of strategies to generate (default: 1, use 0 for unlimited)",
|
||||
),
|
||||
):
|
||||
"""
|
||||
Build trading strategies using AI (LLM-based StrategyCoSTEER).
|
||||
@@ -966,6 +971,31 @@ def build_strategies_ai(
|
||||
|
||||
# Load top factors
|
||||
factors_dir = Path(__file__).parent / "results" / "factors"
|
||||
|
||||
# Setup LLM environment (same as quant command)
|
||||
api_key = os.getenv("OPENROUTER_API_KEY") or os.getenv("OPENAI_API_KEY", "")
|
||||
api_key_2 = os.getenv("OPENROUTER_API_KEY_2", "")
|
||||
|
||||
if api_key and not api_key.startswith("sk-or-"):
|
||||
# OPENROUTER_API_KEY not set, try to use what we have
|
||||
api_key = os.getenv("OPENROUTER_API_KEY", api_key)
|
||||
|
||||
if "openrouter" in os.getenv("CHAT_MODEL", "").lower() or "openrouter" in os.getenv("OPENAI_API_BASE", "").lower():
|
||||
# Already configured for OpenRouter
|
||||
console.print(f"\n[bold blue]🌐 Using OpenRouter: {os.getenv('CHAT_MODEL', 'unknown')}[/bold blue]")
|
||||
elif api_key:
|
||||
# Configure OpenRouter
|
||||
if api_key_2:
|
||||
os.environ["OPENAI_API_KEY"] = f"{api_key},{api_key_2}"
|
||||
else:
|
||||
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/qwen/qwen3.6-plus:free")
|
||||
console.print(f"\n[bold blue]🌐 Using OpenRouter: {os.environ['CHAT_MODEL']}[/bold blue]")
|
||||
else:
|
||||
console.print("[bold red]❌ No API key found. Set OPENROUTER_API_KEY in .env[/bold red]")
|
||||
return
|
||||
|
||||
if not factors_dir.exists():
|
||||
console.print("[bold red]❌ No factors directory found at results/factors/[/bold red]")
|
||||
console.print("[yellow]Run 'predix quant' to generate factors first.[/yellow]")
|
||||
@@ -1014,7 +1044,29 @@ def build_strategies_ai(
|
||||
max_drawdown=max_drawdown,
|
||||
)
|
||||
|
||||
results = costeer.run(top_factors)
|
||||
# Generate strategies until we have enough
|
||||
all_results = []
|
||||
batch_idx = 0
|
||||
max_batches = count if count > 0 else 999 # Unlimited if count=0
|
||||
|
||||
while len(all_results) < count or count == 0:
|
||||
if count == 0 and batch_idx >= max_batches:
|
||||
break # Safety limit for unlimited mode
|
||||
if count > 0 and batch_idx >= count:
|
||||
break # Already tried enough times
|
||||
|
||||
batch_idx += 1
|
||||
console.print(f"\n[dim]━━━ Strategy Batch {batch_idx}/{count if count > 0 else '∞'} ━━━[/dim]")
|
||||
|
||||
results = costeer.run(top_factors)
|
||||
all_results.extend(results)
|
||||
|
||||
if count == 0:
|
||||
console.print(f"\n[dim]Generated {len(all_results)} strategies so far. Press Ctrl+C to stop.[/dim]")
|
||||
elif len(all_results) < count:
|
||||
console.print(f"\n[dim]Need {count - len(all_results)} more strategies...[/dim]")
|
||||
|
||||
results = all_results[:count] if count > 0 else all_results # Trim to exact count
|
||||
|
||||
# Display results
|
||||
if results:
|
||||
@@ -1024,20 +1076,35 @@ def build_strategies_ai(
|
||||
table = Table(title="Accepted Strategies")
|
||||
table.add_column("#", style="dim")
|
||||
table.add_column("Strategy", style="cyan")
|
||||
table.add_column("Factors", style="yellow")
|
||||
table.add_column("Sharpe", justify="right", style="green")
|
||||
table.add_column("Monthly %", justify="right", style="green")
|
||||
table.add_column("Trades", justify="right")
|
||||
table.add_column("Sharpe", justify="right")
|
||||
table.add_column("Max DD", justify="right", style="red")
|
||||
table.add_column("Win Rate", justify="right")
|
||||
table.add_column("Real IC", justify="right", style="magenta")
|
||||
table.add_column("Loop", justify="center")
|
||||
|
||||
for i, r in enumerate(results, 1):
|
||||
# Monthly return: use real backtest if available, else estimate
|
||||
rb = r.get('real_backtest', {})
|
||||
if isinstance(rb, dict) and rb.get('status') == 'success':
|
||||
monthly_pct = rb.get('monthly_return_pct', r.get('monthly_return_pct', 0))
|
||||
n_trades = rb.get('n_trades', '-')
|
||||
real_ic = rb.get('ic', 0)
|
||||
else:
|
||||
monthly_pct = r.get('monthly_return_pct', r.get('real_monthly_return', 0))
|
||||
n_trades = '-'
|
||||
real_ic = rb.get('ic', 0) if isinstance(rb, dict) else 0
|
||||
|
||||
table.add_row(
|
||||
str(i),
|
||||
r.get("strategy_name", "unknown")[:30],
|
||||
str(len(r.get("factor_names", []))),
|
||||
f"{r.get('sharpe_ratio', 0):.3f}",
|
||||
f"{r.get('max_drawdown', 0):.2%}",
|
||||
f"{r.get('win_rate', 0):.2%}",
|
||||
f"{monthly_pct:.2f}%",
|
||||
str(n_trades),
|
||||
f"{r.get('sharpe', r.get('sharpe_ratio', 0)):.3f}",
|
||||
f"{r.get('max_drawdown', r.get('est_max_drawdown', 0)):.2%}",
|
||||
f"{r.get('win_rate', r.get('est_win_rate', 0)):.2%}",
|
||||
f"{real_ic:.4f}" if real_ic else "-",
|
||||
str(r.get("loop", "?")),
|
||||
)
|
||||
|
||||
|
||||
@@ -192,6 +192,10 @@ class QuantRDLoop(RDLoop):
|
||||
logger.error(f"Factor extraction failed.")
|
||||
raise FactorEmptyError("Factor extraction failed.")
|
||||
|
||||
# Increment factor count for tracking
|
||||
if hasattr(self, 'trace') and hasattr(self.trace, 'increment_factor_count'):
|
||||
self.trace.increment_factor_count()
|
||||
|
||||
# Handle failed experiments gracefully (don't break the loop)
|
||||
if getattr(exp, "failed", False):
|
||||
reason = getattr(exp, "failure_reason", "unknown")
|
||||
|
||||
@@ -16,6 +16,15 @@ from rdagent.utils.agent.tpl import T
|
||||
class QuantTrace(Trace):
|
||||
def __init__(self, scen: Scenario) -> None:
|
||||
super().__init__(scen)
|
||||
self._factor_count = 0
|
||||
|
||||
def get_factor_count(self) -> int:
|
||||
"""Return the number of factors generated so far."""
|
||||
return self._factor_count
|
||||
|
||||
def increment_factor_count(self) -> None:
|
||||
"""Increment the factor count."""
|
||||
self._factor_count += 1
|
||||
# Initialize the controller with default weights
|
||||
self.controller = EnvController()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user