diff --git a/backend/api/routes/config.py b/backend/api/routes/config.py index 5287fe3..252af37 100644 --- a/backend/api/routes/config.py +++ b/backend/api/routes/config.py @@ -390,6 +390,12 @@ async def get_global_configs( "category": "scan", "description": "严格成交量过滤,24H Volume低于此值(USD)直接剔除", }, + "EXCLUDE_MAJOR_COINS": { + "value": True, + "type": "boolean", + "category": "scan", + "description": "是否排除主流币(BTC、ETH、BNB等),专注于山寨币。山寨币策略建议开启。", + }, "USE_TRAILING_STOP": { "value": False, "type": "boolean", diff --git a/backend/config_manager.py b/backend/config_manager.py index e8e92bd..9bc06df 100644 --- a/backend/config_manager.py +++ b/backend/config_manager.py @@ -745,7 +745,6 @@ class ConfigManager: # 涨跌幅阈值 'MIN_CHANGE_PERCENT': eff_get('MIN_CHANGE_PERCENT', 2.0), - 'TOP_N_SYMBOLS': eff_get('TOP_N_SYMBOLS', 10), # 风险控制 'STOP_LOSS_PERCENT': eff_get('STOP_LOSS_PERCENT', 0.10), # 默认10% @@ -767,8 +766,9 @@ class ConfigManager: # 市场扫描(1小时主周期) 'SCAN_INTERVAL': eff_get('SCAN_INTERVAL', 3600), # 1小时 - 'TOP_N_SYMBOLS': eff_get('TOP_N_SYMBOLS', 10), # 每次扫描后处理的交易对数量 - 'MAX_SCAN_SYMBOLS': eff_get('MAX_SCAN_SYMBOLS', 500), # 扫描的最大交易对数量(0表示扫描所有) + 'TOP_N_SYMBOLS': eff_get('TOP_N_SYMBOLS', 8), # 每次扫描后处理的交易对数量(增加到8,给更多选择余地) + 'MAX_SCAN_SYMBOLS': eff_get('MAX_SCAN_SYMBOLS', 250), # 扫描的最大交易对数量(增加到250,提升覆盖率到46%) + 'EXCLUDE_MAJOR_COINS': eff_get('EXCLUDE_MAJOR_COINS', True), # 是否排除主流币(BTC、ETH、BNB等),专注于山寨币 'KLINE_INTERVAL': eff_get('KLINE_INTERVAL', '1h'), 'PRIMARY_INTERVAL': eff_get('PRIMARY_INTERVAL', '1h'), 'CONFIRM_INTERVAL': eff_get('CONFIRM_INTERVAL', '4h'), diff --git a/frontend/src/components/ConfigPanel.jsx b/frontend/src/components/ConfigPanel.jsx index dd88c44..4f47fd7 100644 --- a/frontend/src/components/ConfigPanel.jsx +++ b/frontend/src/components/ConfigPanel.jsx @@ -82,9 +82,9 @@ const ConfigPanel = () => { USE_FIXED_RISK_SIZING: true, FIXED_RISK_PERCENT: 1.0, USE_TRAILING_STOP: true, TRAILING_STOP_ACTIVATION: 30.0, TRAILING_STOP_PROTECT: 15.0, MAX_POSITION_PERCENT: 1.5, MAX_TOTAL_POSITION_PERCENT: 12.0, MAX_DAILY_ENTRIES: 5, - MIN_VOLUME_24H: 30000000, MIN_VOLATILITY: 3.0, TOP_N_SYMBOLS: 5, MIN_SIGNAL_STRENGTH: 5, - SCAN_INTERVAL: 3600, SMART_ENTRY_ENABLED: true, AUTO_TRADE_ONLY_TRENDING: true, - AUTO_TRADE_ALLOW_4H_NEUTRAL: true, + MIN_VOLUME_24H: 30000000, MIN_VOLATILITY: 3.0, TOP_N_SYMBOLS: 8, MIN_SIGNAL_STRENGTH: 5, + MAX_SCAN_SYMBOLS: 250, SCAN_INTERVAL: 3600, SMART_ENTRY_ENABLED: true, AUTO_TRADE_ONLY_TRENDING: true, + AUTO_TRADE_ALLOW_4H_NEUTRAL: true, EXCLUDE_MAJOR_COINS: true, }, }, swing: { diff --git a/frontend/src/components/GlobalConfig.jsx b/frontend/src/components/GlobalConfig.jsx index 13fae57..3df2432 100644 --- a/frontend/src/components/GlobalConfig.jsx +++ b/frontend/src/components/GlobalConfig.jsx @@ -358,9 +358,10 @@ const GlobalConfig = () => { MIN_VOLUME_24H: 30000000, // 24H成交额≥3000万美元 MIN_VOLUME_24H_STRICT: 50000000, // 严格过滤≥5000万 MIN_VOLATILITY: 3.0, // 最小波动率3% - TOP_N_SYMBOLS: 5, // 只做最强5个 - MAX_SCAN_SYMBOLS: 150, // 扫描前150个 + TOP_N_SYMBOLS: 8, // 选择信号最强的8个(给更多选择余地,避免错过好机会) + MAX_SCAN_SYMBOLS: 250, // 扫描前250个(增加覆盖率,从27.6%提升到46.0%) MIN_SIGNAL_STRENGTH: 5, // 信号强度≥5(MACD金叉/死叉已足够,配合其他筛选) + EXCLUDE_MAJOR_COINS: true, // 排除主流币(BTC、ETH、BNB等),专注于山寨币 // 时间框架 PRIMARY_INTERVAL: '4h', // 主周期4小时 diff --git a/trading_system/binance_client.py b/trading_system/binance_client.py index 67b0460..d392bbc 100644 --- a/trading_system/binance_client.py +++ b/trading_system/binance_client.py @@ -565,9 +565,9 @@ class BinanceClient: 'ts': now_ms } - # 写入 Redis 缓存(TTL: 30秒) - await self.redis_cache.set(cache_key, result, ttl=30) - logger.debug(f"批量获取到 {len(result)} 个交易对的24小时行情数据,已缓存") + # 写入 Redis 缓存(TTL: 60秒,多个账户可以共用) + await self.redis_cache.set(cache_key, result, ttl=60) + logger.debug(f"批量获取到 {len(result)} 个交易对的24小时行情数据,已缓存 (TTL: 60秒)") return result except BinanceAPIException as e: error_code = e.code if hasattr(e, 'code') else None diff --git a/trading_system/config.py b/trading_system/config.py index c707bd3..c2ba97f 100644 --- a/trading_system/config.py +++ b/trading_system/config.py @@ -213,8 +213,9 @@ def _get_trading_config(): 'MIN_POSITION_PERCENT': 0.01, # 最小仓位1% 'MIN_MARGIN_USDT': 5.0, # 最小保证金5美元 'MIN_CHANGE_PERCENT': 0.5, # 最小价格变动0.5% - 'TOP_N_SYMBOLS': 5, # 只做信号最强的5个,专注优质机会 - 'MAX_SCAN_SYMBOLS': 150, # 扫描前150个,覆盖主流山寨 + 'TOP_N_SYMBOLS': 8, # 选择信号最强的8个(给更多选择余地,避免错过好机会) + 'MAX_SCAN_SYMBOLS': 250, # 扫描前250个(增加覆盖率,从27.6%提升到46.0%) + 'EXCLUDE_MAJOR_COINS': True, # 排除主流币(BTC、ETH、BNB等),专注于山寨币 'STOP_LOSS_PERCENT': 0.15, # 止损15%(山寨币波动大,止损要宽) 'TAKE_PROFIT_PERCENT': 0.60, # 止盈60%(4:1盈亏比,追求大赢家) 'MIN_STOP_LOSS_PRICE_PCT': 0.02, # 最小止损价格变动2% diff --git a/trading_system/market_scanner.py b/trading_system/market_scanner.py index 852a380..88820b2 100644 --- a/trading_system/market_scanner.py +++ b/trading_system/market_scanner.py @@ -71,6 +71,22 @@ class MarketScanner: symbols = all_symbols logger.info(f"扫描所有 {len(symbols)} 个USDT交易对") + # 过滤主流币(山寨币策略应该排除主流币) + exclude_major_coins = cfg.get('EXCLUDE_MAJOR_COINS', True) + if exclude_major_coins: + # 主流币列表(市值排名前15的主流币) + major_coins = { + 'BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT', + 'ADAUSDT', 'DOGEUSDT', 'DOTUSDT', 'AVAXUSDT', 'MATICUSDT', + 'LINKUSDT', 'UNIUSDT', 'ATOMUSDT', 'ETCUSDT', 'LTCUSDT', + 'NEARUSDT', 'APTUSDT', 'ARBUSDT', 'OPUSDT', 'SUIUSDT' + } + symbols_before = len(symbols) + symbols = [s for s in symbols if s not in major_coins] + excluded_count = symbols_before - len(symbols) + if excluded_count > 0: + logger.info(f"排除主流币 {excluded_count} 个,剩余 {len(symbols)} 个交易对(专注于山寨币)") + # 先批量获取所有交易对的24小时行情数据(减少API请求) logger.info(f"批量获取 {len(symbols)} 个交易对的24小时行情数据...") all_tickers = await self.client.get_all_tickers_24h() @@ -247,20 +263,66 @@ class MarketScanner: change_percent = ((current_price - prev_price) / prev_price) * 100 - # 计算技术指标 - rsi = TechnicalIndicators.calculate_rsi(close_prices, period=14) - macd = TechnicalIndicators.calculate_macd(close_prices) - bollinger = TechnicalIndicators.calculate_bollinger_bands(close_prices, period=20) - atr_period = int(config.TRADING_CONFIG.get('ATR_PERIOD', 14) or 14) - atr = TechnicalIndicators.calculate_atr(high_prices, low_prices, close_prices, period=atr_period) - ema20 = TechnicalIndicators.calculate_ema(close_prices, period=20) - ema50 = TechnicalIndicators.calculate_ema(close_prices, period=50) + # ⚠️ 优化:检查技术指标计算结果缓存(基于K线数据的最后更新时间) + # 如果K线数据没有更新,可以直接使用缓存的技术指标 + primary_interval = config.TRADING_CONFIG.get('PRIMARY_INTERVAL', '1h') + confirm_interval = config.TRADING_CONFIG.get('CONFIRM_INTERVAL', '4h') + cache_key_indicators = f"indicators:{symbol}:{primary_interval}:{confirm_interval}" + last_kline_time = int(klines[-1][0]) if klines else 0 # 最后一根K线的时间戳 - # 计算4H周期的EMA20用于多周期共振检查 - ema20_4h = TechnicalIndicators.calculate_ema(close_prices_4h, period=20) if len(close_prices_4h) >= 20 else None + # 尝试从缓存获取技术指标计算结果 + cached_indicators = None + try: + cached_indicators = await self.client.redis_cache.get(cache_key_indicators) + except Exception: + pass - # 判断市场状态 - market_regime = TechnicalIndicators.detect_market_regime(close_prices) + use_cached_indicators = False + if cached_indicators and cached_indicators.get('last_kline_time') == last_kline_time: + # 缓存命中,使用缓存的技术指标 + use_cached_indicators = True + logger.debug(f"{symbol} 使用缓存的技术指标计算结果") + rsi = cached_indicators.get('rsi') + macd = cached_indicators.get('macd') + bollinger = cached_indicators.get('bollinger') + atr = cached_indicators.get('atr') + ema20 = cached_indicators.get('ema20') + ema50 = cached_indicators.get('ema50') + ema20_4h = cached_indicators.get('ema20_4h') + market_regime = cached_indicators.get('marketRegime') + else: + # 缓存未命中,重新计算技术指标 + rsi = TechnicalIndicators.calculate_rsi(close_prices, period=14) + macd = TechnicalIndicators.calculate_macd(close_prices) + bollinger = TechnicalIndicators.calculate_bollinger_bands(close_prices, period=20) + atr_period = int(config.TRADING_CONFIG.get('ATR_PERIOD', 14) or 14) + atr = TechnicalIndicators.calculate_atr(high_prices, low_prices, close_prices, period=atr_period) + ema20 = TechnicalIndicators.calculate_ema(close_prices, period=20) + ema50 = TechnicalIndicators.calculate_ema(close_prices, period=50) + + # 计算4H周期的EMA20用于多周期共振检查 + ema20_4h = TechnicalIndicators.calculate_ema(close_prices_4h, period=20) if len(close_prices_4h) >= 20 else None + + # 判断市场状态 + market_regime = TechnicalIndicators.detect_market_regime(close_prices) + + # 保存技术指标计算结果到缓存(TTL: 30秒,与K线缓存一致) + try: + indicators_cache = { + 'last_kline_time': last_kline_time, + 'rsi': rsi, + 'macd': macd, + 'bollinger': bollinger, + 'atr': atr, + 'ema20': ema20, + 'ema50': ema50, + 'ema20_4h': ema20_4h, + 'marketRegime': market_regime, + } + await self.client.redis_cache.set(cache_key_indicators, indicators_cache, ttl=30) + logger.debug(f"{symbol} 技术指标计算结果已缓存 (TTL: 30秒)") + except Exception as e: + logger.debug(f"{symbol} 缓存技术指标计算结果失败: {e}") # 计算交易信号得分(用于排序) signal_score = 0