# source: https://raw.githubusercontent.com/ikonclast/bob_der_botmeister/da05d31149b2f7577580f600e6bb591310274fe0/scripts/working_policy_batch.py
#!/usr/bin/env python3
"""
WORKING Policy Engine Batch Runner v3.0
Based on successful DEBUG_TEST backtest
"""

import json
import subprocess
import uuid
import os
import re
import time
from pathlib import Path
from datetime import datetime
from typing import Dict

CONFIG = {
    "total_runs": 320,
    "target_valid": 300,
    "pairs": ["BTC/USDT", "ETH/USDT", "SOL/USDT", "ADA/USDT", "AVAX/USDT"],
    "fee": 0.0015,
    "min_trades": 200,
    "max_drawdown": 0.25,
    "min_pf": 1.10,
    "output_dir": "/home/node/.openclaw/workspace/bob_quant/policy_dataset_final",
}

class WorkingBatchRunner:
    def __init__(self):
        self.output_dir = Path(CONFIG["output_dir"])
        self.output_dir.mkdir(parents=True, exist_ok=True)
        self.runs_dir = self.output_dir / "runs"
        self.runs_dir.mkdir(exist_ok=True)
        self.stats = {"total": 0, "completed": 0, "positive": 0, "negative": 0, "low": 0, "failed": 0}
        
    def get_params(self, run_num: int) -> Dict:
        """Generate varied params - simple EMA variations that produce trades"""
        import random
        random.seed(run_num * 42)  # Different seed pattern
        
        # Key insight from DEBUG_TEST: 0.995/0.985 with EMA 20 gives 345 trades
        # Vary around this sweet spot
        return {
            "ema_period": random.choice([15, 18, 20, 22, 25, 30]),
            "entry_mult": round(random.uniform(0.993, 0.997), 4),  # Tighter = more trades
            "exit_mult": round(random.uniform(0.983, 0.987), 4),
            "stoploss": round(random.uniform(-0.05, -0.02), 3),
            "trailing": round(random.uniform(0.008, 0.020), 3),
            "family": random.choice([
                "ema_trend", "ema_relaxed", "ema_tight", "ema_volatility",
                "trend_follow", "momentum_ema", "breakout_ema", "hybrid_ema"
            ]),
            "timeframe": random.choice(["1h", "2h", "4h"]),
        }
    
    def generate_strategy(self, params: Dict, run_id: str) -> str:
        """Generate EMA strategy - proven to produce trades"""
        class_name = f"POL{run_id}"
        
        return f'''from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta

class Github_ikonclast_bob_der_botmeister__working_policy_batch__20260716_095920(IStrategy):
    timeframe = '{params["timeframe"]}'
    stoploss = {params["stoploss"]:.4f}
    trailing_stop = True
    trailing_stop_positive = {params["trailing"]:.4f}
    
    minimal_roi = {{"0": 0.03, "30": 0.015, "60": 0.008}}
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['ema'] = ta.EMA(dataframe, timeperiod={params["ema_period"]})
        return dataframe
    
    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[:, 'buy'] = 0
        dataframe.loc[dataframe['close'] > dataframe['ema'] * {params["entry_mult"]:.4f}, 'buy'] = 1
        return dataframe
    
    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[:, 'sell'] = 0
        dataframe.loc[dataframe['close'] < dataframe['ema'] * {params["exit_mult"]:.4f}, 'sell'] = 1
        return dataframe
'''
    
    def run_backtest(self, params: Dict, run_id: str) -> Dict:
        """Execute backtest with proper parsing"""
        try:
            class_name = f"POL{run_id}"
            strategy_code = self.generate_strategy(params, run_id)
            
            # Write strategy
            strategy_path = Path(f"/opt/docker/freqtrade/shared_data/user_data/strategies/Github_ikonclast_bob_der_botmeister__working_policy_batch__20260716_095920.py")
            with open(strategy_path, 'w') as f:
                f.write(strategy_code)
            
            # Write config
            config = {
                "max_open_trades": 5,
                "stake_currency": "USDT",
                "stake_amount": 100,
                "dry_run": True,
                "dry_run_wallet": 1000,
                "timeframe": params["timeframe"],
                "fee": CONFIG["fee"],
                "trading_mode": "spot",
                "exchange": {"name": "binance", "pair_whitelist": CONFIG["pairs"]},
                "pairlists": [{"method": "StaticPairList"}],
                "entry_pricing": {"price_side": "other", "use_order_book": False, "order_book_top": 1},
                "exit_pricing": {"price_side": "other", "use_order_book": False, "order_book_top": 1},
                "telegram": {"enabled": False, "token": "dummy", "chat_id": "123"},
                "api_server": {"enabled": False, "listen_ip_address": "127.0.0.1", "listen_port": 8080, "username": "a", "password": "a"},
            }
            
            config_path = Path(f"/opt/docker/freqtrade/shared_data/user_data/configs/cfg{run_id}.json")
            with open(config_path, 'w') as f:
                json.dump(config, f)
            
            # Run docker
            output_file = f"/tmp/bt{run_id}.txt"
            
            cmd = [
                'docker', 'run', '--rm', '--network', 'host',
                '-v', '/opt/docker/freqtrade/shared_data/user_data:/freqtrade/user_data:rw',
                'freqtradeorg/freqtrade:stable',
                'backtesting',
                '--strategy', class_name,
                '--config', str(config_path),
                '--timeframe', params["timeframe"],
                '--timerange', '20230601-20231231',
                '--fee', str(CONFIG["fee"]),
                '--export', 'none'
            ]
            
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=180)
            output = result.stdout + result.stderr
            
            # Cleanup
            for f in [strategy_path, config_path]:
                try:
                    if os.path.exists(f):
                        os.unlink(f)
                except:
                    pass
            
            # Parse metrics - use patterns from actual output
            metrics = {"trades": 0, "net_profit": -100, "max_drawdown": 1.0, "winrate": 0}
            
            # Parse from actual DEBUG_TEST output format
            # Example: "Total/Daily Avg Trades: 345 / 1.62"
            trades_match = re.search(r'Total/Daily Avg Trades\s+([0-9]+)\s*/', output)
            if trades_match:
                metrics["trades"] = int(trades_match.group(1))
            
            # "Total profit %: -11.08%"
            profit_match = re.search(r'Total profit %\s+([0-9\-.]+)%', output)
            if profit_match:
                metrics["net_profit"] = float(profit_match.group(1))
            
            # "Max % of account underwater: 11.15%"
            dd_match = re.search(r'Max % of account underwater\s+([0-9.]+)%', output)
            if dd_match:
                metrics["max_drawdown"] = float(dd_match.group(1)) / 100
            
            # "Win/Draw/Loss: 165 0 180" or check strategy summary table
            win_match = re.search(r'Win\s+Draw\s+Loss\s+Win%\s*\n.*?(\d+)\s+\d+\s+\d+\s+([0-9.]+)', output)
            if win_match:
                metrics["winrate"] = float(win_match.group(2))
            
            # Alternative: Parse from summary table line
            # │ DEBUG_TEST │ 345 │ -0.32 │ -110.835 │ -11.08 │
            summary_match = re.search(rf'Github_ikonclast_bob_der_botmeister__working_policy_batch__20260716_095920[^0-9]*([0-9]+)[^0-9\-]*([0-9\-.]+)[^0-9\-]*([0-9\-.]+)', output)
            if summary_match:
                metrics["trades"] = int(summary_match.group(1))
            
            return {
                "status": "success" if metrics["trades"] > 0 else "no_trades",
                "metrics": metrics,
                "full_output": output[-500:] if len(output) > 500 else output,  # Save last 500 chars for debug
            }
            
        except Exception as e:
            return {"status": "error", "error": str(e)}
    
    def label(self, metrics: Dict) -> str:
        if metrics["trades"] < CONFIG["min_trades"]:
            return "LOW_SAMPLE"
        if metrics["net_profit"] >= 0 and metrics["max_drawdown"] <= CONFIG["max_drawdown"]:
            return "POSITIVE"
        return "NEGATIVE"
    
    def save(self, run_id: str, params: Dict, result: Dict):
        """Save run results"""
        run_dir = self.runs_dir / run_id
        run_dir.mkdir(exist_ok=True)
        
        metrics = result.get("metrics", {})
        label = self.label(metrics)
        
        with open(run_dir / "params.json", 'w') as f:
            json.dump(params, f)
        with open(run_dir / "metrics.json", 'w') as f:
            json.dump({**metrics, "label": label}, f)
        with open(run_dir / "meta.json", 'w') as f:
            json.dump({
                "run_id": run_id,
                "timestamp": datetime.now().isoformat(),
                "family": params["family"]
            }, f)
        
        return label
    
    def run(self):
        """Main loop"""
        print("=" * 60)
        print("🚀 WORKING POLICY BATCH - v3.0")
        print("Based on successful DEBUG_TEST (345 trades)")
        print("=" * 60)
        
        for i in range(1, CONFIG["total_runs"] + 1):
            run_id = str(uuid.uuid4())[:6]
            
            params = self.get_params(i)
            result = self.run_backtest(params, run_id)
            
            if result.get("status") == "success":
                label = self.save(run_id, params, result)
                self.stats["completed"] += 1
                self.stats[label.lower()] += 1
                m = result["metrics"]
                status_emoji = "✅" if label == "POSITIVE" else ("📉" if label == "NEGATIVE" else "📊")
                print(f"[{i}/320] {run_id}: {status_emoji} {label} | {m['trades']} trades, {m['net_profit']:.1f}%, {m['max_drawdown']*100:.1f}% DD")
            else:
                self.stats["failed"] += 1
                status = result.get('status', 'unknown')
                err = result.get('error', '')[:30]
                print(f"[{i}/320] {run_id}: ❌ {status} - {err}")
            
            self.stats["total"] += 1
            
            # Progress every 20
            if i % 20 == 0 or i == 1:
                print(f"\n📊 [{i}/320] Comp:{self.stats['completed']} Fail:{self.stats['failed']} | POS:{self.stats['positive']} NEG:{self.stats['negative']} LOW:{self.stats['low']}\n")
        
        # Final report
        manifest = {
            "version": "3.0-final",
            "timestamp": datetime.now().isoformat(),
            "stats": self.stats,
        }
        with open(self.output_dir / "manifest.json", 'w') as f:
            json.dump(manifest, f, indent=2)
        
        print("\n" + "=" * 60)
        print(f"✅ DONE: {self.stats['completed']}/{self.stats['total']}")
        print(f"POS:{self.stats['positive']} NEG:{self.stats['negative']} LOW:{self.stats['low']} FAIL:{self.stats['failed']}")
        print("=" * 60)

if __name__ == "__main__":
    WorkingBatchRunner().run()
