309b11ae30
- kline.entity.ts: compress_segmentby 移除 interval(基表固定 1m 无需分段),schedule_interval 365d→30d 与 init-db SQL 一致 - data-source.ts: 生产环境关闭 synchronize,以 init-db SQL 脚本为建表唯一来源 - 新增 data/db/init-db/ 初始化 SQL 链: 01-timescaledb.sql — 启用 TimescaleDB 扩展 02-init-tables.sql — 核心业务表 + klines hypertable(7d chunk / 30d 压缩) 03-continuous-aggregates.sql — 分层连续聚合视图(5m→15m→30m→1h→4h→1d→1w)
137 lines
4.5 KiB
TypeScript
137 lines
4.5 KiB
TypeScript
// ============================================================
|
||
// kline.entity.ts — TimescaleDB K 线 Hypertable 实体
|
||
// ============================================================
|
||
// 映射到 PostgreSQL klines 表(TimescaleDB hypertable)。
|
||
// 不继承 CommonBaseEntity — 使用 @timescaledb/typeorm 的
|
||
// @Hypertable / @TimeColumn 装饰器管理 TimescaleDB 特性。
|
||
//
|
||
// 关键 TimescaleDB 特性(由 @Hypertable 装饰器自动配置):
|
||
// - 自动按 time 列做时间分区(by_range)
|
||
// - 列式压缩(compress),30 天后自动执行
|
||
// - 通过 ContinuousAggregate 生成高周期 K 线视图
|
||
//
|
||
// 注意:@timescaledb/typeorm v0.0.1 为实验版本,
|
||
// 不支持空间分区(partitioning_column)。
|
||
// 若需要空间分区,可通过 db/init-db/ 下的 SQL 脚本手动添加。
|
||
// ============================================================
|
||
|
||
import { Hypertable, TimeColumn } from "@timescaledb/typeorm";
|
||
import {
|
||
Entity,
|
||
PrimaryColumn,
|
||
Column,
|
||
CreateDateColumn,
|
||
UpdateDateColumn,
|
||
} from "typeorm";
|
||
|
||
import type { KlineInterval } from '../../types';
|
||
|
||
/**
|
||
* 1 分钟 K 线 Hypertable
|
||
*
|
||
* 存储交易所推送的 OHLCV 数据。写入使用 UPSERT
|
||
* (ON CONFLICT DO UPDATE),已存在的 K 线只更新
|
||
* high/low/close/volume 增量。
|
||
*
|
||
* 高周期 K 线(5m+)通过 TimescaleDB 连续聚合视图
|
||
* 从 1m 表自动生成,无需单独建表。
|
||
*/
|
||
@Hypertable({
|
||
compression: {
|
||
compress: true,
|
||
compress_orderby: "time DESC",
|
||
compress_segmentby: "exchange, symbol",
|
||
policy: {
|
||
schedule_interval: "30 days", // 30 天后自动压缩
|
||
},
|
||
},
|
||
})
|
||
@Entity("klines")
|
||
export class Kline {
|
||
/**
|
||
* 复合主键:交易所 + 交易对 + 周期 + 时间
|
||
*
|
||
* 设计原因:
|
||
* - 不同 symbol 在同一时刻有各自的 K 线,单列 time PK 会导致跨 symbol 冲突
|
||
* - 四列复合主键 = 业务唯一性语义,同时满足 TimescaleDB hypertable 要求
|
||
* (分区列 time 必须包含在主键中)
|
||
* - 不再需要额外的 @Index unique — 复合主键已保证唯一性
|
||
*/
|
||
/** 交易所标识(binance / okx / bybit) */
|
||
@PrimaryColumn("text")
|
||
exchange!: string;
|
||
|
||
/** 交易对符号(如 BTCUSDT) */
|
||
@PrimaryColumn("text")
|
||
symbol!: string;
|
||
|
||
/** K 线周期(1m) */
|
||
@PrimaryColumn("text")
|
||
interval!: KlineInterval;
|
||
|
||
/** K 线开盘时间(UTC)— @timescaledb/typeorm 自动标记为时间分区列 */
|
||
@TimeColumn()
|
||
@PrimaryColumn("timestamptz")
|
||
time!: Date;
|
||
|
||
// ============================================================
|
||
// OHLCV 价格数据(NUMERIC(20,8) 精度,与交易所对齐)
|
||
// ============================================================
|
||
|
||
/** 开盘价 */
|
||
@Column("numeric", { precision: 20, scale: 8 })
|
||
open!: number;
|
||
|
||
/** 最高价 */
|
||
@Column("numeric", { precision: 20, scale: 8 })
|
||
high!: number;
|
||
|
||
/** 最低价 */
|
||
@Column("numeric", { precision: 20, scale: 8 })
|
||
low!: number;
|
||
|
||
/** 收盘价 */
|
||
@Column("numeric", { precision: 20, scale: 8 })
|
||
close!: number;
|
||
|
||
/** 成交量(base 币种) */
|
||
@Column("numeric", { precision: 20, scale: 8 })
|
||
volume!: number;
|
||
|
||
// ============================================================
|
||
// 扩展字段(Binance 等交易所提供)
|
||
// ============================================================
|
||
|
||
/** 成交额(quote 币种) */
|
||
@Column("numeric", { precision: 20, scale: 8, nullable: true })
|
||
quote_volume?: number;
|
||
|
||
/** 主动买入成交量(base 币种) */
|
||
@Column("numeric", { precision: 20, scale: 8, nullable: true })
|
||
taker_buy_base_vol?: number;
|
||
|
||
/** 主动买入成交额(quote 币种) */
|
||
@Column("numeric", { precision: 20, scale: 8, nullable: true })
|
||
taker_buy_quote_vol?: number;
|
||
|
||
/** 成交笔数 */
|
||
@Column("integer", { nullable: true })
|
||
trade_count?: number;
|
||
|
||
/** K 线是否已关闭(true = 该周期 K 线不再变化) */
|
||
@Column("boolean", { default: true })
|
||
is_closed!: boolean;
|
||
|
||
// ============================================================
|
||
// 审计字段
|
||
// ============================================================
|
||
|
||
/** 记录首次写入时间 */
|
||
@CreateDateColumn({ type: "timestamptz", name: "created_at" })
|
||
createdAt!: Date;
|
||
|
||
/** 记录最后更新时间 */
|
||
@UpdateDateColumn({ type: "timestamptz", name: "updated_at" })
|
||
updatedAt!: Date;
|
||
}
|