增加合约市场

This commit is contained in:
aaron 2026-02-20 22:02:17 +08:00
parent 75592857a3
commit 5173c05e65
3 changed files with 328 additions and 2 deletions

View File

@ -426,7 +426,9 @@ class LLMSignalAnalyzer:
agent_type: 智能体类型支持 'crypto', 'stock', 'smart'
"""
from app.config import get_settings
from app.services.binance_service import binance_service
self.news_service = get_news_service()
self.binance_service = binance_service
settings = get_settings()
# 根据智能体类型选择模型配置
@ -482,6 +484,17 @@ class LLMSignalAnalyzer:
# 获取新闻数据
news_text = await self._get_news_context(symbol, symbols or [symbol])
# 获取合约市场数据(仅加密货币)
futures_data = None
if self.agent_type == 'crypto':
try:
futures_data = self.binance_service.get_futures_market_data(symbol)
if futures_data:
logger.info(f"{symbol} 资金费率: {futures_data.get('funding_rate', {}).get('funding_rate_percent', 0):.4f}% | "
f"情绪: {futures_data.get('market_sentiment', '')}")
except Exception as e:
logger.warning(f"获取 {symbol} 合约数据失败: {e}")
# 根据智能体类型选择提示词
if self.agent_type == 'stock':
system_prompt = self.STOCK_SYSTEM_PROMPT
@ -489,7 +502,7 @@ class LLMSignalAnalyzer:
system_prompt = self.CRYPTO_SYSTEM_PROMPT
# 构建数据提示
data_prompt = self._build_data_prompt(symbol, data, news_text, position_info)
data_prompt = self._build_data_prompt(symbol, data, news_text, position_info, futures_data)
# 调用 LLM
response = llm_service.chat([
@ -595,7 +608,8 @@ class LLMSignalAnalyzer:
return "\n".join(lines)
def _build_data_prompt(self, symbol: str, data: Dict[str, pd.DataFrame],
news_text: str = "", position_info: Dict[str, Any] = None) -> str:
news_text: str = "", position_info: Dict[str, Any] = None,
futures_data: Dict[str, Any] = None) -> str:
"""构建数据提示词"""
parts = [f"# {symbol} 市场数据分析\n"]
parts.append(f"分析时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
@ -606,6 +620,10 @@ class LLMSignalAnalyzer:
current_price = float(data['5m'].iloc[-1]['close'])
parts.append(f"**当前价格**: ${current_price:,.2f}\n")
# === 新增:合约市场数据 ===
if futures_data and self.agent_type == 'crypto':
parts.append(self.binance_service.format_futures_data_for_llm(symbol, futures_data))
# === 新增:账户和持仓信息 ===
if position_info:
parts.append("\n## 账户与持仓状态")

View File

@ -22,6 +22,7 @@ class BinanceService:
# Binance API 基础 URL
BASE_URL = "https://api.binance.com"
FUTURES_URL = "https://fapi.binance.com" # 合约 API
def __init__(self, api_key: str = "", api_secret: str = ""):
"""
@ -272,6 +273,233 @@ class BinanceService:
logger.error(f"获取 {symbol} 24h 统计失败: {e}")
return None
def get_funding_rate(self, symbol: str) -> Optional[Dict[str, Any]]:
"""
获取资金费率
Args:
symbol: 交易对 'BTCUSDT'
Returns:
包含资金费率信息的字典
"""
try:
url = f"{self.FUTURES_URL}/fapi/v1/premiumIndex"
params = {'symbol': symbol}
response = self._session.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
# 解析资金费率
funding_rate = float(data.get('lastFundingRate', 0))
mark_price = float(data.get('markPrice', 0))
index_price = float(data.get('indexPrice', 0))
next_funding_time = int(data.get('nextFundingTime', 0))
# 判断市场情绪
if funding_rate > 0.01: # > 0.1%
sentiment = "极度贪婪"
sentiment_level = "extreme_greed"
elif funding_rate > 0.05: # > 0.05%
sentiment = "贪婪"
sentiment_level = "greed"
elif funding_rate < -0.01: # < -0.1%
sentiment = "极度恐惧"
sentiment_level = "extreme_fear"
elif funding_rate < -0.05: # < -0.05%
sentiment = "恐惧"
sentiment_level = "fear"
else:
sentiment = "中性"
sentiment_level = "neutral"
return {
'funding_rate': funding_rate,
'funding_rate_percent': funding_rate * 100, # 转为百分比
'mark_price': mark_price,
'index_price': index_price,
'next_funding_time': next_funding_time,
'sentiment': sentiment,
'sentiment_level': sentiment_level
}
except Exception as e:
logger.error(f"获取 {symbol} 资金费率失败: {e}")
return None
def get_open_interest(self, symbol: str) -> Optional[Dict[str, Any]]:
"""
获取持仓量
Args:
symbol: 交易对 'BTCUSDT'
Returns:
包含持仓量信息的字典
"""
try:
url = f"{self.FUTURES_URL}/fapi/v1/openInterest"
params = {'symbol': symbol}
response = self._session.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
open_interest = float(data.get('openInterest', 0))
open_interest_value = float(data.get('openInterestValue', 0))
return {
'open_interest': open_interest,
'open_interest_value': open_interest_value,
'timestamp': int(data.get('time', 0))
}
except Exception as e:
logger.error(f"获取 {symbol} 持仓量失败: {e}")
return None
def get_open_interest_hist(self, symbol: str, period: str = '5m',
limit: int = 30) -> Optional[List[Dict[str, Any]]]:
"""
获取历史持仓量用于计算变化趋势
Args:
symbol: 交易对
period: 周期 (5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d)
limit: 获取数量 (最大 500)
Returns:
持仓量历史列表
"""
try:
# Binance 使用正确的 API 端点
url = f"{self.FUTURES_URL}/futures/data/openInterestHist"
params = {
'symbol': symbol,
'period': period,
'limit': limit
}
response = self._session.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
return data
except Exception as e:
logger.error(f"获取 {symbol} 历史持仓量失败: {e}")
return None
def get_futures_market_data(self, symbol: str) -> Optional[Dict[str, Any]]:
"""
获取合约市场综合数据资金费率 + 持仓量 + 趋势分析
Args:
symbol: 交易对
Returns:
综合市场数据
"""
try:
# 并发获取数据
funding_rate = self.get_funding_rate(symbol)
open_interest = self.get_open_interest(symbol)
if not funding_rate or not open_interest:
logger.warning(f"获取 {symbol} 合约数据不完整")
return None
# 获取历史持仓量计算趋势
hist_oi = self.get_open_interest_hist(symbol, period='1h', limit=24)
oi_change = 0
oi_change_percent = 0
if hist_oi and len(hist_oi) >= 2:
oi_24h_ago = float(hist_oi[-1].get('sumOpenInterest', 0))
oi_now = float(hist_oi[0].get('sumOpenInterest', 0))
oi_change = oi_now - oi_24h_ago
oi_change_percent = (oi_change / oi_24h_ago * 100) if oi_24h_ago > 0 else 0
# 计算溢价率
premium_rate = 0
if funding_rate.get('index_price', 0) > 0:
premium_rate = ((funding_rate['mark_price'] - funding_rate['index_price'])
/ funding_rate['index_price'] * 100)
return {
'funding_rate': funding_rate,
'open_interest': open_interest,
'oi_change_24h': oi_change,
'oi_change_percent_24h': oi_change_percent,
'premium_rate': premium_rate,
'market_sentiment': funding_rate.get('sentiment', ''),
'sentiment_level': funding_rate.get('sentiment_level', '')
}
except Exception as e:
logger.error(f"获取 {symbol} 合约市场数据失败: {e}")
return None
def format_futures_data_for_llm(self, symbol: str,
market_data: Dict[str, Any]) -> str:
"""
格式化合约数据供 LLM 分析
Args:
symbol: 交易对
market_data: 合约市场数据
Returns:
格式化的文本
"""
if not market_data:
return ""
lines = [f"\n## {symbol} 合约市场数据\n"]
# 资金费率
funding = market_data.get('funding_rate', {})
if funding:
fr = funding.get('funding_rate_percent', 0)
sentiment = funding.get('sentiment', '')
lines.append(f"### 资金费率")
lines.append(f"• 当前费率: {fr:.4f}%")
lines.append(f"• 市场情绪: {sentiment}")
lines.append(f"• 标记价格: ${funding.get('mark_price', 0):,.2f}")
lines.append(f"• 指数价格: ${funding.get('index_price', 0):,.2f}")
# 资金费率分析
if fr > 0.1:
lines.append(f"• ⚠️ 极高费率,多头过度杠杆,警惕回调风险")
elif fr > 0.05:
lines.append(f"• 正费率,多头占优但未极端")
elif fr < -0.1:
lines.append(f"• ⚠️ 极低费率,空头过度杠杆,可能反弹")
elif fr < -0.05:
lines.append(f"• 负费率,空头占优但未极端")
# 持仓量
oi = market_data.get('open_interest', {})
if oi:
lines.append(f"\n### 持仓量")
lines.append(f"• 当前持仓: {oi.get('open_interest', 0):,.0f}")
lines.append(f"• 持仓金额: ${oi.get('open_interest_value', 0):,.0f}")
# 持仓量变化
oi_change = market_data.get('oi_change_percent_24h', 0)
if oi_change != 0:
lines.append(f"• 24h变化: {oi_change:+.2f}%")
if oi_change > 10:
lines.append(f"• ⚠️ 持仓大幅增加,资金加速流入")
elif oi_change < -10:
lines.append(f"• ⚠️ 持仓大幅减少,资金加速流出")
# 溢价率
premium = market_data.get('premium_rate', 0)
if premium != 0:
lines.append(f"\n### 溢价分析")
lines.append(f"• 现货溢价: {premium:+.2f}%")
if premium > 1:
lines.append(f"• ⚠️ 高溢价,市场过热")
elif premium < -1:
lines.append(f"• ⚠️ 负溢价,市场偏冷")
return "\n".join(lines)
# 全局实例
binance_service = BinanceService()

80
scripts/test_futures_data.py Executable file
View File

@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""
测试合约市场数据获取
"""
import sys
import os
# 确保路径正确
script_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(script_dir)
backend_dir = os.path.join(project_root, 'backend')
sys.path.insert(0, backend_dir)
from app.services.binance_service import binance_service
def main():
print("=" * 60)
print("📊 测试 Binance 合约市场数据获取")
print("=" * 60)
symbols = ['BTCUSDT', 'ETHUSDT']
for symbol in symbols:
print(f"\n{'='*60}")
print(f"📈 {symbol} 合约市场数据")
print(f"{'='*60}")
# 1. 获取资金费率
print(f"\n🔍 获取资金费率...")
funding_rate = binance_service.get_funding_rate(symbol)
if funding_rate:
print(f"✅ 资金费率: {funding_rate['funding_rate_percent']:.4f}%")
print(f" 市场情绪: {funding_rate['sentiment']}")
print(f" 标记价格: ${funding_rate['mark_price']:,.2f}")
print(f" 指数价格: ${funding_rate['index_price']:,.2f}")
# 2. 获取持仓量
print(f"\n🔍 获取持仓量...")
open_interest = binance_service.get_open_interest(symbol)
if open_interest:
print(f"✅ 持仓量: {open_interest['open_interest']:,.0f}")
print(f" 持仓金额: ${open_interest['open_interest_value']:,.0f}")
# 3. 获取历史持仓量
print(f"\n🔍 获取历史持仓量计算24h变化...")
hist_oi = binance_service.get_open_interest_hist(symbol, period='1h', limit=24)
if hist_oi and len(hist_oi) >= 2:
oi_now = float(hist_oi[0].get('sumOpenInterest', 0))
oi_24h = float(hist_oi[-1].get('sumOpenInterest', 0))
oi_change = oi_now - oi_24h
oi_change_pct = (oi_change / oi_24h * 100) if oi_24h > 0 else 0
print(f"✅ 24h前: {oi_24h:,.0f}")
print(f" 当前: {oi_now:,.0f}")
print(f" 变化: {oi_change:+,.0f} 张 ({oi_change_pct:+.2f}%)")
# 4. 获取综合数据
print(f"\n🔍 获取综合合约数据...")
market_data = binance_service.get_futures_market_data(symbol)
if market_data:
print(f"✅ 综合数据获取成功")
print(f" 资金费率: {market_data['funding_rate']['funding_rate_percent']:.4f}%")
print(f" 持仓24h变化: {market_data['oi_change_percent_24h']:+.2f}%")
print(f" 溢价率: {market_data['premium_rate']:+.2f}%")
print(f" 市场情绪: {market_data['market_sentiment']}")
# 5. 格式化给 LLM 的数据
print(f"\n🤖 格式化给 LLM 的数据:")
print(f"{''*60}")
if market_data:
formatted = binance_service.format_futures_data_for_llm(symbol, market_data)
print(formatted)
print("\n" + "=" * 60)
print("✅ 测试完成")
print("=" * 60)
if __name__ == "__main__":
main()