a
This commit is contained in:
parent
bb795604a3
commit
db2bd6a3b2
|
|
@ -55,6 +55,54 @@ async def get_recommendations(
|
||||||
end_date=end_dt
|
end_date=end_dt
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 如果是获取有效推荐,尝试更新实时价格
|
||||||
|
if status == 'active' or (status is None and len(recommendations) > 0):
|
||||||
|
try:
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
current_file = Path(__file__)
|
||||||
|
backend_path = current_file.parent.parent.parent
|
||||||
|
project_root = backend_path.parent
|
||||||
|
trading_system_path = project_root / 'trading_system'
|
||||||
|
|
||||||
|
if trading_system_path.exists():
|
||||||
|
sys.path.insert(0, str(trading_system_path))
|
||||||
|
from binance_client import BinanceClient
|
||||||
|
import config
|
||||||
|
|
||||||
|
# 创建客户端实例(不需要连接,只需要访问价格缓存)
|
||||||
|
client = BinanceClient(
|
||||||
|
api_key=config.BINANCE_API_KEY,
|
||||||
|
api_secret=config.BINANCE_API_SECRET,
|
||||||
|
testnet=config.USE_TESTNET
|
||||||
|
)
|
||||||
|
|
||||||
|
# 更新推荐中的实时价格和涨跌幅
|
||||||
|
for rec in recommendations:
|
||||||
|
symbol = rec.get('symbol')
|
||||||
|
if symbol:
|
||||||
|
# 尝试从WebSocket缓存获取实时价格
|
||||||
|
realtime_price = client.get_realtime_price(symbol)
|
||||||
|
if realtime_price is not None:
|
||||||
|
# 更新价格
|
||||||
|
old_price = rec.get('current_price', 0)
|
||||||
|
rec['current_price'] = realtime_price
|
||||||
|
|
||||||
|
# 计算新的涨跌幅
|
||||||
|
if old_price > 0:
|
||||||
|
change_percent = ((realtime_price - old_price) / old_price) * 100
|
||||||
|
rec['change_percent'] = round(change_percent, 4)
|
||||||
|
rec['price_updated'] = True # 标记价格已更新
|
||||||
|
else:
|
||||||
|
rec['price_updated'] = False
|
||||||
|
else:
|
||||||
|
rec['price_updated'] = False
|
||||||
|
else:
|
||||||
|
rec['price_updated'] = False
|
||||||
|
except Exception as price_update_error:
|
||||||
|
# 如果价格更新失败,不影响返回推荐列表
|
||||||
|
logger.debug(f"更新推荐实时价格失败(不影响返回): {price_update_error}")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"count": len(recommendations),
|
"count": len(recommendations),
|
||||||
|
|
@ -69,9 +117,58 @@ async def get_recommendations(
|
||||||
async def get_active_recommendations():
|
async def get_active_recommendations():
|
||||||
"""
|
"""
|
||||||
获取当前有效的推荐(未过期、未执行、未取消)
|
获取当前有效的推荐(未过期、未执行、未取消)
|
||||||
|
使用WebSocket实时价格更新推荐中的价格信息
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
recommendations = TradeRecommendation.get_active()
|
recommendations = TradeRecommendation.get_active()
|
||||||
|
|
||||||
|
# 尝试从WebSocket缓存获取实时价格更新推荐中的价格
|
||||||
|
try:
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
current_file = Path(__file__)
|
||||||
|
backend_path = current_file.parent.parent.parent
|
||||||
|
project_root = backend_path.parent
|
||||||
|
trading_system_path = project_root / 'trading_system'
|
||||||
|
|
||||||
|
if trading_system_path.exists():
|
||||||
|
sys.path.insert(0, str(trading_system_path))
|
||||||
|
from binance_client import BinanceClient
|
||||||
|
import config
|
||||||
|
|
||||||
|
# 创建客户端实例(不需要连接,只需要访问价格缓存)
|
||||||
|
client = BinanceClient(
|
||||||
|
api_key=config.BINANCE_API_KEY,
|
||||||
|
api_secret=config.BINANCE_API_SECRET,
|
||||||
|
testnet=config.USE_TESTNET
|
||||||
|
)
|
||||||
|
|
||||||
|
# 更新推荐中的实时价格和涨跌幅
|
||||||
|
for rec in recommendations:
|
||||||
|
symbol = rec.get('symbol')
|
||||||
|
if symbol:
|
||||||
|
# 尝试从WebSocket缓存获取实时价格
|
||||||
|
realtime_price = client.get_realtime_price(symbol)
|
||||||
|
if realtime_price is not None:
|
||||||
|
# 更新价格
|
||||||
|
old_price = rec.get('current_price', 0)
|
||||||
|
rec['current_price'] = realtime_price
|
||||||
|
|
||||||
|
# 计算新的涨跌幅
|
||||||
|
if old_price > 0:
|
||||||
|
change_percent = ((realtime_price - old_price) / old_price) * 100
|
||||||
|
rec['change_percent'] = round(change_percent, 4)
|
||||||
|
rec['price_updated'] = True # 标记价格已更新
|
||||||
|
else:
|
||||||
|
rec['price_updated'] = False
|
||||||
|
else:
|
||||||
|
rec['price_updated'] = False
|
||||||
|
else:
|
||||||
|
rec['price_updated'] = False
|
||||||
|
except Exception as price_update_error:
|
||||||
|
# 如果价格更新失败,不影响返回推荐列表
|
||||||
|
logger.debug(f"更新推荐实时价格失败(不影响返回): {price_update_error}")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"count": len(recommendations),
|
"count": len(recommendations),
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,18 @@ function Recommendations() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadRecommendations()
|
loadRecommendations()
|
||||||
|
|
||||||
|
// 如果是查看有效推荐,每10秒自动刷新一次(获取实时价格)
|
||||||
|
let interval = null
|
||||||
|
if (statusFilter === 'active') {
|
||||||
|
interval = setInterval(loadRecommendations, 10000) // 每10秒刷新
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
}, [statusFilter, directionFilter])
|
}, [statusFilter, directionFilter])
|
||||||
|
|
||||||
const loadRecommendations = async () => {
|
const loadRecommendations = async () => {
|
||||||
|
|
@ -208,9 +220,14 @@ function Recommendations() {
|
||||||
<div className="price-info">
|
<div className="price-info">
|
||||||
<div className="price-item">
|
<div className="price-item">
|
||||||
<label>当前价格:</label>
|
<label>当前价格:</label>
|
||||||
<span>{parseFloat(rec.current_price || 0).toFixed(4)} USDT</span>
|
<span>
|
||||||
|
{parseFloat(rec.current_price || 0).toFixed(4)} USDT
|
||||||
|
{rec.price_updated && (
|
||||||
|
<span className="price-updated-badge" title="价格已通过WebSocket实时更新">🟢</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{rec.change_percent && (
|
{rec.change_percent !== undefined && rec.change_percent !== null && (
|
||||||
<div className="price-item">
|
<div className="price-item">
|
||||||
<label>24h涨跌:</label>
|
<label>24h涨跌:</label>
|
||||||
<span className={rec.change_percent >= 0 ? 'positive' : 'negative'}>
|
<span className={rec.change_percent >= 0 ? 'positive' : 'negative'}>
|
||||||
|
|
|
||||||
|
|
@ -533,8 +533,10 @@ class PositionManager:
|
||||||
)
|
)
|
||||||
|
|
||||||
# 检查止损(使用更新后的止损价)
|
# 检查止损(使用更新后的止损价)
|
||||||
stop_loss = position_info['stopLoss']
|
stop_loss = position_info.get('stopLoss')
|
||||||
if position_info['side'] == 'BUY' and current_price <= stop_loss:
|
if stop_loss is None:
|
||||||
|
logger.warning(f"{symbol} 止损价未设置,跳过止损检查")
|
||||||
|
elif position_info['side'] == 'BUY' and current_price <= stop_loss:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"{symbol} 触发止损: {current_price:.4f} <= {stop_loss:.4f} "
|
f"{symbol} 触发止损: {current_price:.4f} <= {stop_loss:.4f} "
|
||||||
f"(盈亏: {pnl_percent:.2f}%)"
|
f"(盈亏: {pnl_percent:.2f}%)"
|
||||||
|
|
@ -559,7 +561,7 @@ class PositionManager:
|
||||||
closed_positions.append(symbol)
|
closed_positions.append(symbol)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if position_info['side'] == 'SELL' and current_price >= stop_loss:
|
if stop_loss is not None and position_info['side'] == 'SELL' and current_price >= stop_loss:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"{symbol} 触发止损: {current_price:.4f} >= {stop_loss:.4f} "
|
f"{symbol} 触发止损: {current_price:.4f} >= {stop_loss:.4f} "
|
||||||
f"(盈亏: {pnl_percent:.2f}%)"
|
f"(盈亏: {pnl_percent:.2f}%)"
|
||||||
|
|
@ -591,7 +593,7 @@ class PositionManager:
|
||||||
remaining_quantity = position_info.get('remainingQuantity', quantity)
|
remaining_quantity = position_info.get('remainingQuantity', quantity)
|
||||||
|
|
||||||
# 第一目标:盈亏比1:1,了结50%仓位
|
# 第一目标:盈亏比1:1,了结50%仓位
|
||||||
if not partial_profit_taken:
|
if not partial_profit_taken and take_profit_1 is not None:
|
||||||
if position_info['side'] == 'BUY' and current_price >= take_profit_1:
|
if position_info['side'] == 'BUY' and current_price >= take_profit_1:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{symbol} 触发第一目标止盈(盈亏比1:1): {current_price:.4f} >= {take_profit_1:.4f} "
|
f"{symbol} 触发第一目标止盈(盈亏比1:1): {current_price:.4f} >= {take_profit_1:.4f} "
|
||||||
|
|
@ -620,7 +622,7 @@ class PositionManager:
|
||||||
logger.info(f"{symbol} 剩余仓位止损移至成本价,配合移动止损博取更大利润")
|
logger.info(f"{symbol} 剩余仓位止损移至成本价,配合移动止损博取更大利润")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{symbol} 部分止盈失败: {e}")
|
logger.error(f"{symbol} 部分止盈失败: {e}")
|
||||||
elif position_info['side'] == 'SELL' and current_price <= take_profit_1:
|
elif take_profit_1 is not None and position_info['side'] == 'SELL' and current_price <= take_profit_1:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{symbol} 触发第一目标止盈(盈亏比1:1): {current_price:.4f} <= {take_profit_1:.4f} "
|
f"{symbol} 触发第一目标止盈(盈亏比1:1): {current_price:.4f} <= {take_profit_1:.4f} "
|
||||||
f"(盈亏: {pnl_percent:.2f}%)"
|
f"(盈亏: {pnl_percent:.2f}%)"
|
||||||
|
|
@ -649,7 +651,7 @@ class PositionManager:
|
||||||
logger.error(f"{symbol} 部分止盈失败: {e}")
|
logger.error(f"{symbol} 部分止盈失败: {e}")
|
||||||
|
|
||||||
# 第二目标:原始止盈价,平掉剩余仓位
|
# 第二目标:原始止盈价,平掉剩余仓位
|
||||||
if partial_profit_taken:
|
if partial_profit_taken and take_profit_2 is not None:
|
||||||
if position_info['side'] == 'BUY' and current_price >= take_profit_2:
|
if position_info['side'] == 'BUY' and current_price >= take_profit_2:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{symbol} 触发第二目标止盈: {current_price:.4f} >= {take_profit_2:.4f} "
|
f"{symbol} 触发第二目标止盈: {current_price:.4f} >= {take_profit_2:.4f} "
|
||||||
|
|
@ -673,7 +675,7 @@ class PositionManager:
|
||||||
if await self.close_position(symbol, reason=exit_reason):
|
if await self.close_position(symbol, reason=exit_reason):
|
||||||
closed_positions.append(symbol)
|
closed_positions.append(symbol)
|
||||||
continue
|
continue
|
||||||
elif position_info['side'] == 'SELL' and current_price <= take_profit_2:
|
elif take_profit_2 is not None and position_info['side'] == 'SELL' and current_price <= take_profit_2:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{symbol} 触发第二目标止盈: {current_price:.4f} <= {take_profit_2:.4f} "
|
f"{symbol} 触发第二目标止盈: {current_price:.4f} <= {take_profit_2:.4f} "
|
||||||
f"(盈亏: {pnl_percent:.2f}%)"
|
f"(盈亏: {pnl_percent:.2f}%)"
|
||||||
|
|
@ -699,7 +701,7 @@ class PositionManager:
|
||||||
else:
|
else:
|
||||||
# 如果未部分止盈,但达到第二目标,直接全部平仓
|
# 如果未部分止盈,但达到第二目标,直接全部平仓
|
||||||
take_profit = position_info.get('takeProfit')
|
take_profit = position_info.get('takeProfit')
|
||||||
if position_info['side'] == 'BUY' and current_price >= take_profit:
|
if take_profit is not None and position_info['side'] == 'BUY' and current_price >= take_profit:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{symbol} 触发止盈: {current_price:.4f} >= {take_profit:.4f} "
|
f"{symbol} 触发止盈: {current_price:.4f} >= {take_profit:.4f} "
|
||||||
f"(盈亏: {pnl_percent:.2f}%)"
|
f"(盈亏: {pnl_percent:.2f}%)"
|
||||||
|
|
@ -723,7 +725,7 @@ class PositionManager:
|
||||||
closed_positions.append(symbol)
|
closed_positions.append(symbol)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if position_info['side'] == 'SELL' and current_price <= take_profit:
|
if take_profit is not None and position_info['side'] == 'SELL' and current_price <= take_profit:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{symbol} 触发止盈: {current_price:.4f} <= {take_profit:.4f} "
|
f"{symbol} 触发止盈: {current_price:.4f} <= {take_profit:.4f} "
|
||||||
f"(盈亏: {pnl_percent:.2f}%)"
|
f"(盈亏: {pnl_percent:.2f}%)"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user