refactor(data): 重构交易所适配器,修复 Kline 实体复合主键

- 废弃旧 adapter 体系 (base/binance/types.ts),新增 base_rest/rest.ts
  基于 Binance 官方 SDK 实现 REST K 线拉取
- Kline 实体改为四列复合主键 (exchange/symbol/interval/time),
  修复单列 time PK 导致的跨 symbol 写入冲突
- 新增 filterConsecutive():K 线连续性过滤,首缺口截断策略
- 新增 service/kline.ts:批量 UPSERT K 线入库
- 新增 types/ 共享类型定义、example/ 示例、run/ 运行脚本
This commit is contained in:
Rekey
2026-06-08 18:18:16 +08:00
parent 85a0031a78
commit 5e385547c7
16 changed files with 829 additions and 1043 deletions
+77
View File
@@ -0,0 +1,77 @@
import { logger } from "../utils/logger";
import { AppDataSource } from "../db/data-source";
import { Exchange } from "../db/entities/exchange.entity";
import { TradingPair } from "../db/entities/trading-pair.entity";
interface PairSeed {
symbol: string;
baseAsset: string;
quoteAsset: string;
}
const PAIRS: PairSeed[] = [
{ symbol: "BTCUSDT", baseAsset: "BTC", quoteAsset: "USDT" },
{ symbol: "ETHUSDT", baseAsset: "ETH", quoteAsset: "USDT" },
];
async function run(): Promise<void> {
logger.info("Seeding trading pairs...");
const exchangeRepo = AppDataSource.getRepository(Exchange);
const pairRepo = AppDataSource.getRepository(TradingPair);
// 1. 确保 binance 交易所存在
let exchange = await exchangeRepo.findOne({ where: { name: "binance" } });
if (!exchange) {
exchange = exchangeRepo.create({
name: "binance",
label: "Binance",
enabled: true,
});
await exchangeRepo.save(exchange);
logger.info("Created exchange: binance");
} else {
logger.info("Exchange already exists: binance");
}
// 2. 逐个插入交易对(跳过已存在的)
let created = 0;
let skipped = 0;
for (const seed of PAIRS) {
const existing = await pairRepo.findOne({
where: {
exchange: { id: exchange.id },
symbol: seed.symbol,
},
});
if (existing) {
logger.info({ symbol: seed.symbol }, "Trading pair already exists, skipping");
skipped++;
continue;
}
const pair = pairRepo.create({
exchange,
symbol: seed.symbol,
base_asset: seed.baseAsset,
quote_asset: seed.quoteAsset,
active: true,
kline_synthesis_enabled: true,
});
await pairRepo.save(pair);
created++;
logger.info({ symbol: seed.symbol, id: pair.id }, "Created trading pair");
}
logger.info({ created, skipped, total: PAIRS.length }, "Seeding complete");
await AppDataSource.destroy();
process.exit(0);
}
run().catch((err) => {
logger.error({ err }, "Seeding failed");
process.exit(1);
});