522 lines
19 KiB
Python
522 lines
19 KiB
Python
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() |