rwadurian/backend/services/mining-service/src/application/queries/get-price.query.ts

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(),
};
}
}