Bot & Automation

Treding Forex Ai

/root/hermes-projects/Treding Forex Ai

dist/src/config/env.js text
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.env = void 0;
require("dotenv/config");
const zod_1 = require("zod");
const booleanFromEnv = zod_1.z.preprocess((value) => {
    if (typeof value !== "string") {
        return value;
    }
    const normalized = value.trim().toLowerCase();
    if (["true", "1", "yes", "y"].includes(normalized)) {
        return true;
    }
    if (["false", "0", "no", "n", ""].includes(normalized)) {
        return false;
    }
    return value;
}, zod_1.z.boolean());
const optionalNumber = (fallback) => zod_1.z.preprocess((value) => {
    if (value === undefined || value === null || value === "") {
        return fallback;
    }
    return Number(value);
}, zod_1.z.number().finite().positive());
const optionalNonNegativeInteger = (fallback) => zod_1.z.preprocess((value) => {
    if (value === undefined || value === null || value === "") {
        return fallback;
    }
    return Number(value);
}, zod_1.z.number().int().min(0));
const optionalPositiveInteger = (fallback, min = 1) => zod_1.z.preprocess((value) => {
    if (value === undefined || value === null || value === "") {
        return fallback;
    }
    return Number(value);
}, zod_1.z.number().int().min(min));
const envSchema = zod_1.z.object({
    NODE_ENV: zod_1.z.enum(["development", "test", "production"]).default("development"),
    BOT_DISPLAY_NAME: zod_1.z.string().min(1).default("Treding Forex Ai"),
    TELEGRAM_BOT_TOKEN: zod_1.z.string().optional(),
    TRADING_MODE: zod_1.z.enum(["paper", "semi_auto", "full_auto"]).default("paper"),
    ENABLE_REAL_TRADING: booleanFromEnv.default(false),
    ACCOUNT_BALANCE: optionalNumber(1000),
    RISK_PER_TRADE_PERCENT: optionalNumber(1),
    MAX_DAILY_LOSS_PERCENT: optionalNumber(3),
    MAX_OPEN_TRADES: optionalNonNegativeInteger(3),
    MAX_LOT_PER_ORDER: optionalNumber(0.01),
    MARKET_DATA_PROVIDER: zod_1.z.enum(["demo", "twelvedata"]).default("demo"),
    TWELVEDATA_API_KEY: zod_1.z.string().optional(),
    DATA_DIR: zod_1.z.string().min(1).default("./data"),
    ALLOWED_TELEGRAM_USER_IDS: zod_1.z.string().optional(),
    BROKER_PROVIDER: zod_1.z.enum(["disabled", "paper", "mt5_bridge"]).default("disabled"),
    BROKER_API_KEY: zod_1.z.string().optional(),
    BROKER_ACCOUNT_ID: zod_1.z.string().optional(),
    BROKER_BASE_URL: zod_1.z.string().optional(),
    MT5_BRIDGE_URL: zod_1.z.string().url().optional().or(zod_1.z.literal("")),
    MT5_BRIDGE_TOKEN: zod_1.z.string().optional(),
    EXNESS_MT5_LOGIN: zod_1.z.string().optional(),
    EXNESS_MT5_SERVER: zod_1.z.string().optional(),
    EXNESS_ACCOUNT_TYPE: zod_1.z.enum(["demo", "real"]).default("demo"),
    AUTO_TRADING_ENABLED: booleanFromEnv.default(false),
    AUTO_TRADE_SYMBOLS: zod_1.z.string().default("XAUUSD"),
    AUTO_TRADE_MODE: zod_1.z
        .enum(["scalping", "intraday", "swing", "conservative", "aggressive"])
        .default("intraday"),
    AUTO_SCAN_INTERVAL_SECONDS: optionalPositiveInteger(300, 5),
    AUTO_TRADE_COOLDOWN_SECONDS: optionalPositiveInteger(45, 10),
    AUTO_TRADE_COOLDOWN_MINUTES: optionalNumber(1),
    AUTO_MONITOR_INTERVAL_SECONDS: optionalPositiveInteger(30, 5),
    AUTO_MIN_CONFIDENCE: optionalPositiveInteger(70, 1),
    AUTO_MAX_SPREAD_PIPS: optionalNumber(50),
    AUTO_SKIP_ROLLOVER_WINDOW: booleanFromEnv.default(true),
    AUTO_TRADE_CHAT_ID: zod_1.z.string().optional(),
    AUTO_TRADE_USER_ID: zod_1.z.string().optional()
});
const parsedEnv = envSchema.parse(process.env);
exports.env = {
    nodeEnv: parsedEnv.NODE_ENV,
    botDisplayName: parsedEnv.BOT_DISPLAY_NAME,
    telegramBotToken: parsedEnv.TELEGRAM_BOT_TOKEN,
    tradingMode: parsedEnv.TRADING_MODE,
    enableRealTrading: parsedEnv.ENABLE_REAL_TRADING,
    marketDataProvider: parsedEnv.MARKET_DATA_PROVIDER,
    twelveDataApiKey: parsedEnv.TWELVEDATA_API_KEY,
    dataDir: parsedEnv.DATA_DIR,
    allowedTelegramUserIds: parseAllowedUserIds(parsedEnv.ALLOWED_TELEGRAM_USER_IDS),
    brokerProvider: parsedEnv.BROKER_PROVIDER,
    brokerApiKey: parsedEnv.BROKER_API_KEY,
    brokerAccountId: parsedEnv.BROKER_ACCOUNT_ID,
    brokerBaseUrl: parsedEnv.BROKER_BASE_URL,
    exness: {
        mt5BridgeUrl: normalizeOptional(parsedEnv.MT5_BRIDGE_URL),
        mt5BridgeToken: parsedEnv.MT5_BRIDGE_TOKEN,
        mt5Login: parsedEnv.EXNESS_MT5_LOGIN,
        mt5Server: parsedEnv.EXNESS_MT5_SERVER,
        accountType: parsedEnv.EXNESS_ACCOUNT_TYPE
    },
    risk: {
        accountBalance: parsedEnv.ACCOUNT_BALANCE,
        riskPerTradePercent: parsedEnv.RISK_PER_TRADE_PERCENT,
        maxDailyLossPercent: parsedEnv.MAX_DAILY_LOSS_PERCENT,
        maxOpenTrades: parsedEnv.MAX_OPEN_TRADES,
        maxLotPerOrder: parsedEnv.MAX_LOT_PER_ORDER
    },
    autoTrading: {
        enabled: parsedEnv.AUTO_TRADING_ENABLED,
        symbols: parseSymbolList(parsedEnv.AUTO_TRADE_SYMBOLS),
        mode: parsedEnv.AUTO_TRADE_MODE,
        scanIntervalSeconds: parsedEnv.AUTO_SCAN_INTERVAL_SECONDS,
        cooldownSeconds: parsedEnv.AUTO_TRADE_COOLDOWN_SECONDS,
        monitorIntervalSeconds: parsedEnv.AUTO_MONITOR_INTERVAL_SECONDS,
        minConfidence: parsedEnv.AUTO_MIN_CONFIDENCE,
        maxSpreadPips: parsedEnv.AUTO_MAX_SPREAD_PIPS,
        skipRolloverWindow: parsedEnv.AUTO_SKIP_ROLLOVER_WINDOW,
        maxOpenPositions: parsedEnv.MAX_OPEN_TRADES, // 0 = tidak ada batas
        chatId: parseOptionalPositiveInteger(parsedEnv.AUTO_TRADE_CHAT_ID) ??
            firstAllowedUserId(parsedEnv.ALLOWED_TELEGRAM_USER_IDS),
        userId: parseOptionalPositiveInteger(parsedEnv.AUTO_TRADE_USER_ID) ??
            firstAllowedUserId(parsedEnv.ALLOWED_TELEGRAM_USER_IDS)
    }
};
function normalizeOptional(value) {
    if (!value?.trim()) {
        return undefined;
    }
    return value.trim();
}
function parseAllowedUserIds(value) {
    if (!value?.trim()) {
        return new Set();
    }
    return new Set(value
        .split(",")
        .map((item) => Number(item.trim()))
        .filter((item) => Number.isInteger(item) && item > 0));
}
function firstAllowedUserId(value) {
    return [...parseAllowedUserIds(value)].at(0);
}
function parseOptionalPositiveInteger(value) {
    if (!value?.trim()) {
        return undefined;
    }
    const parsed = Number(value.trim());
    return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
}
function parseSymbolList(value) {
    return value
        .split(",")
        .map((item) => item.trim().toUpperCase())
        .filter(Boolean);
}