This commit is contained in:
薇薇安 2026-01-16 23:55:02 +08:00
parent cb33556909
commit c2a78f1f7d
4 changed files with 339 additions and 174 deletions

View File

@ -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 configSource = dashboardData?.trading_config || tradingConfig
const stopLossConfig = configSource?.STOP_LOSS_PERCENT const stopLossConfig = configSource?.STOP_LOSS_PERCENT
const takeProfitConfig = configSource?.TAKE_PROFIT_PERCENT const takeProfitConfig = configSource?.TAKE_PROFIT_PERCENT
// 0.033% // 0.033%
let defaultStopLossPercent = 3.0 // 3% let stopLossPercentMargin = 0.03 // 3%
let defaultTakeProfitPercent = 5.0 // 5% let takeProfitPercentMargin = 0.05 // 5%
if (stopLossConfig) { if (stopLossConfig) {
const configValue = stopLossConfig.value const configValue = stopLossConfig.value
if (typeof configValue === 'number') { if (typeof configValue === 'number') {
defaultStopLossPercent = configValue * 100 stopLossPercentMargin = configValue
} else { } else {
const parsed = parseFloat(configValue) const parsed = parseFloat(configValue)
if (!isNaN(parsed)) { if (!isNaN(parsed)) {
defaultStopLossPercent = parsed * 100 stopLossPercentMargin = parsed
} }
} }
} }
@ -225,32 +233,45 @@ const StatsDashboard = () => {
if (takeProfitConfig) { if (takeProfitConfig) {
const configValue = takeProfitConfig.value const configValue = takeProfitConfig.value
if (typeof configValue === 'number') { if (typeof configValue === 'number') {
defaultTakeProfitPercent = configValue * 100 takeProfitPercentMargin = configValue
} else { } else {
const parsed = parseFloat(configValue) const parsed = parseFloat(configValue)
if (!isNaN(parsed)) { if (!isNaN(parsed)) {
defaultTakeProfitPercent = parsed * 100 takeProfitPercentMargin = parsed
} }
} }
} }
// //
const stopLossAmount = margin * stopLossPercentMargin
const takeProfitAmount = margin * takeProfitPercentMargin
//
// = ( - ) × ( - ) ×
let stopLossPrice = 0 let stopLossPrice = 0
let takeProfitPrice = 0 let takeProfitPrice = 0
if (side === 'BUY') { if (side === 'BUY') {
stopLossPrice = entryPrice * (1 - defaultStopLossPercent / 100) // = - ( / )
takeProfitPrice = entryPrice * (1 + defaultTakeProfitPercent / 100) stopLossPrice = entryPrice - (stopLossAmount / quantity)
// = + ( / )
takeProfitPrice = entryPrice + (takeProfitAmount / quantity)
} else { } 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 ? ((entryPrice - stopLossPrice) / entryPrice) * 100
: ((stopLossPrice - entryPrice) / entryPrice) * 100 : ((stopLossPrice - entryPrice) / entryPrice) * 100
const takeProfitPercent = side === 'BUY' const takeProfitPercentPrice = side === 'BUY'
? ((takeProfitPrice - entryPrice) / entryPrice) * 100 ? ((takeProfitPrice - entryPrice) / entryPrice) * 100
: ((entryPrice - takeProfitPrice) / entryPrice) * 100 : ((entryPrice - takeProfitPrice) / entryPrice) * 100
@ -303,12 +324,16 @@ const StatsDashboard = () => {
</div> </div>
<div className="stop-take-info"> <div className="stop-take-info">
<div className="stop-loss-info"> <div className="stop-loss-info">
止损比例: <span className="negative">-{stopLossPercent.toFixed(2)}%</span> 止损: <span className="negative">-{stopLossPercent.toFixed(2)}%</span>
<span className="stop-note">(of margin)</span>
<span className="stop-price">(: {stopLossPrice.toFixed(4)})</span> <span className="stop-price">(: {stopLossPrice.toFixed(4)})</span>
<span className="stop-amount">(金额: -{stopLossAmount.toFixed(2)} USDT)</span>
</div> </div>
<div className="take-profit-info"> <div className="take-profit-info">
止盈比例: <span className="positive">+{takeProfitPercent.toFixed(2)}%</span> 止盈: <span className="positive">+{takeProfitPercent.toFixed(2)}%</span>
<span className="take-note">(of margin)</span>
<span className="take-price">(: {takeProfitPrice.toFixed(4)})</span> <span className="take-price">(: {takeProfitPrice.toFixed(4)})</span>
<span className="take-amount">(金额: +{takeProfitAmount.toFixed(2)} USDT)</span>
</div> </div>
</div> </div>
<div className="trade-actions"> <div className="trade-actions">

View File

@ -145,30 +145,31 @@ class PositionManager:
logger.debug(f"获取K线数据失败使用固定止损: {e}") logger.debug(f"获取K线数据失败使用固定止损: {e}")
klines = None klines = None
# 使用基于支撑/阻力的动态止损 # 使用基于保证金的止损止盈(结合技术分析)
if klines or bollinger or atr: # 计算保证金和仓位价值
stop_loss_price = self.risk_manager.get_stop_loss_price( position_value = entry_price * quantity
entry_price, side, margin = position_value / leverage if leverage > 0 else position_value
klines=klines,
bollinger=bollinger, # 获取止损止盈百分比(相对于保证金)
atr=atr 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)
# 计算动态止损百分比(用于计算止盈) # 计算基于保证金的止损止盈
if side == 'BUY': stop_loss_price = self.risk_manager.get_stop_loss_price(
stop_loss_pct = (entry_price - stop_loss_price) / entry_price entry_price, side, quantity, leverage,
else: stop_loss_pct=stop_loss_pct_margin,
stop_loss_pct = (stop_loss_price - entry_price) / entry_price klines=klines,
bollinger=bollinger,
# 止盈为止损的2-2.5倍(提高盈亏比) atr=atr
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 # 计算止盈(基于保证金,为止损的倍数)
) # 如果止损是保证金的3%止盈可以是保证金的7.5%2.5倍)
else: take_profit_pct_margin = stop_loss_pct_margin * 2.5
# 回退到固定止损止盈 take_profit_price = self.risk_manager.get_take_profit_price(
stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side) entry_price, side, quantity, leverage,
take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side) take_profit_pct=take_profit_pct_margin
)
# 下单 # 下单
order = await self.client.place_order( order = await self.client.place_order(
@ -598,51 +599,70 @@ class PositionManager:
else: else:
current_price = entry_price 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': if position_info['side'] == 'BUY':
pnl_percent = ((current_price - entry_price) / entry_price) * 100 pnl_amount = (current_price - entry_price) * quantity
else: 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): pnl_percent_margin = (pnl_amount / margin * 100) if margin > 0 else 0
position_info['maxProfit'] = pnl_percent
# 移动止损逻辑(盈利后保护利润) # 也计算价格百分比(用于显示和移动止损)
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) use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', True)
if use_trailing: if use_trailing:
trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) # 相对于保证金
trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) # 相对于保证金
if not position_info.get('trailingStopActivated', False): 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['trailingStopActivated'] = True
# 将止损移至成本价(保本) # 将止损移至成本价(保本)
position_info['stopLoss'] = entry_price position_info['stopLoss'] = entry_price
logger.info( logger.info(
f"{symbol} 移动止损激活: 止损移至成本价 {entry_price:.4f} " f"{symbol} 移动止损激活: 止损移至成本价 {entry_price:.4f} "
f"(盈利: {pnl_percent:.2f}%)" f"(盈利: {pnl_percent_margin:.2f}% of margin)"
) )
else: else:
# 盈利超过2%后,止损移至保护利润位 # 盈利超过阈值后,止损移至保护利润位(基于保证金)
if pnl_percent > 2.0: # 计算需要保护的利润金额
if position_info['side'] == 'BUY': protect_amount = margin * trailing_protect
new_stop_loss = entry_price * (1 + trailing_protect) # 计算对应的止损价
if new_stop_loss > position_info['stopLoss']: if position_info['side'] == 'BUY':
position_info['stopLoss'] = new_stop_loss # 保护利润:当前盈亏 - 保护金额 = (止损价 - 开仓价) × 数量
logger.info( # 所以:止损价 = 开仓价 + (当前盈亏 - 保护金额) / 数量
f"{symbol} 移动止损更新: {new_stop_loss:.4f} " new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity
f"(保护{trailing_protect*100:.1f}%利润)" if new_stop_loss > position_info['stopLoss']:
) position_info['stopLoss'] = new_stop_loss
else: logger.info(
new_stop_loss = entry_price * (1 - trailing_protect) f"{symbol} 移动止损更新: {new_stop_loss:.4f} "
if new_stop_loss < position_info['stopLoss']: f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)"
position_info['stopLoss'] = new_stop_loss )
logger.info( else:
f"{symbol} 移动止损更新: {new_stop_loss:.4f} " # 做空:止损价 = 开仓价 - (当前盈亏 - 保护金额) / 数量
f"(保护{trailing_protect*100:.1f}%利润)" 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') stop_loss = position_info.get('stopLoss')
@ -1064,9 +1084,21 @@ class PositionManager:
ticker = await self.client.get_ticker_24h(symbol) ticker = await self.client.get_ticker_24h(symbol)
current_price = ticker['price'] if ticker else entry_price current_price = ticker['price'] if ticker else entry_price
# 计算止损止盈 # 计算止损止盈(基于保证金)
stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side) leverage = binance_position.get('leverage', 10)
take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side) 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 = { position_info = {
'symbol': symbol, 'symbol': symbol,
@ -1079,7 +1111,7 @@ class PositionManager:
'stopLoss': stop_loss_price, 'stopLoss': stop_loss_price,
'takeProfit': take_profit_price, 'takeProfit': take_profit_price,
'initialStopLoss': stop_loss_price, 'initialStopLoss': stop_loss_price,
'leverage': binance_position.get('leverage', 10), 'leverage': leverage,
'entryReason': 'manual_entry', 'entryReason': 'manual_entry',
'atr': None, 'atr': None,
'maxProfit': 0.0, 'maxProfit': 0.0,
@ -1146,8 +1178,19 @@ class PositionManager:
ticker = await self.client.get_ticker_24h(symbol) ticker = await self.client.get_ticker_24h(symbol)
current_price = ticker['price'] if ticker else entry_price 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 = { position_info = {
'symbol': symbol, 'symbol': symbol,
@ -1160,7 +1203,7 @@ class PositionManager:
'stopLoss': stop_loss_price, 'stopLoss': stop_loss_price,
'takeProfit': take_profit_price, 'takeProfit': take_profit_price,
'initialStopLoss': stop_loss_price, 'initialStopLoss': stop_loss_price,
'leverage': position.get('leverage', 10), 'leverage': leverage,
'entryReason': 'manual_entry_temp', 'entryReason': 'manual_entry_temp',
'atr': None, 'atr': None,
'maxProfit': 0.0, 'maxProfit': 0.0,
@ -1326,51 +1369,70 @@ class PositionManager:
quantity = float(position_info['quantity']) quantity = float(position_info['quantity'])
current_price_float = float(current_price) 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': 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 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): pnl_percent_margin = (pnl_amount / margin * 100) if margin > 0 else 0
position_info['maxProfit'] = pnl_percent
# 移动止损逻辑(盈利后保护利润) # 也计算价格百分比(用于显示)
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) use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', True)
if use_trailing: if use_trailing:
trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) trailing_activation = config.TRADING_CONFIG.get('TRAILING_STOP_ACTIVATION', 0.01) # 相对于保证金
trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) trailing_protect = config.TRADING_CONFIG.get('TRAILING_STOP_PROTECT', 0.01) # 相对于保证金
if not position_info.get('trailingStopActivated', False): 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['trailingStopActivated'] = True
# 将止损移至成本价(保本) # 将止损移至成本价(保本)
position_info['stopLoss'] = entry_price position_info['stopLoss'] = entry_price
logger.info( logger.info(
f"{symbol} [实时监控] 移动止损激活: 止损移至成本价 {entry_price:.4f} " f"{symbol} [实时监控] 移动止损激活: 止损移至成本价 {entry_price:.4f} "
f"(盈利: {pnl_percent:.2f}%)" f"(盈利: {pnl_percent_margin:.2f}% of margin)"
) )
else: else:
# 盈利超过2%后,止损移至保护利润位 # 盈利超过阈值后,止损移至保护利润位(基于保证金)
if pnl_percent > 2.0: # 计算需要保护的利润金额
if position_info['side'] == 'BUY': protect_amount = margin * trailing_protect
new_stop_loss = entry_price * (1 + trailing_protect) # 计算对应的止损价
if new_stop_loss > position_info['stopLoss']: if position_info['side'] == 'BUY':
position_info['stopLoss'] = new_stop_loss # 保护利润:当前盈亏 - 保护金额 = (止损价 - 开仓价) × 数量
logger.info( # 所以:止损价 = 开仓价 + (当前盈亏 - 保护金额) / 数量
f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} " new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity
f"(保护{trailing_protect*100:.1f}%利润)" if new_stop_loss > position_info['stopLoss']:
) position_info['stopLoss'] = new_stop_loss
else: # SELL logger.info(
new_stop_loss = entry_price * (1 - trailing_protect) f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} "
if new_stop_loss < position_info['stopLoss']: f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)"
position_info['stopLoss'] = new_stop_loss )
logger.info( else: # SELL
f"{symbol} [实时监控] 移动止损更新: {new_stop_loss:.4f} " # 做空:止损价 = 开仓价 - (当前盈亏 - 保护金额) / 数量
f"(保护{trailing_protect*100:.1f}%利润)" 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'] stop_loss = position_info['stopLoss']

View File

@ -399,26 +399,48 @@ class RiskManager:
self, self,
entry_price: float, entry_price: float,
side: str, side: str,
quantity: float,
leverage: int,
stop_loss_pct: Optional[float] = None, stop_loss_pct: Optional[float] = None,
klines: Optional[List] = None, klines: Optional[List] = None,
bollinger: Optional[Dict] = None, bollinger: Optional[Dict] = None,
atr: Optional[float] = None atr: Optional[float] = None
) -> float: ) -> float:
""" """
计算止损价格基于支撑/阻力的动态止损 计算止损价格基于保证金的盈亏金额
Args: Args:
entry_price: 入场价格 entry_price: 入场价格
side: 方向 'BUY' 'SELL' side: 方向 'BUY' 'SELL'
stop_loss_pct: 止损百分比如果为None则使用配置值 quantity: 持仓数量
klines: K线数据用于计算支撑/阻力位 leverage: 杠杆倍数
bollinger: 布林带数据用于计算动态止损 stop_loss_pct: 止损百分比相对于保证金如果为None则使用配置值
atr: 平均真实波幅用于计算动态止损 klines: K线数据用于计算支撑/阻力位作为辅助参考
bollinger: 布林带数据用于计算动态止损作为辅助参考
atr: 平均真实波幅用于计算动态止损作为辅助参考
Returns: 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: if klines and len(klines) >= 10:
# 计算支撑/阻力位 # 计算支撑/阻力位
low_prices = [float(k[3]) for k in klines[-20:]] # 最近20根K线的最低价 low_prices = [float(k[3]) for k in klines[-20:]] # 最近20根K线的最低价
@ -427,85 +449,115 @@ class RiskManager:
if side == 'BUY': # 做多,止损放在支撑位下方 if side == 'BUY': # 做多,止损放在支撑位下方
# 找到近期波段低点 # 找到近期波段低点
recent_low = min(low_prices) recent_low = min(low_prices)
# 止损放在低点下方0.5-1% # 止损放在低点下方0.5%
buffer = entry_price * 0.005 # 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'): if bollinger and bollinger.get('lower'):
bollinger_stop = bollinger['lower'] * 0.995 # 布林带下轨下方0.5% 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%止损 if technical_stop < stop_loss_price:
min_stop = entry_price * (1 - 0.05) # 最少5%止损(极端情况) # 技术止损更紧,使用技术止损
logger.info(
dynamic_stop = max(min_stop, min(max_stop, dynamic_stop)) f"动态止损计算 (BUY): 使用技术止损 {technical_stop:.4f} "
f"(基于保证金的止损: {stop_loss_price:.4f}, 止损金额: {stop_loss_amount:.2f} USDT)"
logger.info( )
f"动态止损计算 (BUY): 入场价={entry_price:.4f}, " return technical_stop
f"近期低点={recent_low:.4f}, 动态止损={dynamic_stop:.4f}, " else:
f"止损比例={((entry_price - dynamic_stop) / entry_price * 100):.2f}%" logger.info(
) f"动态止损计算 (BUY): 使用基于保证金的止损 {stop_loss_price:.4f} "
return dynamic_stop f"(技术止损: {technical_stop:.4f}, 止损金额: {stop_loss_amount:.2f} USDT, "
f"止损比例: {stop_loss_percent*100:.1f}% of margin)"
)
return stop_loss_price
else: # 做空,止损放在阻力位上方 else: # 做空,止损放在阻力位上方
# 找到近期波段高点 # 找到近期波段高点
recent_high = max(high_prices) recent_high = max(high_prices)
# 止损放在高点上方0.5-1% # 止损放在高点上方0.5%
buffer = entry_price * 0.005 # 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'): if bollinger and bollinger.get('upper'):
bollinger_stop = bollinger['upper'] * 1.005 # 布林带上轨上方0.5% 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%止损 if technical_stop > stop_loss_price:
max_stop = entry_price * (1 + 0.05) # 最多5%止损(极端情况) # 技术止损更紧,使用技术止损
logger.info(
dynamic_stop = min(max_stop, max(min_stop, dynamic_stop)) f"动态止损计算 (SELL): 使用技术止损 {technical_stop:.4f} "
f"(基于保证金的止损: {stop_loss_price:.4f}, 止损金额: {stop_loss_amount:.2f} USDT)"
logger.info( )
f"动态止损计算 (SELL): 入场价={entry_price:.4f}, " return technical_stop
f"近期高点={recent_high:.4f}, 动态止损={dynamic_stop:.4f}, " else:
f"止损比例={((dynamic_stop - entry_price) / entry_price * 100):.2f}%" logger.info(
) f"动态止损计算 (SELL): 使用基于保证金的止损 {stop_loss_price:.4f} "
return dynamic_stop 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'] logger.info(
f"止损计算 ({side}): 入场价={entry_price:.4f}, 数量={quantity:.4f}, "
if side == 'BUY': # 做多,止损价低于入场价 f"杠杆={leverage}x, 保证金={margin:.4f} USDT, "
return entry_price * (1 - stop_loss_percent) f"止损金额={stop_loss_amount:.4f} USDT ({stop_loss_percent*100:.1f}% of margin), "
else: # 做空,止损价高于入场价 f"止损价={stop_loss_price:.4f}"
return entry_price * (1 + stop_loss_percent) )
return stop_loss_price
def get_take_profit_price( def get_take_profit_price(
self, self,
entry_price: float, entry_price: float,
side: str, side: str,
quantity: float,
leverage: int,
take_profit_pct: Optional[float] = None take_profit_pct: Optional[float] = None
) -> float: ) -> float:
""" """
计算止盈价格 计算止盈价格基于保证金的盈亏金额
Args: Args:
entry_price: 入场价格 entry_price: 入场价格
side: 方向 'BUY' 'SELL' side: 方向 'BUY' 'SELL'
take_profit_pct: 止盈百分比如果为None则使用配置值 quantity: 持仓数量
leverage: 杠杆倍数
take_profit_pct: 止盈百分比相对于保证金如果为None则使用配置值
Returns: 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_percent = take_profit_pct or self.config['TAKE_PROFIT_PERCENT']
# 计算止盈金额(相对于保证金)
take_profit_amount = margin * take_profit_percent
# 计算止盈价(基于止盈金额)
# 止盈金额 = (止盈价 - 开仓价) × 数量
# 所以:止盈价 = 开仓价 + (止盈金额 / 数量)
if side == 'BUY': # 做多,止盈价高于入场价 if side == 'BUY': # 做多,止盈价高于入场价
return entry_price * (1 + take_profit_percent) take_profit_price = entry_price + (take_profit_amount / quantity)
else: # 做空,止盈价低于入场价 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: async def calculate_dynamic_leverage(self, signal_strength: int, symbol: str = None) -> int:
""" """

View File

@ -358,32 +358,54 @@ class TradeRecommender:
current_price = symbol_info['price'] current_price = symbol_info['price']
direction = trade_signal['direction'] direction = trade_signal['direction']
# 计算建议的止损止盈 # 计算建议的止损止盈(基于保证金)
entry_price = current_price 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( stop_loss_price = self.risk_manager.get_stop_loss_price(
entry_price, entry_price,
direction, direction,
estimated_quantity,
leverage,
stop_loss_pct=stop_loss_pct_margin,
klines=symbol_info.get('klines'), klines=symbol_info.get('klines'),
bollinger=symbol_info.get('bollinger'), bollinger=symbol_info.get('bollinger'),
atr=symbol_info.get('atr') 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': if direction == 'BUY':
stop_loss_pct = (entry_price - stop_loss_price) / entry_price stop_loss_pct = (entry_price - stop_loss_price) / entry_price
else: else:
stop_loss_pct = (stop_loss_price - entry_price) / entry_price stop_loss_pct = (stop_loss_price - entry_price) / entry_price
# 第一目标盈亏比1:1 # 第一目标盈亏比1:1相对于保证金
if direction == 'BUY': take_profit_1_pct_margin = stop_loss_pct_margin * 1.0 # 1:1 盈亏比
take_profit_1 = entry_price + (entry_price - stop_loss_price) take_profit_1 = self.risk_manager.get_take_profit_price(
else: entry_price, direction, estimated_quantity, leverage,
take_profit_1 = entry_price - (stop_loss_price - entry_price) take_profit_pct=take_profit_1_pct_margin
)
# 第二目标止损的2.5倍 # 第二目标止损的2.5倍(相对于保证金)
take_profit_2_pct = stop_loss_pct * 2.5 take_profit_2_pct_margin = stop_loss_pct_margin * 2.5
take_profit_2 = self.risk_manager.get_take_profit_price( 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 base_win_rate -= 2
estimated_win_rate = max(35, min(75, base_win_rate)) estimated_win_rate = max(35, min(75, base_win_rate))
# 计算盈亏USDT评估 # 计算盈亏USDT评估基于保证金
position_value = account_balance * suggested_position_pct * config.TRADING_CONFIG.get('LEVERAGE', 10) position_value = account_balance * suggested_position_pct
stop_loss_usdt = position_value * stop_loss_pct margin = position_value / leverage if leverage > 0 else position_value
take_profit_1_usdt = position_value * stop_loss_pct
take_profit_2_usdt = position_value * take_profit_2_pct # 止损止盈金额(相对于保证金)
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_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 expected_pnl_2 = (estimated_win_rate / 100) * take_profit_2_usdt - ((100 - estimated_win_rate) / 100) * stop_loss_usdt