feat: 接入 USDT-M 合约数据 — type 字段方案
- PairType 定义移至 types/kline.ts (spot/um/cm) - Kline 接口新增 type 字段,全链路透传 - klines 5列复合主键 (exchange, symbol, type, interval, time) - 拆出 BinanceFuturesRestClient (USDMClient) - exchanges/index.ts 注册 binance_futures - trading_pairs 唯一约束加 type,种子数据加合约对 - 12个连续聚合视图 SELECT/GROUP BY/INDEX 加 type - 清理 bnkline.ts 废弃代码和 pair.ts 空函数
This commit is contained in:
@@ -20,7 +20,4 @@ export async function fetchKlines(
|
||||
limit = 500,
|
||||
): Promise<Kline[]> {
|
||||
return client.fetchKlines(symbol, startTime, limit);
|
||||
}
|
||||
|
||||
|
||||
console.log(await fetchKlines('BTCUSDT.P', 0, 10));
|
||||
}
|
||||
@@ -9,14 +9,14 @@ const repo = AppDataSource.getRepository(Kline);
|
||||
* 批量 UPSERT K 线数据到 TimescaleDB。
|
||||
*
|
||||
* 映射应用层 KlineItem → 数据库实体,通过 INSERT ... ON CONFLICT DO UPDATE
|
||||
* 实现幂等写入。冲突列为 [exchange, symbol, interval, time](四列复合主键),
|
||||
* 实现幂等写入。冲突列为 [exchange, symbol, type, interval, time](五列复合主键),
|
||||
* 冲突时更新 OHLCV 及扩展字段。
|
||||
*
|
||||
* 适用场景:
|
||||
* - 回补历史 K 线(幂等,重复拉取不产生重复行)
|
||||
* - WebSocket 实时 K 线增量刷新(更新最新一根未闭合 K 线的 high/low/close/volume)
|
||||
*
|
||||
* 注意:依赖 Kline 实体的四列复合主键 [exchange, symbol, interval, time]。
|
||||
* 注意:依赖 Kline 实体的五列复合主键 [exchange, symbol, type, interval, time]。
|
||||
* 若实体 PK 结构变更,需同步更新 conflictPaths。
|
||||
*
|
||||
* @param KlineItems - 应用层标准化 K 线数组
|
||||
@@ -35,6 +35,7 @@ export async function upsertOrUpdateKlines(KlineItems: KlineItem[]) {
|
||||
entity.time = new Date(item.openTime); // Unix ms → Date
|
||||
entity.exchange = item.exchange;
|
||||
entity.symbol = item.symbol;
|
||||
entity.type = item.type;
|
||||
entity.interval = item.interval;
|
||||
entity.open = Number(item.open);
|
||||
entity.high = Number(item.high);
|
||||
@@ -54,11 +55,11 @@ export async function upsertOrUpdateKlines(KlineItems: KlineItem[]) {
|
||||
});
|
||||
|
||||
try {
|
||||
// UPSERT: 冲突列匹配复合主键 [exchange, symbol, interval, time]
|
||||
// 实体已改为四列复合 PK,ON CONFLICT 直接命中主键约束
|
||||
// UPSERT: 冲突列匹配复合主键 [exchange, symbol, type, interval, time]
|
||||
// 实体已改为五列复合 PK,ON CONFLICT 直接命中主键约束
|
||||
// skipUpdateIfNoValuesChanged: 减少不必要的写操作
|
||||
const result = await repo.upsert(entities, {
|
||||
conflictPaths: ["exchange", "symbol", "interval", "time"],
|
||||
conflictPaths: ["exchange", "symbol", "type", "interval", "time"],
|
||||
skipUpdateIfNoValuesChanged: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AppDataSource } from "../db/data-source";
|
||||
import { TradingPair } from "../db/entities/trading-pair.entity";
|
||||
import type { PairType } from '../types';
|
||||
|
||||
const repo = AppDataSource.getRepository(TradingPair);
|
||||
|
||||
@@ -18,11 +19,13 @@ export async function getAllPairs() {
|
||||
* 获取指定交易对的历史 K 线最后补全时间。
|
||||
*
|
||||
* @param symbol - 交易对名称(如 "BTCUSDT")
|
||||
* @param type - 交易对类型(默认 'spot')
|
||||
* @returns 最后补全时间(UTC),若交易对不存在返回 undefined
|
||||
*/
|
||||
export async function getPairLastBackfillTime(symbol: string) {
|
||||
export async function getPairLastBackfillTime(symbol: string, type: PairType = 'spot') {
|
||||
const pair = await repo.findOneBy({
|
||||
symbol
|
||||
symbol,
|
||||
type,
|
||||
});
|
||||
return pair?.last_backfill_time;
|
||||
}
|
||||
@@ -35,11 +38,13 @@ export async function getPairLastBackfillTime(symbol: string) {
|
||||
*
|
||||
* @param symbol - 交易对名称(如 "BTCUSDT")
|
||||
* @param time - 新的最后补全时间(UTC)
|
||||
* @param type - 交易对类型(默认 'spot')
|
||||
* @returns 保存后的交易对实体,若交易对不存在返回 undefined
|
||||
*/
|
||||
export async function updatePairLastBackfillTime(symbol: string, time: Date) {
|
||||
export async function updatePairLastBackfillTime(symbol: string, time: Date, type: PairType = 'spot') {
|
||||
const pair = await repo.findOneBy({
|
||||
symbol
|
||||
symbol,
|
||||
type,
|
||||
});
|
||||
if (pair === null) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user