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;