crypto.ai/cryptoai/agents/crypto_agent.py
2025-04-27 14:34:26 +08:00

552 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()
def send_notifications(self, symbol: str, analysis_data: Dict[str, Any]) -> bool:
"""
发送分析结果通知
Args:
symbol: 交易对符号
analysis_data: 分析数据
Returns:
发送是否成功
"""
if not self.dingtalk_bot:
print(f"钉钉通知未启用,跳过发送 {symbol} 的分析结果")
return False
try:
# 使用已初始化的钉钉机器人实例发送完整分析报告
response = self.dingtalk_bot.send_analysis_report(symbol, analysis_data)
if response.get('errcode') == 0:
print(f"成功发送 {symbol} 分析结果到钉钉")
return True
else:
print(f"发送 {symbol} 分析结果到钉钉失败: {response}")
return False
except Exception as e:
print(f"发送钉钉通知时出错: {e}")
return False