Bot & Automation
Treding Forex Ai
/root/hermes-projects/Treding Forex Ai
dist/src/services/riskManager.js
text
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RiskManager = void 0;
const node_crypto_1 = require("node:crypto");
const risk_1 = require("../constants/risk");
const math_1 = require("../utils/math");
class RiskManager {
riskConfig;
constructor(riskConfig) {
this.riskConfig = riskConfig;
}
evaluate(signal, openTradeCount, todayLossAmount) {
const reasons = [];
if (signal.action === "WAIT") {
reasons.push("Signal WAIT tidak boleh dieksekusi.");
}
const modeConfig = risk_1.TRADING_MODE_CONFIGS[signal.mode];
if (signal.confidence < modeConfig.confidenceThreshold) {
reasons.push(`Confidence ${signal.confidence}% di bawah threshold ${modeConfig.confidenceThreshold}%.`);
}
if (this.riskConfig.maxOpenTrades > 0 && openTradeCount >= this.riskConfig.maxOpenTrades) {
reasons.push(`Open trade sudah mencapai batas ${this.riskConfig.maxOpenTrades}.`);
}
const maxDailyLoss = this.riskConfig.accountBalance * (this.riskConfig.maxDailyLossPercent / 100);
if (Math.abs(todayLossAmount) >= maxDailyLoss) {
reasons.push("Max daily loss sudah tercapai.");
}
if (signal.entry === null || signal.stopLoss === null) {
reasons.push("Entry atau stop loss belum valid.");
}
if (signal.riskRewardRatio < 1.5 && signal.action !== "WAIT") {
reasons.push("Risk reward ratio kurang dari 1:1.5.");
}
if (reasons.length > 0 || signal.entry === null || signal.stopLoss === null) {
return { accepted: false, reasons };
}
const positionSizing = this.calculatePositionSize(signal.symbol, signal.entry, signal.stopLoss, modeConfig.riskMultiplier);
return {
accepted: true,
reasons: ["Risk check lolos untuk paper trade."],
positionSizing
};
}
buildOrderIntent(params) {
const { chatId, userId, signal, positionSizing } = params;
if (signal.action === "WAIT" ||
signal.entry === null ||
signal.stopLoss === null ||
signal.takeProfit1 === null ||
signal.takeProfit2 === null) {
throw new Error("Signal tidak bisa dibuat menjadi order.");
}
return {
id: (0, node_crypto_1.randomUUID)(),
chatId,
userId,
signalId: signal.id,
symbolCode: signal.symbol.code,
action: signal.action,
mode: signal.mode,
entry: signal.entry,
stopLoss: signal.stopLoss,
takeProfit1: signal.takeProfit1,
takeProfit2: signal.takeProfit2,
confidence: signal.confidence,
riskLevel: signal.riskLevel,
riskRewardRatio: signal.riskRewardRatio,
positionSizing,
status: "pending",
createdAt: new Date().toISOString()
};
}
calculatePositionSize(symbol, entry, stopLoss, riskMultiplier) {
const riskAmount = this.riskConfig.accountBalance * (this.riskConfig.riskPerTradePercent / 100) * riskMultiplier;
const stopDistancePips = Math.max(Math.abs(entry - stopLoss) / symbol.pipSize, 1);
const rawLotSize = riskAmount / (stopDistancePips * symbol.pipValuePerLot);
const maxLot = Math.min(symbol.maxLot, this.riskConfig.maxLotPerOrder);
const lotSize = (0, math_1.clamp)((0, math_1.roundTo)(rawLotSize, 2), symbol.minLot, maxLot);
return {
lotSize,
riskAmount: (0, math_1.roundTo)(riskAmount, 2),
stopDistancePips: (0, math_1.roundTo)(stopDistancePips, 1)
};
}
}
exports.RiskManager = RiskManager;