import os import sys import pandas as pd import numpy as np from typing import Dict, Any, List, Optional import datetime import time import json from datetime import datetime, timedelta # 添加项目根目录到Python路径 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from api.binance_api import BinanceAPI from api.deepseek_api import DeepSeekAPI from models.data_processor import DataProcessor from utils.config_loader import ConfigLoader from utils.dingtalk_bot import DingTalkBot class CryptoAgent: """加密货币智能体,用于分析市场数据并生成交易策略""" def __init__(self, config_path: str = None): """ 初始化加密货币智能体 Args: config_path: 配置文件路径,如果为None,则使用默认路径 """ # 加载配置 self.config_loader = ConfigLoader(config_path) # 获取各部分配置 self.binance_config = self.config_loader.get_binance_config() self.deepseek_config = self.config_loader.get_deepseek_config() self.crypto_config = self.config_loader.get_crypto_config() self.data_config = self.config_loader.get_data_config() self.agent_config = self.config_loader.get_agent_config() self.dingtalk_config = self.config_loader.get_dingtalk_config() # 初始化API客户端 self.binance_api = BinanceAPI( api_key=self.binance_config['api_key'], api_secret=self.binance_config['api_secret'], test_mode=self.binance_config['test_mode'] ) self.deepseek_api = DeepSeekAPI( api_key=self.deepseek_config['api_key'], model=self.deepseek_config['model'] ) # 初始化数据处理器 self.data_processor = DataProcessor(storage_path=self.data_config['storage_path']) # 初始化钉钉机器人(如果启用) self.dingtalk_bot = None if self.dingtalk_config.get('enabled', False): self.dingtalk_bot = DingTalkBot( webhook_url=self.dingtalk_config['webhook_url'], secret=self.dingtalk_config.get('secret') ) print("钉钉机器人已启用") # 设置支持的加密货币 self.base_currencies = self.crypto_config['base_currencies'] self.quote_currency = self.crypto_config['quote_currency'] self.symbols = [f"{base}{self.quote_currency}" for base in self.base_currencies] # 设置时间间隔 self.time_interval = self.crypto_config['time_interval'] # 风险等级 self.risk_level = self.agent_config['risk_level'] # 支持的策略 self.strategies = self.agent_config['strategies'] # 分析结果缓存 self.analysis_cache = {} def fetch_historical_data(self, symbol: str, days: int = 30) -> pd.DataFrame: """ 获取历史数据 Args: symbol: 交易对符号,例如 'BTCUSDT' days: 要获取的天数 Returns: 历史数据DataFrame """ print(f"获取{symbol}的历史数据({days}天)...") # 计算开始时间 start_time = datetime.now() - timedelta(days=days) start_str = start_time.strftime("%Y-%m-%d") # 获取K线数据 data = self.binance_api.get_historical_klines( symbol=symbol, interval=self.time_interval, start_str=start_str ) if data.empty: print(f"无法获取{symbol}的历史数据") return pd.DataFrame() # 保存原始数据 raw_data_path = self.data_processor.save_data(symbol, data, data_type='raw') print(f"原始数据已保存到: {raw_data_path}") return data def process_data(self, symbol: str, data: pd.DataFrame) -> pd.DataFrame: """ 处理数据 Args: symbol: 交易对符号,例如 'BTCUSDT' data: 原始数据 Returns: 处理后的数据 """ print(f"处理{symbol}的数据...") # 预处理数据 processed_data = self.data_processor.preprocess_market_data(symbol, data) # 保存处理后的数据 processed_data_path = self.data_processor.save_data(symbol, processed_data, data_type='processed') print(f"处理后的数据已保存到: {processed_data_path}") return processed_data def analyze_market(self, symbol: str, data: pd.DataFrame = None) -> Dict[str, Any]: """ 分析市场 Args: symbol: 交易对符号,例如 'BTCUSDT' data: 要分析的数据,如果为None,则获取最新数据 Returns: 市场分析结果 """ print(f"分析{symbol}的市场情况...") if data is None: # 尝试加载最新的处理后数据 latest_data_file = self.data_processor.get_latest_data_file(symbol, data_type='processed') if latest_data_file: data = self.data_processor.load_data(latest_data_file) else: # 如果没有找到处理后的数据,则获取新数据 raw_data = self.fetch_historical_data(symbol, days=self.data_config['historical_days']) data = self.process_data(symbol, raw_data) if data.empty: print(f"没有可用的{symbol}数据进行分析") return {} # 准备市场数据 market_data = { "symbol": symbol, "current_price": float(data['close'].iloc[-1]), "price_change_24h": float(data['close'].iloc[-1] - data['close'].iloc[-24]), "price_change_percentage_24h": float((data['close'].iloc[-1] - data['close'].iloc[-24]) / data['close'].iloc[-24] * 100), "historical_prices": data['close'].tail(100).tolist(), "volumes": data['volume'].tail(100).tolist(), "technical_indicators": { "rsi": float(data['RSI'].iloc[-1]), "macd": float(data['MACD'].iloc[-1]), "macd_signal": float(data['MACD_Signal'].iloc[-1]), "bollinger_upper": float(data['Bollinger_Upper'].iloc[-1]), "bollinger_lower": float(data['Bollinger_Lower'].iloc[-1]), "ma5": float(data['MA5'].iloc[-1]), "ma10": float(data['MA10'].iloc[-1]), "ma20": float(data['MA20'].iloc[-1]), "ma50": float(data['MA50'].iloc[-1]), "atr": float(data['ATR'].iloc[-1]) } } # 获取当前市场订单簿 order_book = self.binance_api.get_order_book(symbol, limit=10) if order_book: market_data["order_book"] = { "bids": order_book.get('bids', [])[:5], "asks": order_book.get('asks', [])[:5] } # 使用DeepSeek进行市场分析 analysis_result = self.deepseek_api.analyze_market_data(market_data) # 缓存分析结果 self.analysis_cache[symbol] = analysis_result return analysis_result def predict_price(self, symbol: str) -> Dict[str, Any]: """ 预测价格 Args: symbol: 交易对符号,例如 'BTCUSDT' Returns: 价格预测结果 """ print(f"预测{symbol}的价格趋势...") # 尝试加载最新的处理后数据 latest_data_file = self.data_processor.get_latest_data_file(symbol, data_type='processed') if not latest_data_file: # 如果没有找到处理后的数据,则获取新数据 raw_data = self.fetch_historical_data(symbol, days=self.data_config['historical_days']) data = self.process_data(symbol, raw_data) else: data = self.data_processor.load_data(latest_data_file) if data.empty: print(f"没有可用的{symbol}数据进行预测") return {} # 准备历史数据 historical_data = { "symbol": symbol, "current_price": float(data['close'].iloc[-1]), "price_history": data[['open', 'high', 'low', 'close']].tail(90).to_dict(orient='records'), "volume_history": data['volume'].tail(90).tolist(), "technical_indicators": { "rsi_history": data['RSI'].tail(30).tolist(), "macd_history": data['MACD'].tail(30).tolist(), "ma5_history": data['MA5'].tail(30).tolist(), "ma20_history": data['MA20'].tail(30).tolist(), "ma50_history": data['MA50'].tail(30).tolist() } } # 使用DeepSeek进行价格预测 prediction_result = self.deepseek_api.predict_price_trend(symbol, historical_data) return prediction_result def generate_strategy(self, symbol: str, analysis_result: Dict[str, Any] = None) -> Dict[str, Any]: """ 生成交易策略 Args: symbol: 交易对符号,例如 'BTCUSDT' analysis_result: 分析结果,如果为None,则使用缓存或重新分析 Returns: 交易策略 """ print(f"为{symbol}生成交易策略...") if analysis_result is None: # 使用缓存的分析结果或重新分析 if symbol in self.analysis_cache: analysis_result = self.analysis_cache[symbol] else: analysis_result = self.analyze_market(symbol) if not analysis_result: print(f"没有可用的{symbol}分析结果来生成策略") return {} # 使用DeepSeek生成交易策略 strategy = self.deepseek_api.generate_trading_strategy( symbol=symbol, analysis_result=analysis_result, risk_level=self.risk_level ) return strategy def analyze_all_symbols(self) -> Dict[str, Dict[str, Any]]: """ 分析所有支持的交易对 Returns: 所有交易对的分析结果 """ results = {} for symbol in self.symbols: print(f"\n开始分析{symbol}...") # 获取并处理数据 raw_data = self.fetch_historical_data(symbol, days=self.data_config['historical_days']) if not raw_data.empty: processed_data = self.process_data(symbol, raw_data) # 分析市场 analysis_result = self.analyze_market(symbol, processed_data) # 预测价格 prediction_result = self.predict_price(symbol) # 生成策略 strategy = self.generate_strategy(symbol, analysis_result) # 整合结果 results[symbol] = { "analysis": analysis_result, "prediction": prediction_result, "strategy": strategy, "timestamp": datetime.now().isoformat() } # 如果钉钉机器人已启用,发送分析报告 if self.dingtalk_bot: try: print(f"发送{symbol}分析报告到钉钉...") response = self.dingtalk_bot.send_analysis_report(symbol, results[symbol]) if response.get('errcode') == 0: print(f"{symbol}分析报告发送成功") else: print(f"{symbol}分析报告发送失败: {response}") except Exception as e: print(f"发送{symbol}分析报告时出错: {e}") print(f"{symbol}分析完成") else: print(f"跳过{symbol}分析,无法获取数据") return results def execute_strategy(self, symbol: str, strategy: Dict[str, Any]) -> Dict[str, Any]: """ 执行交易策略 Args: symbol: 交易对符号,例如 'BTCUSDT' strategy: 交易策略 Returns: 执行结果 """ if not strategy or 'position' not in strategy: print(f"无法执行{symbol}的策略,策略数据不完整") return {"status": "error", "message": "策略数据不完整"} print(f"执行{symbol}的交易策略...") # 获取当前价格 current_price_info = self.binance_api.get_ticker(symbol) if not current_price_info: print(f"无法获取{symbol}的当前价格") return {"status": "error", "message": "无法获取当前价格"} current_price = float(current_price_info.get('price', 0)) # 根据策略决定操作 position = strategy.get('position', '').upper() result = { "symbol": symbol, "strategy": strategy, "current_price": current_price, "timestamp": datetime.now().isoformat() } # 简单策略执行逻辑 if position == 'BUY' or '买' in position: # 示例:下单买入 # 在实际应用中,这里应该有更复杂的仓位管理和风险管理逻辑 quantity = 0.01 # 示例数量,实际应用中应该基于资金和风险计算 order_result = self.binance_api.place_order( symbol=symbol, side='BUY', type='MARKET', quantity=quantity ) result["action"] = "BUY" result["order_result"] = order_result # 如果钉钉机器人已启用,发送交易通知 if self.dingtalk_bot: self.send_trade_notification(symbol, "买入", quantity, current_price, strategy) elif position == 'SELL' or '卖' in position: # 示例:下单卖出 quantity = 0.01 # 示例数量 order_result = self.binance_api.place_order( symbol=symbol, side='SELL', type='MARKET', quantity=quantity ) result["action"] = "SELL" result["order_result"] = order_result # 如果钉钉机器人已启用,发送交易通知 if self.dingtalk_bot: self.send_trade_notification(symbol, "卖出", quantity, current_price, strategy) else: # 持有或其他策略 result["action"] = "HOLD" result["message"] = "根据策略保持当前仓位" print(f"执行{symbol}策略完成:{result['action']}") return result def send_trade_notification(self, symbol: str, action: str, quantity: float, price: float, strategy: Dict[str, Any]) -> None: """ 发送交易通知到钉钉 Args: symbol: 交易对符号 action: 交易操作(买入或卖出) quantity: 交易数量 price: 交易价格 strategy: 交易策略 """ if not self.dingtalk_bot: return try: # 设置图标 if action == "买入": icon = "🟢" else: icon = "🔴" # 获取策略理由 reasoning = strategy.get('reasoning', '无理由') # 构建通知内容 title = f"{icon} {symbol} {action}交易执行通知" text = f"""### {symbol} {action}交易已执行 **交易详情**: - 操作: {icon} **{action}** - 数量: {quantity} - 价格: {price} - 总额: {quantity * price} {self.quote_currency} **交易理由**: {reasoning} *交易时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}* """ # 发送通知 at_mobiles = self.dingtalk_config.get('at_mobiles', []) at_all = self.dingtalk_config.get('at_all', False) self.dingtalk_bot.send_markdown(title, text, at_mobiles, at_all) print(f"{symbol} {action}交易通知已发送") except Exception as e: print(f"发送交易通知时出错: {e}") def run_analysis_cycle(self) -> Dict[str, Any]: """ 运行一个完整的分析周期 Returns: 分析结果 """ print(f"开始新的分析周期,时间:{datetime.now().isoformat()}") results = self.analyze_all_symbols() # 保存分析结果 timestamp = datetime.now().strftime("%Y%m%d%H%M%S") results_dir = os.path.join(self.data_config['storage_path'], 'analysis_results') os.makedirs(results_dir, exist_ok=True) results_file = os.path.join(results_dir, f"analysis_results_{timestamp}.json") with open(results_file, 'w', encoding='utf-8') as f: json.dump(results, f, indent=2, ensure_ascii=False) print(f"分析结果已保存到:{results_file}") return results def start_agent(self, run_once: bool = False) -> None: """ 启动智能体 Args: run_once: 是否只运行一次分析周期 """ print("启动加密货币分析智能体...") try: if run_once: self.run_analysis_cycle() else: while True: self.run_analysis_cycle() # 等待下一个分析周期 analysis_interval = self.agent_config['analysis_interval'] print(f"等待{analysis_interval}分钟后进行下一次分析...") time.sleep(analysis_interval * 60) except KeyboardInterrupt: print("\n智能体已停止") except Exception as e: print(f"智能体运行时出错: {e}") import traceback traceback.print_exc()