diff --git a/README.md b/README.md index 812ce84..5db7bc3 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,169 @@
- QuantDinger -

QuantDinger

-

- A local-first quant research & trading workspace: market data, indicators, AI analysis, backtesting, and strategy execution in one place. + + QuantDinger Logo + + +

QuantDinger

+ +

+ 🤖 AI-Native · 🔒 Privacy-First · 🚀 All-in-One Quant Workspace

-

- Website - · - Live Demo +

+ The Next-Gen Local Quant Platform: Multi-Market Data, AI Research, Visual Backtesting, and Automated Trading.

-

- License - Backend - Frontend - Demo + +

+ Website · + Live Demo · + Report Bug · + Discussions +

+ +

+ License + Python + Vue + Docker + Stars +

+ +

+ Telegram + Discord + X

--- -This repository is intentionally simple (no PHP gateway): it contains **one Python backend** and **one web UI**. +## 📖 Introduction -- **`backend_api_python/`**: Flask API + strategy runtime + AI agents -- **`quantdinger_vue/`**: Vue 2 UI (Ant Design Vue based) with charts, backtests, and strategy management +**QuantDinger** is a **Local-First** quantitative trading workspace designed for traders, researchers, and geeks. -> This repo does not include real secrets. Configure API keys and credentials via `.env` / environment variables. +Unlike expensive SaaS platforms, QuantDinger returns **data ownership** to you. It features a built-in **LLM-based Multi-Agent Research Team** that autonomously gathers financial intelligence from the web, combines it with local market data, generates professional analysis reports, and seamlessly integrates with your strategy development, backtesting, and live trading workflows. + +### Core Value +- **🛡️ Privacy First**: All strategies, trading logs, and API keys are stored locally in your SQLite database. +- **🧠 AI Empowered**: Not just code completion, but a real AI Research Analyst (powered by OpenRouter/LLM). +- **⚡ Multi-Market**: Native support for **Crypto**, **US Stocks**, **CN/HK Stocks**, **Forex**, and **Futures**. +- **🔌 Out-of-the-Box**: One-click deployment via Docker. No complex environment setup required. --- -### Why QuantDinger +## 📸 Visual Tour -- **Local-first (SQLite) out of the box**: no external database required to run a full workflow locally. -- **AI research team in code**: multi-agent analysis produces structured reports (with optional web search + LLMs). -- **Multi-market data layer**: a factory-based data source abstraction for crypto, US stocks, CN/HK stocks, forex, and futures. -- **From signals to execution**: strategy runtime + a pending-order worker to dispatch queued actions reliably. +
+

📊 Professional Quant Dashboard

+

Real-time monitoring of market dynamics, assets, and strategy status.

+ QuantDinger Dashboard +
+ +
+ + + + + + + + + + +
+

🤖 AI Deep Research

+

Multi-agent collaboration for market sentiment & technical analysis.

+ AI Market Analysis +
+

💬 Smart Trading Assistant

+

Natural language interface for instant market insights.

+ Trading Assistant +
+

📈 Interactive Indicator Analysis

+

Rich library of technical indicators with drag-and-drop analysis.

+ Indicator Analysis +
+

🐍 Python Strategy Gen

+

Built-in editor with AI-assisted strategy coding.

+ Code Generation +
--- -### Highlights (What You Get) +## ✨ Key Features -- **Multi-market market data** - - Data source factory in `backend_api_python/app/data_sources/`. - - Optional proxy support for restricted networks (see `.env`). +### 1. Universal Data Engine +Stop worrying about data APIs. QuantDinger features a powerful Data Source Factory pattern: +- **Crypto**: Seamless integration with **CCXT**, supporting Binance, OKX, Bybit, and 100+ exchanges. +- **Stocks**: Integrates Yahoo Finance, Finnhub, Tiingo (US), and AkShare (CN/HK). +- **Futures/Forex**: Supports OANDA and major futures data sources. +- **Proxy Support**: Built-in proxy configuration for restricted network environments. -- **AI multi-agent analysis** - - Coordinator + role agents in `backend_api_python/app/services/agents/`. - - Optional web search (Google/Bing) and LLM access (OpenRouter). +### 2. AI Multi-Agent Research +Your tireless team of analysts: +- **Coordinator Agent**: Decomposes tasks and manages workflows. +- **Research Agent**: Performs full-web searches (Google/Bing) for macro news. +- **Crypto/Stock Agent**: Specializes in technical and capital flow analysis for specific markets. +- **Report Generation**: Automatically produces structured Daily/Weekly research reports. -- **Indicator engine + backtesting** - - Indicator code storage with safe execution utilities. - - Backtest endpoints + persisted run history (SQLite). +### 3. Robust Strategy Runtime +- **Thread-Based Executor**: Independent thread pool management for strategy execution. +- **Auto-Restore**: Automatically resumes running strategies after system restarts. +- **Pending Order Worker**: Reliable background queue ensures precise signal execution and prevents slippage. -- **Strategy runtime** - - Thread-based executor with startup auto-restore (configurable). - - Background pending-order worker that polls queued orders and dispatches signals (can be disabled). +### 4. Modern Tech Stack +- **Backend**: Python (Flask) + SQLite + Redis (Optional) — Simple, powerful, extensible. +- **Frontend**: Vue 2 + Ant Design Vue + KlineCharts/ECharts — Responsive and interactive. +- **Deployment**: Docker Compose orchestration. -- **Live trading integrations** - - Exchange execution adapters in `backend_api_python/app/services/live_trading/` (CCXT-based where applicable). +--- -- **Local auth (single-user)** - - Simple login (`/login`) with env-configured admin credentials (JWT token). +## 🏦 Supported Exchanges + +Powered by the robust CCXT library, we support nearly all major cryptocurrency exchanges: + +| Binance | OKX | Bybit | Bitget | Gate.io | KuCoin | +|:---:|:---:|:---:|:---:|:---:|:---:| +| | | | | | | + +> Plus Kraken, Huobi, Coinbase Pro, and 100+ others. + +--- + +### Multi-Language Support + +
+ Show supported languages + +QuantDinger is built for a global audience with comprehensive internationalization: + +

+ English + Simplified Chinese + Traditional Chinese + Japanese + Korean + German + French + Thai + Vietnamese + Arabic +

+ +All UI elements, error messages, and documentation are fully translated. Language is auto-detected based on browser settings or can be manually switched in the app. + +
+ +--- + +### Supported Markets + +| Market Type | Data Sources | Trading | +|-------------|--------------|---------| +| **Cryptocurrency** | Binance, OKX, Bitget, + 100 exchanges | ✅ Full support | +| **US Stocks** | Yahoo Finance, Finnhub, Tiingo | ✅ Via broker API | +| **CN/HK Stocks** | AkShare, East Money | ⚡ Data only | +| **Forex** | Finnhub, OANDA | ✅ Via broker API | +| **Futures** | Exchange APIs, AkShare | ⚡ Data only | --- @@ -98,25 +202,247 @@ This repository is intentionally simple (no PHP gateway): it contains **one Pyth --- -### Quick Start (Local Development) +## Quick Start + +### Option 1: Docker Deployment (Recommended) + +The fastest way to get QuantDinger running. + +#### 1. Prepare Configuration + +Linux/macOS: + +```bash +cp docker.env.example backend_api_python/.env +nano backend_api_python/.env +``` + +Windows PowerShell: + +```powershell +Copy-Item docker.env.example backend_api_python/.env +notepad backend_api_python/.env +``` + +**Required settings:** +- `SECRET_KEY` - Application secret, use a random string +- `ADMIN_USER` / `ADMIN_PASSWORD` - Login credentials +- `OPENROUTER_API_KEY` - OpenRouter API key (required for AI analysis) + +#### 2. Build and Start + +```bash +# Build images and start (first run) +docker-compose up -d --build + +# Subsequent starts (no rebuild needed) +docker-compose up -d +``` + +#### 3. Access the Application + +- **Frontend UI**: http://localhost +- **Backend API**: http://localhost:5000 + +#### Docker Commands Reference + +```bash +# View running status +docker-compose ps + +# View logs +docker-compose logs -f + +# View backend logs only +docker-compose logs -f backend + +# View frontend logs only +docker-compose logs -f frontend + +# Stop services +docker-compose down + +# Stop and remove volumes (WARNING: deletes database!) +docker-compose down -v + +# Restart services +docker-compose restart + +# Rebuild and restart +docker-compose up -d --build + +# Enter backend container +docker exec -it quantdinger-backend /bin/bash + +# Enter frontend container +docker exec -it quantdinger-frontend /bin/sh +``` + +#### Docker Architecture + +``` +┌─────────────────┐ ┌─────────────────┐ +│ Frontend │ │ Backend │ +│ (Nginx) │────▶│ (Python) │ +│ Port: 80 │ │ Port: 5000 │ +└─────────────────┘ └─────────────────┘ + │ │ + └───────────────────────┘ + Docker Network +``` + +- **Frontend**: Vue.js app served by Nginx, proxies API requests to backend +- **Backend**: Python Flask API service + +#### Data Persistence + +The following data is mounted to the host and persists across container restarts: + +```yaml +volumes: + - ./backend_api_python/quantdinger.db:/app/quantdinger.db # Database + - ./backend_api_python/logs:/app/logs # Logs + - ./backend_api_python/data:/app/data # Data directory + - ./backend_api_python/.env:/app/.env # Configuration +``` + +#### Customization + +**Change ports** - Edit `docker-compose.yml`: + +```yaml +services: + frontend: + ports: + - "8080:80" # Change to port 8080 + + backend: + ports: + - "5001:5000" # Change to port 5001 +``` + +**Configure HTTPS** - Use a reverse proxy (like Caddy/Nginx): + +```bash +# Using Caddy (automatic HTTPS) +caddy reverse-proxy --from yourdomain.com --to localhost:80 +``` + +#### Production Recommendations + +**Security:** + +```bash +# Generate strong SECRET_KEY +openssl rand -hex 32 + +# Set secure admin password +ADMIN_PASSWORD=your-very-secure-password +``` + +**Resource limits** - Add to `docker-compose.yml`: + +```yaml +services: + backend: + deploy: + resources: + limits: + cpus: '2' + memory: 2G + reservations: + cpus: '0.5' + memory: 512M +``` + +**Log management:** + +```yaml +services: + backend: + logging: + driver: "json-file" + options: + max-size: "100m" + max-file: "3" +``` + +#### Docker Troubleshooting + +**Frontend can't connect to backend:** + +```bash +docker-compose logs backend +curl http://localhost:5000/api/health +``` + +**Database permission issues:** + +```bash +chmod 666 backend_api_python/quantdinger.db +``` + +**Build failures:** + +```bash +# Clear Docker cache and rebuild +docker-compose build --no-cache +``` + +**Out of memory:** + +```bash +# Check memory usage +docker stats + +# Add swap space (Linux) +sudo fallocate -l 2G /swapfile +sudo chmod 600 /swapfile +sudo mkswap /swapfile +sudo swapon /swapfile +``` + +#### Updating + +```bash +# Pull latest code +git pull + +# Rebuild and restart +docker-compose up -d --build +``` + +#### Backup + +```bash +# Backup database +cp backend_api_python/quantdinger.db backup/quantdinger_$(date +%Y%m%d).db + +# Backup configuration +cp backend_api_python/.env backup/.env_$(date +%Y%m%d) +``` + +--- + +### Option 2: Local Development **Prerequisites** - Python 3.10+ recommended - Node.js 16+ recommended -#### 1) Start the backend (Flask API) +#### 1. Start the backend (Flask API) ```bash cd backend_api_python pip install -r requirements.txt -copy env.example .env # Windows PowerShell users can use: Copy-Item env.example .env +cp env.example .env # Windows: copy env.example .env python run.py ``` Backend will be available at `http://localhost:5000`. -#### 2) Start the frontend (Vue UI) +#### 2. Start the frontend (Vue UI) ```bash cd quantdinger_vue @@ -146,8 +472,8 @@ Use `backend_api_python/env.example` as a template. Common settings include: The backend provides REST endpoints for login, market data, indicators, backtesting, strategies, and AI analysis. -- Health: `GET /health` -- Auth: `POST /login`, `POST /logout`, `GET /info` +- Health: `GET /health` (also supports `GET /api/health` for deployment probes) +- Auth (frontend-compatible): `POST /api/user/login`, `POST /api/user/logout`, `GET /api/user/info` For the full route list, see `backend_api_python/app/routes/`. @@ -159,17 +485,74 @@ Licensed under the **Apache License 2.0**. See `LICENSE`. --- +## 🤝 Community & Support + +Join our global community for strategy sharing and technical support: + +- **Telegram (Official)**: [t.me/worldinbroker](https://t.me/worldinbroker) +- **Discord**: [Join Server](https://discord.gg/cn6HVE2KC) +- **YouTube**: [@quantdinger](https://youtube.com/@quantdinger) +- **Email**: [brokermr810@gmail.com](mailto:brokermr810@gmail.com) +- **GitHub Issues**: [Report bugs / Request features](https://github.com/brokermr810/QuantDinger/issues) + +--- + +## ☕ Support the Project + +If QuantDinger helps you profit, consider buying the developers a coffee. Your support keeps the project alive! + +
+ View Crypto Donation Addresses + + **ERC-20 / BEP-20 / Polygon / Arbitrum** + ``` + 0x96fa4962181bea077f8c7240efe46afbe73641a7 + ``` + USDT + ETH +
+ +--- + +### Commercial Services + +
+ Show services + +We offer professional services to help you get the most out of QuantDinger: + +| Service | Description | +|---------|-------------| +| **Deployment & Setup** | One-on-one assistance with server deployment, configuration, and optimization | +| **Custom Strategy Development** | Tailored trading strategies designed for your specific needs and markets | +| **Enterprise Upgrade** | Commercial license, priority support, and advanced features for businesses | +| **Training & Consulting** | Hands-on training sessions and strategic consulting for your trading team | + +**Interested?** Contact us via: +- 📧 Email: [brokermr810@gmail.com](mailto:brokermr810@gmail.com) +- 💬 Telegram: [@worldinbroker](https://t.me/worldinbroker) + +
+ +--- + ### Acknowledgements -QuantDinger stands on the shoulders of great open-source projects, including: - -- **Flask** / **flask-cors** -- **Pandas** -- **CCXT** -- **yfinance**, **akshare**, **requests** -- **Vue 2** and **Ant Design Vue** -- Charting libraries used in the UI (e.g., **KlineCharts**) - -Thanks to maintainers and contributors across these ecosystems. +QuantDinger stands on the shoulders of great open-source projects: +| Project | Description | Link | +|---------|-------------|------| +| **Flask** | Lightweight WSGI web framework | [flask.palletsprojects.com](https://flask.palletsprojects.com/) | +| **flask-cors** | Cross-Origin Resource Sharing extension | [GitHub](https://github.com/corydolphin/flask-cors) | +| **Pandas** | Data analysis and manipulation library | [pandas.pydata.org](https://pandas.pydata.org/) | +| **CCXT** | Cryptocurrency exchange trading library | [github.com/ccxt/ccxt](https://github.com/ccxt/ccxt) | +| **yfinance** | Yahoo Finance market data downloader | [github.com/ranaroussi/yfinance](https://github.com/ranaroussi/yfinance) | +| **akshare** | China financial data interface | [github.com/akfamily/akshare](https://github.com/akfamily/akshare) | +| **requests** | HTTP library for Python | [requests.readthedocs.io](https://requests.readthedocs.io/) | +| **Vue.js** | Progressive JavaScript framework | [vuejs.org](https://vuejs.org/) | +| **Ant Design Vue** | Enterprise-class UI components | [antdv.com](https://antdv.com/) | +| **KlineCharts** | Lightweight financial charting library | [github.com/klinecharts/KLineChart](https://github.com/klinecharts/KLineChart) | +| **Lightweight Charts** | TradingView charting library | [github.com/nicepkg/lightweight-charts](https://github.com/nicepkg/lightweight-charts) | +| **ECharts** | Apache data visualization library | [echarts.apache.org](https://echarts.apache.org/) | +Thanks to all maintainers and contributors across these ecosystems! ❤️ diff --git a/backend_api_python/.dockerignore b/backend_api_python/.dockerignore new file mode 100644 index 0000000..0ac2652 --- /dev/null +++ b/backend_api_python/.dockerignore @@ -0,0 +1,44 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +venv/ +.venv/ +ENV/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# Logs +logs/ +*.log + +# Database (在 docker-compose 中挂载) +*.db + +# Environment +.env +.env.local + +# Data (在 docker-compose 中挂载) +data/memory/*.db + +# Git +.git/ +.gitignore + +# Tests +tests/ +pytest_cache/ +.coverage + +# Documentation +*.md +!README.md + diff --git a/backend_api_python/Dockerfile b/backend_api_python/Dockerfile new file mode 100644 index 0000000..b1f3871 --- /dev/null +++ b/backend_api_python/Dockerfile @@ -0,0 +1,35 @@ +# QuantDinger Backend API Dockerfile +FROM python:3.11-slim + +# Set working directory +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc \ + libffi-dev \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Copy dependency file +COPY requirements.txt . + +# Install Python dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY . . + +# Create log and data directories +RUN mkdir -p logs data/memory + +# Expose port +EXPOSE 5000 + +# Environment variables +ENV PYTHONUNBUFFERED=1 +ENV PYTHON_API_HOST=0.0.0.0 +ENV PYTHON_API_PORT=5000 + +# Start command +CMD ["python", "run.py"] diff --git a/backend_api_python/app/routes/__init__.py b/backend_api_python/app/routes/__init__.py index 538ebe4..15f4764 100644 --- a/backend_api_python/app/routes/__init__.py +++ b/backend_api_python/app/routes/__init__.py @@ -17,6 +17,7 @@ def register_routes(app: Flask): from app.routes.ai_chat import ai_chat_bp from app.routes.indicator import indicator_bp from app.routes.dashboard import dashboard_bp + from app.routes.settings import settings_bp app.register_blueprint(health_bp) app.register_blueprint(auth_bp, url_prefix='/api/user') # 兼容前端 /api/user/login @@ -29,4 +30,5 @@ def register_routes(app: Flask): app.register_blueprint(strategy_bp, url_prefix='/api') app.register_blueprint(credentials_bp, url_prefix='/api/credentials') app.register_blueprint(dashboard_bp, url_prefix='/api/dashboard') + app.register_blueprint(settings_bp, url_prefix='/api/settings') diff --git a/backend_api_python/app/routes/dashboard.py b/backend_api_python/app/routes/dashboard.py index fc68182..a872587 100644 --- a/backend_api_python/app/routes/dashboard.py +++ b/backend_api_python/app/routes/dashboard.py @@ -32,6 +32,13 @@ def _safe_int(v: Any, default: int) -> int: return default +def _safe_float(v: Any, default: float = 0.0) -> float: + try: + return float(v) + except Exception: + return default + + def _safe_json_loads(value: Any, default: Any) -> Any: if value is None: return default @@ -96,6 +103,157 @@ def _calc_pnl_percent(entry_price: float, size: float, pnl: float, leverage: flo return 0.0 +def _compute_performance_stats(trades: List[Dict[str, Any]]) -> Dict[str, Any]: + """ + Compute performance statistics from trade history. + Returns: { + total_trades, winning_trades, losing_trades, win_rate, + total_profit, total_loss, profit_factor, + avg_win, avg_loss, avg_trade, + max_win, max_loss, max_drawdown, max_drawdown_pct + } + """ + total_trades = len(trades) + if total_trades == 0: + return { + "total_trades": 0, + "winning_trades": 0, + "losing_trades": 0, + "win_rate": 0.0, + "total_profit": 0.0, + "total_loss": 0.0, + "profit_factor": 0.0, + "avg_win": 0.0, + "avg_loss": 0.0, + "avg_trade": 0.0, + "max_win": 0.0, + "max_loss": 0.0, + "max_drawdown": 0.0, + "max_drawdown_pct": 0.0, + "best_day": 0.0, + "worst_day": 0.0, + } + + profits = [_safe_float(t.get("profit"), 0.0) for t in trades] + wins = [p for p in profits if p > 0] + losses = [p for p in profits if p < 0] + + winning_trades = len(wins) + losing_trades = len(losses) + win_rate = (winning_trades / total_trades * 100) if total_trades > 0 else 0.0 + + total_profit = sum(wins) if wins else 0.0 + total_loss = abs(sum(losses)) if losses else 0.0 + profit_factor = (total_profit / total_loss) if total_loss > 0 else (total_profit if total_profit > 0 else 0.0) + + avg_win = (total_profit / winning_trades) if winning_trades > 0 else 0.0 + avg_loss = (total_loss / losing_trades) if losing_trades > 0 else 0.0 + avg_trade = sum(profits) / total_trades if total_trades > 0 else 0.0 + + max_win = max(profits) if profits else 0.0 + max_loss = min(profits) if profits else 0.0 + + # Calculate max drawdown from cumulative equity + cumulative = [] + acc = 0.0 + for p in profits: + acc += p + cumulative.append(acc) + + peak = 0.0 + max_drawdown = 0.0 + for val in cumulative: + if val > peak: + peak = val + dd = peak - val + if dd > max_drawdown: + max_drawdown = dd + + max_drawdown_pct = (max_drawdown / peak * 100) if peak > 0 else 0.0 + + # Best/worst day + day_profits: Dict[str, float] = {} + for t in trades: + ts = _safe_int(t.get("created_at"), 0) + if ts <= 0: + continue + day = time.strftime("%Y-%m-%d", time.localtime(ts)) + profit = _safe_float(t.get("profit"), 0.0) + day_profits[day] = day_profits.get(day, 0.0) + profit + + best_day = max(day_profits.values()) if day_profits else 0.0 + worst_day = min(day_profits.values()) if day_profits else 0.0 + + return { + "total_trades": total_trades, + "winning_trades": winning_trades, + "losing_trades": losing_trades, + "win_rate": round(win_rate, 2), + "total_profit": round(total_profit, 2), + "total_loss": round(total_loss, 2), + "profit_factor": round(profit_factor, 2), + "avg_win": round(avg_win, 2), + "avg_loss": round(avg_loss, 2), + "avg_trade": round(avg_trade, 2), + "max_win": round(max_win, 2), + "max_loss": round(max_loss, 2), + "max_drawdown": round(max_drawdown, 2), + "max_drawdown_pct": round(max_drawdown_pct, 2), + "best_day": round(best_day, 2), + "worst_day": round(worst_day, 2), + } + + +def _compute_strategy_stats(trades: List[Dict[str, Any]], strategies: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + """ + Compute per-strategy statistics. + Only includes strategies that still exist (not deleted). + """ + # Build set of existing strategy IDs + existing_strategy_ids: set = set() + sid_to_name: Dict[int, str] = {} + sid_to_capital: Dict[int, float] = {} + for s in strategies: + sid = _safe_int(s.get("id"), 0) + if sid > 0: + existing_strategy_ids.add(sid) + sid_to_name[sid] = str(s.get("strategy_name") or f"Strategy_{sid}") + sid_to_capital[sid] = _safe_float(s.get("initial_capital"), 0.0) + + # Group trades by strategy (only for existing strategies) + sid_to_trades: Dict[int, List[Dict[str, Any]]] = {} + for t in trades: + sid = _safe_int(t.get("strategy_id"), 0) + # Skip trades from deleted strategies + if sid not in existing_strategy_ids: + continue + if sid not in sid_to_trades: + sid_to_trades[sid] = [] + sid_to_trades[sid].append(t) + + result = [] + for sid, strades in sid_to_trades.items(): + stats = _compute_performance_stats(strades) + total_pnl = sum(_safe_float(t.get("profit"), 0.0) for t in strades) + capital = sid_to_capital.get(sid, 0.0) + roi = (total_pnl / capital * 100) if capital > 0 else 0.0 + + result.append({ + "strategy_id": sid, + "strategy_name": sid_to_name.get(sid, f"Strategy_{sid}"), + "total_trades": stats["total_trades"], + "win_rate": stats["win_rate"], + "profit_factor": stats["profit_factor"], + "total_pnl": round(total_pnl, 2), + "roi": round(roi, 2), + "max_drawdown": stats["max_drawdown"], + }) + + # Sort by total PnL descending + result.sort(key=lambda x: x.get("total_pnl", 0), reverse=True) + return result + + @dashboard_bp.route("/summary", methods=["GET"]) def summary(): """ @@ -183,12 +341,18 @@ def summary(): FROM qd_strategy_trades t LEFT JOIN qd_strategies_trading s ON s.id = t.strategy_id ORDER BY t.created_at DESC - LIMIT 200 + LIMIT 500 """ ) recent_trades = cur.fetchall() or [] cur.close() + # Compute performance statistics + perf_stats = _compute_performance_stats(recent_trades) + + # Compute per-strategy statistics + strategy_stats = _compute_strategy_stats(recent_trades, strategies) + # Total equity/pnl (best-effort) total_initial_capital = 0.0 for s in strategies: @@ -196,7 +360,10 @@ def summary(): total_initial_capital += float(s.get("initial_capital") or 0.0) except Exception: pass - total_pnl = float(total_unrealized_pnl) + + # Include realized PnL from trades + total_realized_pnl = sum(_safe_float(t.get("profit"), 0.0) for t in recent_trades) + total_pnl = float(total_unrealized_pnl + total_realized_pnl) total_equity = float(total_initial_capital + total_pnl) # Daily PnL chart (uses realized profit field if present, otherwise 0) @@ -223,6 +390,80 @@ def summary(): sid_to_unreal[sid] = float(sid_to_unreal.get(sid, 0.0) + float(p.get("unrealized_pnl") or 0.0)) strategy_pnl_chart = [{"name": sid_to_name[sid], "value": float(val)} for sid, val in sid_to_unreal.items()] + # Monthly returns for heatmap + month_to_profit: Dict[str, float] = {} + for trow in recent_trades: + ts = _safe_int(trow.get("created_at"), 0) + if ts <= 0: + continue + month = time.strftime("%Y-%m", time.localtime(ts)) + try: + p = float(trow.get("profit") or 0.0) + except Exception: + p = 0.0 + month_to_profit[month] = month_to_profit.get(month, 0.0) + p + monthly_returns = [{"month": m, "profit": round(v, 2)} for m, v in sorted(month_to_profit.items())] + + # Hourly distribution + hour_to_count: Dict[int, int] = {} + hour_to_profit: Dict[int, float] = {} + for trow in recent_trades: + ts = _safe_int(trow.get("created_at"), 0) + if ts <= 0: + continue + hour = int(time.strftime("%H", time.localtime(ts))) + hour_to_count[hour] = hour_to_count.get(hour, 0) + 1 + hour_to_profit[hour] = hour_to_profit.get(hour, 0.0) + _safe_float(trow.get("profit"), 0.0) + hourly_distribution = [ + {"hour": h, "count": hour_to_count.get(h, 0), "profit": round(hour_to_profit.get(h, 0.0), 2)} + for h in range(24) + ] + + # Calendar data: organized by month for monthly calendar view + # Format: { "2024-01": { "days": { "01": 123.45, "02": -50.0, ... }, "total": 500.0 }, ... } + import calendar as cal_module + from datetime import datetime, timedelta + + calendar_data: Dict[str, Dict[str, Any]] = {} + for d, p in day_to_profit.items(): + try: + dt = datetime.strptime(d, "%Y-%m-%d") + month_key = dt.strftime("%Y-%m") + day_num = dt.strftime("%d") + if month_key not in calendar_data: + # Get number of days in month + year, month = int(dt.strftime("%Y")), int(dt.strftime("%m")) + _, days_in_month = cal_module.monthrange(year, month) + # Get first day of month (0=Monday, 6=Sunday) + first_weekday = cal_module.monthrange(year, month)[0] + calendar_data[month_key] = { + "year": year, + "month": month, + "days_in_month": days_in_month, + "first_weekday": first_weekday, # 0=Mon, 6=Sun + "days": {}, + "total": 0.0, + "win_days": 0, + "lose_days": 0, + } + calendar_data[month_key]["days"][day_num] = round(p, 2) + calendar_data[month_key]["total"] = round(calendar_data[month_key]["total"] + p, 2) + if p > 0: + calendar_data[month_key]["win_days"] += 1 + elif p < 0: + calendar_data[month_key]["lose_days"] += 1 + except Exception: + pass + + # Convert to sorted list for frontend + calendar_months = [] + for month_key in sorted(calendar_data.keys(), reverse=True): + data = calendar_data[month_key] + calendar_months.append({ + "month_key": month_key, + **data + }) + return jsonify( { "code": 1, @@ -230,11 +471,22 @@ def summary(): "data": { "ai_strategy_count": int(ai_enabled_strategy_count), "indicator_strategy_count": int(indicator_strategy_count), - "total_equity": float(total_equity), - "total_pnl": float(total_pnl), + "total_equity": round(total_equity, 2), + "total_pnl": round(total_pnl, 2), + "total_realized_pnl": round(total_realized_pnl, 2), + "total_unrealized_pnl": round(total_unrealized_pnl, 2), + # Performance KPIs + "performance": perf_stats, + # Strategy-level stats + "strategy_stats": strategy_stats, + # Chart data "daily_pnl_chart": daily_pnl_chart, "strategy_pnl_chart": strategy_pnl_chart, - "recent_trades": recent_trades, + "monthly_returns": monthly_returns, + "hourly_distribution": hourly_distribution, + "calendar_months": calendar_months, # Monthly calendar data + # Lists + "recent_trades": recent_trades[:100], # Limit for frontend "current_positions": current_positions, }, } @@ -383,4 +635,3 @@ def delete_pending_order(order_id: int): except Exception as e: logger.error(f"dashboard delete pendingOrders failed: {e}", exc_info=True) return jsonify({"code": 0, "msg": str(e), "data": None}), 500 - diff --git a/backend_api_python/app/routes/health.py b/backend_api_python/app/routes/health.py index 0977b20..3d9fd12 100644 --- a/backend_api_python/app/routes/health.py +++ b/backend_api_python/app/routes/health.py @@ -26,3 +26,8 @@ def health_check(): 'timestamp': datetime.now().isoformat() }) + +@health_bp.route('/api/health', methods=['GET']) +def api_health_check(): + """兼容路径:用于容器健康检查/反代探针等场景。""" + return health_check() diff --git a/backend_api_python/app/routes/settings.py b/backend_api_python/app/routes/settings.py new file mode 100644 index 0000000..e2e91f3 --- /dev/null +++ b/backend_api_python/app/routes/settings.py @@ -0,0 +1,363 @@ +""" +Settings API - 读取和保存 .env 配置 +""" +import os +import re +from flask import Blueprint, request, jsonify +from app.utils.logger import get_logger +from app.utils.config_loader import clear_config_cache + +logger = get_logger(__name__) + +settings_bp = Blueprint('settings', __name__) + +# .env 文件路径 +ENV_FILE_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), '.env') + +# 配置项定义(分组)- 完整对照 env.example +CONFIG_SCHEMA = { + 'auth': { + 'title': '认证配置', + 'items': [ + {'key': 'SECRET_KEY', 'label': 'Secret Key', 'type': 'password', 'default': 'quantdinger-secret-key-change-me'}, + {'key': 'ADMIN_USER', 'label': '管理员用户名', 'type': 'text', 'default': 'quantdinger'}, + {'key': 'ADMIN_PASSWORD', 'label': '管理员密码', 'type': 'password', 'default': '123456'}, + ] + }, + 'server': { + 'title': '服务器配置', + 'items': [ + {'key': 'PYTHON_API_HOST', 'label': '监听地址', 'type': 'text', 'default': '0.0.0.0'}, + {'key': 'PYTHON_API_PORT', 'label': '端口', 'type': 'number', 'default': '5000'}, + {'key': 'PYTHON_API_DEBUG', 'label': '调试模式', 'type': 'boolean', 'default': 'False'}, + ] + }, + 'worker': { + 'title': '订单处理配置', + 'items': [ + {'key': 'ENABLE_PENDING_ORDER_WORKER', 'label': '启用订单处理Worker', 'type': 'boolean', 'default': 'True'}, + {'key': 'PENDING_ORDER_STALE_SEC', 'label': '订单超时时间(秒)', 'type': 'number', 'default': '90'}, + ] + }, + 'notification': { + 'title': '信号通知配置', + 'items': [ + {'key': 'SIGNAL_WEBHOOK_URL', 'label': 'Webhook URL', 'type': 'text', 'required': False}, + {'key': 'SIGNAL_WEBHOOK_TOKEN', 'label': 'Webhook Token', 'type': 'password', 'required': False}, + {'key': 'SIGNAL_NOTIFY_TIMEOUT_SEC', 'label': '通知超时(秒)', 'type': 'number', 'default': '6'}, + {'key': 'TELEGRAM_BOT_TOKEN', 'label': 'Telegram Bot Token', 'type': 'password', 'required': False, 'link': 'https://t.me/BotFather', 'link_text': 'settings.link.createBot'}, + ] + }, + 'smtp': { + 'title': '邮件SMTP配置', + 'items': [ + {'key': 'SMTP_HOST', 'label': 'SMTP服务器', 'type': 'text', 'required': False}, + {'key': 'SMTP_PORT', 'label': 'SMTP端口', 'type': 'number', 'default': '587'}, + {'key': 'SMTP_USER', 'label': 'SMTP用户名', 'type': 'text', 'required': False}, + {'key': 'SMTP_PASSWORD', 'label': 'SMTP密码', 'type': 'password', 'required': False}, + {'key': 'SMTP_FROM', 'label': '发件人地址', 'type': 'text', 'required': False}, + {'key': 'SMTP_USE_TLS', 'label': '使用TLS', 'type': 'boolean', 'default': 'True'}, + {'key': 'SMTP_USE_SSL', 'label': '使用SSL', 'type': 'boolean', 'default': 'False'}, + ] + }, + 'twilio': { + 'title': 'Twilio短信配置', + 'items': [ + {'key': 'TWILIO_ACCOUNT_SID', 'label': 'Account SID', 'type': 'password', 'required': False, 'link': 'https://console.twilio.com/', 'link_text': 'settings.link.getApi'}, + {'key': 'TWILIO_AUTH_TOKEN', 'label': 'Auth Token', 'type': 'password', 'required': False}, + {'key': 'TWILIO_FROM_NUMBER', 'label': '发送号码', 'type': 'text', 'required': False}, + ] + }, + 'strategy': { + 'title': '策略执行配置', + 'items': [ + {'key': 'DISABLE_RESTORE_RUNNING_STRATEGIES', 'label': '禁用自动恢复策略', 'type': 'boolean', 'default': 'False'}, + {'key': 'STRATEGY_TICK_INTERVAL_SEC', 'label': '策略Tick间隔(秒)', 'type': 'number', 'default': '10'}, + {'key': 'PRICE_CACHE_TTL_SEC', 'label': '价格缓存TTL(秒)', 'type': 'number', 'default': '10'}, + ] + }, + 'proxy': { + 'title': '代理配置', + 'items': [ + {'key': 'PROXY_PORT', 'label': '代理端口', 'type': 'text', 'required': False}, + {'key': 'PROXY_HOST', 'label': '代理主机', 'type': 'text', 'default': '127.0.0.1'}, + {'key': 'PROXY_SCHEME', 'label': '代理协议', 'type': 'select', 'options': ['socks5h', 'socks5', 'http', 'https'], 'default': 'socks5h'}, + {'key': 'PROXY_URL', 'label': '完整代理URL', 'type': 'text', 'required': False}, + ] + }, + 'app': { + 'title': '应用配置', + 'items': [ + {'key': 'CORS_ORIGINS', 'label': 'CORS来源', 'type': 'text', 'default': '*'}, + {'key': 'RATE_LIMIT', 'label': '速率限制(每分钟)', 'type': 'number', 'default': '100'}, + {'key': 'ENABLE_CACHE', 'label': '启用缓存', 'type': 'boolean', 'default': 'False'}, + {'key': 'ENABLE_REQUEST_LOG', 'label': '启用请求日志', 'type': 'boolean', 'default': 'True'}, + {'key': 'ENABLE_AI_ANALYSIS', 'label': '启用AI分析', 'type': 'boolean', 'default': 'True'}, + ] + }, + 'ai': { + 'title': 'AI/LLM配置', + 'items': [ + {'key': 'OPENROUTER_API_KEY', 'label': 'OpenRouter API Key', 'type': 'password', 'required': False, 'link': 'https://openrouter.ai/keys', 'link_text': 'settings.link.getApiKey'}, + {'key': 'OPENROUTER_API_URL', 'label': 'OpenRouter API URL', 'type': 'text', 'default': 'https://openrouter.ai/api/v1/chat/completions'}, + {'key': 'OPENROUTER_MODEL', 'label': '默认模型', 'type': 'text', 'default': 'openai/gpt-4o', 'link': 'https://openrouter.ai/models', 'link_text': 'settings.link.viewModels'}, + {'key': 'OPENROUTER_TEMPERATURE', 'label': 'Temperature', 'type': 'number', 'default': '0.7'}, + {'key': 'OPENROUTER_MAX_TOKENS', 'label': 'Max Tokens', 'type': 'number', 'default': '4000'}, + {'key': 'OPENROUTER_TIMEOUT', 'label': '超时时间(秒)', 'type': 'number', 'default': '300'}, + {'key': 'OPENROUTER_CONNECT_TIMEOUT', 'label': '连接超时(秒)', 'type': 'number', 'default': '30'}, + {'key': 'AI_MODELS_JSON', 'label': '模型列表(JSON)', 'type': 'text', 'default': '{}', 'required': False}, + ] + }, + 'market': { + 'title': '市场预设', + 'items': [ + {'key': 'MARKET_TYPES_JSON', 'label': '市场类型(JSON)', 'type': 'text', 'default': '[]', 'required': False}, + {'key': 'TRADING_SUPPORTED_SYMBOLS_JSON', 'label': '支持的交易对(JSON)', 'type': 'text', 'default': '[]', 'required': False}, + ] + }, + 'data_source': { + 'title': '数据源配置', + 'items': [ + {'key': 'DATA_SOURCE_TIMEOUT', 'label': '数据源超时(秒)', 'type': 'number', 'default': '30'}, + {'key': 'DATA_SOURCE_RETRY', 'label': '重试次数', 'type': 'number', 'default': '3'}, + {'key': 'DATA_SOURCE_RETRY_BACKOFF', 'label': '重试退避(秒)', 'type': 'number', 'default': '0.5'}, + {'key': 'FINNHUB_API_KEY', 'label': 'Finnhub API Key', 'type': 'password', 'required': False, 'link': 'https://finnhub.io/register', 'link_text': 'settings.link.freeRegister'}, + {'key': 'FINNHUB_TIMEOUT', 'label': 'Finnhub超时(秒)', 'type': 'number', 'default': '10'}, + {'key': 'FINNHUB_RATE_LIMIT', 'label': 'Finnhub速率限制', 'type': 'number', 'default': '60'}, + {'key': 'CCXT_DEFAULT_EXCHANGE', 'label': 'CCXT默认交易所', 'type': 'text', 'default': 'binance', 'link': 'https://github.com/ccxt/ccxt#supported-cryptocurrency-exchange-markets', 'link_text': 'settings.link.supportedExchanges'}, + {'key': 'CCXT_TIMEOUT', 'label': 'CCXT超时(ms)', 'type': 'number', 'default': '10000'}, + {'key': 'CCXT_PROXY', 'label': 'CCXT代理', 'type': 'text', 'required': False}, + {'key': 'AKSHARE_TIMEOUT', 'label': 'Akshare超时(秒)', 'type': 'number', 'default': '30'}, + {'key': 'YFINANCE_TIMEOUT', 'label': 'YFinance超时(秒)', 'type': 'number', 'default': '30'}, + {'key': 'TIINGO_API_KEY', 'label': 'Tiingo API Key', 'type': 'password', 'required': False, 'link': 'https://www.tiingo.com/account/api/token', 'link_text': 'settings.link.getToken'}, + {'key': 'TIINGO_TIMEOUT', 'label': 'Tiingo超时(秒)', 'type': 'number', 'default': '10'}, + ] + }, + 'search': { + 'title': '搜索配置', + 'items': [ + {'key': 'SEARCH_PROVIDER', 'label': '搜索提供商', 'type': 'select', 'options': ['google', 'bing', 'none'], 'default': 'google'}, + {'key': 'SEARCH_MAX_RESULTS', 'label': '最大结果数', 'type': 'number', 'default': '10'}, + {'key': 'SEARCH_GOOGLE_API_KEY', 'label': 'Google API Key', 'type': 'password', 'required': False, 'link': 'https://developers.google.com/custom-search/v1/introduction', 'link_text': 'settings.link.applyApi'}, + {'key': 'SEARCH_GOOGLE_CX', 'label': 'Google CX', 'type': 'text', 'required': False, 'link': 'https://programmablesearchengine.google.com/controlpanel/all', 'link_text': 'settings.link.createSearchEngine'}, + {'key': 'SEARCH_BING_API_KEY', 'label': 'Bing API Key', 'type': 'password', 'required': False, 'link': 'https://www.microsoft.com/en-us/bing/apis/bing-web-search-api', 'link_text': 'settings.link.applyApi'}, + {'key': 'INTERNAL_API_KEY', 'label': '内部API Key', 'type': 'password', 'required': False}, + ] + }, +} + + +def read_env_file(): + """读取 .env 文件""" + env_values = {} + + if not os.path.exists(ENV_FILE_PATH): + logger.warning(f".env file not found at {ENV_FILE_PATH}") + return env_values + + try: + with open(ENV_FILE_PATH, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + # 跳过空行和注释 + if not line or line.startswith('#'): + continue + # 解析 KEY=VALUE + if '=' in line: + key, value = line.split('=', 1) + key = key.strip() + value = value.strip() + # 移除引号 + if (value.startswith('"') and value.endswith('"')) or \ + (value.startswith("'") and value.endswith("'")): + value = value[1:-1] + env_values[key] = value + except Exception as e: + logger.error(f"Failed to read .env file: {e}") + + return env_values + + +def write_env_file(env_values): + """写入 .env 文件,保留注释和格式""" + lines = [] + existing_keys = set() + + # 读取原文件保留格式 + if os.path.exists(ENV_FILE_PATH): + try: + with open(ENV_FILE_PATH, 'r', encoding='utf-8') as f: + for line in f: + original_line = line + stripped = line.strip() + + # 保留空行和注释 + if not stripped or stripped.startswith('#'): + lines.append(original_line) + continue + + # 更新已存在的键 + if '=' in stripped: + key = stripped.split('=', 1)[0].strip() + if key in env_values: + existing_keys.add(key) + value = env_values[key] + # 如果值包含特殊字符,用引号包裹 + if ' ' in str(value) or '"' in str(value) or "'" in str(value): + lines.append(f'{key}="{value}"\n') + else: + lines.append(f'{key}={value}\n') + else: + lines.append(original_line) + else: + lines.append(original_line) + except Exception as e: + logger.error(f"Failed to read .env file for update: {e}") + + # 添加新的键 + new_keys = set(env_values.keys()) - existing_keys + if new_keys: + if lines and not lines[-1].endswith('\n'): + lines.append('\n') + lines.append('\n# Added by Settings UI\n') + for key in sorted(new_keys): + value = env_values[key] + if ' ' in str(value) or '"' in str(value) or "'" in str(value): + lines.append(f'{key}="{value}"\n') + else: + lines.append(f'{key}={value}\n') + + # 写入文件 + try: + with open(ENV_FILE_PATH, 'w', encoding='utf-8') as f: + f.writelines(lines) + return True + except Exception as e: + logger.error(f"Failed to write .env file: {e}") + return False + + +@settings_bp.route('/schema', methods=['GET']) +def get_settings_schema(): + """获取配置项定义""" + return jsonify({ + 'code': 1, + 'msg': 'success', + 'data': CONFIG_SCHEMA + }) + + +@settings_bp.route('/values', methods=['GET']) +def get_settings_values(): + """获取当前配置值 - 包括敏感信息(真实值)""" + env_values = read_env_file() + + # 构建返回数据,返回真实值 + result = {} + for group_key, group in CONFIG_SCHEMA.items(): + result[group_key] = {} + for item in group['items']: + key = item['key'] + value = env_values.get(key, item.get('default', '')) + result[group_key][key] = value + # 标记密码类型是否已配置 + if item['type'] == 'password': + result[group_key][f'{key}_configured'] = bool(value) + + return jsonify({ + 'code': 1, + 'msg': 'success', + 'data': result + }) + + +@settings_bp.route('/save', methods=['POST']) +def save_settings(): + """保存配置""" + try: + data = request.get_json() + if not data: + return jsonify({'code': 0, 'msg': '无效的请求数据'}) + + # 读取当前配置 + current_env = read_env_file() + + # 更新配置 + updates = {} + for group_key, group_values in data.items(): + if group_key not in CONFIG_SCHEMA: + continue + + for item in CONFIG_SCHEMA[group_key]['items']: + key = item['key'] + if key in group_values: + new_value = group_values[key] + + # 空值处理 + if new_value is None or new_value == '': + if not item.get('required', True): + updates[key] = '' + else: + updates[key] = str(new_value) + + # 合并更新 + current_env.update(updates) + + # 写入文件 + if write_env_file(current_env): + # 清除配置缓存 + clear_config_cache() + + return jsonify({ + 'code': 1, + 'msg': '配置保存成功', + 'data': { + 'updated_keys': list(updates.keys()), + 'requires_restart': True # 标记需要重启 + } + }) + else: + return jsonify({'code': 0, 'msg': '保存配置失败'}) + + except Exception as e: + logger.error(f"Failed to save settings: {e}") + return jsonify({'code': 0, 'msg': f'保存失败: {str(e)}'}) + + +@settings_bp.route('/test-connection', methods=['POST']) +def test_connection(): + """测试API连接""" + try: + data = request.get_json() + service = data.get('service') + + if service == 'openrouter': + # 测试 OpenRouter 连接 + from app.services.llm import LLMService + llm = LLMService() + result = llm.test_connection() + if result: + return jsonify({'code': 1, 'msg': 'OpenRouter连接成功'}) + else: + return jsonify({'code': 0, 'msg': 'OpenRouter连接失败'}) + + elif service == 'finnhub': + # 测试 Finnhub 连接 + import requests + api_key = data.get('api_key') or os.getenv('FINNHUB_API_KEY') + if not api_key: + return jsonify({'code': 0, 'msg': 'API Key未配置'}) + resp = requests.get( + f'https://finnhub.io/api/v1/quote?symbol=AAPL&token={api_key}', + timeout=10 + ) + if resp.status_code == 200: + return jsonify({'code': 1, 'msg': 'Finnhub连接成功'}) + else: + return jsonify({'code': 0, 'msg': f'Finnhub连接失败: {resp.status_code}'}) + + return jsonify({'code': 0, 'msg': '未知服务'}) + + except Exception as e: + logger.error(f"Connection test failed: {e}") + return jsonify({'code': 0, 'msg': f'测试失败: {str(e)}'}) diff --git a/backend_api_python/app/services/strategy.py b/backend_api_python/app/services/strategy.py index 1f87923..142d147 100644 --- a/backend_api_python/app/services/strategy.py +++ b/backend_api_python/app/services/strategy.py @@ -593,6 +593,7 @@ class StrategyService: indicator_config = payload.get('indicator_config') if payload.get('indicator_config') is not None else (existing.get('indicator_config') or {}) trading_config = payload.get('trading_config') if payload.get('trading_config') is not None else (existing.get('trading_config') or {}) exchange_config = payload.get('exchange_config') if payload.get('exchange_config') is not None else (existing.get('exchange_config') or {}) + ai_model_config = payload.get('ai_model_config') if payload.get('ai_model_config') is not None else (existing.get('ai_model_config') or {}) symbol = (trading_config or {}).get('symbol') timeframe = (trading_config or {}).get('timeframe') @@ -617,6 +618,7 @@ class StrategyService: exchange_config = ?, indicator_config = ?, trading_config = ?, + ai_model_config = ?, updated_at = ? WHERE id = ? """, @@ -633,6 +635,7 @@ class StrategyService: self._dump_json_or_encrypt(exchange_config, encrypt=False) if exchange_config else '', self._dump_json_or_encrypt(indicator_config, encrypt=False), self._dump_json_or_encrypt(trading_config, encrypt=False), + self._dump_json_or_encrypt(ai_model_config, encrypt=False), now, strategy_id ) diff --git a/backend_api_python/app/services/trading_executor.py b/backend_api_python/app/services/trading_executor.py index c030f35..8cebb52 100644 --- a/backend_api_python/app/services/trading_executor.py +++ b/backend_api_python/app/services/trading_executor.py @@ -9,7 +9,7 @@ try: import resource # Linux/Unix only except Exception: resource = None -from typing import Dict, List, Any, Optional +from typing import Dict, List, Any, Optional, Tuple from datetime import datetime import json from decimal import Decimal, ROUND_DOWN, ROUND_UP @@ -486,10 +486,12 @@ class TradingExecutor: # 初始化策略状态 trading_config = strategy['trading_config'] indicator_config = strategy['indicator_config'] + ai_model_config = strategy.get('ai_model_config') or {} execution_mode = (strategy.get('execution_mode') or 'signal').strip().lower() if execution_mode not in ['signal', 'live']: execution_mode = 'signal' notification_config = strategy.get('notification_config') or {} + strategy_name = strategy.get('strategy_name') or f"strategy_{int(strategy_id)}" symbol = trading_config.get('symbol', '') timeframe = trading_config.get('timeframe', '1H') @@ -943,6 +945,7 @@ class TradingExecutor: ok = self._execute_signal( strategy_id=strategy_id, + strategy_name=strategy_name, exchange=exchange, symbol=symbol, current_price=execute_price, @@ -957,6 +960,7 @@ class TradingExecutor: execution_mode=execution_mode, notification_config=notification_config, trading_config=trading_config, + ai_model_config=ai_model_config, ) if ok: logger.info(f"Strategy {strategy_id} signal executed: {signal_type} @ {execute_price}") @@ -1005,7 +1009,7 @@ class TradingExecutor: id, strategy_name, strategy_type, status, initial_capital, leverage, decide_interval, execution_mode, notification_config, - indicator_config, exchange_config, trading_config + indicator_config, exchange_config, trading_config, ai_model_config FROM qd_strategies_trading WHERE id = %s """ @@ -1015,7 +1019,7 @@ class TradingExecutor: if strategy: # 解析JSON字段 - for field in ['indicator_config', 'trading_config', 'notification_config']: + for field in ['indicator_config', 'trading_config', 'notification_config', 'ai_model_config']: if isinstance(strategy.get(field), str): try: strategy[field] = json.loads(strategy[field]) @@ -1839,6 +1843,7 @@ class TradingExecutor: def _execute_signal( self, strategy_id: int, + strategy_name: str, exchange: Any, symbol: str, current_price: float, @@ -1855,6 +1860,7 @@ class TradingExecutor: execution_mode: str = 'signal', notification_config: Optional[Dict[str, Any]] = None, trading_config: Optional[Dict[str, Any]] = None, + ai_model_config: Optional[Dict[str, Any]] = None, signal_ts: int = 0, ): """执行具体的交易信号""" @@ -1868,11 +1874,49 @@ class TradingExecutor: if market_type == 'spot' and 'short' in signal_type: return False + sig = (signal_type or "").strip().lower() + + # 1.1 开仓 AI 过滤(仅 open_*) + if sig in ("open_long", "open_short") and self._is_entry_ai_filter_enabled(ai_model_config=ai_model_config, trading_config=trading_config): + ok_ai, ai_info = self._entry_ai_filter_allows( + strategy_id=strategy_id, + symbol=symbol, + signal_type=sig, + ai_model_config=ai_model_config, + trading_config=trading_config, + ) + if not ok_ai: + # Best-effort persist a browser notification so UI can show "HOLD due to AI filter". + reason = (ai_info or {}).get("reason") or "ai_filter_rejected" + ai_decision = (ai_info or {}).get("ai_decision") or "" + title = f"AI过滤拦截开仓 | {symbol}" + msg = f"策略信号={sig},AI决策={ai_decision or 'UNKNOWN'},原因={reason};已HOLD(不下单)" + self._persist_browser_notification( + strategy_id=strategy_id, + symbol=symbol, + signal_type="ai_filter_hold", + title=title, + message=msg, + payload={ + "event": "qd.ai_filter", + "strategy_id": int(strategy_id), + "strategy_name": str(strategy_name or ""), + "symbol": str(symbol or ""), + "signal_type": str(sig), + "ai_decision": str(ai_decision), + "reason": str(reason), + "signal_ts": int(signal_ts or 0), + }, + ) + logger.info( + f"AI entry filter rejected: strategy_id={strategy_id} symbol={symbol} signal={sig} ai={ai_decision} reason={reason}" + ) + return False + # 2. 计算下单数量 available_capital = self._get_available_capital(strategy_id, initial_capital) amount = 0.0 - sig = (signal_type or "").strip().lower() # Frontend position sizing alignment: # - open_* uses entry_pct from trading_config if provided (0~1 or 0~100 are both accepted) @@ -2006,6 +2050,164 @@ class TradingExecutor: logger.error(f"Failed to execute signal: {e}") return False + def _is_entry_ai_filter_enabled(self, *, ai_model_config: Optional[Dict[str, Any]], trading_config: Optional[Dict[str, Any]]) -> bool: + """Detect whether the strategy enabled 'AI filter on entry (open positions only)'.""" + amc = ai_model_config if isinstance(ai_model_config, dict) else {} + tc = trading_config if isinstance(trading_config, dict) else {} + + # Accept multiple key names for forward/backward compatibility. + candidates = [ + amc.get("entry_ai_filter_enabled"), + amc.get("entryAiFilterEnabled"), + amc.get("ai_filter_enabled"), + amc.get("aiFilterEnabled"), + amc.get("enable_ai_filter"), + amc.get("enableAiFilter"), + tc.get("entry_ai_filter_enabled"), + tc.get("ai_filter_enabled"), + tc.get("enable_ai_filter"), + tc.get("enableAiFilter"), + ] + for v in candidates: + if v is None: + continue + if isinstance(v, bool): + return bool(v) + s = str(v).strip().lower() + if s in ("1", "true", "yes", "y", "on", "enabled"): + return True + if s in ("0", "false", "no", "n", "off", "disabled"): + return False + return False + + def _entry_ai_filter_allows( + self, + *, + strategy_id: int, + symbol: str, + signal_type: str, + ai_model_config: Optional[Dict[str, Any]], + trading_config: Optional[Dict[str, Any]], + ) -> Tuple[bool, Dict[str, Any]]: + """ + Run internal AI analysis and decide whether an entry signal is allowed. + + Returns: + (allowed, info) + - allowed: True -> proceed; False -> hold (reject open) + - info: {ai_decision, reason, analysis_error?} + """ + amc = ai_model_config if isinstance(ai_model_config, dict) else {} + tc = trading_config if isinstance(trading_config, dict) else {} + + # Market for AnalysisService. Live trading executor is Crypto-focused. + market = str(amc.get("market") or amc.get("analysis_market") or "Crypto").strip() or "Crypto" + + # Optional model override (OpenRouter model id) + model = amc.get("model") or amc.get("openrouter_model") or amc.get("openrouterModel") or None + model = str(model).strip() if model else None + + # Prefer zh-CN for local UI; can be overridden. + language = amc.get("language") or amc.get("lang") or tc.get("language") or "zh-CN" + language = str(language or "zh-CN") + + try: + # Lazy import to avoid circular deps + heavy init unless the filter is enabled and entry signal happens. + from app.services.analysis import AnalysisService + + service = AnalysisService() + result = service.analyze(market, symbol, language, model=model) + + if isinstance(result, dict) and result.get("error"): + return False, {"ai_decision": "", "reason": "analysis_error", "analysis_error": str(result.get("error") or "")} + + ai_dec = self._extract_ai_trade_decision(result) + if not ai_dec: + return False, {"ai_decision": "", "reason": "missing_ai_decision"} + + expected = "BUY" if signal_type == "open_long" else "SELL" + if ai_dec == expected: + return True, {"ai_decision": ai_dec, "reason": "match"} + if ai_dec == "HOLD": + return False, {"ai_decision": ai_dec, "reason": "ai_hold"} + return False, {"ai_decision": ai_dec, "reason": "direction_mismatch"} + except Exception as e: + return False, {"ai_decision": "", "reason": "analysis_exception", "analysis_error": str(e)} + + def _extract_ai_trade_decision(self, analysis_result: Any) -> str: + """ + Normalize AI analysis output into one of: BUY / SELL / HOLD / "". + We primarily look at final_decision.decision, with fallbacks. + """ + if not isinstance(analysis_result, dict): + return "" + + def _pick(*paths: str) -> str: + for p in paths: + cur: Any = analysis_result + ok = True + for k in p.split("."): + if not isinstance(cur, dict): + ok = False + break + cur = cur.get(k) + if ok and cur is not None: + s = str(cur).strip() + if s: + return s + return "" + + raw = _pick("final_decision.decision", "trader_decision.decision", "decision", "final.decision") + s = raw.strip().upper() + if not s: + return "" + + # Common variants / synonyms + if "BUY" in s or s == "LONG" or "LONG" in s: + return "BUY" + if "SELL" in s or s == "SHORT" or "SHORT" in s: + return "SELL" + if "HOLD" in s or "WAIT" in s or "NEUTRAL" in s: + return "HOLD" + return s if s in ("BUY", "SELL", "HOLD") else "" + + def _persist_browser_notification( + self, + *, + strategy_id: int, + symbol: str, + signal_type: str, + title: str, + message: str, + payload: Optional[Dict[str, Any]] = None, + ) -> None: + """Best-effort persist notification row for the frontend '通知' panel (browser channel).""" + try: + now = int(time.time()) + with get_db_connection() as db: + cur = db.cursor() + cur.execute( + """ + INSERT INTO qd_strategy_notifications + (strategy_id, symbol, signal_type, channels, title, message, payload_json, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + int(strategy_id), + str(symbol or ""), + str(signal_type or ""), + "browser", + str(title or ""), + str(message or ""), + json.dumps(payload or {}, ensure_ascii=False), + int(now), + ), + ) + db.commit() + cur.close() + except Exception as e: + logger.warning(f"persist_browser_notification failed: {e}") + def _execute_exchange_order( self, exchange: Any, diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..dbd3c89 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,61 @@ +# QuantDinger Docker Compose +# Quick start: docker-compose up -d + +version: '3.8' + +services: + # Backend API Service + backend: + build: + context: ./backend_api_python + dockerfile: Dockerfile + container_name: quantdinger-backend + restart: unless-stopped + ports: + - "5000:5000" + volumes: + # Persistent database and logs + - ./backend_api_python/quantdinger.db:/app/quantdinger.db + - ./backend_api_python/logs:/app/logs + - ./backend_api_python/data:/app/data + # Configuration file (optional, for development) + - ./backend_api_python/.env:/app/.env:ro + environment: + - PYTHON_API_HOST=0.0.0.0 + - PYTHON_API_PORT=5000 + - TZ=Asia/Shanghai + networks: + - quantdinger-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5000/api/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Frontend Web Service + frontend: + build: + context: ./quantdinger_vue + dockerfile: Dockerfile + container_name: quantdinger-frontend + restart: unless-stopped + ports: + - "80:80" + depends_on: + - backend + networks: + - quantdinger-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/health"] + interval: 30s + timeout: 10s + retries: 3 + +networks: + quantdinger-network: + driver: bridge + +# Optional: Add data volumes (for more complex persistence) +# volumes: +# backend-data: +# backend-logs: diff --git a/docker.env.example b/docker.env.example new file mode 100644 index 0000000..f1f01b2 --- /dev/null +++ b/docker.env.example @@ -0,0 +1,79 @@ +# QuantDinger Docker Environment Configuration +# Copy this file to .env and modify the settings +# cp docker.env.example backend_api_python/.env + +# ========================= +# Authentication +# ========================= +SECRET_KEY=your-secure-secret-key-change-me +ADMIN_USER=admin +ADMIN_PASSWORD=your-secure-password + +# ========================= +# Network Configuration +# ========================= +PYTHON_API_HOST=0.0.0.0 +PYTHON_API_PORT=5000 +PYTHON_API_DEBUG=False +CORS_ORIGINS=* + +# ========================= +# OpenRouter / LLM (Required for AI analysis) +# ========================= +OPENROUTER_API_KEY=your-openrouter-api-key +OPENROUTER_API_URL=https://openrouter.ai/api/v1/chat/completions +OPENROUTER_MODEL=openai/gpt-4o +OPENROUTER_TEMPERATURE=0.7 +OPENROUTER_MAX_TOKENS=4000 +OPENROUTER_TIMEOUT=300 + +# ========================= +# Data Source API Keys (fill as needed) +# ========================= +# Finnhub (US stocks / forex) +FINNHUB_API_KEY= + +# Tiingo (optional backup data source) +TIINGO_API_KEY= + +# ========================= +# Notification Settings (optional) +# ========================= +# Telegram +TELEGRAM_BOT_TOKEN= + +# Email / SMTP +SMTP_HOST= +SMTP_PORT=587 +SMTP_USER= +SMTP_PASSWORD= +SMTP_FROM= + +# Twilio SMS +TWILIO_ACCOUNT_SID= +TWILIO_AUTH_TOKEN= +TWILIO_FROM_NUMBER= + +# Webhook +SIGNAL_WEBHOOK_URL= +SIGNAL_WEBHOOK_TOKEN= + +# ========================= +# Strategy Execution Settings +# ========================= +ENABLE_PENDING_ORDER_WORKER=true +STRATEGY_TICK_INTERVAL_SEC=10 + +# ========================= +# Search Engine (optional, for AI analysis) +# ========================= +SEARCH_PROVIDER=google +SEARCH_GOOGLE_API_KEY= +SEARCH_GOOGLE_CX= + +# ========================= +# Proxy Configuration (if network requires) +# ========================= +PROXY_PORT= +PROXY_HOST=127.0.0.1 +PROXY_SCHEME=socks5h diff --git a/docs/screenshots/.gitkeep b/docs/screenshots/.gitkeep new file mode 100644 index 0000000..139597f --- /dev/null +++ b/docs/screenshots/.gitkeep @@ -0,0 +1,2 @@ + + diff --git a/docs/screenshots/ai_analysis1.png b/docs/screenshots/ai_analysis1.png new file mode 100644 index 0000000..c09df11 Binary files /dev/null and b/docs/screenshots/ai_analysis1.png differ diff --git a/docs/screenshots/ai_analysis2.png b/docs/screenshots/ai_analysis2.png new file mode 100644 index 0000000..23a8924 Binary files /dev/null and b/docs/screenshots/ai_analysis2.png differ diff --git a/docs/screenshots/dashboard.png b/docs/screenshots/dashboard.png new file mode 100644 index 0000000..10672a1 Binary files /dev/null and b/docs/screenshots/dashboard.png differ diff --git a/docs/screenshots/indicator_analysis.png b/docs/screenshots/indicator_analysis.png new file mode 100644 index 0000000..d5e3977 Binary files /dev/null and b/docs/screenshots/indicator_analysis.png differ diff --git a/docs/screenshots/indicator_creat_python_code.png b/docs/screenshots/indicator_creat_python_code.png new file mode 100644 index 0000000..524e7cf Binary files /dev/null and b/docs/screenshots/indicator_creat_python_code.png differ diff --git a/docs/screenshots/trading_assistant.png b/docs/screenshots/trading_assistant.png new file mode 100644 index 0000000..58b5126 Binary files /dev/null and b/docs/screenshots/trading_assistant.png differ diff --git a/quantdinger_vue/.dockerignore b/quantdinger_vue/.dockerignore new file mode 100644 index 0000000..ac8548e --- /dev/null +++ b/quantdinger_vue/.dockerignore @@ -0,0 +1,41 @@ +# Dependencies +node_modules/ + +# Build output (会在容器内构建) +dist/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Environment +.env +.env.local +.env.*.local + +# Git +.git/ +.gitignore + +# Tests +tests/ +coverage/ +.nyc_output/ + +# Documentation +*.md +!README.md + +# OS +.DS_Store +Thumbs.db + diff --git a/quantdinger_vue/Dockerfile b/quantdinger_vue/Dockerfile new file mode 100644 index 0000000..cc9976e --- /dev/null +++ b/quantdinger_vue/Dockerfile @@ -0,0 +1,33 @@ +# QuantDinger Frontend Dockerfile +# Stage 1: Build +FROM node:18-alpine as builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ +COPY pnpm-lock.yaml* ./ +COPY yarn.lock* ./ + +# Install dependencies (prefer npm) +RUN npm install --legacy-peer-deps + +# Copy source code +COPY . . + +# Build production version +RUN npm run build + +# Stage 2: Production image (using nginx) +FROM nginx:alpine + +# Copy build artifacts +COPY --from=builder /app/dist /usr/share/nginx/html + +# Copy nginx configuration +COPY deploy/nginx-docker.conf /etc/nginx/conf.d/default.conf + +# Expose port +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/quantdinger_vue/deploy/nginx-docker.conf b/quantdinger_vue/deploy/nginx-docker.conf new file mode 100644 index 0000000..891ded4 --- /dev/null +++ b/quantdinger_vue/deploy/nginx-docker.conf @@ -0,0 +1,43 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # Gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + gzip_min_length 1000; + + # Static asset caching + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # API proxy to backend + location /api/ { + proxy_pass http://backend:5000/api/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 300s; + proxy_connect_timeout 75s; + } + + # SPA routing support + location / { + try_files $uri $uri/ /index.html; + } + + # Health check + location /health { + return 200 'OK'; + add_header Content-Type text/plain; + } +} diff --git a/quantdinger_vue/src/api/settings.js b/quantdinger_vue/src/api/settings.js new file mode 100644 index 0000000..2c6642d --- /dev/null +++ b/quantdinger_vue/src/api/settings.js @@ -0,0 +1,46 @@ +import request from '@/utils/request' + +/** + * 获取配置项定义 + */ +export function getSettingsSchema () { + return request({ + url: '/api/settings/schema', + method: 'get' + }) +} + +/** + * 获取当前配置值 + */ +export function getSettingsValues () { + return request({ + url: '/api/settings/values', + method: 'get' + }) +} + +/** + * 保存配置 + * @param {Object} data - 配置数据 + */ +export function saveSettings (data) { + return request({ + url: '/api/settings/save', + method: 'post', + data + }) +} + +/** + * 测试API连接 + * @param {string} service - 服务名称 (openrouter, finnhub, etc.) + * @param {Object} params - 额外参数 + */ +export function testConnection (service, params = {}) { + return request({ + url: '/api/settings/test-connection', + method: 'post', + data: { service, ...params } + }) +} diff --git a/quantdinger_vue/src/config/router.config.js b/quantdinger_vue/src/config/router.config.js index 0d7c994..f9c181f 100644 --- a/quantdinger_vue/src/config/router.config.js +++ b/quantdinger_vue/src/config/router.config.js @@ -43,6 +43,13 @@ export const asyncRouterMap = [ name: 'IndicatorCommunity', component: () => import('@/views/indicator-community'), meta: { title: 'menu.dashboard.community', keepAlive: false, icon: 'shop', permission: ['dashboard'] } + }, + // 系统设置 + { + path: '/settings', + name: 'Settings', + component: () => import('@/views/settings'), + meta: { title: 'menu.settings', keepAlive: false, icon: 'setting', permission: ['dashboard'] } } // other diff --git a/quantdinger_vue/src/layouts/BasicLayout.vue b/quantdinger_vue/src/layouts/BasicLayout.vue index f1ebc75..e122d3a 100644 --- a/quantdinger_vue/src/layouts/BasicLayout.vue +++ b/quantdinger_vue/src/layouts/BasicLayout.vue @@ -200,17 +200,17 @@ export default { // Static footer config (local OSS build) menuFooterConfig: { contact: { - support_url: 'https://github.com/', - feature_request_url: 'https://github.com/', - email: 'support@example.com', - live_chat_url: 'https://github.com/' + support_url: 'https://t.me/worldinbroker', + feature_request_url: 'https://github.com/brokermr810/QuantDinger/issues', + email: 'brokermr810@gmail.com', + live_chat_url: 'https://t.me/worldinbroker' }, social_accounts: [ - { name: 'GitHub', icon: 'github', url: 'https://github.com/' }, - { name: 'X', icon: 'x', url: 'https://x.com/' }, - { name: 'Discord', icon: 'discord', url: 'https://discord.com/' }, - { name: 'Telegram', icon: 'telegram', url: 'https://t.me/' }, - { name: 'YouTube', icon: 'youtube', url: 'https://www.youtube.com/' } + { name: 'GitHub', icon: 'github', url: 'https://github.com/brokermr810/QuantDinger' }, + { name: 'X', icon: 'x', url: 'https://x.com/HenryCryption' }, + { name: 'Discord', icon: 'discord', url: 'https://discord.gg/cn6HVE2KC' }, + { name: 'Telegram', icon: 'telegram', url: 'https://t.me/worldinbroker' }, + { name: 'YouTube', icon: 'youtube', url: 'https://youtube.com/@quantdinger' } ], legal: { user_agreement: '', diff --git a/quantdinger_vue/src/locales/lang/en-US.js b/quantdinger_vue/src/locales/lang/en-US.js index 9313c69..ca9337b 100644 --- a/quantdinger_vue/src/locales/lang/en-US.js +++ b/quantdinger_vue/src/locales/lang/en-US.js @@ -19,6 +19,7 @@ const locale = { 'menu.dashboard.indicator': 'Indicator Analysis', 'menu.dashboard.community': 'Indicator Community', 'menu.dashboard.tradingAssistant': 'Trading Assistant', + 'menu.settings': 'Settings', 'menu.dashboard.aiTradingAssistant': 'AI Trading Assistant', 'menu.dashboard.signalRobot': 'Signal Robot', 'menu.dashboard.monitor': 'Monitor', @@ -937,6 +938,41 @@ const locale = { 'dashboard.enabled': 'Enabled', 'dashboard.pnlHistory': 'P&L History', 'dashboard.strategyPerformance': 'Strategy Performance', + 'dashboard.strategyRanking': 'Strategy Ranking', + // New KPI labels + 'dashboard.winRate': 'Win Rate', + 'dashboard.profitFactor': 'Profit Factor', + 'dashboard.maxDrawdown': 'Max Drawdown', + 'dashboard.totalTrades': 'Total Trades', + 'dashboard.runningStrategies': 'Running Strategies', + 'dashboard.equityCurve': 'Equity Curve', + 'dashboard.strategyAllocation': 'Strategy Allocation', + 'dashboard.drawdownCurve': 'Drawdown Curve', + 'dashboard.drawdown': 'Drawdown', + 'dashboard.hourlyDistribution': 'Hourly Distribution', + // Chart labels + 'dashboard.dailyPnl': 'Daily P&L', + 'dashboard.cumulativePnl': 'Cumulative P&L', + 'dashboard.tradeCount': 'Trade Count', + 'dashboard.profit': 'Profit', + 'dashboard.noData': 'No Data', + 'dashboard.noStrategyData': 'No strategy data', + // Ranking labels + 'dashboard.ranking.totalProfit': 'Total Profit', + 'dashboard.ranking.roi': 'ROI', + 'dashboard.ranking.trades': 'Trades', + // Units and labels + 'dashboard.unit.trades': '', + 'dashboard.unit.strategies': '', + 'dashboard.label.avgDaily': 'Avg Daily', + 'dashboard.label.avgProfit': 'Avg Profit', + 'dashboard.label.win': 'W', + 'dashboard.label.lose': 'L', + 'dashboard.label.trade': 'Trades', + 'dashboard.label.indicator': 'Indicator', + 'dashboard.label.totalPnl': 'Total P&L', + 'dashboard.label.maxDrawdownPoint': 'Max DD', + 'dashboard.profitCalendar': 'Profit Calendar', 'dashboard.recentTrades': 'Recent Trades', 'dashboard.currentPositions': 'Current Positions', 'dashboard.table.time': 'Time', @@ -969,6 +1005,12 @@ const locale = { 'dashboard.orderTable.deleteConfirm': 'Delete this record?', 'dashboard.orderTable.deleteSuccess': 'Deleted', 'dashboard.orderTable.deleteFailed': 'Delete failed', + 'dashboard.newOrderNotify': 'New Order Alert', + 'dashboard.newOrderDesc': 'New order execution record', + 'dashboard.soundEnabled': 'Sound notification enabled', + 'dashboard.soundDisabled': 'Sound notification disabled', + 'dashboard.clickToMute': 'Click to mute', + 'dashboard.clickToUnmute': 'Click to unmute', 'dashboard.signalType.openLong': 'Open Long', 'dashboard.signalType.openShort': 'Open Short', 'dashboard.signalType.closeLong': 'Close Long', @@ -1817,7 +1859,124 @@ const locale = { 'signal-robot.indicator.volume': 'Volume', 'signal-robot.indicator.changePct1h': '1H Change %', 'signal-robot.indicator.changePct24h': '24H Change %', - 'signal-robot.indicator.signalLine': 'Signal Line' + 'signal-robot.indicator.signalLine': 'Signal Line', + + // Settings + 'settings.title': 'System Settings', + 'settings.description': 'Configure application settings, API keys, and system preferences', + 'settings.save': 'Save Settings', + 'settings.reset': 'Reset', + 'settings.saveSuccess': 'Settings saved successfully', + 'settings.saveFailed': 'Failed to save settings', + 'settings.loadFailed': 'Failed to load settings', + 'settings.default': 'Default', + 'settings.pleaseSelect': 'Please select', + 'settings.inputApiKey': 'Enter key', + 'settings.getApi': 'Get API', + // Settings link texts + 'settings.link.getApi': 'Get API', + 'settings.link.getApiKey': 'Get API Key', + 'settings.link.getToken': 'Get Token', + 'settings.link.createBot': 'Create Bot', + 'settings.link.viewModels': 'View Models', + 'settings.link.freeRegister': 'Free Register', + 'settings.link.supportedExchanges': 'Supported Exchanges', + 'settings.link.applyApi': 'Apply API', + 'settings.link.createSearchEngine': 'Create Search Engine', + 'settings.restartRequired': 'Settings saved. Some changes require Python service restart to take effect.', + 'settings.copyRestartCmd': 'Copy restart command', + 'settings.copySuccess': 'Copied', + 'settings.copyFailed': 'Copy failed', + // Settings groups + 'settings.group.auth': 'Authentication', + 'settings.group.server': 'Server Configuration', + 'settings.group.worker': 'Order Worker', + 'settings.group.notification': 'Signal Notification', + 'settings.group.smtp': 'Email SMTP', + 'settings.group.twilio': 'Twilio SMS', + 'settings.group.strategy': 'Strategy Execution', + 'settings.group.proxy': 'Proxy Configuration', + 'settings.group.app': 'Application', + 'settings.group.ai': 'AI/LLM Configuration', + 'settings.group.market': 'Market Presets', + 'settings.group.data_source': 'Data Sources', + 'settings.group.search': 'Search Configuration', + // Settings fields - Auth + 'settings.field.SECRET_KEY': 'Secret Key', + 'settings.field.ADMIN_USER': 'Admin Username', + 'settings.field.ADMIN_PASSWORD': 'Admin Password', + // Settings fields - Server + 'settings.field.PYTHON_API_HOST': 'Listen Address', + 'settings.field.PYTHON_API_PORT': 'Port', + 'settings.field.PYTHON_API_DEBUG': 'Debug Mode', + // Settings fields - Worker + 'settings.field.ENABLE_PENDING_ORDER_WORKER': 'Enable Order Worker', + 'settings.field.PENDING_ORDER_STALE_SEC': 'Order Stale Timeout (sec)', + // Settings fields - Notification + 'settings.field.SIGNAL_WEBHOOK_URL': 'Webhook URL', + 'settings.field.SIGNAL_WEBHOOK_TOKEN': 'Webhook Token', + 'settings.field.SIGNAL_NOTIFY_TIMEOUT_SEC': 'Notify Timeout (sec)', + 'settings.field.TELEGRAM_BOT_TOKEN': 'Telegram Bot Token', + // Settings fields - SMTP + 'settings.field.SMTP_HOST': 'SMTP Host', + 'settings.field.SMTP_PORT': 'SMTP Port', + 'settings.field.SMTP_USER': 'SMTP Username', + 'settings.field.SMTP_PASSWORD': 'SMTP Password', + 'settings.field.SMTP_FROM': 'From Address', + 'settings.field.SMTP_USE_TLS': 'Use TLS', + 'settings.field.SMTP_USE_SSL': 'Use SSL', + // Settings fields - Twilio + 'settings.field.TWILIO_ACCOUNT_SID': 'Account SID', + 'settings.field.TWILIO_AUTH_TOKEN': 'Auth Token', + 'settings.field.TWILIO_FROM_NUMBER': 'From Number', + // Settings fields - Strategy + 'settings.field.DISABLE_RESTORE_RUNNING_STRATEGIES': 'Disable Auto Restore', + 'settings.field.STRATEGY_TICK_INTERVAL_SEC': 'Tick Interval (sec)', + 'settings.field.PRICE_CACHE_TTL_SEC': 'Price Cache TTL (sec)', + // Settings fields - Proxy + 'settings.field.PROXY_PORT': 'Proxy Port', + 'settings.field.PROXY_HOST': 'Proxy Host', + 'settings.field.PROXY_SCHEME': 'Proxy Scheme', + 'settings.field.PROXY_URL': 'Full Proxy URL', + // Settings fields - App + 'settings.field.CORS_ORIGINS': 'CORS Origins', + 'settings.field.RATE_LIMIT': 'Rate Limit (per min)', + 'settings.field.ENABLE_CACHE': 'Enable Cache', + 'settings.field.ENABLE_REQUEST_LOG': 'Enable Request Log', + 'settings.field.ENABLE_AI_ANALYSIS': 'Enable AI Analysis', + // Settings fields - AI + 'settings.field.OPENROUTER_API_KEY': 'OpenRouter API Key', + 'settings.field.OPENROUTER_API_URL': 'OpenRouter API URL', + 'settings.field.OPENROUTER_MODEL': 'Default Model', + 'settings.field.OPENROUTER_TEMPERATURE': 'Temperature', + 'settings.field.OPENROUTER_MAX_TOKENS': 'Max Tokens', + 'settings.field.OPENROUTER_TIMEOUT': 'Timeout (sec)', + 'settings.field.OPENROUTER_CONNECT_TIMEOUT': 'Connect Timeout (sec)', + 'settings.field.AI_MODELS_JSON': 'Models JSON', + // Settings fields - Market + 'settings.field.MARKET_TYPES_JSON': 'Market Types JSON', + 'settings.field.TRADING_SUPPORTED_SYMBOLS_JSON': 'Supported Symbols JSON', + // Settings fields - Data Source + 'settings.field.DATA_SOURCE_TIMEOUT': 'Timeout (sec)', + 'settings.field.DATA_SOURCE_RETRY': 'Retry Count', + 'settings.field.DATA_SOURCE_RETRY_BACKOFF': 'Retry Backoff (sec)', + 'settings.field.FINNHUB_API_KEY': 'Finnhub API Key', + 'settings.field.FINNHUB_TIMEOUT': 'Finnhub Timeout (sec)', + 'settings.field.FINNHUB_RATE_LIMIT': 'Finnhub Rate Limit', + 'settings.field.CCXT_DEFAULT_EXCHANGE': 'CCXT Default Exchange', + 'settings.field.CCXT_TIMEOUT': 'CCXT Timeout (ms)', + 'settings.field.CCXT_PROXY': 'CCXT Proxy', + 'settings.field.AKSHARE_TIMEOUT': 'Akshare Timeout (sec)', + 'settings.field.YFINANCE_TIMEOUT': 'YFinance Timeout (sec)', + 'settings.field.TIINGO_API_KEY': 'Tiingo API Key', + 'settings.field.TIINGO_TIMEOUT': 'Tiingo Timeout (sec)', + // Settings fields - Search + 'settings.field.SEARCH_PROVIDER': 'Search Provider', + 'settings.field.SEARCH_MAX_RESULTS': 'Max Results', + 'settings.field.SEARCH_GOOGLE_API_KEY': 'Google API Key', + 'settings.field.SEARCH_GOOGLE_CX': 'Google CX', + 'settings.field.SEARCH_BING_API_KEY': 'Bing API Key', + 'settings.field.INTERNAL_API_KEY': 'Internal API Key' } export default { diff --git a/quantdinger_vue/src/locales/lang/zh-CN.js b/quantdinger_vue/src/locales/lang/zh-CN.js deleted file mode 100644 index 4ef8cdd..0000000 --- a/quantdinger_vue/src/locales/lang/zh-CN.js +++ /dev/null @@ -1,1751 +0,0 @@ -import antd from 'ant-design-vue/es/locale-provider/zh_CN' -import momentCN from 'moment/locale/zh-cn' - -const components = { - antLocale: antd, - momentName: 'zh-cn', - momentLocale: momentCN -} - -const locale = { - 'submit': '提交', - 'save': '保存', - 'submit.ok': '提交成功', - 'save.ok': '保存成功', - 'menu.welcome': '欢迎', - 'menu.home': '主页', - 'menu.dashboard': '仪表盘', - 'menu.dashboard.indicator': '指标分析', - 'menu.dashboard.community': '指标社区', - 'menu.dashboard.analysis': 'AI 分析', - 'menu.dashboard.tradingAssistant': '交易助手', - 'menu.dashboard.aiTradingAssistant': 'AI交易助手', - 'menu.dashboard.signalRobot': '信号机器人', - 'menu.dashboard.monitor': '监控页', - 'menu.dashboard.workplace': '工作台', - 'menu.form': '表单页', - 'menu.form.basic-form': '基础表单', - 'menu.form.step-form': '分步表单', - 'menu.form.step-form.info': '分步表单(填写转账信息)', - 'menu.form.step-form.confirm': '分步表单(确认转账信息)', - 'menu.form.step-form.result': '分步表单(完成)', - 'menu.form.advanced-form': '高级表单', - 'menu.list': '列表页', - 'menu.list.table-list': '查询表格', - 'menu.list.basic-list': '标准列表', - 'menu.list.card-list': '卡片列表', - 'menu.list.search-list': '搜索列表', - 'menu.list.search-list.articles': '搜索列表(文章)', - 'menu.list.search-list.projects': '搜索列表(项目)', - 'menu.list.search-list.applications': '搜索列表(应用)', - 'menu.profile': '详情页', - 'menu.profile.basic': '基础详情页', - 'menu.profile.advanced': '高级详情页', - 'menu.result': '结果页', - 'menu.result.success': '成功页', - 'menu.result.fail': '失败页', - 'menu.exception': '异常页', - 'menu.exception.not-permission': '403', - 'menu.exception.not-find': '404', - 'menu.exception.server-error': '500', - 'menu.exception.trigger': '触发错误', - 'menu.account': '个人页', - 'menu.account.center': '个人中心', - 'menu.account.settings': '个人设置', - 'menu.account.trigger': '触发报错', - 'menu.account.logout': '退出登录', - 'menu.wallet': '我的钱包', - 'menu.docs': '文档中心', - 'menu.docs.detail': '文档详情', - 'menu.header.refreshPage': '刷新页面', - 'menu.invite.friends': '邀请好友', - 'app.setting.pagestyle': '整体风格设置', - 'app.setting.pagestyle.light': '亮色菜单风格', - 'app.setting.pagestyle.dark': '暗色菜单风格', - 'app.setting.pagestyle.realdark': '暗黑模式', - 'app.setting.themecolor': '主题色', - 'app.setting.navigationmode': '导航模式', - 'app.setting.sidemenu.nav': '侧边栏导航', - 'app.setting.topmenu.nav': '顶部栏导航', - 'app.setting.content-width': '内容区域宽度', - 'app.setting.content-width.tooltip': '该设定仅 [顶部栏导航] 时有效', - 'app.setting.content-width.fixed': '固定', - 'app.setting.content-width.fluid': '流式', - 'app.setting.fixedheader': '固定 Header', - 'app.setting.fixedheader.tooltip': '固定 Header 时可配置', - 'app.setting.autoHideHeader': '下滑时隐藏 Header', - 'app.setting.fixedsidebar': '固定侧边菜单', - 'app.setting.sidemenu': '侧边菜单布局', - 'app.setting.topmenu': '顶部菜单布局', - 'app.setting.othersettings': '其他设置', - 'app.setting.weakmode': '色弱模式', - 'app.setting.multitab': '多页签模式', - 'app.setting.copy': '拷贝设置', - 'app.setting.loading': '加载主题中', - 'app.setting.copyinfo': '拷贝设置成功 src/config/defaultSettings.js', - 'app.setting.copy.success': '复制完毕', - 'app.setting.copy.fail': '复制失败', - 'app.setting.theme.switching': '正在切换主题!', - 'app.setting.production.hint': '配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件', - 'app.setting.themecolor.daybreak': '拂晓蓝(默认)', - 'app.setting.themecolor.dust': '薄暮', - 'app.setting.themecolor.volcano': '火山', - 'app.setting.themecolor.sunset': '日暮', - 'app.setting.themecolor.cyan': '明青', - 'app.setting.themecolor.green': '极光绿', - 'app.setting.themecolor.geekblue': '极客蓝', - 'app.setting.themecolor.purple': '酱紫', - 'app.setting.tooltip': '页面设置', - 'user.login.userName': '用户名', - 'user.login.password': '密码', - 'user.login.username.placeholder': '账户: admin', - 'user.login.password.placeholder': '密码: admin or ant.design', - 'user.login.message-invalid-credentials': '登录失败,请检查邮箱和验证码', - 'user.login.message-invalid-verification-code': '验证码错误', - 'user.login.tab-login-credentials': '账户密码登录', - 'user.login.tab-login-email': '邮箱登录', - 'user.login.tab-login-mobile': '手机号登录', - 'user.login.captcha.placeholder': '请输入图形验证码', - 'user.login.mobile.placeholder': '手机号', - 'user.login.mobile.verification-code.placeholder': '验证码', - 'user.login.email.placeholder': '请输入邮箱地址', - 'user.login.email.verification-code.placeholder': '请输入验证码', - 'user.login.email.sending': '验证码发送中...', - 'user.login.email.send-success-title': '提示', - 'user.login.email.send-success': '验证码发送成功,请查收邮件', - 'user.login.sms.send-success': '验证码发送成功,请查收短信', - 'user.login.remember-me': '自动登录', - 'user.login.forgot-password': '忘记密码', - 'user.login.sign-in-with': '其他登录方式', - 'user.login.signup': '注册账户', - 'user.login.login': '登录', - 'user.register.register': '注册', - 'user.register.email.placeholder': '邮箱', - 'user.register.password.placeholder': '请至少输入 6 个字符。请不要使用容易被猜到的密码。', - 'user.register.password.popover-message': '请至少输入 6 个字符。请不要使用容易被猜到的密码。', - 'user.register.confirm-password.placeholder': '确认密码', - 'user.register.get-verification-code': '获取验证码', - 'user.register.sign-in': '使用已有账户登录', - 'user.register-result.msg': '你的账户:{email} 注册成功', - 'user.register-result.activation-email': '激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。', - 'user.register-result.back-home': '返回首页', - 'user.register-result.view-mailbox': '查看邮箱', - 'user.email.required': '请输入邮箱地址!', - 'user.email.wrong-format': '邮箱地址格式错误!', - 'user.userName.required': '请输入帐户名或邮箱地址', - 'user.password.required': '请输入密码!', - 'user.password.twice.msg': '两次输入的密码不匹配!', - 'user.password.strength.msg': '密码强度不够 ', - 'user.password.strength.strong': '强度:强', - 'user.password.strength.medium': '强度:中', - 'user.password.strength.low': '强度:低', - 'user.password.strength.short': '强度:太短', - 'user.confirm-password.required': '请确认密码!', - 'user.phone-number.required': '请输入正确的手机号', - 'user.phone-number.wrong-format': '手机号格式错误!', - 'user.verification-code.required': '请输入验证码!', - 'user.captcha.required': '请输入图形验证码!', - 'user.login.infos': 'QuantDinger 是一个 AI 多智能体股票分析辅助工具,不具备证券投资咨询资质。平台中的所有分析结果、评分、参考意见均由 AI 基于历史数据自动生成,仅供学习、研究与技术交流使用,不构成任何投资建议或决策依据。股票投资存在市场风险、流动性风险、政策风险等多种风险,可能导致本金损失。用户应基于自身风险承受能力独立决策,使用本工具产生的任何投资行为及其后果由用户自行承担。市场有风险,投资需谨慎。', - 'user.login.tab-login-web3': 'Web3 登录', - 'user.login.web3.tip': '使用钱包进行签名登录', - 'user.login.web3.connect': '连接钱包并登录', - 'user.login.web3.no-wallet': '未检测到钱包', - 'user.login.web3.no-address': '未获取到钱包地址', - 'user.login.web3.nonce-failed': '获取随机数失败', - 'user.login.web3.verify-failed': '签名验证失败', - 'user.login.web3.success': '登录成功', - 'user.login.web3.failed': '钱包登录失败', - 'nav.no_wallet': '未检测到钱包', - 'nav.copy': '复制', - 'nav.copy_success': '复制成功', - 'user.login.oauth.google': '使用 Google 登录', - 'user.login.oauth.github': '使用 GitHub 登录', - 'user.login.oauth.loading': '正在跳转到授权页面...', - 'user.login.oauth.failed': 'OAuth 登录失败', - 'user.login.oauth.get-url-failed': '获取授权链接失败', - 'user.login.subtitle': '驱动的全球市场量化洞察', - 'user.login.legal.title': '法律免责声明', - 'user.login.legal.view': '查看法律免责声明', - 'user.login.legal.collapse': '收起法律免责声明', - 'user.login.legal.agree': '我已阅读并同意法律免责声明', - 'user.login.legal.required': '请先阅读并勾选法律免责声明', - 'user.login.legal.content': 'QuantDinger 是一个 AI 多智能体股票分析辅助工具,不具备证券投资咨询资质。平台中的所有分析结果、评分、参考意见均由 AI 基于历史数据自动生成,仅供学习、研究与技术交流使用,不构成任何投资建议或决策依据。股票投资存在市场风险、流动性风险、政策风险等多种风险,可能导致本金损失。用户应基于自身风险承受能力独立决策,使用本工具产生的任何投资行为及其后果由用户自行承担。市场有风险,投资需谨慎。', - 'user.login.privacy.title': '用户隐私条款', - 'user.login.privacy.view': '查看用户隐私条款', - 'user.login.privacy.collapse': '收起用户隐私条款', - 'user.login.privacy.content': '我们重视您的隐私与数据保护。1) 收集范围:仅收集实现功能所需的信息(如邮箱、手机号、区号、Web3 钱包地址)以及必要的日志与设备信息。2) 使用目的:用于账户登录与安全校验、服务功能提供、问题排查与合规要求。3) 存储与安全:数据加密存储,并采取必要的权限与访问控制措施,尽力防止未经授权的访问、披露或丢失。4) 共享与第三方:除法律法规要求或履行服务所必需外,不会与第三方共享您的个人信息;若涉及第三方服务(如钱包、短信服务商),仅在实现功能所需的最小范围内处理。5) Cookies/本地存储:用于登录态与必要的会话维持(如令牌、PHPSESSID),您可在浏览器中进行清理或限制。6) 个人权利:您可根据法律法规行使查询、更正、删除、撤回同意等权利。7) 变更与通知:本条款更新后将在页面显著位置提示。继续使用本服务即表示您已阅读并同意更新内容。若您不同意本条款或其中任何更新,请停止使用本服务并联系我们。', - 'account.basicInfo': '基础信息', - 'account.id': '用户ID', - 'account.username': '用户名', - 'account.nickname': '昵称', - 'account.email': '邮箱', - 'account.mobile': '手机号', - 'account.web3address': '钱包地址', - 'account.pid': '推荐人ID', - 'account.level': '用户等级', - 'account.money': '余额', - 'account.qdtBalance': 'QDT 余额', - 'account.score': '积分', - 'account.createtime': '注册时间', - 'account.inviteLink': '邀请链接', - 'account.recharge': '充值', - 'account.rechargeTip': '请使用微信或支付宝扫描下方二维码进行充值', - 'account.qrCodePlaceholder': '二维码占位符(模拟)', - 'account.rechargeAmount': '充值金额', - 'account.enterAmount': '请输入充值金额', - 'account.rechargeHint': '最小充值金额:1 QDT', - 'account.confirmRecharge': '确认充值', - 'account.enterValidAmount': '请输入有效的充值金额', - 'account.rechargeSuccess': '充值成功!已充值 {amount} QDT', - 'account.settings.menuMap.basic': '基本设置', - 'account.settings.menuMap.security': '安全设置', - 'account.settings.menuMap.notification': '新消息通知', - 'account.settings.menuMap.moneyLog': '资金明细', - 'account.moneyLog.empty': '暂无资金明细', - 'account.moneyLog.total': '共 {total} 条记录', - 'account.moneyLog.type.purchase': '购买指标', - 'account.moneyLog.type.recharge': '充值', - 'account.moneyLog.type.refund': '退款', - 'account.moneyLog.type.reward': '奖励', - 'account.moneyLog.type.income': '指标收入', - 'account.moneyLog.type.commission': '平台手续费', - 'wallet.balance': 'QDT 余额', - 'wallet.recharge': '充值', - 'wallet.withdraw': '提现', - 'wallet.filter': '筛选', - 'wallet.reset': '重置', - 'wallet.totalRecharge': '累计充值', - 'wallet.totalWithdraw': '累计提现', - 'wallet.totalIncome': '累计收入', - 'wallet.records': '交易记录与资金明细', - 'wallet.tradingRecords': '交易记录', - 'wallet.moneyLog': '资金明细', - 'wallet.rechargeTip': '请使用微信或支付宝扫描下方二维码进行充值', - 'wallet.qrCodePlaceholder': '二维码占位符(模拟)', - 'wallet.rechargeAmount': '充值金额', - 'wallet.enterAmount': '请输入充值金额', - 'wallet.rechargeHint': '最小充值金额:1 QDT', - 'wallet.confirmRecharge': '确认充值', - 'wallet.enterValidAmount': '请输入有效的充值金额', - 'wallet.rechargeSuccess': '充值成功!已充值 {amount} QDT', - 'wallet.rechargeFailed': '充值失败', - 'wallet.withdrawTip': '请输入提现金额和提现地址', - 'wallet.withdrawAmount': '提现金额', - 'wallet.enterWithdrawAmount': '请输入提现金额', - 'wallet.withdrawHint': '最小提现金额:1 QDT', - 'wallet.withdrawAddress': '提现地址(可选)', - 'wallet.enterWithdrawAddress': '请输入提现地址', - 'wallet.confirmWithdraw': '确认提现', - 'wallet.enterValidWithdrawAmount': '请输入有效的提现金额', - 'wallet.insufficientBalance': '余额不足', - 'wallet.withdrawSuccess': '提现成功!已提现 {amount} QDT', - 'wallet.withdrawFailed': '提现失败', - 'wallet.noTradingRecords': '暂无交易记录', - 'wallet.noMoneyLog': '暂无资金明细', - 'wallet.loadTradingRecordsFailed': '加载交易记录失败', - 'wallet.loadMoneyLogFailed': '加载资金明细失败', - 'wallet.moneyLogTotal': '共 {total} 条记录', - 'wallet.moneyLogTypeTitle': '类型', - 'wallet.moneyLogType.all': '全部类型', - 'wallet.table.time': '时间', - 'wallet.table.type': '类型', - 'wallet.table.price': '价格', - 'wallet.table.amount': '数量', - 'wallet.table.money': '金额', - 'wallet.table.balance': '余额', - 'wallet.table.memo': '备注', - 'wallet.table.value': '价值', - 'wallet.table.profit': '盈亏', - 'wallet.table.commission': '手续费', - 'wallet.table.total': '共 {total} 条记录', - 'wallet.tradeType.buy': '买入', - 'wallet.tradeType.sell': '卖出', - 'wallet.tradeType.liquidation': '强平', - 'wallet.tradeType.openLong': '开多', - 'wallet.tradeType.addLong': '加多', - 'wallet.tradeType.closeLong': '平多', - 'wallet.tradeType.closeLongStop': '止损平多', - 'wallet.tradeType.closeLongProfit': '止盈平多', - 'wallet.tradeType.openShort': '开空', - 'wallet.tradeType.addShort': '加空', - 'wallet.tradeType.closeShort': '平空', - 'wallet.tradeType.closeShortStop': '止损平空', - 'wallet.tradeType.closeShortProfit': '止盈平空', - 'wallet.moneyLogType.purchase': '购买指标', - 'wallet.moneyLogType.recharge': '充值', - 'wallet.moneyLogType.withdraw': '提现', - 'wallet.moneyLogType.refund': '退款', - 'wallet.moneyLogType.reward': '奖励', - 'wallet.moneyLogType.income': '收入', - 'wallet.moneyLogType.commission': '手续费', - 'wallet.web3Address.required': '请先填写Web3钱包地址', - 'wallet.web3Address.requiredDescription': '充值前需要先绑定您的Web3钱包地址,用于接收充值', - 'wallet.web3Address.placeholder': '请输入您的Web3钱包地址(0x开头)', - 'wallet.web3Address.save': '保存钱包地址', - 'wallet.web3Address.saveSuccess': '钱包地址保存成功', - 'wallet.web3Address.saveFailed': '保存钱包地址失败', - 'wallet.web3Address.invalidFormat': '请输入有效的Web3钱包地址(以太坊格式:0x开头42位字符,或TRC20格式:T开头34位字符)', - 'wallet.selectCoin': '选择币种', - 'wallet.selectChain': '选择链', - 'wallet.chain.eth': 'ETH(ERC20)', - 'wallet.chain.trc20': 'TRC20', - 'wallet.chain.bsc': 'BSC', - 'wallet.targetQdtAmount': '想要兑换的QDT数量(可选)', - 'wallet.targetQdtAmount.placeholder': '请输入想要兑换的QDT数量,当前QDT价格:{price} USDT', - 'wallet.targetQdtAmount.requiredUsdt': '需要充值:{amount} USDT', - 'wallet.rechargeAddress': '充值地址', - 'wallet.copyAddress': '复制', - 'wallet.copySuccess': '复制成功!', - 'wallet.copyFailed': '复制失败,请手动复制', - 'wallet.rechargeAddressHint': '请确保使用{chain}链向此地址充值{coin}', - 'wallet.qdtPrice.loading': '加载中...', - 'wallet.rechargeTip.new': '请选择币种和链,然后扫描下方二维码进行充值', - 'wallet.exchangeRate': '1 QDT ≈ {rate} USDT', - 'wallet.withdrawableAmount': '可提现金额', - 'wallet.totalBalance': '总余额', - 'wallet.insufficientWithdrawable': '可提现金额不足,当前可提现:{amount} QDT', - 'wallet.withdrawAddressRequired': '请输入提现地址', - 'wallet.withdrawAddressHint': '请确保地址正确,提现后无法撤销', - 'wallet.withdrawSubmitSuccess': '提现申请提交成功,请等待审核', - 'menu.footer.contactUs': '联系我们', - 'menu.footer.getSupport': '获取支持', - 'menu.footer.socialAccounts': '社交账户', - 'menu.footer.userAgreement': '用户协议', - 'menu.footer.privacyPolicy': '隐私条例', - 'menu.footer.support': 'Support', - 'menu.footer.featureRequest': 'Feature request', - 'menu.footer.email': 'Email', - 'menu.footer.liveChat': '24/7 live chat', - 'dashboard.analysis.title': 'Multi-Dimensional Analysis', - 'dashboard.analysis.subtitle': 'AI驱动的综合金融分析平台', - 'dashboard.analysis.selectSymbol': '选择或输入标的代码', - 'dashboard.analysis.selectModel': '选择模型', - 'dashboard.analysis.startAnalysis': '开始分析', - 'dashboard.analysis.history': '历史记录', - 'dashboard.analysis.tab.overview': '综合分析', - 'dashboard.analysis.tab.fundamental': '基本面', - 'dashboard.analysis.tab.technical': '技术', - 'dashboard.analysis.tab.news': '新闻', - 'dashboard.analysis.tab.sentiment': '情绪', - 'dashboard.analysis.tab.risk': '风险', - 'dashboard.analysis.tab.debate': '多空辩论', - 'dashboard.analysis.tab.decision': '最终决策', - 'dashboard.analysis.empty.selectSymbol': '选择标的开始分析', - 'dashboard.analysis.empty.selectSymbolDesc': '从自选股列表中选择或输入标的代码,获取多维度AI分析报告', - 'dashboard.analysis.empty.startAnalysis': '点击"开始分析"按钮进行多维度分析', - 'dashboard.analysis.empty.startAnalysisDesc': '我们将从基本面、技术、新闻、情绪和风险等多个维度为您提供全面的分析', - 'dashboard.analysis.empty.noData': '暂无{type}分析数据,请先进行综合分析', - 'dashboard.analysis.empty.noWatchlist': '暂无自选股', - 'dashboard.analysis.empty.noHistory': '暂无历史记录', - 'dashboard.analysis.empty.watchlistHint': '暂无自选股,请先', - 'dashboard.analysis.empty.noDebateData': '暂无辩论数据', - 'dashboard.analysis.empty.noDecisionData': '暂无决策数据', - 'dashboard.analysis.empty.selectAgent': '请选择一个 Agent 查看分析结果', - 'dashboard.analysis.loading.analyzing': '正在分析中,请稍候...', - 'dashboard.analysis.loading.fundamental': '正在分析基本面数据...', - 'dashboard.analysis.loading.technical': '正在分析技术指标...', - 'dashboard.analysis.loading.news': '正在分析新闻数据...', - 'dashboard.analysis.loading.sentiment': '正在分析市场情绪...', - 'dashboard.analysis.loading.risk': '正在评估风险...', - 'dashboard.analysis.loading.debate': '正在进行多空辩论...', - 'dashboard.analysis.loading.decision': '正在生成最终决策...', - 'dashboard.analysis.score.overall': '综合评分', - 'dashboard.analysis.score.recommendation': '投资建议', - 'dashboard.analysis.score.confidence': '置信度', - 'dashboard.analysis.dimension.fundamental': '基本面', - 'dashboard.analysis.dimension.technical': '技术', - 'dashboard.analysis.dimension.news': '新闻', - 'dashboard.analysis.dimension.sentiment': '情绪', - 'dashboard.analysis.dimension.risk': '风险', - 'dashboard.analysis.card.dimensionScores': '各维度评分', - 'dashboard.analysis.card.overviewReport': '综合分析报告', - 'dashboard.analysis.card.financialMetrics': '财务指标', - 'dashboard.analysis.card.fundamentalReport': '基本面分析报告', - 'dashboard.analysis.card.technicalIndicators': '技术指标', - 'dashboard.analysis.card.technicalReport': '技术分析报告', - 'dashboard.analysis.card.newsList': '相关新闻', - 'dashboard.analysis.card.newsReport': '新闻分析报告', - 'dashboard.analysis.card.sentimentIndicators': '情绪指标', - 'dashboard.analysis.card.sentimentReport': '情绪分析报告', - 'dashboard.analysis.card.riskMetrics': '风险指标', - 'dashboard.analysis.card.riskReport': '风险评估报告', - 'dashboard.analysis.card.bullView': '看涨观点 (Bull)', - 'dashboard.analysis.card.bearView': '看跌观点 (Bear)', - 'dashboard.analysis.card.researchConclusion': '研究员结论', - 'dashboard.analysis.card.traderPlan': '交易员计划', - 'dashboard.analysis.card.riskDebate': '风险委员会辩论', - 'dashboard.analysis.card.finalDecision': '最终决策 (Final Decision)', - 'dashboard.analysis.card.tradePlanDetail': '交易计划详情', - 'dashboard.analysis.tradingPlan.entry_price': '入场价格', - 'dashboard.analysis.tradingPlan.position_size': '仓位大小', - 'dashboard.analysis.tradingPlan.stop_loss': '止损', - 'dashboard.analysis.tradingPlan.take_profit': '止盈', - 'dashboard.analysis.label.confidence': '置信度', - 'dashboard.analysis.label.keyPoints': '核心要点', - 'dashboard.analysis.label.riskWarning': '风险提示', - 'dashboard.analysis.risk.risky': '激进派观点 (Risky)', - 'dashboard.analysis.risk.neutral': '中立派观点 (Neutral)', - 'dashboard.analysis.risk.safe': '保守派观点 (Safe)', - 'dashboard.analysis.risk.conclusion': '结论', - 'dashboard.analysis.feature.fundamental': '基本面分析', - 'dashboard.analysis.feature.technical': '技术分析', - 'dashboard.analysis.feature.news': '新闻分析', - 'dashboard.analysis.feature.sentiment': '情绪分析', - 'dashboard.analysis.feature.risk': '风险评估', - 'dashboard.analysis.watchlist.title': '我的自选股', - 'dashboard.analysis.watchlist.add': '添加', - 'dashboard.analysis.watchlist.addStock': '添加股票', - 'dashboard.analysis.modal.addStock.title': '添加自选股', - 'dashboard.analysis.modal.addStock.confirm': '确定', - 'dashboard.analysis.modal.addStock.cancel': '取消', - 'dashboard.analysis.modal.addStock.market': '市场类型', - 'dashboard.analysis.modal.addStock.marketPlaceholder': '请选择市场', - 'dashboard.analysis.modal.addStock.marketRequired': '请选择市场类型', - 'dashboard.analysis.modal.addStock.symbol': '股票代码', - 'dashboard.analysis.modal.addStock.symbolPlaceholder': '例如: AAPL, TSLA, GOOGL, 000001, BTC', - 'dashboard.analysis.modal.addStock.symbolRequired': '请输入股票代码', - 'dashboard.analysis.modal.addStock.searchPlaceholder': '搜索标的代码或名称', - 'dashboard.analysis.modal.addStock.searchOrInputPlaceholder': '搜索或输入标的代码(如:AAPL、BTC/USDT、EUR/USD)', - 'dashboard.analysis.modal.addStock.searchOrInputHint': '支持搜索数据库中的标的,或直接输入代码(系统将自动获取名称)', - 'dashboard.analysis.modal.addStock.search': '搜索', - 'dashboard.analysis.modal.addStock.searchResults': '搜索结果', - 'dashboard.analysis.modal.addStock.hotSymbols': '热门标的', - 'dashboard.analysis.modal.addStock.noHotSymbols': '暂无热门标的', - 'dashboard.analysis.modal.addStock.selectedSymbol': '已选标的', - 'dashboard.analysis.modal.addStock.pleaseSelectSymbol': '请先选择一个标的', - 'dashboard.analysis.modal.addStock.pleaseSelectOrEnterSymbol': '请先选择一个标的或输入标的代码', - 'dashboard.analysis.modal.addStock.pleaseEnterSymbol': '请输入标的代码', - 'dashboard.analysis.modal.addStock.pleaseSelectMarket': '请先选择市场类型', - 'dashboard.analysis.modal.addStock.searchFailed': '搜索失败,请稍后重试', - 'dashboard.analysis.modal.addStock.noSearchResults': '未找到匹配的标的', - 'dashboard.analysis.modal.addStock.willAutoFetchName': '系统将自动获取名称', - 'dashboard.analysis.modal.addStock.addDirectly': '直接添加', - 'dashboard.analysis.modal.addStock.nameWillBeFetched': '名称将在添加时自动获取', - 'dashboard.analysis.market.AShare': 'A股', - 'dashboard.analysis.market.USStock': '美股', - 'dashboard.analysis.market.HShare': '港股', - 'dashboard.analysis.market.Crypto': '加密货币', - 'dashboard.analysis.market.Forex': '外汇', - 'dashboard.analysis.market.Futures': '期货', - 'dashboard.analysis.modal.history.title': '历史分析记录', - 'dashboard.analysis.modal.history.viewResult': '查看结果', - 'dashboard.analysis.modal.history.completeTime': '完成时间', - 'dashboard.analysis.modal.history.error': '错误', - 'dashboard.analysis.status.pending': '待处理', - 'dashboard.analysis.status.processing': '处理中', - 'dashboard.analysis.status.completed': '已完成', - 'dashboard.analysis.status.failed': '失败', - 'dashboard.analysis.message.selectSymbol': '请先选择标的', - 'dashboard.analysis.message.taskCreated': '分析任务已创建,正在后台执行...', - 'dashboard.analysis.message.analysisComplete': '分析完成', - 'dashboard.analysis.message.analysisCompleteCache': '分析完成(使用缓存数据)', - 'dashboard.analysis.message.analysisFailed': '分析失败,请稍后重试', - 'dashboard.analysis.message.addStockSuccess': '添加成功', - 'dashboard.analysis.message.addStockFailed': '添加失败', - 'dashboard.analysis.message.removeStockSuccess': '移除成功', - 'dashboard.analysis.message.removeStockFailed': '移除失败', - 'dashboard.analysis.test': '工专路 {no} 号店', - 'dashboard.analysis.introduce': '指标说明', - 'dashboard.analysis.total-sales': '总销售额', - 'dashboard.analysis.day-sales': '日均销售额¥', - 'dashboard.analysis.visits': '访问量', - 'dashboard.analysis.visits-trend': '访问量趋势', - 'dashboard.analysis.visits-ranking': '门店访问量排名', - 'dashboard.analysis.day-visits': '日访问量', - 'dashboard.analysis.week': '周同比', - 'dashboard.analysis.day': '日同比', - 'dashboard.analysis.payments': '支付笔数', - 'dashboard.analysis.conversion-rate': '转化率', - 'dashboard.analysis.operational-effect': '运营活动效果', - 'dashboard.analysis.sales-trend': '销售趋势', - 'dashboard.analysis.sales-ranking': '门店销售额排名', - 'dashboard.analysis.all-year': '全年', - 'dashboard.analysis.all-month': '本月', - 'dashboard.analysis.all-week': '本周', - 'dashboard.analysis.all-day': '今日', - 'dashboard.analysis.search-users': '搜索用户数', - 'dashboard.analysis.per-capita-search': '人均搜索次数', - 'dashboard.analysis.online-top-search': '线上热门搜索', - 'dashboard.analysis.the-proportion-of-sales': '销售额类别占比', - 'dashboard.analysis.dropdown-option-one': '操作一', - 'dashboard.analysis.dropdown-option-two': '操作二', - 'dashboard.analysis.channel.all': '全部渠道', - 'dashboard.analysis.channel.online': '线上', - 'dashboard.analysis.channel.stores': '门店', - 'dashboard.analysis.sales': '销售额', - 'dashboard.analysis.traffic': '客流量', - 'dashboard.analysis.table.rank': '排名', - 'dashboard.analysis.table.search-keyword': '搜索关键词', - 'dashboard.analysis.table.users': '用户数', - 'dashboard.analysis.table.weekly-range': '周涨幅', - 'dashboard.indicator.selectSymbol': '选择或输入标的代码', - 'dashboard.indicator.emptyWatchlistHint': '暂无自选股,请先添加', - 'dashboard.indicator.hint.selectSymbol': '请选择标的开始分析', - 'dashboard.indicator.hint.selectSymbolDesc': '在上方搜索框中选择或输入股票代码,查看K线图表和技术指标', - 'dashboard.indicator.retry': '重试', - 'dashboard.indicator.panel.title': '技术指标', - 'dashboard.indicator.panel.realtimeOn': '关闭实时更新', - 'dashboard.indicator.panel.realtimeOff': '开启实时更新', - 'dashboard.indicator.panel.themeLight': '切换到深色主题', - 'dashboard.indicator.panel.themeDark': '切换到浅色主题', - 'dashboard.indicator.section.enabled': '已启用', - 'dashboard.indicator.section.added': '我添加的指标', - 'dashboard.indicator.section.custom': '自己创建的指标', - 'dashboard.indicator.section.bought': '我选购的指标', - 'dashboard.indicator.section.myCreated': '我创建的指标', - 'dashboard.indicator.empty': '暂无指标,请先添加或创建指标', - 'dashboard.indicator.buy': '购买指标', - 'dashboard.indicator.action.start': '启动', - 'dashboard.indicator.action.stop': '关闭', - 'dashboard.indicator.action.edit': '编辑', - 'dashboard.indicator.action.delete': '删除', - 'dashboard.indicator.action.backtest': '回测', - 'dashboard.indicator.status.normal': '正常', - 'dashboard.indicator.status.normalPermanent': '正常(永久有效)', - 'dashboard.indicator.status.expired': '已过期', - 'dashboard.indicator.expiry.permanent': '永久有效', - 'dashboard.indicator.expiry.noExpiry': '无到期时间', - 'dashboard.indicator.expiry.expired': '已过期:{date}', - 'dashboard.indicator.expiry.expiresOn': '到期时间:{date}', - 'dashboard.indicator.delete.confirmTitle': '确认删除', - 'dashboard.indicator.delete.confirmContent': '确定要删除指标"{name}"吗?此操作不可恢复。', - 'dashboard.indicator.delete.confirmOk': '删除', - 'dashboard.indicator.delete.confirmCancel': '取消', - 'dashboard.indicator.delete.success': '删除成功', - 'dashboard.indicator.delete.failed': '删除失败', - 'dashboard.indicator.save.success': '保存成功', - 'dashboard.indicator.save.failed': '保存失败', - 'dashboard.indicator.error.loadWatchlistFailed': '加载自选股失败', - 'dashboard.indicator.error.chartNotReady': '图表组件未初始化,请先选择标的并等待图表加载完成', - 'dashboard.indicator.error.chartMethodNotReady': '图表组件方法未就绪,请稍后重试', - 'dashboard.indicator.error.chartExecuteNotReady': '图表组件执行方法未就绪,请稍后重试', - 'dashboard.indicator.error.parseFailed': '无法解析Python代码', - 'dashboard.indicator.error.parseFailedCheck': '无法解析Python代码,请检查代码格式', - 'dashboard.indicator.error.addIndicatorFailed': '添加指标失败', - 'dashboard.indicator.error.runIndicatorFailed': '运行指标失败', - 'dashboard.indicator.error.pleaseLogin': '请先登录', - 'dashboard.indicator.error.loadDataFailed': '数据加载失败', - 'dashboard.indicator.error.loadDataFailedDesc': '请检查网络连接', - 'dashboard.indicator.error.pythonEngineFailed': 'Python 引擎加载失败,指标功能可能无法使用', - 'dashboard.indicator.error.chartInitFailed': '图表初始化失败', - 'dashboard.indicator.warning.enterCode': '请先输入指标代码', - 'dashboard.indicator.warning.pyodideLoadFailed': 'Python 引擎加载失败', - 'dashboard.indicator.warning.pyodideLoadFailedDesc': '您当前所在区域或网络环境无法使用该功能', - 'dashboard.indicator.warning.chartNotInitialized': '图表未初始化,无法使用画线工具', - 'dashboard.indicator.success.runIndicator': '指标运行成功', - 'dashboard.indicator.success.clearDrawings': '已清除所有画线', - 'dashboard.indicator.sma': 'SMA (均线组合)', - 'dashboard.indicator.ema': 'EMA (指数均线组合)', - 'dashboard.indicator.rsi': 'RSI (相对强弱)', - 'dashboard.indicator.macd': 'MACD', - 'dashboard.indicator.bb': '布林带 (Bollinger Bands)', - 'dashboard.indicator.atr': 'ATR (平均真实波幅)', - 'dashboard.indicator.cci': 'CCI (商品通道指数)', - 'dashboard.indicator.williams': 'Williams %R (威廉指标)', - 'dashboard.indicator.mfi': 'MFI (资金流量指标)', - 'dashboard.indicator.adx': 'ADX (平均趋向指数)', - 'dashboard.indicator.obv': 'OBV (能量潮)', - 'dashboard.indicator.adosc': 'ADOSC (积累/派发振荡器)', - 'dashboard.indicator.ad': 'AD (积累/派发线)', - 'dashboard.indicator.kdj': 'KDJ (随机指标)', - 'dashboard.indicator.signal.buy': 'BUY', - 'dashboard.indicator.signal.sell': 'SELL', - 'dashboard.indicator.signal.supertrendBuy': 'SuperTrend Buy', - 'dashboard.indicator.signal.supertrendSell': 'SuperTrend Sell', - 'dashboard.indicator.chart.kline': 'K线', - 'dashboard.indicator.chart.volume': '成交量', - 'dashboard.indicator.chart.uptrend': 'Up Trend', - 'dashboard.indicator.chart.downtrend': 'Down Trend', - 'dashboard.indicator.tooltip.time': '时间', - 'dashboard.indicator.tooltip.open': '开', - 'dashboard.indicator.tooltip.close': '收', - 'dashboard.indicator.tooltip.high': '高', - 'dashboard.indicator.tooltip.low': '低', - 'dashboard.indicator.tooltip.volume': '成交量', - 'dashboard.indicator.drawing.line': '线段', - 'dashboard.indicator.drawing.horizontalLine': '水平线', - 'dashboard.indicator.drawing.verticalLine': '垂直线', - 'dashboard.indicator.drawing.ray': '射线', - 'dashboard.indicator.drawing.straightLine': '直线', - 'dashboard.indicator.drawing.parallelLine': '平行线', - 'dashboard.indicator.drawing.priceLine': '价格线', - 'dashboard.indicator.drawing.priceChannel': '价格通道', - 'dashboard.indicator.drawing.fibonacciLine': '斐波那契线', - 'dashboard.indicator.drawing.clearAll': '清除所有画线', - 'dashboard.indicator.market.AShare': 'A股', - 'dashboard.indicator.market.USStock': '美股', - 'dashboard.indicator.market.HShare': '港股', - 'dashboard.indicator.market.Crypto': '加密货币', - 'dashboard.indicator.market.Forex': '外汇', - 'dashboard.indicator.market.Futures': '期货', - 'dashboard.indicator.create': '创建指标', - 'dashboard.indicator.editor.title': '创建/编辑指标', - 'dashboard.indicator.editor.name': '指标名称', - 'dashboard.indicator.editor.nameRequired': '请输入指标名称', - 'dashboard.indicator.editor.namePlaceholder': '请输入指标名称', - 'dashboard.indicator.editor.description': '指标描述', - 'dashboard.indicator.editor.descriptionPlaceholder': '请输入指标描述(可选)', - 'dashboard.indicator.editor.code': '指标脚本(Python)', - 'dashboard.indicator.editor.codeRequired': '请输入指标代码', - 'dashboard.indicator.editor.codePlaceholder': '请输入Python代码', - 'dashboard.indicator.editor.run': '运行', - 'dashboard.indicator.editor.runHint': '点击运行按钮可以在K线图上预览指标效果', - 'dashboard.indicator.editor.guide': '开发指南', - 'dashboard.indicator.editor.guideTitle': 'Python 指标开发指南', - 'dashboard.indicator.editor.save': '保存', - 'dashboard.indicator.editor.cancel': '取消', - 'dashboard.indicator.editor.unnamed': '未命名指标', - 'dashboard.indicator.editor.publishToCommunity': '发布到社区', - 'dashboard.indicator.editor.publishToCommunityHint': '发布后,其他用户可以在社区中查看和使用您的指标', - 'dashboard.indicator.editor.indicatorType': '指标类型', - 'dashboard.indicator.editor.indicatorTypeRequired': '请选择指标类型', - 'dashboard.indicator.editor.indicatorTypePlaceholder': '请选择指标类型', - 'dashboard.indicator.editor.previewImage': '预览图', - 'dashboard.indicator.editor.uploadImage': '上传图片', - 'dashboard.indicator.editor.previewImageHint': '建议尺寸:800x400,大小不超过2MB', - 'dashboard.indicator.editor.pricing': '售价(QDT)', - 'dashboard.indicator.editor.pricingType.free': '免费', - 'dashboard.indicator.editor.pricingType.permanent': '永久', - 'dashboard.indicator.editor.pricingType.monthly': '月付', - 'dashboard.indicator.editor.price': '价格', - 'dashboard.indicator.editor.priceRequired': '请输入价格', - 'dashboard.indicator.editor.pricePlaceholder': '请输入价格', - 'dashboard.indicator.editor.pricingHint': '免费指标虽然价格为0,但平台会根据您的指标使用量和好评率给予奖励(QDT)', - 'dashboard.indicator.editor.aiGenerate': '智能生成', - 'dashboard.indicator.editor.aiPromptPlaceholder': '描述清楚你的信号逻辑(只输出 buy/sell)和绘图(plots)。仓位/风控/加减仓在回测配置里设置。', - 'dashboard.indicator.editor.aiGenerateBtn': 'AI生成代码', - 'dashboard.indicator.editor.aiPromptRequired': '请输入您的想法', - 'dashboard.indicator.editor.aiGenerateSuccess': '代码生成成功', - 'dashboard.indicator.editor.aiGenerateError': '代码生成失败,请稍后重试', - 'dashboard.indicator.boundary.message': '提示:指标脚本只负责“计算 + 绘图 + buy/sell 信号”;仓位、风控、加减仓、手续费/滑点属于策略执行配置。', - 'dashboard.indicator.boundary.indicatorRule': "指标脚本请只输出 buy/sell(并设置 df['buy']/df['sell'])。不要在脚本里写仓位管理、止盈止损、加减仓。", - 'dashboard.indicator.boundary.backtestRule': '规则:同一根K线若出现主信号(buy/sell→开/平仓/反手),本K线将跳过所有加仓与减仓。', - 'dashboard.indicator.backtest.title': '指标回测', - 'dashboard.indicator.backtest.config': '回测参数', - 'dashboard.indicator.backtest.startDate': '开始日期', - 'dashboard.indicator.backtest.endDate': '结束日期', - 'dashboard.indicator.backtest.selectStartDate': '选择开始日期', - 'dashboard.indicator.backtest.selectEndDate': '选择结束日期', - 'dashboard.indicator.backtest.startDateRequired': '请选择开始日期', - 'dashboard.indicator.backtest.endDateRequired': '请选择结束日期', - 'dashboard.indicator.backtest.initialCapital': '初始资金', - 'dashboard.indicator.backtest.initialCapitalRequired': '请输入初始资金', - 'dashboard.indicator.backtest.commission': '手续费率', - 'dashboard.indicator.backtest.leverage': '杠杆倍率', - 'dashboard.indicator.backtest.tradeDirection': '交易方向', - 'dashboard.indicator.backtest.longOnly': '做多', - 'dashboard.indicator.backtest.shortOnly': '做空', - 'dashboard.indicator.backtest.both': '双向', - 'dashboard.indicator.backtest.run': '开始回测', - 'dashboard.indicator.backtest.rerun': '重新回测', - 'dashboard.indicator.backtest.close': '关闭', - 'dashboard.indicator.backtest.running': '正在进行指标回测...', - 'dashboard.indicator.backtest.results': '回测结果', - 'dashboard.indicator.backtest.totalReturn': '总收益率', - 'dashboard.indicator.backtest.annualReturn': '年化收益', - 'dashboard.indicator.backtest.maxDrawdown': '最大回撤', - 'dashboard.indicator.backtest.sharpeRatio': '夏普比率', - 'dashboard.indicator.backtest.winRate': '胜率', - 'dashboard.indicator.backtest.profitFactor': '盈亏比', - 'dashboard.indicator.backtest.totalTrades': '交易次数', - 'dashboard.indicator.totalReturn': '总收益率', - 'dashboard.indicator.annualReturn': '年化收益率', - 'dashboard.indicator.maxDrawdown': '最大回撤', - 'dashboard.indicator.sharpeRatio': '夏普比率', - 'dashboard.indicator.winRate': '胜率', - 'dashboard.indicator.profitFactor': '盈亏比', - 'dashboard.indicator.totalTrades': '总交易次数', - 'dashboard.indicator.backtest.totalCommission': '总手续费', - 'dashboard.indicator.backtest.equityCurve': '收益曲线', - 'dashboard.indicator.backtest.strategy': '指标收益', - 'dashboard.indicator.backtest.benchmark': '基准收益', - 'dashboard.indicator.backtest.tradeHistory': '交易记录', - 'dashboard.indicator.backtest.tradeTime': '时间', - 'dashboard.indicator.backtest.tradeType': '类型', - 'dashboard.indicator.backtest.buy': '买入', - 'dashboard.indicator.backtest.sell': '卖出', - 'dashboard.indicator.backtest.liquidation': '爆仓', - 'dashboard.indicator.backtest.openLong': '开多', - 'dashboard.indicator.backtest.closeLong': '平多', - 'dashboard.indicator.backtest.closeLongStop': '平多(止损)', - 'dashboard.indicator.backtest.closeLongProfit': '平多(止盈)', - 'dashboard.indicator.backtest.addLong': '加多仓', - 'dashboard.indicator.backtest.openShort': '开空', - 'dashboard.indicator.backtest.closeShort': '平空', - 'dashboard.indicator.backtest.closeShortStop': '平空(止损)', - 'dashboard.indicator.backtest.closeShortProfit': '平空(止盈)', - 'dashboard.indicator.backtest.addShort': '加空仓', - 'dashboard.indicator.backtest.closeLongTrailing': '平多(移动止损/止盈)', - 'dashboard.indicator.backtest.reduceLong': '多头减仓', - 'dashboard.indicator.backtest.closeShortTrailing': '平空(移动止损/止盈)', - 'dashboard.indicator.backtest.reduceShort': '空头减仓', - 'dashboard.indicator.backtest.price': '价格', - 'dashboard.indicator.backtest.amount': '数量', - 'dashboard.indicator.backtest.balance': '账户余额', - 'dashboard.indicator.backtest.profit': '盈亏', - 'dashboard.indicator.backtest.success': '回测完成', - 'dashboard.indicator.backtest.failed': '回测失败,请稍后重试', - 'dashboard.indicator.backtest.noIndicatorCode': '该指标没有可回测的代码', - 'dashboard.indicator.backtest.noSymbol': '请先选择交易标的', - 'dashboard.indicator.backtest.dateRangeExceeded': '回测时间范围超出限制:{timeframe}周期最多可回测{maxRange}', - 'dashboard.indicator.backtest.dateRangeExceededDays': '回测时间范围超出限制:{timeframe}周期最多可回测{maxRange}({maxDays}天)', - 'dashboard.indicator.backtest.metaLine': '标的:{symbol} | 市场:{market} | 周期:{timeframe}', - 'dashboard.indicator.backtest.savedRunId': '回测已保存,记录ID:{id}', - 'dashboard.indicator.backtest.historyTitle': '回测记录', - 'dashboard.indicator.backtest.historyRefresh': '刷新', - 'dashboard.indicator.backtest.historyView': '查看', - 'dashboard.indicator.backtest.historyNoData': '暂无回测记录', - 'dashboard.indicator.backtest.historyUseCurrent': '仅当前币种/周期', - 'dashboard.indicator.backtest.historyFilterSymbol': '币种(Symbol)', - 'dashboard.indicator.backtest.historyFilterTimeframe': '周期(Timeframe)', - 'dashboard.indicator.backtest.historyApply': '应用筛选', - 'dashboard.indicator.backtest.historyAIAnalyze': 'AI分析', - 'dashboard.indicator.backtest.historyAIAnalyzeTitle': 'AI 分析建议', - 'dashboard.indicator.backtest.historyNoAIResult': '暂无 AI 分析结果', - 'dashboard.indicator.backtest.historyRunId': '记录ID', - 'dashboard.indicator.backtest.historyCreatedAt': '时间', - 'dashboard.indicator.backtest.historyRange': '区间', - 'dashboard.indicator.backtest.historyStatus': '状态', - 'dashboard.indicator.backtest.historyStatusSuccess': '成功', - 'dashboard.indicator.backtest.historyStatusFailed': '失败', - 'dashboard.indicator.backtest.historyActions': '操作', - 'dashboard.indicator.backtest.prev': '上一步', - 'dashboard.indicator.backtest.next': '下一步', - 'dashboard.indicator.backtest.steps.strategy.title': '执行配置', - 'dashboard.indicator.backtest.steps.strategy.desc': '仓位/风控/加减仓(策略层)', - 'dashboard.indicator.backtest.steps.trading.title': '回测环境', - 'dashboard.indicator.backtest.steps.trading.desc': '时间/资金/费率/杠杆/滑点', - 'dashboard.indicator.backtest.steps.results.title': '结果', - 'dashboard.indicator.backtest.steps.results.desc': '统计与交易明细', - 'dashboard.indicator.backtest.panel.risk': '执行配置:风控(止损/移动止盈)', - 'dashboard.indicator.backtest.panel.scale': '执行配置:加仓(顺势/逆势)', - 'dashboard.indicator.backtest.panel.reduce': '执行配置:减仓(顺势/逆势)', - 'dashboard.indicator.backtest.panel.position': '执行配置:开仓仓位', - 'dashboard.indicator.backtest.field.stopLossPct': '止损(%)', - 'dashboard.indicator.backtest.field.takeProfitPct': '止盈(%)', - 'dashboard.indicator.backtest.field.trailingEnabled': '移动止盈', - 'dashboard.indicator.backtest.field.trailingStopPct': '移动回撤(%)', - 'dashboard.indicator.backtest.field.trailingActivationPct': '移动止盈激活(%)', - 'dashboard.indicator.backtest.field.slippage': '滑点(%)', - 'dashboard.indicator.backtest.field.trendAddEnabled': '顺势加仓', - 'dashboard.indicator.backtest.field.dcaAddEnabled': '逆势加仓', - 'dashboard.indicator.backtest.field.trendAddStepPct': '加仓触发(%)', - 'dashboard.indicator.backtest.field.dcaAddStepPct': '加仓触发(%)', - 'dashboard.indicator.backtest.field.trendAddSizePct': '每次加仓资金占比(%)', - 'dashboard.indicator.backtest.field.dcaAddSizePct': '每次加仓资金占比(%)', - 'dashboard.indicator.backtest.field.trendAddMaxTimes': '最多加仓次数', - 'dashboard.indicator.backtest.field.dcaAddMaxTimes': '最多加仓次数', - 'dashboard.indicator.backtest.field.trendReduceEnabled': '顺势减仓', - 'dashboard.indicator.backtest.field.adverseReduceEnabled': '逆势减仓', - 'dashboard.indicator.backtest.field.trendReduceStepPct': '顺势触发(%)', - 'dashboard.indicator.backtest.field.adverseReduceStepPct': '逆势触发(%)', - 'dashboard.indicator.backtest.field.trendReduceSizePct': '每次减仓比例(%)', - 'dashboard.indicator.backtest.field.adverseReduceSizePct': '每次逆势减仓比例(%)', - 'dashboard.indicator.backtest.field.trendReduceMaxTimes': '最多顺势减仓次数', - 'dashboard.indicator.backtest.field.adverseReduceMaxTimes': '最多逆势减仓次数', - 'dashboard.indicator.backtest.field.entryPct': '开仓资金占比(%)', - 'dashboard.indicator.backtest.field.minOrderPct': '单次最小资金占比(%)(可选)', - 'dashboard.indicator.backtest.hint.entryPctMax': '最大可开仓:{maxPct}%(为后续加仓预留资金)', - 'dashboard.docs.title': '文档中心', - 'dashboard.docs.search.placeholder': '搜索文档...', - 'dashboard.docs.category.all': '全部', - 'dashboard.docs.category.guide': '开发指南', - 'dashboard.docs.category.api': 'API文档', - 'dashboard.docs.category.tutorial': '教程', - 'dashboard.docs.category.faq': '常见问题', - 'dashboard.docs.featured.title': '推荐文档', - 'dashboard.docs.featured.tag': '推荐', - 'dashboard.docs.list.views': '浏览', - 'dashboard.docs.list.author': '作者', - 'dashboard.docs.list.empty': '暂无文档', - 'dashboard.docs.list.backToAll': '返回全部文档', - 'dashboard.docs.list.total': '共 {count} 篇文档', - 'dashboard.docs.detail.back': '返回文档列表', - 'dashboard.docs.detail.updatedAt': '更新于', - 'dashboard.docs.detail.related': '相关文档', - 'dashboard.docs.detail.notFound': '文档不存在', - 'dashboard.docs.detail.error': '文档不存在或已被删除', - 'dashboard.docs.search.result': '搜索结果', - 'dashboard.docs.search.keyword': '关键词', - 'community.filter.indicatorType': '指标类型', - 'community.filter.all': '全部', - 'community.filter.other': '其他选项', - 'community.filter.pricing': '定价类型', - 'community.filter.allPricing': '全部定价', - 'community.filter.sortBy': '排序方式', - 'community.filter.search': '搜索', - 'community.filter.searchPlaceholder': '搜索指标名称', - 'community.indicatorType.trend': '趋势型', - 'community.indicatorType.momentum': '动量型', - 'community.indicatorType.volatility': '波动率', - 'community.indicatorType.volume': '成交量', - 'community.indicatorType.custom': '自定义', - 'community.pricing.free': '免费', - 'community.pricing.paid': '付费', - 'community.sort.downloads': '下载量', - 'community.sort.rating': '评分', - 'community.sort.newest': '最新发布', - 'community.pagination.total': '共 {total} 个指标', - 'community.noDescription': '暂无描述', - 'community.detail.type': '类型', - 'community.detail.pricing': '定价', - 'community.detail.rating': '评分', - 'community.detail.downloads': '下载量', - 'community.detail.author': '作者', - 'community.detail.description': '简介', - 'community.detail.detailContent': '详细说明', - 'community.detail.backtestStats': '回测统计', - 'community.action.purchase': '购买指标', - 'community.action.addToMyIndicators': '添加到我的指标', - 'community.action.favorite': '收藏', - 'community.action.unfavorite': '取消收藏', - 'community.action.buyNow': '立即购买', - 'community.action.renew': '续费', - 'community.purchase.price': '价格', - 'community.purchase.permanent': '永久', - 'community.purchase.monthly': '月', - 'community.purchase.confirmBuy': '确认购买该指标({price} QDT)?', - 'community.purchase.confirmRenew': '确认续费该指标({price} QDT/月)?', - 'community.purchase.confirmFree': '确认添加该免费指标?', - 'community.purchase.confirmTitle': '确认操作', - 'community.purchase.owned': '您已购买该指标(永久有效)', - 'community.purchase.ownIndicator': '这是您发布的指标', - 'community.purchase.expired': '您的订阅已过期', - 'community.purchase.expiresOn': '到期时间:{date}', - 'community.tabs.detail': '指标详情', - 'community.tabs.backtest': '回测数据', - 'community.tabs.ratings': '用户评价', - 'community.rating.myRating': '我的评分', - 'community.rating.stars': '{count} 星', - 'community.rating.commentPlaceholder': '分享您的使用体验...', - 'community.rating.submit': '提交评分', - 'community.rating.modify': '修改', - 'community.rating.saveModify': '保存修改', - 'community.rating.cancel': '取消', - 'community.rating.selectRating': '请选择评分', - 'community.rating.success': '评分成功', - 'community.rating.modifySuccess': '修改评价成功', - 'community.rating.failed': '评分失败', - 'community.rating.noRatings': '暂无评价', - 'community.backtest.note': '以上数据为历史回测结果,仅供参考,不代表未来收益', - 'community.backtest.noData': '暂无回测数据', - 'community.backtest.uploadHint': '作者尚未上传回测数据', - 'community.message.loadFailed': '加载指标列表失败', - 'community.message.purchaseProcessing': '正在处理购买请求...', - 'community.message.downloadSuccess': '指标已添加到我的指标列表', - 'community.message.favoriteSuccess': '收藏成功', - 'community.message.unfavoriteSuccess': '取消收藏成功', - 'community.message.operationSuccess': '操作成功', - 'community.message.operationFailed': '操作失败', - 'dashboard.totalEquity': '总权益', - 'dashboard.totalPnL': '总盈亏', - 'dashboard.aiStrategies': 'AI 策略', - 'dashboard.indicatorStrategies': '指标策略', - 'dashboard.running': '运行中', - 'dashboard.enabled': '已启用', - 'dashboard.pnlHistory': '历史盈亏', - 'dashboard.strategyPerformance': '策略盈亏占比', - 'dashboard.recentTrades': '最近交易', - 'dashboard.currentPositions': '当前持仓', - 'dashboard.table.time': '时间', - 'dashboard.table.strategy': '策略名称', - 'dashboard.table.symbol': '标的', - 'dashboard.table.type': '类型', - 'dashboard.table.side': '方向', - 'dashboard.table.size': '持仓量', - 'dashboard.table.entryPrice': '开仓均价', - 'dashboard.table.price': '价格', - 'dashboard.table.amount': '数量', - 'dashboard.table.profit': '盈亏', - 'dashboard.pendingOrders': '订单执行记录', - 'dashboard.totalOrders': '共 {total} 条', - 'dashboard.viewError': '查看错误', - 'dashboard.filled': '已成交', - 'dashboard.orderTable.time': '创建时间', - 'dashboard.orderTable.strategy': '策略', - 'dashboard.orderTable.symbol': '交易对', - 'dashboard.orderTable.signalType': '信号类型', - 'dashboard.orderTable.amount': '数量', - 'dashboard.orderTable.price': '成交价', - 'dashboard.orderTable.status': '状态', - 'dashboard.orderTable.timeInfo': '时间', - 'dashboard.orderTable.executedAt': '执行时间', - 'dashboard.orderTable.exchange': '交易所', - 'dashboard.orderTable.notify': '通知方式', - 'dashboard.orderTable.actions': '操作', - 'dashboard.orderTable.delete': '删除', - 'dashboard.orderTable.deleteConfirm': '确认删除这条记录?', - 'dashboard.orderTable.deleteSuccess': '删除成功', - 'dashboard.orderTable.deleteFailed': '删除失败', - 'dashboard.signalType.openLong': '开多', - 'dashboard.signalType.openShort': '开空', - 'dashboard.signalType.closeLong': '平多', - 'dashboard.signalType.closeShort': '平空', - 'dashboard.signalType.addLong': '加多', - 'dashboard.signalType.addShort': '加空', - 'dashboard.status.pending': '待处理', - 'dashboard.status.processing': '处理中', - 'dashboard.status.completed': '已完成', - 'dashboard.status.failed': '失败', - 'dashboard.status.cancelled': '已取消', - 'dashboard.table.pnl': '未实现盈亏', - 'form.basic-form.basic.title': '基础表单', - 'form.basic-form.basic.description': '表单页用于向用户收集或验证信息,基础表单常见于数据项较少的表单场景。', - 'form.basic-form.title.label': '标题', - 'form.basic-form.title.placeholder': '给目标起个名字', - 'form.basic-form.title.required': '请输入标题', - 'form.basic-form.date.label': '起止日期', - 'form.basic-form.placeholder.start': '开始日期', - 'form.basic-form.placeholder.end': '结束日期', - 'form.basic-form.date.required': '请选择起止日期', - 'form.basic-form.goal.label': '目标描述', - 'form.basic-form.goal.placeholder': '请输入你的阶段性工作目标', - 'form.basic-form.goal.required': '请输入目标描述', - 'form.basic-form.standard.label': '衡量标准', - 'form.basic-form.standard.placeholder': '请输入衡量标准', - 'form.basic-form.standard.required': '请输入衡量标准', - 'form.basic-form.client.label': '客户', - 'form.basic-form.client.required': '请描述你服务的客户', - 'form.basic-form.label.tooltip': '目标的服务对象', - 'form.basic-form.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号', - 'form.basic-form.invites.label': '邀评人', - 'form.basic-form.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人', - 'form.basic-form.weight.label': '权重', - 'form.basic-form.weight.placeholder': '请输入', - 'form.basic-form.public.label': '目标公开', - 'form.basic-form.label.help': '客户、邀评人默认被分享', - 'form.basic-form.radio.public': '公开', - 'form.basic-form.radio.partially-public': '部分公开', - 'form.basic-form.radio.private': '不公开', - 'form.basic-form.publicUsers.placeholder': '公开给', - 'form.basic-form.option.A': '同事一', - 'form.basic-form.option.B': '同事二', - 'form.basic-form.option.C': '同事三', - 'form.basic-form.email.required': '请输入邮箱地址!', - 'form.basic-form.email.wrong-format': '邮箱地址格式错误!', - 'form.basic-form.userName.required': '请输入用户名!', - 'form.basic-form.password.required': '请输入密码!', - 'form.basic-form.password.twice': '两次输入的密码不匹配!', - 'form.basic-form.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。', - 'form.basic-form.strength.strong': '强度:强', - 'form.basic-form.strength.medium': '强度:中', - 'form.basic-form.strength.short': '强度:太短', - 'form.basic-form.confirm-password.required': '请确认密码!', - 'form.basic-form.phone-number.required': '请输入手机号!', - 'form.basic-form.phone-number.wrong-format': '手机号格式错误!', - 'form.basic-form.verification-code.required': '请输入验证码!', - 'form.basic-form.form.get-captcha': '获取验证码', - 'form.basic-form.captcha.second': '秒', - 'form.basic-form.form.optional': '(选填)', - 'form.basic-form.form.submit': '提交', - 'form.basic-form.form.save': '保存', - 'form.basic-form.email.placeholder': '邮箱', - 'form.basic-form.password.placeholder': '至少6位密码,区分大小写', - 'form.basic-form.confirm-password.placeholder': '确认密码', - 'form.basic-form.phone-number.placeholder': '手机号', - 'form.basic-form.verification-code.placeholder': '验证码', - 'result.success.title': '提交成功', - 'result.success.description': '提交结果页用于反馈一系列操作任务的处理结果, 如果仅是简单操作,使用 Message 全局提示反馈即可。 本文字区域可以展示简单的补充说明,如果有类似展示 “单据”的需求,下面这个灰色区域可以呈现比较复杂的内容。', - 'result.success.operate-title': '项目名称', - 'result.success.operate-id': '项目 ID', - 'result.success.principal': '负责人', - 'result.success.operate-time': '生效时间', - 'result.success.step1-title': '创建项目', - 'result.success.step1-operator': '曲丽丽', - 'result.success.step2-title': '部门初审', - 'result.success.step2-operator': '周毛毛', - 'result.success.step2-extra': '催一下', - 'result.success.step3-title': '财务复核', - 'result.success.step4-title': '完成', - 'result.success.btn-return': '返回列表', - 'result.success.btn-project': '查看项目', - 'result.success.btn-print': '打印', - 'result.fail.error.title': '提交失败', - 'result.fail.error.description': '请核对并修改以下信息后,再重新提交。', - 'result.fail.error.hint-title': '您提交的内容有如下错误:', - 'result.fail.error.hint-text1': '您的账户已被冻结', - 'result.fail.error.hint-btn1': '立即解冻', - 'result.fail.error.hint-text2': '您的账户还不具备申请资格', - 'result.fail.error.hint-btn2': '立即升级', - 'result.fail.error.btn-text': '返回修改', - 'account.settings.menuMap.custom': '个性化', - 'account.settings.menuMap.binding': '账号绑定', - 'account.settings.basic.avatar': '头像', - 'account.settings.basic.change-avatar': '更换头像', - 'account.settings.basic.email': '邮箱', - 'account.settings.basic.email-message': '请输入您的邮箱!', - 'account.settings.basic.nickname': '昵称', - 'account.settings.basic.nickname-message': '请输入您的昵称!', - 'account.settings.basic.profile': '个人简介', - 'account.settings.basic.profile-message': '请输入个人简介!', - 'account.settings.basic.profile-placeholder': '个人简介', - 'account.settings.basic.country': '国家/地区', - 'account.settings.basic.country-message': '请输入您的国家或地区!', - 'account.settings.basic.geographic': '所在省市', - 'account.settings.basic.geographic-message': '请输入您的所在省市!', - 'account.settings.basic.address': '街道地址', - 'account.settings.basic.address-message': '请输入您的街道地址!', - 'account.settings.basic.phone': '联系电话', - 'account.settings.basic.phone-message': '请输入您的联系电话!', - 'account.settings.basic.update': '更新基本信息', - 'account.settings.basic.update.success': '更新基本信息成功', - 'account.settings.security.strong': '强', - 'account.settings.security.medium': '中', - 'account.settings.security.weak': '弱', - 'account.settings.security.password': '账户密码', - 'account.settings.security.password-description': '当前密码强度:', - 'account.settings.security.phone': '密保手机', - 'account.settings.security.phone-description': '已绑定手机:', - 'account.settings.security.question': '密保问题', - 'account.settings.security.question-description': '未设置密保问题,密保问题可有效保护账户安全', - 'account.settings.security.email': '绑定邮箱', - 'account.settings.security.email-description': '已绑定邮箱:', - 'account.settings.security.mfa': 'MFA 设备', - 'account.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认', - 'account.settings.security.modify': '修改', - 'account.settings.security.set': '设置', - 'account.settings.security.bind': '绑定', - 'account.settings.binding.taobao': '绑定淘宝', - 'account.settings.binding.taobao-description': '当前未绑定淘宝账号', - 'account.settings.binding.alipay': '绑定支付宝', - 'account.settings.binding.alipay-description': '当前未绑定支付宝账号', - 'account.settings.binding.dingding': '绑定钉钉', - 'account.settings.binding.dingding-description': '当前未绑定钉钉账号', - 'account.settings.binding.bind': '绑定', - 'account.settings.notification.password': '账户密码', - 'account.settings.notification.password-description': '其他用户的消息将以站内信的形式通知', - 'account.settings.notification.messages': '系统消息', - 'account.settings.notification.messages-description': '系统消息将以站内信的形式通知', - 'account.settings.notification.todo': '待办任务', - 'account.settings.notification.todo-description': '待办任务将以站内信的形式通知', - 'account.settings.settings.open': '开', - 'account.settings.settings.close': '关', - 'trading-assistant.title': '交易助手', - 'trading-assistant.strategyList': '策略列表', - 'trading-assistant.createStrategy': '创建策略', - 'trading-assistant.noStrategy': '暂无策略', - 'trading-assistant.selectStrategy': '请从左侧选择一个策略查看详情', - 'trading-assistant.startStrategy': '启动策略', - 'trading-assistant.stopStrategy': '停止策略', - 'trading-assistant.editStrategy': '编辑策略', - 'trading-assistant.deleteStrategy': '删除策略', - 'trading-assistant.status.running': '运行中', - 'trading-assistant.status.stopped': '已停止', - 'trading-assistant.status.error': '错误', - 'trading-assistant.strategyType.IndicatorStrategy': '技术指标策略', - 'trading-assistant.strategyType.PromptBasedStrategy': '提示词策略', - 'trading-assistant.strategyType.GridStrategy': '网格策略', - 'trading-assistant.tabs.tradingRecords': '交易记录', - 'trading-assistant.tabs.positions': '持仓记录', - 'trading-assistant.tabs.equityCurve': '净值曲线', - 'trading-assistant.form.step1': '选择指标', - 'trading-assistant.form.step2': '交易所配置', - 'trading-assistant.form.step3': '策略参数', - 'trading-assistant.form.step2Params': '参数配置', - 'trading-assistant.form.step3Signal': '信号推送', - 'trading-assistant.form.marketCategory': '交易种类', - 'trading-assistant.form.marketCategoryHint': '先选择交易种类,用于判断是否支持实盘接入(目前仅加密可选实盘)。', - 'trading-assistant.market.AShare': 'A股', - 'trading-assistant.market.USStock': '美股', - 'trading-assistant.market.Crypto': '加密货币', - 'trading-assistant.market.Forex': '外汇', - 'trading-assistant.form.indicator': '选择指标', - 'trading-assistant.form.indicatorHint': '只能选择您已购买或创建的技术指标', - 'trading-assistant.form.qdtCostHints': '使用策略将消耗QDT,请确保账户有足够的QDT余额', - 'trading-assistant.form.indicatorDescription': '指标描述', - 'trading-assistant.form.noDescription': '暂无描述', - 'trading-assistant.form.exchange': '选择交易所', - 'trading-assistant.form.apiKey': 'API Key', - 'trading-assistant.form.secretKey': 'Secret Key', - 'trading-assistant.form.passphrase': 'Passphrase', - 'trading-assistant.form.testConnection': '测试连接', - 'trading-assistant.form.strategyName': '策略名称', - 'trading-assistant.form.symbol': '交易对', - 'trading-assistant.form.symbolHint': '标的格式取决于所选市场', - 'trading-assistant.form.symbolHintCrypto': '加密:例如 BTC/USDT', - 'trading-assistant.form.symbolHintGeneral': '请输入所选市场的标的代码(如 600519、AAPL、EURUSD)。', - 'trading-assistant.form.initialCapital': '投入金额', - 'trading-assistant.form.marketType': '市场类型', - 'trading-assistant.form.marketTypeFutures': '合约', - 'trading-assistant.form.marketTypeSpot': '现货', - 'trading-assistant.form.marketTypeHint': '合约支持双向交易和杠杆,现货仅支持做多且杠杆固定为1倍', - 'trading-assistant.form.leverage': '杠杆倍数', - 'trading-assistant.form.leverageHint': '合约:1-125倍,现货:固定1倍', - 'trading-assistant.form.spotLeverageFixed': '现货交易杠杆固定为1倍', - 'trading-assistant.form.spotOnlyLongHint': '现货交易仅支持做多', - 'trading-assistant.form.tradeDirection': '交易方向', - 'trading-assistant.form.tradeDirectionLong': '仅做多', - 'trading-assistant.form.tradeDirectionShort': '仅做空', - 'trading-assistant.form.tradeDirectionBoth': '双向交易', - 'trading-assistant.form.timeframe': '时间周期', - 'trading-assistant.form.klinePeriod': 'K线周期', - 'trading-assistant.form.timeframe1m': '1分钟', - 'trading-assistant.form.timeframe5m': '5分钟', - 'trading-assistant.form.timeframe15m': '15分钟', - 'trading-assistant.form.timeframe30m': '30分钟', - 'trading-assistant.form.timeframe1H': '1小时', - 'trading-assistant.form.timeframe4H': '4小时', - 'trading-assistant.form.timeframe1D': '1天', - 'trading-assistant.form.selectStrategyType': '选择策略类型', - 'trading-assistant.form.indicatorStrategy': '指标策略', - 'trading-assistant.form.indicatorStrategyDesc': '基于技术指标的自动化交易策略', - 'trading-assistant.form.aiStrategy': 'AI策略', - 'trading-assistant.form.aiStrategyDesc': '基于AI智能决策的自动化交易策略', - 'trading-assistant.form.enableAiFilter': '启用AI智能决策过滤', - 'trading-assistant.form.enableAiFilterHint': '启用后,指标信号将经过AI智能过滤,提高交易质量', - 'trading-assistant.form.aiFilterPrompt': '自定义提示词', - 'trading-assistant.form.aiFilterPromptHint': '为AI过滤提供自定义指令,留空则使用系统默认', - 'trading-assistant.validation.strategyTypeRequired': '请选择策略类型', - 'trading-assistant.validation.marketCategoryRequired': '请选择交易种类', - 'trading-assistant.form.advancedSettings': '高级设置', - 'trading-assistant.form.orderMode': '下单模式', - 'trading-assistant.form.orderModeMaker': '挂单(Maker)', - 'trading-assistant.form.orderModeTaker': '市价(Taker)', - 'trading-assistant.form.orderModeHint': '挂单模式使用限价单,手续费更低;市价模式立即成交,手续费较高', - 'trading-assistant.form.makerWaitSec': '挂单等待时间(秒)', - 'trading-assistant.form.makerWaitSecHint': '挂单后等待成交的时间,超时后取消并重试', - 'trading-assistant.form.makerRetries': '挂单重试次数', - 'trading-assistant.form.makerRetriesHint': '挂单未成交时的最大重试次数', - 'trading-assistant.form.fallbackToMarket': '挂单失败降级市价', - 'trading-assistant.form.fallbackToMarketHint': '开/平仓挂单未成交时,是否降级为市价单以确保成交', - 'trading-assistant.form.marginMode': '保证金模式', - 'trading-assistant.form.marginModeCross': '全仓', - 'trading-assistant.form.marginModeIsolated': '逐仓', - 'trading-assistant.form.stopLossPct': '止损比例(%)', - 'trading-assistant.form.stopLossPctHint': '设置止损百分比,0表示不启用止损', - 'trading-assistant.form.takeProfitPct': '止盈比例(%)', - 'trading-assistant.form.takeProfitPctHint': '设置止盈百分比,0表示不启用止盈', - 'trading-assistant.form.commission': '手续费(%)', - 'trading-assistant.form.commissionHint': '交易手续费百分比(可选)', - 'trading-assistant.form.slippage': '滑点(%)', - 'trading-assistant.form.slippageHint': '预估滑点百分比(可选)', - 'trading-assistant.form.executionMode': '执行方式', - 'trading-assistant.form.executionModeSignal': '仅信号推送', - 'trading-assistant.form.executionModeLive': '实盘交易(仅加密)', - 'trading-assistant.form.liveTradingCryptoOnlyHint': '目前只有加密货币支持选择实盘交易,其他市场仅支持信号推送。', - 'trading-assistant.form.notifyChannels': '信号推送方式', - 'trading-assistant.form.notifyChannelsHint': '选择买卖信号、止盈止损等事件的推送方式。', - 'trading-assistant.notify.browser': '浏览器通知', - 'trading-assistant.notify.email': '邮箱', - 'trading-assistant.notify.phone': '手机号', - 'trading-assistant.notify.telegram': 'Telegram', - 'trading-assistant.notify.discord': 'Discord', - 'trading-assistant.notify.webhook': 'Webhook', - 'trading-assistant.form.notifyEmail': '邮箱', - 'trading-assistant.form.notifyPhone': '手机号', - 'trading-assistant.form.notifyTelegram': 'Telegram(chat id / 用户名)', - 'trading-assistant.form.notifyDiscord': 'Discord(Webhook URL)', - 'trading-assistant.form.notifyWebhook': 'Webhook URL', - 'trading-assistant.form.liveTradingConfigTitle': '交易所信息', - 'trading-assistant.form.liveTradingConfigHint': '填写交易所 APIKey 等信息,可先测试连接再保存。', - 'trading-assistant.form.savedCredential': '已保存的交易所凭证', - 'trading-assistant.form.savedCredentialHint': '可选择已保存的凭证自动填充(可选)。', - 'trading-assistant.form.saveCredential': '保存本次交易所凭证(下次免重复输入)', - 'trading-assistant.form.credentialName': '凭证名称(可选)', - 'trading-assistant.form.signalMode': '信号模式', - 'trading-assistant.form.signalModeConfirmed': '确认模式', - 'trading-assistant.form.signalModeAggressive': '激进模式', - 'trading-assistant.form.signalModeHint': '确认模式:仅检查已完成的K线;激进模式:也检查正在形成的K线', - 'trading-assistant.form.cancel': '取消', - 'trading-assistant.form.prev': '上一步', - 'trading-assistant.form.next': '下一步', - 'trading-assistant.form.confirmCreate': '确认创建', - 'trading-assistant.form.confirmEdit': '确认修改', - 'trading-assistant.messages.createSuccess': '策略创建成功', - 'trading-assistant.messages.createFailed': '创建策略失败', - 'trading-assistant.messages.updateSuccess': '策略更新成功', - 'trading-assistant.messages.updateFailed': '更新策略失败', - 'trading-assistant.messages.deleteSuccess': '策略删除成功', - 'trading-assistant.messages.deleteFailed': '删除策略失败', - 'trading-assistant.messages.startSuccess': '策略启动成功', - 'trading-assistant.messages.startFailed': '启动策略失败', - 'trading-assistant.messages.stopSuccess': '策略停止成功', - 'trading-assistant.messages.stopFailed': '停止策略失败', - 'trading-assistant.messages.loadFailed': '获取策略列表失败', - 'trading-assistant.messages.runningWarning': '策略运行中,请先停止策略后再修改', - 'trading-assistant.messages.deleteConfirmWithName': '确定要删除策略“{name}”吗?此操作不可恢复。', - 'trading-assistant.messages.deleteConfirm': '确定要删除该策略吗?此操作不可恢复。', - 'trading-assistant.messages.loadTradesFailed': '获取交易记录失败', - 'trading-assistant.messages.loadPositionsFailed': '获取持仓记录失败', - 'trading-assistant.messages.loadEquityFailed': '获取净值曲线失败', - 'trading-assistant.messages.loadIndicatorsFailed': '加载指标列表失败,请稍后重试', - 'trading-assistant.messages.spotLimitations': '现货交易已自动设置为仅做多、1倍杠杆', - 'trading-assistant.messages.autoFillApiConfig': '已自动填充该交易所的历史API配置', - 'trading-assistant.placeholders.selectIndicator': '请选择指标', - 'trading-assistant.placeholders.selectExchange': '请选择交易所', - 'trading-assistant.placeholders.selectMarketCategory': '请选择交易种类', - 'trading-assistant.placeholders.inputApiKey': '请输入API Key', - 'trading-assistant.placeholders.inputSecretKey': '请输入Secret Key', - 'trading-assistant.placeholders.inputPassphrase': '请输入Passphrase', - 'trading-assistant.placeholders.inputStrategyName': '请输入策略名称', - 'trading-assistant.placeholders.selectSymbol': '请选择交易对', - 'trading-assistant.placeholders.inputSymbol': '请输入标的代码', - 'trading-assistant.placeholders.selectTimeframe': '请选择时间周期', - 'trading-assistant.placeholders.selectKlinePeriod': '请选择K线周期', - 'trading-assistant.placeholders.inputAiFilterPrompt': '请输入自定义提示词(可选)', - 'trading-assistant.placeholders.inputEmail': '请输入邮箱', - 'trading-assistant.placeholders.inputPhone': '请输入手机号', - 'trading-assistant.placeholders.inputTelegram': '请输入 Telegram chat id / 用户名', - 'trading-assistant.placeholders.inputDiscord': '请输入 Discord webhook url', - 'trading-assistant.placeholders.inputWebhook': '请输入 webhook url', - 'trading-assistant.placeholders.selectSavedCredential': '请选择已保存的凭证', - 'trading-assistant.placeholders.inputCredentialName': '例如:Binance 主账号', - 'trading-assistant.validation.indicatorRequired': '请选择指标', - 'trading-assistant.validation.exchangeRequired': '请选择交易所', - 'trading-assistant.validation.apiKeyRequired': '请输入API Key', - 'trading-assistant.validation.secretKeyRequired': '请输入Secret Key', - 'trading-assistant.validation.passphraseRequired': '请输入Passphrase', - 'trading-assistant.validation.exchangeConfigIncomplete': '请填写完整的交易所配置信息', - 'trading-assistant.validation.testConnectionRequired': '请先点击"测试连接"按钮并确保连接成功', - 'trading-assistant.validation.testConnectionFailed': '连接测试失败,请检查配置后重新测试', - 'trading-assistant.validation.strategyNameRequired': '请输入策略名称', - 'trading-assistant.validation.symbolRequired': '请选择交易对', - 'trading-assistant.validation.initialCapitalRequired': '请输入投入金额', - 'trading-assistant.validation.leverageRequired': '请输入杠杆倍数', - 'trading-assistant.validation.emailInvalid': '邮箱格式不正确', - 'trading-assistant.validation.notifyChannelRequired': '请至少选择一种信号推送方式', - 'trading-assistant.table.time': '时间', - 'trading-assistant.table.type': '类型', - 'trading-assistant.table.price': '价格', - 'trading-assistant.table.amount': '数量', - 'trading-assistant.table.value': '金额', - 'trading-assistant.table.commission': '手续费', - 'trading-assistant.table.symbol': '交易对', - 'trading-assistant.table.side': '方向', - 'trading-assistant.table.size': '持仓数量', - 'trading-assistant.table.entryPrice': '开仓价格', - 'trading-assistant.table.currentPrice': '当前价格', - 'trading-assistant.table.unrealizedPnl': '未实现盈亏', - 'trading-assistant.table.pnlPercent': '盈亏比例', - 'trading-assistant.table.buy': '买入', - 'trading-assistant.table.sell': '卖出', - 'trading-assistant.table.long': '做多', - 'trading-assistant.table.short': '做空', - 'trading-assistant.table.noPositions': '暂无持仓', - 'trading-assistant.detail.title': '策略详情', - 'trading-assistant.detail.strategyName': '策略名称', - 'trading-assistant.detail.strategyType': '策略类型', - 'trading-assistant.detail.status': '状态', - 'trading-assistant.detail.tradingMode': '交易模式', - 'trading-assistant.detail.exchange': '交易所', - 'trading-assistant.detail.initialCapital': '初始资金', - 'trading-assistant.detail.totalInvestment': '总投入金额', - 'trading-assistant.detail.currentEquity': '当前净值', - 'trading-assistant.detail.totalPnl': '总盈亏', - 'trading-assistant.detail.indicatorName': '指标名称', - 'trading-assistant.detail.maxLeverage': '最大杠杆', - 'trading-assistant.detail.decideInterval': '决策间隔', - 'trading-assistant.detail.symbols': '交易标的', - 'trading-assistant.detail.createdAt': '创建时间', - 'trading-assistant.detail.updatedAt': '更新时间', - 'trading-assistant.detail.llmConfig': 'AI模型配置', - 'trading-assistant.detail.exchangeConfig': '交易所配置', - 'trading-assistant.detail.provider': '提供商', - 'trading-assistant.detail.modelId': '模型ID', - 'trading-assistant.detail.close': '关闭', - 'trading-assistant.detail.loadFailed': '获取策略详情失败', - 'trading-assistant.equity.noData': '暂无净值数据', - 'trading-assistant.equity.equity': '净值', - 'trading-assistant.exchange.tradingMode': '交易模式', - 'trading-assistant.exchange.virtual': '模拟交易', - 'trading-assistant.exchange.live': '实盘交易', - 'trading-assistant.exchange.selectExchange': '选择交易所', - 'trading-assistant.exchange.walletAddress': '钱包地址', - 'trading-assistant.exchange.walletAddressPlaceholder': '请输入钱包地址(以0x开头)', - 'trading-assistant.exchange.privateKey': '私钥', - 'trading-assistant.exchange.privateKeyPlaceholder': '请输入私钥(64字符)', - 'trading-assistant.exchange.testConnection': '测试连接', - 'trading-assistant.exchange.connectionSuccess': '连接成功', - 'trading-assistant.exchange.connectionFailed': '连接失败', - 'trading-assistant.exchange.testFailed': '连接测试失败', - 'trading-assistant.exchange.fillComplete': '请填写完整的交易所配置信息', - 'trading-assistant.exchange.ipWhitelistTip': '请在交易所API设置中将以下IP添加到白名单:', - 'trading-assistant.strategyTypeOptions.ai': 'AI驱动策略', - 'trading-assistant.strategyTypeOptions.indicator': '技术指标策略', - 'trading-assistant.strategyTypeOptions.aiDeveloping': 'AI驱动策略功能开发中,敬请期待', - 'trading-assistant.strategyTypeOptions.aiDevelopingWarning': 'AI驱动策略功能正在开发中', - 'trading-assistant.indicatorType.trend': '趋势', - 'trading-assistant.indicatorType.momentum': '动量', - 'trading-assistant.indicatorType.volatility': '波动', - 'trading-assistant.indicatorType.volume': '成交量', - 'trading-assistant.indicatorType.custom': '自定义', - 'trading-assistant.exchangeNames': { - 'okx': 'OKX', - 'binance': '币安', - 'hyperliquid': 'Hyperliquid', - 'blockchaincom': 'Blockchain.com', - 'coinbaseexchange': 'Coinbase', - 'gate': 'Gate.io', - 'mexc': 'MEXC', - 'kraken': 'Kraken', - 'bitfinex': 'Bitfinex', - 'bybit': 'Bybit', - 'kucoin': 'KuCoin', - 'huobi': '火币', - 'bitget': 'Bitget', - 'bitmex': 'BitMEX', - 'deribit': 'Deribit', - 'phemex': 'Phemex', - 'bitmart': 'BitMart', - 'bitstamp': 'Bitstamp', - 'bittrex': 'Bittrex', - 'poloniex': 'Poloniex', - 'gemini': 'Gemini', - 'cryptocom': 'Crypto.com', - 'bitflyer': 'bitFlyer', - 'upbit': 'Upbit', - 'bithumb': 'Bithumb', - 'coinone': 'Coinone', - 'zb': 'ZB', - 'lbank': 'LBank', - 'bibox': 'Bibox', - 'bigone': 'BigONE', - 'bitrue': 'Bitrue', - 'coinex': 'CoinEx', - 'digifinex': 'DigiFinex', - 'ftx': 'FTX', - 'ftxus': 'FTX US', - 'binanceus': 'Binance US', - 'binancecoinm': 'Binance COIN-M', - 'binanceusdm': 'Binance USDⓈ-M' - }, - 'ai-trading-assistant.title': 'AI交易助手', - 'ai-trading-assistant.strategyList': '策略列表', - 'ai-trading-assistant.createStrategy': '创建策略', - 'ai-trading-assistant.noStrategy': '暂无策略', - 'ai-trading-assistant.selectStrategy': '请从左侧选择一个策略查看详情', - 'ai-trading-assistant.startStrategy': '启动策略', - 'ai-trading-assistant.stopStrategy': '停止策略', - 'ai-trading-assistant.editStrategy': '编辑策略', - 'ai-trading-assistant.deleteStrategy': '删除策略', - 'ai-trading-assistant.status.running': '运行中', - 'ai-trading-assistant.status.stopped': '已停止', - 'ai-trading-assistant.status.error': '错误', - 'ai-trading-assistant.tabs.tradingRecords': '交易记录', - 'ai-trading-assistant.tabs.positions': '持仓记录', - 'ai-trading-assistant.tabs.aiDecisions': 'AI决策记录', - 'ai-trading-assistant.tabs.equityCurve': '净值曲线', - 'ai-trading-assistant.form.createTitle': '创建AI交易策略', - 'ai-trading-assistant.form.editTitle': '编辑AI交易策略', - 'ai-trading-assistant.form.strategyName': '策略名称', - 'ai-trading-assistant.form.modelId': 'AI模型', - 'ai-trading-assistant.form.modelIdHint': '使用系统OpenRouter服务,无需配置API Key', - 'ai-trading-assistant.form.decideInterval': '决策间隔', - 'ai-trading-assistant.form.decideInterval5m': '5分钟', - 'ai-trading-assistant.form.decideInterval10m': '10分钟', - 'ai-trading-assistant.form.decideInterval30m': '30分钟', - 'ai-trading-assistant.form.decideInterval1h': '1小时', - 'ai-trading-assistant.form.decideInterval4h': '4小时', - 'ai-trading-assistant.form.decideInterval1d': '1天', - 'ai-trading-assistant.form.decideInterval1w': '1周', - 'ai-trading-assistant.form.decideIntervalHint': 'AI做决策的时间间隔', - 'ai-trading-assistant.form.runPeriod': '运行周期', - 'ai-trading-assistant.form.runPeriodHint': '策略运行的开始时间和结束时间', - 'ai-trading-assistant.form.startDate': '开始日期', - 'ai-trading-assistant.form.endDate': '结束日期', - 'ai-trading-assistant.form.qdtCostTitle': 'QDT扣费说明', - 'ai-trading-assistant.form.qdtCostHint': '每次AI决策将扣费 {cost} 个QDT,请确保账户有足够的QDT余额。策略运行期间,每次决策都会实时扣费。', - 'ai-trading-assistant.form.apiKey': 'API Key', - 'ai-trading-assistant.form.exchange': '选择交易所', - 'ai-trading-assistant.form.secretKey': 'Secret Key', - 'ai-trading-assistant.form.passphrase': 'Passphrase', - 'ai-trading-assistant.form.testConnection': '测试连接', - 'ai-trading-assistant.form.symbol': '交易对', - 'ai-trading-assistant.form.symbolHint': '选择要交易的交易对', - 'ai-trading-assistant.form.initialCapital': '投入金额(保证金)', - 'ai-trading-assistant.form.leverage': '杠杆倍数', - 'ai-trading-assistant.form.timeframe': '时间周期', - 'ai-trading-assistant.form.timeframe1m': '1分钟', - 'ai-trading-assistant.form.timeframe5m': '5分钟', - 'ai-trading-assistant.form.timeframe15m': '15分钟', - 'ai-trading-assistant.form.timeframe30m': '30分钟', - 'ai-trading-assistant.form.timeframe1H': '1小时', - 'ai-trading-assistant.form.timeframe4H': '4小时', - 'ai-trading-assistant.form.timeframe1D': '1天', - 'ai-trading-assistant.form.marketType': '市场类型', - 'ai-trading-assistant.form.marketTypeFutures': '合约', - 'ai-trading-assistant.form.marketTypeSpot': '现货', - 'ai-trading-assistant.form.totalPnl': '总盈亏', - 'ai-trading-assistant.form.customPrompt': '自定义提示词', - 'ai-trading-assistant.form.customPromptHint': '可选,用于自定义AI的交易策略和决策逻辑', - 'ai-trading-assistant.form.cancel': '取消', - 'ai-trading-assistant.form.prev': '上一步', - 'ai-trading-assistant.form.next': '下一步', - 'ai-trading-assistant.form.confirmCreate': '确认创建', - 'ai-trading-assistant.form.confirmEdit': '确认修改', - 'ai-trading-assistant.messages.createSuccess': '策略创建成功', - 'ai-trading-assistant.messages.createFailed': '创建策略失败', - 'ai-trading-assistant.messages.updateSuccess': '策略更新成功', - 'ai-trading-assistant.messages.updateFailed': '更新策略失败', - 'ai-trading-assistant.messages.deleteSuccess': '策略删除成功', - 'ai-trading-assistant.messages.deleteFailed': '删除策略失败', - 'ai-trading-assistant.messages.startSuccess': '策略启动成功', - 'ai-trading-assistant.messages.startFailed': '启动策略失败', - 'ai-trading-assistant.messages.stopSuccess': '策略停止成功', - 'ai-trading-assistant.messages.stopFailed': '停止策略失败', - 'ai-trading-assistant.messages.loadFailed': '获取策略列表失败', - 'ai-trading-assistant.messages.loadDecisionsFailed': '获取AI决策记录失败', - 'ai-trading-assistant.messages.deleteConfirm': '确定要删除该策略吗?此操作不可恢复。', - 'ai-trading-assistant.placeholders.inputStrategyName': '请输入策略名称', - 'ai-trading-assistant.placeholders.selectModelId': '请选择AI模型', - 'ai-trading-assistant.placeholders.selectDecideInterval': '请选择决策间隔', - 'ai-trading-assistant.placeholders.startTime': '开始时间', - 'ai-trading-assistant.placeholders.endTime': '结束时间', - 'ai-trading-assistant.placeholders.inputApiKey': '请输入API Key', - 'ai-trading-assistant.placeholders.selectExchange': '请选择交易所', - 'ai-trading-assistant.placeholders.inputSecretKey': '请输入Secret Key', - 'ai-trading-assistant.placeholders.inputPassphrase': '请输入Passphrase', - 'ai-trading-assistant.placeholders.selectSymbol': '请选择交易对,如:BTC/USDT', - 'ai-trading-assistant.placeholders.selectTimeframe': '请选择时间周期', - 'ai-trading-assistant.placeholders.inputCustomPrompt': '请输入自定义提示词(可选)', - 'ai-trading-assistant.validation.strategyNameRequired': '请输入策略名称', - 'ai-trading-assistant.validation.modelIdRequired': '请选择AI模型', - 'ai-trading-assistant.validation.runPeriodRequired': '请选择运行周期', - 'ai-trading-assistant.validation.apiKeyRequired': '请输入API Key', - 'ai-trading-assistant.validation.exchangeRequired': '请选择交易所', - 'ai-trading-assistant.validation.secretKeyRequired': '请输入Secret Key', - 'ai-trading-assistant.validation.symbolRequired': '请选择交易对', - 'ai-trading-assistant.validation.initialCapitalRequired': '请输入投入金额', - 'ai-trading-assistant.table.time': '时间', - 'ai-trading-assistant.table.type': '类型', - 'ai-trading-assistant.table.price': '价格', - 'ai-trading-assistant.table.amount': '数量', - 'ai-trading-assistant.table.value': '金额', - 'ai-trading-assistant.table.symbol': '交易对', - 'ai-trading-assistant.table.side': '方向', - 'ai-trading-assistant.table.size': '持仓数量', - 'ai-trading-assistant.table.entryPrice': '开仓价格', - 'ai-trading-assistant.table.currentPrice': '当前价格', - 'ai-trading-assistant.table.unrealizedPnl': '未实现盈亏', - 'ai-trading-assistant.table.profit': '盈亏', - 'ai-trading-assistant.table.openLong': '开多', - 'ai-trading-assistant.table.closeLong': '平多', - 'ai-trading-assistant.table.openShort': '开空', - 'ai-trading-assistant.table.closeShort': '平空', - 'ai-trading-assistant.table.addLong': '加多', - 'ai-trading-assistant.table.addShort': '加空', - 'ai-trading-assistant.table.closeShortProfit': '平空止盈', - 'ai-trading-assistant.table.closeShortStop': '平空止损', - 'ai-trading-assistant.table.closeLongProfit': '平多止盈', - 'ai-trading-assistant.table.closeLongStop': '平多止损', - 'ai-trading-assistant.table.buy': '买入', - 'ai-trading-assistant.table.sell': '卖出', - 'ai-trading-assistant.table.long': '做多', - 'ai-trading-assistant.table.short': '做空', - 'ai-trading-assistant.table.hold': '持有', - 'ai-trading-assistant.table.reasoning': '分析理由', - 'ai-trading-assistant.table.decisions': '决策', - 'ai-trading-assistant.table.riskAssessment': '风险评估', - 'ai-trading-assistant.table.confidence': '置信度', - 'ai-trading-assistant.table.totalRecords': '共 {total} 条记录', - 'ai-trading-assistant.table.noPositions': '暂无持仓', - 'ai-trading-assistant.detail.title': '策略详情', - 'ai-trading-assistant.equity.noData': '暂无净值数据', - 'ai-trading-assistant.equity.equity': '净值', - 'ai-trading-assistant.exchange.testFailed': '连接测试失败', - 'ai-trading-assistant.exchange.connectionSuccess': '连接成功', - 'ai-trading-assistant.exchange.connectionFailed': '连接失败', - 'ai-trading-assistant.form.advancedSettings': '高级设置', - 'ai-trading-assistant.form.orderMode': '下单模式', - 'ai-trading-assistant.form.orderModeMaker': '挂单(Maker)', - 'ai-trading-assistant.form.orderModeTaker': '市价(Taker)', - 'ai-trading-assistant.form.orderModeHint': '挂单模式使用限价单,手续费更低;市价模式立即成交,手续费较高', - 'ai-trading-assistant.form.makerWaitSec': '挂单等待时间(秒)', - 'ai-trading-assistant.form.makerWaitSecHint': '挂单后等待成交的时间,超时后取消并重试', - 'ai-trading-assistant.form.makerRetries': '挂单重试次数', - 'ai-trading-assistant.form.makerRetriesHint': '挂单未成交时的最大重试次数', - 'ai-trading-assistant.form.fallbackToMarket': '挂单失败降级市价', - 'ai-trading-assistant.form.fallbackToMarketHint': '开/平仓挂单未成交时,是否降级为市价单以确保成交', - 'ai-trading-assistant.form.marginMode': '保证金模式', - 'ai-trading-assistant.form.marginModeCross': '全仓', - 'ai-trading-assistant.form.marginModeIsolated': '逐仓', - 'ai-analysis.title': '量子交易引擎', - 'ai-analysis.system.online': '在线', - 'ai-analysis.system.agents': '智能体', - 'ai-analysis.system.active': '活跃', - 'ai-analysis.system.stage': '阶段', - 'ai-analysis.panel.roster': '智能体阵容', - 'ai-analysis.panel.thinking': '思考中...', - 'ai-analysis.panel.done': '完成', - 'ai-analysis.panel.standby': '待机', - 'ai-analysis.input.title': '量子交易引擎', - 'ai-analysis.input.placeholder': '选择目标资产 (如 BTC/USDT)', - 'ai-analysis.input.watchlist': '自选股', - 'ai-analysis.input.start': '开始分析', - 'ai-analysis.input.recent': '最近任务:', - 'ai-analysis.vis.stage': '阶段', - 'ai-analysis.vis.processing': '处理中', - 'ai-analysis.result.complete': '分析完成', - 'ai-analysis.result.signal': '最终信号', - 'ai-analysis.result.confidence': '置信度:', - 'ai-analysis.result.new': '新分析', - 'ai-analysis.result.full': '查看完整报告', - 'ai-analysis.logs.title': '系统日志', - 'ai-analysis.modal.title': '机密报告', - 'ai-analysis.modal.fundamental': '基本面分析', - 'ai-analysis.modal.technical': '技术面分析', - 'ai-analysis.modal.sentiment': '情绪面分析', - 'ai-analysis.modal.risk': '风险评估', - 'ai-analysis.stage.idle': '待机', - 'ai-analysis.stage.1': '第一阶段:多维分析', - 'ai-analysis.stage.2': '第二阶段:多空辩论', - 'ai-analysis.stage.3': '第三阶段:战略规划', - 'ai-analysis.stage.4': '第四阶段:风控审查', - 'ai-analysis.stage.complete': '完成', - 'ai-analysis.agent.investment_director': '投资总监', - 'ai-analysis.agent.role.investment_director': '综合分析 & 最终结论', - 'ai-analysis.agent.market': '市场分析师', - 'ai-analysis.agent.role.market': '技术 & 市场数据', - 'ai-analysis.agent.fundamental': '基本面分析师', - 'ai-analysis.agent.role.fundamental': '财务 & 估值', - 'ai-analysis.agent.technical': '技术分析师', - 'ai-analysis.agent.role.technical': '技术指标 & 图表', - 'ai-analysis.agent.news': '新闻分析师', - 'ai-analysis.agent.role.news': '全球新闻过滤', - 'ai-analysis.agent.sentiment': '情绪分析师', - 'ai-analysis.agent.role.sentiment': '社交 & 情绪', - 'ai-analysis.agent.risk': '风险分析师', - 'ai-analysis.agent.role.risk': '基础风险检查', - 'ai-analysis.agent.bull': '看涨研究员', - 'ai-analysis.agent.role.bull': '增长催化剂挖掘', - 'ai-analysis.agent.bear': '看跌研究员', - 'ai-analysis.agent.role.bear': '风险 & 缺陷挖掘', - 'ai-analysis.agent.manager': '研究经理', - 'ai-analysis.agent.role.manager': '辩论主持人', - 'ai-analysis.agent.trader': '交易员', - 'ai-analysis.agent.role.trader': '执行策略师', - 'ai-analysis.agent.risky': '激进分析师', - 'ai-analysis.agent.role.risky': '激进策略', - 'ai-analysis.agent.neutral': '平衡分析师', - 'ai-analysis.agent.role.neutral': '平衡策略', - 'ai-analysis.agent.safe': '保守分析师', - 'ai-analysis.agent.role.safe': '保守策略', - 'ai-analysis.agent.cro': '风控经理 (CRO)', - 'ai-analysis.agent.role.cro': '最终决策权威', - 'ai-analysis.script.market': '正在从主要交易所获取 OHLCV 数据...', - 'ai-analysis.script.fundamental': '正在检索季度财务报告...', - 'ai-analysis.script.technical': '正在分析技术指标和图表形态...', - 'ai-analysis.script.news': '正在扫描全球财经新闻...', - 'ai-analysis.script.sentiment': '正在分析社交媒体趋势...', - 'ai-analysis.script.risk': '正在计算历史波动率...', - 'invite.inviteLink': '邀请链接', - 'invite.copy': '复制链接', - 'invite.copySuccess': '复制成功!', - 'invite.copyFailed': '复制失败,请手动复制', - 'invite.noInviteLink': '邀请链接未生成', - 'invite.totalInvites': '累计邀请', - 'invite.totalReward': '累计奖励', - 'invite.rules': '邀请规则', - 'invite.rule1': '每成功邀请一位好友注册,您将获得奖励', - 'invite.rule2': '被邀请的好友完成首次交易,您将获得额外奖励', - 'invite.rule3': '邀请奖励将直接发放到您的账户', - 'invite.inviteList': '邀请列表', - 'invite.tasks': '任务中心', - 'invite.inviteeName': '被邀请人', - 'invite.inviteTime': '邀请时间', - 'invite.status': '状态', - 'invite.reward': '奖励', - 'invite.active': '活跃', - 'invite.inactive': '未激活', - 'invite.completed': '已完成', - 'invite.claimed': '已领取', - 'invite.pending': '待完成', - 'invite.goToTask': '去完成', - 'invite.claimReward': '领取奖励', - 'invite.verify': '验证完成', - 'invite.verifySuccess': '验证成功!任务已完成', - 'invite.verifyNotCompleted': '任务尚未完成,请先完成任务', - 'invite.verifyFailed': '验证失败,请稍后重试', - 'invite.claimSuccess': '成功领取 {reward} QDT!', - 'invite.claimFailed': '领取失败,请稍后重试', - 'invite.totalRecords': '共 {total} 条记录', - 'invite.task.twitter.title': '转发推文到 X (Twitter)', - 'invite.task.twitter.desc': '分享我们的官方推文到您的 X (Twitter) 账号', - 'invite.task.youtube.title': '关注我们的 YouTube 频道', - 'invite.task.youtube.desc': '订阅并关注我们的 YouTube 官方频道', - 'invite.task.telegram.title': '加入 Telegram 群组', - 'invite.task.telegram.desc': '加入我们的官方 Telegram 社区群组', - 'invite.task.discord.title': '加入 Discord 服务器', - 'invite.task.discord.desc': '加入我们的 Discord 社区服务器', - 'message': '-', - 'layouts.usermenu.dialog.title': '信息', - 'layouts.usermenu.dialog.content': '您确定要注销吗?', - 'layouts.userLayout.title': '于不确定中,寻见真理', - // Signal Robot - 'signal-robot.title': '信号机器人控制台', - 'signal-robot.createBot': '创建新机器人', - 'signal-robot.search.nameOrSymbol': '名称/币种', - 'signal-robot.search.placeholder': '搜索机器人名称或币种', - 'signal-robot.search.status': '状态', - 'signal-robot.search.statusAll': '全部', - 'signal-robot.search.statusRunning': '运行中', - 'signal-robot.search.statusPaused': '已暂停', - 'signal-robot.search.query': '查询', - 'signal-robot.search.reset': '重置', - 'signal-robot.table.botName': '机器人名称', - 'signal-robot.table.symbolTimeframe': '交易对/周期', - 'signal-robot.table.triggerStrategy': '触发策略', - 'signal-robot.table.notificationChannels': '通知方式', - 'signal-robot.table.status': '状态', - 'signal-robot.table.action': '操作', - 'signal-robot.table.triggerConditions': '{count} 个触发条件', - 'signal-robot.table.monitoring': '监控中', - 'signal-robot.table.paused': '已暂停', - 'signal-robot.table.edit': '编辑', - 'signal-robot.table.pause': '暂停', - 'signal-robot.table.start': '启动', - 'signal-robot.table.delete': '删除', - 'signal-robot.table.deleteConfirm': '确定要删除这个机器人吗?', - 'signal-robot.table.deleteSuccess': '删除成功', - 'signal-robot.table.startSuccess': '机器人已启动', - 'signal-robot.table.pauseSuccess': '机器人已暂停', - 'signal-robot.channel.telegram': 'Telegram', - 'signal-robot.channel.discord': 'Discord', - 'signal-robot.channel.email': 'Email', - 'signal-robot.channel.webhook': 'Webhook', - 'signal-robot.channel.browser': '浏览器推送', - 'signal-robot.modal.createTitle': '创建新信号机器人', - 'signal-robot.modal.editTitle': '编辑信号机器人', - 'signal-robot.modal.step.basic': '基础设置', - 'signal-robot.modal.step.entry': '入场策略', - 'signal-robot.modal.step.position': '资金管理', - 'signal-robot.modal.step.risk': '风控止损', - 'signal-robot.modal.step.notify': '通知配置', - 'signal-robot.modal.prev': '上一步', - 'signal-robot.modal.next': '下一步', - 'signal-robot.modal.preview': '预览回测', - 'signal-robot.modal.complete': '完成并运行', - 'signal-robot.modal.saveSuccess': '保存成功', - 'signal-robot.modal.previewDeveloping': '预览功能开发中...', - 'signal-robot.modal.basicInfoRequired': '请完善基础信息', - 'signal-robot.basic.alert': '设置机器人的基本信息', - 'signal-robot.basic.title': '基础信息', - 'signal-robot.basic.botName': '机器人名称', - 'signal-robot.basic.botNamePlaceholder': '例如:BTC SuperTrend 策略', - 'signal-robot.basic.symbol': '交易对', - 'signal-robot.basic.symbolPlaceholder': '选择自选股', - 'signal-robot.basic.noWatchlist': '暂无自选股,请先在行情页添加', - 'signal-robot.basic.timeframe': 'K线周期', - 'signal-robot.basic.timeframe15m': '15分钟', - 'signal-robot.basic.timeframe30m': '30分钟', - 'signal-robot.basic.timeframe1h': '1小时', - 'signal-robot.basic.timeframe4h': '4小时', - 'signal-robot.basic.timeframe1d': '1天', - 'signal-robot.basic.autoNameSuggestion': '{symbol} 信号机器人', - 'signal-robot.entry.alert': '当满足以下所有条件时,触发入场信号', - 'signal-robot.entry.condition': '条件 {index}', - 'signal-robot.entry.indicator': '指标', - 'signal-robot.entry.indicatorSupertrend': 'SuperTrend', - 'signal-robot.entry.indicatorEma': 'EMA (指数移动平均)', - 'signal-robot.entry.indicatorRsi': 'RSI (相对强弱)', - 'signal-robot.entry.indicatorMacd': 'MACD', - 'signal-robot.entry.indicatorBollinger': '布林带 (Bollinger)', - 'signal-robot.entry.indicatorKdj': 'KDJ', - 'signal-robot.entry.indicatorMa': 'MA (移动平均)', - 'signal-robot.entry.fastPeriod': '快线周期', - 'signal-robot.entry.slowPeriod': '慢线周期', - 'signal-robot.entry.signalPeriod': '信号周期', - 'signal-robot.entry.stdDev': '标准差', - 'signal-robot.entry.maType': 'MA类型', - 'signal-robot.entry.conditionDiffGtDea': '快线 > 慢线', - 'signal-robot.entry.conditionDiffLtDea': '快线 < 慢线', - 'signal-robot.entry.conditionGoldCross': '金叉 (快穿慢)', - 'signal-robot.entry.conditionDeathCross': '死叉 (快穿慢)', - 'signal-robot.entry.conditionPriceAboveUpper': '价格 > 上轨', - 'signal-robot.entry.conditionPriceBelowLower': '价格 < 下轨', - 'signal-robot.entry.conditionPriceAboveMid': '价格 > 中轨', - 'signal-robot.entry.conditionPriceBelowMid': '价格 < 中轨', - 'signal-robot.entry.conditionKGtD': 'K > D', - 'signal-robot.entry.conditionKLtD': 'K < D', - 'signal-robot.entry.atrPeriod': 'ATR 周期', - 'signal-robot.entry.multiplier': '乘数 (Multiplier)', - 'signal-robot.entry.triggerSignal': '触发信号', - 'signal-robot.entry.signalTrendBullish': '趋势转多 (Trend Bullish)', - 'signal-robot.entry.signalTrendBearish': '趋势转空 (Trend Bearish)', - 'signal-robot.entry.signalIsUptrend': '处于多头趋势中 (Is Uptrend)', - 'signal-robot.entry.signalIsDowntrend': '处于空头趋势中 (Is Downtrend)', - 'signal-robot.entry.period': '周期 (Period)', - 'signal-robot.entry.compareCondition': '比较条件', - 'signal-robot.entry.priceAbove': '价格 > EMA (Price Above)', - 'signal-robot.entry.priceBelow': '价格 < EMA (Price Below)', - 'signal-robot.entry.crossUp': '价格上穿 EMA (Golden Cross)', - 'signal-robot.entry.crossDown': '价格下穿 EMA (Death Cross)', - 'signal-robot.entry.compare': '比较', - 'signal-robot.entry.greaterThan': '大于', - 'signal-robot.entry.lessThan': '小于', - 'signal-robot.entry.crossUpShort': '上穿', - 'signal-robot.entry.crossDownShort': '下穿', - 'signal-robot.entry.threshold': '阈值', - 'signal-robot.entry.selectIndicator': '请选择指标以配置参数', - 'signal-robot.entry.addCondition': '添加判断条件', - 'signal-robot.position.alert': '配置首仓大小、杠杆及加仓规则', - 'signal-robot.position.basicTitle': '基础仓位管理', - 'signal-robot.position.initialSize': '首仓大小 (资金占比 %)', - 'signal-robot.position.initialSizeHint': '建议 5% - 20%', - 'signal-robot.position.leverage': '杠杆倍数 (Leverage)', - 'signal-robot.position.leverageHint': '请确保交易所账户支持该杠杆', - 'signal-robot.position.maxPyramiding': '最大加仓次数', - 'signal-robot.position.maxPyramidingHint': '0 表示不加仓', - 'signal-robot.position.pyramidingTitle': '加仓规则 (Pyramiding)', - 'signal-robot.position.pyramidingEnabled': '开启', - 'signal-robot.position.pyramidingDisabled': '关闭', - 'signal-robot.position.pyramidingCondition': '加仓触发条件', - 'signal-robot.position.priceRisePct': '价格每上涨 (多头) / 下跌 (空头)', - 'signal-robot.position.profitPct': '盈利达到', - 'signal-robot.position.triggerThreshold': '触发阈值 (%)', - 'signal-robot.position.triggerThresholdHint': '例如 3%:涨3%加仓一次', - 'signal-robot.position.addSize': '单次加仓大小 (资金占比 %)', - 'signal-robot.position.pyramidingDisabledHint': '加仓功能已关闭,仅执行首仓', - 'signal-robot.risk.alert': '配置止盈止损及平仓规则', - 'signal-robot.risk.stopLossTitle': '止损 (Stop Loss)', - 'signal-robot.risk.stopLossEnabled': '开启', - 'signal-robot.risk.stopLossDisabled': '关闭', - 'signal-robot.risk.stopLossType': '止损类型', - 'signal-robot.risk.stopLossFixedPct': '固定百分比 (Fixed %)', - 'signal-robot.risk.stopLossAtrMultiplier': 'ATR 倍数 (ATR Multiplier)', - 'signal-robot.risk.stopLossValue': '数值', - 'signal-robot.risk.stopLossFixedHint': '例如 2%:亏损 2% 止损', - 'signal-robot.risk.stopLossAtrHint': '例如 2.0:价格触及 ATR*2.0 止损', - 'signal-robot.risk.stopLossDisabledHint': '未启用止损(高风险)', - 'signal-robot.risk.trailingStopTitle': '移动止盈 (Trailing Stop)', - 'signal-robot.risk.activationProfit': '激活门槛 (Activation Profit %)', - 'signal-robot.risk.activationProfitHint': '盈利达到多少时开始追踪', - 'signal-robot.risk.callbackPct': '回撤比例 (Callback %)', - 'signal-robot.risk.callbackPctHint': '最高点回撤多少时触发止盈', - 'signal-robot.risk.trailingStopDisabledHint': '未启用移动止盈', - 'signal-robot.risk.otherExitTitle': '其他平仓规则', - 'signal-robot.risk.signalExit': '允许反向信号平仓 (Signal Exit)', - 'signal-robot.risk.signalExitHint': '当出现反向信号时(如做多时出现做空信号),立即平仓', - 'signal-robot.notify.alert': '配置信号触发后的通知方式', - 'signal-robot.notify.channelTitle': '通知渠道', - 'signal-robot.notify.discordWebhook': 'Discord Webhook', - 'signal-robot.notify.genericWebhook': 'Generic Webhook', - 'signal-robot.notify.webhookUrl': 'Webhook URL', - 'signal-robot.notify.webhookUrlPlaceholder': 'https://example.com/webhook', - 'signal-robot.notify.discordUrl': 'Discord URL', - 'signal-robot.notify.discordUrlPlaceholder': 'https://discord.com/api/webhooks/...', - 'signal-robot.operator.greaterThan': '大于', - 'signal-robot.operator.lessThan': '小于', - 'signal-robot.operator.equal': '等于', - 'signal-robot.operator.goldenCross': '金叉', - 'signal-robot.operator.deathCross': '死叉', - 'signal-robot.operator.increaseBy': '上涨超', - 'signal-robot.operator.decreaseBy': '下跌超', - 'signal-robot.indicator.price': '价格', - 'signal-robot.indicator.rsi': 'RSI', - 'signal-robot.indicator.kdjK': 'KDJ(K)', - 'signal-robot.indicator.kdjJ': 'KDJ(J)', - 'signal-robot.indicator.macd': 'MACD', - 'signal-robot.indicator.macdHist': 'MACD柱', - 'signal-robot.indicator.bollingerUp': '布林上轨', - 'signal-robot.indicator.bollingerLow': '布林下轨', - 'signal-robot.indicator.volume': '成交量', - 'signal-robot.indicator.changePct1h': '1H涨幅', - 'signal-robot.indicator.changePct24h': '24H涨幅', - 'signal-robot.indicator.signalLine': '信号线' -} - -export default { - ...components, - ...locale -} diff --git a/quantdinger_vue/src/locales/lang/zh-TW.js b/quantdinger_vue/src/locales/lang/zh-TW.js index 2cc3cb6..ba1bd91 100644 --- a/quantdinger_vue/src/locales/lang/zh-TW.js +++ b/quantdinger_vue/src/locales/lang/zh-TW.js @@ -19,6 +19,7 @@ const locale = { 'menu.dashboard.community': '指標社區', 'menu.dashboard.analysis': 'AI 分析', 'menu.dashboard.tradingAssistant': '交易助手', + 'menu.settings': '系统设置', 'menu.dashboard.aiTradingAssistant': 'AI交易助手', 'menu.dashboard.signalRobot': '信號機器人', 'menu.dashboard.monitor': '監控頁', @@ -846,6 +847,41 @@ const locale = { 'dashboard.enabled': '已啟用', 'dashboard.pnlHistory': '歷史盈虧', 'dashboard.strategyPerformance': '策略盈虧佔比', + 'dashboard.drawdown': '回撤曲線', + 'dashboard.strategyRanking': '策略排行榜', + // New KPI labels + 'dashboard.winRate': '勝率', + 'dashboard.profitFactor': '盈虧比', + 'dashboard.maxDrawdown': '最大回撤', + 'dashboard.totalTrades': '總交易', + 'dashboard.runningStrategies': '運行中策略', + 'dashboard.equityCurve': '權益曲線', + 'dashboard.strategyAllocation': '策略分佈', + 'dashboard.drawdownCurve': '回撤曲線', + 'dashboard.hourlyDistribution': '交易時段', + // Chart labels + 'dashboard.dailyPnl': '日盈虧', + 'dashboard.cumulativePnl': '累計盈虧', + 'dashboard.tradeCount': '交易次數', + 'dashboard.profit': '盈虧', + 'dashboard.noData': '暫無數據', + 'dashboard.noStrategyData': '暫無策略數據', + // Ranking labels + 'dashboard.ranking.totalProfit': '總收益', + 'dashboard.ranking.roi': '收益率', + 'dashboard.ranking.trades': '交易數', + // Units and labels + 'dashboard.unit.trades': '筆', + 'dashboard.unit.strategies': '個', + 'dashboard.label.avgDaily': '日均', + 'dashboard.label.avgProfit': '平均盈利', + 'dashboard.label.win': '勝', + 'dashboard.label.lose': '負', + 'dashboard.label.trade': '交易', + 'dashboard.label.indicator': '指標', + 'dashboard.label.totalPnl': '總盈虧', + 'dashboard.label.maxDrawdownPoint': '最大回撤', + 'dashboard.profitCalendar': '收益日曆', 'dashboard.recentTrades': '最近交易', 'dashboard.currentPositions': '當前持倉', 'dashboard.table.time': '時間', @@ -878,6 +914,12 @@ const locale = { 'dashboard.orderTable.deleteConfirm': '確認刪除這條記錄?', 'dashboard.orderTable.deleteSuccess': '刪除成功', 'dashboard.orderTable.deleteFailed': '刪除失敗', + 'dashboard.newOrderNotify': '新訂單提醒', + 'dashboard.newOrderDesc': '有新的訂單執行記錄', + 'dashboard.soundEnabled': '聲音提醒已開啟', + 'dashboard.soundDisabled': '聲音提醒已關閉', + 'dashboard.clickToMute': '點擊關閉聲音提醒', + 'dashboard.clickToUnmute': '點擊開啟聲音提醒', 'dashboard.signalType.openLong': '開多', 'dashboard.signalType.openShort': '開空', 'dashboard.signalType.closeLong': '平多', @@ -1521,7 +1563,124 @@ const locale = { 'message': '-', 'layouts.usermenu.dialog.title': '信息', 'layouts.usermenu.dialog.content': '您確定要注銷嗎?', - 'layouts.userLayout.title': '於不確定中,尋見真理' + 'layouts.userLayout.title': '於不確定中,尋見真理', + + // Settings + 'settings.title': '系統設置', + 'settings.description': '配置應用設置、API密鑰和系統偏好', + 'settings.save': '保存設置', + 'settings.reset': '重置', + 'settings.saveSuccess': '設置保存成功', + 'settings.saveFailed': '保存設置失敗', + 'settings.loadFailed': '加載配置失敗', + 'settings.default': '默認', + 'settings.pleaseSelect': '請選擇', + 'settings.inputApiKey': '請輸入密鑰', + 'settings.getApi': '獲取API', + // Settings link texts + 'settings.link.getApi': '獲取API', + 'settings.link.getApiKey': '獲取API Key', + 'settings.link.getToken': '獲取Token', + 'settings.link.createBot': '創建Bot', + 'settings.link.viewModels': '查看模型列表', + 'settings.link.freeRegister': '免費註冊', + 'settings.link.supportedExchanges': '支持的交易所', + 'settings.link.applyApi': '申請API', + 'settings.link.createSearchEngine': '創建搜索引擎', + 'settings.restartRequired': '配置已保存,部分配置需要重啟Python服務才能生效', + 'settings.copyRestartCmd': '複製重啟命令', + 'settings.copySuccess': '複製成功', + 'settings.copyFailed': '複製失敗', + // Settings groups + 'settings.group.auth': '認證配置', + 'settings.group.server': '服務器配置', + 'settings.group.worker': '訂單處理配置', + 'settings.group.notification': '信號通知配置', + 'settings.group.smtp': '郵件SMTP配置', + 'settings.group.twilio': 'Twilio短信配置', + 'settings.group.strategy': '策略執行配置', + 'settings.group.proxy': '代理配置', + 'settings.group.app': '應用配置', + 'settings.group.ai': 'AI/LLM配置', + 'settings.group.market': '市場預設', + 'settings.group.data_source': '數據源配置', + 'settings.group.search': '搜索配置', + // Settings fields - Auth + 'settings.field.SECRET_KEY': 'Secret Key', + 'settings.field.ADMIN_USER': '管理員用戶名', + 'settings.field.ADMIN_PASSWORD': '管理員密碼', + // Settings fields - Server + 'settings.field.PYTHON_API_HOST': '監聽地址', + 'settings.field.PYTHON_API_PORT': '端口', + 'settings.field.PYTHON_API_DEBUG': '調試模式', + // Settings fields - Worker + 'settings.field.ENABLE_PENDING_ORDER_WORKER': '啟用訂單處理Worker', + 'settings.field.PENDING_ORDER_STALE_SEC': '訂單超時時間(秒)', + // Settings fields - Notification + 'settings.field.SIGNAL_WEBHOOK_URL': 'Webhook URL', + 'settings.field.SIGNAL_WEBHOOK_TOKEN': 'Webhook Token', + 'settings.field.SIGNAL_NOTIFY_TIMEOUT_SEC': '通知超時(秒)', + 'settings.field.TELEGRAM_BOT_TOKEN': 'Telegram Bot Token', + // Settings fields - SMTP + 'settings.field.SMTP_HOST': 'SMTP服務器', + 'settings.field.SMTP_PORT': 'SMTP端口', + 'settings.field.SMTP_USER': 'SMTP用戶名', + 'settings.field.SMTP_PASSWORD': 'SMTP密碼', + 'settings.field.SMTP_FROM': '發件人地址', + 'settings.field.SMTP_USE_TLS': '使用TLS', + 'settings.field.SMTP_USE_SSL': '使用SSL', + // Settings fields - Twilio + 'settings.field.TWILIO_ACCOUNT_SID': 'Account SID', + 'settings.field.TWILIO_AUTH_TOKEN': 'Auth Token', + 'settings.field.TWILIO_FROM_NUMBER': '發送號碼', + // Settings fields - Strategy + 'settings.field.DISABLE_RESTORE_RUNNING_STRATEGIES': '禁用自動恢復策略', + 'settings.field.STRATEGY_TICK_INTERVAL_SEC': '策略Tick間隔(秒)', + 'settings.field.PRICE_CACHE_TTL_SEC': '價格緩存TTL(秒)', + // Settings fields - Proxy + 'settings.field.PROXY_PORT': '代理端口', + 'settings.field.PROXY_HOST': '代理主機', + 'settings.field.PROXY_SCHEME': '代理協議', + 'settings.field.PROXY_URL': '完整代理URL', + // Settings fields - App + 'settings.field.CORS_ORIGINS': 'CORS來源', + 'settings.field.RATE_LIMIT': '速率限制(每分鐘)', + 'settings.field.ENABLE_CACHE': '啟用緩存', + 'settings.field.ENABLE_REQUEST_LOG': '啟用請求日誌', + 'settings.field.ENABLE_AI_ANALYSIS': '啟用AI分析', + // Settings fields - AI + 'settings.field.OPENROUTER_API_KEY': 'OpenRouter API Key', + 'settings.field.OPENROUTER_API_URL': 'OpenRouter API URL', + 'settings.field.OPENROUTER_MODEL': '默認模型', + 'settings.field.OPENROUTER_TEMPERATURE': 'Temperature', + 'settings.field.OPENROUTER_MAX_TOKENS': 'Max Tokens', + 'settings.field.OPENROUTER_TIMEOUT': '超時時間(秒)', + 'settings.field.OPENROUTER_CONNECT_TIMEOUT': '連接超時(秒)', + 'settings.field.AI_MODELS_JSON': '模型列表(JSON)', + // Settings fields - Market + 'settings.field.MARKET_TYPES_JSON': '市場類型(JSON)', + 'settings.field.TRADING_SUPPORTED_SYMBOLS_JSON': '支持的交易對(JSON)', + // Settings fields - Data Source + 'settings.field.DATA_SOURCE_TIMEOUT': '數據源超時(秒)', + 'settings.field.DATA_SOURCE_RETRY': '重試次數', + 'settings.field.DATA_SOURCE_RETRY_BACKOFF': '重試退避(秒)', + 'settings.field.FINNHUB_API_KEY': 'Finnhub API Key', + 'settings.field.FINNHUB_TIMEOUT': 'Finnhub超時(秒)', + 'settings.field.FINNHUB_RATE_LIMIT': 'Finnhub速率限制', + 'settings.field.CCXT_DEFAULT_EXCHANGE': 'CCXT默認交易所', + 'settings.field.CCXT_TIMEOUT': 'CCXT超時(ms)', + 'settings.field.CCXT_PROXY': 'CCXT代理', + 'settings.field.AKSHARE_TIMEOUT': 'Akshare超時(秒)', + 'settings.field.YFINANCE_TIMEOUT': 'YFinance超時(秒)', + 'settings.field.TIINGO_API_KEY': 'Tiingo API Key', + 'settings.field.TIINGO_TIMEOUT': 'Tiingo超時(秒)', + // Settings fields - Search + 'settings.field.SEARCH_PROVIDER': '搜索提供商', + 'settings.field.SEARCH_MAX_RESULTS': '最大結果數', + 'settings.field.SEARCH_GOOGLE_API_KEY': 'Google API Key', + 'settings.field.SEARCH_GOOGLE_CX': 'Google CX', + 'settings.field.SEARCH_BING_API_KEY': 'Bing API Key', + 'settings.field.INTERNAL_API_KEY': '內部API Key' } export default { diff --git a/quantdinger_vue/src/views/dashboard/index.vue b/quantdinger_vue/src/views/dashboard/index.vue index 6dd86ab..b81f02e 100644 --- a/quantdinger_vue/src/views/dashboard/index.vue +++ b/quantdinger_vue/src/views/dashboard/index.vue @@ -1,251 +1,505 @@ diff --git a/quantdinger_vue/src/views/settings/index.vue b/quantdinger_vue/src/views/settings/index.vue new file mode 100644 index 0000000..e11936b --- /dev/null +++ b/quantdinger_vue/src/views/settings/index.vue @@ -0,0 +1,621 @@ + + + + + diff --git a/quantdinger_vue/src/views/trading-assistant/components/PositionRecords.vue b/quantdinger_vue/src/views/trading-assistant/components/PositionRecords.vue index 6a95d46..10a6339 100644 --- a/quantdinger_vue/src/views/trading-assistant/components/PositionRecords.vue +++ b/quantdinger_vue/src/views/trading-assistant/components/PositionRecords.vue @@ -211,6 +211,11 @@ export default {