import 'dotenv/config' /** * 简化的环境变量管理器 * 统一处理所有环境变量读取,避免重复的 process.env 调用 */ export class SimpleEnv { /** * 获取环境变量值 */ static get(key, defaultValue) { if (this.cache.has(key)) { return this.cache.get(key) } const value = process.env[key] || defaultValue this.cache.set(key, value) return value } /** * 获取必需的环境变量值,如果不存在则抛出错误 */ static require(key) { const value = this.get(key) if (!value) { throw new Error(`Required environment variable ${key} is not set`) } return value } /** * 获取布尔值环境变量 */ static bool(key, defaultValue = false) { const value = this.get(key) if (!value) return defaultValue return ['true', '1', 'yes', 'on'].includes(value.toLowerCase()) } /** * 获取数字环境变量 */ static number(key, defaultValue) { const value = this.get(key) if (!value) return defaultValue const num = parseInt(value, 10) return isNaN(num) ? defaultValue : num } /** * 获取 JSON 环境变量 */ static json(key, defaultValue) { const value = this.get(key) if (!value) return defaultValue try { return JSON.parse(value) } catch { return defaultValue } } /** * 获取数组环境变量(逗号分隔) */ static array(key, defaultValue = []) { const value = this.get(key) if (!value) return defaultValue return value .split(',') .map(s => s.trim()) .filter(s => s.length > 0) } /** * 清除缓存 */ static clearCache() { this.cache.clear() } /** * 批量获取带前缀的环境变量 */ static getPrefix(prefix) { const result = {} Object.keys(process.env).forEach(key => { if (key.startsWith(prefix)) { const shortKey = key.slice(prefix.length) const value = process.env[key] if (value) { result[shortKey] = value } } }) return result } } SimpleEnv.cache = new Map() /** * 预定义的配置键,只包含需要从环境变量读取的配置 */ export const EnvKeys = { // 基础配置 (可选,有默认值) NODE_ENV: 'NODE_ENV', LOG_LEVEL: 'LOG_LEVEL', // Aster DEX 认证 (必需) ASTER_ORDER_USER: 'ASTER_ORDER_USER', ASTER_API_KEY: 'ASTER_API_KEY', ASTER_API_SECRET: 'ASTER_API_SECRET', // Aster 第二账户 (可选) ASTER2_ORDER_USER: 'ASTER2_ORDER_USER', ASTER2_ORDER_SIGNER: 'ASTER2_ORDER_SIGNER', PRIVATE_KEY2: 'PRIVATE_KEY2', // Pacifica DEX 认证 (必需) PACIFICA_ACCOUNT: 'PACIFICA_ACCOUNT', PACIFICA_ACCOUNT_PRIVATE_KEY: 'PACIFICA_ACCOUNT_PRIVATE_KEY', // 测试配置 (可选) PACIFICA_ENABLE_TEST_ORDER: 'PACIFICA_ENABLE_TEST_ORDER', PACIFICA_TEST_QTY: 'PACIFICA_TEST_QTY', // Binance (可选) BINANCE_API_KEY: 'BINANCE_API_KEY', BINANCE_SECRET_KEY: 'BINANCE_SECRET_KEY', // Proxy 配置 (可选) PROXY_ENABLED: 'PROXY_ENABLED', PROXY_PROTOCOL: 'PROXY_PROTOCOL', PROXY_HOST: 'PROXY_HOST', PROXY_PORT: 'PROXY_PORT', PROXY_USERNAME: 'PROXY_USERNAME', PROXY_PASSWORD: 'PROXY_PASSWORD', // 高级代理配置 (会话管理) PROXY_SESSION_PREFIX: 'PROXY_SESSION_PREFIX', PROXY_SESSION_SUFFIX: 'PROXY_SESSION_SUFFIX', PROXY_SESSION_STATIC: 'PROXY_SESSION_STATIC', // 交易所专用代理 (可选,优先级高于全局代理) ASTER_PROXY_PROTOCOL: 'ASTER_PROXY_PROTOCOL', ASTER_PROXY_HOST: 'ASTER_PROXY_HOST', ASTER_PROXY_PORT: 'ASTER_PROXY_PORT', ASTER_PROXY_USER: 'ASTER_PROXY_USER', ASTER_PROXY_PASS: 'ASTER_PROXY_PASS', ASTER_PROXY_SESSION_PREFIX: 'ASTER_PROXY_SESSION_PREFIX', ASTER_PROXY_SESSION_SUFFIX: 'ASTER_PROXY_SESSION_SUFFIX', ASTER_PROXY_SESSION_STATIC: 'ASTER_PROXY_SESSION_STATIC', // Pacifica专用代理 PACIFICA_PROXY_PROTOCOL: 'PACIFICA_PROXY_PROTOCOL', PACIFICA_PROXY_HOST: 'PACIFICA_PROXY_HOST', PACIFICA_PROXY_PORT: 'PACIFICA_PROXY_PORT', PACIFICA_PROXY_USER: 'PACIFICA_PROXY_USER', PACIFICA_PROXY_PASS: 'PACIFICA_PROXY_PASS', PACIFICA_PROXY_SESSION_PREFIX: 'PACIFICA_PROXY_SESSION_PREFIX', PACIFICA_PROXY_SESSION_SUFFIX: 'PACIFICA_PROXY_SESSION_SUFFIX', PACIFICA_PROXY_SESSION_STATIC: 'PACIFICA_PROXY_SESSION_STATIC', // Binance专用代理 BINANCE_PROXY_PROTOCOL: 'BINANCE_PROXY_PROTOCOL', BINANCE_PROXY_HOST: 'BINANCE_PROXY_HOST', BINANCE_PROXY_PORT: 'BINANCE_PROXY_PORT', BINANCE_PROXY_USER: 'BINANCE_PROXY_USER', BINANCE_PROXY_PASS: 'BINANCE_PROXY_PASS', BINANCE_PROXY_SESSION_PREFIX: 'BINANCE_PROXY_SESSION_PREFIX', BINANCE_PROXY_SESSION_SUFFIX: 'BINANCE_PROXY_SESSION_SUFFIX', BINANCE_PROXY_SESSION_STATIC: 'BINANCE_PROXY_SESSION_STATIC', } /** * 统一的配置对象 - 将固定配置写死,只有认证信息从环境变量读取 */ export const Config = { // 基础配置 nodeEnv: () => SimpleEnv.get(EnvKeys.NODE_ENV, 'development'), logLevel: () => SimpleEnv.get(EnvKeys.LOG_LEVEL, 'info'), isDev: () => Config.nodeEnv() === 'development', isProd: () => Config.nodeEnv() === 'production', // Aster 配置 - 固定的服务端点,只有认证信息动态 aster: { // 固定配置 - 不从环境变量读取 wsUrl: 'wss://fstream.asterdex.com', httpBase: 'https://fapi.asterdex.com', subscribeSymbols: ['BTCUSDT', 'ETHUSDT'], wsPingInterval: 30000, wsPongTimeout: 10000, wsReconnectInterval: 5000, wsMaxReconnectAttempts: 10, recvWindow: 50000, // 动态配置 - 从环境变量读取 orderUser: () => SimpleEnv.get(EnvKeys.ASTER_ORDER_USER), apiKey: () => SimpleEnv.get(EnvKeys.ASTER_API_KEY), apiSecret: () => SimpleEnv.get(EnvKeys.ASTER_API_SECRET), // 第二账户 orderUser2: () => SimpleEnv.get(EnvKeys.ASTER2_ORDER_USER), orderSigner2: () => SimpleEnv.get(EnvKeys.ASTER2_ORDER_SIGNER), privateKey2: () => SimpleEnv.get(EnvKeys.PRIVATE_KEY2), }, // Pacifica 配置 - 固定的服务端点,只有认证信息动态 pacifica: { // 固定配置 - 不从环境变量读取 baseUrl: 'https://api.pacifica.fi', wsUrl: 'wss://ws.pacifica.fi/ws', symbol: 'BTC-USD', defaultQty: 0.001, // 动态配置 - 从环境变量读取 account: () => SimpleEnv.get(EnvKeys.PACIFICA_ACCOUNT), accountPrivateKey: () => SimpleEnv.get(EnvKeys.PACIFICA_ACCOUNT_PRIVATE_KEY), enableTestOrder: () => SimpleEnv.bool(EnvKeys.PACIFICA_ENABLE_TEST_ORDER, false), testQty: () => SimpleEnv.number(EnvKeys.PACIFICA_TEST_QTY, 0.001), }, // Binance 配置 - 只有认证信息 binance: { // 固定配置 baseUrl: 'https://api.binance.com', wsUrl: 'wss://stream.binance.com:9443/ws', // 动态配置 apiKey: () => SimpleEnv.get(EnvKeys.BINANCE_API_KEY), secretKey: () => SimpleEnv.get(EnvKeys.BINANCE_SECRET_KEY), }, // Proxy 配置 - 支持全局和交易所专用代理 proxy: { // 全局代理配置 enabled: () => SimpleEnv.bool(EnvKeys.PROXY_ENABLED, false), protocol: () => SimpleEnv.get(EnvKeys.PROXY_PROTOCOL, 'http'), host: () => SimpleEnv.get(EnvKeys.PROXY_HOST), port: () => SimpleEnv.number(EnvKeys.PROXY_PORT, 8080), username: () => SimpleEnv.get(EnvKeys.PROXY_USERNAME), password: () => SimpleEnv.get(EnvKeys.PROXY_PASSWORD), // 会话管理 (高级功能) sessionPrefix: () => SimpleEnv.get(EnvKeys.PROXY_SESSION_PREFIX), sessionSuffix: () => SimpleEnv.get(EnvKeys.PROXY_SESSION_SUFFIX), sessionStatic: () => SimpleEnv.get(EnvKeys.PROXY_SESSION_STATIC), // 交易所专用代理配置 aster: { protocol: () => SimpleEnv.get(EnvKeys.ASTER_PROXY_PROTOCOL, 'http'), host: () => SimpleEnv.get(EnvKeys.ASTER_PROXY_HOST), port: () => SimpleEnv.number(EnvKeys.ASTER_PROXY_PORT, 12321), user: () => SimpleEnv.get(EnvKeys.ASTER_PROXY_USER), pass: () => SimpleEnv.get(EnvKeys.ASTER_PROXY_PASS), sessionPrefix: () => SimpleEnv.get(EnvKeys.ASTER_PROXY_SESSION_PREFIX), sessionSuffix: () => SimpleEnv.get(EnvKeys.ASTER_PROXY_SESSION_SUFFIX), sessionStatic: () => SimpleEnv.get(EnvKeys.ASTER_PROXY_SESSION_STATIC), isConfigured: () => { return !!Config.proxy.aster.host() && !!Config.proxy.aster.user() }, }, pacifica: { protocol: () => SimpleEnv.get(EnvKeys.PACIFICA_PROXY_PROTOCOL, 'http'), host: () => SimpleEnv.get(EnvKeys.PACIFICA_PROXY_HOST), port: () => SimpleEnv.number(EnvKeys.PACIFICA_PROXY_PORT, 8080), user: () => SimpleEnv.get(EnvKeys.PACIFICA_PROXY_USER), pass: () => SimpleEnv.get(EnvKeys.PACIFICA_PROXY_PASS), sessionPrefix: () => SimpleEnv.get(EnvKeys.PACIFICA_PROXY_SESSION_PREFIX), sessionSuffix: () => SimpleEnv.get(EnvKeys.PACIFICA_PROXY_SESSION_SUFFIX), sessionStatic: () => SimpleEnv.get(EnvKeys.PACIFICA_PROXY_SESSION_STATIC), isConfigured: () => { return !!Config.proxy.pacifica.host() && !!Config.proxy.pacifica.user() }, }, binance: { protocol: () => SimpleEnv.get(EnvKeys.BINANCE_PROXY_PROTOCOL, 'http'), host: () => SimpleEnv.get(EnvKeys.BINANCE_PROXY_HOST), port: () => SimpleEnv.number(EnvKeys.BINANCE_PROXY_PORT, 8080), user: () => SimpleEnv.get(EnvKeys.BINANCE_PROXY_USER), pass: () => SimpleEnv.get(EnvKeys.BINANCE_PROXY_PASS), sessionPrefix: () => SimpleEnv.get(EnvKeys.BINANCE_PROXY_SESSION_PREFIX), sessionSuffix: () => SimpleEnv.get(EnvKeys.BINANCE_PROXY_SESSION_SUFFIX), sessionStatic: () => SimpleEnv.get(EnvKeys.BINANCE_PROXY_SESSION_STATIC), isConfigured: () => { return !!Config.proxy.binance.host() && !!Config.proxy.binance.user() }, }, // 生成随机会话ID (8位字符) generateSessionId: () => { const chars = 'abcdefghijklmnopqrstuvwxyz0123456789' let result = '' for (let i = 0; i < 8; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)) } return result }, // 构建代理密码 (支持会话管理) buildPassword: exchange => { let config // 优先使用交易所专用配置 if (exchange === 'aster' && Config.proxy.aster.isConfigured()) { config = { pass: Config.proxy.aster.pass(), prefix: Config.proxy.aster.sessionPrefix(), suffix: Config.proxy.aster.sessionSuffix(), static: Config.proxy.aster.sessionStatic(), } } else if (exchange === 'pacifica' && Config.proxy.pacifica.isConfigured()) { config = { pass: Config.proxy.pacifica.pass(), prefix: Config.proxy.pacifica.sessionPrefix(), suffix: Config.proxy.pacifica.sessionSuffix(), static: Config.proxy.pacifica.sessionStatic(), } } else if (exchange === 'binance' && Config.proxy.binance.isConfigured()) { config = { pass: Config.proxy.binance.pass(), prefix: Config.proxy.binance.sessionPrefix(), suffix: Config.proxy.binance.sessionSuffix(), static: Config.proxy.binance.sessionStatic(), } } else { // 使用全局配置 config = { pass: Config.proxy.password(), prefix: Config.proxy.sessionPrefix(), suffix: Config.proxy.sessionSuffix(), static: Config.proxy.sessionStatic(), } } // 方式一:直接使用完整密码 if (config.pass) { return config.pass } // 方式二:使用前后缀+会话ID if (config.prefix && config.suffix) { const sessionId = config.static || Config.proxy.generateSessionId() return `${config.prefix}${sessionId}${config.suffix}` } return undefined }, // 获取代理URL (支持交易所专用配置) getUrl: exchange => { let proxyConfig // 优先使用交易所专用代理 if (exchange === 'aster' && Config.proxy.aster.isConfigured()) { proxyConfig = { protocol: Config.proxy.aster.protocol(), host: Config.proxy.aster.host(), port: Config.proxy.aster.port(), username: Config.proxy.aster.user(), password: Config.proxy.buildPassword('aster'), } } else if (exchange === 'pacifica' && Config.proxy.pacifica.isConfigured()) { proxyConfig = { protocol: Config.proxy.pacifica.protocol(), host: Config.proxy.pacifica.host(), port: Config.proxy.pacifica.port(), username: Config.proxy.pacifica.user(), password: Config.proxy.buildPassword('pacifica'), } } else if (exchange === 'binance' && Config.proxy.binance.isConfigured()) { proxyConfig = { protocol: Config.proxy.binance.protocol(), host: Config.proxy.binance.host(), port: Config.proxy.binance.port(), username: Config.proxy.binance.user(), password: Config.proxy.buildPassword('binance'), } } else if (Config.proxy.enabled() && Config.proxy.host()) { // 全局代理 proxyConfig = { protocol: Config.proxy.protocol(), host: Config.proxy.host(), port: Config.proxy.port(), username: Config.proxy.username(), password: Config.proxy.buildPassword(), } } else { return undefined } const { protocol, host, port, username, password } = proxyConfig if (username && password) { return `${protocol}://${username}:${password}@${host}:${port}` } else { return `${protocol}://${host}:${port}` } }, // 检查代理是否配置 (全局或交易所专用) isConfigured: exchange => { if (exchange === 'aster') { return Config.proxy.aster.isConfigured() } else if (exchange === 'pacifica') { return Config.proxy.pacifica.isConfigured() } else if (exchange === 'binance') { return Config.proxy.binance.isConfigured() } return Config.proxy.enabled() && !!Config.proxy.host() }, // 检查任何代理是否配置 isAnyConfigured: () => { return ( Config.proxy.isConfigured() || Config.proxy.aster.isConfigured() || Config.proxy.pacifica.isConfigured() || Config.proxy.binance.isConfigured() ) }, }, // 获取所有配置(用于调试) getAll: () => ({ nodeEnv: Config.nodeEnv(), logLevel: Config.logLevel(), aster: { wsUrl: Config.aster.wsUrl, httpBase: Config.aster.httpBase, subscribeSymbols: Config.aster.subscribeSymbols, orderUser: Config.aster.orderUser() ? '已设置' : '未设置', apiKey: Config.aster.apiKey() ? '已设置' : '未设置', }, pacifica: { baseUrl: Config.pacifica.baseUrl, wsUrl: Config.pacifica.wsUrl, symbol: Config.pacifica.symbol, account: Config.pacifica.account() ? '已设置' : '未设置', enableTestOrder: Config.pacifica.enableTestOrder(), }, binance: { baseUrl: Config.binance.baseUrl, wsUrl: Config.binance.wsUrl, apiKey: Config.binance.apiKey() ? '已设置' : '未设置', }, proxy: { enabled: Config.proxy.enabled(), configured: Config.proxy.isConfigured(), url: Config.proxy.getUrl() ? '已配置' : '未配置', }, }), } /** * 智能账户发现器 - 简化版 */ export class SmartAccountDiscovery { /** * 发现 Pacifica 账户 */ static discoverPacifica() { const accounts = [] // 检查基础账户 const baseAccount = SimpleEnv.get('PACIFICA_ACCOUNT') const baseKey = SimpleEnv.get('PACIFICA_ACCOUNT_PRIVATE_KEY') if (baseAccount && baseKey) { accounts.push({ name: 'Pacifica Main', account: baseAccount, privateKey: baseKey, suffix: '', }) } // 检查编号账户 (1-5) for (let i = 1; i <= 5; i++) { const account = SimpleEnv.get(`PACIFICA_ACCOUNT_${i}`) const key = SimpleEnv.get(`PACIFICA_PRIVATE_KEY_${i}`) if (account && key) { accounts.push({ name: `Pacifica ${i}`, account, privateKey: key, suffix: `_${i}`, }) } } // 检查角色账户 const roles = ['MAIN', 'HEDGE', 'BACKUP'] roles.forEach(role => { const account = SimpleEnv.get(`PACIFICA_ACCOUNT_${role}`) const key = SimpleEnv.get(`PACIFICA_PRIVATE_KEY_${role}`) if (account && key) { accounts.push({ name: `Pacifica ${role.toLowerCase()}`, account, privateKey: key, suffix: `_${role}`, }) } }) return accounts } /** * 发现 Aster 账户 */ static discoverAster() { const accounts = [] // 检查基础账户 const baseUser = SimpleEnv.get('ASTER_ORDER_USER') const baseSigner = SimpleEnv.get('ASTER_ORDER_SIGNER') const baseKey = SimpleEnv.get('PRIVATE_KEY') if (baseUser && baseKey) { accounts.push({ name: 'Aster Main', user: baseUser, signer: baseSigner, privateKey: baseKey, suffix: '', }) } // 检查第二账户 const user2 = SimpleEnv.get('ASTER2_ORDER_USER') const signer2 = SimpleEnv.get('ASTER2_ORDER_SIGNER') const key2 = SimpleEnv.get('PRIVATE_KEY2') if (user2 && key2) { accounts.push({ name: 'Aster 2', user: user2, signer: signer2, privateKey: key2, suffix: '_2', }) } // 检查编号账户 (1-3) for (let i = 1; i <= 3; i++) { const user = SimpleEnv.get(`ASTER_ORDER_USER_${i}`) const signer = SimpleEnv.get(`ASTER_ORDER_SIGNER_${i}`) const key = SimpleEnv.get(`ASTER_PRIVATE_KEY_${i}`) if (user && key) { accounts.push({ name: `Aster ${i}`, user, signer, privateKey: key, suffix: `_${i}`, }) } } return accounts } /** * 发现 Binance 账户 */ static discoverBinance() { const accounts = [] // 检查基础账户 const baseKey = SimpleEnv.get('BINANCE_API_KEY') const baseSecret = SimpleEnv.get('BINANCE_SECRET_KEY') if (baseKey && baseSecret) { accounts.push({ name: 'Binance Main', apiKey: baseKey, secretKey: baseSecret, suffix: '', }) } // 检查编号账户 (1-3) for (let i = 1; i <= 3; i++) { const key = SimpleEnv.get(`BINANCE_API_KEY_${i}`) const secret = SimpleEnv.get(`BINANCE_SECRET_KEY_${i}`) if (key && secret) { accounts.push({ name: `Binance ${i}`, apiKey: key, secretKey: secret, suffix: `_${i}`, }) } } return accounts } /** * 发现所有账户 */ static discoverAll() { return { pacifica: this.discoverPacifica(), aster: this.discoverAster(), binance: this.discoverBinance(), } } }