feat(i18n): add missing translations for trading-assistant modal

- Add step2Params and step3Signal translations
- Add execution mode related translations (signal/live)
- Add notification channel translations (browser/email/phone/telegram/discord/webhook)
- Add live trading config translations (savedCredential, credentialName, etc.)
- Add batch operation message translations (batchCreateSuccess, batchStart/Stop/Delete)
- Add placeholders for email, phone, telegram, discord, webhook inputs
- Add validation messages for email and notify channel
- Add strategy group translations (startAll, stopAll, deleteAll, symbolCount)
- Support both zh-CN (Simplified Chinese) and zh-TW (Traditional Chinese)
This commit is contained in:
TIANHE
2026-01-11 22:15:54 +08:00
parent 74138b4cb2
commit e7cb9c6493
8 changed files with 972 additions and 85 deletions
+165
View File
@@ -74,6 +74,171 @@ def create_strategy():
return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500
@strategy_bp.route('/strategies/batch-create', methods=['POST'])
def batch_create_strategies():
"""
批量创建策略(多币种)
请求体:
strategy_name: 策略基础名称
symbols: 币种数组,如 ["Crypto:BTC/USDT", "Crypto:ETH/USDT"]
... 其他策略配置
"""
try:
payload = request.get_json() or {}
payload['user_id'] = int(payload.get('user_id') or 1)
payload['strategy_type'] = payload.get('strategy_type') or 'IndicatorStrategy'
result = get_strategy_service().batch_create_strategies(payload)
if result['success']:
return jsonify({
'code': 1,
'msg': f"成功创建 {result['total_created']} 个策略",
'data': result
})
else:
return jsonify({
'code': 0,
'msg': '批量创建失败',
'data': result
})
except Exception as e:
logger.error(f"batch_create_strategies failed: {str(e)}")
logger.error(traceback.format_exc())
return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500
@strategy_bp.route('/strategies/batch-start', methods=['POST'])
def batch_start_strategies():
"""
批量启动策略
请求体:
strategy_ids: 策略ID数组
strategy_group_id: 策略组ID
"""
try:
payload = request.get_json() or {}
strategy_ids = payload.get('strategy_ids') or []
strategy_group_id = payload.get('strategy_group_id')
# 如果提供了策略组ID,获取组内所有策略
if strategy_group_id and not strategy_ids:
strategy_ids = get_strategy_service().get_strategies_by_group(strategy_group_id)
if not strategy_ids:
return jsonify({'code': 0, 'msg': '请提供策略ID', 'data': None}), 400
# 先更新数据库状态
result = get_strategy_service().batch_start_strategies(strategy_ids)
# 然后启动执行器
executor = get_trading_executor()
for sid in result.get('success_ids', []):
try:
executor.start_strategy(sid)
except Exception as e:
logger.error(f"Failed to start executor for strategy {sid}: {e}")
return jsonify({
'code': 1 if result['success'] else 0,
'msg': f"成功启动 {len(result.get('success_ids', []))} 个策略",
'data': result
})
except Exception as e:
logger.error(f"batch_start_strategies failed: {str(e)}")
logger.error(traceback.format_exc())
return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500
@strategy_bp.route('/strategies/batch-stop', methods=['POST'])
def batch_stop_strategies():
"""
批量停止策略
请求体:
strategy_ids: 策略ID数组
strategy_group_id: 策略组ID
"""
try:
payload = request.get_json() or {}
strategy_ids = payload.get('strategy_ids') or []
strategy_group_id = payload.get('strategy_group_id')
if strategy_group_id and not strategy_ids:
strategy_ids = get_strategy_service().get_strategies_by_group(strategy_group_id)
if not strategy_ids:
return jsonify({'code': 0, 'msg': '请提供策略ID', 'data': None}), 400
# 先停止执行器
executor = get_trading_executor()
for sid in strategy_ids:
try:
executor.stop_strategy(sid)
except Exception as e:
logger.error(f"Failed to stop executor for strategy {sid}: {e}")
# 然后更新数据库状态
result = get_strategy_service().batch_stop_strategies(strategy_ids)
return jsonify({
'code': 1 if result['success'] else 0,
'msg': f"成功停止 {len(result.get('success_ids', []))} 个策略",
'data': result
})
except Exception as e:
logger.error(f"batch_stop_strategies failed: {str(e)}")
logger.error(traceback.format_exc())
return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500
@strategy_bp.route('/strategies/batch-delete', methods=['DELETE'])
def batch_delete_strategies():
"""
批量删除策略
请求体:
strategy_ids: 策略ID数组
strategy_group_id: 策略组ID
"""
try:
payload = request.get_json() or {}
strategy_ids = payload.get('strategy_ids') or []
strategy_group_id = payload.get('strategy_group_id')
if strategy_group_id and not strategy_ids:
strategy_ids = get_strategy_service().get_strategies_by_group(strategy_group_id)
if not strategy_ids:
return jsonify({'code': 0, 'msg': '请提供策略ID', 'data': None}), 400
# 先停止执行器
executor = get_trading_executor()
for sid in strategy_ids:
try:
executor.stop_strategy(sid)
except Exception as e:
pass # 忽略停止错误
# 然后删除
result = get_strategy_service().batch_delete_strategies(strategy_ids)
return jsonify({
'code': 1 if result['success'] else 0,
'msg': f"成功删除 {len(result.get('success_ids', []))} 个策略",
'data': result
})
except Exception as e:
logger.error(f"batch_delete_strategies failed: {str(e)}")
logger.error(traceback.format_exc())
return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500
@strategy_bp.route('/strategies/update', methods=['PUT'])
def update_strategy():
try:
+153 -1
View File
@@ -2,6 +2,7 @@ import os
import time
import json
import threading
import uuid
from typing import List, Dict, Any, Optional
from datetime import datetime
@@ -534,6 +535,10 @@ class StrategyService:
trading_config = payload.get('trading_config') or {}
exchange_config = payload.get('exchange_config') or {}
# 策略组字段
strategy_group_id = payload.get('strategy_group_id') or ''
group_base_name = payload.get('group_base_name') or ''
# Denormalized fields for quick list rendering
symbol = (trading_config or {}).get('symbol')
timeframe = (trading_config or {}).get('timeframe')
@@ -549,8 +554,9 @@ class StrategyService:
(strategy_name, strategy_type, market_category, execution_mode, notification_config,
status, symbol, timeframe, initial_capital, leverage, market_type,
exchange_config, indicator_config, trading_config, ai_model_config, decide_interval,
strategy_group_id, group_base_name,
created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
name,
@@ -569,6 +575,8 @@ class StrategyService:
self._dump_json_or_encrypt(trading_config, encrypt=False),
self._dump_json_or_encrypt(payload.get('ai_model_config') or {}, encrypt=False),
int(payload.get('decide_interval') or 300),
strategy_group_id,
group_base_name,
now,
now
)
@@ -578,6 +586,150 @@ class StrategyService:
cur.close()
return int(new_id)
def batch_create_strategies(self, payload: Dict[str, Any]) -> Dict[str, Any]:
"""
批量创建策略多币种
Args:
payload: 包含 symbols数组和其他策略配置
Returns:
{
'success': True/False,
'strategy_group_id': '...',
'created_ids': [1, 2, 3],
'failed_symbols': []
}
"""
symbols = payload.get('symbols') or []
if not symbols or not isinstance(symbols, list):
raise ValueError("symbols array is required")
base_name = (payload.get('strategy_name') or '').strip()
if not base_name:
raise ValueError("strategy_name is required")
# 生成策略组ID
strategy_group_id = str(uuid.uuid4())[:8]
created_ids = []
failed_symbols = []
for symbol in symbols:
try:
# 为每个币种创建单独的策略
single_payload = dict(payload)
# 解析 symbol(可能是 "Market:SYMBOL" 格式)
if isinstance(symbol, str) and ':' in symbol:
parts = symbol.split(':', 1)
market_category = parts[0]
symbol_name = parts[1]
else:
market_category = payload.get('market_category') or 'Crypto'
symbol_name = symbol
# 策略名称加币种后缀
single_payload['strategy_name'] = f"{base_name}-{symbol_name}"
single_payload['strategy_group_id'] = strategy_group_id
single_payload['group_base_name'] = base_name
single_payload['market_category'] = market_category
# 更新 trading_config 中的 symbol
trading_config = dict(single_payload.get('trading_config') or {})
trading_config['symbol'] = symbol_name
single_payload['trading_config'] = trading_config
new_id = self.create_strategy(single_payload)
created_ids.append(new_id)
except Exception as e:
logger.error(f"Failed to create strategy for symbol {symbol}: {e}")
failed_symbols.append({'symbol': symbol, 'error': str(e)})
return {
'success': len(created_ids) > 0,
'strategy_group_id': strategy_group_id,
'group_base_name': base_name,
'created_ids': created_ids,
'failed_symbols': failed_symbols,
'total_created': len(created_ids),
'total_failed': len(failed_symbols)
}
def batch_start_strategies(self, strategy_ids: List[int]) -> Dict[str, Any]:
"""批量启动策略"""
success_ids = []
failed_ids = []
for sid in strategy_ids:
try:
self.update_strategy_status(sid, 'running')
success_ids.append(sid)
except Exception as e:
logger.error(f"Failed to start strategy {sid}: {e}")
failed_ids.append({'id': sid, 'error': str(e)})
return {
'success': len(success_ids) > 0,
'success_ids': success_ids,
'failed_ids': failed_ids
}
def batch_stop_strategies(self, strategy_ids: List[int]) -> Dict[str, Any]:
"""批量停止策略"""
success_ids = []
failed_ids = []
for sid in strategy_ids:
try:
self.update_strategy_status(sid, 'stopped')
success_ids.append(sid)
except Exception as e:
logger.error(f"Failed to stop strategy {sid}: {e}")
failed_ids.append({'id': sid, 'error': str(e)})
return {
'success': len(success_ids) > 0,
'success_ids': success_ids,
'failed_ids': failed_ids
}
def batch_delete_strategies(self, strategy_ids: List[int]) -> Dict[str, Any]:
"""批量删除策略"""
success_ids = []
failed_ids = []
for sid in strategy_ids:
try:
self.delete_strategy(sid)
success_ids.append(sid)
except Exception as e:
logger.error(f"Failed to delete strategy {sid}: {e}")
failed_ids.append({'id': sid, 'error': str(e)})
return {
'success': len(success_ids) > 0,
'success_ids': success_ids,
'failed_ids': failed_ids
}
def get_strategies_by_group(self, strategy_group_id: str) -> List[Dict[str, Any]]:
"""获取策略组内的所有策略"""
try:
with get_db_connection() as db:
cur = db.cursor()
cur.execute(
"SELECT id FROM qd_strategies_trading WHERE strategy_group_id = ?",
(strategy_group_id,)
)
rows = cur.fetchall() or []
cur.close()
return [row['id'] for row in rows]
except Exception as e:
logger.error(f"get_strategies_by_group failed: {e}")
return []
def update_strategy(self, strategy_id: int, payload: Dict[str, Any]) -> bool:
now = int(time.time())
existing = self.get_strategy(strategy_id)
+3 -1
View File
@@ -104,7 +104,9 @@ def _init_db_schema(conn):
ensure_columns("qd_strategies_trading", {
"market_category": "TEXT DEFAULT 'Crypto'",
"execution_mode": "TEXT DEFAULT 'signal'",
"notification_config": "TEXT DEFAULT ''"
"notification_config": "TEXT DEFAULT ''",
"strategy_group_id": "TEXT DEFAULT ''", # 策略组ID,批量创建的策略共享同一个组ID
"group_base_name": "TEXT DEFAULT ''" # 策略组基础名称(用于显示)
})
# 2. 持仓表