# source: https://raw.githubusercontent.com/CETANGZHI/flowainew/94cc7095db1259c5f1cb053bdf5c74bb0c4c16c3/backend/app/services/trading_service.py
Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
Trading服务
集成Freqtrade，实现策略回测和交易功能
Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646

from typing import Dict, Any, List, Optional
import subprocess
import json
import os
import logging
import re
from datetime import datetime
import uuid

from app.config import settings
from app.cache import get_redis
from app.metrics import backtest_queue_length, backtest_tasks_total


logger = logging.getLogger(__name__)


class TradingService:
    Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646交易服务类Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646

    def __init__(self):
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646初始化交易服务Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        self.freqtrade_path = settings.FREQTRADE_PATH
        self.redis = get_redis()

    async def run_backtest(
        self,
        strategy_code: str,
        pairs: List[str],
        timerange: str,
        timeframe: str = Github_CETANGZHI_flowainew__trading_service__20260114_1146461hGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
        initial_balance: float = 10000,
        user_id: Optional[str] = None,
    ) -> Dict[str, Any]:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646执行策略回测（提交 Celery 异步任务）

        该方法负责：
        - 解析并验证策略类名（第一个继承自 IStrategy 的类）；
        - 保存并验证策略代码；
        - 在 Redis 中写入任务的初始状态；
        - 提交 Celery 异步任务，由 worker 在后台执行真实回测。

        注意：本方法不会等待回测完成，而是立即返回 task_id 和 pending 状态，
        由前端通过 `/status/{task_id}` 或 `/result/{task_id}` 轮询获取最终结果。
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        try:
            # 生成任务ID
            task_id = str(uuid.uuid4())

            # 从用户代码中提取第一个继承自 IStrategy 的策略类名
            strategy_name = self._extract_strategy_class_name(strategy_code)
            if not strategy_name:
                return {
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646task_idGithub_CETANGZHI_flowainew__trading_service__20260114_114646: task_id,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646statusGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646failedGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646errorGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646未在策略代码中找到继承自 IStrategy 的策略类，请检查是否存在类似 `class Github_CETANGZHI_flowainew__trading_service__20260114_114646(IStrategy):` 的定义。Github_CETANGZHI_flowainew__trading_service__20260114_114646,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646error_codeGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646STRATEGY_VALIDATION_ERRORGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                }

            # 保存策略代码到文件（文件名包含策略类名 + task_id，避免并发覆盖）
            strategy_file = await self._save_strategy(task_id, strategy_code, strategy_name)

            # 验证策略代码
            is_valid, error = await self._validate_strategy(strategy_file)
            if not is_valid:
                return {
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646task_idGithub_CETANGZHI_flowainew__trading_service__20260114_114646: task_id,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646statusGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646failedGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646errorGithub_CETANGZHI_flowainew__trading_service__20260114_114646: fGithub_CETANGZHI_flowainew__trading_service__20260114_114646策略代码验证失败: {error}Github_CETANGZHI_flowainew__trading_service__20260114_114646,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646error_codeGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646STRATEGY_VALIDATION_ERRORGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                }

            # 保存任务状态到Redis
            await self._save_task_status(
                task_id,
                {
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646statusGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646pendingGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646pairsGithub_CETANGZHI_flowainew__trading_service__20260114_114646: pairs,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646timerangeGithub_CETANGZHI_flowainew__trading_service__20260114_114646: timerange,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646timeframeGithub_CETANGZHI_flowainew__trading_service__20260114_114646: timeframe,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646messageGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646回测任务已创建，等待执行Github_CETANGZHI_flowainew__trading_service__20260114_114646,
                    Github_CETANGZHI_flowainew__trading_service__20260114_114646created_atGithub_CETANGZHI_flowainew__trading_service__20260114_114646: datetime.now().isoformat(),
                },
            )

            # 记录回测任务提交指标
            backtest_tasks_total.labels(status=Github_CETANGZHI_flowainew__trading_service__20260114_114646submittedGithub_CETANGZHI_flowainew__trading_service__20260114_114646).inc()
            backtest_queue_length.inc()

            # 提交 Celery 异步任务（真正执行 Freqtrade 回测）
            from app.tasks import run_backtest_task

            run_backtest_task.delay(
                task_id=task_id,
                strategy_file=strategy_file,
                strategy_name=strategy_name,
                pairs=pairs,
                timerange=timerange,
                timeframe=timeframe,
                initial_balance=initial_balance,
                user_id=user_id,
            )

            return {
                Github_CETANGZHI_flowainew__trading_service__20260114_114646task_idGithub_CETANGZHI_flowainew__trading_service__20260114_114646: task_id,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646statusGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646pendingGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646messageGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646回测任务已提交，正在排队执行Github_CETANGZHI_flowainew__trading_service__20260114_114646,
            }

        except Exception as e:  # pragma: no cover - 防御性错误处理
            logger.error(Github_CETANGZHI_flowainew__trading_service__20260114_114646Error in run_backtest: %sGithub_CETANGZHI_flowainew__trading_service__20260114_114646, e, exc_info=True)
            return {
                Github_CETANGZHI_flowainew__trading_service__20260114_114646task_idGithub_CETANGZHI_flowainew__trading_service__20260114_114646: task_id if Github_CETANGZHI_flowainew__trading_service__20260114_114646task_idGithub_CETANGZHI_flowainew__trading_service__20260114_114646 in locals() else Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646statusGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646failedGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646errorGithub_CETANGZHI_flowainew__trading_service__20260114_114646: str(e),
                Github_CETANGZHI_flowainew__trading_service__20260114_114646error_codeGithub_CETANGZHI_flowainew__trading_service__20260114_114646: Github_CETANGZHI_flowainew__trading_service__20260114_114646INTERNAL_ERRORGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
            }

    async def _save_strategy(self, task_id: str, strategy_code: str, strategy_name: str) -> str:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646保存策略代码到文件

        文件名约定：`{strategy_name}_{task_id}.py`，其中 `strategy_name` 为第一个
        继承自 IStrategy 的类名，便于在 Freqtrade 中使用 `--strategy` 参数调用。

        Args:
            task_id: 任务ID
            strategy_code: 策略代码
            strategy_name: 策略类名（继承自 IStrategy）

        Returns:
            策略文件路径
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        # 创建策略目录
        strategy_dir = os.path.join(self.freqtrade_path, Github_CETANGZHI_flowainew__trading_service__20260114_114646user_dataGithub_CETANGZHI_flowainew__trading_service__20260114_114646, Github_CETANGZHI_flowainew__trading_service__20260114_114646strategiesGithub_CETANGZHI_flowainew__trading_service__20260114_114646)
        os.makedirs(strategy_dir, exist_ok=True)

        # 保存策略文件，文件名包含策略类名和 task_id，避免并发任务相互覆盖
        strategy_file = os.path.join(strategy_dir, fGithub_CETANGZHI_flowainew__trading_service__20260114_114646{strategy_name}_{task_id}.pyGithub_CETANGZHI_flowainew__trading_service__20260114_114646)
        with open(strategy_file, Github_CETANGZHI_flowainew__trading_service__20260114_114646wGithub_CETANGZHI_flowainew__trading_service__20260114_114646, encoding=Github_CETANGZHI_flowainew__trading_service__20260114_114646utf-8Github_CETANGZHI_flowainew__trading_service__20260114_114646) as f:
            f.write(strategy_code)

        logger.info(Github_CETANGZHI_flowainew__trading_service__20260114_114646Strategy saved to %s (class: %s)Github_CETANGZHI_flowainew__trading_service__20260114_114646, strategy_file, strategy_name)
        return strategy_file

    def _extract_strategy_class_name(self, strategy_code: str) -> Optional[str]:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646从策略代码中提取第一个继承自 IStrategy 的策略类名。

        解析规则：
        - 仅解析单行 class 定义，例如：
          `class Github_CETANGZHI_flowainew__trading_service__20260114_114646(IStrategy):` 或 `class Github_CETANGZHI_flowainew__trading_service__20260114_114646(SomeBase, IStrategy):`
        - 取第一个基类列表中包含 `IStrategy` 的类名。
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        pattern = rGithub_CETANGZHI_flowainew__trading_service__20260114_114646^\s*class\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)\s*:Github_CETANGZHI_flowainew__trading_service__20260114_114646
        for line in strategy_code.splitlines():
            match = re.match(pattern, line)
            if not match:
                continue

            class_name, bases_str = match.group(1), match.group(2)
            # 按逗号拆分基类，并去除空白
            bases = [b.strip() for b in bases_str.split(Github_CETANGZHI_flowainew__trading_service__20260114_114646,Github_CETANGZHI_flowainew__trading_service__20260114_114646) if b.strip()]
            # 判断是否存在 IStrategy 基类（允许形如 module.IStrategy）
            if any(base.endswith(Github_CETANGZHI_flowainew__trading_service__20260114_114646IStrategyGithub_CETANGZHI_flowainew__trading_service__20260114_114646) for base in bases):
                return class_name

        return None

    async def _validate_strategy(self, strategy_file: str) -> tuple[bool, Optional[str]]:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646验证策略代码语法是否正确。

        Args:
            strategy_file: 策略文件路径

        Returns:
            (是否有效, 错误信息)
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        try:
            # 1. 安全检查: 使用 AST 静态扫描禁止危险操作
            security_check = self._check_security(strategy_file)
            if not security_check[0]:
                return False, fGithub_CETANGZHI_flowainew__trading_service__20260114_114646安全检查失败: {security_check[1]}Github_CETANGZHI_flowainew__trading_service__20260114_114646

            # 2. 语法检查: 使用 compile
            with open(strategy_file, Github_CETANGZHI_flowainew__trading_service__20260114_114646rGithub_CETANGZHI_flowainew__trading_service__20260114_114646, encoding=Github_CETANGZHI_flowainew__trading_service__20260114_114646utf-8Github_CETANGZHI_flowainew__trading_service__20260114_114646) as f:
                code = f.read()

            compile(code, strategy_file, Github_CETANGZHI_flowainew__trading_service__20260114_114646execGithub_CETANGZHI_flowainew__trading_service__20260114_114646)
            return True, None
        except SyntaxError as e:
            return False, fGithub_CETANGZHI_flowainew__trading_service__20260114_114646语法错误: {str(e)}Github_CETANGZHI_flowainew__trading_service__20260114_114646
        except Exception as e:
            return False, str(e)

    def _check_security(self, strategy_file: str) -> tuple[bool, Optional[str]]:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646使用 AST 检查策略代码是否包含不安全的操作Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        import ast
        
        # 禁止的模块列表
        FORBIDDEN_MODULES = {
            'os', 'sys', 'subprocess', 'shutil', 'socket', 'requests', 'urllib', 'http',
            'pickle', 'marshal', 'base64', 'netrc', 'ftplib', 'telnetlib', 'importlib'
        }
        
        # 禁止的函数调用（由于是静态检查，只能覆盖部分显式调用）
        FORBIDDEN_FUNCTIONS = {'exec', 'eval', 'open', 'compile'}

        try:
            with open(strategy_file, Github_CETANGZHI_flowainew__trading_service__20260114_114646rGithub_CETANGZHI_flowainew__trading_service__20260114_114646, encoding=Github_CETANGZHI_flowainew__trading_service__20260114_114646utf-8Github_CETANGZHI_flowainew__trading_service__20260114_114646) as f:
                source = f.read()
            
            tree = ast.parse(source)
            
            for node in ast.walk(tree):
                # 检查 import
                if isinstance(node, (ast.Import, ast.ImportFrom)):
                    names = []
                    if isinstance(node, ast.Import):
                        names = [n.name.split('.')[0] for n in node.names]
                    elif isinstance(node, ast.ImportFrom):
                        if node.module:
                            names = [node.module.split('.')[0]]
                    
                    for name in names:
                        if name in FORBIDDEN_MODULES:
                            return False, fGithub_CETANGZHI_flowainew__trading_service__20260114_114646禁止导入模块: {name}Github_CETANGZHI_flowainew__trading_service__20260114_114646

                # 检查属性访问
                if isinstance(node, ast.Attribute):
                    # 🚨 Security Fix: P0.1
                    # Block access to private attributes (standard Python sandbox escape vector)
                    if node.attr.startswith(Github_CETANGZHI_flowainew__trading_service__20260114_114646__Github_CETANGZHI_flowainew__trading_service__20260114_114646):
                         return False, fGithub_CETANGZHI_flowainew__trading_service__20260114_114646 禁止访问私有属性: {node.attr}Github_CETANGZHI_flowainew__trading_service__20260114_114646

                # 检查函数调用
                if isinstance(node, ast.Call):
                    if isinstance(node.func, ast.Name):
                        if node.func.id in FORBIDDEN_FUNCTIONS:
                            return False, fGithub_CETANGZHI_flowainew__trading_service__20260114_114646禁止调用函数: {node.func.id}Github_CETANGZHI_flowainew__trading_service__20260114_114646
                            
            return True, None
            
        except Exception as e:
            return False, fGithub_CETANGZHI_flowainew__trading_service__20260114_114646安全检查执行错误: {str(e)}Github_CETANGZHI_flowainew__trading_service__20260114_114646

    async def _execute_backtest(
        self,
        strategy_file: str,
        pairs: List[str],
        timerange: str,
        timeframe: str,
        initial_balance: float
    ) -> Dict[str, Any]:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        执行Freqtrade回测

        Args:
            strategy_file: 策略文件路径
            pairs: 交易对列表
            timerange: 时间范围
            timeframe: 时间周期
            initial_balance: 初始资金

        Returns:
            回测结果
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        try:
            # 非 mock 模式下，从策略文件中解析 IStrategy 子类名，作为 Freqtrade 的 --strategy 参数
            with open(strategy_file, Github_CETANGZHI_flowainew__trading_service__20260114_114646rGithub_CETANGZHI_flowainew__trading_service__20260114_114646, encoding=Github_CETANGZHI_flowainew__trading_service__20260114_114646utf-8Github_CETANGZHI_flowainew__trading_service__20260114_114646) as f:
                code = f.read()

            strategy_name = self._extract_strategy_class_name(code) or os.path.basename(strategy_file).replace(Github_CETANGZHI_flowainew__trading_service__20260114_114646.pyGithub_CETANGZHI_flowainew__trading_service__20260114_114646, Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646)

            cmd = [
                Github_CETANGZHI_flowainew__trading_service__20260114_114646freqtradeGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646backtestingGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646--strategyGithub_CETANGZHI_flowainew__trading_service__20260114_114646, strategy_name,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646--timerangeGithub_CETANGZHI_flowainew__trading_service__20260114_114646, timerange,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646--timeframeGithub_CETANGZHI_flowainew__trading_service__20260114_114646, timeframe,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646--pairsGithub_CETANGZHI_flowainew__trading_service__20260114_114646, *pairs,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646--starting-balanceGithub_CETANGZHI_flowainew__trading_service__20260114_114646, str(initial_balance),
                Github_CETANGZHI_flowainew__trading_service__20260114_114646--exportGithub_CETANGZHI_flowainew__trading_service__20260114_114646, Github_CETANGZHI_flowainew__trading_service__20260114_114646tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646--export-filenameGithub_CETANGZHI_flowainew__trading_service__20260114_114646, fGithub_CETANGZHI_flowainew__trading_service__20260114_114646backtest_{strategy_name}.jsonGithub_CETANGZHI_flowainew__trading_service__20260114_114646
            ]

            logger.info(fGithub_CETANGZHI_flowainew__trading_service__20260114_114646Executing: {' '.join(cmd)}Github_CETANGZHI_flowainew__trading_service__20260114_114646)

            result = subprocess.run(
                cmd,
                cwd=self.freqtrade_path,
                capture_output=True,
                text=True,
                timeout=300
            )

            if result.returncode != 0:
                raise Exception(fGithub_CETANGZHI_flowainew__trading_service__20260114_114646回测失败: {result.stderr}Github_CETANGZHI_flowainew__trading_service__20260114_114646)

            # 优先从Freqtrade导出的JSON结果文件中解析结构化回测数据
            result_dir = os.path.join(self.freqtrade_path, Github_CETANGZHI_flowainew__trading_service__20260114_114646user_dataGithub_CETANGZHI_flowainew__trading_service__20260114_114646, Github_CETANGZHI_flowainew__trading_service__20260114_114646backtest_resultsGithub_CETANGZHI_flowainew__trading_service__20260114_114646)
            result_file = os.path.join(result_dir, fGithub_CETANGZHI_flowainew__trading_service__20260114_114646backtest-result-{strategy_name}.jsonGithub_CETANGZHI_flowainew__trading_service__20260114_114646)

            if os.path.exists(result_file):
                try:
                    with open(result_file, Github_CETANGZHI_flowainew__trading_service__20260114_114646rGithub_CETANGZHI_flowainew__trading_service__20260114_114646, encoding=Github_CETANGZHI_flowainew__trading_service__20260114_114646utf-8Github_CETANGZHI_flowainew__trading_service__20260114_114646) as f:
                        raw_data = json.load(f)
                    return self._parse_freqtrade_result(raw_data)
                except Exception as parse_error:
                    logger.error(
                        Github_CETANGZHI_flowainew__trading_service__20260114_114646Error parsing Freqtrade result file %s: %sGithub_CETANGZHI_flowainew__trading_service__20260114_114646,
                        result_file,
                        parse_error,
                        exc_info=True,
                    )
            else:
                logger.warning(Github_CETANGZHI_flowainew__trading_service__20260114_114646Freqtrade result file not found: %sGithub_CETANGZHI_flowainew__trading_service__20260114_114646, result_file)

            # 如果结果文件不存在或解析失败，返回基础指标，避免前端直接报错
            return {
                Github_CETANGZHI_flowainew__trading_service__20260114_114646total_tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646winning_tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646losing_tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646win_rateGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646total_profitGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646total_profit_percentGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646avg_profitGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_profitGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_lossGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646sharpe_ratioGithub_CETANGZHI_flowainew__trading_service__20260114_114646: None,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_drawdownGithub_CETANGZHI_flowainew__trading_service__20260114_114646: None,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_drawdown_percentGithub_CETANGZHI_flowainew__trading_service__20260114_114646: None,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: []
            }

        except subprocess.TimeoutExpired:
            raise Exception(Github_CETANGZHI_flowainew__trading_service__20260114_114646回测超时（超过5分钟）Github_CETANGZHI_flowainew__trading_service__20260114_114646)
        except Exception as e:
            logger.error(fGithub_CETANGZHI_flowainew__trading_service__20260114_114646Error executing backtest: {e}Github_CETANGZHI_flowainew__trading_service__20260114_114646, exc_info=True)
            raise

    def _parse_freqtrade_result(self, raw_data: Dict[str, Any]) -> Dict[str, Any]:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646解析Freqtrade原始JSON输出为统一的回测结果结构Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        try:
            strategy_dict = raw_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646strategyGithub_CETANGZHI_flowainew__trading_service__20260114_114646) or {}
            if not strategy_dict:
                raise ValueError(Github_CETANGZHI_flowainew__trading_service__20260114_114646Freqtrade result JSON 缺少 'strategy' 字段Github_CETANGZHI_flowainew__trading_service__20260114_114646)

            # 目前只支持单策略，取第一个策略的数据
            first_strategy_key = next(iter(strategy_dict))
            strategy_data = strategy_dict.get(first_strategy_key, {})

            total_trades = strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646, 0) or 0
            winning_trades = strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646winsGithub_CETANGZHI_flowainew__trading_service__20260114_114646, 0) or 0
            losing_trades = strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646lossesGithub_CETANGZHI_flowainew__trading_service__20260114_114646, 0) or 0
            win_rate = (winning_trades / total_trades * 100) if total_trades else 0.0

            return {
                Github_CETANGZHI_flowainew__trading_service__20260114_114646total_tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: total_trades,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646winning_tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: winning_trades,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646losing_tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: losing_trades,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646win_rateGithub_CETANGZHI_flowainew__trading_service__20260114_114646: win_rate,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646total_profitGithub_CETANGZHI_flowainew__trading_service__20260114_114646: strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646profit_totalGithub_CETANGZHI_flowainew__trading_service__20260114_114646, 0.0) or 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646total_profit_percentGithub_CETANGZHI_flowainew__trading_service__20260114_114646: strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646profit_total_pctGithub_CETANGZHI_flowainew__trading_service__20260114_114646, 0.0) or 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646avg_profitGithub_CETANGZHI_flowainew__trading_service__20260114_114646: strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646profit_meanGithub_CETANGZHI_flowainew__trading_service__20260114_114646, 0.0) or 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_profitGithub_CETANGZHI_flowainew__trading_service__20260114_114646: strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646profit_maxGithub_CETANGZHI_flowainew__trading_service__20260114_114646, 0.0) or 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_lossGithub_CETANGZHI_flowainew__trading_service__20260114_114646: strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646profit_minGithub_CETANGZHI_flowainew__trading_service__20260114_114646, 0.0) or 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646sharpe_ratioGithub_CETANGZHI_flowainew__trading_service__20260114_114646: strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646sharpeGithub_CETANGZHI_flowainew__trading_service__20260114_114646),
                # Freqtrade的回撤字段命名可能因版本不同，这里做兼容处理
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_drawdownGithub_CETANGZHI_flowainew__trading_service__20260114_114646: strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646max_drawdownGithub_CETANGZHI_flowainew__trading_service__20260114_114646, strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646max_drawdown_absGithub_CETANGZHI_flowainew__trading_service__20260114_114646)),
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_drawdown_percentGithub_CETANGZHI_flowainew__trading_service__20260114_114646: strategy_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646max_drawdown_pctGithub_CETANGZHI_flowainew__trading_service__20260114_114646),
                # 保留原始交易列表，供前端或Artifact做更深入的分析
                Github_CETANGZHI_flowainew__trading_service__20260114_114646tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: raw_data.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646, []),
            }
        except Exception as e:
            logger.error(Github_CETANGZHI_flowainew__trading_service__20260114_114646Error parsing Freqtrade JSON result: %sGithub_CETANGZHI_flowainew__trading_service__20260114_114646, e, exc_info=True)
            # 解析失败时，返回基础结构，避免调用方崩溃
            return {
                Github_CETANGZHI_flowainew__trading_service__20260114_114646total_tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646winning_tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646losing_tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646win_rateGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646total_profitGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646total_profit_percentGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646avg_profitGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_profitGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_lossGithub_CETANGZHI_flowainew__trading_service__20260114_114646: 0.0,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646sharpe_ratioGithub_CETANGZHI_flowainew__trading_service__20260114_114646: None,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_drawdownGithub_CETANGZHI_flowainew__trading_service__20260114_114646: None,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646max_drawdown_percentGithub_CETANGZHI_flowainew__trading_service__20260114_114646: None,
                Github_CETANGZHI_flowainew__trading_service__20260114_114646tradesGithub_CETANGZHI_flowainew__trading_service__20260114_114646: [],
            }

    async def _save_task_status(self, task_id: str, status: Dict[str, Any]) -> None:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        保存任务状态到Redis

        Args:
            task_id: 任务ID
            status: 状态信息
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        cache_key = fGithub_CETANGZHI_flowainew__trading_service__20260114_114646backtest:task:{task_id}Github_CETANGZHI_flowainew__trading_service__20260114_114646
        self.redis.set(cache_key, status, ttl=3600 * 24)  # 保存24小时

    async def get_task_status(self, task_id: str) -> Optional[Dict[str, Any]]:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        获取任务状态

        Args:
            task_id: 任务ID

        Returns:
            任务状态
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        cache_key = fGithub_CETANGZHI_flowainew__trading_service__20260114_114646backtest:task:{task_id}Github_CETANGZHI_flowainew__trading_service__20260114_114646
        return self.redis.get(cache_key)

    async def generate_strategy_code(
        self,
        description: str,
        indicators: List[str] = None
    ) -> str:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        生成策略代码 (使用 AI 动态生成)

        Args:
            description: 策略描述
            indicators: 技术指标列表

        Returns:
            策略代码
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        from langchain_openai import ChatOpenAI
        from langchain_core.prompts import ChatPromptTemplate
        from langchain_core.output_parsers import StrOutputParser

        if not settings.DEEPSEEK_API_KEY:
            logger.warning(Github_CETANGZHI_flowainew__trading_service__20260114_114646DEEPSEEK_API_KEY not set, falling back to template.Github_CETANGZHI_flowainew__trading_service__20260114_114646)
            return self._get_fallback_strategy_template()

        try:
            from app.services.model_registry import get_model_registry
            
            # 使用 ModelRegistry 获取 trading 专用模型
            model_registry = get_model_registry()
            trading_model = model_registry.get(Github_CETANGZHI_flowainew__trading_service__20260114_114646ai.model.tradingGithub_CETANGZHI_flowainew__trading_service__20260114_114646, settings.DEEPSEEK_MODEL)
            
            # 初始化 LLM
            llm = ChatOpenAI(
                model=trading_model,
                openai_api_base=settings.DEEPSEEK_API_BASE,
                openai_api_key=settings.DEEPSEEK_API_KEY,
                temperature=0.7,
                max_tokens=4096,
            )

            # 构建提示词
            indicators_str = Github_CETANGZHI_flowainew__trading_service__20260114_114646, Github_CETANGZHI_flowainew__trading_service__20260114_114646.join(indicators) if indicators else Github_CETANGZHI_flowainew__trading_service__20260114_114646常用技术指标(RSI, MACD, Bollinger Bands)Github_CETANGZHI_flowainew__trading_service__20260114_114646
            
            system_prompt = Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646你是一个专业的加密货币量化交易策略专家，精通 Freqtrade 框架。
你的任务是根据用户的描述，编写一个完整的、可运行的 Freqtrade 策略代码。

要求：
1. 必须继承 `IStrategy` 类。
2. 必须包含 `minimal_roi`, `stoploss`, `timeframe` 等必要属性。
3. 必须实现 `populate_indicators`, `populate_entry_trend`, `populate_exit_trend` 方法。
4. 代码必须符合 Python 语法规范，并包含必要的 import 语句。
5. 只返回 Python 代码，不要包含 Markdown 代码块标记（如 ```python ... ```），也不要包含其他解释性文字。
6. 确保代码可以直接保存为 .py 文件并被 Freqtrade 加载。
Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646

            user_prompt = fGithub_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
请根据以下要求生成一个 Freqtrade 策略：

策略描述：
{description}

建议使用的指标：
{indicators_str}

请编写完整的策略代码：
Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
            
            prompt = ChatPromptTemplate.from_messages([
                (Github_CETANGZHI_flowainew__trading_service__20260114_114646systemGithub_CETANGZHI_flowainew__trading_service__20260114_114646, system_prompt),
                (Github_CETANGZHI_flowainew__trading_service__20260114_114646userGithub_CETANGZHI_flowainew__trading_service__20260114_114646, user_prompt),
            ])

            chain = prompt | llm | StrOutputParser()

            logger.info(fGithub_CETANGZHI_flowainew__trading_service__20260114_114646Generating strategy code for: {description}Github_CETANGZHI_flowainew__trading_service__20260114_114646)
            code = await chain.ainvoke({})
            
            # 清理可能存在的 Markdown 标记
            code = code.replace(Github_CETANGZHI_flowainew__trading_service__20260114_114646```pythonGithub_CETANGZHI_flowainew__trading_service__20260114_114646, Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646).replace(Github_CETANGZHI_flowainew__trading_service__20260114_114646```Github_CETANGZHI_flowainew__trading_service__20260114_114646, Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646).strip()
            
            # 简单的有效性检查
            if Github_CETANGZHI_flowainew__trading_service__20260114_114646classGithub_CETANGZHI_flowainew__trading_service__20260114_114646 not in code or Github_CETANGZHI_flowainew__trading_service__20260114_114646(IStrategy)Github_CETANGZHI_flowainew__trading_service__20260114_114646 not in code:
                logger.warning(Github_CETANGZHI_flowainew__trading_service__20260114_114646Generated code seems invalid, falling back to template.Github_CETANGZHI_flowainew__trading_service__20260114_114646)
                return self._get_fallback_strategy_template()
                
            return code

        except Exception as e:
            logger.error(fGithub_CETANGZHI_flowainew__trading_service__20260114_114646Error generating strategy code with AI: {e}Github_CETANGZHI_flowainew__trading_service__20260114_114646, exc_info=True)
            return self._get_fallback_strategy_template()

    def _get_fallback_strategy_template(self) -> str:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646获取后备策略模板Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        return '''
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta
import pandas_ta as pta

class Github_CETANGZHI_flowainew__trading_service__20260114_114646(IStrategy):
    Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
    AI生成的策略 (Fallback Template)
    Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646

    # 策略参数
    minimal_roi = {
        Github_CETANGZHI_flowainew__trading_service__20260114_1146460Github_CETANGZHI_flowainew__trading_service__20260114_114646: 0.10,
        Github_CETANGZHI_flowainew__trading_service__20260114_11464630Github_CETANGZHI_flowainew__trading_service__20260114_114646: 0.05,
        Github_CETANGZHI_flowainew__trading_service__20260114_11464660Github_CETANGZHI_flowainew__trading_service__20260114_114646: 0.02,
        Github_CETANGZHI_flowainew__trading_service__20260114_114646120Github_CETANGZHI_flowainew__trading_service__20260114_114646: 0.01
    }

    stoploss = -0.05
    timeframe = '1h'

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646添加技术指标Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)

        # MACD
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']

        # Bollinger Bands
        bollinger = ta.BBANDS(dataframe, timeperiod=20)
        dataframe['bb_upper'] = bollinger['upperband']
        dataframe['bb_middle'] = bollinger['middleband']
        dataframe['bb_lower'] = bollinger['lowerband']

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646入场信号Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        dataframe.loc[
            (
                (dataframe['rsi'] < 30) &
                (dataframe['macd'] > dataframe['macdsignal'])
            ),
            'enter_long'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646出场信号Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
        dataframe.loc[
            (
                (dataframe['rsi'] > 70) |
                (dataframe['macd'] < dataframe['macdsignal'])
            ),
            'exit_long'] = 1

        return dataframe
'''


# 全局交易服务实例
trading_service = TradingService()


def get_trading_service() -> TradingService:
    Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
    获取交易服务实例

    用于FastAPI依赖注入

    Returns:
        TradingService: 交易服务实例
    Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646Github_CETANGZHI_flowainew__trading_service__20260114_114646
    return trading_service

