108 lines
3.0 KiB
TypeScript
108 lines
3.0 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { PriceSnapshotRepository, PriceSnapshotEntity } from '../../infrastructure/persistence/repositories/price-snapshot.repository';
|
|
|
|
export interface PriceDto {
|
|
snapshotTime: Date;
|
|
price: string;
|
|
sharePool: string;
|
|
blackHoleAmount: string;
|
|
circulationPool: string;
|
|
effectiveDenominator: string;
|
|
}
|
|
|
|
export interface KLineDataDto {
|
|
time: Date;
|
|
open: string;
|
|
high: string;
|
|
low: string;
|
|
close: string;
|
|
}
|
|
|
|
@Injectable()
|
|
export class GetPriceQuery {
|
|
constructor(private readonly priceRepository: PriceSnapshotRepository) {}
|
|
|
|
async getCurrentPrice(): Promise<PriceDto | null> {
|
|
const snapshot = await this.priceRepository.getLatestSnapshot();
|
|
if (!snapshot) {
|
|
return null;
|
|
}
|
|
return this.toDto(snapshot);
|
|
}
|
|
|
|
async getPriceAt(time: Date): Promise<PriceDto | null> {
|
|
const snapshot = await this.priceRepository.getSnapshotAt(time);
|
|
if (!snapshot) {
|
|
return null;
|
|
}
|
|
return this.toDto(snapshot);
|
|
}
|
|
|
|
async getPriceHistory(
|
|
startTime: Date,
|
|
endTime: Date,
|
|
interval: 'minute' | 'hour' | 'day',
|
|
): Promise<PriceDto[]> {
|
|
const snapshots = await this.priceRepository.getPriceHistory(startTime, endTime, interval);
|
|
return snapshots.map((s) => this.toDto(s));
|
|
}
|
|
|
|
async getKLineData(
|
|
startTime: Date,
|
|
endTime: Date,
|
|
interval: 'minute' | 'hour' | 'day',
|
|
): Promise<KLineDataDto[]> {
|
|
const snapshots = await this.priceRepository.getPriceHistory(startTime, endTime, 'minute');
|
|
|
|
// 按间隔分组
|
|
const grouped = new Map<string, PriceSnapshotEntity[]>();
|
|
|
|
for (const snapshot of snapshots) {
|
|
let key: string;
|
|
if (interval === 'minute') {
|
|
key = snapshot.snapshotTime.toISOString().substring(0, 16);
|
|
} else if (interval === 'hour') {
|
|
key = snapshot.snapshotTime.toISOString().substring(0, 13);
|
|
} else {
|
|
key = snapshot.snapshotTime.toISOString().substring(0, 10);
|
|
}
|
|
|
|
if (!grouped.has(key)) {
|
|
grouped.set(key, []);
|
|
}
|
|
grouped.get(key)!.push(snapshot);
|
|
}
|
|
|
|
// 生成 K 线数据
|
|
const kLineData: KLineDataDto[] = [];
|
|
|
|
for (const [key, items] of grouped) {
|
|
if (items.length === 0) continue;
|
|
|
|
const prices = items.map((i) => i.price.value.toNumber());
|
|
const times = items.map((i) => i.snapshotTime);
|
|
|
|
kLineData.push({
|
|
time: times[0],
|
|
open: items[0].price.toString(),
|
|
high: Math.max(...prices).toString(),
|
|
low: Math.min(...prices).toString(),
|
|
close: items[items.length - 1].price.toString(),
|
|
});
|
|
}
|
|
|
|
return kLineData.sort((a, b) => a.time.getTime() - b.time.getTime());
|
|
}
|
|
|
|
private toDto(snapshot: PriceSnapshotEntity): PriceDto {
|
|
return {
|
|
snapshotTime: snapshot.snapshotTime,
|
|
price: snapshot.price.toString(),
|
|
sharePool: snapshot.sharePool.toString(),
|
|
blackHoleAmount: snapshot.blackHoleAmount.toString(),
|
|
circulationPool: snapshot.circulationPool.toString(),
|
|
effectiveDenominator: snapshot.effectiveDenominator.toString(),
|
|
};
|
|
}
|
|
}
|