diff --git a/frontend/src/components/StatsDashboard.jsx b/frontend/src/components/StatsDashboard.jsx
index c7652f0..537dc41 100644
--- a/frontend/src/components/StatsDashboard.jsx
+++ b/frontend/src/components/StatsDashboard.jsx
@@ -201,23 +201,31 @@ const StatsDashboard = () => {
}
}
- // 从配置获取止损止盈比例(优先使用dashboard返回的配置,其次使用单独加载的配置)
+ // 计算保证金(用于基于保证金的止损止盈)
+ const quantity = parseFloat(trade.quantity || 0)
+ const leverage = parseFloat(trade.leverage || 10)
+ const entryValue = trade.entry_value_usdt !== undefined
+ ? parseFloat(trade.entry_value_usdt)
+ : (quantity * entryPrice)
+ const margin = leverage > 0 ? entryValue / leverage : entryValue
+
+ // 从配置获取止损止盈比例(相对于保证金)
const configSource = dashboardData?.trading_config || tradingConfig
const stopLossConfig = configSource?.STOP_LOSS_PERCENT
const takeProfitConfig = configSource?.TAKE_PROFIT_PERCENT
- // 配置值是小数形式(0.03表示3%),需要转换为百分比
- let defaultStopLossPercent = 3.0 // 默认3%
- let defaultTakeProfitPercent = 5.0 // 默认5%
+ // 配置值是小数形式(0.03表示3%),相对于保证金
+ let stopLossPercentMargin = 0.03 // 默认3%(相对于保证金)
+ let takeProfitPercentMargin = 0.05 // 默认5%(相对于保证金)
if (stopLossConfig) {
const configValue = stopLossConfig.value
if (typeof configValue === 'number') {
- defaultStopLossPercent = configValue * 100
+ stopLossPercentMargin = configValue
} else {
const parsed = parseFloat(configValue)
if (!isNaN(parsed)) {
- defaultStopLossPercent = parsed * 100
+ stopLossPercentMargin = parsed
}
}
}
@@ -225,32 +233,45 @@ const StatsDashboard = () => {
if (takeProfitConfig) {
const configValue = takeProfitConfig.value
if (typeof configValue === 'number') {
- defaultTakeProfitPercent = configValue * 100
+ takeProfitPercentMargin = configValue
} else {
const parsed = parseFloat(configValue)
if (!isNaN(parsed)) {
- defaultTakeProfitPercent = parsed * 100
+ takeProfitPercentMargin = parsed
}
}
}
- // 计算止损价和止盈价(基于默认比例)
+ // 计算止损止盈金额(相对于保证金)
+ const stopLossAmount = margin * stopLossPercentMargin
+ const takeProfitAmount = margin * takeProfitPercentMargin
+
+ // 计算止损价和止盈价(基于保证金金额)
+ // 止损金额 = (开仓价 - 止损价) × 数量 或 (止损价 - 开仓价) × 数量
let stopLossPrice = 0
let takeProfitPrice = 0
if (side === 'BUY') {
- stopLossPrice = entryPrice * (1 - defaultStopLossPercent / 100)
- takeProfitPrice = entryPrice * (1 + defaultTakeProfitPercent / 100)
+ // 做多:止损价 = 开仓价 - (止损金额 / 数量)
+ stopLossPrice = entryPrice - (stopLossAmount / quantity)
+ // 做多:止盈价 = 开仓价 + (止盈金额 / 数量)
+ takeProfitPrice = entryPrice + (takeProfitAmount / quantity)
} else {
- stopLossPrice = entryPrice * (1 + defaultStopLossPercent / 100)
- takeProfitPrice = entryPrice * (1 - defaultTakeProfitPercent / 100)
+ // 做空:止损价 = 开仓价 + (止损金额 / 数量)
+ stopLossPrice = entryPrice + (stopLossAmount / quantity)
+ // 做空:止盈价 = 开仓价 - (止盈金额 / 数量)
+ takeProfitPrice = entryPrice - (takeProfitAmount / quantity)
}
- // 计算止损比例和止盈比例(相对于入场价)
- const stopLossPercent = side === 'BUY'
+ // 计算止损比例和止盈比例(相对于保证金,用于显示)
+ const stopLossPercent = stopLossPercentMargin * 100 // 相对于保证金
+ const takeProfitPercent = takeProfitPercentMargin * 100 // 相对于保证金
+
+ // 也计算价格百分比(用于参考)
+ const stopLossPercentPrice = side === 'BUY'
? ((entryPrice - stopLossPrice) / entryPrice) * 100
: ((stopLossPrice - entryPrice) / entryPrice) * 100
- const takeProfitPercent = side === 'BUY'
+ const takeProfitPercentPrice = side === 'BUY'
? ((takeProfitPrice - entryPrice) / entryPrice) * 100
: ((entryPrice - takeProfitPrice) / entryPrice) * 100
@@ -303,12 +324,16 @@ const StatsDashboard = () => {
- 止损比例: -{stopLossPercent.toFixed(2)}%
+ 止损: -{stopLossPercent.toFixed(2)}%
+ (of margin)
(价: {stopLossPrice.toFixed(4)})
+ (金额: -{stopLossAmount.toFixed(2)} USDT)
- 止盈比例: +{takeProfitPercent.toFixed(2)}%
+ 止盈: +{takeProfitPercent.toFixed(2)}%
+ (of margin)
(价: {takeProfitPrice.toFixed(4)})
+ (金额: +{takeProfitAmount.toFixed(2)} USDT)
diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py
index 34a06c5..4dff62d 100644
--- a/trading_system/position_manager.py
+++ b/trading_system/position_manager.py
@@ -145,30 +145,31 @@ class PositionManager:
logger.debug(f"获取K线数据失败,使用固定止损: {e}")
klines = None
- # 使用基于支撑/阻力的动态止损
- if klines or bollinger or atr:
- stop_loss_price = self.risk_manager.get_stop_loss_price(
- entry_price, side,
- klines=klines,
- bollinger=bollinger,
- atr=atr
- )
-
- # 计算动态止损百分比(用于计算止盈)
- if side == 'BUY':
- stop_loss_pct = (entry_price - stop_loss_price) / entry_price
- else:
- stop_loss_pct = (stop_loss_price - entry_price) / entry_price
-
- # 止盈为止损的2-2.5倍(提高盈亏比)
- take_profit_pct = stop_loss_pct * 2.5
- take_profit_price = self.risk_manager.get_take_profit_price(
- entry_price, side, take_profit_pct=take_profit_pct
- )
- else:
- # 回退到固定止损止盈
- stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side)
- take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side)
+ # 使用基于保证金的止损止盈(结合技术分析)
+ # 计算保证金和仓位价值
+ position_value = entry_price * quantity
+ margin = position_value / leverage if leverage > 0 else position_value
+
+ # 获取止损止盈百分比(相对于保证金)
+ stop_loss_pct_margin = self.risk_manager.config.get('STOP_LOSS_PERCENT', 0.03)
+ take_profit_pct_margin = self.risk_manager.config.get('TAKE_PROFIT_PERCENT', 0.05)
+
+ # 计算基于保证金的止损止盈
+ stop_loss_price = self.risk_manager.get_stop_loss_price(
+ entry_price, side, quantity, leverage,
+ stop_loss_pct=stop_loss_pct_margin,
+ klines=klines,
+ bollinger=bollinger,
+ atr=atr
+ )
+
+ # 计算止盈(基于保证金,为止损的倍数)
+ # 如果止损是保证金的3%,止盈可以是保证金的7.5%(2.5倍)
+ take_profit_pct_margin = stop_loss_pct_margin * 2.5
+ take_profit_price = self.risk_manager.get_take_profit_price(
+ entry_price, side, quantity, leverage,
+ take_profit_pct=take_profit_pct_margin
+ )
# 下单
order = await self.client.place_order(
@@ -598,51 +599,70 @@ class PositionManager:
else:
current_price = entry_price
- # 计算当前盈亏
+ # 计算当前盈亏(基于保证金)
+ leverage = position_info.get('leverage', 10)
+ position_value = entry_price * quantity
+ margin = position_value / leverage if leverage > 0 else position_value
+
+ # 计算盈亏金额
if position_info['side'] == 'BUY':
- pnl_percent = ((current_price - entry_price) / entry_price) * 100
+ pnl_amount = (current_price - entry_price) * quantity
else:
- pnl_percent = ((entry_price - current_price) / entry_price) * 100
+ pnl_amount = (entry_price - current_price) * quantity
- # 更新最大盈利
- if pnl_percent > position_info.get('maxProfit', 0):
- position_info['maxProfit'] = pnl_percent
+ # 计算盈亏百分比(相对于保证金,与币安一致)
+ pnl_percent_margin = (pnl_amount / margin * 100) if margin > 0 else 0
- # 移动止损逻辑(盈利后保护利润)
+ # 也计算价格百分比(用于显示和移动止损)
+ if position_info['side'] == 'BUY':
+ pnl_percent_price = ((current_price - entry_price) / entry_price) * 100
+ else:
+ pnl_percent_price = ((entry_price - current_price) / entry_price) * 100
+
+ # 更新最大盈利(基于保证金)
+ if pnl_percent_margin > position_info.get('maxProfit', 0):
+ position_info['maxProfit'] = pnl_percent_margin
+
+ # 移动止损逻辑(盈利后保护利润,基于保证金)
use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', True)
if use_trailing:
- trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01)
- trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01)
+ trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) # 相对于保证金
+ trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) # 相对于保证金
if not position_info.get('trailingStopActivated', False):
- # 盈利超过阈值后,激活移动止损
- if pnl_percent > trailing_activation * 100:
+ # 盈利超过阈值后(相对于保证金),激活移动止损
+ if pnl_percent_margin > trailing_activation * 100:
position_info['trailingStopActivated'] = True
# 将止损移至成本价(保本)
position_info['stopLoss'] = entry_price
logger.info(
f"{symbol} 移动止损激活: 止损移至成本价 {entry_price:.4f} "
- f"(盈利: {pnl_percent:.2f}%)"
+ f"(盈利: {pnl_percent_margin:.2f}% of margin)"
)
else:
- # 盈利超过2%后,止损移至保护利润位
- if pnl_percent > 2.0:
- if position_info['side'] == 'BUY':
- new_stop_loss = entry_price * (1 + trailing_protect)
- if new_stop_loss > position_info['stopLoss']:
- position_info['stopLoss'] = new_stop_loss
- logger.info(
- f"{symbol} 移动止损更新: {new_stop_loss:.4f} "
- f"(保护{trailing_protect*100:.1f}%利润)"
- )
- else:
- new_stop_loss = entry_price * (1 - trailing_protect)
- if new_stop_loss < position_info['stopLoss']:
- position_info['stopLoss'] = new_stop_loss
- logger.info(
- f"{symbol} 移动止损更新: {new_stop_loss:.4f} "
- f"(保护{trailing_protect*100:.1f}%利润)"
- )
+ # 盈利超过阈值后,止损移至保护利润位(基于保证金)
+ # 计算需要保护的利润金额
+ protect_amount = margin * trailing_protect
+ # 计算对应的止损价
+ if position_info['side'] == 'BUY':
+ # 保护利润:当前盈亏 - 保护金额 = (止损价 - 开仓价) × 数量
+ # 所以:止损价 = 开仓价 + (当前盈亏 - 保护金额) / 数量
+ new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity
+ if new_stop_loss > position_info['stopLoss']:
+ position_info['stopLoss'] = new_stop_loss
+ logger.info(
+ f"{symbol} 移动止损更新: {new_stop_loss:.4f} "
+ f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)"
+ )
+ else:
+ # 做空:止损价 = 开仓价 - (当前盈亏 - 保护金额) / 数量
+ new_stop_loss = entry_price - (pnl_amount - protect_amount) / quantity
+ if new_stop_loss < position_info['stopLoss']:
+ position_info['stopLoss'] = new_stop_loss
+ logger.info(
+ f"{symbol} 移动止损更新: {new_stop_loss:.4f} "
+ f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)"
+ )
# 检查止损(使用更新后的止损价)
stop_loss = position_info.get('stopLoss')
@@ -1064,9 +1084,21 @@ class PositionManager:
ticker = await self.client.get_ticker_24h(symbol)
current_price = ticker['price'] if ticker else entry_price
- # 计算止损止盈
- stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side)
- take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side)
+ # 计算止损止盈(基于保证金)
+ leverage = binance_position.get('leverage', 10)
+ stop_loss_pct_margin = self.risk_manager.config.get('STOP_LOSS_PERCENT', 0.03)
+ take_profit_pct_margin = self.risk_manager.config.get('TAKE_PROFIT_PERCENT', 0.05)
+ # 止盈为止损的2.5倍
+ take_profit_pct_margin = stop_loss_pct_margin * 2.5
+
+ stop_loss_price = self.risk_manager.get_stop_loss_price(
+ entry_price, side, quantity, leverage,
+ stop_loss_pct=stop_loss_pct_margin
+ )
+ take_profit_price = self.risk_manager.get_take_profit_price(
+ entry_price, side, quantity, leverage,
+ take_profit_pct=take_profit_pct_margin
+ )
position_info = {
'symbol': symbol,
@@ -1079,7 +1111,7 @@ class PositionManager:
'stopLoss': stop_loss_price,
'takeProfit': take_profit_price,
'initialStopLoss': stop_loss_price,
- 'leverage': binance_position.get('leverage', 10),
+ 'leverage': leverage,
'entryReason': 'manual_entry',
'atr': None,
'maxProfit': 0.0,
@@ -1146,8 +1178,19 @@ class PositionManager:
ticker = await self.client.get_ticker_24h(symbol)
current_price = ticker['price'] if ticker else entry_price
- stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side)
- take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side)
+ # 计算止损止盈(基于保证金)
+ leverage = position.get('leverage', 10)
+ stop_loss_pct_margin = self.risk_manager.config.get('STOP_LOSS_PERCENT', 0.03)
+ take_profit_pct_margin = stop_loss_pct_margin * 2.5
+
+ stop_loss_price = self.risk_manager.get_stop_loss_price(
+ entry_price, side, quantity, leverage,
+ stop_loss_pct=stop_loss_pct_margin
+ )
+ take_profit_price = self.risk_manager.get_take_profit_price(
+ entry_price, side, quantity, leverage,
+ take_profit_pct=take_profit_pct_margin
+ )
position_info = {
'symbol': symbol,
@@ -1160,7 +1203,7 @@ class PositionManager:
'stopLoss': stop_loss_price,
'takeProfit': take_profit_price,
'initialStopLoss': stop_loss_price,
- 'leverage': position.get('leverage', 10),
+ 'leverage': leverage,
'entryReason': 'manual_entry_temp',
'atr': None,
'maxProfit': 0.0,
@@ -1326,51 +1369,70 @@ class PositionManager:
quantity = float(position_info['quantity'])
current_price_float = float(current_price)
- # 计算当前盈亏
+ # 计算当前盈亏(基于保证金)
+ leverage = position_info.get('leverage', 10)
+ position_value = entry_price * quantity
+ margin = position_value / leverage if leverage > 0 else position_value
+
+ # 计算盈亏金额
if position_info['side'] == 'BUY':
- pnl_percent = ((current_price_float - entry_price) / entry_price) * 100
+ pnl_amount = (current_price_float - entry_price) * quantity
else: # SELL
- pnl_percent = ((entry_price - current_price_float) / entry_price) * 100
+ pnl_amount = (entry_price - current_price_float) * quantity
- # 更新最大盈利
- if pnl_percent > position_info.get('maxProfit', 0):
- position_info['maxProfit'] = pnl_percent
+ # 计算盈亏百分比(相对于保证金,与币安一致)
+ pnl_percent_margin = (pnl_amount / margin * 100) if margin > 0 else 0
- # 移动止损逻辑(盈利后保护利润)
+ # 也计算价格百分比(用于显示)
+ if position_info['side'] == 'BUY':
+ pnl_percent_price = ((current_price_float - entry_price) / entry_price) * 100
+ else: # SELL
+ pnl_percent_price = ((entry_price - current_price_float) / entry_price) * 100
+
+ # 更新最大盈利(基于保证金)
+ if pnl_percent_margin > position_info.get('maxProfit', 0):
+ position_info['maxProfit'] = pnl_percent_margin
+
+ # 移动止损逻辑(盈利后保护利润,基于保证金)
use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', True)
if use_trailing:
- trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01)
- trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01)
+ trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) # 相对于保证金
+ trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) # 相对于保证金
if not position_info.get('trailingStopActivated', False):
- # 盈利超过阈值后,激活移动止损
- if pnl_percent > trailing_activation * 100:
+ # 盈利超过阈值后(相对于保证金),激活移动止损
+ if pnl_percent_margin > trailing_activation * 100:
position_info['trailingStopActivated'] = True
# 将止损移至成本价(保本)
position_info['stopLoss'] = entry_price
logger.info(
f"{symbol} [实时监控] 移动止损激活: 止损移至成本价 {entry_price:.4f} "
- f"(盈利: {pnl_percent:.2f}%)"
+ f"(盈利: {pnl_percent_margin:.2f}% of margin)"
)
else:
- # 盈利超过2%后,止损移至保护利润位
- if pnl_percent > 2.0:
- if position_info['side'] == 'BUY':
- new_stop_loss = entry_price * (1 + trailing_protect)
- if new_stop_loss > position_info['stopLoss']:
- position_info['stopLoss'] = new_stop_loss
- logger.info(
- f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} "
- f"(保护{trailing_protect*100:.1f}%利润)"
- )
- else: # SELL
- new_stop_loss = entry_price * (1 - trailing_protect)
- if new_stop_loss < position_info['stopLoss']:
- position_info['stopLoss'] = new_stop_loss
- logger.info(
- f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} "
- f"(保护{trailing_protect*100:.1f}%利润)"
- )
+ # 盈利超过阈值后,止损移至保护利润位(基于保证金)
+ # 计算需要保护的利润金额
+ protect_amount = margin * trailing_protect
+ # 计算对应的止损价
+ if position_info['side'] == 'BUY':
+ # 保护利润:当前盈亏 - 保护金额 = (止损价 - 开仓价) × 数量
+ # 所以:止损价 = 开仓价 + (当前盈亏 - 保护金额) / 数量
+ new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity
+ if new_stop_loss > position_info['stopLoss']:
+ position_info['stopLoss'] = new_stop_loss
+ logger.info(
+ f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} "
+ f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)"
+ )
+ else: # SELL
+ # 做空:止损价 = 开仓价 - (当前盈亏 - 保护金额) / 数量
+ new_stop_loss = entry_price - (pnl_amount - protect_amount) / quantity
+ if new_stop_loss < position_info['stopLoss']:
+ position_info['stopLoss'] = new_stop_loss
+ logger.info(
+ f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} "
+ f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)"
+ )
# 检查止损
stop_loss = position_info['stopLoss']
diff --git a/trading_system/risk_manager.py b/trading_system/risk_manager.py
index 63a3eaa..e1bc81c 100644
--- a/trading_system/risk_manager.py
+++ b/trading_system/risk_manager.py
@@ -399,26 +399,48 @@ class RiskManager:
self,
entry_price: float,
side: str,
+ quantity: float,
+ leverage: int,
stop_loss_pct: Optional[float] = None,
klines: Optional[List] = None,
bollinger: Optional[Dict] = None,
atr: Optional[float] = None
) -> float:
"""
- 计算止损价格(基于支撑/阻力的动态止损)
+ 计算止损价格(基于保证金的盈亏金额)
Args:
entry_price: 入场价格
side: 方向 'BUY' 或 'SELL'
- stop_loss_pct: 止损百分比,如果为None则使用配置值
- klines: K线数据,用于计算支撑/阻力位
- bollinger: 布林带数据,用于计算动态止损
- atr: 平均真实波幅,用于计算动态止损
+ quantity: 持仓数量
+ leverage: 杠杆倍数
+ stop_loss_pct: 止损百分比(相对于保证金),如果为None则使用配置值
+ klines: K线数据,用于计算支撑/阻力位(作为辅助参考)
+ bollinger: 布林带数据,用于计算动态止损(作为辅助参考)
+ atr: 平均真实波幅,用于计算动态止损(作为辅助参考)
Returns:
止损价格
"""
- # 优先使用基于技术结构的动态止损
+ # 计算保证金和仓位价值
+ position_value = entry_price * quantity
+ margin = position_value / leverage if leverage > 0 else position_value
+
+ # 获取止损百分比(相对于保证金)
+ stop_loss_percent = stop_loss_pct or self.config['STOP_LOSS_PERCENT']
+
+ # 计算止损金额(相对于保证金)
+ stop_loss_amount = margin * stop_loss_percent
+
+ # 计算止损价(基于止损金额)
+ # 止损金额 = (开仓价 - 止损价) × 数量
+ # 所以:止损价 = 开仓价 - (止损金额 / 数量)
+ if side == 'BUY': # 做多,止损价低于入场价
+ stop_loss_price = entry_price - (stop_loss_amount / quantity)
+ else: # 做空,止损价高于入场价
+ stop_loss_price = entry_price + (stop_loss_amount / quantity)
+
+ # 如果提供了技术分析数据,可以调整止损价(但不能超过基于保证金的止损范围)
if klines and len(klines) >= 10:
# 计算支撑/阻力位
low_prices = [float(k[3]) for k in klines[-20:]] # 最近20根K线的最低价
@@ -427,85 +449,115 @@ class RiskManager:
if side == 'BUY': # 做多,止损放在支撑位下方
# 找到近期波段低点
recent_low = min(low_prices)
- # 止损放在低点下方0.5-1%
+ # 止损放在低点下方0.5%
buffer = entry_price * 0.005 # 0.5%缓冲
- dynamic_stop = recent_low - buffer
+ technical_stop = recent_low - buffer
# 如果布林带可用,也可以考虑布林带下轨
if bollinger and bollinger.get('lower'):
bollinger_stop = bollinger['lower'] * 0.995 # 布林带下轨下方0.5%
- dynamic_stop = max(dynamic_stop, bollinger_stop)
+ technical_stop = max(technical_stop, bollinger_stop)
- # 确保止损不超过固定止损范围(1%-5%)
- fixed_stop_pct = stop_loss_pct or self.config['STOP_LOSS_PERCENT']
- max_stop = entry_price * (1 - 0.01) # 最多1%止损
- min_stop = entry_price * (1 - 0.05) # 最少5%止损(极端情况)
-
- dynamic_stop = max(min_stop, min(max_stop, dynamic_stop))
-
- logger.info(
- f"动态止损计算 (BUY): 入场价={entry_price:.4f}, "
- f"近期低点={recent_low:.4f}, 动态止损={dynamic_stop:.4f}, "
- f"止损比例={((entry_price - dynamic_stop) / entry_price * 100):.2f}%"
- )
- return dynamic_stop
+ # 使用技术止损,但不能超过基于保证金的止损范围(允许更紧的止损)
+ # 如果技术止损更紧(价格更低),使用技术止损;否则使用基于保证金的止损
+ if technical_stop < stop_loss_price:
+ # 技术止损更紧,使用技术止损
+ logger.info(
+ f"动态止损计算 (BUY): 使用技术止损 {technical_stop:.4f} "
+ f"(基于保证金的止损: {stop_loss_price:.4f}, 止损金额: {stop_loss_amount:.2f} USDT)"
+ )
+ return technical_stop
+ else:
+ logger.info(
+ f"动态止损计算 (BUY): 使用基于保证金的止损 {stop_loss_price:.4f} "
+ f"(技术止损: {technical_stop:.4f}, 止损金额: {stop_loss_amount:.2f} USDT, "
+ f"止损比例: {stop_loss_percent*100:.1f}% of margin)"
+ )
+ return stop_loss_price
else: # 做空,止损放在阻力位上方
# 找到近期波段高点
recent_high = max(high_prices)
- # 止损放在高点上方0.5-1%
+ # 止损放在高点上方0.5%
buffer = entry_price * 0.005 # 0.5%缓冲
- dynamic_stop = recent_high + buffer
+ technical_stop = recent_high + buffer
# 如果布林带可用,也可以考虑布林带上轨
if bollinger and bollinger.get('upper'):
bollinger_stop = bollinger['upper'] * 1.005 # 布林带上轨上方0.5%
- dynamic_stop = min(dynamic_stop, bollinger_stop)
+ technical_stop = min(technical_stop, bollinger_stop)
- # 确保止损不超过固定止损范围(1%-5%)
- fixed_stop_pct = stop_loss_pct or self.config['STOP_LOSS_PERCENT']
- min_stop = entry_price * (1 + 0.01) # 最少1%止损
- max_stop = entry_price * (1 + 0.05) # 最多5%止损(极端情况)
-
- dynamic_stop = min(max_stop, max(min_stop, dynamic_stop))
-
- logger.info(
- f"动态止损计算 (SELL): 入场价={entry_price:.4f}, "
- f"近期高点={recent_high:.4f}, 动态止损={dynamic_stop:.4f}, "
- f"止损比例={((dynamic_stop - entry_price) / entry_price * 100):.2f}%"
- )
- return dynamic_stop
+ # 使用技术止损,但不能超过基于保证金的止损范围(允许更紧的止损)
+ # 如果技术止损更紧(价格更高),使用技术止损;否则使用基于保证金的止损
+ if technical_stop > stop_loss_price:
+ # 技术止损更紧,使用技术止损
+ logger.info(
+ f"动态止损计算 (SELL): 使用技术止损 {technical_stop:.4f} "
+ f"(基于保证金的止损: {stop_loss_price:.4f}, 止损金额: {stop_loss_amount:.2f} USDT)"
+ )
+ return technical_stop
+ else:
+ logger.info(
+ f"动态止损计算 (SELL): 使用基于保证金的止损 {stop_loss_price:.4f} "
+ f"(技术止损: {technical_stop:.4f}, 止损金额: {stop_loss_amount:.2f} USDT, "
+ f"止损比例: {stop_loss_percent*100:.1f}% of margin)"
+ )
+ return stop_loss_price
- # 回退到固定百分比止损
- stop_loss_percent = stop_loss_pct or self.config['STOP_LOSS_PERCENT']
-
- if side == 'BUY': # 做多,止损价低于入场价
- return entry_price * (1 - stop_loss_percent)
- else: # 做空,止损价高于入场价
- return entry_price * (1 + stop_loss_percent)
+ # 没有技术分析数据,直接使用基于保证金的止损
+ logger.info(
+ f"止损计算 ({side}): 入场价={entry_price:.4f}, 数量={quantity:.4f}, "
+ f"杠杆={leverage}x, 保证金={margin:.4f} USDT, "
+ f"止损金额={stop_loss_amount:.4f} USDT ({stop_loss_percent*100:.1f}% of margin), "
+ f"止损价={stop_loss_price:.4f}"
+ )
+ return stop_loss_price
def get_take_profit_price(
self,
entry_price: float,
- side: str,
+ side: str,
+ quantity: float,
+ leverage: int,
take_profit_pct: Optional[float] = None
) -> float:
"""
- 计算止盈价格
+ 计算止盈价格(基于保证金的盈亏金额)
Args:
entry_price: 入场价格
side: 方向 'BUY' 或 'SELL'
- take_profit_pct: 止盈百分比,如果为None则使用配置值
+ quantity: 持仓数量
+ leverage: 杠杆倍数
+ take_profit_pct: 止盈百分比(相对于保证金),如果为None则使用配置值
Returns:
止盈价格
"""
+ # 计算保证金和仓位价值
+ position_value = entry_price * quantity
+ margin = position_value / leverage if leverage > 0 else position_value
+
+ # 获取止盈百分比(相对于保证金)
take_profit_percent = take_profit_pct or self.config['TAKE_PROFIT_PERCENT']
+ # 计算止盈金额(相对于保证金)
+ take_profit_amount = margin * take_profit_percent
+
+ # 计算止盈价(基于止盈金额)
+ # 止盈金额 = (止盈价 - 开仓价) × 数量
+ # 所以:止盈价 = 开仓价 + (止盈金额 / 数量)
if side == 'BUY': # 做多,止盈价高于入场价
- return entry_price * (1 + take_profit_percent)
+ take_profit_price = entry_price + (take_profit_amount / quantity)
else: # 做空,止盈价低于入场价
- return entry_price * (1 - take_profit_percent)
+ take_profit_price = entry_price - (take_profit_amount / quantity)
+
+ logger.info(
+ f"止盈计算 ({side}): 入场价={entry_price:.4f}, 数量={quantity:.4f}, "
+ f"杠杆={leverage}x, 保证金={margin:.4f} USDT, "
+ f"止盈金额={take_profit_amount:.4f} USDT ({take_profit_percent*100:.1f}% of margin), "
+ f"止盈价={take_profit_price:.4f}"
+ )
+ return take_profit_price
async def calculate_dynamic_leverage(self, signal_strength: int, symbol: str = None) -> int:
"""
diff --git a/trading_system/trade_recommender.py b/trading_system/trade_recommender.py
index 7066e65..49e8c76 100644
--- a/trading_system/trade_recommender.py
+++ b/trading_system/trade_recommender.py
@@ -358,32 +358,54 @@ class TradeRecommender:
current_price = symbol_info['price']
direction = trade_signal['direction']
- # 计算建议的止损止盈
+ # 计算建议的止损止盈(基于保证金)
entry_price = current_price
+
+ # 估算仓位数量和杠杆(用于计算止损止盈)
+ # 使用建议的仓位比例和账户余额来估算
+ account_balance = symbol_info.get('account_balance', 1000)
+ suggested_position_pct = symbol_info.get('suggested_position_pct', 0.05)
+ leverage = config.TRADING_CONFIG.get('LEVERAGE', 10)
+
+ # 估算仓位价值
+ estimated_position_value = account_balance * suggested_position_pct
+ estimated_quantity = estimated_position_value / entry_price if entry_price > 0 else 0
+
+ # 计算基于保证金的止损止盈
+ stop_loss_pct_margin = config.TRADING_CONFIG.get('STOP_LOSS_PERCENT', 0.03)
+ take_profit_pct_margin = config.TRADING_CONFIG.get('TAKE_PROFIT_PERCENT', 0.05)
+
stop_loss_price = self.risk_manager.get_stop_loss_price(
entry_price,
direction,
+ estimated_quantity,
+ leverage,
+ stop_loss_pct=stop_loss_pct_margin,
klines=symbol_info.get('klines'),
bollinger=symbol_info.get('bollinger'),
atr=symbol_info.get('atr')
)
- # 计算止损百分比
+ # 计算止损百分比(相对于保证金,用于显示)
+ estimated_margin = estimated_position_value / leverage if leverage > 0 else estimated_position_value
+ stop_loss_amount = estimated_margin * stop_loss_pct_margin
if direction == 'BUY':
stop_loss_pct = (entry_price - stop_loss_price) / entry_price
else:
stop_loss_pct = (stop_loss_price - entry_price) / entry_price
- # 第一目标:盈亏比1:1
- if direction == 'BUY':
- take_profit_1 = entry_price + (entry_price - stop_loss_price)
- else:
- take_profit_1 = entry_price - (stop_loss_price - entry_price)
+ # 第一目标:盈亏比1:1(相对于保证金)
+ take_profit_1_pct_margin = stop_loss_pct_margin * 1.0 # 1:1 盈亏比
+ take_profit_1 = self.risk_manager.get_take_profit_price(
+ entry_price, direction, estimated_quantity, leverage,
+ take_profit_pct=take_profit_1_pct_margin
+ )
- # 第二目标:止损的2.5倍
- take_profit_2_pct = stop_loss_pct * 2.5
+ # 第二目标:止损的2.5倍(相对于保证金)
+ take_profit_2_pct_margin = stop_loss_pct_margin * 2.5
take_profit_2 = self.risk_manager.get_take_profit_price(
- entry_price, direction, take_profit_pct=take_profit_2_pct
+ entry_price, direction, estimated_quantity, leverage,
+ take_profit_pct=take_profit_2_pct_margin
)
# 建议仓位(根据信号强度调整)
@@ -421,11 +443,15 @@ class TradeRecommender:
base_win_rate -= 2
estimated_win_rate = max(35, min(75, base_win_rate))
- # 计算盈亏USDT评估
- position_value = account_balance * suggested_position_pct * config.TRADING_CONFIG.get('LEVERAGE', 10)
- stop_loss_usdt = position_value * stop_loss_pct
- take_profit_1_usdt = position_value * stop_loss_pct
- take_profit_2_usdt = position_value * take_profit_2_pct
+ # 计算盈亏USDT评估(基于保证金)
+ position_value = account_balance * suggested_position_pct
+ margin = position_value / leverage if leverage > 0 else position_value
+
+ # 止损止盈金额(相对于保证金)
+ stop_loss_usdt = margin * stop_loss_pct_margin
+ take_profit_1_usdt = margin * take_profit_1_pct_margin
+ take_profit_2_usdt = margin * take_profit_2_pct_margin
+
expected_pnl_1 = (estimated_win_rate / 100) * take_profit_1_usdt - ((100 - estimated_win_rate) / 100) * stop_loss_usdt
expected_pnl_2 = (estimated_win_rate / 100) * take_profit_2_usdt - ((100 - estimated_win_rate) / 100) * stop_loss_usdt