""" Monte Carlo Envelope Mapper CLI =============================== Command-line interface for running Monte Carlo envelope mapping of the Nautilus-Dolphin trading system. Usage: python run_mc_envelope.py --mode run --stage 1 --n-samples 500 python run_mc_envelope.py --mode train --output-dir mc_results/ python run_mc_envelope.py --mode assess --assess my_config.json Reference: MONTE_CARLO_SYSTEM_ENVELOPE_SPEC.md Section 11 """ import argparse import json import sys from pathlib import Path # Add parent to path for imports sys.path.insert(0, str(Path(__file__).parent.parent)) def create_parser() -> argparse.ArgumentParser: """Create argument parser.""" parser = argparse.ArgumentParser( description="Monte Carlo System Envelope Mapper for DOLPHIN NG", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Run full envelope mapping python run_mc_envelope.py --mode run --n-samples 500 --n-workers 7 # Train ML models on completed results python run_mc_envelope.py --mode train # Assess a configuration file python run_mc_envelope.py --mode assess --assess config.json # Generate summary report python run_mc_envelope.py --mode report """ ) parser.add_argument( '--mode', choices=['sample', 'validate', 'run', 'train', 'assess', 'report'], default='run', help='Operation mode (default: run)' ) parser.add_argument( '--n-samples', type=int, default=500, help='Samples per switch vector (default: 500)' ) parser.add_argument( '--n-workers', type=int, default=-1, help='Parallel workers (-1 for auto, default: auto)' ) parser.add_argument( '--batch-size', type=int, default=1000, help='Trials per batch file (default: 1000)' ) parser.add_argument( '--output-dir', type=str, default='mc_results', help='Results directory (default: mc_results/)' ) parser.add_argument( '--stage', type=int, choices=[1, 2], default=1, help='Stage: 1=reduced, 2=full (default: 1)' ) parser.add_argument( '--seed', type=int, default=42, help='Master RNG seed (default: 42)' ) parser.add_argument( '--config', type=str, help='JSON config file for parameter overrides' ) parser.add_argument( '--resume', action='store_true', help='Resume from existing results' ) parser.add_argument( '--assess', type=str, help='JSON file with config to assess (for mode=assess)' ) parser.add_argument( '--max-trials', type=int, help='Maximum total trials (for testing)' ) parser.add_argument( '--quiet', action='store_true', help='Reduce output verbosity' ) return parser def cmd_sample(args): """Sample configurations only.""" from mc import MCSampler print("="*70) print("MONTE CARLO CONFIGURATION SAMPLER") print("="*70) sampler = MCSampler(base_seed=args.seed) print(f"\nGenerating trials (n_samples_per_switch={args.n_samples})...") trials = sampler.generate_trials( n_samples_per_switch=args.n_samples, max_trials=args.max_trials ) # Save output_path = Path(args.output_dir) / "manifests" / "all_configs.json" sampler.save_trials(trials, output_path) print(f"\n[OK] Generated and saved {len(trials)} configurations") return 0 def cmd_validate(args): """Validate configurations.""" from mc import MCSampler, MCValidator print("="*70) print("MONTE CARLO CONFIGURATION VALIDATOR") print("="*70) # Load configurations config_path = Path(args.output_dir) / "manifests" / "all_configs.json" if not config_path.exists(): print(f"[ERROR] Configurations not found: {config_path}") print("Run with --mode sample first") return 1 sampler = MCSampler() trials = sampler.load_trials(config_path) print(f"\nValidating {len(trials)} configurations...") validator = MCValidator(verbose=not args.quiet) results = validator.validate_batch(trials) # Stats stats = validator.get_validity_stats(results) print(f"\n{'='*70}") print("VALIDATION RESULTS") print(f"{'='*70}") print(f"Total: {stats['total']}") print(f"Valid: {stats['valid']} ({stats['validity_rate']*100:.1f}%)") print(f"Rejected V1 (range): {stats.get('rejected_v1', 0)}") print(f"Rejected V2 (constraints): {stats.get('rejected_v2', 0)}") print(f"Rejected V3 (cross-group): {stats.get('rejected_v3', 0)}") print(f"Rejected V4 (degenerate): {stats.get('rejected_v4', 0)}") # Save validation results output_path = Path(args.output_dir) / "manifests" / "validation_results.json" with open(output_path, 'w') as f: json.dump([r.to_dict() for r in results], f, indent=2) print(f"\n[OK] Validation results saved: {output_path}") return 0 def cmd_run(args): """Run full envelope mapping.""" from mc import MCRunner print("="*70) print("MONTE CARLO ENVELOPE MAPPER") print("="*70) print(f"Mode: {'Stage 1 (reduced)' if args.stage == 1 else 'Stage 2 (full)'}") print(f"Samples per switch: {args.n_samples}") print(f"Workers: {args.n_workers if args.n_workers > 0 else 'auto'}") print(f"Output: {args.output_dir}") print(f"Seed: {args.seed}") print(f"Resume: {args.resume}") print("="*70) runner = MCRunner( output_dir=args.output_dir, n_workers=args.n_workers, batch_size=args.batch_size, base_seed=args.seed, verbose=not args.quiet ) stats = runner.run_envelope_mapping( n_samples_per_switch=args.n_samples, max_trials=args.max_trials, resume=args.resume ) # Save stats stats_path = Path(args.output_dir) / "run_stats.json" with open(stats_path, 'w') as f: json.dump(stats, f, indent=2, default=str) print(f"\n[OK] Run complete. Stats saved: {stats_path}") return 0 def cmd_train(args): """Train ML models.""" from mc import MCML print("="*70) print("MONTE CARLO ML TRAINER") print("="*70) ml = MCML(output_dir=args.output_dir) try: results = ml.train_all_models() print("\n[OK] Training complete") return 0 except Exception as e: print(f"\n[ERROR] Training failed: {e}") import traceback traceback.print_exc() return 1 def cmd_assess(args): """Assess a configuration.""" from mc import DolphinForewarner, MCTrialConfig if not args.assess: print("[ERROR] --assess flag required with path to config JSON") return 1 config_path = Path(args.assess) if not config_path.exists(): print(f"[ERROR] Config file not found: {config_path}") return 1 print("="*70) print("DOLPHIN FOREWARNING ASSESSMENT") print("="*70) # Load config with open(config_path, 'r') as f: config_dict = json.load(f) # Create forewarner forewarner = DolphinForewarner(models_dir=f"{args.output_dir}/models") # Assess if 'trial_id' in config_dict: config = MCTrialConfig.from_dict(config_dict) else: # Assume flat config config = MCTrialConfig(**config_dict) report = forewarner.assess(config) # Print report print(f"\nConfiguration:") print(f" vel_div_threshold: {config.vel_div_threshold}") print(f" max_leverage: {config.max_leverage}") print(f" fraction: {config.fraction}") print(f"\nPredictions:") print(f" ROI: {report.predicted_roi:.2f}%") print(f" Max DD: {report.predicted_max_dd:.2f}%") print(f" Champion probability: {report.champion_probability:.1%}") print(f" Catastrophic probability: {report.catastrophic_probability:.1%}") print(f" Envelope score: {report.envelope_score:.2f}") print(f"\nWarnings:") if report.warnings: for w in report.warnings: print(f" ! {w}") else: print(" (none)") # Save report report_path = Path(args.output_dir) / "forewarning_report.json" with open(report_path, 'w') as f: json.dump(report.to_dict(), f, indent=2, default=str) print(f"\n[OK] Report saved: {report_path}") return 0 def cmd_report(args): """Generate summary report.""" from mc import MCRunner print("="*70) print("MONTE CARLO REPORT GENERATOR") print("="*70) runner = MCRunner(output_dir=args.output_dir) report = runner.generate_report( output_path=f"{args.output_dir}/envelope_report.md" ) print(report) return 0 def main(): """Main entry point.""" parser = create_parser() args = parser.parse_args() # Dispatch try: if args.mode == 'sample': return cmd_sample(args) elif args.mode == 'validate': return cmd_validate(args) elif args.mode == 'run': return cmd_run(args) elif args.mode == 'train': return cmd_train(args) elif args.mode == 'assess': return cmd_assess(args) elif args.mode == 'report': return cmd_report(args) else: print(f"[ERROR] Unknown mode: {args.mode}") return 1 except KeyboardInterrupt: print("\n\n[INTERRUPTED] Stopping...") return 130 except Exception as e: print(f"\n[ERROR] {e}") import traceback traceback.print_exc() return 1 if __name__ == "__main__": sys.exit(main())