Coverage for src / monte_neo / cli / menu / results.py: 0%

130 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-28 16:27 +0200

1"""Results viewing and display.""" 

2 

3from __future__ import annotations 

4 

5import json 

6import time 

7from typing import TYPE_CHECKING 

8 

9import questionary 

10from rich.console import Console 

11from rich.panel import Panel 

12from rich.table import Table 

13 

14from monte_neo.cli.styles import CUSTOM_STYLE 

15 

16if TYPE_CHECKING: 

17 from monte_neo.cli.menu.main import InteractiveMenu 

18 

19console = Console() 

20 

21 

22def view_results_workflow(menu: InteractiveMenu) -> None: 

23 """View saved results.""" 

24 results_dir = menu.config.data_dir / "results" 

25 if not results_dir.exists(): 

26 console.print("[yellow]⚠ No results directory found.[/]\n") 

27 return 

28 

29 files = list(results_dir.glob("*.json")) 

30 if not files: 

31 console.print("[yellow]⚠ No saved results found.[/]\n") 

32 return 

33 

34 choices = [f.stem for f in files] + ["🔙 Back"] 

35 selected = questionary.select("Select result to view:", choices=choices, style=CUSTOM_STYLE).ask() 

36 

37 if not selected or selected == "🔙 Back": 

38 return 

39 

40 _display_result_file(results_dir / f"{selected}.json") 

41 

42 

43def _display_result_file(file_path): 

44 try: 

45 with open(file_path) as f: 

46 data = json.load(f) 

47 

48 console.print(f"\n[bold cyan]📄 Results for {file_path.stem}[/]") 

49 

50 if "metrics" in data: 

51 table = Table(title="Metrics") 

52 table.add_column("Metric", style="cyan") 

53 table.add_column("Value", style="green") 

54 for k, v in data["metrics"].items(): 

55 val = f"{v:.4f}" if isinstance(v, float) else str(v) 

56 table.add_row(k, val) 

57 console.print(table) 

58 

59 if "mc_details" in data and data["mc_details"].get("step_results"): 

60 steps_table = Table(title="Monte Carlo Sequential Details") 

61 steps_table.add_column("Method", style="cyan") 

62 steps_table.add_column("Status", style="bold") 

63 steps_table.add_column("Pass Rate", style="green") 

64 steps_table.add_column("Advice", style="yellow") 

65 

66 for step in data["mc_details"]["step_results"]: 

67 status = "[green]PASS[/]" if step["passed"] else "[red]FAIL[/]" 

68 steps_table.add_row( 

69 step["method"], 

70 status, 

71 f"{step['rate']:.1%}", 

72 step["advice"] 

73 ) 

74 console.print(steps_table) 

75 

76 if "config" in data: 

77 config = data['config'] 

78 content = config.get("source_code", "\n".join([f"{k}: {v}" for k, v in config.items()])) 

79 console.print(Panel(content, title="Configuration", border_style="blue")) 

80 

81 except Exception as e: 

82 console.print(f"[red]Error loading result: {e}[/]") 

83 

84 

85def _format_time(seconds: float) -> str: 

86 """Format seconds into M:SS or S.SSSs.""" 

87 if seconds < 0.001: 

88 return f"{seconds*1000:.3f}ms" 

89 if seconds < 1.0: 

90 return f"{seconds:.4f}s" 

91 if seconds < 60: 

92 return f"{seconds:.2f}s" 

93 minutes = int(seconds // 60) 

94 remaining_seconds = seconds % 60 

95 return f"{minutes}m {remaining_seconds:.1f}s" 

96 

97 

98def show_generation_result(menu: InteractiveMenu, result) -> None: 

99 """Display generation results and optionally save/plot.""" 

100 console.print() 

101 

102 # Success depends on mc_pass_rate >= mc_pass_threshold 

103 threshold = getattr(menu, "_mc_pass_threshold", 0.80) 

104 

105 if result.success: 

106 console.print(f"[bold green]✓ Indicator generated successfully! (MC Pass Rate {result.mc_pass_rate:.1%} >= {threshold:.0%})[/]\n") 

107 elif result.indicator: 

108 console.print(f"[bold yellow]⚠ Best indicator found but did not meet production criteria ({result.mc_pass_rate:.1%} < {threshold:.0%})[/]") 

109 console.print(f"[dim]Requirement: Monte Carlo Pass Rate must be > {threshold:.0%} to be considered stable.[/]\n") 

110 else: 

111 console.print(f"[bold red]❌ No suitable indicator found matching criteria (MC Rate > {threshold:.0%})[/]\n") 

112 

113 if result.success: 

114 _print_trust_certificate(result) 

115 

116 table = Table(title="Generation Results") 

117 table.add_column("Metric", style="cyan") 

118 table.add_column("Value", style="green") 

119 table.add_row("Total Time", _format_time(result.elapsed_time)) 

120 table.add_row("Iterations", f"{result.iterations_tried:,}") 

121 table.add_row("MC Pass Rate", f"{result.mc_pass_rate:.1%}") 

122 

123 # Add Hardware Timing Stats if available 

124 if hasattr(result, "mc_details") and "timing_stats" in result.mc_details: 

125 stats = result.mc_details["timing_stats"] 

126 if "kernel_execution" in stats: 

127 table.add_row("GPU Kernel", _format_time(stats['kernel_execution'])) 

128 if "data_prep" in stats: 

129 table.add_row("GPU Data Prep", _format_time(stats['data_prep'])) 

130 if "result_formatting" in stats: 

131 table.add_row("GPU Post-Process", _format_time(stats['result_formatting'])) 

132 if "total" in stats: 

133 table.add_row("GPU Total", _format_time(stats['total'])) 

134 

135 console.print(table) 

136 

137 if hasattr(result, "mc_details") and result.mc_details.get("step_results"): 

138 steps_table = Table(title="Monte Carlo Sequential Details") 

139 steps_table.add_column("Method", style="cyan") 

140 steps_table.add_column("Status", style="bold") 

141 steps_table.add_column("Pass Rate", style="green") 

142 steps_table.add_column("Advice", style="yellow") 

143 

144 for step in result.mc_details["step_results"]: 

145 status = "[green]PASS[/]" if step["passed"] else "[red]FAIL[/]" 

146 steps_table.add_row( 

147 step["method"], 

148 status, 

149 f"{step['rate']:.1%}", 

150 step["advice"] 

151 ) 

152 console.print(steps_table) 

153 

154 if result.indicator: 

155 # Get formula using the new method 

156 formula = result.indicator.get_formula() 

157 

158 # Color based on success 

159 border_color = "green" if result.success else "yellow" 

160 header_text = "Best Indicator Found" if result.success else "Best Indicator Found (Below Criteria)" 

161 

162 console.print(Panel( 

163 f"[bold cyan]Indicator:[/] {result.indicator.name}\n" 

164 f"[bold cyan]Formula:[/] {formula}\n" 

165 f"[bold cyan]Parameters:[/] {result.parameters}", 

166 title=header_text, 

167 border_style=border_color 

168 )) 

169 _save_result(menu, result) 

170 if questionary.confirm("Show chart?", style=CUSTOM_STYLE).ask(): 

171 _plot_result(menu, result) 

172 else: 

173 console.print("\n[red]❌ No indicator was found during the search.[/]") 

174 

175 

176def _save_result(menu: InteractiveMenu, result) -> None: 

177 results_dir = menu.config.data_dir / "results" 

178 results_dir.mkdir(parents=True, exist_ok=True) 

179 

180 timestamp = int(time.time()) 

181 name = result.indicator.name if result.indicator else "unknown" 

182 file_path = results_dir / f"result_{timestamp}_{name}.json" 

183 

184 data = { 

185 "timestamp": timestamp, 

186 "type": name, 

187 "metrics": result.final_metrics, 

188 "config": result.parameters, 

189 "mc_pass_rate": result.mc_pass_rate, 

190 "mc_details": getattr(result, "mc_details", {}), 

191 } 

192 

193 with open(file_path, "w") as f: 

194 json.dump(data, f, indent=4) 

195 console.print(f"[dim]Result saved to: results/{file_path.name}[/]") 

196 

197 

198def _plot_result(menu: InteractiveMenu, result) -> None: 

199 from monte_neo.visualization.charts import ChartGenerator 

200 if hasattr(menu, "_last_data") and menu._last_data is not None: 

201 # Use generate_signals_fast for better performance if available 

202 if hasattr(result.indicator, "generate_signals_fast"): 

203 signals = result.indicator.generate_signals_fast(menu._last_data) 

204 else: 

205 signals = result.indicator.generate_signals(menu._last_data) 

206 

207 chart_gen = ChartGenerator() 

208 chart_gen.plot_with_signals(menu._last_data, signals, title=f"Best: {result.indicator.name}") 

209 else: 

210 console.print("[yellow]⚠ No data available to plot chart. Please run generation first.[/]") 

211 

212 

213def _print_trust_certificate(result): 

214 """Print a robustness certificate.""" 

215 console.print() 

216 

217 # Check if we have detailed steps 

218 details = "" 

219 if hasattr(result, "mc_details") and result.mc_details.get("step_results"): 

220 for step in result.mc_details["step_results"]: 

221 if step["passed"]: 

222 details += f"[green]✔ {step['method']}[/]\n" 

223 

224 if not details: 

225 details = "[green]✔ Monte Carlo Simulation (Aggregated)[/]" 

226 

227 certificate = f""" 

228 [bold green]🌟 CERTIFICATE OF ROBUSTNESS 🌟[/] 

229  

230 This certifies that the indicator: 

231 [bold white]{result.indicator.name}[/] 

232  

233 Has successfully passed rigorous Monte Carlo Stress Tests: 

234{details} 

235 [bold]Pass Rate: {result.mc_pass_rate:.1%}[/] 

236  

237 Status: [bold green]PRODUCTION READY[/] 

238 """ 

239 

240 console.print(Panel( 

241 certificate, 

242 border_style="green", 

243 expand=False 

244 ))