Files
trade/data/config/index.ts
T
Rekey b4c7636731 添加 USDT-M 合约数据支持(配置层 + 清理多余字段)
- 配置层:env.yaml 新增 binance_futures API Key 段,validators + config 同步
- 清理 TradingPair 实体:删除 kline_interval、kline_intervals、kline_synthesis_enabled
- 删除 fetchKlines 系列函数的 interval 参数,硬编码为 1m
- 更新 SQL seed 数据、example、base_rest 接口、types 接口
- 新增 AGENTS/08-boundaries.md 执行纪律
- 新增 PLAN-add-futures-data.md 方案文档
2026-06-15 23:24:21 +08:00

191 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// config.ts — 中心化配置模块(YAML 驱动)
// ============================================================
// 职责:
// 1. 从项目根目录 env.yaml 加载配置
// 2. 使用 validateConfig() 校验并类型收窄
// 3. 导出按职责分组的强类型配置对象(pgsql / redis / logging
//
// 使用方式:
// import { pgsql, redis, logging } from "./config";
// const ds = new DataSource({ ...pgsql });
// const redisClient = new Redis(redis.url);
//
// 配置文件位置:data/env.yaml(软链接 → 项目根目录 env.yaml)
// TypeScript / Python 模块共享同一份配置。
// ============================================================
import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { parse as parseYaml } from "yaml";
import { validateConfig, type EnvConfig } from "./validators";
// ============================================================
// 1. 定位并读取 env.yaml
// ============================================================
/**
* 计算项目根目录的绝对路径。
* data/config.ts → data/ → <project_root>/
*
* 兼容 ESM(无 __dirname)和 Bun 运行时。
*/
function getProjectRoot(): string {
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// config/index.ts → config/ → data/ → <project_root>
return resolve(__dirname, "../..");
}
/**
* 读取 data/env.yaml(软链接指向项目根目录 env.yaml)并解析为原始对象。
* 文件不存在时抛出明确错误,不做静默降级。
*/
function loadYamlConfig(): unknown {
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// config/index.ts → config/ → data/env.yaml
const yamlPath = resolve(__dirname, "../env.yaml");
let content: string;
try {
content = readFileSync(yamlPath, "utf-8");
} catch {
throw new Error(
`[config] 无法读取配置文件: ${yamlPath}\n` +
`请确保 data/env.yaml 软链接指向项目根目录的 env.yaml。`,
);
}
const parsed = parseYaml(content);
if (parsed === null || parsed === undefined) {
throw new Error(`[config] env.yaml 解析结果为空: ${yamlPath}`);
}
return parsed;
}
// ============================================================
// 2. 加载 & 校验
// ============================================================
/** 经校验后的 env.yaml 配置(强类型) */
const rawConfig: EnvConfig = (() => {
const raw = loadYamlConfig();
return validateConfig(raw);
})();
// ============================================================
// 3. 按职责分组的导出配置对象
// ============================================================
/**
* PostgreSQL / TimescaleDB 连接配置
*
* 连接池参数(max / idleTimeoutMillis / connectionTimeoutMillis
* 为硬编码常量,不在 env.yaml 中暴露,避免误调导致连接耗尽。
*/
export const pgsql = {
host: rawConfig.db.host,
port: rawConfig.db.port,
database: rawConfig.db.name,
user: rawConfig.db.user,
password: rawConfig.db.password,
/** pg.Pool 连接上限,避免连接数暴涨 */
max: 20,
/** 连接空闲超时(毫秒) */
idleTimeoutMillis: 30000,
/** 连接获取超时(毫秒) */
connectionTimeoutMillis: 5000,
} as const;
/**
* Redis 连接与发布配置
*
* channelPrefix / retryDelayBaseMs / maxRetries 为硬编码常量,
* 跨模块保持一致,不需要通过配置文件修改。
*/
export const redis = {
/** Redis 连接 URLioredis 可直接使用) */
url: rawConfig.redis.url,
/** 是否启用 Pub/Sub 发布行情数据(开发环境可关闭以节省资源) */
publishEnabled: rawConfig.redis.publish_enabled,
/** 频道前缀,避免多环境 key 冲突 */
channelPrefix: "trade",
/** 重连策略:指数退避基数(毫秒) */
retryDelayBaseMs: 1000,
/** 最大重试次数 */
maxRetries: 10,
} as const;
/**
* 日志配置
*
* pretty 由 NODE_ENV 自动推导,不在 env.yaml 中独立配置。
*/
export const logging = {
/** 日志级别:trace / debug / info / warn / error / fatal */
level: rawConfig.logging.level,
/** 运行环境(production 时 pino 输出 JSON 便于日志采集) */
nodeEnv: rawConfig.logging.node_env,
/** 是否启用 pino-pretty(开发环境友好输出) */
pretty: rawConfig.logging.node_env === "development",
} as const;
/**
* 交易所 API 密钥配置
*
* 按交易所 ID 索引,目前仅 binance。
* 新增交易所时在 env.yaml 的 exchange 段添加对应子配置即可。
*/
export const exchange = {
binance: {
apiKey: rawConfig.exchange.binance.api_key,
apiSecret: rawConfig.exchange.binance.api_secret,
},
/** USDT-M 永续合约 API Key */
binanceFutures: {
apiKey: rawConfig.exchange.binance_futures.api_key,
apiSecret: rawConfig.exchange.binance_futures.api_secret,
},
} as const;
// ============================================================
// 4. 工具:运行时打印配置概要(不含敏感信息)
// ============================================================
/** 打印脱敏后的配置概要,便于启动排查 */
export function printConfigSummary(): void {
const summary = {
projectRoot: getProjectRoot(),
pgsql: {
host: pgsql.host,
port: pgsql.port,
database: pgsql.database,
user: pgsql.user,
password: "***",
},
redis: {
url: redis.url.replace(/\/\/.*@/, "//***@"), // 隐藏密码
publishEnabled: redis.publishEnabled,
},
exchange: {
binance: {
apiKey: exchange.binance.apiKey.slice(0, 6) + "***",
apiSecret: "***",
},
binanceFutures: {
apiKey: exchange.binanceFutures.apiKey.slice(0, 6) + "***",
apiSecret: "***",
},
},
logging: {
level: logging.level,
nodeEnv: logging.nodeEnv,
pretty: logging.pretty,
},
};
console.log("[config] 配置概要:", JSON.stringify(summary, null, 2));
}