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

45 statements  

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

1"""Indicator generation workflow.""" 

2 

3from __future__ import annotations 

4 

5import time 

6from typing import TYPE_CHECKING 

7 

8import questionary 

9 

10from monte_neo.cli.styles import CUSTOM_STYLE 

11from monte_neo.core.generator import GeneratorConfig, IndicatorGenerator 

12from monte_neo.utils.console import console 

13 

14if TYPE_CHECKING: 

15 from monte_neo.cli.menu.main import InteractiveMenu 

16 

17 

18def generate_indicator_workflow(menu: InteractiveMenu, sequential: bool = False) -> None: 

19 """Generate indicator workflow.""" 

20 title = "🚀 Generate Indicator" if not sequential else "🔄 Sequential Generate Indicator" 

21 console.print(f"\n[bold cyan]{title}[/]\n") 

22 

23 if not menu._target_metrics: 

24 console.print("[yellow]⚠ Please set target metrics first[/]\n") 

25 return 

26 

27 files = menu.storage.list_files() 

28 if not files: 

29 console.print("[yellow]⚠ No data available. Download data first.[/]\n") 

30 return 

31 

32 file_choices = [f"{f['symbol']}_{f['timeframe']}" for f in files] 

33 selected = questionary.select("Select data:", choices=file_choices, style=CUSTOM_STYLE).ask() 

34 

35 if not selected: 

36 return 

37 

38 symbol, timeframe = selected.split("_") 

39 

40 # Get iterations and types 

41 iterations = _get_iterations() 

42 if not iterations: return 

43 

44 indicator_types = _get_indicator_types() 

45 if not indicator_types: return 

46 

47 # Confirm and Run 

48 if not questionary.confirm("Start generation?", style=CUSTOM_STYLE).ask(): 

49 return 

50 

51 _run_generation(menu, symbol, timeframe, iterations, indicator_types, sequential) 

52 

53 

54def _get_iterations() -> int | None: 

55 return questionary.select( 

56 "Number of iterations:", 

57 choices=[ 

58 {"name": "1,000 (fast test)", "value": 1000}, 

59 {"name": "10,000 (standard)", "value": 10000}, 

60 {"name": "100,000 (thorough)", "value": 100000}, 

61 {"name": "1M (heavy)", "value": 1000000}, 

62 {"name": "10M (expert)", "value": 10000000}, 

63 {"name": "100M (extreme)", "value": 100000000}, 

64 {"name": "1B (insane)", "value": 1000000000}, 

65 ], 

66 style=CUSTOM_STYLE, 

67 ).ask() 

68 

69 

70def _get_indicator_types() -> list[str] | None: 

71 return questionary.checkbox( 

72 "Select indicator types to search:", 

73 choices=[ 

74 {"name": "SMA", "value": "sma", "checked": True}, 

75 {"name": "RSI", "value": "rsi", "checked": True}, 

76 {"name": "MACD", "value": "macd", "checked": True}, 

77 {"name": "🧬 Dynamic", "value": "dynamic", "checked": True}, 

78 ], 

79 style=CUSTOM_STYLE, 

80 ).ask() 

81 

82 

83def _run_generation(menu: InteractiveMenu, symbol: str, timeframe: str, iterations: int, types: list[str], sequential: bool = False) -> None: 

84 data = menu.storage.load(symbol, timeframe) 

85 menu._last_data = data # Restore to allow charting after generation 

86 

87 config = GeneratorConfig( 

88 max_iterations=iterations, 

89 target_metrics=menu._target_metrics, 

90 indicator_types=types, 

91 population_size=menu._pop_size, 

92 generations=menu._generations, 

93 mutation_rate=menu._mutation_rate, 

94 crossover_rate=menu._crossover_rate, 

95 use_sl_tp=menu._use_sl_tp, 

96 stop_loss_pct=menu._stop_loss_pct, 

97 take_profit_pct=menu._take_profit_pct, 

98 use_mc_shuffling="shuffling" in menu._mc_methods, 

99 use_mc_noise="noise" in menu._mc_methods, 

100 use_mc_sensitivity="sensitivity" in menu._mc_methods, 

101 use_mc_walk_forward="walk_forward" in menu._mc_methods, 

102 use_mc_block_bootstrap="block_bootstrap" in menu._mc_methods, 

103 use_sequential_mc=sequential, 

104 mc_pass_threshold=getattr(menu, "_mc_pass_threshold", 0.80), 

105 use_gpu=getattr(menu, "_use_gpu", True), 

106 gpu_precision=getattr(menu, "_gpu_precision", "float32"), 

107 metal_driver=getattr(menu, "_metal_driver", "cpp"), 

108 initial_capital=menu.config.initial_capital, 

109 leverage=menu.config.leverage, 

110 ) 

111 

112 generator = IndicatorGenerator(config) 

113 menu.progress.start(iterations, "Generating indicator...") 

114 generator.set_progress_callback(menu.progress.update) 

115 

116 result = generator.generate(data) 

117 time.sleep(0.1) 

118 menu.progress.update(iterations, iterations, "Done") 

119 menu.progress.stop() 

120 

121 from monte_neo.cli.menu.results import show_generation_result 

122 show_generation_result(menu, result)