refactor(trading): 价格数据源从 SharePool 切换到做市商 cashBalance

积分股价格公式中的"绿积分"(分子)本质上就是做市商的积分值余额,
SharePool 是多余的抽象层。此次改动将价格计算的数据源从独立的
SharePool 表切换到做市商的 TradingAccount.cashBalance。

主要变更:
- price.service.ts: 通过 PrismaService 直接读取做市商 cashBalance
  作为价格公式的分子(绿积分),移除 SharePoolRepository 依赖
- asset.service.ts: getMarketOverview() 同步切换数据源
- order.service.ts: 10%交易手续费从 sharePoolRepository.feeIn()
  改为 increment 回做市商的 TradingAccount,手续费自然留存在池中
- burn.service.ts: 移除 SharePool 初始化逻辑和依赖
- infrastructure.module.ts: 移除 SharePoolRepository 注册和导出
- seed.ts: 移除 SharePool 57.6亿初始化

价格公式、10%手续费扣除、销毁机制均保持不变,仅切换数据源。
SharePool 表和 repository 文件保留在磁盘上供历史数据参考。

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-02 23:13:59 -08:00
parent 14e70b56bb
commit 999d0389b3
6 changed files with 70 additions and 61 deletions

View File

@ -93,22 +93,7 @@ async function main() {
console.log('Black hole account already exists');
}
// 4. 初始化积分股池(绿积分池)
// 初始绿积分为 57.6亿,使初始价格约为 0.576 (57.6亿 / 100.02亿 ≈ 0.576)
const INITIAL_GREEN_POINTS = '5760000000'; // 57.6亿
const existingSharePool = await prisma.sharePool.findFirst();
if (!existingSharePool) {
await prisma.sharePool.create({
data: {
greenPoints: new Decimal(INITIAL_GREEN_POINTS),
totalInflow: new Decimal(INITIAL_GREEN_POINTS),
totalOutflow: new Decimal(0),
},
});
console.log(`Created share pool with initial green points: ${INITIAL_GREEN_POINTS} (57.6亿)`);
} else {
console.log('Share pool already exists');
}
// 4. 绿积分(价格分子)现在由做市商的 cashBalance 提供,不再需要独立的 SharePool
// 5. 初始化流通池
const existingCirculationPool = await prisma.circulationPool.findFirst();

View File

@ -4,7 +4,7 @@ import { TradingCalculatorService } from '../../domain/services/trading-calculat
import { TradingAccountRepository } from '../../infrastructure/persistence/repositories/trading-account.repository';
import { BlackHoleRepository } from '../../infrastructure/persistence/repositories/black-hole.repository';
import { CirculationPoolRepository } from '../../infrastructure/persistence/repositories/circulation-pool.repository';
import { SharePoolRepository } from '../../infrastructure/persistence/repositories/share-pool.repository';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { PriceService } from './price.service';
import { Money } from '../../domain/value-objects/money.vo';
import Decimal from 'decimal.js';
@ -59,11 +59,14 @@ export class AssetService {
private readonly calculator = new TradingCalculatorService();
private readonly miningServiceUrl: string;
// 缓存做市商 accountSequence
private mmAccountSequence: string | null = null;
constructor(
private readonly tradingAccountRepository: TradingAccountRepository,
private readonly blackHoleRepository: BlackHoleRepository,
private readonly circulationPoolRepository: CirculationPoolRepository,
private readonly sharePoolRepository: SharePoolRepository,
private readonly prisma: PrismaService,
private readonly priceService: PriceService,
private readonly configService: ConfigService,
) {
@ -218,6 +221,24 @@ export class AssetService {
};
}
/**
* "绿积分"
*/
private async getGreenPoints(): Promise<Money> {
if (!this.mmAccountSequence) {
const config = await this.prisma.marketMakerConfig.findUnique({
where: { name: 'MAIN_MARKET_MAKER' },
});
this.mmAccountSequence = config?.accountSequence || null;
}
if (!this.mmAccountSequence) return Money.zero();
const account = await this.prisma.tradingAccount.findUnique({
where: { accountSequence: this.mmAccountSequence },
});
return account ? new Money(account.cashBalance) : Money.zero();
}
/**
*
*/
@ -232,13 +253,11 @@ export class AssetService {
burnTarget: string;
burnProgress: string;
}> {
const [sharePool, blackHole, circulationPool] = await Promise.all([
this.sharePoolRepository.getPool(),
const [greenPoints, blackHole, circulationPool] = await Promise.all([
this.getGreenPoints(),
this.blackHoleRepository.getBlackHole(),
this.circulationPoolRepository.getPool(),
]);
const greenPoints = sharePool?.greenPoints || Money.zero();
const blackHoleAmount = blackHole?.totalBurned || Money.zero();
const circulationPoolAmount = circulationPool?.totalShares || Money.zero();

View File

@ -2,7 +2,6 @@ import { Injectable, Logger } from '@nestjs/common';
import { TradingCalculatorService } from '../../domain/services/trading-calculator.service';
import { BlackHoleRepository } from '../../infrastructure/persistence/repositories/black-hole.repository';
import { CirculationPoolRepository } from '../../infrastructure/persistence/repositories/circulation-pool.repository';
import { SharePoolRepository } from '../../infrastructure/persistence/repositories/share-pool.repository';
import { TradingConfigRepository } from '../../infrastructure/persistence/repositories/trading-config.repository';
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
import { RedisService } from '../../infrastructure/redis/redis.service';
@ -39,7 +38,6 @@ export class BurnService {
constructor(
private readonly blackHoleRepository: BlackHoleRepository,
private readonly circulationPoolRepository: CirculationPoolRepository,
private readonly sharePoolRepository: SharePoolRepository,
private readonly tradingConfigRepository: TradingConfigRepository,
private readonly outboxRepository: OutboxRepository,
private readonly redis: RedisService,
@ -243,11 +241,10 @@ export class BurnService {
*
*/
async initialize(): Promise<void> {
const [existingConfig, existingBlackHole, existingSharePool, existingCirculationPool] =
const [existingConfig, existingBlackHole, existingCirculationPool] =
await Promise.all([
this.tradingConfigRepository.getConfig(),
this.blackHoleRepository.getBlackHole(),
this.sharePoolRepository.getPool(),
this.circulationPoolRepository.getPool(),
]);
@ -263,13 +260,7 @@ export class BurnService {
this.logger.log('Black hole initialized');
}
// 初始化积分股池(绿积分池),初始值 57.6亿
// 使初始价格约为 0.576 (57.6亿 / 100.02亿 ≈ 0.576)
if (!existingSharePool) {
const initialGreenPoints = new Money('5760000000'); // 57.6亿
await this.sharePoolRepository.initializePool(initialGreenPoints);
this.logger.log(`Share pool initialized with ${initialGreenPoints.toFixed(8)} green points (57.6亿)`);
}
// 绿积分(价格分子)现在由做市商的 cashBalance 提供,不再需要独立的 SharePool
// 初始化流通池
if (!existingCirculationPool) {

View File

@ -2,7 +2,6 @@ import { Injectable, Logger } from '@nestjs/common';
import { OrderRepository } from '../../infrastructure/persistence/repositories/order.repository';
import { TradingAccountRepository } from '../../infrastructure/persistence/repositories/trading-account.repository';
import { CirculationPoolRepository } from '../../infrastructure/persistence/repositories/circulation-pool.repository';
import { SharePoolRepository } from '../../infrastructure/persistence/repositories/share-pool.repository';
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { RedisService } from '../../infrastructure/redis/redis.service';
@ -32,7 +31,6 @@ export class OrderService {
private readonly orderRepository: OrderRepository,
private readonly accountRepository: TradingAccountRepository,
private readonly circulationPoolRepository: CirculationPoolRepository,
private readonly sharePoolRepository: SharePoolRepository,
private readonly outboxRepository: OutboxRepository,
private readonly prisma: PrismaService,
private readonly redis: RedisService,
@ -338,21 +336,22 @@ export class OrderService {
this.logger.warn(`Failed to add shares to circulation pool: ${error}`);
}
// 10%手续费进入积分股池(非关键操作,可以在事务外执行
// 10%手续费存入做市商账户(做市商的 cashBalance 即积分股池的绿积分
try {
await this.sharePoolRepository.feeIn(
tradeFee,
match.trade.tradeNo,
match.buyOrder.accountSequence,
match.sellOrder.accountSequence,
match.buyOrder.source,
match.sellOrder.source,
);
this.logger.debug(
`Trade fee added to share pool: ${tradeFee.toFixed(8)}, tradeNo=${match.trade.tradeNo}`,
);
const mmConfig = await this.prisma.marketMakerConfig.findUnique({
where: { name: 'MAIN_MARKET_MAKER' },
});
if (mmConfig) {
await this.prisma.tradingAccount.update({
where: { accountSequence: mmConfig.accountSequence },
data: { cashBalance: { increment: tradeFee.value } },
});
this.logger.debug(
`Trade fee deposited to market maker: ${tradeFee.toFixed(8)}, tradeNo=${match.trade.tradeNo}`,
);
}
} catch (error) {
this.logger.error(`Failed to add trade fee to share pool: ${error}`);
this.logger.error(`Failed to deposit trade fee to market maker: ${error}`);
}
this.logger.log(

View File

@ -1,10 +1,10 @@
import { Injectable, Logger } from '@nestjs/common';
import { TradingCalculatorService } from '../../domain/services/trading-calculator.service';
import { BlackHoleRepository } from '../../infrastructure/persistence/repositories/black-hole.repository';
import { SharePoolRepository } from '../../infrastructure/persistence/repositories/share-pool.repository';
import { CirculationPoolRepository } from '../../infrastructure/persistence/repositories/circulation-pool.repository';
import { PriceSnapshotRepository } from '../../infrastructure/persistence/repositories/price-snapshot.repository';
import { TradingConfigRepository } from '../../infrastructure/persistence/repositories/trading-config.repository';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { Money } from '../../domain/value-objects/money.vo';
import Decimal from 'decimal.js';
@ -26,27 +26,47 @@ export class PriceService {
private readonly logger = new Logger(PriceService.name);
private readonly calculator = new TradingCalculatorService();
// 缓存做市商 accountSequence避免每次查 MarketMakerConfig
private mmAccountSequence: string | null = null;
constructor(
private readonly blackHoleRepository: BlackHoleRepository,
private readonly sharePoolRepository: SharePoolRepository,
private readonly circulationPoolRepository: CirculationPoolRepository,
private readonly priceSnapshotRepository: PriceSnapshotRepository,
private readonly tradingConfigRepository: TradingConfigRepository,
private readonly prisma: PrismaService,
) {}
/**
* "绿积分"
* cashBalance 绿
*/
private async getGreenPoints(): Promise<Money> {
if (!this.mmAccountSequence) {
const config = await this.prisma.marketMakerConfig.findUnique({
where: { name: 'MAIN_MARKET_MAKER' },
});
this.mmAccountSequence = config?.accountSequence || null;
}
if (!this.mmAccountSequence) return Money.zero();
const account = await this.prisma.tradingAccount.findUnique({
where: { accountSequence: this.mmAccountSequence },
});
return account ? new Money(account.cashBalance) : Money.zero();
}
/**
*
*/
async getCurrentPrice(): Promise<PriceInfo> {
const [sharePool, blackHole, circulationPool, config, firstSnapshot] = await Promise.all([
this.sharePoolRepository.getPool(),
const [greenPoints, blackHole, circulationPool, config, firstSnapshot] = await Promise.all([
this.getGreenPoints(),
this.blackHoleRepository.getBlackHole(),
this.circulationPoolRepository.getPool(),
this.tradingConfigRepository.getConfig(),
this.priceSnapshotRepository.getFirstSnapshot(),
]);
const greenPoints = sharePool?.greenPoints || Money.zero();
const blackHoleAmount = blackHole?.totalBurned || Money.zero();
const circulationPoolAmount = circulationPool?.totalShares || Money.zero();
@ -152,14 +172,12 @@ export class PriceService {
*/
async createSnapshot(): Promise<void> {
try {
const [sharePool, blackHole, circulationPool, config] = await Promise.all([
this.sharePoolRepository.getPool(),
const [greenPoints, blackHole, circulationPool, config] = await Promise.all([
this.getGreenPoints(),
this.blackHoleRepository.getBlackHole(),
this.circulationPoolRepository.getPool(),
this.tradingConfigRepository.getConfig(),
]);
const greenPoints = sharePool?.greenPoints || Money.zero();
const blackHoleAmount = blackHole?.totalBurned || Money.zero();
const circulationPoolAmount = circulationPool?.totalShares || Money.zero();

View File

@ -8,7 +8,6 @@ import { OrderRepository } from './persistence/repositories/order.repository';
import { OutboxRepository } from './persistence/repositories/outbox.repository';
import { TradingConfigRepository } from './persistence/repositories/trading-config.repository';
import { BlackHoleRepository } from './persistence/repositories/black-hole.repository';
import { SharePoolRepository } from './persistence/repositories/share-pool.repository';
import { CirculationPoolRepository } from './persistence/repositories/circulation-pool.repository';
import { PriceSnapshotRepository } from './persistence/repositories/price-snapshot.repository';
import { ProcessedEventRepository } from './persistence/repositories/processed-event.repository';
@ -53,7 +52,6 @@ import { IdentityClient } from './identity/identity.client';
OutboxRepository,
TradingConfigRepository,
BlackHoleRepository,
SharePoolRepository,
CirculationPoolRepository,
PriceSnapshotRepository,
ProcessedEventRepository,
@ -81,7 +79,6 @@ import { IdentityClient } from './identity/identity.client';
OutboxRepository,
TradingConfigRepository,
BlackHoleRepository,
SharePoolRepository,
CirculationPoolRepository,
PriceSnapshotRepository,
ProcessedEventRepository,