Files
trade/data/config/index.ts
T
Rekey 85a0031a78 feat(data): 实现 Binance WebSocket 适配器与架构重构
- 新增 exchanges/ 模块:MarketDataFeed 统一接口、BaseExchangeAdapter 抽象基类、
  BinanceAdapter 完整实现(WebSocket + REST)
- WebSocket 层基于 binance 官方 SDK 的 WebsocketClient,自动多路复用与断线重连
- REST 层使用 MainClient(Spot),实现 fetchKlines 自动分页补拉 + fetchMarkets 元数据解析
- 数据标准化:Ticker/Trade/OrderBook/Kline 类型定义与 Binance 原生格式互转
- 引入 RxJS Subject 作为统一事件流管道,按 eventType 运行时路由分发
- 重构 config/:YAML 驱动配置加载 + 零依赖运行时校验(fail-fast)
- 重构 db/:TypeORM DataSource 配置 + TimescaleDB K 线 Hypertable 实体
- 新增 utils/logger.ts:Pino 结构化日志(开发环境 pino-pretty 彩色输出)
- 新增 env.yaml 作为 TS/Python 共享的统一环境配置源
- 删除旧版手写 SQL schema 与散落配置文件,收敛到 TypeORM 实体管理
- 安装 rxjs@7.8.2 依赖
2026-06-08 01:24:48 +08:00

161 lines
5.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);
//
// 配置文件位置:<project_root>/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, "../..");
}
/**
* 从项目根目录读取 env.yaml 并解析为原始对象。
* 文件不存在时抛出明确错误,不做静默降级。
*/
function loadYamlConfig(): unknown {
const root = getProjectRoot();
const yamlPath = resolve(root, "env.yaml");
let content: string;
try {
content = readFileSync(yamlPath, "utf-8");
} catch {
throw new Error(
`[config] 无法读取配置文件: ${yamlPath}\n` +
`请确保项目根目录存在 env.yaml(可参考 data/.env.example 的结构)。`,
);
}
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;
// ============================================================
// 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,
},
logging: {
level: logging.level,
nodeEnv: logging.nodeEnv,
pretty: logging.pretty,
},
};
console.log("[config] 配置概要:", JSON.stringify(summary, null, 2));
}