refactor: migrate API keys to config, extend Kline intervals, add DB extensions
Security: - Move hardcoded Binance API key/secret from rest.ts to env.yaml (exchange config segment) - Add ExchangeConfig validation in config/validators.ts - Export typed exchange config from config/index.ts - Update AGENTS/07-caveats.md to reflect the new policy Kline intervals (add 3m / 2h / 6h / 8h / 1mon): - TypeScript: update KlineInterval type, KLINE_INTERVAL_MS mapping, build_aggregates_sql refresh chain - Python: sync KlineInterval Literal type, INTERVAL_TO_TABLE and INTERVAL_MS mappings, db_test table list - SQL: add 5 continuous aggregate materialized views (klines_3m/2h/6h/8h/1mon) with indexes - SQL: extend default kline_intervals in trading_pairs table - SQL: add cross-sectional query indexes for klines_1d and klines_1w DB: - Enable pg_prewarm extension (backtest warmup) - Enable pg_stat_statements extension (slow query monitoring) Other: - data/run/exchange.ts: graceful pgsql shutdown after backfill completes - Config path: load from data/env.yaml (symlink) instead of project root
This commit is contained in:
+26
-5
@@ -11,7 +11,7 @@
|
||||
// const ds = new DataSource({ ...pgsql });
|
||||
// const redisClient = new Redis(redis.url);
|
||||
//
|
||||
// 配置文件位置:<project_root>/env.yaml
|
||||
// 配置文件位置:data/env.yaml(软链接 → 项目根目录 env.yaml)
|
||||
// TypeScript / Python 模块共享同一份配置。
|
||||
// ============================================================
|
||||
|
||||
@@ -39,12 +39,14 @@ function getProjectRoot(): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从项目根目录读取 env.yaml 并解析为原始对象。
|
||||
* 读取 data/env.yaml(软链接指向项目根目录 env.yaml)并解析为原始对象。
|
||||
* 文件不存在时抛出明确错误,不做静默降级。
|
||||
*/
|
||||
function loadYamlConfig(): unknown {
|
||||
const root = getProjectRoot();
|
||||
const yamlPath = resolve(root, "env.yaml");
|
||||
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 {
|
||||
@@ -52,7 +54,7 @@ function loadYamlConfig(): unknown {
|
||||
} catch {
|
||||
throw new Error(
|
||||
`[config] 无法读取配置文件: ${yamlPath}\n` +
|
||||
`请确保项目根目录存在 env.yaml(可参考 data/.env.example 的结构)。`,
|
||||
`请确保 data/env.yaml 软链接指向项目根目录的 env.yaml。`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,6 +133,19 @@ export const logging = {
|
||||
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,
|
||||
},
|
||||
} as const;
|
||||
|
||||
// ============================================================
|
||||
// 4. 工具:运行时打印配置概要(不含敏感信息)
|
||||
// ============================================================
|
||||
@@ -150,6 +165,12 @@ export function printConfigSummary(): void {
|
||||
url: redis.url.replace(/\/\/.*@/, "//***@"), // 隐藏密码
|
||||
publishEnabled: redis.publishEnabled,
|
||||
},
|
||||
exchange: {
|
||||
binance: {
|
||||
apiKey: exchange.binance.apiKey.slice(0, 6) + "***",
|
||||
apiSecret: "***",
|
||||
},
|
||||
},
|
||||
logging: {
|
||||
level: logging.level,
|
||||
nodeEnv: logging.nodeEnv,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
export interface EnvConfig {
|
||||
db: DbConfig;
|
||||
redis: RedisConfig;
|
||||
exchange: ExchangeConfig;
|
||||
logging: LoggingConfig;
|
||||
}
|
||||
|
||||
@@ -31,6 +32,18 @@ export interface RedisConfig {
|
||||
publish_enabled: boolean;
|
||||
}
|
||||
|
||||
/** 交易所 API 密钥配置(按交易所 ID 索引) */
|
||||
export interface ExchangeConfig {
|
||||
binance: ExchangeApiKeys;
|
||||
// 未来扩展:okx、bybit 等
|
||||
[exchangeId: string]: ExchangeApiKeys | undefined;
|
||||
}
|
||||
|
||||
export interface ExchangeApiKeys {
|
||||
api_key: string;
|
||||
api_secret: string;
|
||||
}
|
||||
|
||||
export interface LoggingConfig {
|
||||
level: "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
||||
node_env: "development" | "production" | "test";
|
||||
@@ -77,6 +90,21 @@ export function validateConfig(raw: unknown): EnvConfig {
|
||||
const redisUrl = assertString(redisObj["url"], "redis.url");
|
||||
const redisPublishEnabled = assertBoolean(redisObj["publish_enabled"], "redis.publish_enabled");
|
||||
|
||||
// --- exchange ---
|
||||
const exchange = obj["exchange"];
|
||||
if (typeof exchange !== "object" || exchange === null) {
|
||||
throw new Error("[config] env.yaml 缺少 exchange 配置段");
|
||||
}
|
||||
const exObj = exchange as Record<string, unknown>;
|
||||
|
||||
const binance = exObj["binance"];
|
||||
if (typeof binance !== "object" || binance === null) {
|
||||
throw new Error("[config] env.yaml exchange 缺少 binance 配置");
|
||||
}
|
||||
const binanceObj = binance as Record<string, unknown>;
|
||||
const binanceApiKey = assertString(binanceObj["api_key"], "exchange.binance.api_key");
|
||||
const binanceApiSecret = assertString(binanceObj["api_secret"], "exchange.binance.api_secret");
|
||||
|
||||
// --- logging ---
|
||||
const logging = obj["logging"];
|
||||
if (typeof logging !== "object" || logging === null) {
|
||||
@@ -99,6 +127,12 @@ export function validateConfig(raw: unknown): EnvConfig {
|
||||
url: redisUrl,
|
||||
publish_enabled: redisPublishEnabled,
|
||||
},
|
||||
exchange: {
|
||||
binance: {
|
||||
api_key: binanceApiKey,
|
||||
api_secret: binanceApiSecret,
|
||||
},
|
||||
},
|
||||
logging: {
|
||||
level: logLevel,
|
||||
node_env: nodeEnv,
|
||||
|
||||
Reference in New Issue
Block a user