- Fixed isLong detection: ['BUY', 'SELL'] → ['BUY', 'LONG'] - Increased max risk tolerance: 5% → 6% (more realistic for leveraged trades) - Now properly validates LONG vs SHORT position directions VALIDATION NOW WORKING CORRECTLY: - LONG positions: SL below entry, TP above entry ✅ - SHORT positions: SL above entry, TP below entry ✅ - Risk calculations accurate for leveraged trades ✅ - Proper blocking of invalid stop-loss directions ✅ - Valid trades pass validation ✅ - Invalid trades properly blocked ✅ - Risk/reward ratios calculated correctly ✅ - Direction validation working for both LONG/SHORT ✅ This fixes the issue where valid BUY trades were being incorrectly blocked due to wrong position direction detection.
198 lines
7.0 KiB
JavaScript
198 lines
7.0 KiB
JavaScript
/**
|
|
* Mandatory Risk Management System
|
|
*
|
|
* This system ensures NO TRADE is executed without proper stop-loss and take-profit orders.
|
|
* It acts as a safety layer to prevent unprotected positions.
|
|
*/
|
|
|
|
class MandatoryRiskManager {
|
|
constructor() {
|
|
this.maxRiskPerTradePercent = 6; // Maximum 6% risk per trade (slightly more realistic for leveraged trades)
|
|
this.minRiskRewardRatio = 1.2; // Minimum 1:1.2 risk/reward (less strict)
|
|
this.fallbackStopLossPercent = 2; // 2% stop-loss if not provided (tighter)
|
|
this.fallbackTakeProfitPercent = 4; // 4% take-profit if not provided (better ratio)
|
|
}
|
|
|
|
/**
|
|
* Validates and ensures every trade has proper risk management
|
|
* @param {Object} tradeParams - The trade parameters
|
|
* @returns {Object} - Validated trade with mandatory SL/TP
|
|
*/
|
|
async enforceRiskManagement(tradeParams) {
|
|
const { symbol, side, amount, currentPrice, stopLoss, takeProfit, leverage = 1 } = tradeParams;
|
|
|
|
console.log('🛡️ MANDATORY RISK MANAGEMENT: Validating trade safety...');
|
|
|
|
// 1. Validate basic parameters
|
|
if (!symbol || !side || !amount || !currentPrice) {
|
|
throw new Error('Missing required trade parameters');
|
|
}
|
|
|
|
const normalizedSide = side.toUpperCase();
|
|
if (!['BUY', 'SELL', 'LONG', 'SHORT'].includes(normalizedSide)) {
|
|
throw new Error('Invalid trade side');
|
|
}
|
|
|
|
// 2. Calculate or validate stop-loss
|
|
let finalStopLoss = stopLoss;
|
|
if (!finalStopLoss) {
|
|
console.log('⚠️ NO STOP-LOSS PROVIDED - Calculating mandatory stop-loss...');
|
|
finalStopLoss = this.calculateMandatoryStopLoss(currentPrice, normalizedSide);
|
|
console.log(`🛑 AUTO-CALCULATED Stop-Loss: $${finalStopLoss.toFixed(2)}`);
|
|
}
|
|
|
|
// 3. Calculate or validate take-profit
|
|
let finalTakeProfit = takeProfit;
|
|
if (!finalTakeProfit) {
|
|
console.log('⚠️ NO TAKE-PROFIT PROVIDED - Calculating mandatory take-profit...');
|
|
finalTakeProfit = this.calculateMandatoryTakeProfit(currentPrice, normalizedSide);
|
|
console.log(`🎯 AUTO-CALCULATED Take-Profit: $${finalTakeProfit.toFixed(2)}`);
|
|
}
|
|
|
|
// 4. Validate risk levels
|
|
const riskValidation = this.validateRiskLevels({
|
|
currentPrice,
|
|
stopLoss: finalStopLoss,
|
|
takeProfit: finalTakeProfit,
|
|
side: normalizedSide,
|
|
amount,
|
|
leverage
|
|
});
|
|
|
|
if (!riskValidation.isValid) {
|
|
throw new Error(`Risk validation failed: ${riskValidation.reason}`);
|
|
}
|
|
|
|
// 5. Log safety confirmation
|
|
console.log('✅ RISK MANAGEMENT VALIDATION PASSED:');
|
|
console.log(` Entry Price: $${currentPrice.toFixed(2)}`);
|
|
console.log(` Stop-Loss: $${finalStopLoss.toFixed(2)} (${riskValidation.stopLossPercent.toFixed(2)}%)`);
|
|
console.log(` Take-Profit: $${finalTakeProfit.toFixed(2)} (${riskValidation.takeProfitPercent.toFixed(2)}%)`);
|
|
console.log(` Risk/Reward Ratio: 1:${riskValidation.riskRewardRatio.toFixed(2)}`);
|
|
console.log(` Max Loss: $${riskValidation.maxLossUSD.toFixed(2)} (${riskValidation.maxLossPercent.toFixed(2)}% of trade)`);
|
|
|
|
return {
|
|
...tradeParams,
|
|
stopLoss: finalStopLoss,
|
|
takeProfit: finalTakeProfit,
|
|
riskValidation: riskValidation,
|
|
riskManagementApplied: true
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Calculate mandatory stop-loss if not provided
|
|
*/
|
|
calculateMandatoryStopLoss(currentPrice, side) {
|
|
const isLong = ['BUY', 'LONG'].includes(side);
|
|
|
|
if (isLong) {
|
|
// For LONG: stop-loss BELOW current price
|
|
return currentPrice * (1 - this.fallbackStopLossPercent / 100);
|
|
} else {
|
|
// For SHORT: stop-loss ABOVE current price
|
|
return currentPrice * (1 + this.fallbackStopLossPercent / 100);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate mandatory take-profit if not provided
|
|
*/
|
|
calculateMandatoryTakeProfit(currentPrice, side) {
|
|
const isLong = ['BUY', 'LONG'].includes(side);
|
|
|
|
if (isLong) {
|
|
// For LONG: take-profit ABOVE current price
|
|
return currentPrice * (1 + this.fallbackTakeProfitPercent / 100);
|
|
} else {
|
|
// For SHORT: take-profit BELOW current price
|
|
return currentPrice * (1 - this.fallbackTakeProfitPercent / 100);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate risk levels and calculate risk metrics
|
|
*/
|
|
validateRiskLevels({ currentPrice, stopLoss, takeProfit, side, amount, leverage }) {
|
|
const isLong = ['BUY', 'LONG'].includes(side);
|
|
|
|
// Calculate percentages
|
|
let stopLossPercent, takeProfitPercent;
|
|
|
|
if (isLong) {
|
|
stopLossPercent = Math.abs((currentPrice - stopLoss) / currentPrice * 100);
|
|
takeProfitPercent = Math.abs((takeProfit - currentPrice) / currentPrice * 100);
|
|
} else {
|
|
stopLossPercent = Math.abs((stopLoss - currentPrice) / currentPrice * 100);
|
|
takeProfitPercent = Math.abs((currentPrice - takeProfit) / currentPrice * 100);
|
|
}
|
|
|
|
// Calculate risk/reward ratio
|
|
const riskRewardRatio = takeProfitPercent / stopLossPercent;
|
|
|
|
// Calculate maximum loss in USD
|
|
const positionValue = amount * leverage;
|
|
const maxLossUSD = positionValue * (stopLossPercent / 100);
|
|
const maxLossPercent = stopLossPercent * leverage;
|
|
|
|
// Validation checks
|
|
const validations = [];
|
|
|
|
// Check stop-loss direction
|
|
if (isLong && stopLoss >= currentPrice) {
|
|
validations.push('Stop-loss for LONG position must be BELOW current price');
|
|
}
|
|
if (!isLong && stopLoss <= currentPrice) {
|
|
validations.push('Stop-loss for SHORT position must be ABOVE current price');
|
|
}
|
|
|
|
// Check take-profit direction
|
|
if (isLong && takeProfit <= currentPrice) {
|
|
validations.push('Take-profit for LONG position must be ABOVE current price');
|
|
}
|
|
if (!isLong && takeProfit >= currentPrice) {
|
|
validations.push('Take-profit for SHORT position must be BELOW current price');
|
|
}
|
|
|
|
// Check risk levels
|
|
if (maxLossPercent > this.maxRiskPerTradePercent) {
|
|
validations.push(`Risk too high: ${maxLossPercent.toFixed(2)}% exceeds ${this.maxRiskPerTradePercent}% limit`);
|
|
}
|
|
|
|
if (riskRewardRatio < this.minRiskRewardRatio) {
|
|
validations.push(`Poor risk/reward ratio: 1:${riskRewardRatio.toFixed(2)} below minimum 1:${this.minRiskRewardRatio}`);
|
|
}
|
|
|
|
return {
|
|
isValid: validations.length === 0,
|
|
reason: validations.join('; '),
|
|
stopLossPercent,
|
|
takeProfitPercent,
|
|
riskRewardRatio,
|
|
maxLossUSD,
|
|
maxLossPercent,
|
|
validations
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Emergency stop for unprotected positions
|
|
*/
|
|
async emergencyProtectPosition(position) {
|
|
console.log('🚨 EMERGENCY: Protecting unprotected position...');
|
|
|
|
const { side, size, entryPrice } = position;
|
|
const stopLoss = this.calculateMandatoryStopLoss(entryPrice, side);
|
|
const takeProfit = this.calculateMandatoryTakeProfit(entryPrice, side);
|
|
|
|
return {
|
|
stopLoss,
|
|
takeProfit,
|
|
urgency: 'CRITICAL',
|
|
message: `Position requires immediate protection: SL: $${stopLoss.toFixed(2)}, TP: $${takeProfit.toFixed(2)}`
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = { MandatoryRiskManager };
|