Coverage for src / monte_neo / cli / app.py: 22%
90 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-28 16:27 +0200
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-28 16:27 +0200
1"""Main CLI application.
3Entry point for the Monte-Neo CLI.
4"""
6from __future__ import annotations
8import argparse
9import sys
10from typing import TYPE_CHECKING
12from rich.console import Console
14from monte_neo._version import __version__
15from monte_neo.cli.menu import InteractiveMenu
16from monte_neo.cli.styles import print_banner, print_error
17from monte_neo.utils.config import load_config
18from monte_neo.utils.logger import get_logger, setup_logging
20if TYPE_CHECKING:
21 pass
23logger = get_logger(__name__)
24console = Console()
27class MonteNeoCLI:
28 """Main CLI application class."""
30 def __init__(self) -> None:
31 """Initialize CLI."""
32 self.config = load_config()
33 self.menu = InteractiveMenu(self.config)
35 def run(self, interactive: bool = True) -> int:
36 """Run the CLI application.
38 Args:
39 interactive: Run in interactive mode.
41 Returns:
42 Exit code.
43 """
44 print_banner()
46 if interactive:
47 return self.menu.run()
48 else:
49 console.print("[yellow]Non-interactive mode not yet implemented[/]")
50 return 1
52 def run_export(self, json_path: str) -> int:
53 """Export an indicator to C++."""
54 import json
56 from monte_neo.core.optimization.production_exporter import ProductionExporter
58 try:
59 with open(json_path) as f:
60 data = json.load(f)
62 # Mock indicator for export
63 from monte_neo.indicators.dynamic import DynamicIndicator
64 indicator = DynamicIndicator()
65 indicator.set_parameter("source_code", data.get("formula", ""))
67 exporter = ProductionExporter()
68 export_path = exporter.export(indicator, data.get("validation", {}))
70 console.print(f"[green]Successfully exported to: {export_path}[/]")
71 return 0
72 except Exception as e:
73 console.print(f"[red]Export failed: {e}[/]")
74 return 1
76 def run_evolve(self, symbol: str) -> int:
77 """Run AI-driven evolution for a symbol."""
78 from monte_neo.core.evolution_ai import AIEvolutionEngine
79 from monte_neo.data.storage import ParquetStorage
80 from monte_neo.utils.config import Config
82 try:
83 console.print(f"[bold cyan]Starting AI Evolution for {symbol}...[/]")
84 config = Config()
85 storage = ParquetStorage(config.data_dir)
86 data = storage.load(symbol, timeframe=config.default_timeframe)
88 if data is None or data.empty:
89 console.print(f"[red]No data found for {symbol}[/]")
90 return 1
92 engine = AIEvolutionEngine()
93 best_indicator = engine.evolve(data, {"profit_factor": 1.5, "sharpe_ratio": 1.0}, generations=5)
95 console.print("[green]Evolution complete! Best formula:[/]")
96 console.print(f"[bold white]{best_indicator.get_formula()}[/]")
97 return 0
98 except Exception as e:
99 console.print(f"[red]Evolution failed: {e}[/]")
100 return 1
102 def run_headless(
103 self,
104 config_file: str,
105 ) -> int:
106 """Run in headless mode with config file.
108 Args:
109 config_file: Path to YAML config.
111 Returns:
112 Exit code.
113 """
114 from monte_neo.cli.headless import run_headless_generation
116 return run_headless_generation(config_file)
119def parse_args() -> argparse.Namespace:
120 """Parse command line arguments."""
121 parser = argparse.ArgumentParser(
122 prog="monte-neo",
123 description="Monte Carlo Indicator Generator Framework",
124 )
126 parser.add_argument(
127 "--interactive",
128 "-i",
129 action="store_true",
130 default=True,
131 help="Run in interactive mode (default)",
132 )
134 parser.add_argument(
135 "--config",
136 "-c",
137 type=str,
138 help="Path to YAML config file (for headless mode)",
139 )
141 parser.add_argument(
142 "--headless",
143 action="store_true",
144 help="Run in headless mode with config file",
145 )
147 parser.add_argument(
148 "--export",
149 type=str,
150 help="Path to indicator JSON to export to C++",
151 )
153 parser.add_argument(
154 "--evolve",
155 type=str,
156 help="Symbol to run AI evolution for (e.g. BTCUSDT)",
157 )
159 parser.add_argument(
160 "--log-level",
161 type=str,
162 default="INFO",
163 choices=["DEBUG", "INFO", "WARNING", "ERROR"],
164 help="Log level",
165 )
167 parser.add_argument(
168 "--version",
169 "-v",
170 action="version",
171 version=f"monte-neo {__version__}",
172 )
174 return parser.parse_args()
177def main() -> int:
178 """Main entry point."""
179 args = parse_args()
181 # Setup logging
182 setup_logging(args.log_level)
184 try:
185 app = MonteNeoCLI()
187 if args.export:
188 return app.run_export(args.export)
189 elif args.evolve:
190 return app.run_evolve(args.evolve)
191 elif args.headless and args.config:
192 return app.run_headless(args.config)
193 else:
194 return app.run(interactive=args.interactive)
196 except KeyboardInterrupt:
197 console.print("\n[yellow]Interrupted by user[/]")
198 sys.exit(0)
200 except Exception as e:
201 print_error(f"Error: {e}")
202 logger.exception("Unhandled exception")
203 return 1
206if __name__ == "__main__":
207 sys.exit(main())