diff --git a/binance_client.py b/binance_client.py index 7244fbb..8b8b0d5 100644 --- a/binance_client.py +++ b/binance_client.py @@ -6,6 +6,7 @@ import logging from typing import Dict, List, Optional, Any from binance import AsyncClient, BinanceSocketManager from binance.exceptions import BinanceAPIException +from unicorn_websocket import UnicornWebSocketManager import config logger = logging.getLogger(__name__) @@ -28,6 +29,8 @@ class BinanceClient: self.testnet = testnet or config.USE_TESTNET self.client: Optional[AsyncClient] = None self.socket_manager: Optional[BinanceSocketManager] = None + self.unicorn_manager: Optional[UnicornWebSocketManager] = None + self.use_unicorn = config.TRADING_CONFIG.get('USE_UNICORN_WEBSOCKET', True) async def connect(self, timeout: int = None, retries: int = None): """ @@ -65,6 +68,21 @@ class BinanceClient: self.socket_manager = BinanceSocketManager(self.client) logger.info(f"✓ 币安客户端连接成功 (测试网: {self.testnet})") + # 启动Unicorn WebSocket(如果启用) + if self.use_unicorn: + try: + self.unicorn_manager = UnicornWebSocketManager(testnet=self.testnet) + if self.unicorn_manager.start(): + logger.info("✓ Unicorn WebSocket管理器启动成功") + # 启动异步数据处理 + asyncio.create_task(self.unicorn_manager.process_stream_data()) + else: + logger.warning("Unicorn WebSocket管理器启动失败,将使用标准WebSocket") + self.unicorn_manager = None + except Exception as e: + logger.warning(f"启动Unicorn WebSocket失败: {e},将使用标准WebSocket") + self.unicorn_manager = None + # 验证API密钥权限 await self._verify_api_permissions() @@ -118,6 +136,11 @@ class BinanceClient: async def disconnect(self): """断开连接""" + # 停止Unicorn WebSocket + if self.unicorn_manager: + self.unicorn_manager.stop() + self.unicorn_manager = None + if self.client: await self.client.close_connection() logger.info("币安客户端已断开连接") @@ -348,3 +371,40 @@ class BinanceClient: except BinanceAPIException as e: logger.error(f"设置杠杆失败: {e}") return False + + def subscribe_realtime_prices(self, symbols: List[str], callback) -> bool: + """ + 订阅实时价格流(使用Unicorn) + + Args: + symbols: 交易对列表 + callback: 价格更新回调函数 callback(symbol, price, price_data) + + Returns: + 是否成功 + """ + if not self.unicorn_manager: + logger.warning("Unicorn WebSocket未启用,无法订阅实时价格") + return False + + try: + self.unicorn_manager.subscribe_ticker(symbols, callback) + logger.info(f"订阅 {len(symbols)} 个交易对的实时价格流") + return True + except Exception as e: + logger.error(f"订阅实时价格流失败: {e}") + return False + + def get_realtime_price(self, symbol: str) -> Optional[float]: + """ + 获取实时价格(从Unicorn WebSocket) + + Args: + symbol: 交易对 + + Returns: + 实时价格,如果未订阅则返回None + """ + if self.unicorn_manager: + return self.unicorn_manager.get_realtime_price(symbol) + return None \ No newline at end of file diff --git a/config.py b/config.py index f827847..963a6ca 100644 --- a/config.py +++ b/config.py @@ -33,6 +33,16 @@ TRADING_CONFIG = { # 过滤条件 'MIN_VOLUME_24H': 10000000, # 最小24小时成交量:1000万USDT 'MIN_VOLATILITY': 0.02, # 最小波动率:2% + + # 高胜率策略参数 + 'MIN_SIGNAL_STRENGTH': 5, # 最小信号强度(0-10),越高越严格,胜率越高 + 'LEVERAGE': 10, # 杠杆倍数 + 'USE_TRAILING_STOP': True, # 是否使用移动止损 + 'TRAILING_STOP_ACTIVATION': 0.01, # 移动止损激活阈值(盈利1%后激活) + 'TRAILING_STOP_PROTECT': 0.01, # 移动止损保护利润(保护1%利润) + + # Unicorn WebSocket配置 + 'USE_UNICORN_WEBSOCKET': True, # 是否使用Unicorn WebSocket(高性能实时数据流) } # 连接配置 diff --git a/market_scanner.py b/market_scanner.py index f758344..0662189 100644 --- a/market_scanner.py +++ b/market_scanner.py @@ -1,10 +1,11 @@ """ -市场扫描器 - 发现涨跌幅最大的前N个货币对 +市场扫描器 - 发现涨跌幅最大的前N个货币对,并分析技术指标 """ import asyncio import logging from typing import List, Dict, Optional from binance_client import BinanceClient +from indicators import TechnicalIndicators import config logger = logging.getLogger(__name__) @@ -55,10 +56,14 @@ class MarketScanner: and r.get('volume24h', 0) >= config.TRADING_CONFIG['MIN_VOLUME_24H'] ] - # 按涨跌幅绝对值排序,取前N个 + # 按信号得分和涨跌幅综合排序,取前N个 + # 优先考虑技术指标信号得分高的 sorted_results = sorted( filtered_results, - key=lambda x: abs(x['changePercent']), + key=lambda x: ( + x.get('signalScore', 0) * 10, # 信号得分权重更高 + abs(x['changePercent']) # 其次考虑涨跌幅 + ), reverse=True ) @@ -67,26 +72,30 @@ class MarketScanner: self.top_symbols = top_n logger.info(f"扫描完成,找到 {len(top_n)} 个符合条件的交易对") - # 打印结果 + # 打印结果(包含技术指标) for i, symbol_info in enumerate(top_n, 1): + rsi_str = f"RSI:{symbol_info.get('rsi', 0):.1f}" if symbol_info.get('rsi') else "RSI:N/A" + regime_str = symbol_info.get('marketRegime', 'unknown') + score_str = f"信号:{symbol_info.get('signalScore', 0)}" + logger.info( f"{i}. {symbol_info['symbol']}: " - f"{symbol_info['changePercent']:.2f}% " - f"(价格: {symbol_info['price']:.4f}, " - f"成交量: {symbol_info.get('volume24h', 0):.0f})" + f"{symbol_info['changePercent']:.2f}% | " + f"{rsi_str} | {regime_str} | {score_str} | " + f"价格: {symbol_info['price']:.4f}" ) return top_n async def _get_symbol_change(self, symbol: str) -> Optional[Dict]: """ - 获取单个交易对的涨跌幅 + 获取单个交易对的涨跌幅和技术指标 Args: symbol: 交易对 Returns: - 包含涨跌幅信息的字典 + 包含涨跌幅和技术指标信息的字典 """ try: # 获取24小时行情数据 @@ -94,35 +103,95 @@ class MarketScanner: if not ticker: return None - # 获取5分钟K线数据计算精确涨跌幅 - klines = await self.client.get_klines( + # 获取更多K线数据用于技术指标计算 + klines_5m = await self.client.get_klines( symbol=symbol, - interval=config.TRADING_CONFIG['KLINE_INTERVAL'], - limit=2 + interval='5m', + limit=50 # 获取更多数据用于计算指标 ) - if len(klines) < 2: + if len(klines_5m) < 2: return None + # 提取价格数据 + close_prices = [float(k[4]) for k in klines_5m] # 收盘价 + high_prices = [float(k[2]) for k in klines_5m] # 最高价 + low_prices = [float(k[3]) for k in klines_5m] # 最低价 + # 计算5分钟涨跌幅 - current_price = float(klines[-1][4]) # 最新收盘价 - prev_price = float(klines[-2][4]) # 5分钟前收盘价 + current_price = close_prices[-1] + prev_price = close_prices[-2] if len(close_prices) >= 2 else close_prices[0] if prev_price == 0: return None 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 = TechnicalIndicators.calculate_atr(high_prices, low_prices, close_prices, period=14) + ema20 = TechnicalIndicators.calculate_ema(close_prices, period=20) + ema50 = TechnicalIndicators.calculate_ema(close_prices, period=50) + + # 判断市场状态 + market_regime = TechnicalIndicators.detect_market_regime(close_prices) + + # 计算交易信号得分(用于排序) + signal_score = 0 + + # RSI信号(均值回归) + if rsi is not None: + if rsi < 30: # 超卖,做多信号 + signal_score += 3 + elif rsi > 70: # 超买,做空信号 + signal_score += 3 + elif 30 <= rsi <= 70: # 中性区域 + signal_score += 1 + + # MACD信号 + if macd and macd['histogram'] is not None: + if macd['histogram'] > 0 and macd['macd'] > macd['signal']: # 看涨 + signal_score += 2 + elif macd['histogram'] < 0 and macd['macd'] < macd['signal']: # 看跌 + signal_score += 2 + + # 布林带信号(均值回归) + if bollinger: + if current_price <= bollinger['lower']: # 触及下轨,做多 + signal_score += 3 + elif current_price >= bollinger['upper']: # 触及上轨,做空 + signal_score += 3 + elif bollinger['lower'] < current_price < bollinger['upper']: + signal_score += 1 + + # 均线信号 + if ema20 and ema50: + if current_price > ema20 > ema50: # 上升趋势 + signal_score += 1 + elif current_price < ema20 < ema50: # 下降趋势 + signal_score += 1 + return { 'symbol': symbol, 'price': current_price, 'prevPrice': prev_price, 'changePercent': change_percent, 'volume24h': ticker.get('volume', 0), - 'direction': 'UP' if change_percent > 0 else 'DOWN' + 'direction': 'UP' if change_percent > 0 else 'DOWN', + 'rsi': rsi, + 'macd': macd, + 'bollinger': bollinger, + 'atr': atr, + 'ema20': ema20, + 'ema50': ema50, + 'marketRegime': market_regime, + 'signalScore': signal_score, + 'klines': klines_5m[-10:] # 保留最近10根K线 } except Exception as e: - logger.debug(f"获取 {symbol} 涨跌幅失败: {e}") + logger.debug(f"获取 {symbol} 数据失败: {e}") return None def get_top_symbols(self) -> List[Dict]: @@ -137,16 +206,43 @@ class MarketScanner: async def monitor_price(self, symbol: str, callback) -> None: """ 监控单个交易对的价格变化(WebSocket) + 优先使用Unicorn,如果不可用则使用标准WebSocket Args: symbol: 交易对 callback: 价格变化回调函数 """ + # 优先使用Unicorn WebSocket + if self.client.unicorn_manager: + try: + # 订阅实时价格 + self.client.subscribe_realtime_prices([symbol], callback) + logger.info(f"使用Unicorn监控 {symbol} 价格") + return + except Exception as e: + logger.warning(f"Unicorn监控失败,使用标准WebSocket: {e}") + + # 回退到标准WebSocket try: - async with self.client.socket_manager.futures_socket(symbol.lower()) as stream: - async for msg in stream: - if 'data' in msg: - price = float(msg['data']['c']) # 最新价格 - await callback(symbol, price) + if self.client.socket_manager: + async with self.client.socket_manager.futures_socket(symbol.lower()) as stream: + async for msg in stream: + if 'data' in msg: + price = float(msg['data']['c']) # 最新价格 + await callback(symbol, price) except Exception as e: logger.error(f"监控 {symbol} 价格失败: {e}") + + def get_realtime_price(self, symbol: str) -> Optional[float]: + """ + 获取实时价格(从Unicorn WebSocket) + + Args: + symbol: 交易对 + + Returns: + 实时价格 + """ + if self.client: + return self.client.get_realtime_price(symbol) + return None \ No newline at end of file diff --git a/position_manager.py b/position_manager.py index b06050b..21349b0 100644 --- a/position_manager.py +++ b/position_manager.py @@ -29,7 +29,10 @@ class PositionManager: self, symbol: str, change_percent: float, - leverage: int = 10 + leverage: int = 10, + trade_direction: Optional[str] = None, + entry_reason: str = '', + atr: Optional[float] = None ) -> Optional[Dict]: """ 开仓 @@ -59,8 +62,11 @@ class PositionManager: logger.warning(f"{symbol} 仓位计算失败,跳过交易") return None - # 确定交易方向 - side = 'BUY' if change_percent > 0 else 'SELL' + # 确定交易方向(优先使用技术指标信号) + if trade_direction: + side = trade_direction + else: + side = 'BUY' if change_percent > 0 else 'SELL' # 获取当前价格 ticker = await self.client.get_ticker_24h(symbol) @@ -69,6 +75,25 @@ class PositionManager: entry_price = ticker['price'] + # 计算动态止损止盈(使用ATR或固定比例) + if atr and atr > 0: + # 使用ATR计算动态止损(2倍ATR) + atr_stop_loss_pct = (atr * 2) / entry_price + # 限制在合理范围内(1%-5%) + atr_stop_loss_pct = max(0.01, min(0.05, atr_stop_loss_pct)) + stop_loss_price = self.risk_manager.get_stop_loss_price( + entry_price, side, stop_loss_pct=atr_stop_loss_pct + ) + # 止盈为止损的1.5-2倍 + take_profit_pct = atr_stop_loss_pct * 1.8 + take_profit_price = self.risk_manager.get_take_profit_price( + entry_price, side, take_profit_pct=take_profit_pct + ) + else: + # 使用固定止损止盈 + stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side) + take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side) + # 下单 order = await self.client.place_order( symbol=symbol, @@ -78,7 +103,7 @@ class PositionManager: ) if order: - # 记录持仓信息 + # 记录持仓信息(包含动态止损止盈) position_info = { 'symbol': symbol, 'side': side, @@ -86,9 +111,14 @@ class PositionManager: 'entryPrice': entry_price, 'changePercent': change_percent, 'orderId': order.get('orderId'), - 'stopLoss': self.risk_manager.get_stop_loss_price(entry_price, side), - 'takeProfit': self.risk_manager.get_take_profit_price(entry_price, side), - 'leverage': leverage + 'stopLoss': stop_loss_price, + 'takeProfit': take_profit_price, + 'initialStopLoss': stop_loss_price, # 初始止损(用于移动止损) + 'leverage': leverage, + 'entryReason': entry_reason, + 'atr': atr, + 'maxProfit': 0.0, # 记录最大盈利(用于移动止损) + 'trailingStopActivated': False # 移动止损是否已激活 } self.active_positions[symbol] = position_info @@ -193,16 +223,62 @@ class PositionManager: else: pnl_percent = ((entry_price - current_price) / entry_price) * 100 - # 检查止损 + # 更新最大盈利 + if pnl_percent > position_info.get('maxProfit', 0): + position_info['maxProfit'] = pnl_percent + + # 移动止损逻辑(盈利后保护利润) + use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', True) + if use_trailing: + trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) + trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) + + if not position_info.get('trailingStopActivated', False): + # 盈利超过阈值后,激活移动止损 + if pnl_percent > trailing_activation * 100: + position_info['trailingStopActivated'] = True + # 将止损移至成本价(保本) + position_info['stopLoss'] = entry_price + logger.info( + f"{symbol} 移动止损激活: 止损移至成本价 {entry_price:.4f} " + f"(盈利: {pnl_percent:.2f}%)" + ) + else: + # 盈利超过2%后,止损移至保护利润位 + if pnl_percent > 2.0: + if position_info['side'] == 'BUY': + new_stop_loss = entry_price * (1 + trailing_protect) + if new_stop_loss > position_info['stopLoss']: + position_info['stopLoss'] = new_stop_loss + logger.info( + f"{symbol} 移动止损更新: {new_stop_loss:.4f} " + f"(保护{trailing_protect*100:.1f}%利润)" + ) + else: + new_stop_loss = entry_price * (1 - trailing_protect) + if new_stop_loss < position_info['stopLoss']: + position_info['stopLoss'] = new_stop_loss + logger.info( + f"{symbol} 移动止损更新: {new_stop_loss:.4f} " + f"(保护{trailing_protect*100:.1f}%利润)" + ) + + # 检查止损(使用更新后的止损价) stop_loss = position_info['stopLoss'] if position_info['side'] == 'BUY' and current_price <= stop_loss: - logger.warning(f"{symbol} 触发止损: {current_price:.4f} <= {stop_loss:.4f}") + logger.warning( + f"{symbol} 触发止损: {current_price:.4f} <= {stop_loss:.4f} " + f"(盈亏: {pnl_percent:.2f}%)" + ) if await self.close_position(symbol): closed_positions.append(symbol) continue if position_info['side'] == 'SELL' and current_price >= stop_loss: - logger.warning(f"{symbol} 触发止损: {current_price:.4f} >= {stop_loss:.4f}") + logger.warning( + f"{symbol} 触发止损: {current_price:.4f} >= {stop_loss:.4f} " + f"(盈亏: {pnl_percent:.2f}%)" + ) if await self.close_position(symbol): closed_positions.append(symbol) continue @@ -210,13 +286,19 @@ class PositionManager: # 检查止盈 take_profit = position_info['takeProfit'] if position_info['side'] == 'BUY' and current_price >= take_profit: - logger.info(f"{symbol} 触发止盈: {current_price:.4f} >= {take_profit:.4f}") + logger.info( + f"{symbol} 触发止盈: {current_price:.4f} >= {take_profit:.4f} " + f"(盈亏: {pnl_percent:.2f}%)" + ) if await self.close_position(symbol): closed_positions.append(symbol) continue if position_info['side'] == 'SELL' and current_price <= take_profit: - logger.info(f"{symbol} 触发止盈: {current_price:.4f} <= {take_profit:.4f}") + logger.info( + f"{symbol} 触发止盈: {current_price:.4f} <= {take_profit:.4f} " + f"(盈亏: {pnl_percent:.2f}%)" + ) if await self.close_position(symbol): closed_positions.append(symbol) continue diff --git a/requirements.txt b/requirements.txt index bf26138..3104bbf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ python-binance==1.0.19 websocket-client==1.6.1 aiohttp==3.9.1 +unicorn-binance-websocket-api==2.4.0 \ No newline at end of file diff --git a/risk_manager.py b/risk_manager.py index abd8f46..cb6394c 100644 --- a/risk_manager.py +++ b/risk_manager.py @@ -218,36 +218,48 @@ class RiskManager: return True - def get_stop_loss_price(self, entry_price: float, side: str) -> float: + def get_stop_loss_price( + self, + entry_price: float, + side: str, + stop_loss_pct: Optional[float] = None + ) -> float: """ 计算止损价格 Args: entry_price: 入场价格 side: 方向 'BUY' 或 'SELL' + stop_loss_pct: 止损百分比,如果为None则使用配置值 Returns: 止损价格 """ - stop_loss_percent = self.config['STOP_LOSS_PERCENT'] + stop_loss_percent = stop_loss_pct or self.config['STOP_LOSS_PERCENT'] if side == 'BUY': # 做多,止损价低于入场价 return entry_price * (1 - stop_loss_percent) else: # 做空,止损价高于入场价 return entry_price * (1 + stop_loss_percent) - def get_take_profit_price(self, entry_price: float, side: str) -> float: + def get_take_profit_price( + self, + entry_price: float, + side: str, + take_profit_pct: Optional[float] = None + ) -> float: """ 计算止盈价格 Args: entry_price: 入场价格 side: 方向 'BUY' 或 'SELL' + take_profit_pct: 止盈百分比,如果为None则使用配置值 Returns: 止盈价格 """ - take_profit_percent = self.config['TAKE_PROFIT_PERCENT'] + take_profit_percent = take_profit_pct or self.config['TAKE_PROFIT_PERCENT'] if side == 'BUY': # 做多,止盈价高于入场价 return entry_price * (1 + take_profit_percent) diff --git a/strategy.py b/strategy.py index 9a7330e..c4b9a3b 100644 --- a/strategy.py +++ b/strategy.py @@ -55,7 +55,7 @@ class TradingStrategy: await asyncio.sleep(config.TRADING_CONFIG['SCAN_INTERVAL']) continue - # 2. 对每个交易对执行交易逻辑 + # 2. 对每个交易对执行交易逻辑(使用技术指标) for symbol_info in top_symbols: if not self.running: break @@ -63,10 +63,11 @@ class TradingStrategy: symbol = symbol_info['symbol'] change_percent = symbol_info['changePercent'] direction = symbol_info['direction'] + market_regime = symbol_info.get('marketRegime', 'unknown') logger.info( f"处理交易对: {symbol} " - f"({direction} {change_percent:.2f}%)" + f"({direction} {change_percent:.2f}%, 市场状态: {market_regime})" ) # 检查是否应该交易 @@ -78,20 +79,37 @@ class TradingStrategy: logger.info(f"{symbol} 成交量确认失败,跳过") continue - # 优化:趋势确认(可选) - if not await self._check_trend_confirmation(symbol, change_percent): - logger.info(f"{symbol} 趋势确认失败,跳过") + # 使用技术指标判断交易信号(高胜率策略) + trade_signal = await self._analyze_trade_signal(symbol_info) + + if not trade_signal['should_trade']: + logger.info( + f"{symbol} 技术指标分析: {trade_signal['reason']}, 跳过" + ) continue - # 开仓 + # 确定交易方向(基于技术指标) + trade_direction = trade_signal['direction'] + entry_reason = trade_signal['reason'] + + logger.info( + f"{symbol} 交易信号: {trade_direction} | " + f"原因: {entry_reason} | " + f"信号强度: {trade_signal.get('strength', 0)}/10" + ) + + # 开仓(使用改进的仓位管理) position = await self.position_manager.open_position( symbol=symbol, change_percent=change_percent, - leverage=10 # 默认10倍杠杆 + leverage=config.TRADING_CONFIG.get('LEVERAGE', 10), + trade_direction=trade_direction, + entry_reason=entry_reason, + atr=symbol_info.get('atr') ) if position: - logger.info(f"{symbol} 开仓成功") + logger.info(f"{symbol} 开仓成功: {trade_direction} ({entry_reason})") else: logger.warning(f"{symbol} 开仓失败") @@ -138,54 +156,130 @@ class TradingStrategy: return True - async def _check_trend_confirmation( - self, - symbol: str, - change_percent: float - ) -> bool: + async def _analyze_trade_signal(self, symbol_info: Dict) -> Dict: """ - 趋势确认 - 结合多时间周期确认趋势 + 使用技术指标分析交易信号(高胜率策略) Args: - symbol: 交易对 - change_percent: 5分钟涨跌幅 + symbol_info: 交易对信息(包含技术指标) Returns: - 是否通过确认 + 交易信号字典 {'should_trade': bool, 'direction': str, 'reason': str, 'strength': int} """ - try: - # 获取15分钟K线数据,确认更大周期的趋势 - klines_15m = await self.client.get_klines( - symbol=symbol, - interval='15m', - limit=2 - ) + symbol = symbol_info['symbol'] + current_price = symbol_info['price'] + rsi = symbol_info.get('rsi') + macd = symbol_info.get('macd') + bollinger = symbol_info.get('bollinger') + market_regime = symbol_info.get('marketRegime', 'unknown') + ema20 = symbol_info.get('ema20') + ema50 = symbol_info.get('ema50') + + signal_strength = 0 + reasons = [] + direction = None + + # 策略1:均值回归(震荡市场,高胜率) + if market_regime == 'ranging': + # RSI超卖,做多信号 + if rsi and rsi < 30: + signal_strength += 4 + reasons.append(f"RSI超卖({rsi:.1f})") + if direction is None: + direction = 'BUY' - if len(klines_15m) < 2: - return True # 如果无法获取,默认通过 + # RSI超买,做空信号 + elif rsi and rsi > 70: + signal_strength += 4 + reasons.append(f"RSI超买({rsi:.1f})") + if direction is None: + direction = 'SELL' - # 计算15分钟涨跌幅 - current_price_15m = float(klines_15m[-1][4]) - prev_price_15m = float(klines_15m[-2][4]) + # 布林带下轨,做多信号 + if bollinger and current_price <= bollinger['lower']: + signal_strength += 3 + reasons.append("触及布林带下轨") + if direction is None: + direction = 'BUY' - if prev_price_15m == 0: - return True + # 布林带上轨,做空信号 + elif bollinger and current_price >= bollinger['upper']: + signal_strength += 3 + reasons.append("触及布林带上轨") + if direction is None: + direction = 'SELL' + + # 策略2:趋势跟踪(趋势市场) + elif market_regime == 'trending': + # MACD金叉,做多信号 + if macd and macd['macd'] > macd['signal'] and macd['histogram'] > 0: + signal_strength += 3 + reasons.append("MACD金叉") + if direction is None: + direction = 'BUY' - change_15m = ((current_price_15m - prev_price_15m) / prev_price_15m) * 100 + # MACD死叉,做空信号 + elif macd and macd['macd'] < macd['signal'] and macd['histogram'] < 0: + signal_strength += 3 + reasons.append("MACD死叉") + if direction is None: + direction = 'SELL' - # 5分钟和15分钟趋势一致时,确认通过 - if (change_percent > 0 and change_15m > 0) or (change_percent < 0 and change_15m < 0): - return True - - # 如果5分钟涨跌幅很大(>5%),即使15分钟不一致也允许 - if abs(change_percent) > 5: - return True - - return False - - except Exception as e: - logger.debug(f"趋势确认失败 {symbol}: {e}") - return True # 出错时默认通过 + # 均线系统 + if ema20 and ema50: + if current_price > ema20 > ema50: # 上升趋势 + signal_strength += 2 + reasons.append("价格在均线之上") + if direction is None: + direction = 'BUY' + elif current_price < ema20 < ema50: # 下降趋势 + signal_strength += 2 + reasons.append("价格在均线之下") + if direction is None: + direction = 'SELL' + + # 策略3:综合信号(提高胜率) + # 多个指标同时确认时,信号更强 + confirmations = 0 + + # RSI确认 + if rsi: + if direction == 'BUY' and rsi < 50: + confirmations += 1 + elif direction == 'SELL' and rsi > 50: + confirmations += 1 + + # MACD确认 + if macd: + if direction == 'BUY' and macd['histogram'] > 0: + confirmations += 1 + elif direction == 'SELL' and macd['histogram'] < 0: + confirmations += 1 + + # 布林带确认 + if bollinger: + if direction == 'BUY' and current_price < bollinger['middle']: + confirmations += 1 + elif direction == 'SELL' and current_price > bollinger['middle']: + confirmations += 1 + + # 多个指标确认时,增加信号强度 + if confirmations >= 2: + signal_strength += 2 + reasons.append(f"{confirmations}个指标确认") + + # 判断是否应该交易(信号强度 >= 5 才交易,提高胜率) + should_trade = signal_strength >= config.TRADING_CONFIG.get('MIN_SIGNAL_STRENGTH', 5) + + if not should_trade and direction: + reasons.append(f"信号强度不足({signal_strength}/10)") + + return { + 'should_trade': should_trade, + 'direction': direction, + 'reason': ', '.join(reasons) if reasons else '无明确信号', + 'strength': signal_strength + } def stop(self): """停止策略"""