// ============================================================ // kline.entity.ts — TimescaleDB K 线 Hypertable 实体 // ============================================================ // 映射到 PostgreSQL klines 表(TimescaleDB hypertable)。 // 不继承 CommonBaseEntity — 使用 @timescaledb/typeorm 的 // @Hypertable / @TimeColumn 装饰器管理 TimescaleDB 特性。 // // 关键 TimescaleDB 特性(由 @Hypertable 装饰器自动配置): // - 自动按 time 列做时间分区(by_range) // - 列式压缩(compress),7 天后自动执行 // - 通过 ContinuousAggregate 生成高周期 K 线视图 // // 注意:@timescaledb/typeorm v0.0.1 为实验版本, // 不支持空间分区(partitioning_column)。 // 若需要空间分区,可通过 db/init-db/ 下的 SQL 脚本手动添加。 // ============================================================ import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; import { Entity, PrimaryColumn, Column, Index, CreateDateColumn, UpdateDateColumn, } from "typeorm"; /** K 线周期枚举 */ export type KlineInterval = | "1m" | "5m" | "15m" | "30m" | "1h" | "4h" | "1d" | "1w"; /** * 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, interval", policy: { schedule_interval: "365 days", // 365 天后自动压缩 }, }, }) @Index(["exchange", "symbol", "interval", "time"], { unique: true }) @Entity("klines") export class Kline { /** K 线开盘时间(UTC)— @timescaledb/typeorm 自动标记为时间分区列 */ @TimeColumn() @PrimaryColumn("timestamptz") time!: Date; /** 交易所标识(binance / okx / bybit) */ @Column("text") exchange!: string; /** 交易对符号(如 BTCUSDT) */ @Column("text") symbol!: string; /** K 线周期(1m) */ @Column("text") interval!: KlineInterval; // ============================================================ // 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; }