Initial commit
Some checks failed
CI / test (push) Has been cancelled
CI / security (push) Has been cancelled
CI / build (push) Has been cancelled

This commit is contained in:
defiQUG
2025-12-12 15:02:56 -08:00
commit 849e6a8357
891 changed files with 167728 additions and 0 deletions

View File

@@ -0,0 +1,206 @@
// DBIS Accounting Standards Service
// Fair value marking, commodity feed integration
import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
export interface ValuationData {
assetType: string;
assetId: string;
fairValue: number;
currencyCode: string;
valuationDate: Date;
source: string;
}
export class AccountingStandardsService {
/**
* Get valuation rule for asset type
*/
async getValuationRule(assetType: string) {
return await prisma.valuationRule.findFirst({
where: {
assetType,
status: 'active',
effectiveDate: {
lte: new Date(),
},
OR: [
{ expiryDate: null },
{ expiryDate: { gte: new Date() } },
],
},
orderBy: { effectiveDate: 'desc' },
});
}
/**
* Mark asset to fair value
*/
async markToFairValue(assetId: string, assetType: string, fairValue: number, currencyCode: string) {
const rule = await this.getValuationRule(assetType);
if (!rule) {
throw new Error(`No valuation rule found for asset type: ${assetType}`);
}
// Update asset value based on type
switch (assetType) {
case 'commodity':
await this.updateCommodityValue(assetId, fairValue);
break;
case 'security':
await this.updateSecurityValue(assetId, fairValue);
break;
case 'fiat':
case 'cbdc':
// Fiat and CBDC are already at fair value
break;
default:
throw new Error(`Unsupported asset type for fair value marking: ${assetType}`);
}
// Log valuation
await prisma.auditLog.create({
data: {
eventType: 'valuation',
entityType: assetType,
entityId: assetId,
action: 'mark_to_fair_value',
details: {
fairValue,
currencyCode,
valuationMethod: rule.valuationMethod,
timestamp: new Date(),
},
},
});
}
/**
* Update commodity value
*/
private async updateCommodityValue(commodityId: string, fairValue: number) {
// In production, would update commodity price
// For now, update commodity spot price if exists
const commodity = await prisma.commodity.findFirst({
where: {
id: commodityId,
},
});
if (commodity) {
await prisma.commodity.update({
where: { id: commodityId },
data: {
spotPrice: new Decimal(fairValue),
lastUpdated: new Date(),
},
});
}
}
/**
* Update security value
*/
private async updateSecurityValue(securityId: string, fairValue: number) {
const security = await prisma.security.findFirst({
where: {
securityId,
},
});
if (security) {
await prisma.security.update({
where: { id: security.id },
data: {
price: new Decimal(fairValue),
updatedAt: new Date(),
},
});
}
}
/**
* Get commodity feed price
*/
async getCommodityFeedPrice(commodityType: string, unit: string): Promise<number | null> {
const commodity = await prisma.commodity.findUnique({
where: {
commodityType_unit: {
commodityType,
unit,
},
},
});
if (!commodity) {
return null;
}
return parseFloat(commodity.spotPrice.toString());
}
/**
* Get FX reference rate
*/
async getFXReferenceRate(baseCurrency: string, quoteCurrency: string): Promise<number | null> {
const fxPair = await prisma.fxPair.findFirst({
where: {
OR: [
{
baseCurrency,
quoteCurrency,
},
{
baseCurrency: quoteCurrency,
quoteCurrency: baseCurrency,
},
],
status: 'active',
},
include: {
trades: {
where: {
status: 'settled',
},
orderBy: { timestampUtc: 'desc' },
take: 1,
},
},
});
if (!fxPair || fxPair.trades.length === 0) {
return null;
}
return parseFloat(fxPair.trades[0].price.toString());
}
/**
* Create valuation rule
*/
async createValuationRule(
assetType: string,
valuationMethod: string,
feedSource?: string,
updateFrequency: string = 'real_time'
) {
return await prisma.valuationRule.create({
data: {
id: require('uuid').v4(),
ruleId: require('uuid').v4(),
assetType,
valuationMethod,
feedSource,
updateFrequency,
status: 'active',
effectiveDate: new Date(),
},
});
}
}
export const accountingStandardsService = new AccountingStandardsService();

View File

@@ -0,0 +1,422 @@
// DBIS Reporting Engine Service
// Generate consolidated statements, SCB reports
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { accountService } from '@/core/accounts/account.service';
import { treasuryService } from '@/core/treasury/treasury.service';
import prisma from '@/shared/database/prisma';
export interface ConsolidatedStatementData {
statementType: string;
periodStart: Date;
periodEnd: Date;
}
export interface SovereignReportData {
sovereignBankId: string;
reportType: string;
reportPeriod: string;
reportDate: Date;
}
export class ReportingEngineService {
/**
* Generate Consolidated Sovereign Liquidity Report (CSLR)
*/
async generateCSLR(periodStart: Date, periodEnd: Date) {
const banks = await prisma.sovereignBank.findMany({
where: { status: 'active' },
include: {
liquidityPools: true,
accounts: true,
},
});
const consolidatedData: Record<string, unknown> = {
periodStart,
periodEnd,
reportDate: new Date(),
totalBanks: banks.length,
liquidityByCurrency: {},
totalLiquidity: 0,
bankDetails: [],
};
for (const bank of banks) {
const bankLiquidity = bank.liquidityPools.reduce(
(sum, pool) => sum + parseFloat(pool.totalLiquidity.toString()),
0
);
const lcr = await treasuryService.calculateLCR(bank.id);
const nsfr = await treasuryService.calculateNSFR(bank.id);
consolidatedData.bankDetails.push({
sovereignBankId: bank.id,
sovereignCode: bank.sovereignCode,
name: bank.name,
totalLiquidity: bankLiquidity,
lcr,
nsfr,
});
// Aggregate by currency
for (const pool of bank.liquidityPools) {
const currency = pool.currencyCode;
if (!consolidatedData.liquidityByCurrency[currency]) {
consolidatedData.liquidityByCurrency[currency] = 0;
}
consolidatedData.liquidityByCurrency[currency] += parseFloat(pool.totalLiquidity.toString());
}
consolidatedData.totalLiquidity += bankLiquidity;
}
const statement = await prisma.consolidatedStatement.create({
data: {
id: uuidv4(),
statementId: uuidv4(),
statementType: 'CSLR',
reportDate: new Date(),
periodStart,
periodEnd,
status: 'final',
statementData: consolidatedData,
publishedAt: new Date(),
},
});
return statement;
}
/**
* Generate Cross-Border Settlement Exposures Report
*/
async generateCrossBorderExposureReport(periodStart: Date, periodEnd: Date) {
const settlements = await prisma.ledgerEntry.findMany({
where: {
timestampUtc: {
gte: periodStart,
lte: periodEnd,
},
status: 'settled',
},
include: {
debitAccount: {
include: {
sovereignBank: true,
},
},
creditAccount: {
include: {
sovereignBank: true,
},
},
},
});
const exposures: Record<string, unknown> = {};
const bankPairs: Record<string, number> = {};
for (const settlement of settlements) {
const debitBank = settlement.debitAccount.sovereignBank.sovereignCode;
const creditBank = settlement.creditAccount.sovereignBank.sovereignCode;
if (debitBank !== creditBank) {
const pairKey = `${debitBank}_${creditBank}`;
const amount = parseFloat(settlement.amount.toString());
if (!bankPairs[pairKey]) {
bankPairs[pairKey] = 0;
}
bankPairs[pairKey] += amount;
// Track exposure by bank
if (!exposures[debitBank]) {
exposures[debitBank] = { outbound: 0, inbound: 0 };
}
if (!exposures[creditBank]) {
exposures[creditBank] = { outbound: 0, inbound: 0 };
}
exposures[debitBank].outbound += amount;
exposures[creditBank].inbound += amount;
}
}
const reportData = {
periodStart,
periodEnd,
reportDate: new Date(),
totalCrossBorderSettlements: settlements.filter(
(s) => s.debitAccount.sovereignBankId !== s.creditAccount.sovereignBankId
).length,
exposures,
bankPairs,
};
const statement = await prisma.consolidatedStatement.create({
data: {
id: uuidv4(),
statementId: uuidv4(),
statementType: 'CrossBorderExposure',
reportDate: new Date(),
periodStart,
periodEnd,
status: 'final',
statementData: reportData,
publishedAt: new Date(),
},
});
return statement;
}
/**
* Generate CBDC Reserve Adequacy Statement
*/
async generateCBDCReserveAdequacy(periodStart: Date, periodEnd: Date) {
const cbdcIssuances = await prisma.cbdcIssuance.findMany({
where: {
timestampUtc: {
gte: periodStart,
lte: periodEnd,
},
},
include: {
sovereignBank: true,
},
});
const adequacyData: Record<string, unknown> = {
periodStart,
periodEnd,
reportDate: new Date(),
totalIssuances: cbdcIssuances.length,
bankAdequacy: [],
totalCBDCIssued: 0,
totalReserveBacking: 0,
};
for (const issuance of cbdcIssuances) {
const bankIssuances = cbdcIssuances.filter(
(i) => i.sovereignBankId === issuance.sovereignBankId
);
const totalIssued = bankIssuances.reduce(
(sum, i) => sum + parseFloat(i.amountMinted.toString()),
0
);
const totalBacking = bankIssuances.reduce(
(sum, i) => sum + parseFloat(i.reserveBacking?.toString() || '0'),
0
);
adequacyData.bankAdequacy.push({
sovereignBankId: issuance.sovereignBankId,
sovereignCode: issuance.sovereignBank.sovereignCode,
totalCBDCIssued: totalIssued,
totalReserveBacking: totalBacking,
adequacyRatio: totalBacking > 0 ? totalIssued / totalBacking : 0,
});
adequacyData.totalCBDCIssued += totalIssued;
adequacyData.totalReserveBacking += totalBacking;
}
const statement = await prisma.consolidatedStatement.create({
data: {
id: uuidv4(),
statementId: uuidv4(),
statementType: 'CBDCReserveAdequacy',
reportDate: new Date(),
periodStart,
periodEnd,
status: 'final',
statementData: adequacyData,
publishedAt: new Date(),
},
});
return statement;
}
/**
* Generate SCB daily liquidity window report
*/
async generateDailyLiquidityReport(sovereignBankId: string, reportDate: Date) {
const lcr = await treasuryService.calculateLCR(sovereignBankId);
const nsfr = await treasuryService.calculateNSFR(sovereignBankId);
const accounts = await accountService.getAccountsBySovereign(sovereignBankId);
const liquidityData = {
reportDate,
lcr,
nsfr,
totalAccounts: accounts.length,
totalBalance: accounts.reduce((sum, acc) => sum + parseFloat(acc.balance), 0),
availableBalance: accounts.reduce((sum, acc) => sum + parseFloat(acc.availableBalance), 0),
reservedBalance: accounts.reduce((sum, acc) => sum + parseFloat(acc.reservedBalance), 0),
};
const report = await prisma.sovereignReport.create({
data: {
id: uuidv4(),
sovereignBankId,
reportId: uuidv4(),
reportType: 'daily_liquidity',
reportPeriod: 'daily',
reportDate,
dueDate: new Date(reportDate.getTime() + 24 * 60 * 60 * 1000), // Next day
status: 'submitted',
reportData: liquidityData,
submittedAt: new Date(),
},
});
return report;
}
/**
* Generate SCB weekly FX reserve update
*/
async generateWeeklyFXReserveReport(sovereignBankId: string, reportDate: Date) {
const accounts = await accountService.getAccountsBySovereign(sovereignBankId);
const fxReserves: Record<string, number> = {};
for (const account of accounts) {
if (account.assetType === 'fiat' || account.assetType === 'cbdc') {
if (!fxReserves[account.currencyCode]) {
fxReserves[account.currencyCode] = 0;
}
fxReserves[account.currencyCode] += parseFloat(account.balance);
}
}
const report = await prisma.sovereignReport.create({
data: {
id: uuidv4(),
sovereignBankId,
reportId: uuidv4(),
reportType: 'weekly_fx_reserve',
reportPeriod: 'weekly',
reportDate,
dueDate: new Date(reportDate.getTime() + 7 * 24 * 60 * 60 * 1000), // Next week
status: 'submitted',
reportData: {
reportDate,
fxReserves,
totalReserves: Object.values(fxReserves).reduce((sum, val) => sum + val, 0),
},
submittedAt: new Date(),
},
});
return report;
}
/**
* Generate SCB monthly AML compliance results
*/
async generateMonthlyAMLComplianceReport(sovereignBankId: string, reportDate: Date) {
const monthStart = new Date(reportDate.getFullYear(), reportDate.getMonth(), 1);
const monthEnd = new Date(reportDate.getFullYear(), reportDate.getMonth() + 1, 0);
const complianceRecords = await prisma.complianceRecord.findMany({
where: {
sovereignBankId,
createdAt: {
gte: monthStart,
lte: monthEnd,
},
},
});
const reportData = {
reportDate,
monthStart,
monthEnd,
totalChecks: complianceRecords.length,
clearCount: complianceRecords.filter((r) => r.status === 'clear').length,
flaggedCount: complianceRecords.filter((r) => r.status === 'flagged').length,
blockedCount: complianceRecords.filter((r) => r.status === 'blocked').length,
averageRiskScore: complianceRecords.length > 0
? complianceRecords.reduce((sum, r) => sum + r.riskScore, 0) / complianceRecords.length
: 0,
};
const report = await prisma.sovereignReport.create({
data: {
id: uuidv4(),
sovereignBankId,
reportId: uuidv4(),
reportType: 'monthly_aml_compliance',
reportPeriod: 'monthly',
reportDate,
dueDate: new Date(reportDate.getFullYear(), reportDate.getMonth() + 1, 15), // 15th of next month
status: 'submitted',
reportData,
submittedAt: new Date(),
},
});
return report;
}
/**
* Generate SCB quarterly CBDC issuance audit
*/
async generateQuarterlyCBDCAudit(sovereignBankId: string, reportDate: Date) {
const quarterStart = new Date(reportDate.getFullYear(), Math.floor(reportDate.getMonth() / 3) * 3, 1);
const quarterEnd = new Date(reportDate.getFullYear(), Math.floor(reportDate.getMonth() / 3) * 3 + 3, 0);
const issuances = await prisma.cbdcIssuance.findMany({
where: {
sovereignBankId,
timestampUtc: {
gte: quarterStart,
lte: quarterEnd,
},
},
});
const reportData = {
reportDate,
quarterStart,
quarterEnd,
totalIssuances: issuances.length,
totalMinted: issuances.reduce((sum, i) => sum + parseFloat(i.amountMinted.toString()), 0),
totalBurned: issuances.reduce((sum, i) => sum + parseFloat(i.amountBurned.toString()), 0),
netChange: issuances.reduce((sum, i) => sum + parseFloat(i.netChange.toString()), 0),
issuances: issuances.map((i) => ({
recordId: i.recordId,
operationType: i.operationType,
amountMinted: parseFloat(i.amountMinted.toString()),
amountBurned: parseFloat(i.amountBurned.toString()),
reserveBacking: i.reserveBacking ? parseFloat(i.reserveBacking.toString()) : null,
timestampUtc: i.timestampUtc,
})),
};
const report = await prisma.sovereignReport.create({
data: {
id: uuidv4(),
sovereignBankId,
reportId: uuidv4(),
reportType: 'quarterly_cbdc_audit',
reportPeriod: 'quarterly',
reportDate,
dueDate: new Date(reportDate.getFullYear(), Math.floor(reportDate.getMonth() / 3) * 3 + 3, 15),
status: 'submitted',
reportData,
submittedAt: new Date(),
},
});
return report;
}
}
export const reportingEngineService = new ReportingEngineService();

View File

@@ -0,0 +1,192 @@
// Valuation Service
// Real-time fair value calculation
import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { accountingStandardsService } from './accounting-standards.service';
export class ValuationService {
/**
* Calculate real-time fair value for an asset
*/
async calculateFairValue(assetType: string, assetId: string, currencyCode: string): Promise<number> {
const rule = await accountingStandardsService.getValuationRule(assetType);
if (!rule) {
throw new Error(`No valuation rule found for asset type: ${assetType}`);
}
switch (rule.valuationMethod) {
case 'fair_value':
return await this.calculateFairValueDirect(assetType, assetId, currencyCode);
case 'commodity_feed':
return await this.calculateFromCommodityFeed(assetType, assetId, currencyCode);
case 'fx_reference_rate':
return await this.calculateFromFXRate(assetType, assetId, currencyCode);
default:
throw new Error(`Unsupported valuation method: ${rule.valuationMethod}`);
}
}
/**
* Calculate fair value directly (for fiat, CBDC)
*/
private async calculateFairValueDirect(
assetType: string,
assetId: string,
currencyCode: string
): Promise<number> {
if (assetType === 'fiat' || assetType === 'cbdc') {
// Fiat and CBDC are already at fair value (1:1)
const account = await prisma.bankAccount.findUnique({
where: { id: assetId },
});
if (account) {
return parseFloat(account.balance.toString());
}
}
throw new Error(`Cannot calculate fair value directly for asset type: ${assetType}`);
}
/**
* Calculate from commodity feed
*/
private async calculateFromCommodityFeed(
assetType: string,
assetId: string,
currencyCode: string
): Promise<number> {
if (assetType !== 'commodity') {
throw new Error('Commodity feed valuation only applies to commodities');
}
// Get commodity
const commodity = await prisma.commodity.findFirst({
where: { id: assetId },
});
if (!commodity) {
throw new Error(`Commodity not found: ${assetId}`);
}
// Get current price from feed
const price = await accountingStandardsService.getCommodityFeedPrice(
commodity.commodityType,
commodity.unit
);
if (!price) {
throw new Error(`No price feed available for commodity: ${commodity.commodityType}`);
}
// Get quantity from account or sub-ledger
const account = await prisma.bankAccount.findFirst({
where: {
assetType: 'commodity',
currencyCode: commodity.commodityType,
},
});
const quantity = account ? parseFloat(account.balance.toString()) : 0;
return price * quantity;
}
/**
* Calculate from FX reference rate
*/
private async calculateFromFXRate(
assetType: string,
assetId: string,
currencyCode: string
): Promise<number> {
// Get account
const account = await prisma.bankAccount.findUnique({
where: { id: assetId },
});
if (!account) {
throw new Error(`Account not found: ${assetId}`);
}
const baseAmount = parseFloat(account.balance.toString());
// If account currency matches target currency, no conversion needed
if (account.currencyCode === currencyCode) {
return baseAmount;
}
// Get FX rate
const fxRate = await accountingStandardsService.getFXReferenceRate(
account.currencyCode,
currencyCode
);
if (!fxRate) {
throw new Error(
`No FX rate available for ${account.currencyCode}/${currencyCode}`
);
}
return baseAmount * fxRate;
}
/**
* Mark all assets to fair value (batch operation)
*/
async markAllToFairValue(sovereignBankId?: string) {
const where: { assetType?: string; sovereignBankId?: string } = {};
if (sovereignBankId) {
where.sovereignBankId = sovereignBankId;
}
const accounts = await prisma.bankAccount.findMany({
where,
include: {
sovereignBank: true,
},
});
const results = [];
for (const account of accounts) {
try {
const fairValue = await this.calculateFairValue(
account.assetType,
account.id,
account.currencyCode
);
await accountingStandardsService.markToFairValue(
account.id,
account.assetType,
fairValue,
account.currencyCode
);
results.push({
accountId: account.id,
assetType: account.assetType,
fairValue,
success: true,
});
} catch (error) {
results.push({
accountId: account.id,
assetType: account.assetType,
error: error instanceof Error ? error.message : 'Unknown error',
success: false,
});
}
}
return results;
}
}
export const valuationService = new ValuationService();